/* Loaded globally, not scoped: the shell classes are used by MainLayout and by
   its children, and Blazor's scope attribute does not cross component boundaries.
   Put this in wwwroot/css/ and link it once — AFTER glass.css and the locale
   selector's sheet, since it restyles the selector's trigger for the header. */

/* ===========================================================================
   App shell — liquid glass
   Requires tokens.css. The header, nav rail, drawer and connection banner live
   here (shared by MainLayout and its children — the scope attribute does not
   cross component boundaries). The user-menu styles were split out to the scoped
   AccountMenu.razor.css, since every one of those classes is rendered by
   AccountMenu alone.
   =========================================================================== */

.app {
  --header-h: 60px;
  /* Chrome recedes, content floats. The rail is the darkest surface in the app:
     darker than the header, clearly darker than the scene behind the content.
     At 50% tint it read as "the background, slightly dimmed", which is exactly
     the thing a navigation edge should not read as. */
  --header-bg: rgba(8, 14, 30, .78);
  --rail-bg:   rgba(5, 10, 24, .90);
  --rail-w: 248px;
  --rail-w-collapsed: 68px;
  min-height: 100vh;
}

/* ---- header ----------------------------------------------------------------
   THE IMPORTANT BIT: the blur lives on a pseudo-element, not on .app-header.

   `backdrop-filter` on an element makes it a containing block for every
   position:fixed descendant. Put it on the header and the locale selector's
   mobile bottom sheet — and any dialog opened from the user menu — anchors to
   the header instead of the viewport. Moving the filter to ::before keeps the
   glass and leaves the header a normal ancestor.
   ---------------------------------------------------------------------------- */
.app-header {
  position: sticky; top: 0; z-index: 40;
  height: var(--header-h);
  display: flex; align-items: center; gap: 12px;
  padding: 0 16px;
  border-bottom: 1px solid var(--glass-rim);
}
.app-header::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: var(--header-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-gloss);
}

.app-burger {
  display: none; width: 38px; height: 38px; flex: none;
  align-items: center; justify-content: center; cursor: pointer;
  border: 1px solid var(--glass-rim); border-radius: var(--r-sm);
  background: var(--glass-fill); color: var(--ink-dim);
}
.app-burger:hover { background: var(--glass-fill-2); color: var(--ink); }
.app-burger svg { width: 17px; height: 17px; }

/* Brand block. Live text rather than the lockup SVG: it stays crisp at any zoom,
   inherits the theme, and lets the slogan drop out on narrow screens without
   swapping assets. The proportions match logo-lockup-header.svg — mark 34,
   wordmark 17, slogan 9.5 — so the header and the exported asset agree. */
.app-brand {
  display: inline-flex; align-items: center; gap: 10px;
  text-decoration: none; color: var(--ink); min-width: 0;
}
.app-brand svg { width: 34px; height: 34px; flex: none; }
.app-brand .txt { display: grid; gap: 1px; min-width: 0; }
.app-brand .wm {
  font-size: 17px; font-weight: 700; letter-spacing: -.03em; line-height: 1.05;
  white-space: nowrap;
}
.app-brand .wm i { font-style: normal; color: var(--primary-line); }
.app-brand .slogan {
  font-size: 9.5px; font-weight: 500; letter-spacing: .04em; line-height: 1.1;
  color: var(--ink-mute); white-space: nowrap;
}

/* Below this the slogan competes with the controls on the right, and a header
   that wraps is worse than one without a tagline. The mark and wordmark stay. */
@media (max-width: 720px) {
  .app-brand .slogan { display: none; }
  .app-brand svg { width: 28px; height: 28px; }
  .app-brand .wm { font-size: 17px; }
}

.app-spacer { flex: 1 1 auto; }
.app-headright { display: flex; align-items: center; gap: 8px; }
.app-divider { width: 1px; height: 24px; background: var(--glass-rim); flex: none; margin: 0 2px; }

