/* 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. Splits into MainLayout.razor.css, NavMenu.razor.css and
   UserMenu.razor.css; kept in one file here so the preview can use it whole.
   =========================================================================== */

.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); }
.app.is-collapsed .app-collapse span { opacity: 0; }

/* ---- 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%; }

/* ---- user menu ------------------------------------------------------------------ */
.app-user { position: relative; }
.app-userbtn {
  display: flex; align-items: center; gap: 10px; height: 44px; padding: 0 10px 0 6px;
  border: 1px solid transparent; border-radius: var(--r-md); cursor: pointer;
  background: transparent; color: var(--ink);
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.app-userbtn:hover { background: var(--glass-fill-2); border-color: var(--glass-rim); }
.app-user.is-open .app-userbtn { background: var(--glass-fill-2); border-color: var(--glass-rim-2); }
.app-userbtn .who { display: grid; gap: 1px; text-align: left; min-width: 0; }
.app-userbtn .nm { font-size: 13px; font-weight: 600; line-height: 1.2; white-space: nowrap; }
/* the tenant is the thing people get wrong in a multi-tenant app, so it is always
   on screen rather than one click away */
.app-userbtn .tn {
  font-size: 11px; color: var(--ink-mute); line-height: 1.2; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 150px;
}
.app-userbtn .caret { color: var(--ink-mute); flex: none; }
.app-userbtn .caret svg { width: 13px; height: 13px; }

.app-avatar { position: relative; width: 32px; height: 32px; flex: none; }
.app-avatar img, .app-avatar .ini {
  width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: grid; place-items: center;
  font-size: 12px; font-weight: 600; color: #fff;
  box-shadow: 0 0 0 1px var(--glass-rim-2), inset 0 0 0 1px rgba(0,0,0,.2);
}
.app-avatar .dot {
  position: absolute; right: -1px; bottom: -1px; width: 11px; height: 11px; border-radius: 50%;
  box-shadow: 0 0 0 2.5px #0B1426;
  /* Sit above the connection ring (::after), which otherwise paints over the dot. */
  z-index: 2;
}
.dot--available { background: var(--success-line); }
.dot--oncall { background: var(--info-line); }
.dot--away { background: var(--warning-line); }
.dot--dnd { background: var(--danger-line); }
.dot--busy { background: var(--busy-line); }
.dot--offline { background: var(--ink-mute); }

.app-usermenu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 50; width: 288px;
  /* Base ink for the menu — the name (.nm) relies on it; without it the username
     inherits whatever the header resolves to and can vanish on the dark panel. */
  color: var(--ink);
  background: rgba(9, 18, 38, .96); border: 1px solid var(--glass-rim-2);
  border-radius: var(--r-lg); overflow: hidden;
  -webkit-backdrop-filter: var(--glass-blur-lg); backdrop-filter: var(--glass-blur-lg);
  box-shadow: var(--glass-gloss), var(--glass-drop-lg);
  opacity: 0; transform: translateY(-6px); pointer-events: none;
  transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.app-user.is-open .app-usermenu { opacity: 1; transform: none; pointer-events: auto; }

.app-umhead { display: flex; align-items: center; gap: 11px; padding: 14px 14px 12px; }
.app-umhead .app-avatar { width: 42px; height: 42px; }
.app-umhead .app-avatar .ini { font-size: 15px; }
.app-umhead .who { display: grid; gap: 2px; min-width: 0; }
.app-umhead .nm { font-size: 14px; font-weight: 600; }
.app-umhead .em { font-size: 11.5px; color: var(--ink-mute); overflow: hidden; text-overflow: ellipsis; }

/* Presence sits at the top because in this app it is the most-changed setting —
   people set themselves to Do not disturb far more often than they edit a profile. */
.app-umpresence { padding: 0 10px 10px; display: grid; gap: 2px; }
.app-umlabel {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-mute); padding: 4px 4px 5px;
}
.app-presopt {
  display: flex; align-items: center; gap: 9px; min-height: 34px; padding: 0 8px;
  border: 0; width: 100%; text-align: left; cursor: pointer; border-radius: var(--r-sm);
  background: transparent; color: var(--ink-dim); font-family: var(--font-ui); font-size: 13px;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.app-presopt:hover { background: var(--glass-fill-2); color: var(--ink); }
.app-presopt i { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.app-presopt .tick { margin-left: auto; color: var(--primary-line); opacity: 0; }
.app-presopt .tick svg { width: 14px; height: 14px; }
.app-presopt.is-on { color: var(--ink); background: color-mix(in srgb, var(--primary-1) 16%, transparent); }
.app-presopt.is-on .tick { opacity: 1; }
.app-presopt .auto { font-family: var(--font-mono); font-size: 10px; color: var(--ink-mute); margin-left: auto; }

/* "Do not disturb · resets end of today" — a subtle line under the switcher when the status is time-boxed. */
.app-umhint { font-family: var(--font-mono); font-size: 10px; color: var(--ink-mute); padding: 3px 8px 1px; }

.app-umsep { height: 1px; background: var(--glass-rim); margin: 2px 0; }

/* tenant row — only meaningful when the account has more than one */
.app-tenant {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px;
  padding: 0 14px; border: 0; cursor: pointer; text-align: left;
  background: transparent; color: var(--ink); font-family: var(--font-ui);
  transition: background var(--t-fast) var(--ease);
}
.app-tenant:hover { background: var(--glass-fill-2); }
.app-tenant .mark {
  width: 26px; height: 26px; border-radius: var(--r-xs); flex: none; display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 11px; font-weight: 700; color: #231005;
  background: linear-gradient(150deg, var(--primary-1), var(--primary-2));
}
.app-tenant .tt { display: grid; gap: 1px; min-width: 0; flex: 1; }
.app-tenant .t1 { font-size: 13px; font-weight: 550; }
.app-tenant .t2 { font-size: 10.5px; color: var(--ink-mute); }
.app-tenant .sw { color: var(--ink-mute); flex: none; }
.app-tenant .sw svg { width: 17px; height: 17px; }

.app-umitem {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 38px;
  padding: 0 14px; border: 0; cursor: pointer; text-align: left; text-decoration: none;
  background: transparent; color: var(--ink-dim); font-family: var(--font-ui); font-size: 13px;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.app-umitem:hover { background: var(--glass-fill-2); color: var(--ink); }
.app-umitem svg { width: 15px; height: 15px; flex: none; }
.app-umitem--danger { color: var(--danger-line); }
.app-umitem--danger:hover { background: color-mix(in srgb, var(--danger-line) 14%, transparent); color: var(--danger-line); }
.app-umfoot { padding: 8px 14px 10px; }
.app-umfoot .v { font-family: var(--font-mono); font-size: 10px; color: var(--ink-mute); }

/* ---- 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; }
  .app-userbtn .who { display: none; }
  .app-userbtn { padding: 0 6px; }
}

@media (prefers-reduced-motion: reduce) {
  .app-rail, .app-usermenu, .app-scrim { transition: none; }
}

/* ===========================================================================
   Connection state (Azure SignalR)

   A solid ring around the avatar that changes colour. No spinners, no pulses:
   this sits next to a live video feed, and continuous animation on a screen that
   is already compositing blurred glass over decoding video costs repaints for no
   information — the colour carries all of it.

   The 200ms colour fade is there so a change registers as a change rather than a
   repaint artefact. Drop the transition line if you want it truly instant.

   Colour note: disconnected uses --danger-line, NOT --live. The on-air red is
   reserved for recording, and two different reds both meaning "bad" is worse
   than one.
   =========================================================================== */

.app-avatar::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid transparent; pointer-events: none;
  transition: border-color 200ms var(--ease);
}

.conn-connected    .app-avatar::after { border-color: color-mix(in srgb, var(--success-line) 80%, transparent); }
.conn-connecting   .app-avatar::after { border-color: color-mix(in srgb, var(--ink-dim) 80%, transparent); }
.conn-reconnecting .app-avatar::after { border-color: var(--warning-line); }
.conn-disconnected .app-avatar::after { border-color: var(--danger-line); }

/* Connecting is NEUTRAL SLATE, not cyan. --success-line (#43DCA6) and --info-line
   (#4FD8E8) are adjacent hues; side by side on a 2px ring they are the same colour
   to anyone not comparing swatches. Slate is also the more honest reading: on
   first connect there is no verdict yet, and grey is what "don't know" looks like.

   Four distinct hues now: green / slate / amber / red. Connected sits at 80% so it
   stays calmer than the two states that need attention, which are full strength.

   Worth knowing more broadly: green-vs-cyan is close everywhere in this palette.
   The presence dots lean on it too (Available green, On a call cyan) — those get
   away with it because a word sits next to them. A bare ring has no word, so it
   cannot.  */

/* opt out and healthy goes silent again */
.conn-quiet.conn-connected .app-avatar::after { border-color: transparent; }

/* While the transport is down, presence is stale: the dot may say Available but
   nobody can reach this person. Grey it rather than leave it confidently wrong. */
.conn-disconnected .app-avatar .dot,
.conn-reconnecting .app-avatar .dot { background: var(--ink-mute); }

@media (prefers-reduced-motion: reduce) {
  .app-avatar::after { transition: none; }
}

/* ---- the row in the user menu ---------------------------------------------
   Always present, so "is it connected?" is answerable without waiting for
   something to go wrong. */
.app-conn {
  display: flex; align-items: center; gap: 9px; padding: 9px 14px;
  font-size: 12.5px; color: var(--ink-dim);
}
.app-conn i { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.app-conn .lbl { flex: 1; min-width: 0; }
.app-conn .ms { font-family: var(--font-mono); font-size: 10.5px; color: var(--ink-mute); }
.app-conn--connected i { background: var(--success-line); }
.app-conn--connecting i { background: var(--info-line); }
.app-conn--reconnecting i { background: var(--warning-line); }
.app-conn--disconnected i { background: var(--danger-line); }
.app-conn--disconnected { color: var(--danger-line); }
.app-conn button {
  border: 1px solid var(--glass-rim-2); background: var(--glass-fill-2); color: var(--ink);
  font-family: var(--font-ui); font-size: 11.5px; font-weight: 600;
  padding: 5px 10px; border-radius: var(--r-sm); cursor: pointer;
}
.app-conn button:hover { background: rgba(255,255,255,.14); }

/* ---- 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);
}