/* header icon button */
.app-iconbtn {
  width: 38px; height: 38px; flex: none; display: grid; place-items: center; cursor: pointer;
  border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; color: var(--ink-dim);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.app-iconbtn:hover { background: var(--glass-fill-2); color: var(--ink); }
.app-iconbtn svg { width: 17px; height: 17px; }

/* The locale selector sits in the header in Independent mode — region AND
   language, because they answer different questions. Language decides what the UI
   is written in; country decides date order, decimal separator, units and paper
   size. A Dutch site running an English UI still wants 07-08-2026 and 1.234,56.
   The trigger takes the header's button shape rather than its own bordered pill. */
.app-locale-embed .cls__trigger {
  min-height: 38px; padding: 0 10px; gap: 7px;
  background: transparent; border-color: transparent; box-shadow: none;
  color: var(--ink-dim); font-size: 13px;
}
.app-locale-embed .cls__trigger:hover { background: var(--glass-fill-2); color: var(--ink); }
.app-locale-embed .cls__triggerlabel {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: .06em;
}
/* the flag replaces the globe once a country is committed */
.app-locale-embed .cls__flag--round { width: 19px; height: 19px; }
/* Desktop only: the panel hangs off the right edge, or it runs off screen from a
   trigger this far right. Below 560px the selector becomes a full-width bottom
   sheet and must keep `inset: auto 0 0 0` — scoping this stopped it collapsing
   to a 235px stub anchored to the right. */
@media (min-width: 561px) {
  .app-locale-embed .cls__panel { left: auto; right: 0; }
}

/* ---- body ------------------------------------------------------------------- */
.app-body { display: flex; align-items: flex-start; min-height: calc(100vh - var(--header-h)); }

/* ---- rail --------------------------------------------------------------------
   Same pseudo-element trick, same reason. */
.app-rail {
  position: sticky; top: var(--header-h); flex: none;
  width: var(--rail-w); height: calc(100vh - var(--header-h));
  display: flex; flex-direction: column; gap: 4px;
  padding: 14px 10px;
  border-right: 1px solid rgba(0, 0, 0, .5);
  transition: width var(--t-base) var(--ease);
  z-index: 30;
}
.app-rail::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(255,255,255,.022), transparent 180px),
    var(--rail-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  /* a lit inner edge on the right so the boundary is a seam, not a fade */
  box-shadow: inset -1px 0 0 rgba(255,255,255,.045);
}
.app.is-collapsed .app-rail { width: var(--rail-w-collapsed); }

.app-navsec {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-mute); padding: 12px 12px 5px;
  white-space: nowrap; overflow: hidden;
}
.app.is-collapsed .app-navsec { opacity: 0; height: 12px; padding-top: 6px; padding-bottom: 0; }

.app-nav {
  display: flex; align-items: center; gap: 11px;
  min-height: 40px; padding: 0 12px; border-radius: var(--r-sm);
  color: var(--ink-dim); font-size: 13.5px; font-weight: 500;
  text-decoration: none; white-space: nowrap; overflow: hidden;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.app-nav:hover { background: var(--glass-fill-2); color: var(--ink); }
.app-nav svg { width: 17px; height: 17px; flex: none; }
.app-nav .txt { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.app.is-collapsed .app-nav .txt,
.app.is-collapsed .app-nav .badge { opacity: 0; pointer-events: none; }

/* Active is a quiet tint plus a lit edge — the same restraint as the call rail,
   because a full warm slab on every screen makes the whole app read warm. */
.app-nav.active {
  color: var(--ink);
  background: color-mix(in srgb, var(--primary-1) 20%, var(--glass-fill-2));
  box-shadow: inset 2px 0 0 var(--primary-line), inset 0 1px 0 rgba(255,255,255,.14);
}

/* Live counts are the app's whole point, so they get the system's cyan and sit
   in the nav rather than in a notification centre nobody opens. */
.app-nav .badge {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
  min-width: 20px; height: 19px; padding: 0 6px; border-radius: 10px;
  display: inline-grid; place-items: center; flex: none;
  color: var(--info-line); background: color-mix(in srgb, var(--info-line) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--info-line) 34%, transparent);
}
.app-nav .badge--live { color: #fff; background: var(--live); border-color: transparent; }

.app-railfoot { margin-top: auto; display: grid; gap: 4px; }
.app-collapse {
  display: flex; align-items: center; gap: 11px; min-height: 36px; padding: 0 12px;
  border: 0; background: transparent; border-radius: var(--r-sm); cursor: pointer;
  color: var(--ink-mute); font-family: var(--font-ui); font-size: 12.5px; white-space: nowrap;
}
.app-collapse:hover { background: var(--glass-fill-2); color: var(--ink); }
.app-collapse svg { width: 16px; height: 16px; flex: none; transition: transform var(--t-base) var(--ease); }
.app.is-collapsed .app-collapse svg { transform: rotate(180deg); }
/* display:none, not opacity:0 — a transparent label keeps its layout box, so the nowrap text (which a
   grid item won't shrink below) pushed the button wider than the collapsed rail and stayed hoverable
   outside the bar. Removing it from layout shrinks the button to just the chevron. */
.app.is-collapsed .app-collapse span { display: none; }

/* ---- content ------------------------------------------------------------------ */
.app-main { flex: 1 1 auto; min-width: 0; padding: 26px 28px 64px; }
.app-pagehead { margin-bottom: var(--sp-6); }
.app-pagehead h1 { font-size: var(--fs-h1); letter-spacing: -.025em; margin: 0 0 6px; font-weight: 700; }
.app-pagehead p { margin: 0; color: var(--ink-dim); font-size: var(--fs-sm); }

/* Routes that own the whole screen — the call, mostly — drop the chrome.
   The main area also has to be *given* a height: without this the call frame
   collapses to its min-height and floats in the middle of an empty page. */
.app.is-immersive .app-rail { display: none; }
.app.is-immersive .app-main {
  padding: 0;
  height: calc(100vh - var(--header-h));
  min-height: 0;
}
.app.is-immersive .app-body { min-height: 0; }
.app-callhost { height: 100%; }

/* ---- skip link ------------------------------------------------------------------ */
.app-skip {
  position: absolute; left: -9999px; top: 8px; z-index: 100;
  padding: 10px 14px; border-radius: var(--r-sm); font-size: 13px; font-weight: 600;
  background: var(--primary-line); color: #231005;
}
.app-skip:focus { left: 8px; }

/* ---- drawer on phones -------------------------------------------------------------
   The rail becomes an overlay rather than squeezing the content to nothing. */
.app-scrim { display: none; }
@media (max-width: 900px) {
  .app-burger { display: inline-flex; }
  .app-rail {
    position: fixed; top: var(--header-h); left: 0; bottom: 0; width: var(--rail-w);
    transform: translateX(-100%); transition: transform var(--t-slow) var(--ease);
    border-right: 1px solid var(--glass-rim-2);
  }
  .app-rail::before { background: rgba(5, 10, 24, .97); }
  .app.is-drawer .app-rail { transform: none; }
  .app.is-collapsed .app-rail { width: var(--rail-w); }
  .app.is-collapsed .app-nav .txt, .app.is-collapsed .app-nav .badge { opacity: 1; }
  .app-collapse { display: none; }
  .app-scrim {
    display: block; position: fixed; inset: var(--header-h) 0 0 0; z-index: 29;
    background: rgba(3, 7, 16, .55); opacity: 0; pointer-events: none;
    transition: opacity var(--t-base) var(--ease);
  }
  .app.is-drawer .app-scrim { opacity: 1; pointer-events: auto; }
  .app-main { padding: 20px 16px 56px; }
}

@media (prefers-reduced-motion: reduce) {
  .app-rail, .app-scrim { transition: none; }
}

/* ---- banner ----------------------------------------------------------------
   The ring is enough for a blip. A real outage means missed call requests, so
   after the disruption outlives a few seconds it gets a bar that cannot be
   missed. Held back deliberately: showing it instantly makes every 400ms
   network hiccup flash a scary banner. */
.app-connbar {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 16px; font-size: 12.5px; font-weight: 550;
  border-bottom: 1px solid var(--glass-rim);
  background: color-mix(in srgb, var(--danger-line) 16%, rgba(8,14,30,.9));
  color: var(--ink);
}
.app-connbar--warn { background: color-mix(in srgb, var(--warning-line) 15%, rgba(8,14,30,.9)); }
.app-connbar i { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--danger-line); }
.app-connbar--warn i { background: var(--warning-line); }
.app-connbar .grow { flex: 1; min-width: 0; }
.app-connbar .since { font-family: var(--font-mono); font-size: 11px; color: var(--ink-dim); }
.app-connbar button {
  border: 1px solid var(--glass-rim-2); background: var(--glass-fill-2); color: var(--ink);
  font-family: var(--font-ui); font-size: 12px; font-weight: 600;
  padding: 6px 12px; border-radius: var(--r-sm); cursor: pointer;
}
.app-connbar button:hover { background: rgba(255,255,255,.16); }

/* preview only: shows which simulated state is active */
.conn-btn.is-current {
  color: #fff;
  background: linear-gradient(180deg, var(--primary-1), var(--primary-2));
  border-color: transparent;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.3);
}
