/* ── Theme tokens (#581 white-label; #590 token overhaul) ─────────────────────
   ONE semantic token set drives every surface, line, text and accent colour.
   --brand* derive from the TENANT's primary colour (ThemeState.BrandStyleCss
   renders :root + .theme-dark overrides after this sheet); light/dark flips via
   .theme-dark on the app shell wrapper. RULES: chrome + kit + component classes
   use tokens ONLY — no raw surface/text/line hex anywhere below (the splash is
   the one exception: it paints before the tenant brand loads). Pages may keep
   Tailwind slate/semantic utilities — the bridge at the end of this block
   re-skins the ones in real use (see the census in #590). */
:root {
  --brand: #4f46e5;              /* tenant primary (overridden per tenant) */
  --brand-strong: #4338ca;       /* hover/pressed */
  --brand-soft: #eef2ff;         /* tinted fills (active nav, chips)       */
  --brand-line: #c7d2fe;         /* tinted borders (grid toolbars)         */
  --brand-text: #4f46e5;         /* brand-coloured TEXT (links, accents)   */
  --brand-ring: rgba(79, 70, 229, .14);
  --on-brand: #ffffff;           /* text on brand-filled surfaces          */
  --surface: #ffffff;            /* cards, popovers, table rows            */
  --surface-muted: #f8fafc;      /* inset panels, inputs, user card        */
  --surface-page: #f1f5f9;       /* the page behind everything             */
  --line: #e2e8f0;               /* standard border                        */
  --line-soft: #f1f5f9;          /* hairlines + hover fills                */
  --text: #1e293b;
  --text-muted: #64748b;
  --text-faint: #94a3b8;
  --ok: #059669;      --ok-soft: #ecfdf5;
  --ok-strong: #047857;            /* badge text on --ok-soft: --ok is only ~3.6:1 there (axe #330b) */
  --warn: #b45309;    --warn-soft: #fffbeb;
  --extra: #0f766e;   --extra-soft: #ccfbf1;     /* the residence calendar's EXTRA day — its own hue, never confused with a day over */
  --danger: #dc2626;  --danger-soft: #fef2f2;
  --info: #1d4ed8;    --info-soft: #eff6ff;
  --accent: #7c3aed;  --accent-soft: #ede9fe;
}
/* DARK = NEAR-BLACK + VIVID (2026-08-24 revamp, his brief: "get rid of that grayish-blueish style,
   near black background, vivid colours, better contrast"). The old ramp was Tailwind SLATE — every
   surface carried a blue cast (#0f172a page, #1e293b card), so the whole app read as washed navy and
   the accents, being pastel 300/400-weight tints, had nothing to sit against. Two changes, no new
   tokens:
     1. The neutral ramp is now NEUTRAL — a zinc-flavoured near-black with no measurable hue. The page
        drops to #09090b (near black) and the card rises to #17171b, so cards read as LIT rather than
        as a lighter shade of the same blue. The step page→card is bigger than the old one, which is
        what makes a card edge legible without a bright border.
     2. Semantics are VIVID, not pastel — full-chroma green/amber/red/blue/violet, each solved to
        clear 4.5:1 as TEXT on --surface (ratios in the comments). On a near-black ground saturation
        costs nothing: the old #93c5fd-style tints were desaturated to survive a mid-navy card.
   The soft fills are DEEP TINTS, not washes (deepened 2026-08-24 on his "vivid enough?"). An alpha
   wash of a bright hue gains colour only by getting LIGHTER, which is what eats the text sitting on
   it — at .22 the danger chip fell to 4.19:1. Deepening the HUE instead buys chroma at the same
   lightness: every fill carries 1.7–2.0x the chroma of the .16 wash it replaces, its own semantic
   still clears AA on it (6.3 ok / 6.4 warn / 4.6 danger / 4.6 info / 4.7 accent), and the fill now
   reads off the card at 1.22–1.54 rather than 1.05–1.38. Opaque, like the light theme's own tints. */
.theme-dark {
  --brand-soft: #211f4b;         /* deep brand tint (re-derived per tenant brand) */
  --brand-line: #2d2973;
  --brand-text: #938eef;         /* vivid on dark surfaces, 6.3:1 (re-derived per tenant) */
  --brand-ring: rgba(147, 142, 239, .32);
  --surface: #17171b;            /* cards, popovers, table rows — the LIT plane */
  --surface-muted: #121216;      /* inset panels, inputs, zebra                  */
  --surface-page: #09090b;       /* near black — the page behind everything      */
  --line: #2c2c33;               /* standard border                              */
  --line-soft: #212127;          /* hairlines + hover fills                      */
  --text: #f4f4f5;               /* 16.4:1 on --surface                          */
  --text-muted: #a1a1aa;         /* 7.0:1                                        */
  --text-faint: #8b8b95;         /* 5.3:1 — was 3.2:1 and used for real text     */
  --ok: #2bd97c;      --ok-soft: #0f4026;
  --ok-strong: #2bd97c;            /* 9.5:1 — light-on-dark clears 4.5:1 unaided */
  --warn: #ffb020;    --warn-soft: #4b350c;      /* 9.6:1 on the card, 6.4 on its own fill */
  --extra: #2dd4bf;   --extra-soft: #113c38;     /* teal — reads apart from both the blue and the red */
  --danger: #ff5257;  --danger-soft: #49181a;    /* 5.6:1 / 4.6 */
  --info: #4d9dff;    --info-soft: #1a3353;      /* 6.4:1 / 4.6 */
  --accent: #a78bff;  --accent-soft: #362e53;    /* 6.6:1 / 4.7 */
}

/* ── Dark-theme bridge ────────────────────────────────────────────────────────
   Pages keep their Tailwind colour utilities; in dark the shell class re-skins
   every utility the pages actually use (grep census, #590) onto the tokens.
   Semantic (red/emerald/amber/indigo) text lightens so it stays readable. */
.theme-dark { background: var(--surface-page); color: var(--text); color-scheme: dark; }
.theme-dark .bg-white { background-color: var(--surface) !important; }
.theme-dark .bg-slate-50, .theme-dark .bg-slate-100 { background-color: var(--surface-muted) !important; }
.theme-dark .bg-slate-200 { background-color: var(--line) !important; }
.theme-dark .hover\:bg-slate-50:hover, .theme-dark .hover\:bg-slate-100:hover,
.theme-dark .hover\:bg-white:hover { background-color: var(--line-soft) !important; }
.theme-dark .text-slate-800, .theme-dark .text-slate-900, .theme-dark .text-slate-700 { color: var(--text) !important; }
.theme-dark .text-slate-600, .theme-dark .text-slate-500 { color: var(--text-muted) !important; }
.theme-dark .text-slate-400, .theme-dark .text-slate-300, .theme-dark .text-slate-200 { color: var(--text-faint) !important; }
.theme-dark .hover\:text-slate-700:hover, .theme-dark .hover\:text-slate-600:hover { color: var(--text) !important; }
.theme-dark .border-slate-100, .theme-dark .border-slate-200, .theme-dark .border-slate-300 { border-color: var(--line) !important; }
.theme-dark .hover\:border-slate-300:hover { border-color: var(--text-faint) !important; }
.theme-dark .divide-slate-100 > * + *, .theme-dark .divide-slate-200 > * + * { border-color: var(--line) !important; }
/* On near-black a black shadow is invisible, so elevation in dark is carried by a HAIRLINE RING —
   the same trick the reference dashboards use: the card edge, not the drop, says "raised". */
.theme-dark .shadow-sm { box-shadow: 0 1px 2px rgba(0, 0, 0, .6), 0 0 0 1px rgba(255, 255, 255, .045); }
.theme-dark .shadow-lg { box-shadow: 0 16px 40px rgba(0, 0, 0, .75), 0 0 0 1px rgba(255, 255, 255, .07); }
/* Brand-coloured text/fills follow the tenant colour in BOTH themes. */
.text-indigo-600, .text-indigo-700, .hover\:text-indigo-600:hover, .hover\:text-indigo-700:hover { color: var(--brand-text) !important; }
.bg-indigo-50 { background-color: var(--brand-soft) !important; }
.bg-indigo-500, .bg-indigo-600, .hover\:bg-indigo-600:hover { background-color: var(--brand) !important; }
.hover\:bg-indigo-50:hover { background-color: var(--brand-soft) !important; }
.hover\:border-indigo-300:hover, .border-indigo-300 { border-color: var(--brand-line) !important; }
/* Semantic text stays readable on dark; soft chips flip to deep tints. */
.theme-dark .text-red-600, .theme-dark .text-red-700, .theme-dark .hover\:text-red-500:hover { color: var(--danger) !important; }
.theme-dark .text-emerald-600, .theme-dark .text-emerald-700 { color: var(--ok) !important; }
.theme-dark .text-amber-600, .theme-dark .text-amber-700, .theme-dark .hover\:text-amber-700:hover { color: var(--warn) !important; }
.theme-dark .bg-red-50, .theme-dark .hover\:bg-red-50:hover { background-color: var(--danger-soft) !important; }
.theme-dark .border-red-100, .theme-dark .border-red-200 { border-color: rgba(248, 113, 113, .35) !important; }
.theme-dark .bg-emerald-50 { background-color: var(--ok-soft) !important; }
.theme-dark .bg-amber-50 { background-color: var(--warn-soft) !important; }
.theme-dark .bg-indigo-50\/40 { background-color: var(--brand-soft) !important; }
/* Skeletons + zebra stripes need their own dark ramp (gradients can't token-swap). */
.theme-dark .gx-skel { background: linear-gradient(90deg, #1b1b21 25%, #26262e 50%, #1b1b21 75%); background-size: 200% 100%; }
.theme-dark .kpi-skel-line { background: linear-gradient(90deg, #1b1b21 25%, #26262e 50%, #1b1b21 75%); background-size: 200% 100%; }.theme-dark aside { scrollbar-color: rgba(255, 255, 255, .12) transparent; }
/* Tailwind preflight defaults border-color to gray-200 on EVERY element — flip it with the theme so a bare
   `border-b` never paints a bright hairline on dark. Specific border-colour utilities still override. */
.theme-dark *, .theme-dark ::before, .theme-dark ::after { border-color: var(--line); }
/* Native controls follow the brand in both themes. */
input[type="checkbox"], input[type="radio"], input[type="range"], progress { accent-color: var(--brand); }

/* ── Scrollbars (his rulings, 2026-08-18 — both of them): the FAT NATIVE bar appears nowhere, and
   INVISIBLE is just as wrong ("all scrollbars are invisible now. That's not what I've asked").
   ONE treatment everywhere instead: the nav/grid thin bar — `scrollbar-width: thin` + a tokenized
   thumb, universal. A bar only renders where content actually overflows, so quiet panels stay
   clean; where one appears, it is always this one. Setting the standard property also makes
   Chromium ignore any legacy `::-webkit-scrollbar` styling, so nothing re-fattens. Never override
   this with `none` (an invisible scroller) or a custom webkit bar on a new container. */
* { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }

/* ═══════════════════════════════════════════════════════════════════
   Tera Lodge — Custom component styles
   Layout classes supplement Tailwind utility classes.
   Data Grid styles live in the second half of this file.
═══════════════════════════════════════════════════════════════════ */

/* ============================================================
   Loading splash — full-screen overlay shown during the auth
   check and workspace load (LoadingSplash.razor). Restored here
   after the BFF/render-mode conversion (ADR-0024) replaced
   index.html, where these styles used to live. Fixes bug #46.
   ============================================================ */
.splash {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: radial-gradient(120% 120% at 50% 0%, #6366f1 0%, #4f46e5 45%, #4338ca 100%);
    color: #fff;
    /* NO entrance fade here: .splash mounts up to 3× (boot copy → <Authorizing> → workspace load),
       and replaying splash-in on each mount caused the ~1s blink (#47). The fade lives on the
       #app-splash boot wrapper only; every live instance renders already-opaque, so the handoff is
       a straight swap with nothing to flash. */
}
.splash__panel { display: flex; flex-direction: column; align-items: center; text-align: center; max-width: 340px; }
.splash__logo {
    width: 64px; height: 64px; margin-bottom: 20px;
    filter: drop-shadow(0 8px 20px rgba(15, 23, 42, .35));
    animation: splash-float 2.8s ease-in-out infinite;
}
.splash__logo svg { width: 100%; height: 100%; display: block; }
.splash__wordmark { font-size: 28px; font-weight: 800; letter-spacing: -.02em; line-height: 1; }
.splash__wordmark b { font-weight: 800; }
.splash__product { font-weight: 600; opacity: .8; margin-left: 4px; }
.splash__tagline { margin-top: 8px; font-size: 13px; font-weight: 500; color: rgba(255, 255, 255, .8); }
.splash__bar {
    position: relative; width: 180px; height: 4px; margin-top: 28px;
    border-radius: 9999px; background: rgba(255, 255, 255, .22); overflow: hidden;
}
.splash__bar::after {
    content: ""; position: absolute; top: 0; left: 0; height: 100%; width: 40%;
    border-radius: 9999px; background: #fff; animation: splash-slide 1.15s ease-in-out infinite;
}
.splash__hint { margin-top: 14px; font-size: 12.5px; color: rgba(255, 255, 255, .7); }
@keyframes splash-slide { 0% { transform: translateX(-110%); } 100% { transform: translateX(360%); } }
@keyframes splash-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
@keyframes splash-in { from { opacity: 0; } to { opacity: 1; } }
/* Boot-splash wrapper (App.razor): fade IN on first paint (the only entrance fade in play — see
   .splash above) and fade OUT when LoadingSplash removes it (app.js). The live .splash renders opaque
   underneath, so the wrapper fading out reveals an identical screen — a seamless handoff, no dip. */
#app-splash { animation: splash-in .25s ease-out; transition: opacity .25s ease; }
#app-splash.is-hiding { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
    .splash__logo, .splash__bar::after { animation: none; }
    #app-splash { animation: none; transition: none; }
}

/* ── Sidebar navigation — exact values from mocks/app.css ───────── */

/* Hide native disclosure triangle on all summary elements */
details > summary { list-style: none; cursor: pointer; user-select: none; }
details > summary::-webkit-details-marker,
details > summary::marker { display: none; }

/* Chevron rotation */
.chevron { transition: transform 0.2s cubic-bezier(0.4,0,0.2,1); }
details[open] > summary .chevron { transform: rotate(180deg); }

/* Submenu entrance animation */
@keyframes slideDown {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Sidebar scrollbar */
aside { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.08) transparent; }
aside::-webkit-scrollbar { width: 3px; }
aside::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.08); border-radius: 2px; }

/* ═══ THE NAV (2026-08-24 rebuild) — icon doors under quiet section labels ═══
   Anatomy: every DOOR is its own icon + label on a 34px row; the ACTIVE door wears the grid's
   selection idiom (soft brand fill + a 3px brand bar down its leading edge). Groups are uppercase
   MICRO-LABELS with the group's accent as a small dot — open by default, collapsible at the label.
   The old indent guide-line and roundel accordion are retired. Tokens only (white-label + dark). */

.nav-body { flex: 1; overflow-y: auto; padding: 0 14px 18px; }

/* The search dock — sticky, with real air under the logo row; a QUIET muted well, not a bordered
   twin of the topbar's global search: the border only arrives with the hand (hover) or the caret
   (focus). Background var(--surface) because the sidebar is .bg-white, which .theme-dark remaps to
   exactly that token — one rule, right in both themes. */
.nav-search-dock { position: sticky; top: 0; z-index: 2; background: var(--surface); padding: 14px 0 10px; }
/* Rows scrolling under the dock fade out instead of guillotining against it. */
.nav-search-dock::after { content: ""; position: absolute; left: 0; right: 0; bottom: -10px; height: 10px;
    background: linear-gradient(to bottom, var(--surface), transparent); pointer-events: none; }
.nav-search-wrap { display: flex; align-items: center; gap: 9px; height: 36px; padding: 0 11px;
    border-radius: 10px; background: var(--surface-muted); border: 1px solid transparent;
    transition: border-color .15s, box-shadow .15s, background .15s; }
.nav-search-wrap:hover { border-color: var(--line); }
.nav-search-wrap:focus-within { background: var(--surface); border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-ring); }
.nav-search-wrap > svg { width: 15px; height: 15px; color: var(--text-faint); flex-shrink: 0; }
.nav-search { flex: 1; min-width: 0; border: none; background: transparent; outline: none;
    font-size: 13px; font-weight: 500; color: var(--text); }
.nav-search::placeholder { color: var(--text-faint); font-weight: 500; }
/* The native clear affordance sits at a different spot in every browser; ours is a real button. */
.nav-search::-webkit-search-cancel-button { display: none; }
.nav-clear { display: inline-flex; border: none; background: transparent; padding: 3px; border-radius: 6px;
    color: var(--text-faint); cursor: pointer; flex-shrink: 0; transition: color .12s, background .12s; }
.nav-clear:hover { color: var(--text); background: var(--line-soft); }
.nav-clear svg { width: 13px; height: 13px; }

/* Section labels — uppercase micro-type with the group's accent dot (the colour still tells one
   section from another); the whole label toggles its section. The chevron is faint furniture until
   the pointer is on the label. */
.nav-label { display: flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px;
    margin-top: 10px; border-radius: 8px; font-size: 10.5px; font-weight: 700;
    letter-spacing: .09em; text-transform: uppercase; color: var(--text-faint); }
summary.nav-label { transition: background .15s, color .15s; }
summary.nav-label:hover { color: var(--text-muted); background: var(--surface-muted); }
summary.nav-label.grp-active { color: var(--text-muted); }
.nav-dot { width: 6px; height: 6px; border-radius: 2px; flex-shrink: 0; }
.nav-chevron { width: 13px; height: 13px; margin-left: auto; color: var(--text-faint); opacity: .55;
    transition: opacity .15s, transform 0.2s cubic-bezier(0.4,0,0.2,1); }
summary.nav-label:hover .nav-chevron { opacity: 1; }

/* Doors — one anatomy for a section door and a pinned one. .fav-item deliberately stays its own
   class (a pinned row is a SHORTCUT, a copy) so the canonical row in its section remains the one
   and only a.submenu-item.active for a given page. */
.submenu-item, .fav-item { position: relative; display: flex; align-items: center; gap: 10px;
    height: 34px; padding: 0 10px; border-radius: 9px; font-size: 13px; font-weight: 500;
    color: var(--text-muted); text-decoration: none; width: 100%; transition: background .15s, color .15s; }
.submenu-item .nav-ico, .fav-item .nav-ico { width: 17px; height: 17px; flex-shrink: 0;
    color: var(--text-faint); transition: color .15s; }
.submenu-item:hover, .fav-item:hover { color: var(--text); background: var(--surface-muted); }
.submenu-item:hover .nav-ico, .fav-item:hover .nav-ico { color: var(--text-muted); }
/* Active = the grid's selection idiom: soft brand fill + a 3px brand bar down the leading edge. */
.submenu-item.active, .fav-item.active { color: var(--brand-text); font-weight: 600; background: var(--brand-soft); }
.submenu-item.active .nav-ico, .fav-item.active .nav-ico { color: var(--brand-text); }
.submenu-item.active::before, .fav-item.active::before { content: ""; position: absolute; left: 0;
    top: 8px; bottom: 8px; width: 3px; border-radius: 2px; background: var(--brand); }
.nav-doors { padding: 2px 0 4px; }
details[open] .nav-doors { animation: slideDown 0.15s ease-out; }
.nav-row + .nav-row { margin-top: 1px; }

/* One hairline between the menu's tiers — under the favourites rail, above the utility sections. */
.nav-sep { height: 1px; background: var(--line-soft); margin: 12px 8px 4px; }

/* The star is revealed on row hover (the grid's row-chip idiom) and stays lit once pinned, so the rail
   can be un-pinned without hunting. Keyboard focus reveals it too — it must not be mouse-only. Pinned =
   FILLED in the kit's favourite colour (the gx-flag-star / .is-star family). */
.nav-row { position: relative; }
.nav-row .nav-fav { position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    display: inline-flex; border: none; background: transparent; padding: 3px; border-radius: 6px;
    color: var(--text-faint); cursor: pointer; opacity: 0; transition: opacity .12s, color .12s, background .12s; }
.nav-row:hover .nav-fav, .nav-row .nav-fav:focus-visible, .nav-row .nav-fav.on { opacity: 1; }
.nav-row .nav-fav:hover { color: var(--warn); background: var(--warn-soft); }
.nav-row .nav-fav.on { color: var(--warn); }
.nav-row .nav-fav.on svg { fill: currentColor; }
.nav-row .nav-fav svg { width: 13px; height: 13px; }
/* No hover on touch — a star that never appears can't be tapped (the gx row-chip rule). */
@media (hover: none) { .nav-row .nav-fav { opacity: 1; } }
/* Room for the star so a long label never runs under it. */
.nav-row .submenu-item, .nav-row .fav-item { padding-right: 30px; }

/* The favourites rail REORDERS by drag and drop (no grip icon — his call: the rows are the handle).
   The row being carried fades, and the row it would land before draws the brand hairline above itself. */
.fav-row { cursor: grab; }
.fav-row.fav-carried { opacity: .35; cursor: grabbing; }
.fav-row.fav-drop-before { position: relative; }
.fav-row.fav-drop-before::before { content: ""; position: absolute; left: 8px; right: 8px; top: -1px;
    height: 2px; border-radius: 1px; background: var(--brand); }
.fav-dragging .fav-item, .fav-dragging .nav-fav { pointer-events: none; }

/* Nothing matched — say so, and hand back the way out. */
.nav-empty { display: flex; flex-direction: column; align-items: center; gap: 8px;
    padding: 22px 10px 10px; text-align: center; }
.nav-empty svg { width: 20px; height: 20px; color: var(--text-faint); }
.nav-empty p { font-size: 12px; color: var(--text-muted); }
.nav-empty-clear { border: 1px solid var(--line); background: var(--surface); color: var(--text);
    font-size: 12px; font-weight: 600; padding: 5px 12px; border-radius: 9px; cursor: pointer;
    transition: background .15s, border-color .15s; }
.nav-empty-clear:hover { background: var(--surface-muted); border-color: var(--brand); }

.badge-count {
    margin-left: auto;
    font-size: 11px;
    padding: 1px 7px;
    border-radius: 9999px;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* ── Table wrapper + scrollbar ───────────────────────────────────── */
.gx-table-wrap { overflow-x: auto; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.gx-table-wrap::-webkit-scrollbar { width: 5px; height: 5px; }
.gx-table-wrap::-webkit-scrollbar-track { background: transparent; }
.gx-table-wrap::-webkit-scrollbar-thumb { background: var(--line); border-radius: 10px; }
.gx-table-wrap::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }

.th-filter-wrap { position: relative; display: inline-flex; align-items: center; }

/* 13px, matching .gx-td — NOT 13.5. The half-pixel made every identity cell a different size from the
   rest of its own row, which is why StudentCell had been overriding it inline and the Students grid looked
   right while everything else drifted (his find, 2026-08-21).
   NO hover link styling either. The name used to turn brand-purple and
   underline on hover — a visited-hyperlink look sitting next to the row's avatar, and a treatment that
   appears nowhere in the style guide. The row's own hover fill and the pointer cursor are the affordance;
   the name is the row, not a hyperlink to somewhere else. */
.gx-name-link { font-size: 13px; font-weight: 600; color: var(--text); text-decoration: none; display: block; }

/* Quiet ghost icon-button for in-cell row actions (non-grid tables, forms). inline-flex (not flex)
   so multiple action buttons sit side-by-side, not stacked. The grid's own cell actions use the
   filled .gx-mini-btn tile; this is the lighter treatment for page-authored rows. */
.gx-action { width: 26px; height: 26px; border-radius: 6px; border: none; background: transparent; display: inline-flex; align-items: center; justify-content: center; color: var(--text-muted); cursor: pointer; transition: background .1s, color .1s; vertical-align: middle; }
.gx-action:hover { background: var(--line-soft); color: var(--text); }
.gx-action.danger:hover { background: var(--danger-soft); color: var(--danger); }
.gx-action svg { width: 13px; height: 13px; }


/* ── File-type chip (documents, #137) ────────────────────────────── */
.gx-fileicon { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 8px; background: var(--brand-soft); color: var(--brand); font-size: 9px; font-weight: 700; letter-spacing: .03em; }

/* ── Loading skeleton ────────────────────────────────────────────── */
@keyframes gx-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
.gx-skel { height: 11px; border-radius: 5px; background: linear-gradient(90deg, var(--line-soft) 25%, var(--line) 50%, var(--line-soft) 75%); background-size: 200% 100%; animation: gx-shimmer 1.6s infinite linear; }



/* ── Responsive ──────────────────────────────────────────────────── */
@media (max-width: 640px)  { .gx-table-wrap { -webkit-overflow-scrolling: touch; } }
/* Status badges */
.badge { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 9999px; font-size: 11px; font-weight: 600; }
.badge-active { background: var(--ok-soft); color: var(--ok); }
.badge-pending { background: var(--warn-soft); color: var(--warn); }
.badge-suspended { background: var(--danger-soft); color: var(--danger); }
.badge-graduated { background: var(--info-soft); color: var(--info); }
/* GPA */
.gpa-high { color: var(--ok); font-weight: 700; }
.gpa-mid  { color: var(--warn); font-weight: 700; }
.gpa-low  { color: var(--danger); font-weight: 700; }
/* Attendance bar */
.att-wrap { display: flex; flex-direction: column; gap: 3px; }
.att-bar { height: 3px; border-radius: 2px; background: var(--line); overflow: hidden; }
.att-fill { height: 100%; border-radius: 2px; }
/* Filter chips */
.filter-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 6px 3px 9px; border-radius: 8px; font-size: 11.5px; font-weight: 500; color: var(--brand-text); background: var(--brand-soft); border: 1px solid var(--brand-line); white-space: nowrap; }
.sdot { width: 7px; height: 7px; border-radius: 9999px; flex-shrink: 0; display: inline-block; }
/* Spinning animation */
@keyframes spin { to { transform: rotate(360deg); } }
.spinning { animation: spin 0.6s linear infinite; }
/* Bulk bar */
#bulk-bar { display: none; }
#bulk-bar.visible { display: flex; }

/* ═══════════════════════════════════════════════════════════════════
   ENROLMENT WIZARD  (/students/enroll)
   Multi-step form. Layout via Tailwind utilities in markup; the bespoke
   widgets (stepper, pill pickers, swatches, live preview, success) live
   here. Tokens match the Tera Lodge palette: indigo #6366f1 · slate scale.
═══════════════════════════════════════════════════════════════════ */

/* ── Stepper ─────────────────────────────────────────────────────── */
.enr-stepper { display: flex; align-items: flex-start; gap: 0; }
.enr-step { display: flex; flex-direction: column; align-items: center; gap: 7px; flex: 0 0 auto; position: relative; background: none; border: none; padding: 0; font-family: inherit; cursor: default; min-width: 0; }
.enr-step.is-nav { cursor: pointer; }
.enr-step-dot {
    width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
    font-size: 13px; font-weight: 700; flex-shrink: 0; position: relative; z-index: 1;
    background: var(--surface); color: var(--text-muted); border: 2px solid var(--line);
    transition: background .25s, border-color .25s, color .25s, box-shadow .25s, transform .15s;
}
.enr-step.is-nav:hover .enr-step-dot { border-color: var(--brand-line); color: var(--brand); }
.enr-step.is-active .enr-step-dot { background: var(--brand); border-color: var(--brand); color: #fff; box-shadow: 0 4px 12px rgba(99,102,241,.35); }
.enr-step.is-done .enr-step-dot { background: var(--brand-soft); border-color: var(--brand); color: var(--brand); }
.enr-step.is-done.is-nav:hover .enr-step-dot { background: var(--brand-soft); }
.enr-step-dot svg { width: 15px; height: 15px; }
.enr-step-label { font-size: 11.5px; font-weight: 600; color: var(--text-muted); white-space: nowrap; transition: color .25s; text-align: center; }
.enr-step.is-active .enr-step-label { color: var(--brand); }
.enr-step.is-done .enr-step-label { color: var(--text-muted); }
.enr-step-line { flex: 1 1 auto; height: 2px; background: var(--line); margin-top: 16px; border-radius: 2px; position: relative; overflow: hidden; min-width: 12px; }
.enr-step-line::after { content: ""; position: absolute; inset: 0; background: var(--brand); transform: scaleX(0); transform-origin: left; transition: transform .35s ease; }
.enr-step-line.is-filled::after { transform: scaleX(1); }

/* compact stepper (mobile): hide labels + lines, show dots only */
@media (max-width: 640px) {
    .enr-step-label { display: none; }
    .enr-step-dot { width: 30px; height: 30px; font-size: 12px; }
}

/* ── Step panel transition ───────────────────────────────────────── */
.enr-panel { animation: enr-slide .28s cubic-bezier(.22,.61,.36,1); }
@keyframes enr-slide { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.enr-section-title { font-size: 16px; font-weight: 700; color: var(--text); letter-spacing: -.3px; }
.enr-section-sub { font-size: 12.5px; color: var(--text-muted); margin-top: 2px; }

/* ── Fields ──────────────────────────────────────────────────────── */
.enr-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.enr-label { font-size: 12px; font-weight: 600; color: var(--text); display: flex; align-items: center; gap: 5px; }

/* ── compact-form — the DENSE editor treatment (the application record's per-section pencil forms:
   "grotesque… taking too much screen space"). Wrap any form region in .compact-form and every
   FormField/kit control inside drops to the house 30px scale with caps micro-labels. */
.compact-form .enr-field { gap: 3px; }
/* --text-muted, never --text-faint: a 10px label is TEXT and needs 4.5:1 (the axe gate reads it). */
.compact-form .enr-label { font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); transition: color .15s; }
.compact-form .enr-hint { font-size: 10.5px; }
.compact-form input:not([type="checkbox"]):not([type="radio"]), .compact-form select {
    height: 30px !important; padding: 0 8px !important; font-size: 12.5px !important;
    border-radius: 7px !important; border-width: 1px; }
.compact-form textarea { padding: 6px 8px !important; font-size: 12.5px !important; border-radius: 7px !important; }

/* The LIFE layer ("give all the form controls a lot more of life") — raw controls inside a
   compact form gain the kit inputs' behaviour: muted rest → surface on focus with a brand ring,
   a hover border shift, a lit-up micro-label while typing, branded checkboxes, a real select
   chevron. background-COLOR everywhere (a `background:` shorthand wipes the chevron image). */
.compact-form input:not([type="checkbox"]):not([type="radio"]), .compact-form select, .compact-form textarea {
    background-color: var(--surface-muted); border-color: var(--line); color: var(--text);
    outline: none; transition: border-color .15s, box-shadow .15s, background-color .15s; }
.compact-form input:hover:not(:focus), .compact-form select:hover:not(:focus), .compact-form textarea:hover:not(:focus) { border-color: var(--text-faint); }
.compact-form input:focus, .compact-form select:focus, .compact-form textarea:focus {
    background-color: var(--surface); border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.compact-form .enr-field:focus-within .enr-label { color: var(--brand-text); }
.compact-form select:not([multiple]) { appearance: none; -webkit-appearance: none; cursor: pointer; padding-right: 26px !important;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 8px center; }
.compact-form input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; }
.enr-req { color: var(--danger); font-weight: 700; }
.enr-optional { font-size: 11px; font-weight: 500; color: var(--text-faint); }
.enr-hint { font-size: 11.5px; color: var(--text-muted); line-height: 1.4; }

.enr-input, .enr-select, .enr-textarea {
    width: 100%; box-sizing: border-box; font-family: inherit; font-size: 13.5px; color: var(--text);
    background: var(--surface-muted); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px;
    outline: none; transition: border-color .15s, box-shadow .15s, background .15s;
}
.enr-input::placeholder, .enr-textarea::placeholder { color: var(--text-muted); }
/* background-COLOR, not the shorthand — the shorthand was wiping .enr-select's chevron image on focus. */
.enr-input:focus, .enr-select:focus, .enr-textarea:focus { background-color: var(--surface); border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.enr-input:hover:not(:focus), .enr-select:hover:not(:focus), .enr-textarea:hover:not(:focus) { border-color: var(--text-faint); }
.enr-textarea { resize: vertical; min-height: 78px; line-height: 1.5; }
.enr-select { appearance: none; cursor: pointer; padding-right: 36px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 12px center;
}

/* input with a leading icon and/or trailing adornment */
.enr-input-wrap { position: relative; display: flex; align-items: center; }
.enr-input-wrap .enr-lead-icon { position: absolute; left: 12px; width: 15px; height: 15px; color: var(--text-muted); pointer-events: none; }
.enr-input-wrap.has-lead .enr-input { padding-left: 36px; }

/* validation states */
.enr-field.has-error .enr-input,
.enr-field.has-error .enr-select,
.enr-field.has-error .enr-textarea { border-color: #fca5a5; background: var(--danger-soft); }
.enr-field.has-error .enr-input:focus,
.enr-field.has-error .enr-select:focus,
.enr-field.has-error .enr-textarea:focus { border-color: var(--danger); box-shadow: 0 0 0 3px rgba(239,68,68,.14); }
.enr-error { display: flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 500; color: var(--danger); animation: enr-shake .3s; }
.enr-error::before { content: ""; display: inline-block; width: 12px; height: 12px; flex-shrink: 0; background: currentColor;
    -webkit-mask: center/contain no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath stroke-linecap='round' d='M12 7.5v5M12 16h.01'/%3E%3C/svg%3E");
    mask: center/contain no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath stroke-linecap='round' d='M12 7.5v5M12 16h.01'/%3E%3C/svg%3E"); }
@keyframes enr-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-3px); } 40%,80% { transform: translateX(3px); } }

/* suggestion chip (e.g. suggested email) */
.enr-suggest { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 8px; font-size: 11.5px; font-weight: 500; color: var(--brand); background: var(--brand-soft); border: 1px solid var(--brand-line); cursor: pointer; transition: background .12s, border-color .12s; font-family: inherit; }
.enr-suggest:hover { background: var(--brand-soft); border-color: var(--brand-line); }
.enr-suggest svg { width: 12px; height: 12px; }

/* ── Pill selector (radio-as-pills) ──────────────────────────────── */
.enr-pillset { display: flex; flex-wrap: wrap; gap: 8px; }
.enr-pill {
    display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px; border-radius: 10px;
    font-family: inherit; font-size: 13px; font-weight: 500; color: var(--text);
    background: var(--surface); border: 1.5px solid var(--line); cursor: pointer; white-space: nowrap;
    transition: border-color .15s, background .15s, color .15s, box-shadow .15s, transform .12s;
}
.enr-pill:hover { border-color: var(--brand-line); background: var(--surface-muted); color: var(--text); }
.enr-pill:active { transform: scale(.97); }
.enr-pill.is-selected { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-text); font-weight: 600; box-shadow: 0 0 0 1px var(--brand); }
.enr-pill .enr-pill-check { width: 14px; height: 14px; color: var(--brand); }
.enr-pill .enr-pill-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }

/* grade picker — fixed-width cells in a tidy grid */
.enr-grade-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
@media (min-width: 480px) { .enr-grade-grid { grid-template-columns: repeat(6, 1fr); } }
.enr-grade-cell { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; padding: 11px 4px; border-radius: 11px; border: 1.5px solid var(--line); background: var(--surface); cursor: pointer; font-family: inherit; transition: border-color .15s, background .15s, box-shadow .15s, transform .12s; }
.enr-grade-cell:hover { border-color: var(--brand-line); background: var(--surface-muted); }
.enr-grade-cell:active { transform: scale(.96); }
.enr-grade-cell.is-selected { border-color: var(--brand); background: var(--brand-soft); box-shadow: 0 0 0 1px var(--brand); }
.enr-grade-num { font-size: 18px; font-weight: 800; color: var(--text); letter-spacing: -.5px; line-height: 1; }
.enr-grade-cell.is-selected .enr-grade-num { color: var(--brand-text); }
.enr-grade-cap { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); }

/* ── Avatar swatches ─────────────────────────────────────────────── */
.enr-swatch-row { display: flex; flex-wrap: wrap; gap: 9px; }
.enr-swatch { width: 30px; height: 30px; border-radius: 9px; cursor: pointer; border: none; padding: 0; position: relative; transition: transform .14s, box-shadow .14s; }
.enr-swatch:hover { transform: scale(1.12); }
.enr-swatch.is-selected { box-shadow: 0 0 0 2px #fff, 0 0 0 4px currentColor; }
.enr-swatch.is-selected::after { content: ""; position: absolute; inset: 0; display: flex; background: center/14px no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M4.5 12.75l6 6 9-13.5'/%3E%3C/svg%3E"); }

/* ── Toggle switch ───────────────────────────────────────────────── */
.enr-switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; }
.enr-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.enr-switch-track { width: 40px; height: 23px; border-radius: 999px; background: var(--line); padding: 2px; transition: background .2s; flex-shrink: 0; }
.enr-switch input:checked ~ .enr-switch-track { background: var(--brand); }
.enr-switch input:focus-visible ~ .enr-switch-track { box-shadow: 0 0 0 3px rgba(99,102,241,.25); }
.enr-switch-thumb { width: 19px; height: 19px; border-radius: 50%; background: var(--surface); box-shadow: 0 1px 3px rgba(0,0,0,.25); transition: transform .2s cubic-bezier(.4,0,.2,1); }
.enr-switch input:checked ~ .enr-switch-track .enr-switch-thumb { transform: translateX(17px); }
.enr-switch-label { font-size: 13px; color: var(--text); font-weight: 500; }

/* ── Live preview card ───────────────────────────────────────────── */
.enr-preview { position: sticky; top: 0; }
.enr-preview-banner { height: 64px; background: linear-gradient(135deg, var(--brand), var(--brand-strong)); position: relative; }
.enr-preview-av { position: relative; z-index: 1; width: 76px; height: 76px; border-radius: 18px; border: 3px solid #fff; box-shadow: 0 6px 18px rgba(99,102,241,.28); margin: -38px auto 0; display: block; background: var(--brand-soft); transition: box-shadow .2s; }
.enr-preview-name { font-size: 16px; font-weight: 700; color: var(--text); text-align: center; margin-top: 10px; letter-spacing: -.2px; }
.enr-preview-name.is-empty { color: var(--text-faint); font-weight: 600; }
.enr-preview-id { font-size: 11.5px; color: var(--text-muted); text-align: center; margin-top: 2px; font-variant-numeric: tabular-nums; }
.enr-preview-tags { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-top: 12px; }
.enr-tag { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; background: var(--line-soft); color: var(--text); }
.enr-tag.indigo { background: var(--brand-soft); color: var(--brand-text); }
/* Intake-origin tone modifiers (#122): metro=indigo, regional=emerald, interstate=amber, overseas=sky. */
.enr-tag.emerald { background: var(--ok-soft); color: var(--ok); }
.enr-tag.amber { background: var(--warn-soft); color: var(--warn); }
.enr-tag.sky { background: var(--info-soft); color: var(--info); }
.enr-preview-rows { margin-top: 16px; display: flex; flex-direction: column; }
.enr-prow { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-top: 1px solid var(--line-soft); font-size: 12.5px; }
.enr-prow svg { width: 14px; height: 14px; color: var(--brand-line); flex-shrink: 0; }
.enr-prow-k { color: var(--text-muted); flex-shrink: 0; }
.enr-prow-v { color: var(--text); font-weight: 500; margin-left: auto; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60%; }
.enr-prow-v.is-empty { color: var(--text-faint); font-weight: 400; font-style: italic; }

/* completion ring */
.enr-ring-wrap { display: flex; align-items: center; gap: 11px; }
.enr-ring { --pct: 0; width: 44px; height: 44px; flex-shrink: 0; position: relative; }
.enr-ring svg { transform: rotate(-90deg); }
.enr-ring-track { stroke: var(--line-soft); }
.enr-ring-fill { stroke: var(--brand-text); stroke-linecap: round; transition: stroke-dashoffset .4s ease; }
.enr-ring-pct { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 800; color: var(--brand); }

/* ── Status badges (preview + review, reuse grid palette) ────────── */
.enr-badge { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 9999px; font-size: 11px; font-weight: 600; }
.enr-badge .dot { width: 6px; height: 6px; border-radius: 50%; }
.enr-badge.active { background: var(--ok-soft); color: var(--ok); } .enr-badge.active .dot { background: #22c55e; }
.enr-badge.pending { background: var(--warn-soft); color: var(--warn); } .enr-badge.pending .dot { background: #f59e0b; }

/* ── Review step ─────────────────────────────────────────────────── */
.enr-review-group { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
.enr-review-head { display: flex; align-items: center; gap: 9px; padding: 11px 16px; background: var(--surface-muted); border-bottom: 1px solid var(--line); }
.enr-review-head h4 { font-size: 13px; font-weight: 700; color: var(--text); }
.enr-review-edit { margin-left: auto; display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-weight: 600; color: var(--brand); background: none; border: none; cursor: pointer; padding: 4px 8px; border-radius: 7px; font-family: inherit; transition: background .12s; }
.enr-review-edit:hover { background: var(--brand-soft); }
.enr-review-edit svg { width: 11px; height: 11px; }
.enr-review-body { display: grid; grid-template-columns: repeat(2, 1fr); gap: 13px 20px; padding: 15px 16px; }
@media (max-width: 520px) { .enr-review-body { grid-template-columns: 1fr; } }
.enr-rv-k { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin-bottom: 3px; }
.enr-rv-v { font-size: 13.5px; color: var(--text); font-weight: 500; }
.enr-rv-v.is-empty { color: var(--text-faint); font-style: italic; font-weight: 400; }
.enr-rv-full { grid-column: 1 / -1; }

/* consent box */
.enr-consent { display: flex; gap: 11px; padding: 14px 16px; border-radius: 12px; border: 1.5px solid var(--line); background: var(--surface-muted); cursor: pointer; transition: border-color .15s, background .15s; }
.enr-consent:hover { border-color: var(--brand-line); }
.enr-consent.is-checked { border-color: var(--brand); background: var(--brand-soft); }
.enr-consent.has-error { border-color: #fca5a5; background: var(--danger-soft); }
.enr-consent input { width: 17px; height: 17px; accent-color: var(--brand); margin-top: 1px; flex-shrink: 0; cursor: pointer; }
.enr-consent-text { font-size: 12.5px; color: var(--text); line-height: 1.5; }

/* ── Footer nav ──────────────────────────────────────────────────── */
.enr-nav { display: flex; align-items: center; gap: 10px; padding-top: 4px; }
.enr-btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; height: 42px; padding: 0 20px; border-radius: 11px; font-family: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer; transition: background .14s, border-color .14s, color .14s, box-shadow .14s, transform .1s; white-space: nowrap; }
.enr-btn:active { transform: translateY(1px); }
.enr-btn svg { width: 15px; height: 15px; }
.enr-btn-ghost { background: var(--surface); border: 1px solid var(--line); color: var(--text); }
.enr-btn-ghost:hover { border-color: var(--text-faint); background: var(--surface-muted); color: var(--text); }
.enr-btn-primary { background: var(--brand); border: 1px solid var(--brand); color: #fff; box-shadow: 0 2px 8px var(--brand-ring); }
.enr-btn-primary:hover { background: var(--brand); border-color: var(--brand); box-shadow: 0 4px 14px var(--brand-ring); }
.enr-btn-success { background: #10b981; border: 1px solid var(--ok); color: #fff; box-shadow: 0 2px 8px rgba(16,185,129,.32); }
.enr-btn-success:hover { background: #059669; border-color: var(--ok); }
.enr-btn:disabled { opacity: .55; cursor: not-allowed; box-shadow: none; transform: none; }
.enr-btn-spinner { width: 15px; height: 15px; border: 2px solid rgba(255,255,255,.45); border-top-color: #fff; border-radius: 50%; animation: spin .65s linear infinite; }

/* ── Draft-resume banner ─────────────────────────────────────────── */
.enr-draft { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 12px; background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 32%, var(--surface)); font-size: 12.5px; color: var(--warn); }
.enr-draft svg { width: 15px; height: 15px; color: var(--warn); flex-shrink: 0; }
.enr-draft-btn { font-weight: 700; color: var(--warn); background: none; border: none; cursor: pointer; font-family: inherit; font-size: 12.5px; padding: 3px 8px; border-radius: 6px; transition: background .12s; }
.enr-draft-btn:hover { background: var(--warn-soft); }
.enr-draft-x { margin-left: auto; }
.enr-check { width: 84px; height: 84px; margin: 0 auto 18px; }
.enr-check circle { stroke: #10b981; stroke-width: 3; fill: var(--ok-soft); stroke-dasharray: 251; stroke-dashoffset: 251; animation: enr-draw-c .5s ease forwards; }
.enr-check path { stroke: #10b981; stroke-width: 4; fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 60; stroke-dashoffset: 60; animation: enr-draw-t .35s .45s ease forwards; }
@keyframes enr-draw-c { to { stroke-dashoffset: 0; } }
@keyframes enr-draw-t { to { stroke-dashoffset: 0; } }

/* pure-CSS confetti burst */
.enr-confetti { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.enr-confetti i { position: absolute; top: -12px; width: 8px; height: 13px; border-radius: 2px; opacity: 0; animation: enr-fall 2.4s ease-in forwards; }
@keyframes enr-fall { 0% { opacity: 0; transform: translateY(-10px) rotate(0); } 10% { opacity: 1; } 100% { opacity: 0; transform: translateY(420px) rotate(540deg); } }
.enr-confetti i:nth-child(1)  { left: 8%;  background: var(--brand); animation-delay: 0s;    }
.enr-confetti i:nth-child(2)  { left: 18%; background: #f59e0b; animation-delay: .12s;  }
.enr-confetti i:nth-child(3)  { left: 28%; background: #10b981; animation-delay: .05s;  }
.enr-confetti i:nth-child(4)  { left: 38%; background: #ec4899; animation-delay: .22s;  }
.enr-confetti i:nth-child(5)  { left: 48%; background: #8b5cf6; animation-delay: .08s;  }
.enr-confetti i:nth-child(6)  { left: 58%; background: #06b6d4; animation-delay: .3s;   }
.enr-confetti i:nth-child(7)  { left: 68%; background: #f97316; animation-delay: .15s;  }
.enr-confetti i:nth-child(8)  { left: 78%; background: #22c55e; animation-delay: .26s;  }
.enr-confetti i:nth-child(9)  { left: 88%; background: #a855f7; animation-delay: .1s;   }
.enr-confetti i:nth-child(10) { left: 14%; background: #0ea5e9; animation-delay: .34s;  }
.enr-confetti i:nth-child(11) { left: 63%; background: #ef4444; animation-delay: .4s;   }
.enr-confetti i:nth-child(12) { left: 33%; background: #14b8a6; animation-delay: .18s;  }

/* ── "What happens next" list (success) ──────────────────────────── */
.enr-next { display: flex; flex-direction: column; }
.enr-next li { display: flex; gap: 11px; padding: 10px 0; align-items: flex-start; border-top: 1px solid var(--line-soft); }
.enr-next li:first-child { border-top: none; }
.enr-next-ic { width: 30px; height: 30px; border-radius: 9px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.enr-next-ic svg { width: 15px; height: 15px; }
.enr-next-t { font-size: 12.5px; font-weight: 600; color: var(--text); }
.enr-next-d { font-size: 11px; color: var(--text-muted); margin-top: 1px; line-height: 1.4; word-break: break-word; }

/* ── University wizard extras (reuse the .enr-* palette; preview crest, course rows) ── */
/* Preview crest — the avatar analogue: a gradient tile lifted over the banner, like .enr-preview-av */
.enr-uni-crest {
    position: relative; z-index: 1; width: 64px; height: 64px; margin: -32px auto 0;
    border-radius: 16px; border: 3px solid var(--surface); background: linear-gradient(135deg, var(--brand), var(--brand-strong));
    box-shadow: 0 6px 18px rgba(99,102,241,.28); display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: 22px; font-weight: 800; letter-spacing: -.5px;
}

/* Empty-courses placeholder inside the step */
.enr-uni-courses-empty { text-align: center; padding: 26px 16px; color: var(--text-muted); }
.enr-uni-courses-empty svg { width: 32px; height: 32px; color: var(--text-faint); margin: 0 auto 8px; }
.enr-uni-courses-empty p { font-size: 13px; }

/* A saved course row — level chip + name/meta + remove */
.enr-uni-course-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 12px; background: var(--surface-muted); border: 1px solid var(--line); transition: border-color .15s, background .15s; }
.enr-uni-course-row:hover { border-color: var(--brand-line); background: var(--surface-muted); }
.enr-uni-course-name { font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.enr-uni-course-meta { font-size: 11px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.enr-uni-course-lvl { flex-shrink: 0; min-width: 38px; height: 26px; padding: 0 8px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; font-size: 10.5px; font-weight: 800; letter-spacing: .03em; }
.enr-uni-course-lvl.is-ug { background: var(--brand-soft); color: var(--brand-text); }
.enr-uni-course-lvl.is-pg { background: var(--accent-soft); color: var(--accent); }
.enr-uni-course-lvl.is-research { background: var(--info-soft); color: var(--info); }
.enr-uni-course-lvl.is-voc { background: var(--warn-soft); color: var(--warn); }
.enr-uni-course-lvl.is-other { background: var(--line-soft); color: var(--text); }

/* Add-course sub-form panel */
.enr-uni-addcourse { background: var(--surface-muted); border: 1px solid var(--brand-line); border-radius: 14px; padding: 16px; }
.enr-uni-addcourse-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin-bottom: 12px; }

/* ── Grade colour-coding (#135) ──────────────────────────────────────
   A compact grade chip used in the result-set list, the subject rows and the live preview. The tone tracks
   the outcome: HD/D green, C/P blue, F-family red, Other neutral (reuses the enr-tag tone palette). */
/* Unified with .badge (pills are rounded-full, weight 600) so every grid's tags look identical (#331). */
.res-grade { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 9999px; font-size: 11px; font-weight: 600; line-height: 1.4; background: var(--line-soft); color: var(--text-muted); white-space: nowrap; }
/* The grade form's colour pick: the chip preview, then the palette swatches; "none" is the neutral chip. */
.grade-swatches { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.grade-swatches .res-grade { margin-right: 8px; }
.grade-swatch { width: 18px; height: 18px; border-radius: 5px; border: 2px solid transparent; cursor: pointer; padding: 0; transition: transform .12s; }
.grade-swatch:hover { transform: scale(1.2); }
.grade-swatch.is-current { border-color: var(--text); }
.grade-swatch.is-none { background: var(--line-soft); position: relative; }
.grade-swatch.is-none::after { content: ""; position: absolute; inset: 3px; border-top: 2px solid var(--text-muted); transform: rotate(-45deg) translateY(4px); }
.res-grade.is-pass { background: var(--ok-soft); color: var(--ok); }            /* counts as a pass — green */
.res-grade.is-fail { background: var(--danger-soft); color: var(--danger); }    /* counts as a fail — red   */
.res-grade.is-other { background: var(--line-soft); color: var(--text); }       /* neither, or not in the catalogue — neutral */

@media (prefers-reduced-motion: reduce) {
    .enr-panel, .enr-error, .enr-check circle, .enr-check path, .enr-confetti i { animation: none; }
    .enr-check circle, .enr-check path { stroke-dashoffset: 0; }
}

/* ═══════════════════════════════════════════════════════════════════
   REUSABLE UI KIT  (Shared/Ui/* — PageHeader, Card, AppButton, Badge,
   EmptyState, FormField, TextInput, SelectInput, ModalDialog)
   Tokens match the Tera Lodge palette: indigo #6366f1 · slate scale ·
   success #10b981 · amber #f59e0b · red #ef4444. FormField/TextInput/
   SelectInput reuse the existing .enr-* form styles above.
═══════════════════════════════════════════════════════════════════ */

/* ── AppButton ───────────────────────────────────────────────────── */
.ui-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    height: 38px; padding: 0 16px; border-radius: 10px; font-family: inherit;
    font-size: 13px; font-weight: 600; cursor: pointer; white-space: nowrap;
    border: 1px solid transparent;
    transition: background .14s, border-color .14s, color .14s, box-shadow .14s, transform .1s;
}
.ui-btn:active:not(:disabled) { transform: translateY(1px); }
.ui-btn:disabled { opacity: .55; cursor: not-allowed; box-shadow: none; transform: none; }
.ui-btn svg { width: 15px; height: 15px; flex-shrink: 0; }

.ui-btn-primary { background: var(--brand); border-color: var(--brand); color: #fff; box-shadow: 0 2px 8px var(--brand-ring); }
.ui-btn-primary:hover:not(:disabled) { background: var(--brand); border-color: var(--brand); box-shadow: 0 4px 14px var(--brand-ring); }

.ui-btn-secondary { background: var(--surface); border-color: var(--line); color: var(--text); }
.ui-btn-secondary:hover:not(:disabled) { border-color: var(--text-faint); background: var(--surface-muted); color: var(--text); }

.ui-btn.is-armed { border-color: color-mix(in srgb, var(--danger) 45%, var(--surface)); background: var(--danger-soft); color: var(--danger); }   /* the first tap armed it; the second fires */
.ui-btn-ghost { background: transparent; border-color: transparent; color: var(--text-muted); }
.ui-btn-ghost:hover:not(:disabled) { background: var(--line-soft); color: var(--text); }

.ui-btn-danger { background: #ef4444; border-color: var(--danger); color: #fff; box-shadow: 0 2px 8px rgba(239,68,68,.28); }
.ui-btn-danger:hover:not(:disabled) { background: #dc2626; border-color: var(--danger); }

/* inline spinner (uses the global @keyframes spin) — no JS */
.ui-btn-spinner {
    width: 14px; height: 14px; border-radius: 50%;
    border: 2px solid rgba(255,255,255,.45); border-top-color: #fff;
    animation: spin .65s linear infinite; flex-shrink: 0;
}
.ui-btn-secondary .ui-btn-spinner,
.ui-btn-ghost .ui-btn-spinner { border-color: var(--brand-ring); border-top-color: var(--brand); }

/* ── Badge (pill) ────────────────────────────────────────────────── */
.ui-badge { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 9999px; font-size: 11px; font-weight: 600; white-space: nowrap; }
.ui-badge-neutral { background: var(--line-soft); color: var(--text); }
.ui-badge-success { background: var(--ok-soft); color: var(--ok-strong); }
.ui-badge-warning { background: var(--warn-soft); color: var(--warn); }
.ui-badge-danger  { background: var(--danger-soft); color: var(--danger); }
.ui-badge-info    { background: var(--brand-soft); color: var(--brand-text); }

/* ── EmptyState ──────────────────────────────────────────────────── */
.ui-empty { padding: 56px 20px; text-align: center; }
.ui-empty-icon { display: inline-flex; }
.ui-empty-icon svg { width: 40px; height: 40px; color: var(--text-faint); margin-bottom: 12px; }
.ui-empty-title { font-size: 14.5px; font-weight: 600; color: var(--text); margin-bottom: 5px; }
.ui-empty-msg { font-size: 13px; color: var(--text-muted); }

/* ── Dashboard KPI cards (#124) ──────────────────────────────────────
   Mobile-first responsive grid: 1 col by default, then 2 / 3 / 4 columns as the
   viewport widens. Each card is an anchor (links to the relevant page) with a tinted
   icon, a big tabular number and a label. Pure CSS — no JS, no charts. Palette matches
   the Tera Lodge tokens (indigo #6366f1 · slate scale). */
.kpi-grid {
    display: grid; gap: 14px;
    grid-template-columns: repeat(1, minmax(0, 1fr));
}
@media (min-width: 480px)  { .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 768px)  { .kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .kpi-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* Base styling for the remaining RAW <table class="dg"> pages (15 pages: Muster, Kitchen forecast,
   AccessControl, AllocationConsole, ...) — restored after the #575 cleanup removed the legacy grid layer
   (the pinned/resize/mobile-card machinery stays deleted; these static tables use only this base).
   Cells are TRANSPARENT (the old #fff cells masked row-state tints like Muster's red Unaccounted rows);
   hover lives on the row for the same reason. Converting these pages to the DataGrid kit is #577 work. */
table.dg { width: 100%; border-collapse: collapse; font-size: 13.5px; font-family: inherit; }
table.dg thead th { position: sticky; top: 0; z-index: 2; background: var(--surface-muted); border-bottom: 1px solid var(--line); padding: 9px 14px; text-align: left; font-size: 11px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .06em; white-space: nowrap; }
table.dg td { padding: 10px 14px; color: var(--text); vertical-align: middle; }
table.dg tbody tr { border-bottom: 1px solid var(--line-soft); transition: background .08s; }
table.dg tbody tr:last-child { border-bottom: none; }
table.dg tbody tr:hover { background: var(--surface-muted); }

/* Hero stat card decor (from mocks/index.html) — soft circles floating over the gradient. */
.stat-card-decor-1 { position: absolute; width: 110px; height: 110px; border-radius: 9999px; background: rgba(255,255,255,0.15); top: -24px; right: -24px; }
.stat-card-decor-2 { position: absolute; width: 72px; height: 72px; border-radius: 9999px; background: rgba(255,255,255,0.08); bottom: -20px; right: 20px; }

.kpi-card {
    display: flex; flex-direction: column; gap: 2px;
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 16px;
    padding: 16px 18px; text-decoration: none;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
    transition: border-color .14s, box-shadow .14s, transform .1s;
}
.kpi-card:hover {
    border-color: var(--brand-line); box-shadow: 0 6px 18px rgba(99, 102, 241, .12);
    transform: translateY(-1px);
}
.kpi-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: 10px; margin-bottom: 8px;
}
.kpi-icon svg { width: 19px; height: 19px; }
.kpi-value { font-size: 28px; font-weight: 800; color: var(--text); line-height: 1.1; font-variant-numeric: tabular-nums; }
.kpi-label { font-size: 12.5px; font-weight: 600; color: var(--text-muted); }

/* Loading skeleton — same card shell, shimmering placeholder lines (CSS only). */
.kpi-skeleton { pointer-events: none; }
.kpi-skel-line {
    height: 12px; border-radius: 6px;
    background: linear-gradient(90deg, var(--line-soft) 25%, var(--line) 37%, var(--line-soft) 63%);
    background-size: 400% 100%; animation: kpi-shimmer 1.4s ease infinite;
}
@keyframes kpi-shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }

/* By-status strip — label · CSS bar · count, one row per status. */
.kpi-bar-row {
    display: grid; grid-template-columns: 140px 1fr 48px; align-items: center; gap: 12px;
    text-decoration: none;
}
.kpi-bar-label { font-size: 12.5px; font-weight: 600; color: var(--text); text-transform: capitalize; }
.kpi-bar-track { height: 10px; border-radius: 9999px; background: var(--line-soft); overflow: hidden; }
.kpi-bar-fill { display: block; height: 100%; border-radius: 9999px; transition: width .3s ease; }
.kpi-bar-count { font-size: 13px; font-weight: 700; color: var(--text); text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 480px) {
    .kpi-bar-row { grid-template-columns: 1fr 40px; }
    .kpi-bar-track { grid-column: 1 / -1; order: 3; }
}

/* ── ModalDialog ─────────────────────────────────────────────────── */
.ui-modal-overlay {
    position: fixed; inset: 0; z-index: 900; display: flex; align-items: center; justify-content: center;
    padding: 20px; background: rgba(15,23,42,.45); animation: ui-modal-fade .15s ease;
}
@keyframes ui-modal-fade { from { opacity: 0; } to { opacity: 1; } }
.ui-modal {
    width: 100%; max-width: 560px; max-height: calc(100vh - 40px); display: flex; flex-direction: column;
    background: var(--surface); border-radius: 18px; border: 1px solid var(--line);
    box-shadow: 0 20px 60px rgba(15,23,42,.22), 0 4px 16px rgba(15,23,42,.08);
    overflow: hidden; animation: ui-modal-pop .16s ease;
}
@keyframes ui-modal-pop { from { opacity: 0; transform: scale(.97) translateY(8px); } to { opacity: 1; transform: none; } }
.ui-modal-head { display: flex; align-items: center; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--line-soft); flex-shrink: 0; }
.ui-modal-title { font-size: 15px; font-weight: 700; color: var(--text); letter-spacing: -.2px; }
.ui-modal-close { margin-left: auto; width: 30px; height: 30px; border-radius: 8px; border: none; background: transparent; color: var(--text-muted); cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: background .12s, color .12s; }
.ui-modal-close:hover { background: var(--line-soft); color: var(--text); }
.ui-modal-close svg { width: 16px; height: 16px; }
.ui-modal-body { padding: 20px; overflow-y: auto; }
.ui-modal-foot { display: flex; align-items: center; justify-content: flex-end; gap: 10px; padding: 14px 20px; border-top: 1px solid var(--line-soft); background: var(--surface-muted); flex-shrink: 0; flex-wrap: wrap; }

/* The residence calendar's picked-up handle (a brand ring, until it is dropped or put back down) and its
   ARMED day (the double-tap's first half: the next tap here fires — the strip above says what). */
.ayc-day.is-armed { box-shadow: 0 0 0 2px var(--brand), 0 0 0 5px var(--brand-ring); transform: scale(1.12); z-index: 1; }
.ayc-day.is-pending { box-shadow: 0 0 0 2px var(--warn), 0 0 0 5px color-mix(in srgb, var(--warn) 28%, transparent); transform: scale(1.12); z-index: 1; animation: ayc-arm-pulse 1.2s ease-in-out infinite; }
@keyframes ayc-arm-pulse { 50% { box-shadow: 0 0 0 2px var(--warn), 0 0 0 7px color-mix(in srgb, var(--warn) 14%, transparent); } }
.ayc-arm {
    display: flex; align-items: center; gap: 8px; margin-bottom: 10px; padding: 7px 10px;
    border: 1px solid color-mix(in srgb, var(--warn) 32%, var(--surface)); border-radius: 9px;
    background: var(--warn-soft); color: var(--text); font-size: 12px; font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
    .ui-modal-overlay, .ui-modal, .ui-btn-spinner { animation: none; }
    .ayc-day.is-pending { animation: none; }
}

/* ── Student-year status timeline (#131) — the lifecycle history on the edit form's Status step. A simple
   vertical rail: a coloured dot per change (reusing the .res-grade tone), then the status, date, actor + note. */
.sy-timeline { position: relative; margin-top: 12px; padding-left: 6px; }
.sy-timeline-item { position: relative; display: flex; gap: 12px; padding: 0 0 16px 18px; border-left: 2px solid var(--line); }
.sy-timeline-item:last-child { border-left-color: transparent; padding-bottom: 0; }
.sy-timeline-dot { position: absolute; left: -7px; top: 2px; width: 12px; height: 12px; padding: 0; border-radius: 9999px; border: 2px solid #fff; box-shadow: 0 0 0 1px var(--line); }
.sy-timeline-body { flex: 1; min-width: 0; }

/* ── Universal search (#136) — the /search results page. It draws the command panel's OWN tab strip and
   rows (.tb-tabs / .tb-res, further down) from the same components, so only its shell lives here. */

/* ── Cohort chips (#150 quota/cohort dashboard) ──────────────────── */
/* A small read-only count pill for a cohort dimension (fresher/returner, gender, faculty, scholarship, legacy).
   Colour is set inline per dimension; this just sets the shape + the count emphasis. */
.cohort-chip { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 9999px;
    font-size: 13px; font-weight: 600; line-height: 1; }
.cohort-chip strong { font-weight: 800; font-variant-numeric: tabular-nums; }

/* THE RESULTS PAGE IS THE DROPDOWN, FULL SIZE: the same tab strip and the same rows on one surface, so
   the two never read as different products. Only the shell is page-specific. */
.search-panel { background: var(--surface); border: 1px solid var(--line-soft); border-radius: 14px; overflow: hidden; }
.search-panel-rows { padding: 6px; }

/* The matched token inside a row title — the row supplies the colour (see .tb-res-title .search-mark). */
.search-mark { background: transparent; padding: 0; font-weight: 800; }

/* ═══════════════ THE TOPBAR (2026-08-24 rebuild, .tb-*) ═══════════════
   Three zones on one 64px line: WHERE YOU ARE (home · section dot · trail), the COMMAND BAR, and
   WHO/WHAT (org menu · action tiles · identity). Tokens only — the bar is chrome, so a white-labelled
   college's colour reaches it without anything being passed in, and dark comes free.

   The GLASS is an underlay SIBLING (.tb-glass), never on .tb itself: backdrop-filter on an ancestor
   becomes the containing block for position:fixed descendants, which pinned every dropdown's
   click-away scrim to the 64px bar. */

.tb { position: sticky; top: 0; z-index: 20; flex-shrink: 0; height: 64px;
      border-bottom: 1px solid var(--line-soft); box-shadow: 0 1px 2px rgba(15,23,42,.04); }
.tb-glass { position: absolute; inset: 0; z-index: -1;
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }
.tb-row { display: flex; align-items: center; gap: 10px; height: 100%; padding: 0 12px; }
@media (min-width: 1024px) { .tb-row { padding: 0 18px; } }
.tb-left { display: flex; align-items: center; gap: 4px; min-width: 0; flex-shrink: 1; }
.tb-mid { flex: 1; display: flex; justify-content: center; min-width: 0; padding: 0 8px; }
.tb-right { display: flex; align-items: center; gap: 6px; flex-shrink: 0; margin-left: auto; }
/* --line, not --line-soft: soft is ~invisible against the bar's own surface, so the divider after the
   notifications tile read as nothing at all. A separator you cannot see is not a separator. */
.tb-div { width: 1px; height: 26px; background: var(--line); margin: 0 6px; flex-shrink: 0; }

/* ── Left: where you are ─────────────────────────────────────────────────────── */
.tb-home { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px;
    border-radius: 10px; flex-shrink: 0; color: var(--brand-text); background: var(--brand-soft);
    transition: background .15s, color .15s, transform .15s; }
.tb-home svg { width: 16px; height: 16px; }
.tb-home:hover { background: var(--brand); color: var(--on-brand); }
.tb-trail { display: none; align-items: center; gap: 2px; min-width: 0; }
@media (min-width: 768px) { .tb-trail { display: flex; } }
/* The section dot — the owning nav group's accent, the same language the sidebar's section labels
   speak, so the bar says which part of the app you are in without spending a word on it. */
.tb-section-dot { width: 6px; height: 6px; border-radius: 2px; flex-shrink: 0; margin: 0 8px 0 6px; }
.tb-crumb { flex-shrink: 0; padding: 5px 8px; border-radius: 8px; font-size: 12.5px; font-weight: 600;
    color: var(--text-muted); text-decoration: none; transition: background .15s, color .15s; }
a.tb-crumb:hover { background: var(--surface-muted); color: var(--brand-text); }
.tb-crumb-sep { width: 13px; height: 13px; flex-shrink: 0; color: var(--text-faint); }
.tb-crumb-leaf { min-width: 0; padding: 5px 2px 5px 8px; font-size: 13.5px; font-weight: 800;
    letter-spacing: -.01em; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── The command bar ─────────────────────────────────────────────────────────── */
/* The bar's middle zone has room to spare, and a search box people type NAMES into should look like it
   expects one. NOT a pill: every other control in the app is a rounded rectangle, and the capsule was the
   one shape that matched nothing around it. */
.tb-cmd-wrap { position: relative; width: 100%; max-width: 760px; }
.tb-cmd { display: flex; align-items: center; gap: 10px; height: 38px; padding: 0 8px 0 13px;
    border-radius: 10px; background: var(--surface-muted); border: 1px solid var(--line-soft);
    transition: background .15s, border-color .15s, box-shadow .15s; }
.tb-cmd:hover { border-color: var(--line); }
.tb-cmd:focus-within, .tb-cmd.is-open { background: var(--surface); border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-ring); }
.tb-cmd-ico { width: 16px; height: 16px; flex-shrink: 0; color: var(--text-faint); transition: color .15s; }
.tb-cmd:focus-within .tb-cmd-ico { color: var(--brand-text); }
.tb-cmd-input { flex: 1; min-width: 0; border: none; background: transparent; outline: none;
    font-family: inherit; font-size: 13px; color: var(--text); }
.tb-cmd-input::placeholder { color: var(--text-faint); }
/* The NATIVE WebKit clear ✕ / decorations render under our own controls and read as misalignment. */
.tb-cmd-input { -webkit-appearance: none; appearance: none; }
.tb-cmd-input::-webkit-search-cancel-button,
.tb-cmd-input::-webkit-search-decoration,
.tb-cmd-input::-webkit-search-results-button,
.tb-cmd-input::-webkit-search-results-decoration { -webkit-appearance: none; display: none; }
.tb-cmd-clear { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
    border: none; border-radius: 7px; background: var(--line-soft); color: var(--text-muted);
    cursor: pointer; flex-shrink: 0; transition: background .12s, color .12s; }
.tb-cmd-clear:hover { background: var(--danger-soft); color: var(--danger); }
.tb-cmd-clear svg { width: 12px; height: 12px; }
/* The shortcut chip. ⌘K / Ctrl K is the one keyboard hint every modern app has taught people to
   read — unlike the ↵ hint this replaces ("I have no idea what is that"). */
/* --text-muted, not --text-faint: faint on the bar's surface is ~2.8:1 and this is real text
   (the style guide's 4.5:1 rule), not a decorative glyph. */
.tb-kbd { flex-shrink: 0; padding: 3px 7px; border-radius: 6px; background: var(--surface);
    border: 1px solid var(--line); font-family: inherit; font-size: 10px; font-weight: 700;
    line-height: 1; color: var(--text-muted); white-space: nowrap; }

/* The panel. One shape serves recents, results, the searching state and the empty state. */
.tb-scrim { position: fixed; inset: 0; z-index: 30; }
.tb-panel { position: absolute; left: 0; right: 0; top: calc(100% + 10px); z-index: 40;
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 16px; overflow: hidden;
    box-shadow: 0 20px 48px rgba(15,23,42,.18), 0 2px 8px rgba(15,23,42,.06);
    animation: tb-pop .14s cubic-bezier(.16,1,.3,1); }
@keyframes tb-pop { from { opacity: 0; transform: translateY(-6px) scale(.985); }
                    to   { opacity: 1; transform: none; } }
.tb-panel-hd { display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 10px 14px 6px; }
.tb-panel-hd-label { font-size: 10px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
    color: var(--text-muted); }
.tb-panel-hd-act { border: none; background: transparent; font-family: inherit; font-size: 11px;
    font-weight: 700; color: var(--brand-text); cursor: pointer; padding: 2px 4px; border-radius: 6px; }
.tb-panel-hd-act:hover { background: var(--brand-soft); }
.tb-panel-list { max-height: 56vh; overflow-y: auto; padding: 4px 6px 6px; }
/* A results GROUP header — a flat list of eight mixed rows makes you read every one to find the
   students. Label + a rule that takes the remaining width. */
.tb-panel-group { display: flex; align-items: center; gap: 8px; padding: 8px 9px 4px; }
.tb-panel-group-label { font-size: 10px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
    color: var(--text-muted); flex-shrink: 0; }
/* --line, not --line-soft: a hairline the eye cannot find is dead markup, and this one carries the
   group's width across the panel. */
.tb-panel-group-rule { flex: 1; height: 1px; background: var(--line); }

/* A row — one anatomy for a hit, a recent term, a quick door and a notification. */
.tb-hit { display: flex; align-items: center; gap: 11px; width: 100%; padding: 8px 9px; border: none;
    border-radius: 11px; background: transparent; text-align: left; text-decoration: none;
    font-family: inherit; cursor: pointer; transition: background .12s; }
.tb-hit:hover, .tb-hit.is-cursor { background: var(--surface-muted); }
/* The keyboard cursor must be louder than hover — it is the thing Enter will open. */
.tb-hit.is-cursor { background: var(--brand-soft); box-shadow: inset 0 0 0 1px var(--brand-line); }
.tb-hit-ico { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
    border-radius: 10px; flex-shrink: 0; }
.tb-hit-ico svg { width: 16px; height: 16px; }
.tb-hit-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.tb-hit-title { font-size: 13px; font-weight: 600; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-hit-sub { font-size: 11px; color: var(--text-muted); margin-top: 1px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-hit-chev { width: 15px; height: 15px; flex-shrink: 0; color: var(--text-faint); opacity: 0;
    transition: opacity .12s; }
.tb-hit:hover .tb-hit-chev, .tb-hit.is-cursor .tb-hit-chev { opacity: 1; }
.tb-hit.is-cursor .tb-hit-chev { color: var(--brand-text); }

.tb-panel-foot { display: flex; align-items: center; justify-content: center; gap: 7px; width: 100%;
    padding: 10px 12px; border: none; border-top: 1px solid var(--line-soft); background: var(--surface-page);
    font-family: inherit; font-size: 12.5px; font-weight: 700; }
.tb-panel-all { color: var(--brand-text); cursor: pointer; transition: background .12s; }
.tb-panel-all:hover { background: var(--brand-soft); }
.tb-panel-all svg { width: 14px; height: 14px; }
/* The key legend — it teaches the panel's own controls, so it sits in the panel. */

.tb-panel-empty { display: flex; flex-direction: column; align-items: center; gap: 4px;
    padding: 26px 18px 24px; text-align: center; }
.tb-panel-empty-ico { display: inline-flex; align-items: center; justify-content: center; width: 40px;
    height: 40px; border-radius: 13px; margin-bottom: 6px; background: var(--surface-muted); color: var(--text-faint); }
.tb-panel-empty-ico svg { width: 19px; height: 19px; }
.tb-panel-empty-title { font-size: 13px; font-weight: 700; color: var(--text); }
.tb-panel-empty-sub { font-size: 11.5px; color: var(--text-muted); }

/* ═══ THE COMMAND PANEL — blocks on top, rows below, one footer ═══
   Every hue in here arrives as --hit-accent (set inline per type) and is MIXED against the theme's
   own surface tokens: a pale wash on paper, a deep tint on the near-black card, the glyph lifted
   toward white on dark so it clears the tint it sits on. Never a literal light-theme hex. */
/* THE PANEL DROPS FROM THE BOX AND KEEPS ITS WIDTH — the box is 760px now, which spells every tab label out
   on one row without the panel having to grow past the control it belongs to. */
.tb-panel-cmd { border-radius: 12px; }

/* THE TABS — the student profile's tab strip: equal cells split by hair dividers, a soft rounded icon
   chip, two lines (the type name over how many answered). The active cell tints in its own colour, its
   chip goes saturated with a white glyph, and a bar underlines it into the rows below. A cell IS the
   scope. Scrolls sideways when many types answer, without a scrollbar. */
/* WRAPS, never scrolls: a tab that has slid out of sight is a type the person cannot see answered, and a
   sideways scrollbar inside a dropdown is nobody's idea of a tab strip. Six kinds plus All take two rows on
   a laptop and one on a desk. */
.tb-tabs { display: flex; align-items: stretch; flex-wrap: wrap; border-bottom: 1px solid var(--line-soft); }
/* A cell GROWS to share the strip but never SHRINKS below its label — a tab that reads "Appl…" has lost
   the one word it exists to say. When the labels together outrun the panel, the strip scrolls instead. */
.tb-tab { display: flex; align-items: center; gap: 7px; flex: 1 0 auto; padding: 7px 9px;
    border: none; border-bottom: 2px solid transparent; background: transparent; text-align: left;
    font-family: inherit; cursor: pointer; transition: background .12s; }
.tb-tab + .tb-tab { border-left: 1px solid var(--line-soft); }
.tb-tab:hover { background: var(--surface-muted); }
.tb-tab.is-on { background: color-mix(in srgb, var(--tab-accent, var(--brand)) 9%, var(--surface));
    border-bottom-color: var(--tab-accent, var(--brand)); }
.theme-dark .tb-tab.is-on { background: color-mix(in srgb, var(--tab-accent, var(--brand)) 18%, var(--surface)); }
/* The chip carries the strip's colour: a tint of its own hue at rest with a VIVID glyph, and when the cell
   is on, the profile tab strip's two-colour gradient with a white glyph and a glow of its own light. */
.tb-tab-chip { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
    border-radius: 8px; flex-shrink: 0;
    background: color-mix(in srgb, var(--tab-accent, var(--brand)) 15%, var(--surface));
    color: var(--tab-accent, var(--brand-text)); transition: background .12s, box-shadow .12s, color .12s; }
.theme-dark .tb-tab-chip { background: color-mix(in srgb, var(--tab-accent, var(--brand)) 30%, var(--surface));
    color: color-mix(in srgb, var(--tab-accent2, var(--tab-accent, var(--brand))) 82%, #fff); }
.tb-tab:hover .tb-tab-chip { background: color-mix(in srgb, var(--tab-accent, var(--brand)) 24%, var(--surface)); }
.theme-dark .tb-tab:hover .tb-tab-chip { background: color-mix(in srgb, var(--tab-accent, var(--brand)) 40%, var(--surface)); }
.tb-tab.is-on .tb-tab-chip, .theme-dark .tb-tab.is-on .tb-tab-chip {
    background: linear-gradient(135deg, var(--tab-accent, var(--brand)), var(--tab-accent2, var(--tab-accent, var(--brand))));
    color: #fff; box-shadow: 0 2px 9px color-mix(in srgb, var(--tab-accent, var(--brand)) 45%, transparent); }
.tb-tab-chip svg { width: 13px; height: 13px; }
.tb-tab-text { display: flex; flex-direction: column; min-width: 0; }
.tb-tab-label { font-size: 11.5px; font-weight: 700; color: var(--text); white-space: nowrap; }
.tb-tab.is-on .tb-tab-label { color: var(--tab-accent, var(--brand-text)); }
.theme-dark .tb-tab.is-on .tb-tab-label { color: color-mix(in srgb, var(--tab-accent, var(--brand)) 62%, #fff); }
.tb-tab-hint { font-size: 10px; color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.tb-tab.is-on .tb-tab-hint { color: color-mix(in srgb, var(--tab-accent, var(--brand)) 70%, var(--text-muted)); }
.theme-dark .tb-tab.is-on .tb-tab-hint { color: color-mix(in srgb, var(--tab-accent, var(--brand)) 45%, var(--text-muted)); }
.tb-tab.is-skel { pointer-events: none; }

/* RECENT TERMS — chips, because a term is a word, not a record. */
.tb-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 14px 10px; }
.tb-chip { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 11px 0 9px; border-radius: 999px;
    border: 1px solid var(--line-soft); background: var(--surface-muted); font-family: inherit; font-size: 12px;
    font-weight: 600; color: var(--text); cursor: pointer; transition: background .12s, border-color .12s; }
.tb-chip svg { width: 13px; height: 13px; color: var(--text-faint); }
.tb-chip:hover { background: var(--brand-soft); border-color: var(--brand-line); color: var(--brand-text); }
.tb-chip:hover svg { color: var(--brand-text); }

/* A SECTION label — the group's name, how many answered, and its own way to the rest. The profile's
   section grammar (uppercase, quiet), not a coloured heading. */
/* No section headers: the tabs above name every type and count it, and each row wears its own kind chip —
   a "STUDENTS 1" bar between rows says both a third time. Rows just flow, like sitting with like. */
.tb-panel-rows { padding: 6px; max-height: 52vh; }

/* A ROW is the Users page's person card: the photo (or initials in the type's colour) as a 40px
   rounded square — never a circle — the name over the supporting line, and a small KIND chip that says
   which sort of record this is. Quiet at rest, a soft fill on hover, the type's wash under the cursor. */
/* ⚠ NOT .tb-row — that is the top bar's own layout row (above). A search row that borrowed the name
   once restyled the whole header: a hand cursor and a hover wash across the entire bar. */
.tb-res { display: flex; align-items: center; gap: 9px; width: 100%; padding: 5px 8px; border: none;
    border-radius: 10px; background: transparent; text-align: left; text-decoration: none;
    font-family: inherit; cursor: pointer; transition: background .1s; }
.tb-res:hover { background: var(--surface-muted); }
.tb-res.is-cursor { background: color-mix(in srgb, var(--hit-accent, var(--brand)) 9%, var(--surface));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--hit-accent, var(--brand)) 30%, var(--surface)); }
.theme-dark .tb-res.is-cursor { background: color-mix(in srgb, var(--hit-accent, var(--brand)) 18%, var(--surface));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--hit-accent, var(--brand)) 40%, var(--surface)); }
.tb-res-face { width: 30px; height: 30px; border-radius: 8px; flex-shrink: 0; object-fit: cover; background: var(--surface-muted); }
.tb-res-ico { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px;
    border-radius: 9px; flex-shrink: 0;
    background: color-mix(in srgb, var(--hit-accent, var(--brand)) 12%, var(--surface));
    color: var(--hit-accent, var(--brand-text)); }
.theme-dark .tb-res-ico { background: color-mix(in srgb, var(--hit-accent, var(--brand)) 26%, var(--surface));
    color: color-mix(in srgb, var(--hit-accent, var(--brand)) 62%, #fff); }
.tb-res-ico svg { width: 14px; height: 14px; }
.tb-res-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.tb-res-title { font-size: 12.5px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The lit token is bold in the type's colour — no chip, the row is the chip. */
.tb-res-title .search-mark { background: transparent; color: var(--hit-accent, var(--brand-text)); padding: 0; border-radius: 0; }
.theme-dark .tb-res-title .search-mark { color: color-mix(in srgb, var(--hit-accent, var(--brand)) 62%, #fff); }
.tb-res-sub { font-size: 11px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* THE KIND CHIP — what sort of record this is, in the type's own colour. The one loud thing on the row. */
.tb-res-kind { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; height: 19px; padding: 0 7px 0 6px;
    border-radius: 999px; font-size: 9.5px; font-weight: 800; letter-spacing: .02em; white-space: nowrap;
    background: color-mix(in srgb, var(--hit-accent, var(--brand)) 11%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--hit-accent, var(--brand)) 26%, var(--surface));
    color: var(--hit-accent, var(--brand-text)); }
.theme-dark .tb-res-kind { background: color-mix(in srgb, var(--hit-accent, var(--brand)) 24%, var(--surface));
    border-color: color-mix(in srgb, var(--hit-accent, var(--brand)) 42%, var(--surface));
    color: color-mix(in srgb, var(--hit-accent, var(--brand)) 62%, #fff); }
.tb-res-kind svg { width: 10px; height: 10px; }
.tb-res-chev { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-faint); opacity: 0; transition: opacity .1s, transform .1s; }
.tb-res:hover .tb-res-chev, .tb-res.is-cursor .tb-res-chev { opacity: 1; transform: translateX(2px); }
.tb-res.is-cursor .tb-res-chev { color: var(--hit-accent, var(--brand-text)); }

/* THE FOOTER — how wide the search looked, and a quiet link to everything. (No key legend: the arrows and
   Enter still work for anyone who reaches for them, but printing a keyboard lesson under every search was
   chrome nobody asked for.) */
.tb-foot { display: flex; align-items: center; gap: 10px; padding: 6px 9px 6px 11px;
    border-top: 1px solid var(--line-soft); background: var(--surface-page); }
.tb-foot-meta { flex: 1; font-size: 11px; color: var(--text-muted); }
.tb-foot-all { display: inline-flex; align-items: center; gap: 5px; padding: 5px 9px; border: none; border-radius: 8px;
    background: transparent; color: var(--brand-text); font-family: inherit; font-size: 12px; font-weight: 700;
    cursor: pointer; transition: background .12s; }
.tb-foot-all:hover { background: var(--brand-soft); }
.tb-foot-all svg { width: 13px; height: 13px; }

/* Shimmer skeletons while the debounced query runs — the tabs and the rows, in their own shapes. */
.tb-skel { display: flex; align-items: center; gap: 9px; min-height: 42px; padding: 5px 8px; }
.tb-skel-ico { width: 24px; height: 24px; border-radius: 8px; flex-shrink: 0; display: block; }
.tb-skel-ico.is-row { width: 30px; height: 30px; border-radius: 8px; }
.tb-skel-lines { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.tb-skel-line { height: 9px; border-radius: 5px; display: block; }
.tb-skel-line.is-sub { opacity: .6; }
.tb-skel-ico, .tb-skel-line { background: linear-gradient(90deg, var(--surface-muted) 25%, var(--line-soft) 50%, var(--surface-muted) 75%);
    background-size: 200% 100%; animation: search-shimmer 1.2s ease-in-out infinite; }
@keyframes search-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ── Right: action tiles ─────────────────────────────────────────────────────── */
/* Quiet at rest, then a SOFT TINT OF THEIR OWN FUNCTION COLOUR on hover — the grid kit's
   .gx-mini-btn recipe, softened for chrome (tinting at rest would make a rainbow of a bar that is
   meant to be calm). */
.tb-act { position: relative; display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border: none; border-radius: 11px; background: transparent;
    color: var(--text-muted); cursor: pointer; flex-shrink: 0;
    transition: background .15s, color .15s; }
.tb-act svg { width: 18px; height: 18px; }
.tb-act:hover { background: var(--surface-muted); color: var(--text); }
.tb-act.is-theme:hover { background: var(--accent-soft); color: var(--accent); }
.tb-act.is-bell:hover { background: var(--brand-soft); color: var(--brand-text); }
.tb-burger { display: inline-flex; }
@media (min-width: 1024px) { .tb-burger { display: none; } }
/* The theme glyph turns as it swaps — a switch you can SEE flip. */
.tb-act-swap { display: inline-flex; animation: tb-swap .32s cubic-bezier(.16,1,.3,1); }
@keyframes tb-swap { from { transform: rotate(-70deg) scale(.7); opacity: .3; } to { transform: none; opacity: 1; } }
/* The bell swings once when a notification ARRIVES — a bell that never moves is furniture. */
.tb-act.is-ringing svg { transform-origin: 50% 4px; animation: tb-ring .9s cubic-bezier(.36,.07,.19,.97); }
@keyframes tb-ring {
    0%, 100% { transform: rotate(0); }
    10% { transform: rotate(15deg); }  25% { transform: rotate(-13deg); }
    40% { transform: rotate(9deg); }   55% { transform: rotate(-6deg); }
    70% { transform: rotate(3deg); }   85% { transform: rotate(-1deg); }
}
.tb-badge { position: absolute; top: 2px; right: 2px; display: inline-flex; align-items: center;
    justify-content: center; min-width: 17px; height: 17px; padding: 0 4px; border-radius: 9999px;
    background: var(--danger); color: #fff; border: 2px solid var(--surface);
    font-size: 9.5px; font-weight: 800; line-height: 1; }

/* ── Right: the org menu (was a raw <select> — browser chrome in a designed bar) ── */
.tb-pop-wrap { position: relative; flex-shrink: 0; }
.tb-org { display: none; align-items: center; gap: 8px; height: 36px; padding: 0 8px 0 6px;
    border: 1px solid var(--line-soft); border-radius: 999px; background: var(--surface-muted);
    font-family: inherit; cursor: pointer; max-width: 190px; transition: background .15s, border-color .15s; }
@media (min-width: 640px) { .tb-org { display: inline-flex; } }
.tb-org:hover { background: var(--surface); border-color: var(--line); }
.tb-org-ico { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
    border-radius: 8px; flex-shrink: 0; background: var(--brand-soft); color: var(--brand-text);
    font-size: 11.5px; font-weight: 800; }
.tb-org-name { font-size: 12.5px; font-weight: 700; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-org-chev { width: 13px; height: 13px; flex-shrink: 0; color: var(--text-faint); }

/* ── Right: the identity pill ────────────────────────────────────────────────── */
/* NO RESTING CONTAINER — the identity is not a chip. It sat in a bordered pill of its own next to the
   action tiles, which wear none, so the one element that is just "who you are" was the loudest thing on
   the bar. Bare at rest, with the same quiet hover fill every other control in the bar uses. */
.tb-id { display: inline-flex; align-items: center; gap: 9px; height: 40px; padding: 0 8px 0 5px;
    border: none; border-radius: 11px; background: transparent;
    font-family: inherit; cursor: pointer; flex-shrink: 0; transition: background .15s; }
.tb-id:hover { background: var(--surface-muted); }
/* ROUNDED SQUARE, never a circle — the app has no round avatars anywhere (StudentAvatar is 8px under
   64px, 16px above, and the radius scale tops out at 11px for small tiles). 9px here matches what the
   bar carried before the rebuild. */
.tb-id-avatar { width: 30px; height: 30px; border-radius: 9px; object-fit: cover; flex-shrink: 0;
    box-shadow: 0 0 0 2px var(--brand-ring); }
.tb-id-text { display: none; flex-direction: column; align-items: flex-start; min-width: 0; }
@media (min-width: 1024px) { .tb-id-text { display: flex; } }
.tb-id-name { font-size: 12.5px; font-weight: 700; color: var(--text); line-height: 1.2;
    max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-id-role { font-size: 10.5px; font-weight: 600; color: var(--brand-text); line-height: 1.2; }
.tb-id-chev { display: none; width: 14px; height: 14px; flex-shrink: 0; color: var(--text-faint); }
@media (min-width: 1024px) { .tb-id-chev { display: block; } }

/* ── Dropdown menus (org · notifications · account) — one shell, one row idiom ── */
.tb-menu { position: absolute; right: 0; top: calc(100% + 10px); z-index: 40; overflow: hidden;
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 16px;
    box-shadow: 0 20px 48px rgba(15,23,42,.18), 0 2px 8px rgba(15,23,42,.06);
    animation: tb-pop .14s cubic-bezier(.16,1,.3,1); }
.tb-menu-org { width: 268px; }
.tb-menu-notif { width: 360px; max-width: calc(100vw - 24px); }
.tb-menu-user { width: 272px; }
.tb-menu-hd { display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 11px 14px 8px; border-bottom: 1px solid var(--line-soft); }
.tb-menu-body { padding: 6px; }
.tb-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 9px; border: none;
    border-radius: 10px; background: transparent; font-family: inherit; font-size: 13px; font-weight: 500;
    color: var(--text); text-align: left; cursor: pointer; transition: background .12s, color .12s; }
.tb-menu-item:hover { background: var(--surface-muted); }
.tb-menu-item.is-on { background: var(--brand-soft); }
.tb-menu-item.is-danger:hover { background: var(--danger-soft); color: var(--danger); }
.tb-menu-ico { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
    border-radius: 9px; flex-shrink: 0; }
.tb-menu-ico svg { width: 15px; height: 15px; }
.tb-menu-item-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.tb-menu-item-title { font-size: 12.5px; font-weight: 700; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-menu-item-sub { font-size: 10.5px; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-menu-tick { width: 15px; height: 15px; flex-shrink: 0; color: var(--brand-text); }
/* The unread count on the account menu's Notifications row — the bell's badge, restated where the door is.
   --info-soft/--info rather than the bell's red: in a menu it is a COUNT you are reading, not an alarm. */
.tb-menu-count { flex-shrink: 0; padding: 1px 8px; border-radius: 9999px; background: var(--info-soft);
    color: var(--info); font-size: 10.5px; font-weight: 800; font-variant-numeric: tabular-nums; }
.tb-menu-sep { display: block; height: 1px; background: var(--line-soft); margin: 5px 3px; }
/* The org menu's rows carry their own list padding (they have no .tb-menu-body wrapper). */
.tb-menu-org .tb-menu-item { margin: 0 6px; width: calc(100% - 12px); }
.tb-menu-org .tb-menu-item:first-of-type { margin-top: 4px; }
.tb-menu-org .tb-menu-item:last-of-type { margin-bottom: 6px; }

/* The account menu's identity card. */
.tb-id-card { display: flex; align-items: center; gap: 11px; padding: 13px 14px;
    border-bottom: 1px solid var(--line-soft); background: var(--surface-page); }
.tb-id-card-avatar { width: 38px; height: 38px; border-radius: 11px; object-fit: cover; flex-shrink: 0; }
.tb-id-card-text { min-width: 0; }
.tb-id-card-name { font-size: 13px; font-weight: 700; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-id-card-mail { font-size: 11px; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-id-card-role { display: inline-block; margin-top: 4px; padding: 2px 8px; border-radius: 9999px;
    background: var(--brand-soft); color: var(--brand-text); font-size: 10px; font-weight: 800;
    letter-spacing: .03em; }

/* A notification row: the typed roundel above, plus an UNREAD RAIL down the leading edge — the state
   belongs on the row's edge, where a scan down the list reads it in one pass. */
.tb-note { position: relative; display: flex; align-items: flex-start; gap: 11px; width: 100%;
    padding: 9px 9px 9px 13px; border: none; border-radius: 11px; background: transparent;
    text-align: left; font-family: inherit; cursor: pointer; transition: background .12s; }
.tb-note:hover { background: var(--surface-muted); }
.tb-note-rail { position: absolute; left: 3px; top: 12px; bottom: 12px; width: 3px; border-radius: 2px;
    background: transparent; }
.tb-note.is-unread .tb-note-rail { background: var(--brand); }
.tb-note.is-unread .tb-hit-title { font-weight: 800; }
.tb-note .tb-hit-title { white-space: normal; overflow: visible; }
.tb-note .tb-hit-sub { white-space: normal; overflow: visible;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.tb-note-when { margin-top: 3px; font-size: 10.5px; font-weight: 600; color: var(--text-muted); }

/* Motion is decoration here — every one of these has a still resting state. */
@media (prefers-reduced-motion: reduce) {
    .tb-panel, .tb-menu, .tb-act-swap, .tb-act.is-ringing svg,
    .tb-skel-ico, .tb-skel-line { animation: none; }
}


/* ── Tabs (detail-lodge tab strip, #347) ────────────────────────────────────────── */
.gx-tab { display:inline-flex; align-items:center; gap:6px; padding:9px 14px; border:none; background:transparent;
    font-family:inherit; font-size:13px; font-weight:600; color: var(--text-muted); cursor:pointer; white-space:nowrap;
    border-bottom:2px solid transparent; margin-bottom:-1px; transition:color .12s, border-color .12s; }
.gx-tab:hover { color: var(--text); }
.gx-tab.active { color:var(--brand); border-bottom-color:var(--brand); }
.gx-tab-badge { font-size:10px; font-weight:700; padding:1px 7px; border-radius:9999px; background:var(--brand-soft); color:var(--brand); }
/* standard related-row layout inside RelatedPanel */
.gx-related-row { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:9px 0;
    border-bottom:1px solid var(--line-soft); font-size:13px; }
.gx-related-row:last-child { border-bottom:none; }
.gx-related-row .lbl { color: var(--text-muted); font-size:12px; }
.gx-related-row .val { color: var(--text); font-weight:600; text-align:right; }

/* bulk-bar select (page-supplied bulk actions, #336) */
.gx-bulk-select { height:28px; padding:0 8px; border:1px solid var(--brand-line); border-radius:7px; background: var(--surface);
    font-family:inherit; font-size:12px; font-weight:600; color: var(--text); cursor:pointer; }

/* Export-format picker (#325) — a no-JS <details> disclosure listing CSV/XLSX/PDF download links */
.export-menu { position: relative; display: inline-block; }
.export-menu-summary { list-style: none; cursor: pointer; user-select: none; }
.export-menu-summary::-webkit-details-marker { display: none; }
.export-menu-chevron { transition: transform .15s ease; opacity: .7; }
.export-menu[open] .export-menu-chevron { transform: rotate(180deg); }
.export-menu-list {
    position: absolute; right: 0; top: calc(100% + 6px); z-index: 30; min-width: 160px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 6px;
    box-shadow: 0 12px 28px -8px rgba(15, 23, 42, .18); display: flex; flex-direction: column; gap: 2px;
}
.export-menu-item {
    display: block; padding: 8px 10px; border-radius: 7px; font-size: 13px; font-weight: 500;
    color: var(--text); text-decoration: none;
}
.export-menu-item:hover { background: var(--line-soft); color: var(--brand); }

/* ── The interview-desk calendar (Day / Work week / Week / Month) ─────────────────────────────
   A framed grid with a header band, weekend tinting, half-hour rules, a live "now" line and
   slot chips with an accent bar + hover lift. Classes (not inline styles) because the chips
   need hover/selection states. */
.desk-toast {
    position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%); z-index: 80;
    display: flex; align-items: center; gap: 10px; max-width: min(92vw, 560px);
    background: var(--danger-soft); border: 1px solid color-mix(in srgb, var(--danger) 32%, var(--surface)); color: var(--danger);
    padding: 10px 14px; border-radius: 12px; font-size: 13px; font-weight: 600;
    box-shadow: 0 12px 30px -8px rgba(15, 23, 42, .25);
    animation: desk-toast-in .18s ease;
}
@keyframes desk-toast-in { from { opacity: 0; transform: translate(-50%, 8px); } }
/* ── The calendar CHROME. Life comes from COLOUR, not from weight (the gx-mini-btn doctrine: a soft
   tint of the control's function colour at rest, filling toward solid on hover). Weights follow the
   style guide's scale — 600 for buttons and labels, 700 for titles — because 700/800 everywhere is
   how the grid header once ended up shouting. ── */
.cal-nav {
    display: inline-flex; align-items: stretch; height: 32px; border: 1px solid var(--line-soft);
    border-radius: 10px; background: var(--surface); overflow: hidden; box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.cal-nav button {
    border: none; background: transparent; font-family: inherit; cursor: pointer; color: var(--text-muted);
    display: inline-flex; align-items: center; justify-content: center; transition: background .12s ease, color .12s ease;
}
.cal-nav button:hover { background: var(--brand-soft); color: var(--brand-text); }
.cal-nav .arrow { width: 32px; }
.cal-nav .arrow svg { width: 15px; height: 15px; }
/* Today is the one ACTION in the nav — it wears a standing brand tint and fills on hover. */
.cal-nav .today {
    padding: 0 13px; font-size: 12px; font-weight: 600; color: var(--brand-text); background: var(--brand-soft);
    border-left: 1px solid var(--line-soft); border-right: 1px solid var(--line-soft);
}
.cal-nav .today:hover { background: var(--brand); color: #fff; }
.cal-label { font-size: 13.5px; font-weight: 700; color: var(--text); letter-spacing: -.01em; white-space: nowrap; }
.cal-seg {
    display: inline-flex; align-items: center; height: 32px; background: var(--surface-muted);
    border: 1px solid var(--line-soft); border-radius: 10px; padding: 2px; gap: 2px;
}
.cal-seg button {
    display: inline-flex; align-items: center; gap: 5px;
    height: 26px; padding: 0 11px; border: none; border-radius: 8px; font-family: inherit; font-size: 11.5px;
    font-weight: 600; cursor: pointer; white-space: nowrap; background: transparent; color: var(--text-muted);
    transition: background .12s ease, color .12s ease, box-shadow .12s ease;
}
.cal-seg button svg { width: 13px; height: 13px; }
.cal-seg button:hover:not(.is-active) { background: var(--surface); color: var(--text); }
.cal-seg button.is-active { background: var(--brand-soft); color: var(--brand-text); box-shadow: inset 0 0 0 1px var(--brand-line); }
/* A legend chip carries a soft tint of the STREAM it toggles (--tone), so the filter row reads as the
   streams themselves rather than a line of grey text. Switched off, it drains to an outline. */
/* THE LEGEND IS A CHIP AT REST, so it is painted by the chip's recipe (2026-08-27). It used to mix its
   tone with `transparent` while the chips mixed with a surface, which is why the pill and the thing it
   names never quite matched. Same numbers, same ground, same colour. */
.cal-legend {
    display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; color: var(--text);
    border: 1px solid color-mix(in srgb, var(--tone, var(--text-faint)) 34%, var(--surface));
    background: color-mix(in srgb, var(--tone, var(--text-faint)) 16%, var(--surface));
    cursor: pointer; font-family: inherit;
    padding: 4px 9px; border-radius: 8px; transition: background .12s ease, border-color .12s ease, opacity .12s ease;
}
.theme-dark .cal-legend {
    background: color-mix(in srgb, var(--tone, var(--text-faint)) 24%, var(--line-soft));
    border-color: color-mix(in srgb, var(--tone, var(--text-faint)) 52%, var(--line));
}
.cal-legend:hover { background: color-mix(in srgb, var(--tone, var(--text-faint)) 34%, var(--surface)); }
.theme-dark .cal-legend:hover { background: color-mix(in srgb, var(--tone, var(--text-faint)) 36%, var(--line-soft)); }
.cal-legend.is-off { opacity: .5; background: transparent; border-color: var(--line-soft); color: var(--text-muted); text-decoration: line-through; }
.cal-select {
    height: 30px; appearance: none; -webkit-appearance: none; border: 1px solid var(--line-soft); border-radius: 10px;
    background-color: var(--surface);
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%2394a3b8" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="m6 9 6 6 6-6"/></svg>');
    background-repeat: no-repeat; background-position: right 9px center; background-size: 12px;
    padding: 0 28px 0 11px; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--text);
    cursor: pointer; box-shadow: 0 1px 2px rgba(15, 23, 42, .04); transition: border-color .12s ease, box-shadow .12s ease;
}
.cal-select:hover { border-color: var(--line); }
.cal-select:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 15%, transparent); }
.cal-frame { border: 1px solid var(--line-soft); border-radius: 14px; overflow: hidden; background: var(--surface); }
/* ── The desk SHELL: rail + calendar fill the viewport via a FLEX HEIGHT CHAIN, not a magic
   chrome constant — the toolbar can wrap to any number of rows and the frame takes exactly
   what remains ("there's an issue with the viewport again"). Only the truly fixed chrome
   (topbar + page paddings) is a constant. Narrow screens fall back to natural flow. ── */
.desk-shell { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; }
@media (min-width: 1024px) {
    .desk-shell { align-items: stretch; height: calc(100dvh - 140px); min-height: 480px; flex-wrap: nowrap; }
    .desk-shell .chase-rail { max-height: none; flex: 1 1 0; min-height: 0; }
    .desk-shell .cal-fill { height: auto; flex: 1 1 0; min-height: 0; }
    /* The %-laid time grid fits its frame BY CONSTRUCTION — never show a vertical bar for it
       (the month view keeps auto: its cells have a real min-height). */
    .desk-shell .cal-fill-clip { overflow-y: hidden; }
}
/* Narrow-screen fallback: a bounded frame (percent layout needs a definite height). */
.cal-fill { height: calc(100dvh - 300px); min-height: 420px; overflow: auto; }
.cal-head { background: var(--surface-muted); border-bottom: 1px solid var(--line-soft); position: sticky; top: 0; z-index: 8; }
.cal-daybtn {
    display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px;
    border: none; border-radius: 999px; font-size: 13px; font-weight: 600; font-family: inherit;
    cursor: pointer; background: transparent; color: var(--text); transition: background .12s ease, color .12s ease;
}
.cal-daybtn:hover { background: var(--brand-soft); color: var(--brand-text); }
.cal-daybtn.is-today { background: var(--brand); color: #fff; font-weight: 700; }
.cal-dayhead {
    border: none; background: transparent; font-family: inherit; cursor: pointer;
    padding: 3px 10px; border-radius: 999px; font-size: 11px; font-weight: 600;
    letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted);
    transition: background .12s ease, color .12s ease;
}
.cal-dayhead:hover { background: var(--brand-soft); color: var(--brand-text); }
.cal-dayhead.is-today { background: var(--brand); color: #fff; }
.slot-pop {
    position: fixed; z-index: 71; width: 318px; background: var(--surface); border: 1px solid var(--line);
    border-radius: 13px; padding: 12px 14px; box-shadow: 0 18px 40px -10px rgba(15, 23, 42, .3);
    animation: slot-pop-in .14s ease;
}
@keyframes slot-pop-in { from { opacity: 0; transform: translateY(5px); } }
.cal-col { position: relative; border-left: 1px solid var(--line-soft); }
.cal-col.is-weekend { background: color-mix(in srgb, var(--surface-muted) 65%, transparent); }
/* TODAY'S COLUMN IS A GROUND, NOT A STATUS (2026-08-27 — "you may need to update the daily shade
   colour"). At 38% brand it was a purple wash strong enough to be read as a status tone, sitting directly
   under the Teams-purple chips and competing with them. Today is already named loudly by the solid brand
   pill in the column head; the column itself only has to feel slightly lifted, so it uses a neutral lift
   that no status can be confused with. */
.cal-col.is-today { background: color-mix(in srgb, var(--text) 5%, transparent); }
.cal-line { position: absolute; left: 0; right: 0; border-top: 1px solid var(--line-soft); opacity: .6; pointer-events: none; }
.cal-line.is-half { border-top-style: dotted; opacity: .35; }
.cal-now { position: absolute; left: 0; right: 0; height: 0; border-top: 2px solid #ef4444; z-index: 4; pointer-events: none; }
.cal-now::before { content: ""; position: absolute; left: -1px; top: -4px; width: 7px; height: 7px; border-radius: 999px; background: #ef4444; }
.cal-strip { position: absolute; left: 0; right: 0; z-index: 1; }
.cal-chip {
    position: absolute; z-index: 2; min-height: 20px; overflow: hidden; text-align: left; padding: 3px 7px 3px 8px; border-radius: 8px;
    cursor: pointer; font-family: inherit; border: 1px solid transparent; border-left-width: 3px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
    transition: box-shadow .12s ease, transform .12s ease;
}
.cal-chip:hover { box-shadow: 0 4px 10px -2px rgba(15, 23, 42, .18); transform: translateY(-1px); z-index: 5; }
.cal-chip.is-selected { box-shadow: 0 0 0 2px var(--brand), 0 4px 10px -2px rgba(15, 23, 42, .15); z-index: 6; }
/* ══ ONE TONE, ONE RECIPE (2026-08-27 — "not the same colour displayed on top"). ══
   Every status wore its own hand-built trio of background / border / ink, and the LEGEND that names those
   same statuses built its colour a third way (`--tone` mixed with `transparent`). So the Teams pill above
   the grid and the Teams chip inside it were three different purples by construction, and no amount of
   nudging one would ever make them agree.

   Now a variant declares only `--tone`, and this one recipe paints from it — the same recipe the legend,
   the year dots and the year chips use. Change the hue in one place and the pill, the chip, the dot and
   the mini-chip all move together, because they are the same arithmetic on the same number.

   Mixing into a SOLID ground (surface, and the line tones on near-black) rather than into `transparent`
   is what makes a chip read identically wherever it sits — including on the tinted today column, which
   used to bleed through a translucent chip and shift its colour. */
.cal-chip {
    background: color-mix(in srgb, var(--tone) 16%, var(--surface));
    border-color: color-mix(in srgb, var(--tone) 34%, var(--surface));
    border-left-color: var(--tone);
    color: color-mix(in srgb, var(--tone) 45%, var(--text));
}
.theme-dark .cal-chip {
    background: color-mix(in srgb, var(--tone) 24%, var(--line-soft));
    border-color: color-mix(in srgb, var(--tone) 52%, var(--line));
}
.cal-chip--open { --tone: #38bdf8; border-style: dashed; border-left-style: solid; }
.cal-chip--requested { --tone: #f59e0b; }
/* Confirmed splits by MODE: Teams wears the Teams brand purple, in person wears teal. */
.cal-chip--confirmed { --tone: #6264a7; }
.cal-chip--inperson { --tone: #0d9488; }
/* Completed interview — a record, not work: no grab cursor (not draggable). */
.cal-chip--done { --tone: var(--ok); cursor: default; opacity: .92; }
.cal-cell { min-height: 104px; border-right: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); padding: 5px; transition: background .12s ease; }
.cal-cell:hover { background: color-mix(in srgb, var(--surface-muted) 80%, transparent); }
/* OUT-OF-MONTH DAYS KEEP THE MONTH'S OWN GROUND. A second background turned the row either side of the
   month into a different-looking week, and beside a term band it read as a third colour on one stretch.
   The muted day NUMBER is the entire cue — the wall-planner convention. (The year view goes further and
   drops those days altogether: see .cal-yearblank.) */
.cal-cell.is-out .cal-daybtn { color: var(--text-muted); }
.cal-cell.is-today { background: color-mix(in srgb, var(--brand-soft) 38%, transparent); }

/* ── TERM BANDS — the stretch a day sits INSIDE (an academic period), painted as the cell's ground so the
   events keep the foreground. `--band` is the stream's accent, set inline by the page; the WEIGHT lives
   here so it can be tuned in one place. It reads two ways: a tinted ground carried at ONE strength across
   the whole stretch, and its NAME once per month. Mixed against --surface rather than transparent so it
   composites the same way on the near-black ramp. */
/* ⚠⚠ ONE PERIOD IS ONE COLOUR. Every banded day gets the SAME fill, whichever card it is being drawn in.
   This started as three fills (in-month, out-of-month, today) and a per-row ribbon, and the result was a
   single semester wearing four different shades: 24–28 Feb read one way inside February's card and another
   inside March's, because there they are "out of month". Out-of-month has no business dividing a term into
   segments; the per-row ribbon went for the same reason — an edge on every week chopped one stretch into
   five blocks. Neither out-of-month background exists any more (month keeps the ground, year drops the day),
   so `.has-band` no longer has to outrank one. */
.cal-cell.has-band,
.cal-yearcell.has-band,
.cal-col.has-band {
    background: color-mix(in srgb, var(--band) 26%, var(--surface));
    box-shadow: none;
}
/* TODAY KEEPS THE BAND'S FILL — it is already unmistakable from its day-number pill, and giving it its own
   background punched a visible hole in the middle of the term (on the near-black ramp the "today" tint is
   DARKER than the band, so the current day read as a gap in the stretch). This rule exists only to outrank
   `.cal-yearcell.is-today`, which sits further down the sheet at the same specificity; without it today
   becomes yet another shade in the year view. */
.cal-cell.has-band.is-today, .cal-yearcell.has-band.is-today, .cal-col.has-band.is-today {
    background: color-mix(in srgb, var(--band) 26%, var(--surface));
}
.cal-cell.has-band:hover { background: color-mix(in srgb, var(--band) 30%, var(--surface)); }
/* (No in-cell band label. It was tried twice — once per week row, then once per month — and both read as
   a chip in the one place a chip means "an event". The ground says there is a term; the legend says which
   stream; the cell's tooltip names it. Only the LIST view labels a band, because it has no cell to tint.) */
.cal-mchip {
    display: flex; align-items: center; gap: 5px; width: 100%; overflow: hidden; padding: 2px 6px;
    margin-bottom: 2px; border: none; border-radius: 6px; cursor: pointer; font-family: inherit;
    font-size: 10.5px; font-weight: 600; line-height: 1.5; background: transparent; color: var(--text);
    transition: background .12s ease, box-shadow .12s ease;
}
.cal-mchip:hover { background: var(--line-soft); }
.cal-mchip.is-selected { box-shadow: 0 0 0 2px var(--brand); }
.cal-mchip .dot { width: 7px; height: 7px; border-radius: 999px; flex-shrink: 0; }
.cal-mchip .dot--open { border: 1.5px dashed #38bdf8; background: var(--info-soft); }
.cal-mchip .dot--requested { background: #f59e0b; }
.cal-mchip .dot--confirmed { background: #6264a7; }
.cal-mchip .dot--inperson { background: #0d9488; }
.cal-mchip .dot--done { background: #059669; }
.cal-mchip .txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-more { display: block; border: none; background: transparent; padding: 1px 6px; font-family: inherit; font-size: 10px; font-weight: 600; color: var(--text-muted); cursor: pointer; border-radius: 6px; }
.cal-more:hover { background: var(--brand-soft); color: var(--brand-text); }
.cal-chip.is-droptarget, .cal-mchip.is-droptarget { box-shadow: inset 0 0 0 2px var(--brand); animation: cal-drop-pulse 1.2s ease-in-out infinite; }
@keyframes cal-drop-pulse { 50% { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--brand) 45%, transparent); } }

/* ── THE COLLEGE SCHEDULE (#583) — the month grid, on top of the shared cal-* kit ──────────
   The month view sizes to its CONTENT (six rows at most): a magic viewport height clipped the
   last week of every month, so `.cal-fill` stays for the day/week time grids — which genuinely
   need a definite height for their %-laid chips — and the month never scrolls at all.
   On a phone the 760px-wide month grid was unreadable, so it becomes an AGENDA: empty days drop
   out and the days that have something stack full width, each headed by its own date. */
.cal-monthframe { position: relative; overflow: auto; }
/* A month chip TINTED with its stream's accent (passed as --tone) instead of a bare dot on white:
   a class, not an inline background, so hover still has somewhere to go. */
.cal-mchip.is-toned { background: color-mix(in srgb, var(--tone) 14%, transparent); }
.cal-mchip.is-toned:hover { background: color-mix(in srgb, var(--tone) 30%, transparent); }
.cal-monthwrap { min-width: 760px; display: flex; flex-direction: column; min-height: 100%; }
.cal-monthgrid { display: grid; grid-template-columns: repeat(7, 1fr); grid-auto-rows: minmax(112px, auto); flex: 1; }
.cal-weekhead { display: grid; grid-template-columns: repeat(7, 1fr); }
/* 600, not 700: small uppercase at 700 SHOUTS — the lesson the grid's header band already learned. */
.cal-wd { text-align: center; padding: 7px 0; font-size: 10.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); }
/* The stacked agenda's per-day heading — desktop reads the column, so it only exists on narrow. */
.cal-daylabel { display: none; font-size: 11.5px; font-weight: 700; color: var(--text); letter-spacing: -.01em; margin-bottom: 4px; }
/* Nothing in the window at all: a quiet note floating over the (still-drawn) grid. */
.cal-emptynote {
    position: absolute; inset: 0; z-index: 6; display: flex; flex-direction: column; align-items: center;
    justify-content: center; gap: 8px; pointer-events: none; text-align: center;
    /* A soft scrim so the empty grid recedes and the message reads as the answer, not an overlap. */
    background: color-mix(in srgb, var(--surface) 74%, transparent);
}
.cal-emptynote .ico {
    display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px;
    border-radius: 13px; background: var(--surface-muted); color: var(--text-faint);
}
.cal-emptynote .msg {
    padding: 5px 14px; border-radius: 9999px; background: var(--surface); border: 1px solid var(--line-soft);
    font-size: 12px; font-weight: 600; color: var(--text-muted); box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
}

/* ── LIST view (FullCalendar's listWeek/listMonth) — a day band, then one row per item. ── */
.cal-list { display: flex; flex-direction: column; }
.cal-listband {
    position: sticky; top: 0; z-index: 5; display: flex; align-items: baseline; gap: 10px;
    padding: 8px 14px; background: var(--surface-page); border-bottom: 1px solid var(--line-soft);
}
.cal-listband .dow { font-size: 12.5px; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
.cal-listband .date { margin-left: auto; font-size: 11.5px; font-weight: 600; color: var(--text-muted); }
/* The TERM the day sits in — context on the band, never a row among the events (the grid views tint the
   cell instead; a reading list has no cell). Tinted from the stream's own accent so it re-skins in dark. */
.cal-listband .term {
    padding: 1px 8px; border-radius: 999px; font-size: 10.5px; font-weight: 700; white-space: nowrap;
    background: color-mix(in srgb, var(--tone) 16%, transparent); color: var(--text-muted);
}
.cal-listband.is-today { background: var(--brand-soft); }
.cal-listband.is-today .dow, .cal-listband.is-today .date { color: var(--brand-text); }
.cal-listrow {
    display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; padding: 9px 14px;
    border: none; border-bottom: 1px solid var(--line-soft); background: transparent; cursor: pointer;
    font-family: inherit; transition: background .12s ease;
}
.cal-listrow:hover { background: color-mix(in srgb, var(--tone) 12%, transparent); }
.cal-listrow .time { flex: 0 0 118px; font-size: 11.5px; font-weight: 600; color: var(--text-muted); }
.cal-listrow .bar { flex: 0 0 4px; height: 18px; border-radius: 2px; background: var(--tone); }
.cal-listrow .title { min-width: 0; font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-listrow .where { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; }
.cal-listrow .src {
    margin-left: auto; flex-shrink: 0; padding: 2px 9px; border-radius: 9999px; font-size: 10.5px; font-weight: 600;
    color: var(--text); background: color-mix(in srgb, var(--tone) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--tone) 30%, transparent);
}

/* ── YEAR view — the twelve months, in one of TWO layouts:
     STACKED  one per row, full width, real chips with their titles (the reading layout)
     GRID     three per row, MINI, each day a row of stream dots (the density layout)          ── */
.cal-year { display: flex; flex-direction: column; }
.cal-yearmonth { border-bottom: 1px solid var(--line-soft); }
.cal-yearmonth:last-child { border-bottom: none; }
.cal-yearbar {
    position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 9px;
    padding: 7px 14px; background: var(--surface-page); border-bottom: 1px solid var(--line-soft);
}
.cal-yearopen { border: none; background: transparent; padding: 0; cursor: pointer; font-family: inherit; }
.cal-yearbar .name { font-size: 12.5px; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
.cal-yearopen:hover .name { color: var(--brand-text); }
.cal-yearbar .n {
    padding: 1px 8px; border-radius: 9999px; background: var(--brand-soft); color: var(--brand-text);
    font-size: 10.5px; font-weight: 600;
}
.cal-yearbar.is-now .name { color: var(--brand-text); }
.cal-yeargrid { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal-yearcell { min-height: 62px; padding: 4px 5px; border-right: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); }
/* The neighbour month's days are NOT DRAWN here — twelve months on one screen means every spillover day
   is also a real cell in the card next door, so it appeared twice, in two shades. The blank holds the
   week's column so the columns stay under their weekday headings; it draws nothing at all. */
.cal-yearblank { min-height: 62px; }
.cal-yearcell.is-today { background: color-mix(in srgb, var(--brand-soft) 45%, transparent); }
.cal-yearnum {
    border: none; background: transparent; padding: 0; cursor: pointer; font-family: inherit;
    font-size: 10px; font-weight: 600; color: var(--text-muted);
}
.cal-yearnum:hover { color: var(--brand-text); }
.cal-yearnum.is-today { color: var(--brand-text); font-weight: 700; }
.cal-ychip {
    display: flex; align-items: center; gap: 4px; width: 100%; overflow: hidden; text-align: left; margin-top: 2px;
    padding: 1px 5px; border: none; border-radius: 5px; cursor: pointer; font-family: inherit;
    font-size: 9.5px; font-weight: 600; color: var(--text);
    background: color-mix(in srgb, var(--tone) 16%, transparent); transition: background .12s ease;
}
.cal-ychip:hover { background: color-mix(in srgb, var(--tone) 32%, transparent); }
.cal-ychip .txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* MINI (three per row): a day is too narrow for a title, so the streams read as DOTS. */
/* align-items:start — a 6-week month must not stretch its 5-week neighbours into a blank trailing row. */
.cal-year.is-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; padding: 12px; align-items: start; }
.cal-year.is-grid .cal-yearmonth { border: 1px solid var(--line-soft); border-radius: 12px; overflow: hidden; }
.cal-year.is-grid .cal-yearbar { position: static; padding: 6px 10px; }
.cal-year.is-grid .cal-yearbar .name { font-size: 11.5px; }
.cal-year.is-grid .cal-wd { font-size: 9px; padding: 4px 0; letter-spacing: .03em; }
.cal-year.is-grid .cal-yearcell { min-height: 40px; padding: 3px 2px; text-align: center; }
.cal-year.is-grid .cal-yearblank { min-height: 40px; }
.cal-year.is-grid .cal-yearnum { font-size: 9.5px; }
.cal-ydots { display: flex; flex-wrap: wrap; justify-content: center; gap: 2px; margin-top: 2px; }
.cal-ydot { width: 5px; height: 5px; border-radius: 999px; background: var(--tone); }
.cal-ymore { font-size: 8px; font-weight: 600; color: var(--text-faint); line-height: 5px; }
@media (max-width: 1100px) { .cal-year.is-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

@media (max-width: 700px) {
    .cal-monthwrap { min-width: 0; }
    .cal-listrow { flex-wrap: wrap; gap: 6px 10px; }
    .cal-listrow .time { flex: 0 0 auto; }
    .cal-listrow .src { margin-left: auto; }
    .cal-listrow .where { width: 100%; }
    .cal-year.is-grid { grid-template-columns: 1fr; padding: 8px; }
    .cal-monthgrid { display: block; grid-auto-rows: auto; }
    .cal-weekhead { display: none; }
    .cal-daylabel { display: block; }
    .cal-cell { min-height: 0; padding: 10px 12px; border-right: none; }
    .cal-cell.is-empty { display: none; }
    .cal-cell .cal-daybtn { display: none; }                      /* the date is in the heading now */
    .cal-cell .cal-mchip { font-size: 12px; padding: 5px 8px; }
    .cal-monthframe { min-height: 240px; }
    /* The range segment spans the row rather than hugging one edge — the grid page bar's phone rule. */
    .cal-pagebar .cal-seg { width: 100%; }
    .cal-pagebar .cal-seg button { flex: 1 1 0; }
}
.chase-rail {
    display: flex; flex-direction: column; gap: 8px;
    max-height: calc(100vh - 252px); min-height: 200px; overflow-y: auto; padding: 2px 6px 2px 2px;
    border-radius: 10px; transition: outline-color .12s ease;
}
.chase-rail.is-dropzone { outline: 2px dashed var(--brand); outline-offset: 3px; }
.chase-rail-head { font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint); }
/* ── Admissions dashboard ─────────────────────────────────────────────────── */
.dash-tile {
    display: flex; align-items: center; gap: 10px; padding: 14px 16px; text-decoration: none;
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 14px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
    transition: box-shadow .12s ease, transform .12s ease, border-color .12s ease;
}
.dash-tile:hover { box-shadow: 0 6px 14px -4px rgba(15, 23, 42, .16); transform: translateY(-1px); border-color: var(--line); }
.dash-roundel { width: 32px; height: 32px; border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.dash-num { font-size: 19px; font-weight: 800; color: var(--text); line-height: 1.1; }
.dash-cap { font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.dash-person {
    display: inline-flex; align-items: center; gap: 7px; padding: 5px 10px 5px 6px; text-decoration: none;
    border: 1px solid var(--line-soft); border-radius: 999px; background: var(--surface);
    transition: border-color .12s ease, box-shadow .12s ease;
}
.dash-person:hover { border-color: var(--line); box-shadow: 0 2px 6px rgba(15, 23, 42, .1); }
.card-menu {
    position: absolute; right: 0; top: calc(100% + 6px); z-index: 65; min-width: 230px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 6px;
    box-shadow: 0 14px 32px -8px rgba(15, 23, 42, .25);
    display: flex; flex-direction: column; gap: 2px;
}
.card-menu button {
    display: block; width: 100%; text-align: left; padding: 7px 10px; border: none; border-radius: 8px;
    background: transparent; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--text); cursor: pointer;
}
.card-menu button:hover { background: var(--line-soft); }
.card-menu .card-menu-line { height: 1px; background: var(--line-soft); margin: 4px 6px; }
.card-menu p { padding: 5px 10px 3px; font-size: 10.5px; line-height: 1.45; color: var(--text-faint); }
/* ── The interview desk run-sheet — one band per booked interview, built in the APPLICATION-HERO
   language ("it should look a little bit like the hero on the application page"): a real grid that
   USES the width — when · who · labelled fact columns · actions — instead of a thin strip with a
   hole in the middle. Deliberately NO hover effects. ── */
.runsheet-card {
    position: relative; overflow: hidden;
    display: grid;
    grid-template-columns: auto auto minmax(260px, 1.5fr) minmax(0, 1.4fr) auto;
    align-items: center; gap: 14px 20px;
    background: var(--surface); border: 1px solid var(--line-soft);
    border-radius: 16px; padding: 14px 18px 14px 22px; margin-bottom: 10px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
}
/* The TIME decorates the band ("the time should actually be the photo... decorate the block with
   the time differently"): a mode-coloured edge stripe + a kicker line, instead of a tile stealing
   the lead slot from the applicant's face. */
.runsheet-card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px; }
.runsheet-card.is-teams::before { background: linear-gradient(180deg, #6264a7, #8083c9); }
.runsheet-card.is-person::before { background: linear-gradient(180deg, #0d9488, #2dd4bf); }
.runsheet-card.is-done { opacity: .78; }
.runsheet-card.is-overdue { border-color: color-mix(in srgb, var(--danger) 32%, var(--surface)); background: linear-gradient(90deg, var(--danger-soft), var(--surface) 26%); }
/* Below ~1280px the fact columns drop to their own full-width row; below ~840px the actions follow. */
@media (max-width: 1279px) {
    .runsheet-card { grid-template-columns: auto auto minmax(180px, 1fr) auto; }
    .rs-grid { grid-column: 1 / -1; padding-top: 2px; }
}
@media (max-width: 839px) {
    .runsheet-card { grid-template-columns: auto auto 1fr; }
    .rs-actions { grid-column: 1 / -1; border-left: none; padding-left: 0; padding-top: 10px; border-top: 1px solid var(--line-soft); }
}
/* The time keeps the left, as its OWN BLOCK ("keep the time on the left, but on a separate
   block"): a tinted panel flush with the card's edges, divided from the rest by a rule — not a
   floating tile competing with the applicant's face. */
.rs-when {
    align-self: stretch; min-width: 108px;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    margin: -14px 0 -14px -22px; padding: 10px 16px 10px 20px;
    border-right: 1px solid var(--line-soft);
}
/* Same fix as .cal-chip--confirmed, and now it matters more: the run sheet is the LIST view (2026-08-26),
   not a page someone visits rarely. #f5f5fd was a white panel on a near-black card. */
.rs-when.is-teams { background: color-mix(in srgb, #6264a7 12%, var(--surface)); }
.theme-dark .rs-when.is-teams { background: color-mix(in srgb, #6264a7 22%, var(--line-soft)); }
.rs-when.is-person { background: color-mix(in srgb, #0d9488 12%, var(--surface)); }
.theme-dark .rs-when.is-person { background: color-mix(in srgb, #0d9488 20%, var(--line-soft)); }
.rs-when b { font-size: 19px; font-weight: 800; letter-spacing: -.01em; line-height: 1; }
/* The big time reads AGAINST its own panel, so the ink mixes toward the page's text rather than sitting
   at a fixed dark purple that vanishes on a dark ground. */
.rs-when.is-teams b { color: color-mix(in srgb, #6264a7 42%, var(--text)); }
.rs-when.is-person b { color: color-mix(in srgb, #0d9488 42%, var(--text)); }
.rs-when span { font-size: 9.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .09em; opacity: .75; }
.rs-when i { font-style: normal; font-size: 10.5px; font-weight: 700; color: var(--text-muted); }
/* The FACE leads the band — plain, no ring: StudentAvatar renders a ROUNDED SQUARE for a real
   photo, so a circular disc behind it just pokes out at the corners ("remove that circle"). The
   interview's colour is already carried by the edge stripe and the time panel. */
.rs-face { flex-shrink: 0; display: flex; }
.rs-ident { display: flex; align-items: center; gap: 13px; min-width: 0; }
.runsheet-mode {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 1px 8px; border-radius: 999px; font-size: 10.5px; font-weight: 800;
}
.runsheet-mode.is-teams { background: color-mix(in srgb, #6264a7 12%, var(--surface)); border: 1px solid color-mix(in srgb, #6264a7 32%, var(--surface)); color: color-mix(in srgb, #6264a7 42%, var(--text)); }
.theme-dark .runsheet-mode.is-teams { background: color-mix(in srgb, #6264a7 22%, var(--line-soft)); border-color: color-mix(in srgb, #6264a7 50%, var(--line)); }
.runsheet-mode.is-person { background: color-mix(in srgb, #0d9488 12%, var(--surface)); border: 1px solid color-mix(in srgb, #0d9488 32%, var(--surface)); color: color-mix(in srgb, #0d9488 42%, var(--text)); }
.theme-dark .runsheet-mode.is-person { background: color-mix(in srgb, #0d9488 20%, var(--line-soft)); border-color: color-mix(in srgb, #0d9488 50%, var(--line)); }
/* The labelled fact columns — the hero's Personal/Contact/Academic pattern, sized to fill. */
.rs-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(134px, 1fr)); gap: 10px 20px; min-width: 0; align-items: start; }
.rs-lab {
    font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    color: var(--text-faint); margin-bottom: 4px;
}
.rs-val { font-size: 12.5px; color: var(--text); display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rs-val b { font-weight: 700; }
.rs-val .rs-dim { color: var(--text-muted); }
.rs-val .rs-line { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rs-actions {
    display: flex; align-items: center; gap: 6px;
    align-self: stretch; padding-left: 20px; border-left: 1px solid var(--line-soft);
}
/* ── The INTERVIEW SCREEN (full revamp): click-first capture. Segments to score, ticks for
   questions, chips for picks — typing is the last resort. Small controls, the record page's
   design language, autosave instead of a save button. ── */
.iv-grid { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 16px; align-items: start; }
@media (max-width: 1150px) { .iv-grid { grid-template-columns: 1fr; } }

.iv-save {
    display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
    font-size: 11px; font-weight: 700; padding: 3px 11px; border-radius: 999px;
    border: 1px solid var(--line-soft); color: var(--text-muted); background: var(--surface);
}
/* Token families, not the hand-picked hexes they started as — the save pill is chrome, so it
   re-derives on the tenant brand and on dark like everything else (#581/#590). */
.iv-save.is-dirty { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 40%, var(--surface)); color: var(--warn); }
.iv-save.is-saved { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 40%, var(--surface)); color: var(--ok-strong); }
.iv-save.is-error { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 40%, var(--surface)); color: var(--danger); }

/* One scored category: label left, five click segments right. */
.iv-cat { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px dashed var(--line-soft); }
.iv-cat:last-child { border-bottom: none; }
/* The scores pack into columns ("too much blank space on the mid of screen… I meant the score block"):
   label + segments stay one line, but rows flow into as many ~420px columns as the card fits, so the
   dead middle run disappears and ten impressions land in 3-4 short rows instead of ten tall ones. */
.iv-cats { display: grid; grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); column-gap: 34px; row-gap: 0; align-items: center; }
.iv-cats .iv-cat, .iv-cats .iv-cat:last-child { border-bottom: 1px dashed var(--line-soft); } /* uniform dashes — a grid has no single "last row" cell */
.iv-cat-label { flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 600; color: var(--text); }
.iv-cat-label i { display: block; font-style: normal; font-size: 10.5px; font-weight: 500; color: var(--text-faint); }
.iv-seg { display: flex; gap: 4px; flex-shrink: 0; }
.iv-seg button {
    width: 30px; height: 26px; border-radius: 7px; border: 1px solid var(--line-soft); background: var(--surface);
    font-family: inherit; font-size: 11px; font-weight: 700; color: var(--text-faint);
    cursor: pointer; padding: 0; transition: background .1s ease, color .1s ease, border-color .1s ease;
}
.iv-seg button:hover { border-color: var(--brand); color: var(--brand); }
.iv-seg button.is-on { background: var(--brand); border-color: var(--brand); color: #fff; }
.iv-clear { border: none; background: transparent; color: var(--text-faint); font-size: 13px; cursor: pointer; padding: 0 2px; flex-shrink: 0; }
.iv-clear:hover { color: var(--danger); }

/* One prepared question: a big tick, the text, the hoped-for answer as a chip, a whisper note. */
.iv-q { display: flex; align-items: flex-start; gap: 10px; padding: 9px 12px; border: 1px solid var(--line-soft); border-radius: 12px; background: var(--surface); }
.iv-q.is-ticked { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 32%, var(--surface)); }
.iv-tick {
    width: 30px; height: 30px; border-radius: 9px; border: 2px solid var(--line); background: var(--surface);
    color: transparent; font-size: 15px; font-weight: 800; cursor: pointer; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center; padding: 0; transition: all .12s ease;
}
.iv-q.is-ticked .iv-tick { background: #059669; border-color: var(--ok); color: #fff; }
.iv-q-text { font-size: 13px; font-weight: 600; color: var(--text); }
.iv-q-exp {
    display: inline-flex; align-items: center; gap: 4px; margin-top: 2px;
    font-size: 10.5px; font-weight: 700; padding: 1px 8px; border-radius: 999px;
    background: var(--brand-soft); border: 1px solid var(--brand-line); color: var(--brand-text);
}
.iv-q-note {
    width: 100%; height: 26px; margin-top: 5px; padding: 0 8px; border: 1px solid transparent; border-radius: 7px;
    background: transparent; font-family: inherit; font-size: 11.5px; color: var(--text-muted);
}
.iv-q-note:hover, .iv-q-note:focus { border-color: var(--line-soft); background: var(--surface); outline: none; }
.iv-q-remove { margin-left: auto; border: none; background: transparent; color: var(--text-faint); cursor: pointer; font-size: 13px; padding: 2px 4px; flex-shrink: 0; display: inline-flex; align-items: center; }
.iv-q-remove:hover { color: var(--danger); }
.iv-q-remove svg { width: 13px; height: 13px; }

/* One-tap hint chips on a question ("just hints/keywords") — tap when you hear it. */
.iv-hint {
    height: 22px; padding: 0 9px; border-radius: 999px; border: 1px dashed var(--line); background: var(--surface);
    font-family: inherit; font-size: 10.5px; font-weight: 600; color: var(--text-muted); cursor: pointer;
    transition: background .1s ease, color .1s ease, border-color .1s ease;
}
.iv-hint:hover { border-color: var(--ok); color: var(--ok-strong); }
.iv-hint.is-hit { background: var(--ok-soft); border: 1px solid color-mix(in srgb, var(--ok) 32%, var(--surface)); color: var(--ok-strong); }

/* Quiet labelled text lines in the rail ("fix this mess" — text, not chip soup). */
.iv-lines { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: var(--text); }
.iv-lines b { font-weight: 700; }
.iv-k { color: var(--text-faint); font-weight: 600; margin-right: 6px; }
.iv-dim { color: var(--text-muted); }

/* The Default-questions BOARD: one column per topic, drag cards between/within columns. */
.qbb-board { display: flex; gap: 12px; align-items: flex-start; overflow-x: auto; padding-bottom: 12px; }
.qbb-col {
    flex: 0 0 262px; width: 262px; background: var(--surface); border: 1px solid var(--line-soft);
    border-radius: 14px; box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
}
.qbb-col.is-dragover { border-color: var(--brand); background: #fafaff; }
.qbb-col-new { border-style: dashed; background: transparent; box-shadow: none; padding: 8px; }
.qbb-col-head { padding: 10px 10px 8px; border-radius: 14px 14px 0 0; }
.qbb-name {
    flex: 1; min-width: 0; border: 1px solid transparent; border-radius: 7px; background: transparent;
    font-family: inherit; font-size: 13px; font-weight: 800; color: var(--text); height: 26px; padding: 0 6px;
}
.qbb-name:hover, .qbb-name:focus { border-color: var(--line-soft); background: var(--surface); outline: none; }
.qbb-name:focus-visible { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.qbb-cards { display: flex; flex-direction: column; gap: 7px; padding: 4px 10px 10px; min-height: 60px; }
.qbb-card {
    border: 1px solid var(--line-soft); border-radius: 11px; background: var(--surface); padding: 8px 9px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.qbb-card.is-dragover { border-top: 3px solid var(--brand); }
.qbb-grip { cursor: grab; color: var(--text-faint); font-size: 14px; line-height: 1.3; flex-shrink: 0; user-select: none; }
.qbb-grip:active { cursor: grabbing; }
.qbb-text {
    width: 100%; border: 1px solid transparent; border-radius: 7px; background: transparent; resize: none;
    font-family: inherit; font-size: 12.5px; font-weight: 600; color: var(--text); padding: 2px 5px; line-height: 1.35;
    /* A long question AUTO-GROWS instead of sprouting a scrollbar ("bug when the question is long"):
       field-sizing sizes to content (Chromium/Edge); the scrollbar is hidden everywhere else. */
    field-sizing: content; min-height: 2.6em; overflow-y: auto; scrollbar-width: none;
}
.qbb-text::-webkit-scrollbar { display: none; }
.qbb-text:hover, .qbb-text:focus { border-color: var(--line-soft); background: var(--surface); outline: none; }
.qbb-hint-add {
    height: 20px; width: 74px; border: 1px dashed var(--line); border-radius: 999px; background: transparent;
    font-family: inherit; font-size: 10px; color: var(--text-muted); padding: 0 8px;
}
.qbb-hint-add:focus { outline: none; border-color: var(--brand); }
.qbb-add {
    height: 30px; border: 1px dashed var(--line); border-radius: 9px; background: transparent; width: 100%;
    font-family: inherit; font-size: 12px; color: var(--text-muted); padding: 0 10px;
}
.qbb-add:focus { outline: none; border-color: var(--brand); background: var(--surface); }

/* ── THE tag chip (every surface that wears a tag: the board, the application hero, grid cells,
   board cards, the interview screens). ONE recipe — callers set `--tag` + geometry via
   TagShapes.ChipVars and keep only their own size inline.

   The three colours are MIXED AGAINST THE TOKENS, never composited with an alpha suffix: the old
   `background:@colour+"1a"; color:@colour` put the raw tag colour on a 10% tint of itself, which
   fails 4.5:1 on 50 of the board's 65 swatches in light — and on 56 of them in dark, where the
   alpha composites over `--surface: #1e293b` so the fill goes dark AND the text with it (the slate
   `#334155` as a tag landed at 1.37:1 — all but invisible). Mixing toward `--text` instead flips
   the text light on dark automatically, the same trick `--ok-strong` plays. Ratios are solved, not
   eyeballed: 40% tag hue in the text over a 14% fill clears 4.5:1 for every swatch in BOTH themes
   (worst case 6.19 light on `#0bbc76`, 7.18 dark on `#8c1830`). Re-solve both themes across the
   whole palette before raising the fill or the hue — or before growing the palette again.

   DARK MIXES AGAINST THE LINE TOKENS, NOT THE SURFACE (re-solved 2026-08-24 for the near-black
   ramp). Mixing toward the surface only separates a chip when the surface is at the far end of the
   lightness scale from the tag: on white it always was, on a #17171b card it is not. Against the
   near-black surface the chip BODY landed at 1.05–1.26:1 (53 of 65 swatches under 1.20) and the
   EDGE at 1.20–2.17 — and the whole DEEP row (`#8c1830`, `#045183`, … hues that are themselves
   near-black) went to 1.05 body / 1.20 edge, i.e. a chip with no chip: floating text. Mixing into
   `--line-soft`/`--line` instead — the surface already lifted one and two steps, the tones dark
   borders and hover fills use anyway — puts a FLOOR under both: body 1.19–1.66, edge 1.47–3.16
   (never dimmer than a plain `--line` border), text-on-fill 6.31–7.18. The text mix is unchanged in
   both themes. */
/* THE TONE OVERRIDES (--tag-fill/-ink/-edge, and their -d twins for dark) come from TagTones, which
   solves each one per tag colour — CSS cannot, since color-mix is a fixed ratio and there is no
   "lighten until legible". A SOFT type emits none of them and these color-mix fallbacks stand, which
   is why every tag that predates tones looks exactly as it did. */
.tera-tag {
    display: inline-flex; align-items: center; border-width: 1px; white-space: nowrap;
    --tag: var(--text-muted);
    background: var(--tag-fill, color-mix(in srgb, var(--tag) 14%, var(--surface)));
    color: var(--tag-ink, color-mix(in srgb, var(--tag) 40%, var(--text)));
    border-color: var(--tag-edge, color-mix(in srgb, var(--tag) 40%, var(--surface)));
}
.theme-dark .tera-tag {
    background: var(--tag-fill-d, color-mix(in srgb, var(--tag) 22%, var(--line-soft)));
    color: var(--tag-ink-d, color-mix(in srgb, var(--tag) 40%, var(--text)));
    border-color: var(--tag-edge-d, color-mix(in srgb, var(--tag) 50%, var(--line)));
}
/* Archived reads as RETIRED without touching contrast — any opacity dim drops the worst pair to
   2.4:1 (dark), so the cue is the archive glyph + a struck label, never a faded chip. */
.tera-tag.is-archived .tera-tag-label { text-decoration: line-through; text-decoration-thickness: 1px; }
.tera-tag svg { width: 11px; height: 11px; flex-shrink: 0; }

/* ── Tags board (/tags) — the interview board's language without its fixed kanban columns: type
   BLOCKS flow in masonry columns, each holding its tags as chips. Click a chip = next palette
   colour; drag onto another block = change type; onto another chip = merge (confirmed inline). */
/* ONE GROUP PER ROW, alphabetical (his call). Nothing floats and nothing has to be packed: each
   group is a full-width band under the page header, so the header and the bands read as one stack
   rather than a hero card hovering over a mosaic. (It replaced a CSS `columns:` masonry, which is a
   text flow — it filled column 1 top-to-bottom before column 2, so blocks read downward, their tops
   never lined up, and the new-group placeholder landed wherever the flow dumped it.) */
.tagb-board { display: flex; flex-direction: column; gap: 10px; }
.tagb-block {
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 14px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
}
/* Full width means the name must not stretch across the band, but a FIXED width is just as wrong —
   it parked the count in dead space mid-band, the same distance out whether the group was "Cohort"
   or "Support needs". field-sizing shrinks the field to its text so the count sits WITH the name
   (Chromium; elsewhere it falls back to the default input width, which is merely consistent). */
.tagb-head .qbb-name { flex: 0 1 auto; field-sizing: content; min-width: 80px; max-width: 260px; }
.tagb-head-label { font-size: 13px; font-weight: 800; color: var(--text-muted); padding: 0 6px; }
.tagb-head-tools { display: flex; align-items: center; gap: 7px; margin-left: auto; flex-shrink: 0; }
/* The new group is a band like any other, just empty and waiting for its name. */
.tagb-block-new { border-style: dashed; box-shadow: none; padding: 6px 12px; }
.tagb-block-new .qbb-name { flex: 1 1 auto; }
/* --brand-soft, not a hand-picked near-white: the hex read as a light wash the dark theme had to
   undo with an override, and the token already carries both themes' value. */
.tagb-block.is-dragover { border-color: var(--brand); background: var(--brand-soft); }
.tagb-head { display: flex; align-items: center; gap: 7px; padding: 10px 12px 4px; }
.tagb-count { font-size: 10.5px; font-weight: 800; color: var(--text-faint); flex-shrink: 0; }
.tagb-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 12px 12px; }
.tagb-chip {
    gap: 5px; height: 24px; padding: 0 10px;
    font-size: 11.5px; font-weight: 600; cursor: pointer; user-select: none;
}
/* Every chip action is keyboard-reachable, so every chip shows where focus is. */
.tagb-chip:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 2px;
}
.tagb-chip.is-dragover { outline: 2px dashed var(--brand); outline-offset: 1px; }
/* The SORT target: the gap between two chips. Zero-width at rest so the row looks untouched, it
   opens into a real target once a chip from this band is in flight — drop in a gap to sort, drop ON
   a chip to merge. One gesture, no modifier, identical on a phone. */
.tagb-slot { width: 0; height: 24px; flex-shrink: 0; border-radius: 999px; transition: width .12s ease, background .12s ease; }
.tagb-slot.is-live { width: 10px; background: var(--line-soft); }
.tagb-slot.is-live.is-over { width: 20px; background: var(--brand); }

/* A merge rewrites every tagged record — it announces itself as a warning, with buttons sized for
   a band rather than the full-height pair that dwarfed the row they sat in. */
.tagb-warn {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 12px 8px;
    padding: 7px 10px; border-radius: 10px;
    background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 40%, var(--surface));
}
.tagb-warn svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--warn); }
.tagb-warn-text { flex: 1 1 240px; min-width: 0; font-size: 11.5px; line-height: 1.45; color: var(--text); }
.tagb-warn-go, .tagb-warn-no {
    height: 24px; padding: 0 11px; border-radius: 7px; cursor: pointer; flex-shrink: 0;
    font-family: inherit; font-size: 11px; font-weight: 700;
}
.tagb-warn-go { border: 1px solid var(--warn); background: var(--warn); color: var(--surface); }
.tagb-warn-go:hover { filter: brightness(.94); }
.tagb-warn-go:disabled { opacity: .5; cursor: default; }
.tagb-warn-no { border: 1px solid var(--line); background: var(--surface); color: var(--text); }
.tagb-warn-no:hover { background: var(--line-soft); }
/* Group GEOMETRY — chips render their shape INLINE from TagShapes (radius-only: never clip-path,
   the popover nests INSIDE the chip and would be clipped). The .tagb-shape-* classes below style
   only the mini silhouette/sample buttons, at their own scale. */
.tagb-rename { border: none; background: transparent; outline: none; font-family: inherit; font-size: 11.5px; font-weight: 600; color: inherit; width: 90px; padding: 0; }
/* The chip's swatch grid — hues across, shades down; the current colour wears the ring.
   The kit popover recipe (position:FIXED at 9999 over a 9998 backdrop, placed from a measured
   rect by dgGetRectById): an absolute panel anchored left:0 inside the chip can't flip, so a chip
   in the board's rightmost masonry column threw its 225px grid off-screen. */
.tagb-pop {
    position: fixed; left: var(--pop-left); top: var(--pop-top); z-index: 9999; cursor: default;
    display: grid; grid-template-columns: repeat(13, 16px); gap: 3px; padding: 8px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, .16);
}
.tagb-chip .tagb-swatch {
    width: 16px; height: 16px; border-radius: 5px; border: 2px solid transparent;
    padding: 0; font-size: 0; opacity: 1; cursor: pointer; transition: transform .08s ease;
}
.tagb-chip .tagb-swatch:hover { transform: scale(1.2); }
.tagb-chip .tagb-swatch.is-current { border-color: var(--text); }
/* The last swatch row is the excel "standard colours" band — extra hues, slightly set apart. */
.tagb-chip .tagb-swatch-extra { margin-top: 4px; }
/* The tag's actions, below the grid (shown on the chip CLICK — hover-revealed buttons annoyed him). */
.tagb-actions {
    grid-column: 1 / -1; display: flex; align-items: center; gap: 4px;
    margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line-soft);
}
.tagb-chip .tagb-action {
    height: 22px; padding: 0 8px; border: none; border-radius: 7px; background: transparent;
    font-family: inherit; font-size: 10.5px; font-weight: 600; color: var(--text-muted);
    cursor: pointer; white-space: nowrap;
}
.tagb-chip .tagb-action:hover { background: var(--line-soft); color: var(--text); }
.tagb-chip .tagb-action svg { width: 12px; height: 12px; }
.tagb-chip .tagb-action-danger { margin-left: auto; }
.tagb-chip .tagb-action-danger:hover { background: var(--danger-soft); color: var(--danger); }
/* Retype and merge are DRAG gestures on the board; these are their keyboard equivalents, so the
   whole chip vocabulary is reachable without a mouse. */
.tagb-moves { grid-column: 1 / -1; display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.tagb-chip .tagb-move {
    flex: 1 1 0; min-width: 0; height: 26px; padding: 0 6px; border: 1px solid var(--line);
    border-radius: 7px; background: var(--surface); font-family: inherit; font-size: 10.5px;
    font-weight: 600; color: var(--text-muted); cursor: pointer;
}
.tagb-chip .tagb-move:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
/* The type's shape picker. The bare silhouette read as a stray EMPTY CHIP — as if a tag had lost
   its label — so it sits in a bordered well that says "control", with the silhouette as its value. */
/* ── The TONE well + picker, the shape well's sibling. Same well geometry, but the samples are REAL
   CHIPS (.tera-tag carrying the group's own colour and shape through TagShapes.ChipVars), because a
   tone is a colour treatment and nothing but the actual chip shows what you are choosing. The rows
   stack rather than tile: four options with names, not a grid of silhouettes. */
.tagb-tone-well {
    display: inline-flex; align-items: center; gap: 5px; height: 20px; padding: 0 5px 0 6px;
    border: 1px solid var(--line); border-radius: 7px; background: var(--surface);
    cursor: pointer; flex-shrink: 0;
}
.tagb-tone-well:hover { background: var(--line-soft); }
.tagb-tone-well:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.tagb-tone-well::after {
    content: ''; width: 0; height: 0; flex-shrink: 0;
    border-left: 3px solid transparent; border-right: 3px solid transparent; border-top: 3px solid var(--text-faint);
}
/* The well's preview is a chip with no label — a swatch of the treatment itself. */
.tagb-tone-dot { width: 18px; height: 12px; padding: 0; flex-shrink: 0; pointer-events: none; }
.tagb-tone-pop {
    position: absolute; right: 0; top: calc(100% + 4px); z-index: 9999; cursor: default;
    display: flex; flex-direction: column; gap: 2px; padding: 6px; min-width: 190px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, .16);
}
.tagb-tone-row {
    display: flex; align-items: center; gap: 9px; padding: 5px 7px; border: none; border-radius: 8px;
    background: transparent; cursor: pointer; font-family: inherit; text-align: left; width: 100%;
}
.tagb-tone-row:hover { background: var(--line-soft); }
.tagb-tone-row.is-current { background: var(--brand-soft); }
.tagb-tone-row:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.tagb-tone-sample { font-size: 11px; font-weight: 600; padding: 1px 9px; pointer-events: none; max-width: 110px; overflow: hidden; text-overflow: ellipsis; }
.tagb-tone-name { font-size: 11px; font-weight: 600; color: var(--text-muted); text-transform: capitalize; margin-left: auto; }
.tagb-tone-row.is-current .tagb-tone-name { color: var(--brand-text); }

.tagb-shape-well {
    display: inline-flex; align-items: center; gap: 5px; height: 20px; padding: 0 5px 0 6px;
    border: 1px solid var(--line); border-radius: 7px; background: var(--surface);
    cursor: pointer; flex-shrink: 0;
}
.tagb-shape-well:hover { background: var(--line-soft); }
.tagb-shape-well::after {
    content: ''; width: 0; height: 0; flex-shrink: 0;
    border-left: 3px solid transparent; border-right: 3px solid transparent; border-top: 3px solid var(--text-faint);
}
/* The well is a real button, so it shows focus like every other chip control on the board. */
.tagb-shape-well:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.tagb-shape-btn {
    width: 18px; height: 11px; border: 1.5px solid var(--text-muted); background: var(--surface-muted);
    padding: 0; flex-shrink: 0; pointer-events: none;
}
/* RIGHT-ALIGNED to the well, not measured from JS. The well lives in .tagb-head-tools, which is
   margin-left:auto — so it is always hard against the band's right edge, and a panel that opens
   leftward from it can never leave the screen. The measured-rect recipe belongs to .tagb-pop, whose
   anchor is a chip and can sit anywhere along the band; here it was only a way to fail: the panel
   reads `left: var(--pop-left)`, and a custom property that is missing or invalid makes the whole
   declaration invalid at computed-value time, so `left` falls back to `auto` and a FIXED panel
   renders at its static position — flush right, with its last column 36px off-screen. */
.tagb-shape-pop {
    position: absolute; right: 0; top: calc(100% + 4px); z-index: 9999; cursor: default;
    display: grid; grid-template-columns: repeat(5, 34px); gap: 7px; padding: 8px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, .16);
}
.tagb-shape-pop .tagb-shape-sample {
    width: 34px; height: 18px; border: 2px solid var(--text-muted); background: var(--surface-muted);
    cursor: pointer; padding: 0;
}
.tagb-shape-pop .tagb-shape-sample:hover { border-color: var(--text); }
.tagb-shape-pop .tagb-shape-sample.is-current { border-color: var(--brand); background: var(--brand-soft); }
/* Silhouettes default to the pill and scale each geometry down to their own size. The stitched
   treatment is inline border-style on the button (it combines with every shape). */
.tagb-shape-btn, .tagb-shape-sample { border-radius: 999px; }
.tagb-shape-btn.tagb-shape-rounded, .tagb-shape-sample.tagb-shape-rounded { border-radius: 5px; }
.tagb-shape-btn.tagb-shape-sharp, .tagb-shape-sample.tagb-shape-sharp { border-radius: 1px; }
.tagb-shape-btn.tagb-shape-leaf, .tagb-shape-sample.tagb-shape-leaf { border-radius: 8px 1px 8px 1px; }
.tagb-shape-btn.tagb-shape-petal, .tagb-shape-sample.tagb-shape-petal { border-radius: 1px 8px 1px 8px; }
.tagb-shape-btn.tagb-shape-flag, .tagb-shape-sample.tagb-shape-flag { border-radius: 1px 999px 999px 1px; }
.tagb-shape-btn.tagb-shape-sail, .tagb-shape-sample.tagb-shape-sail { border-radius: 999px 1px 1px 999px; }
.tagb-shape-btn.tagb-shape-drop, .tagb-shape-sample.tagb-shape-drop { border-radius: 999px 999px 999px 1px; }
.tagb-shape-btn.tagb-shape-swoosh, .tagb-shape-sample.tagb-shape-swoosh { border-radius: 999px 2px 999px 2px; }
.tagb-backdrop { position: fixed; inset: 0; z-index: 9998; background: transparent; }
.tagb-add {
    height: 24px; width: 132px; border: 1px dashed var(--line); border-radius: 999px; background: transparent;
    font-family: inherit; font-size: 11px; color: var(--text-muted); padding: 0 10px;
}
.tagb-add:focus { outline: none; border-color: var(--brand); background: var(--surface); }
/* How many tags of this group a record may wear. Was a shouty all-caps pill ("ONE PER RECORD" /
   "MANY PER RECORD") that announced the DEFAULT case louder than the group's own name; it is a
   setting, so it reads as a switch — the .iv-seg score segments at block-header scale. */
/* The standard segmented idiom: a recessed track with the live segment RAISED on it. Deliberately
   NOT brand-filled — a filled segment made the setting the most saturated thing on the page, which
   is how the caps pill went wrong in the first place. The control is quiet until you look at it. */
.tagb-seg {
    display: inline-flex; flex-shrink: 0; padding: 2px; gap: 2px;
    background: var(--surface-muted); border: 1px solid var(--line-soft); border-radius: 8px;
}
.tagb-seg button {
    border: none; background: transparent; padding: 0 7px; height: 17px; border-radius: 5px; cursor: pointer;
    font-family: inherit; font-size: 10.5px; font-weight: 600; color: var(--text-faint);
    transition: background .1s ease, color .1s ease;
}
.tagb-seg button:hover { color: var(--text); }
/* The ON side is COLOURED, and each side gets its OWN hue (2026-08-24, "different colours for each so
   the status can be easily identifiable. The white background is bad"). It used to raise a white pill
   on a near-white track, so the two states differed only by WHICH END the pill sat at — unreadable at a
   glance down a board of a dozen groups. Violet for one-per-record, green for many: far apart in hue,
   and neither carries a good/bad reading the way amber or red would. Both use the soft-fill + semantic-
   text pair, so they are legible in either theme. */
.tagb-seg button.is-on { font-weight: 700; }
.tagb-seg button.is-one.is-on {
    background: var(--accent-soft); color: var(--accent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 32%, var(--surface));
}
.tagb-seg button.is-many.is-on {
    background: var(--ok-soft); color: var(--ok-strong);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ok) 32%, var(--surface));
}
@media (prefers-reduced-motion: reduce) {
    .tagb-chip .tagb-swatch { transition: none; }
    .tagb-chip .tagb-swatch:hover { transform: none; }
}

/* (legacy rail styles kept: qb-arrows reused by the board) */
.qb-shell { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 14px; align-items: start; max-width: 980px; }
@media (max-width: 900px) { .qb-shell { grid-template-columns: 1fr; } }
.qb-topic {
    display: flex; align-items: center; gap: 8px; padding: 7px 9px; border: 1px solid transparent;
    border-radius: 10px; font-size: 12.5px; cursor: pointer;
}
.qb-topic:hover { background: var(--surface-muted); }
.qb-arrows { display: inline-flex; flex-direction: column; gap: 1px; }
.qb-arrows button {
    width: 18px; height: 14px; border: none; background: transparent; color: var(--text-faint);
    font-size: 8px; line-height: 1; cursor: pointer; padding: 0;
}
.qb-arrows button:hover:not(:disabled) { color: var(--brand); }
.qb-arrows button:disabled { opacity: .25; cursor: default; }

/* The conversation flow: pending board cards, the full-width current question, answered cards. */
.ivq-pend {
    max-width: 100%; padding: 6px 11px; border: 1px solid var(--line-soft); border-radius: 9px;
    background: var(--surface); font-family: inherit; font-size: 12px; font-weight: 600; color: var(--text);
    cursor: pointer; text-align: left; transition: box-shadow .1s ease;
}
.ivq-pend:hover { box-shadow: 0 2px 6px rgba(15, 23, 42, .1); }
.iv-current { border: 1.5px solid; border-radius: 14px; padding: 14px 16px; margin-top: 12px; }
.iv-hint-xl {
    height: 34px; padding: 0 16px; border-radius: 10px; border: 1.5px solid var(--line); background: var(--surface);
    font-family: inherit; font-size: 13px; font-weight: 700; color: var(--text-muted); cursor: pointer;
    transition: background .1s ease, color .1s ease, border-color .1s ease;
}
.iv-hint-xl:hover { border-color: var(--text-muted); color: var(--text); }
.ivq-ans {
    max-width: 100%; padding: 6px 10px; border: 1px solid; border-radius: 9px; background: var(--surface);
    cursor: pointer; transition: box-shadow .1s ease;
}
.ivq-ans:hover { box-shadow: 0 2px 6px rgba(15, 23, 42, .1); }

/* The topic flow: the focused topic block, bigger hint chips, up-next pills, answered rows. */
.iv-topic { border: 1.5px solid color-mix(in srgb, var(--accent) 32%, var(--surface)); border-radius: 14px; padding: 12px 14px; background: var(--accent-soft); }
.iv-hint-lg { height: 28px; padding: 0 13px; font-size: 12px; }
.iv-next {
    height: 24px; padding: 0 10px; border-radius: 999px; border: 1px solid var(--line-soft); background: var(--surface);
    font-family: inherit; font-size: 11px; font-weight: 600; color: var(--text-muted); cursor: pointer;
}
.iv-next:hover { border-color: var(--accent); color: var(--accent); }
.iv-done {
    display: flex; align-items: center; gap: 7px; padding: 5px 10px; border: 1px solid var(--line-soft);
    border-radius: 9px; background: var(--surface); font-size: 12px; cursor: pointer;
}
.iv-done:hover { border-color: color-mix(in srgb, var(--ok) 32%, var(--surface)); background: var(--ok-soft); }

/* Tiny neutral fact chip in the rail. */
.iv-mini {
    display: inline-flex; align-items: center; font-size: 10.5px; font-weight: 600; padding: 1px 8px;
    border-radius: 999px; background: var(--surface-muted); border: 1px solid var(--line-soft); color: var(--text-muted);
}

/* Chip picks — one tap answers (financial position, job, bed, parking, yes/no). */
.iv-pick { display: flex; flex-wrap: wrap; gap: 5px; }
.iv-pick button {
    height: 26px; padding: 0 11px; border-radius: 999px; border: 1px solid var(--line-soft); background: var(--surface);
    font-family: inherit; font-size: 11.5px; font-weight: 600; color: var(--text-muted); cursor: pointer;
    transition: background .1s ease, color .1s ease, border-color .1s ease;
}
.iv-pick button:hover { border-color: var(--brand); color: var(--brand); }
.iv-pick button.is-on { background: var(--brand); border-color: var(--brand); color: #fff; }

/* The general assessment — the paper form's five verdicts as big click cards. */
.iv-ass { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.iv-ass button {
    border: 1.5px solid var(--line-soft); border-radius: 13px; background: var(--surface);
    padding: 10px 12px; text-align: left; cursor: pointer; font-family: inherit; transition: all .12s ease;
}
.iv-ass button b { display: block; font-size: 12.5px; font-weight: 800; color: var(--text); }
.iv-ass button span { font-size: 10.5px; color: var(--text-muted); }

/* Rail facts — the run-sheet's labelled-column language, stacked. */
.iv-fact { padding: 8px 0; border-bottom: 1px dashed var(--line-soft); }
.iv-fact:first-child { padding-top: 0; }
.iv-fact:last-child { border-bottom: none; padding-bottom: 0; }

.chase-filter {
    width: 100%; height: 28px; border: 1px solid var(--line-soft); border-radius: 9px; background: var(--surface);
    padding: 0 24px 0 9px; font-family: inherit; font-size: 12px; color: var(--text);
    transition: border-color .12s ease, box-shadow .12s ease;
}
.chase-filter:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 15%, transparent); }
.chase-filter::placeholder { color: var(--text-faint); }
.chase-card {
    display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; width: 100%; flex-shrink: 0;
    border: 1px solid var(--line-soft); border-left: 3px solid color-mix(in srgb, var(--danger) 32%, var(--surface)); border-radius: 11px; background: var(--surface);
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05); cursor: grab;
    transition: box-shadow .12s ease, transform .12s ease, border-color .12s ease;
}
.chase-card.has-suggestion { border-left-color: #f59e0b; }
.chase-card.is-priority { border-left-color: #f59e0b; background: linear-gradient(90deg, var(--warn-soft), var(--surface) 45%); box-shadow: 0 1px 3px rgba(180, 83, 9, .14); }
.chase-card:hover { box-shadow: 0 4px 10px -2px rgba(15, 23, 42, .15); transform: translateY(-1px); border-color: var(--line); }
.chase-card:active { cursor: grabbing; }
.chase-card.is-armed { border-color: var(--brand); box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand) 30%, transparent), 0 4px 10px -2px rgba(15, 23, 42, .15); }
.chase-card .grip { color: var(--text-faint); font-size: 11px; letter-spacing: -1px; flex-shrink: 0; }

/* ── THE ACADEMIC-YEAR CALENDAR (the year page's Periods section) ─────────────────────────────────
   "Is it possible to display this like a simple monthly calendar?" — one grid per month, each day
   tinted by the period covering it. The periods are EDITED HERE: click a day to open its period,
   drag a period's first/last day to shift the boundary it shares with its neighbour.
   Drag feedback is pure CSS — the container gets .is-dragging on drag start (ONE render) and the
   hovered day lights up via :hover, so a drag across 400 cells costs no re-renders at all. */
.ayc-month-label { font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 4px; }
.ayc-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.ayc-wd { text-align: center; font-size: 8px; font-weight: 700; color: var(--text-faint); }
.ayc-day {
    /* position: relative is LOAD-BEARING — the contract dots and today's dot are absolutely positioned
       inside the cell. Without it they resolve against a distant ancestor and vanish from the grid. */
    position: relative;
    aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
    font-size: 9px; font-weight: 600; border-radius: 4px; cursor: pointer;
    user-select: none; -webkit-user-select: none;
    transition: box-shadow .1s ease, transform .1s ease;
}
.ayc-day.is-empty { font-weight: 400; cursor: default; }
/* TODAY IS A DOT, NOT A RING. It used to be an outline — indistinguishable from the drag handle's
   inset ring at 19px, so a stray "today" on an empty day read as something the user had accidentally
   created and couldn't get rid of ("I have no idea on how that aug 11 got set and stuck"). One mark,
   one meaning: rings mean grabbable, the dot means today. */
.ayc-day.is-today { position: relative; font-weight: 800; padding-bottom: 3px; }
.ayc-day.is-today::after {
    content: ''; position: absolute; left: 50%; bottom: 2px; transform: translateX(-50%); z-index: 1;
    width: 3px; height: 3px; border-radius: 999px; background: var(--text);
}
/* THE OPEN PERIOD IS A BAND, NOT A CAGE ("this is also a little grotesque… can we select the days
   but smoothly?"). A 2px ring around every single day drew ~60 hard boxes for one term, and the
   period's FIRST and LAST day lost theirs entirely — .is-handle sets box-shadow too, and being later
   in the file it won, so the two days that most needed to look included looked excluded.
   Now the selection is its own layer: a soft wash + a rail top and bottom, bleeding 3px sideways over
   the 2px grid gap so neighbouring days MERGE into one continuous bar. Only the ends of a run get a
   cap — at the period's edge, the week's edge, or the month's. box-shadow is left free for the drag
   handles, so an edge day is both clearly selected and still visibly grabbable. */
.ayc-day.is-sel::before {
    content: ''; position: absolute; top: -1px; bottom: -1px; left: -3px; right: -3px;
    background: rgba(79, 70, 229, .12);
    border-top: 1.5px solid var(--brand);
    border-bottom: 1.5px solid var(--brand);
    pointer-events: none;
}
.ayc-day.is-sel-start::before {
    left: -1px; border-left: 1.5px solid var(--brand);
    border-top-left-radius: 7px; border-bottom-left-radius: 7px;
}
.ayc-day.is-sel-end::before {
    right: -1px; border-right: 1.5px solid var(--brand);
    border-top-right-radius: 7px; border-bottom-right-radius: 7px;
}
.ayc-day.is-sel { font-weight: 800; }
/* The numeral rides ABOVE the band — a positioned ::before otherwise paints over the parent's text. */
.ayc-num { position: relative; z-index: 1; }
/* A boundary day is the grab point. AT REST it's barely a mark — a whisper of shade over whatever tint
   the day already has, plus a heavier numeral ("can we make it smooth… it just needs to be slightly
   different, almost unperceptible"). A ring on every edge was 20-odd hard outlines across a year.
   The full ring returns ON HOVER, when you're actually reaching for it. */
.ayc-day.is-handle { cursor: grab; font-weight: 800; box-shadow: inset 0 0 0 999px rgba(15, 23, 42, .05); }
.ayc-day.is-handle:hover { box-shadow: inset 0 0 0 999px rgba(15, 23, 42, .05), inset 0 0 0 1.5px currentColor; }
.ayc-day.is-handle:active { cursor: grabbing; }
/* (No combined handle+today rule any more — the two marks no longer compete for the box-shadow.) */
.ayc-cal.is-dragging .ayc-day { cursor: copy; }
.ayc-cal.is-dragging .ayc-day:hover,
.ayc-cal.is-dragging .ayc-day.is-droptarget { box-shadow: 0 0 0 2px var(--brand); transform: scale(1.15); }
/* The period legend — chips instead of a table ("no need to list them below"): the names stay
   readable and each chip is another door into the same editor. */
.ayc-chip {
    display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 999px;
    border: 1px solid transparent; font-family: inherit; font-size: 11px; font-weight: 600;
    cursor: pointer; transition: box-shadow .1s ease;
}
.ayc-chip:hover { box-shadow: 0 1px 4px rgba(15, 23, 42, .12); }
.ayc-chip.is-selected { box-shadow: 0 0 0 2px var(--brand); }
.ayc-chip .dot { width: 7px; height: 7px; border-radius: 999px; flex-shrink: 0; }
/* Painting a new period across empty days — press, sweep, release. The swept run lights up in the
   Term tint so the range you're about to create is the thing you're looking at. */
.ayc-day.is-painting { background: color-mix(in srgb, var(--info) 34%, var(--surface)) !important; color: var(--info) !important; box-shadow: inset 0 0 0 1px #38bdf8; }
.ayc-cal.is-painting { cursor: crosshair; }
.ayc-cal.is-painting .ayc-day { cursor: crosshair; }
/* CONTRACTS ON THE CALENDAR — a DOT per switched-on contract, in that contract.s own colour, sitting
   along the TOP of the day so it never argues with the period tint below or today.s dot at the bottom.
   Unique colours are the point: a shared bar could say how many contracts covered a day, never WHICH. */
.ayc-dots {
    /* z-index keeps the dots above the selection band's ::before, like the numeral. */
    position: absolute; top: 1.5px; left: 0; right: 0; z-index: 1;
    display: flex; justify-content: center; gap: 1.5px; pointer-events: none;
}
.ayc-dot { width: 3px; height: 3px; border-radius: 999px; }

/* The per-contract calendar switch — hollow when off, filled when its dots are on the grid. */
.ayc-swatch {
    width: 14px; height: 14px; flex-shrink: 0; padding: 0; border-radius: 999px; cursor: pointer;
    border: 2px solid currentColor; background: transparent;
    transition: background .1s ease, transform .1s ease;
}
.ayc-swatch:hover { transform: scale(1.15); }
.ayc-swatch.is-on { background: currentColor; }

/* ── THE RETURNER ROSTER, COMPACT ("can we make it more compact?") ────────────────────────────────
   190 residents is a normal cohort, and a full-height AppButton per row set a ~69px row. These are
   22px text buttons, so a row is ~30px and a screen holds three times as many people. */
.rtr-act {
    display: inline-flex; align-items: center; height: 22px; padding: 0 8px; margin-left: 4px;
    border: 1px solid transparent; border-radius: 7px; background: transparent;
    font-family: inherit; font-size: 11px; font-weight: 600; color: var(--text-muted);
    cursor: pointer; white-space: nowrap; transition: background .1s ease, color .1s ease;
}
.rtr-act:hover { background: var(--surface-muted); color: var(--text); }
.rtr-act.is-primary { background: var(--brand); color: #fff; }
.rtr-act.is-primary:hover { background: var(--brand-strong); color: #fff; }
.rtr-act:disabled { opacity: .5; cursor: default; }
.rtr-row > td { padding-top: 4px; padding-bottom: 4px; }
/* Zebra + hover: 190 identical-looking rows are impossible to track across ("its too flat right now…
   give it some life"). The stripe is ~3% ink — enough for the eye to hold a line, not enough to read
   as a second colour — and the hover tint is what tells you which row your cursor is acting on. */
.rtr-row { transition: background .08s ease; }
.rtr-row.is-alt { background: rgba(15, 23, 42, .028); }
.rtr-row:hover { background: color-mix(in srgb, var(--brand) 7%, transparent); }
.rtr-row > td:first-child { border-top-left-radius: 7px; border-bottom-left-radius: 7px; }
.rtr-row > td:last-child { border-top-right-radius: 7px; border-bottom-right-radius: 7px; }

/* The tally chips double as the filter — lit with a ring in their own colour when active. */
.rtr-chip {
    display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px;
    border: 1px solid; border-radius: 999px; font-family: inherit; font-size: 11px; font-weight: 500;
    cursor: pointer; transition: box-shadow .1s ease, transform .1s ease;
}
.rtr-chip:hover { transform: translateY(-1px); }
.rtr-chip.is-on { font-weight: 700; box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 32%, transparent); }
/* "Someone is here right now" — the applicant's app is beating (ApplicantPresence). A slow pulse, not a
   blink: it should read as alive at a glance and never pull the eye off the work. */
.live-dot {
    width: 6px; height: 6px; border-radius: 999px; background: #10b981; flex-shrink: 0;
    box-shadow: 0 0 0 0 rgba(16, 185, 129, .55); animation: live-pulse 2.4s ease-out infinite;
}
@keyframes live-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, .5); }
    70%  { box-shadow: 0 0 0 5px rgba(16, 185, 129, 0); }
    100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }

/* TAP THE WARNING TO FIX IT — the academic-year page's "expected by emails" / "not priced here"
   rows are buttons: they already name the thing, so tapping fills the name in and puts you on the
   one field still to decide. The cue is quiet until hover, so a shelf of warnings still reads as
   information rather than a wall of calls to action. */
.ay-fix-row { transition: filter .1s ease, box-shadow .1s ease; }
.ay-fix-row:hover { filter: brightness(.97); }
.ay-fix-row:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.ay-fix-cue { opacity: 0; transition: opacity .1s ease; }
.ay-fix-row:hover .ay-fix-cue,
.ay-fix-row:focus-visible .ay-fix-cue { opacity: 1; }

/* ─────────────────────────────────────────────────────────────────────────────
   THE DROPDOWN (.tsel) — 2026-08-14. "I need all select dropboxes to be styled
   and consistent… the dropdown is quite small and items are a lot larger."

   SelectInput is a real listbox now, not a native <select>, because the native
   popup is OS chrome no CSS can reach. Two rules keep it honest:

   1. THE PANEL IS THE CONTROL'S WIDTH, never narrower and never wider.
   2. A ROW IS THE CONTROL'S TYPE. Same font-size, same family — the old native
      list rendered at the OS's size, which is why a 13.5px field opened a 15px
      list and looked like it belonged to another application.
   ───────────────────────────────────────────────────────────────────────────── */
.tsel { position: relative; width: 100%; }

/* The CLOSED control is pixel-identical to .enr-input/.enr-select before it —
   same box, same focus ring — so nothing on any form moves. */
.tsel-btn {
    display: flex; align-items: center; gap: 8px;
    width: 100%; box-sizing: border-box;
    font-family: inherit; font-size: 13.5px; line-height: 1.35; text-align: left;
    color: var(--text);
    background: var(--surface-muted);
    border: 1px solid var(--line); border-radius: 10px;
    padding: 10px 12px;
    cursor: pointer; outline: none;
    transition: border-color .15s, box-shadow .15s, background .15s;
}
.tsel-btn:hover:not(:focus-visible):not(:disabled) { border-color: var(--text-faint); }
.tsel-btn:focus-visible,
.tsel.is-open .tsel-btn { background-color: var(--surface); border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.tsel-btn:disabled { cursor: default; opacity: .6; }

.tsel-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tsel-value.is-placeholder { color: var(--text-muted); }

.tsel-chev { width: 12px; height: 12px; flex-shrink: 0; color: var(--text-faint); transition: transform .18s ease, color .15s; }
.tsel-btn:hover .tsel-chev { color: var(--text-muted); }
.tsel.is-open .tsel-chev { transform: rotate(180deg); color: var(--brand); }

/* Outside-click dismiss, with no JS and no focus trap. */
.tsel-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; }

.tsel-panel.is-closed { display: none; }
.tsel-panel {
    position: absolute; z-index: 61; top: calc(100% + 5px); left: 0;
    width: 100%; box-sizing: border-box;          /* RULE 1 — the control's width */
    margin: 0; padding: 5px; list-style: none;
    background: var(--surface);
    border: 1px solid var(--line); border-radius: 12px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, .14), 0 2px 6px rgba(15, 23, 42, .06);
    max-height: 288px; overflow-y: auto; overscroll-behavior: contain;
    animation: tsel-in .14s ease both;
}
@keyframes tsel-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .tsel-panel { animation: none; } }

/* A row: the control's own type, a fixed tick gutter so labels never shift as the
   selection moves, and enough height to be a comfortable target without dwarfing
   the field it belongs to. */
.tsel-opt {
    display: flex; align-items: center; gap: 8px;
    padding: 7px 9px; border-radius: 8px;
    font-size: 13.5px; line-height: 1.35;         /* RULE 2 — matches .tsel-btn */
    color: var(--text); cursor: pointer; outline: none;
    transition: background .1s, color .1s;
}
.tsel-opt:hover { background: var(--line-soft); }
.tsel-opt:focus-visible { background: var(--line-soft); box-shadow: inset 0 0 0 2px var(--brand-ring); }
.tsel-opt.is-selected { color: var(--brand); font-weight: 600; }
.tsel-opt.is-placeholder { color: var(--text-muted); }

.tsel-tick { width: 13px; height: 13px; flex-shrink: 0; color: var(--brand); opacity: 0; }
.tsel-opt.is-selected .tsel-tick { opacity: 1; }

.tsel-opt-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* COMPACT — the dense record editors ("something smaller like in the stage one
   edit form"): the control shrinks and the LIST SHRINKS WITH IT, which is the
   whole point of rule 2. */
.compact-form .tsel-btn { padding: 6px 10px; font-size: 12.5px; border-radius: 8px; }
.compact-form .tsel-opt { padding: 5px 8px; font-size: 12.5px; }
.compact-form .tsel-panel { border-radius: 10px; }

/* Inside a validated field, the control wears the error state its siblings do. */
.enr-field.has-error .tsel-btn { border-color: var(--danger); }
.enr-field.has-error .tsel-btn:focus-visible { box-shadow: 0 0 0 3px rgba(220, 38, 38, .16); }

/* SIZE VARIANTS — the raw <select>s these replaced carried their own inline heights
   (24/28/30/32px) because a toolbar chip is not a form field. Same idea, but the
   LIST follows the control (rule 2), which inline styles could never make it do. */
.tsel-sm .tsel-btn { padding: 5px 9px; font-size: 12.5px; border-radius: 8px; }
.tsel-sm .tsel-opt { padding: 5px 8px; font-size: 12.5px; }
.tsel-sm .tsel-panel { border-radius: 10px; }

.tsel-xs .tsel-btn { padding: 3px 8px; font-size: 11px; border-radius: 7px; background: var(--surface); color: var(--text-muted); }
.tsel-xs .tsel-opt { padding: 4px 7px; font-size: 11px; border-radius: 6px; }
.tsel-xs .tsel-panel { border-radius: 9px; padding: 4px; }
.tsel-xs .tsel-chev { width: 10px; height: 10px; }
.tsel-xs .tsel-tick { width: 11px; height: 11px; }

/* Hugs its content instead of filling the row — for toolbars, where a full-width
   dropdown beside two buttons looks broken. The PANEL still can't be narrower than
   the control, but it may grow past it so long labels stay readable. */
.tsel-auto { width: auto; display: inline-block; }
.tsel-auto .tsel-panel { width: max-content; min-width: 100%; max-width: min(320px, 80vw); }

/* Right-aligned panel — for a control sitting at the end of a row, where a
   left-anchored panel would hang off the edge. */
.tsel-end .tsel-panel { left: auto; right: 0; }

/* ─────────────────────────────────────────────────────────────────────────────
   THE CHOOSER (.chs) — the compact single-choice control. A 28px box reading
   "ROOM · 251" (what it chooses, then what is chosen), and beneath it a menu as
   wide as its longest row, each row carrying an optional fact at its end. Made
   to sit several to a line — the estate walk, a toolbar, a filter band — where
   a full-width labelled form row would stack. Tokens only.
   ───────────────────────────────────────────────────────────────────────────── */
.chs { position: relative; display: inline-block; max-width: 100%; vertical-align: middle; }

.chs-btn {
    display: inline-flex; align-items: center; gap: 8px;
    height: 28px; max-width: 100%; box-sizing: border-box;
    padding: 0 8px 0 10px;
    font-family: inherit; font-size: 12px; line-height: 1.35; text-align: left;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--line); border-radius: 8px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
    cursor: pointer; outline: none;
    transition: border-color .15s, box-shadow .15s, background .15s;
}
.chs-btn:hover:not(:disabled) { border-color: var(--text-faint); background: var(--surface-muted); }
.chs-btn:focus-visible,
.chs.is-open .chs-btn { background: var(--surface); border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.chs-btn:disabled { cursor: default; opacity: .55; }

/* What the box chooses — a small tracked capital beside the value, ruled off from it. The key is a FIXED
   width (--chs-key-w, overridable on a container) so a column of boxes lines its values up. */
.chs { --chs-key-w: 64px; }
.chs-key {
    flex: 0 0 var(--chs-key-w, 64px); width: var(--chs-key-w, 64px); box-sizing: border-box;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    padding-right: 8px; border-right: 1px solid var(--line);
    font-size: 10.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; line-height: 1.35;
    color: var(--text-muted);
}
.chs-ico { width: 13px; height: 13px; flex-shrink: 0; color: var(--text-muted); }
.chs-val { flex: 1; min-width: 0; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chs-val.is-empty { color: var(--text-muted); font-weight: 400; }

.chs-chev { width: 11px; height: 11px; flex-shrink: 0; color: var(--text-faint); transition: transform .18s ease, color .15s; }
.chs-btn:hover .chs-chev { color: var(--text-muted); }
.chs.is-open .chs-chev { transform: rotate(180deg); color: var(--brand); }

/* Outside-click dismiss: a transparent sibling under the menu. */
.chs-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; }

.chs-menu.is-closed { display: none; }
.chs-menu {
    position: absolute; z-index: 61; top: calc(100% + 4px); left: 0;
    min-width: 100%; width: max-content; max-width: min(320px, 80vw); box-sizing: border-box;
    margin: 0; padding: 4px; list-style: none;
    background: var(--surface);
    border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, .14), 0 2px 6px rgba(15, 23, 42, .06);
    max-height: 264px; overflow-y: auto; overscroll-behavior: contain;
    animation: chs-in .12s ease both;
}
@keyframes chs-in { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .chs-menu { animation: none; } }

.chs-row {
    display: flex; align-items: center; gap: 10px;
    padding: 6px 8px 6px 9px; border-radius: 7px;
    font-size: 12px; line-height: 1.35; font-weight: 400;
    color: var(--text); cursor: pointer; outline: none;
    transition: background .1s, color .1s;
}
.chs-row:hover, .chs-row:focus-visible { background: var(--line-soft); }
.chs-row.is-selected { background: var(--brand-soft); color: var(--brand); font-weight: 600; }
.chs-row.is-selected:focus-visible { box-shadow: inset 0 0 0 2px var(--brand-ring); }

.chs-row-icon { width: 13px; height: 13px; flex-shrink: 0; color: var(--text-muted); }
.chs-row.is-selected .chs-row-icon { color: var(--brand); }
.chs-row-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chs-row-meta { flex-shrink: 0; font-size: 10.5px; font-weight: 400; color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.chs-row.is-selected .chs-row-meta { color: var(--brand); opacity: .8; }
.chs-row-tick { width: 12px; height: 12px; flex-shrink: 0; color: var(--brand); visibility: hidden; }
.chs-row.is-selected .chs-row-tick { visibility: visible; }

.chs-none { padding: 8px 9px; font-size: 12px; color: var(--text-muted); }

/* Menu anchored to the box's right edge — for a chooser at the end of a line. */
.chs-end .chs-menu { left: auto; right: 0; }

/* One to a line: the box fills its row, so a column of choosers reads as a form. */
.chs-block { display: block; width: 100%; }
.chs-block .chs-btn { display: flex; width: 100%; }

/* A settled fact beside choosers, in the same box: a level with one branch, a room
   with one free bed — read, not chosen. */
.chs-fixed {
    --chs-key-w: 64px;
    display: inline-flex; align-items: center; gap: 8px;
    height: 28px; box-sizing: border-box; padding: 0 10px;
    font-size: 12px; font-weight: 500; line-height: 1.35; color: var(--text);
    background: var(--surface-muted); border: 1px solid var(--line-soft); border-radius: 8px;
    white-space: nowrap;
}
.chs-fixed.chs-block { display: flex; }

/* A typed fact in the fixed box — a date, a number, a line of text: the box is the field. */
label.chs-fixed { cursor: text; }
.chs-fixed input, .chs-fixed textarea {
    flex: 1; min-width: 0; padding: 0; border: 0; outline: none; background: transparent;
    font-family: inherit; font-size: 12px; font-weight: 500; line-height: 1.35; color: var(--text);
}
.chs-fixed input::placeholder { color: var(--text-muted); font-weight: 400; }
.chs-fixed input:disabled { color: var(--text-muted); }
.chs-fixed input[type="date"]::-webkit-calendar-picker-indicator { opacity: .55; cursor: pointer; }
.chs-fixed input[type="file"] { font-size: 11.5px; font-weight: 400; color: var(--text-muted); cursor: pointer; }
.chs-fixed input[type="file"]::file-selector-button {
    height: 20px; margin-right: 8px; padding: 0 8px; border: 1px solid var(--line); border-radius: 6px;
    background: var(--surface); font-family: inherit; font-size: 11px; font-weight: 600; color: var(--text); cursor: pointer;
}
/* Several typed facts on one line, ruled off from each other. */
.chs-sep { width: 1px; height: 14px; background: var(--line); flex-shrink: 0; }
/* A small clear at the end of the box. */
.chs-x {
    display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; flex-shrink: 0;
    padding: 0; border: 0; border-radius: 5px; background: transparent; color: var(--text-muted); cursor: pointer;
}
.chs-x:hover { background: var(--line-soft); color: var(--text); }
.chs-x svg { width: 11px; height: 11px; }
/* A box that wraps — a row of toggle pills, a row of worn chips. */
.chs-fixed.chs-wrap { height: auto; min-height: 28px; flex-wrap: wrap; row-gap: 4px; padding: 4px 10px; white-space: normal; }
.chs-fixed.chs-wrap .chs-key { align-self: center; }
.chs-pill {
    display: inline-flex; align-items: center; gap: 4px; height: 20px; padding: 0 8px; border-radius: 999px;
    border: 1px solid var(--line); background: var(--surface);
    font-family: inherit; font-size: 11px; font-weight: 600; color: var(--text-muted); cursor: pointer; white-space: nowrap;
}
.chs-pill:hover:not(:disabled) { border-color: var(--text-faint); color: var(--text); }
.chs-pill.is-on { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-text); }
.chs-pill svg { width: 10px; height: 10px; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE SECTION ROW AND THE SECTION EDITOR — what sits under a YearSection head.

   A row (.ysr) is one dense line: a crest or an icon, two lines of text, the row's own acts on the
   right. An earlier row dims (.is-past); a row that needs someone's attention tints warn (.is-warn);
   the row a switch is on tints in the section's accent (.is-on, --ysr-accent). The editor (.yse) is
   a dashed panel in the section's colours (--yse-accent / --yse-tint) at the head of the list, built
   from chooser boxes, with Cancel and the one filled act at its foot.
   ═══════════════════════════════════════════════════════════════════════════ */
.ys-list { display: flex; flex-direction: column; gap: 5px; }
.ys-quiet { margin: 0; font-size: 12px; color: var(--text-muted); }
.ys-error { margin: 0 0 9px; font-size: 11.5px; color: var(--danger); }
.ys-facts { --chs-key-w: 84px; }
.ys-text { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--text); white-space: pre-wrap; }

.ysr {
    display: flex; align-items: center; gap: 8px; padding: 7px 10px;
    border: 1px solid var(--line-soft); border-radius: 9px; background: var(--surface);
}
.ysr.is-past { opacity: .62; }
/* A RESULT SET on the Academics tab: the period as a tile on the left, its subjects fanning out on the
   right — one row each. Stacks on a narrow screen. */
.rs { display: grid; grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); gap: 8px 18px; align-items: start; padding: 4px 0; }
.rs + .rs { border-top: 1px dashed var(--line); padding-top: 12px; margin-top: 4px; }
.rs-tile { display: flex; flex-direction: column; gap: 10px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface-muted); position: sticky; top: 12px; }
.rs-tile-top { display: flex; align-items: center; gap: 10px; min-width: 0; }
.rs-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.rs-tile .yse { margin: 0; grid-template-columns: 1fr; min-width: 0; }   /* the tile is narrow: one field per line, nothing wider than the tile */
.rs-tile .yse > * { min-width: 0; max-width: 100%; }
.rs-subjects { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
@media (max-width: 720px) {
    .rs { grid-template-columns: 1fr; }
    .rs-tile { position: static; }
}
.ysr.is-warn { border-color: color-mix(in srgb, var(--warn) 32%, var(--surface)); background: var(--warn-soft); }
.ysr.is-on {
    border-color: color-mix(in srgb, var(--ysr-accent, var(--brand)) 45%, var(--surface));
    background: color-mix(in srgb, var(--ysr-accent, var(--brand)) 6%, var(--surface));
}
.ysr-stack { display: flex; flex-direction: column; gap: 4px; padding: 7px 10px; border: 1px solid var(--line-soft); border-radius: 9px; background: var(--surface); }
.ysr-stack.is-on {
    border-color: color-mix(in srgb, var(--ysr-accent, var(--brand)) 45%, var(--surface));
    background: color-mix(in srgb, var(--ysr-accent, var(--brand)) 6%, var(--surface));
}
.ysr-line { display: flex; align-items: center; gap: 8px; }
.ysr-body { min-width: 0; flex: 1; }
.ysr-title { font-size: 12.5px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ysr-title .is-soft { font-weight: 400; color: var(--text-muted); }
.ysr-sub { font-size: 10.5px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ysr-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-top: 3px; }
.ysr-crest {
    min-width: 26px; height: 20px; padding: 0 5px; flex-shrink: 0; border-radius: 6px;
    background: var(--surface-muted); border: 1px solid var(--line-soft);
    display: flex; align-items: center; justify-content: center;
    font-size: 10px; font-weight: 800; color: var(--text-muted); font-variant-numeric: tabular-nums;
}
.ysr-ico { width: 13px; height: 13px; flex-shrink: 0; color: var(--text-muted); }
.ysr-chip {
    flex-shrink: 0; padding: 2px 7px; border-radius: 5px; white-space: nowrap;
    font-size: 9px; font-weight: 800; letter-spacing: .04em; font-variant-numeric: tabular-nums;
    background: var(--surface-muted); color: var(--text-muted);
}
.ysr-chip:has(.ysr-chip-x) { padding-right: 3px; display: inline-flex; align-items: center; gap: 4px; }
.ysr-chip-x {
    display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; border-radius: 4px;
    border: 0; background: transparent; color: inherit; opacity: .55; font-size: 12px; line-height: 1; cursor: pointer;
}
.ysr-chip-x:hover { opacity: 1; background: color-mix(in srgb, var(--danger) 18%, transparent); color: var(--danger); }
.ysr-chip-x:disabled { opacity: .3; cursor: default; }
.ysr-num { flex-shrink: 0; font-size: 12px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ysr-num.is-big { font-size: 14px; font-weight: 800; }
.ysr-track { position: relative; flex: 1; min-width: 40px; height: 6px; border-radius: 999px; background: var(--surface-muted); }
.ysr-track > span { position: absolute; top: 0; bottom: 0; border-radius: 999px; background: var(--ysr-accent, var(--brand)); opacity: .75; }

/* The row's acts: an icon button, a short text button, a door. */
.ysr-ibtn {
    display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
    padding: 4px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface);
    font-family: inherit; font-size: 11px; font-weight: 700; color: var(--text-muted); cursor: pointer; text-decoration: none;
}
.ysr-ibtn svg { width: 11px; height: 11px; }
.ysr-tbtn {
    display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0; height: 26px; padding: 0 9px;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
    font-family: inherit; font-size: 11px; font-weight: 700; color: var(--text-muted); cursor: pointer; white-space: nowrap; text-decoration: none;
}
.ysr-tbtn svg { width: 11px; height: 11px; }
.ysr-ibtn:hover:not(:disabled), .ysr-tbtn:hover:not(:disabled) { border-color: var(--text-faint); color: var(--text); }
.ysr-ibtn:disabled, .ysr-tbtn:disabled { opacity: .55; cursor: default; }
.ysr-ibtn.is-armed, .ysr-tbtn.is-armed { border-color: color-mix(in srgb, var(--danger) 45%, var(--surface)); background: var(--danger-soft); color: var(--danger); }
.ysr-tbtn.is-warn { border-color: color-mix(in srgb, var(--warn) 32%, var(--surface)); color: var(--warn); }
.ysr-acts { display: flex; flex-wrap: wrap; gap: 6px; }

/* THE EDITOR wears the activities composer's dress: a soft panel, labelled fields in a grid (a label
   above a bordered 30px box), notes and the foot spanning the row, a ghost Cancel beside the one act.
   The chooser boxes and typed-fact boxes keep their markup — inside an editor the key becomes the
   field's label and the value gets the framed box. */
.yse {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 10px 14px; align-items: start;
    padding: 12px 14px; border-radius: 11px;
    border: 1px solid var(--line-soft); background: var(--surface-muted);
}
.yse > .yse-note, .yse > .yse-group, .yse > .yse-foot, .yse > .yse-check, .yse > .chs-wrap, .yse > p, .yse > .yse-wide { grid-column: 1 / -1; }
.yse-note { margin: 0; font-size: 11.5px; color: var(--text-muted); }
.yse-note a { color: var(--brand-text); font-weight: 600; }
.yse-group { font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); margin: 4px 0 -2px; }
.yse-check { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--text-muted); cursor: pointer; }
.yse-foot { display: flex; justify-content: flex-end; gap: 6px; margin-top: 2px; }
.yse-cancel {
    height: 30px; padding: 0 12px; border: 1px solid transparent; border-radius: 9px; background: transparent;
    font-family: inherit; font-size: 12px; font-weight: 600; color: var(--text-muted); cursor: pointer;
}
.yse-cancel:hover:not(:disabled) { background: var(--line-soft); color: var(--text); }
.yse .ui-btn { height: 30px; padding: 0 12px; font-size: 12px; border-radius: 9px; }
.yse .ui-btn-primary { background: var(--surface); color: var(--text); border: 1px solid var(--line); box-shadow: none; }
.yse .ui-btn-primary:hover:not(:disabled) { background: var(--line-soft); }

/* Every field is the same two rows: a 16px label, a 3px gap, a 30px box — so labels and boxes line up
   across the grid whatever the field holds. */
/* A typed fact: the key is the label, the input is the framed box beneath it; a settled fact (no input)
   reads as plain text in the box's row. */
.yse .chs-fixed.chs-block {
    display: grid; grid-template-rows: 16px 30px; row-gap: 3px; align-items: center;
    height: auto; padding: 0; background: transparent; border: 0; white-space: normal;
    font-size: 12px; font-weight: 500; color: var(--text);
}
.yse .chs-fixed .chs-key {
    align-self: end; width: auto; padding: 0; border: 0; overflow: visible;
    font-size: 10.5px; font-weight: 600; letter-spacing: 0; text-transform: none; line-height: 16px; color: var(--text-muted);
}
.yse .chs-fixed input, .yse .chs-fixed textarea {
    width: 100%; height: 30px; box-sizing: border-box; padding: 0 8px; margin: 0;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
    font-size: 12px; font-weight: 400; color: var(--text);
}
.yse .chs-fixed input[type="file"] { padding: 4px 6px; }
.yse .chs-fixed textarea { height: auto; min-height: 30px; padding: 6px 8px; }
.yse .chs-fixed .chs-ico { display: none; }
/* A row of pills: the key above, the pills wrapping beneath. */
.yse .chs-fixed.chs-wrap { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 4px; padding: 0; min-height: 0; }
.yse .chs-fixed.chs-wrap .chs-key { flex-basis: 100%; }

/* A chooser: the key sits above the box as its label; the framed box holds the value and the chevron. */
.yse .chs.chs-block .chs-btn {
    position: relative; display: grid; grid-template-columns: auto 1fr auto; grid-template-rows: 16px 30px; row-gap: 3px;
    align-items: center; column-gap: 6px; height: auto; padding: 0; background: transparent; border: 0; box-shadow: none;
}
.yse .chs.chs-block .chs-btn::after {
    content: ""; position: absolute; left: 0; right: 0; top: 19px; height: 30px; box-sizing: border-box;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface); pointer-events: none; z-index: 0;
    transition: border-color .15s, box-shadow .15s;
}
.yse .chs.chs-block .chs-btn:hover:not(:disabled)::after { border-color: var(--text-faint); }
.yse .chs.chs-block .chs-btn:focus-visible::after, .yse .chs.chs-block.is-open .chs-btn::after { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.yse .chs.chs-block .chs-key {
    grid-column: 1 / -1; grid-row: 1; width: auto; padding: 0; border: 0; overflow: visible; align-self: end;
    font-size: 10.5px; font-weight: 600; letter-spacing: 0; text-transform: none; line-height: 16px; color: var(--text-muted);
}
.yse .chs.chs-block .chs-ico { grid-row: 2; margin-left: 8px; position: relative; z-index: 1; }
.yse .chs.chs-block .chs-val { grid-row: 2; padding: 0 8px; position: relative; z-index: 1; font-weight: 400; }
.yse .chs.chs-block .chs-btn > .chs-ico + .chs-val { padding-left: 0; }
.yse .chs.chs-block .chs-chev { grid-row: 2; margin-right: 8px; position: relative; z-index: 1; }
.yse .chs.chs-block .chs-menu { top: calc(100% + 4px); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE YEAR REPORT CARD (.rc) — the student profile's Overview: one card per academic year, the
   printed school report's shape. A crest and the status chip in the head, then ruled sections — course, a subject/mark/grade ledger, residence with its
   contract-days track, the recognition columns, remarks. The year's colour rides in --rc (set
   inline); everything else is tokens.
   ═══════════════════════════════════════════════════════════════════════════ */
.rc-stack { display: flex; flex-direction: column; gap: 20px; }
/* The rail under the stack: a dashed rule carrying the one act on the records — the year before the
   oldest, added on the spot — with a line of copy under it. */
.rc-rail { margin-top: 14px; }
.rc-rail-line { display: flex; align-items: center; gap: 12px; }
.rc-rail-line > span { flex: 1; border-top: 1px dashed var(--line); }
.rc-rail-btn {
    display: inline-flex; align-items: center; gap: 7px; padding: 9px 18px; border-radius: 999px;
    border: 1px solid var(--line); background: var(--surface); color: var(--text);
    font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.rc-rail-btn:hover { border-color: var(--line-strong, var(--line)); background: var(--surface-muted); }
.rc-rail-btn:disabled { opacity: .5; cursor: default; }
.rc-rail-note { margin: 8px 0 0; text-align: center; font-size: 11.5px; color: var(--text-muted); }
.rc-rail-error { margin: 8px 0 0; text-align: center; font-size: 12px; color: var(--danger); }
.rc {
    --rc: var(--brand);
    position: relative; overflow: hidden;
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 16px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
}
/* z-index keeps the stripe above a dimmed head (opacity makes the head its own paint layer). */
.rc::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--rc); z-index: 1; }
.rc.is-past .rc-head { opacity: .86; }

/* The head: crest · the year and its window · the stamp. */
.rc-head {
    display: flex; align-items: center; gap: 12px; padding: 10px 20px 10px 22px;
    border-bottom: 1px solid var(--line-soft);
    background: color-mix(in srgb, var(--rc) 6%, var(--surface));
}
.rc-crest {
    width: 46px; height: 46px; flex-shrink: 0; border-radius: 13px;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--rc) 16%, var(--surface));
    border: 1.5px solid color-mix(in srgb, var(--rc) 55%, var(--surface));
    box-shadow: inset 0 0 0 3px var(--surface);
    color: var(--rc);
}
.rc-crest-yr { font-size: 15px; font-weight: 800; letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.rc-crest-lbl { margin-top: 2px; font-size: 7.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; }
.rc-title { flex: 1; min-width: 0; }
.rc-kicker { font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted); }
.rc-year { font-size: 19px; font-weight: 800; letter-spacing: -.02em; line-height: 1.15; color: var(--text); }
.rc-window { margin-top: 2px; font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The year's standing — the status chip the year rows wear, a size up, in the YEAR'S colour so the head
   reads as one thing; only an exit keeps its semantic colour. */
.rc-stamp {
    flex-shrink: 0; align-self: flex-start; padding: 3px 10px; border-radius: 999px;
    font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; white-space: nowrap;
    background: color-mix(in srgb, var(--rc) 16%, var(--surface)); color: var(--rc);
}
.rc-stamp.is-warn { background: var(--warn-soft); color: var(--warn); }
.rc-stamp.is-danger { background: var(--danger-soft); color: var(--danger); }


/* The body: ruled sections, each with a tracked heading and its one door. */
.rc-body { padding: 4px 20px 14px 24px; }
.rc-sec { padding: 12px 0 11px; border-bottom: 1px dashed var(--line); }
.rc-sec:last-child { border-bottom: 0; padding-bottom: 6px; }
.rc-sec-h { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
.rc-sec-h > svg { width: 13px; height: 13px; flex-shrink: 0; color: var(--rc); }
.rc-sec-t { font-size: 10.5px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted); white-space: nowrap; }
.rc-rule { flex: 1; height: 1px; background: var(--line-soft); }
.rc-door {
    display: inline-flex; align-items: center; gap: 4px; height: 22px; padding: 0 8px; flex-shrink: 0;
    border: 1px solid var(--line); border-radius: 7px; background: var(--surface);
    font-size: 10.5px; font-weight: 700; color: var(--text-muted); text-decoration: none; white-space: nowrap;
}
.rc-door svg { width: 11px; height: 11px; }
.rc-door:hover { border-color: var(--text-faint); color: var(--text); }
.rc-none { margin: 0; font-size: 12px; font-style: italic; color: var(--text-muted); }
.rc-quiet { margin: 0; padding: 14px 20px 16px 24px; font-size: 12.5px; color: var(--text-muted); }

/* Course: the current one large, the earlier ones beneath it. */
.rc-course { display: flex; align-items: baseline; gap: 4px 10px; flex-wrap: wrap; }
.rc-course-name { font-size: 14.5px; font-weight: 700; color: var(--text); }
.rc-course-uni { font-size: 12px; color: var(--text-muted); }
.rc-course-prev { margin-top: 4px; font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* The results ledger: per teaching period, one line per subject — a tone dot, the subject, the grade it
   stands for, the mark when one was recorded, the code as a chip — on the paper's soft rules. */
.rc-sem { display: flex; align-items: baseline; gap: 8px; margin: 12px 0 4px; font-size: 11.5px; font-weight: 700; color: var(--text); }
.rc-sem:first-child { margin-top: 0; }
.rc-sem-n { font-size: 10.5px; font-weight: 500; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.rc-subjects { display: flex; flex-direction: column; }
.rc-subj {
    display: flex; align-items: center; gap: 10px; min-width: 0; padding: 7px 0;
    border-bottom: 1px solid var(--line-soft); font-size: 12.5px; color: var(--text);
}
.rc-subj:last-child { border-bottom: 0; }
.rc-subj-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.rc-subj-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.rc-subj-grade { flex-shrink: 0; font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.rc-subj-mark { flex-shrink: 0; font-size: 11.5px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.rc-grade {
    display: inline-flex; align-items: center; justify-content: center; min-width: 36px; height: 20px; padding: 0 7px;
    border-radius: 6px; font-size: 11px; font-weight: 800; letter-spacing: .03em; font-variant-numeric: tabular-nums;
}
.rc-sum { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 9px; font-size: 11px; color: var(--text-muted); }
.rc-sum b { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }

/* Residence: a grid of facts and the contract-days track. */
.rc-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px 20px; }
.rc-fact { min-width: 0; }
.rc-fact-k { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted); }
.rc-fact-v { margin-top: 1px; font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-fact-v.is-warn { color: var(--warn); }
.rc-fact-s { margin-top: 1px; font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-track { position: relative; height: 7px; margin-top: 6px; border-radius: 999px; background: var(--surface-muted); overflow: hidden; }
.rc-track > span { position: absolute; top: 0; bottom: 0; left: 0; border-radius: 999px; background: var(--rc); opacity: .8; }
.rc-track > span.is-over { background: var(--danger); opacity: 1; }
.rc-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 7px; }

/* Recognition: leadership · awards · contributions, side by side. */
.rc-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px 22px; }
.rc-col { min-width: 0; }
.rc-col-h { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); }
.rc-col-h svg { width: 12px; height: 12px; }
.rc-list { display: flex; flex-direction: column; gap: 5px; }
.rc-item { display: flex; align-items: center; gap: 8px; min-width: 0; }
.rc-item.is-past { opacity: .62; }
.rc-item-ico { width: 24px; height: 24px; flex-shrink: 0; border-radius: 7px; display: flex; align-items: center; justify-content: center; }
.rc-item-ico svg { width: 12px; height: 12px; }
.rc-item-b { flex: 1; min-width: 0; }
.rc-item-t { font-size: 12.5px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-item-s { font-size: 10.5px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rc-item-n { flex-shrink: 0; font-size: 12px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
.rc-total {
    display: flex; align-items: center; justify-content: space-between; margin-top: 7px; padding-top: 6px; border-top: 1px solid var(--line);
    font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted);
}
.rc-total b { font-size: 14px; color: var(--text); font-variant-numeric: tabular-nums; }
.rc-remarks { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--text); white-space: pre-wrap; }

/* THE HERO FACT LINE — the profile's person facts as one line of quiet glyph-led text under the tags. */
.pf-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; margin-top: 6px; }
.pf-fact { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.pf-fact svg { width: 13px; height: 13px; flex-shrink: 0; color: var(--text-muted); }
/* A leadership role held today — a crown chip beside the name. */
.pf-role {
    display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 999px;
    background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 32%, var(--surface));
    font-size: 11px; font-weight: 700; color: var(--warn); white-space: nowrap;
}
.pf-role svg { width: 12px; height: 12px; flex-shrink: 0; }

/* ═══════════════════════════════════════════════════════════════════════════
   GridNext (gx-*) — the next-generation grid, running side by side with the
   DataGrid on preview pages. Entirely self-contained: no dg-* class is shared.
   Tokens only (white-label rule). The table follows the tonal ladder:
   chrome --surface → firm --line boundary → header band --surface-page →
   rows --surface / zebra --surface-muted → neutral --line-soft hover →
   selection = the only brand-tinted row, with a 3px --brand leading bar.
═══════════════════════════════════════════════════════════════════════════ */

.gx-card {
    position: relative;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: 16px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
    overflow: hidden;
}
.gx-card.is-full { border-radius: 0; }

/* ── Row 1 · page bar ─────────────────────────────────────────────────────── */
.gx-pagebar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 20px 10px;
}
.gx-pagebar-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.gx-title {
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--text);
    white-space: nowrap;
}
.gx-count {
    padding: 2px 10px;
    border-radius: 9999px;
    background: var(--brand-soft);
    color: var(--brand-text);
    font-size: 11.5px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
/* The page bar's way out of your changes — the ACTION, not a status badge. Appears only
   once something has been changed, and stays a quiet neutral control. */
.gx-reset-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 11px 3px 9px;
    border: 1px solid var(--line);
    border-radius: 9999px;
    background: var(--surface);
    color: var(--text-muted);
    font-size: 11.5px;
    font-weight: 600;
    font-family: inherit;
    white-space: nowrap;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.gx-reset-pill:hover { background: var(--line-soft); color: var(--text); }
.gx-reset-pill svg { width: 12px; height: 12px; }

.gx-pagebar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* The page's one filled action + quiet door buttons (page bar right).
   ⚠ The `.ui-*` aliases are the SAME recipe under a kit name, so a CONTENT page's header actions can use
   these two shapes without borrowing the grid's chrome vocabulary — one recipe, two names, never a second
   shape (the style guide's hand-rolled "hero door button" was already this declaration, written out by
   hand on each page that wanted one). Extend both selectors together or they drift apart. */
.gx-action-btn, .ui-actionbtn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    border: none;
    border-radius: 10px;
    background: var(--brand);
    color: #fff;
    font-size: 12.5px;
    font-weight: 700;
    cursor: pointer;
    transition: background .12s ease, box-shadow .12s ease, transform .12s ease;
}
.gx-action-btn:hover, .ui-actionbtn:hover { background: var(--brand-strong); box-shadow: 0 0 0 3px var(--brand-ring); }
.gx-action-btn:active, .ui-actionbtn:active { transform: translateY(1px); }
.gx-action-btn svg, .ui-actionbtn svg { width: 15px; height: 15px; }
.ui-actionbtn { font-family: inherit; text-decoration: none; }
.ui-actionbtn:disabled { opacity: .6; cursor: default; box-shadow: none; background: var(--brand); }
.gx-door-btn, .ui-doorbtn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.gx-door-btn:hover, .ui-doorbtn:hover { background: var(--line-soft); }
.gx-door-btn svg, .ui-doorbtn svg { width: 15px; height: 15px; color: var(--text-muted); }
.ui-doorbtn { font-family: inherit; text-decoration: none; }
.ui-doorbtn:disabled { opacity: .6; cursor: default; }

/* ── Row 2 · tool row — closes the chrome with a FIRM line ────────────────── */
.gx-toolrow {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 0 16px 10px;
    border-bottom: 1px solid var(--line);
}
.gx-tools-left, .gx-tools-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.gx-tools-right { margin-left: auto; }

.gx-search { position: relative; display: flex; align-items: center; }
.gx-search svg {
    position: absolute;
    left: 10px;
    width: 15px;
    height: 15px;
    color: var(--text-faint);
    pointer-events: none;
}
.gx-search input {
    height: 32px;
    width: 250px;
    padding: 0 30px 0 32px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface-muted);
    color: var(--text);
    font-size: 12.5px;
    font-family: inherit;
    outline: none;
    transition: border-color .12s ease, box-shadow .12s ease, background .12s ease;
}
.gx-search input::placeholder { color: var(--text-faint); }
.gx-search input:focus {
    background: var(--surface);
    border-color: var(--brand-line);
    box-shadow: 0 0 0 3px var(--brand-ring);
}
.gx-search-kbd {
    position: absolute;
    right: 9px;
    padding: 1px 6px;
    border: 1px solid var(--line);
    border-radius: 5px;
    background: var(--surface);
    color: var(--text-faint);
    font-size: 10.5px;
    font-weight: 600;
    font-family: inherit;
    pointer-events: none;
}
.gx-search-clear {
    position: absolute;
    right: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.gx-search-clear:hover { background: var(--line-soft); color: var(--text); }
.gx-search-clear svg { position: static; width: 12px; height: 12px; color: inherit; pointer-events: none; }

.gx-tool-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 32px;
    padding: 0 12px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text);
    font-size: 12.5px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
    transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.gx-tool-btn:hover { background: var(--line-soft); }
.gx-tool-btn.is-lit { background: var(--brand-soft); border-color: var(--brand-line); color: var(--brand-text); }
.gx-tool-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.gx-tool-badge {
    min-width: 17px;
    padding: 1px 5px;
    border-radius: 9999px;
    background: var(--brand);
    color: #fff;
    font-size: 10.5px;
    font-weight: 800;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.gx-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text-muted);
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.gx-icon-btn:hover { background: var(--line-soft); color: var(--text); }
.gx-icon-btn svg { width: 15px; height: 15px; }

.gx-export-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 32px;
    padding: 0 12px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text);
    font-size: 12.5px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: background .12s ease;
}
.gx-export-btn:hover { background: var(--line-soft); }
.gx-export-btn svg { width: 15px; height: 15px; color: var(--text-muted); }

/* ── Filter rail ──────────────────────────────────────────────────────────── */
.gx-filter-rail {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 8px 16px;
    border-bottom: 1px solid var(--line-soft);
    background: var(--surface);
}
.gx-filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 6px 4px 11px;
    border: 1px solid var(--brand-line);
    border-radius: 9999px;
    background: var(--brand-soft);
    color: var(--brand-text);
    font-size: 11.5px;
    font-family: inherit;
    cursor: pointer;
    transition: box-shadow .12s ease;
}
.gx-filter-chip:hover { box-shadow: 0 0 0 3px var(--brand-ring); }
.gx-filter-chip-col { font-weight: 800; }
.gx-filter-chip-val { font-weight: 600; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gx-filter-chip-x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 17px;
    height: 17px;
    border-radius: 9999px;
    cursor: pointer;
    transition: background .12s ease;
}
.gx-filter-chip-x:hover { background: var(--brand-line); }
.gx-filter-chip-x svg { width: 10px; height: 10px; }
.gx-filter-clear {
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-size: 11.5px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 8px;
}
.gx-filter-clear:hover { background: var(--line-soft); color: var(--text); }

/* ── The table ────────────────────────────────────────────────────────────── */
.gx-scroller {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    outline: none;
    overscroll-behavior: contain;
}
.gx-scroller:focus-visible { box-shadow: inset 0 0 0 2px var(--brand-ring); }

.gx-table {
    border-collapse: separate;
    border-spacing: 0;
    table-layout: fixed;
    min-width: 100%;
}

/* Header band — the recessed tone, dark labels, sticky. */
.gx-th {
    position: sticky;
    top: 0;
    z-index: 5;
    height: 36px;
    padding: 0;
    background: var(--surface-page);
    border-bottom: 1px solid var(--line);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text);
    white-space: nowrap;
    user-select: none;
}
.gx-th.gx-pin { z-index: 7; }
.gx-th-sorted { box-shadow: inset 0 -2px 0 var(--brand); }
.gx-th-btn {
    display: flex;
    align-items: center;
    gap: 5px;
    width: 100%;
    height: 36px;
    padding: 0 12px;
    border: none;
    background: transparent;
    color: inherit;
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    cursor: pointer;
    overflow: hidden;
}
.gx-th-static { cursor: default; }
.gx-th-label { overflow: hidden; text-overflow: ellipsis; }
.gx-th-sort { display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0; color: var(--brand-text); }
.gx-th-sort svg { width: 12px; height: 12px; transition: transform .12s ease; }
.gx-sort-ghost { color: var(--text-faint); opacity: 0; transition: opacity .12s ease; }
.gx-th:hover .gx-sort-ghost { opacity: .7; }
.gx-sort-rank {
    min-width: 14px;
    height: 14px;
    border-radius: 4px;
    background: var(--brand-soft);
    color: var(--brand-text);
    font-size: 9.5px;
    font-weight: 800;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.gx-th-kebab {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s ease, background .12s ease;
}
.gx-th:hover .gx-th-kebab { opacity: 1; }
.gx-th-kebab:hover { background: var(--line); }
.gx-th-kebab svg { width: 13px; height: 13px; }
.gx-th:hover .gx-th-btn { padding-right: 30px; }
.gx-resize {
    position: absolute;
    right: -4px;
    top: 0;
    width: 8px;
    height: 100%;
    cursor: col-resize;
    z-index: 8;
    touch-action: none;
}
.gx-resize:hover, .gx-resize:active { box-shadow: inset -3px 0 0 var(--brand-line); }
.gx-drop-before { box-shadow: inset 3px 0 0 var(--brand); }
.gx-drop-after { box-shadow: inset -3px 0 0 var(--brand); }

/* Rows — --surface, zebra --surface-muted, NEUTRAL hover, brand-only selection. */
.gx-tr { background: var(--surface); cursor: default; }
.gx-tr.gx-alt { background: var(--surface-muted); }
.gx-tr:hover { background: var(--line-soft); }
.gx-tr.is-selected, .gx-tr.is-selected:hover { background: var(--brand-soft); }
.gx-tr.is-selected > td:first-child { box-shadow: inset 3px 0 0 var(--brand); }
/* Keyboard-nav "current row": a left accent bar + faint tint, not a full border box
   (the outline drew an ugly frame over the row's action buttons). */
.gx-tr.is-active > td { background: var(--brand-soft); }
.gx-tr.is-active > td:first-child { box-shadow: inset 3px 0 0 var(--brand); }

.gx-td {
    padding: 0 12px;
    border-bottom: 1px solid var(--line-soft);
    font-size: 13px;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
}
.gx-cell-text { font-size: 13px; color: var(--text); }
.gx-td-check { padding: 0 0 0 14px; width: 44px; }
/* The header's select-all checkbox must sit on the SAME 14px rail as the row checkboxes — without
   this it inherits the generic centred .gx-th and drifts right of the column. */
.gx-th.gx-th-check { padding: 0 0 0 14px; text-align: left; }

/* Pinned columns: sticky, opaque in every row state, elevation when scrolled. */
.gx-pin { position: sticky; z-index: 2; }
td.gx-pin { background: var(--surface); }
.gx-tr.gx-alt > td.gx-pin { background: var(--surface-muted); }
.gx-tr:hover > td.gx-pin { background: var(--line-soft); }
.gx-tr.is-selected > td.gx-pin, .gx-tr.is-selected:hover > td.gx-pin { background: var(--brand-soft); }
.gx-scroller.is-hscrolled .gx-pin-last { box-shadow: 8px 0 12px -10px rgba(15, 23, 42, .35); }
.gx-scroller.is-hscrolled .gx-th-sorted.gx-pin-last { box-shadow: 8px 0 12px -10px rgba(15, 23, 42, .35), inset 0 -2px 0 var(--brand); }

/* Search-hit highlight inside cells. */
.gx-mark {
    background: var(--brand-soft);
    color: var(--brand-text);
    border-radius: 3px;
    padding: 0 1px;
    font-weight: 700;
}

/* Group bands. */
.gx-band td { border-bottom: 1px solid var(--line); background: var(--surface-page); }
.gx-band-cell { padding: 0 12px; white-space: nowrap; overflow: hidden; }
.gx-band-check { padding: 0 0 0 14px; }
.gx-band-toggle {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    border: none;
    background: transparent;
    color: var(--text);
    font-size: 12.5px;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    padding: 0;
    height: 100%;
}
.gx-band-toggle svg { width: 13px; height: 13px; color: var(--text-muted); transition: transform .12s ease; }
.gx-band-count {
    padding: 1px 8px;
    border-radius: 9999px;
    background: var(--brand-soft);
    color: var(--brand-text);
    font-size: 10.5px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}
.gx-band-aggs { margin-left: 14px; font-size: 11.5px; color: var(--text-muted); }

/* ── Checkboxes — ONE box, everywhere ─────────────────────────────────────────
   The style guide has said "checkboxes are .gx-check, never a native box" since the grid revamp, but 87
   of them across the app were still writing <input type="checkbox"> plain, so forms and side panels wore
   the browser's blue box next to grids wearing the kit's. Rather than patch 87 call sites and miss some,
   the ELEMENT carries the style and `.gx-check` stays as the explicit name — a new checkbox is right by
   default and there is nothing left to remember. (2026-08-21, "checkbox not following UI Design style".) */
input[type="checkbox"],
.gx-check {
    appearance: none;
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    margin: 0;
    border: 1.5px solid var(--text-faint);
    border-radius: 5px;
    background: var(--surface);
    cursor: pointer;
    position: relative;
    flex-shrink: 0;
    vertical-align: middle;
    transition: background .1s ease, border-color .1s ease, box-shadow .1s ease;
}
input[type="checkbox"]:hover:not(:disabled),
.gx-check:hover:not(:disabled) { box-shadow: 0 0 0 3px var(--brand-ring); border-color: var(--brand); }
input[type="checkbox"]:checked,
.gx-check:checked { background: var(--brand); border-color: var(--brand); }
input[type="checkbox"]:checked::after,
.gx-check:checked::after {
    content: "";
    position: absolute;
    inset: 2px;
    background: #fff;
    clip-path: polygon(13% 52%, 5% 65%, 40% 92%, 95% 22%, 83% 12%, 38% 70%);
}
input[type="checkbox"].is-partial,
.gx-check.is-partial { background: var(--brand); border-color: var(--brand); }
input[type="checkbox"].is-partial::after,
.gx-check.is-partial::after {
    content: "";
    position: absolute;
    left: 3px;
    right: 3px;
    top: 50%;
    height: 2px;
    margin-top: -1px;
    border-radius: 2px;
    background: #fff;
    clip-path: none;
}
input[type="checkbox"]:focus-visible,
.gx-check:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
/* Disabled reads as unavailable, not as unticked — a greyed tick is still a tick. */
input[type="checkbox"]:disabled,
.gx-check:disabled { cursor: default; opacity: .5; }
input[type="checkbox"]:disabled:checked,
.gx-check:disabled:checked { background: var(--text-faint); border-color: var(--text-faint); }

/* ── Loading & empty ──────────────────────────────────────────────────────── */
/* Height comes from the row's inline style, so the loading state matches the chosen density. */
.gx-tr-skeleton td { border-bottom: 1px solid var(--line-soft); padding: 0 12px; }
.gx-shimmer {
    display: inline-block;
    height: 12px;
    border-radius: 6px;
    background: linear-gradient(90deg, var(--line-soft) 25%, var(--line) 50%, var(--line-soft) 75%);
    background-size: 200% 100%;
    animation: gx-shimmer 1.4s ease infinite;
}
@keyframes gx-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.gx-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 56px 24px;
    text-align: center;
    white-space: normal;
}
.gx-empty svg { width: 40px; height: 40px; color: var(--text-faint); }
.gx-empty-title { font-size: 14px; font-weight: 700; color: var(--text); }
.gx-empty-sub { font-size: 12.5px; color: var(--text-muted); margin-bottom: 6px; }

/* ── Load more — the sentinel a server-queried grid keeps under its last row ── */
tr.gx-more td, div.gx-more {
    padding: 14px 16px;
    text-align: center;
    border-top: 1px solid var(--line-soft);
}
.gx-more .gx-tool-btn { display: inline-flex; }
.gx-more-busy {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--text-muted);
}
.gx-more-busy svg { width: 16px; height: 16px; }

/* ── Mobile card layout — the data region becomes a stacked card per row instead of a wide table
   (a table only ever shows its first column on a phone). Rendered only when JS matchMedia flags a
   ≤640px viewport, so these rules need no media query of their own. ── */
.gx-scroller.is-mobile { overflow-x: hidden; }
.gx-mcards { display: flex; flex-direction: column; gap: 10px; padding: 12px; }
.gx-mcard {
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--surface);
    padding: 12px 13px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.gx-mcard.is-selected { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-soft); }
.gx-mcard-head { display: flex; align-items: center; gap: 11px; }
.gx-mcard-head .gx-check { flex-shrink: 0; }
.gx-mcard-id { min-width: 0; flex: 1 1 auto; font-weight: 600; }
/* Every remaining column becomes a label:value row, ruled off from the identity and each other. */
.gx-mcard-field {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin-top: 9px;
    padding-top: 9px;
    border-top: 1px solid var(--line-soft);
}
.gx-mcard-k {
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-muted);
}
.gx-mcard-v { min-width: 0; text-align: right; font-size: 13px; color: var(--text); }
.gx-mcard-v .gx-cell-text { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Badge groups / action-button rows in a value cell sit flush-right. */
.gx-mcard-v > div { justify-content: flex-end; }
.gx-mcard-skel { display: flex; flex-direction: column; }
/* Group band → a divider header that still collapses. */
.gx-mcard-band {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 6px 4px;
    background: transparent;
    border: none;
    cursor: pointer;
    font-family: inherit;
    text-align: left;
}
.gx-mcard-band svg { width: 15px; height: 15px; color: var(--text-muted); transition: transform .15s ease; }
.gx-mcard-band-label { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }

/* ── Summary row — the count reads as a figure, the aggregates as stat tiles ── */
.gx-foot {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 7px 16px;
    border-top: 1px solid var(--line);
    background: var(--surface);
    font-size: 12px;
    color: var(--text-muted);
    flex-wrap: wrap;
}
.gx-foot-left { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
.gx-foot-count {
    font-size: 15px;
    font-weight: 800;
    color: var(--text);
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}
.gx-foot-noun { font-size: 12px; color: var(--text-muted); }

/* How much of the whole set is on screen — only shown while something is filtering. */
.gx-foot-prop { display: inline-flex; align-items: center; gap: 7px; }
.gx-foot-prop-bar {
    width: 54px;
    height: 5px;
    border-radius: 9999px;
    background: var(--line);
    overflow: hidden;
}
.gx-foot-prop-bar i {
    display: block;
    height: 100%;
    border-radius: 9999px;
    background: var(--brand);
    transition: width .18s ease;
}
.gx-foot-prop-text { font-size: 11.5px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

.gx-foot-sel {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 4px 2px 8px;
    border-radius: 9999px;
    background: var(--brand-soft);
    color: var(--brand-text);
    font-size: 11.5px;
    font-weight: 600;
}
.gx-foot-sel-n { font-weight: 800; font-variant-numeric: tabular-nums; }
.gx-foot-sel-x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 17px;
    height: 17px;
    border: none;
    border-radius: 9999px;
    background: transparent;
    color: inherit;
    cursor: pointer;
    transition: background .12s ease;
}
.gx-foot-sel-x:hover { background: var(--brand-line); }
.gx-foot-sel-x svg { width: 9px; height: 9px; }

.gx-foot-aggs { margin-left: auto; display: flex; align-items: stretch; gap: 0; flex-wrap: wrap; }
.gx-foot-agg {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 0 14px;
    border-left: 1px solid var(--line-soft);
}
.gx-foot-agg:first-child { border-left: none; }
.gx-foot-agg:last-child { padding-right: 0; }
.gx-foot-agg-label {
    font-size: 9.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-faint);
    white-space: nowrap;
}
.gx-foot-agg-value {
    font-size: 13px;
    font-weight: 800;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.01em;
}

/* ── Bulk bar — floats over the footer on the inverse surface ─────────────── */
.gx-bulk {
    position: absolute;
    left: 50%;
    bottom: 48px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 8px 7px 12px;
    border-radius: 9999px;
    background: var(--text);
    color: var(--surface);
    box-shadow: 0 12px 32px rgba(15, 23, 42, .28);
    z-index: 20;
    animation: gx-bulk-in .16s ease;
    white-space: nowrap;
}
@keyframes gx-bulk-in { from { opacity: 0; transform: translate(-50%, 8px); } to { opacity: 1; transform: translate(-50%, 0); } }
.gx-bulk-count {
    min-width: 22px;
    height: 22px;
    padding: 0 7px;
    border-radius: 9999px;
    background: var(--brand);
    color: #fff;
    font-size: 11.5px;
    font-weight: 800;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-variant-numeric: tabular-nums;
}
.gx-bulk-noun { font-size: 12.5px; font-weight: 600; }
.gx-bulk-sep { width: 1px; height: 18px; background: rgba(148, 163, 184, .4); }
.gx-bulk-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    border: none;
    border-radius: 9999px;
    background: transparent;
    color: inherit;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: background .12s ease;
}
.gx-bulk-btn:hover { background: rgba(148, 163, 184, .25); }
.gx-bulk-btn svg { width: 14px; height: 14px; }
.gx-bulk-clear { opacity: .8; }

/* ── Popovers (fixed backdrop 9998 / panel 9999 — the kit recipe) ─────────── */
.gx-backdrop { position: fixed; inset: 0; z-index: 9998; background: transparent; }
.gx-pop {
    position: fixed;
    z-index: 9999;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 14px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, .16);
    padding: 6px;
    animation: gx-pop-in .12s ease;
    max-height: min(480px, calc(100vh - 24px));
    display: flex;
    flex-direction: column;
}
@keyframes gx-pop-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
.gx-pop-head {
    padding: 8px 10px 5px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-muted);
}
.gx-pop-sect {
    padding: 9px 10px 3px;
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-faint);
}
.gx-pop-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    width: 100%;
    padding: 7px 10px;
    border: none;
    border-radius: 9px;
    background: transparent;
    text-align: left;
    font-family: inherit;
    cursor: pointer;
    transition: background .1s ease;
}
.gx-pop-item:hover { background: var(--line-soft); }
.gx-pop-item.is-on { background: var(--brand-soft); }
.gx-pop-item.is-on .gx-pop-item-main { color: var(--brand-text); }
.gx-pop-item-main { font-size: 12.5px; font-weight: 600; color: var(--text); }
.gx-pop-item-sub { font-size: 11px; color: var(--text-muted); }
.gx-pop-row { display: flex; align-items: center; gap: 2px; }
.gx-pop-row .gx-pop-item { flex: 1 1 auto; }
.gx-pop-x {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border: none;
    border-radius: 7px;
    background: transparent;
    color: var(--text-faint);
    cursor: pointer;
    flex-shrink: 0;
}
.gx-pop-x:hover { background: var(--danger-soft); color: var(--danger); }
.gx-pop-x svg { width: 12px; height: 12px; }
.gx-pop-scroll { overflow-y: auto; max-height: 280px; }
.gx-pop-body { padding: 8px 10px; display: flex; flex-direction: column; gap: 8px; }
.gx-pop-check {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 6px 10px;
    border-radius: 9px;
    cursor: pointer;
    transition: background .1s ease;
}
.gx-pop-check:hover { background: var(--line-soft); }
.gx-pop-check .gx-pop-item-main { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gx-pop-count { font-size: 11px; font-weight: 600; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.gx-pop-foot {
    border-top: 1px solid var(--line-soft);
    margin-top: 5px;
    padding: 8px 6px 4px;
    display: flex;
    flex-direction: column;
    gap: 7px;
}
.gx-pop-foot-row { display: flex; align-items: center; gap: 7px; }
.gx-pop-input {
    height: 32px;
    width: 100%;
    min-width: 0;
    padding: 0 10px;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--surface-muted);
    color: var(--text);
    font-size: 12.5px;
    font-family: inherit;
    outline: none;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.gx-pop-input:focus { background: var(--surface); border-color: var(--brand-line); box-shadow: 0 0 0 3px var(--brand-ring); }
.gx-pop-search { margin: 2px 4px 4px; width: calc(100% - 8px); }
/* inline-flex is load-bearing: the CSS reset makes <svg> display:block, so a button
   without it stacks its icon above its label. */
.gx-pop-act {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 7px 13px;
    border: none;
    border-radius: 9px;
    background: var(--brand);
    color: #fff;
    font-size: 12px;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
    transition: background .12s ease;
}
.gx-pop-act svg { width: 13px; height: 13px; }
.gx-pop-act:hover { background: var(--brand-strong); }
.gx-pop-act:disabled { opacity: .45; cursor: default; }
.gx-pop-act-quiet { background: var(--surface); color: var(--text); border: 1px solid var(--line); font-weight: 600; }
.gx-pop-act-quiet:hover { background: var(--line-soft); }
.gx-pop-pair { display: flex; align-items: center; gap: 7px; }
.gx-pop-pair-sep { color: var(--text-faint); }
.gx-pop-presets { display: flex; gap: 6px; flex-wrap: wrap; }
.gx-pop-preset {
    padding: 4px 10px;
    border: 1px solid var(--line);
    border-radius: 9999px;
    background: var(--surface);
    color: var(--text);
    font-size: 11px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
}
.gx-pop-preset:hover { background: var(--line-soft); }

/* Number-filter histogram. */
.gx-histo { display: flex; align-items: flex-end; gap: 2px; height: 46px; }
.gx-histo i { flex: 1 1 0; background: var(--brand-line); border-radius: 2px 2px 0 0; }
.gx-histo i.is-zero { background: var(--line-soft); }
.gx-histo-scale { display: flex; justify-content: space-between; font-size: 10px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* Density glyphs in the density menu. */
.gx-density-glyph { display: inline-flex; flex-direction: column; justify-content: center; width: 16px; flex-shrink: 0; }
.gx-density-glyph i { display: block; height: 2px; border-radius: 1px; background: var(--text-faint); }
.gx-density-glyph-compact { gap: 2px; }
.gx-density-glyph-cozy { gap: 4px; }
.gx-density-glyph-relaxed { gap: 6px; }

/* ── Motion & responsive ──────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .gx-card *, .gx-pop, .gx-bulk { animation: none !important; transition: none !important; }
}
@media (max-width: 720px) {
    .gx-search input { width: 160px; }
    .gx-pagebar { flex-wrap: wrap; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   GridNext panels — the Views / Columns / Group / Filters surfaces. They share
   one shell (.gx-pop) whose position and width arrive as custom properties, so
   the narrow-screen rules below can re-lay it out as a bottom sheet without
   fighting an inline style.
═══════════════════════════════════════════════════════════════════════════ */

.gx-pop { left: var(--pop-left); top: var(--pop-top); width: var(--pop-w); }

/* ── Panel head ───────────────────────────────────────────────────────────── */
.gx-panel-head {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 12px 10px 14px;
    border-bottom: 1px solid var(--line-soft);
}
.gx-panel-headings { min-width: 0; flex: 1 1 auto; }
.gx-panel-title { font-size: 14px; font-weight: 800; color: var(--text); letter-spacing: -.01em; }
.gx-panel-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
.gx-panel-x {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.gx-panel-x:hover { background: var(--line-soft); color: var(--text); }
.gx-panel-x svg { width: 13px; height: 13px; }

/* ── Panel tool strip (search + scope segmented) ──────────────────────────── */
.gx-panel-tools {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--line-soft);
}
.gx-mini-search { position: relative; display: flex; align-items: center; }
.gx-mini-search svg {
    position: absolute;
    left: 9px;
    width: 13px;
    height: 13px;
    color: var(--text-faint);
    pointer-events: none;
}
.gx-mini-search input {
    height: 32px;
    width: 100%;
    padding: 0 10px 0 29px;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--surface-muted);
    color: var(--text);
    font-size: 12.5px;
    font-family: inherit;
    outline: none;
    transition: border-color .12s ease, box-shadow .12s ease, background .12s ease;
}
.gx-mini-search input::placeholder { color: var(--text-faint); }
.gx-mini-search input:focus { background: var(--surface); border-color: var(--brand-line); box-shadow: 0 0 0 3px var(--brand-ring); }

.gx-seg {
    display: flex;
    gap: 2px;
    padding: 2px;
    border-radius: 9px;
    background: var(--surface-muted);
    border: 1px solid var(--line);
}
.gx-seg-btn {
    flex: 1 1 0;
    padding: 5px 8px;
    border: none;
    border-radius: 7px;
    background: transparent;
    color: var(--text-muted);
    font-size: 11.5px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
    transition: background .12s ease, color .12s ease;
}
.gx-seg-btn:hover { color: var(--text); }
.gx-seg-btn.is-on { background: var(--surface); color: var(--brand-text); box-shadow: 0 1px 2px rgba(15, 23, 42, .06); }

/* ── The unsaved-changes notice (Views) ──────────────────────────────────── */
.gx-notice {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin: 8px 10px 2px;
    padding: 9px 11px;
    border: 1px solid var(--warn);
    border-radius: 11px;
    background: var(--warn-soft);
}
.gx-notice-text { display: flex; flex-direction: column; min-width: 0; flex: 1 1 140px; }
.gx-notice-title { font-size: 12px; font-weight: 800; color: var(--warn); }
.gx-notice-sub { font-size: 11px; color: var(--text-muted); }
.gx-notice-acts { display: flex; gap: 6px; flex-shrink: 0; }

/* ── A selectable row: views, group choices, density ──────────────────────── */
.gx-viewrow {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 8px 10px;
    border: none;
    border-radius: 10px;
    background: transparent;
    text-align: left;
    font-family: inherit;
    cursor: pointer;
    transition: background .1s ease;
}
.gx-viewrow:hover { background: var(--line-soft); }
.gx-viewrow.is-on { background: var(--brand-soft); }
.gx-viewrow-tick {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 17px;
    flex-shrink: 0;
    color: var(--brand);
}
.gx-viewrow-tick svg { width: 13px; height: 13px; }
.gx-viewrow-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.gx-viewrow-name {
    display: block;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.gx-viewrow.is-on .gx-viewrow-name { color: var(--brand-text); }
.gx-viewrow-desc {
    font-size: 11px;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gx-viewrow-wrap { display: flex; align-items: center; gap: 2px; }
.gx-viewrow-wrap .gx-viewrow { flex: 1 1 auto; min-width: 0; }
/* One right-hand slot: persistent status flags in flow; the action cluster floats over them on
   hover (a common, tidy pattern) so the actions never crowd or wrap the name. */
.gx-viewrow-side { display: inline-flex; align-items: center; justify-content: flex-end; gap: 3px; flex-shrink: 0; }
.gx-viewrow-side .fl-check { width: 16px; height: 16px; color: var(--brand-text); margin-right: 1px; }
/* Favourite + default = persistent roundel toggles (near the avatar's size). Coloured when set; an
   unset toggle only appears (faint) on hover, so a plain view isn't cluttered — and NEVER swaps size. */
.gx-flag-btn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
               border: none; border-radius: 8px; background: transparent; color: var(--text-faint); cursor: pointer;
               transition: opacity .12s ease, background .12s ease, color .12s ease; }
.gx-flag-btn svg { width: 17px; height: 17px; }
.gx-flag-star.is-set { background: var(--warn-soft); color: var(--warn); }
.gx-flag-home.is-set { background: var(--brand-soft); color: var(--brand-text); }
.gx-flag-btn:not(.is-set) { opacity: 0; pointer-events: none; }
.gx-viewrow-wrap:hover .gx-flag-btn:not(.is-set),
.gx-viewrow-wrap:focus-within .gx-flag-btn:not(.is-set) { opacity: 1; pointer-events: auto; }
.gx-flag-star:not(.is-set):hover { background: var(--warn-soft); color: var(--warn); }
.gx-flag-home:not(.is-set):hover { background: var(--brand-soft); color: var(--brand-text); }
/* Rename / delete / hide — secondary, hover-only. */
.gx-view-secondary { display: inline-flex; align-items: center; gap: 1px; opacity: 0; pointer-events: none; transition: opacity .12s ease; }
.gx-viewrow-wrap:hover .gx-view-secondary,
.gx-viewrow-wrap:focus-within .gx-view-secondary { opacity: 1; pointer-events: auto; }
.gx-rename { margin: 2px 4px; }

.gx-group-count {
    padding: 1px 7px;
    border-radius: 9999px;
    background: var(--brand-soft);
    color: var(--brand-text);
    font-size: 10px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}
.gx-viewrow.is-on .gx-group-count { background: var(--surface); }

/* ── THE action button — one elegant pattern for every grid action (grid cells,
      columns panel, lenses). A soft-tinted chip at rest that lights up in its
      function colour on hover. ─────────────────────────────────────────────── */
/* Action tile — a filled, colour-coded square (not a round chip). Every grid's row action is
   one of these: it carries a soft tint of its FUNCTION colour at rest, so the button reads off
   the row on sight, then fills to the solid colour on hover. One pattern, used on every grid;
   colour by function via the is-* modifiers below (default = brand). The hover glyph is
   var(--surface) — white on the dark fills of the light theme, near-black on the light fills
   of the dark theme — so it stays legible whichever way round the tokens invert. */
.gx-mini-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    border: none;
    border-radius: 9px;
    background: var(--brand-soft);
    color: var(--brand-text);
    cursor: pointer;
    text-decoration: none;
    transition: background .12s ease, color .12s ease, transform .08s ease, box-shadow .12s ease;
}
.gx-mini-btn:hover { background: var(--brand); color: var(--surface); box-shadow: 0 2px 8px color-mix(in srgb, var(--brand) 35%, transparent); }
.gx-mini-btn:active { transform: scale(.9); }
/* Delete — red tile, fills solid on hover. */
.gx-mini-btn.is-danger { background: var(--danger-soft); color: var(--danger); }
.gx-mini-btn.is-danger:hover { background: var(--danger); color: var(--surface); box-shadow: 0 2px 8px color-mix(in srgb, var(--danger) 35%, transparent); }
/* Favourite — amber tile. */
.gx-mini-btn.is-star { background: var(--warn-soft); color: var(--warn); }
.gx-mini-btn.is-star:hover { background: var(--warn); color: var(--surface); box-shadow: 0 2px 8px color-mix(in srgb, var(--warn) 35%, transparent); }
/* Default / active toggle — solid brand (this is the on-state). */
.gx-mini-btn.is-on { background: var(--brand); color: var(--surface); }
.gx-mini-btn.is-on:hover { background: var(--brand); color: var(--surface); box-shadow: 0 2px 8px color-mix(in srgb, var(--brand) 35%, transparent); }
/* Sync / view / external — blue tile. */
.gx-mini-btn.is-info { background: var(--info-soft); color: var(--info); }
.gx-mini-btn.is-info:hover { background: var(--info); color: var(--surface); box-shadow: 0 2px 8px color-mix(in srgb, var(--info) 35%, transparent); }
/* Activate / enable — green tile. */
.gx-mini-btn.is-ok { background: var(--ok-soft); color: var(--ok-strong); }
.gx-mini-btn.is-ok:hover { background: var(--ok-strong); color: var(--surface); box-shadow: 0 2px 8px color-mix(in srgb, var(--ok-strong) 35%, transparent); }
/* Secondary / neutral (retire, hide, cascade) — slate tile. OUTLINED, not filled: the old fill was
   --surface-muted, which IS the zebra band, so on every second row the button lost its tile entirely
   and sat there as a bare glyph next to two solid ones. An inset ring reads on both bands and keeps
   the box exactly 30px (a real border would grow it). */
.gx-mini-btn.is-muted { background: var(--surface); box-shadow: inset 0 0 0 1px var(--line); color: var(--text-muted); }
.gx-mini-btn.is-muted:hover { background: var(--line); box-shadow: none; color: var(--text); }
/* Armed for a second tap (double-tap to delete — no modal). */
.gx-mini-btn.is-armed, .gx-mini-btn.is-armed:hover { background: var(--danger); color: var(--surface); box-shadow: 0 0 0 3px var(--danger-soft); }
.gx-mini-btn:disabled { opacity: .45; cursor: default; box-shadow: none; }
.gx-mini-btn:disabled:hover { box-shadow: none; }
.gx-mini-btn svg { width: 15px; height: 15px; }

.gx-pop-note {
    padding: 10px 12px;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--text-muted);
}

/* ── Columns list ─────────────────────────────────────────────────────────── */
.gx-collist { padding: 4px; }
.gx-colrow {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 6px 6px 6px 2px;
    border-radius: 10px;
    border-top: 2px solid transparent;
    border-bottom: 2px solid transparent;
    cursor: default;
    transition: background .1s ease;
}
.gx-colrow:hover { background: var(--line-soft); }
.gx-colrow:focus-visible { outline: none; background: var(--line-soft); box-shadow: 0 0 0 2px var(--brand-ring); }
.gx-colrow.is-off .gx-colrow-name,
.gx-colrow.is-off .gx-colrow-meta { color: var(--text-faint); }
.gx-colrow.is-dragging { opacity: .45; }
.gx-colrow.is-drop-before { border-top-color: var(--brand); }
.gx-colrow.is-drop-after { border-bottom-color: var(--brand); }
.gx-colrow-grip {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    flex-shrink: 0;
    color: var(--text-faint);
    cursor: grab;
}
.gx-colrow-grip:active { cursor: grabbing; }
.gx-colrow-grip svg { width: 15px; height: 15px; }
.gx-colrow-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.gx-colrow-name {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text);
}
.gx-colrow-meta {
    font-size: 10.5px;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gx-colrow-acts { display: flex; gap: 3px; flex-shrink: 0; }
.gx-pin-tag {
    padding: 1px 6px;
    border-radius: 9999px;
    background: var(--brand-soft);
    color: var(--brand-text);
    font-size: 9.5px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* ── Filter builder ───────────────────────────────────────────────────────── */
.gx-filterrow {
    border: 1px solid var(--line-soft);
    border-radius: 11px;
    margin: 6px;
    background: var(--surface);
    transition: border-color .12s ease, background .12s ease;
}
.gx-filterrow.is-open { border-color: var(--brand-line); background: var(--surface-muted); }
.gx-filterrow-top { display: flex; align-items: center; gap: 2px; padding-right: 5px; }
.gx-filterrow-head {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
    padding: 9px 4px 9px 8px;
    border: none;
    background: transparent;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}
.gx-filterrow-chev { width: 12px; height: 12px; flex-shrink: 0; color: var(--text-muted); transition: transform .12s ease; }
.gx-filterrow-col { font-size: 12.5px; font-weight: 700; color: var(--text); flex-shrink: 0; }
.gx-filterrow-val {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--brand-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gx-filterrow-val.is-any { color: var(--text-faint); font-weight: 400; font-style: italic; }
.gx-filterrow-body {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 2px 10px 10px;
}
.gx-choicelist { max-height: 190px; overflow-y: auto; margin: 0 -4px; }

.gx-addlist { max-height: 210px; overflow-y: auto; margin-top: 6px; }
.gx-addrow {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 9px;
    border: none;
    border-radius: 9px;
    background: transparent;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: background .1s ease;
}
.gx-addrow:hover { background: var(--line-soft); }
.gx-addrow-name { font-size: 12.5px; font-weight: 600; color: var(--text); flex: 1 1 auto; }
.gx-foot-label {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-faint);
    padding: 0 2px 1px;
}

/* Density glyph sits inline in a .gx-viewrow now. */
.gx-viewrow .gx-density-glyph { margin-top: 2px; }

/* ═══════════════════════════════════════════════════════════════════════════
   Responsive — the grid has to work on a phone. Three moves: the chrome stacks
   and stays reachable, every panel becomes a bottom sheet with touch-sized
   targets, and the table keeps its pinned first column while the rest scrolls.
═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
    .gx-tools-right { margin-left: 0; }
    .gx-toolrow { row-gap: 8px; }
}

@media (max-width: 640px) {
    .gx-card { border-radius: 14px; }

    .gx-pagebar { padding: 12px 14px 8px; row-gap: 8px; }
    .gx-title { font-size: 17px; }
    .gx-pagebar-right { width: 100%; }
    .gx-pagebar-right > * { flex: 1 1 auto; justify-content: center; }

    .gx-toolrow { padding: 0 12px 10px; }
    .gx-tools-left, .gx-tools-right { width: 100%; }
    .gx-search { flex: 1 1 100%; }
    .gx-search input { width: 100%; }
    .gx-search-kbd { display: none; }          /* no physical "/" key to press */
    .gx-hide-mobile { display: none; }         /* fullscreen is meaningless here */

    /* Touch targets: 36px minimum on every control in the chrome. */
    .gx-tool-btn, .gx-icon-btn, .gx-export-btn { height: 36px; }
    .gx-search input { height: 36px; }

    .gx-filter-rail { padding: 8px 12px; }

    /* Every panel becomes a bottom sheet. */
    .gx-backdrop { background: rgba(15, 23, 42, .38); }
    .gx-pop {
        left: 0;
        right: 0;
        top: auto;
        bottom: 0;
        width: auto;
        max-height: 82vh;
        border-radius: 18px 18px 0 0;
        border-bottom: none;
        padding: 4px 4px calc(4px + env(safe-area-inset-bottom));
        animation: gx-sheet-in .18s ease;
    }
    .gx-panel-head { padding: 14px 14px 12px 16px; }
    .gx-panel-title { font-size: 15px; }
    .gx-pop-scroll { max-height: none; }
    .gx-mini-btn { width: 36px; height: 36px; }
    .gx-mini-btn svg { width: 15px; height: 15px; }
    .gx-colrow { padding: 8px 6px; }
    .gx-colrow-grip { display: none; }          /* HTML5 drag doesn't work on touch */
    .gx-seg-btn { padding: 8px; }
    .gx-pop-act { padding: 9px 14px; }
    .gx-pop-check { padding: 9px 10px; }
    .gx-viewrow { padding: 10px; }
    .gx-viewrow-acts { opacity: 1; }            /* nothing to hover with */
    .gx-pop-foot-row { flex-wrap: wrap; }

    /* The bulk bar spans the width so its buttons stay tappable. */
    .gx-bulk {
        left: 8px;
        right: 8px;
        bottom: 8px;
        transform: none;
        justify-content: center;
        flex-wrap: wrap;
        border-radius: 14px;
    }
    .gx-foot { padding: 8px 12px; }
}

@keyframes gx-sheet-in { from { transform: translateY(14px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
    .gx-pop { animation: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   GridNext filter builder, round 2 — each filter KIND carries its own colour
   and glyph (the kit's roundel recipe: soft fill + strong stroke of one family)
   instead of a grey uppercase word, so the panel reads at a glance.
═══════════════════════════════════════════════════════════════════════════ */

.gx-kind {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    border-radius: 9px;
}
.gx-kind svg { width: 14px; height: 14px; }
.gx-kind-text   { background: var(--info-soft);   color: var(--info); }
.gx-kind-choice { background: var(--accent-soft); color: var(--accent); }
.gx-kind-number { background: var(--warn-soft);   color: var(--warn); }
.gx-kind-date   { background: var(--ok-soft);     color: var(--ok-strong); }
.gx-kind-tags   { background: var(--brand-soft);  color: var(--brand-text); }
.gx-kind-plain  { background: var(--line-soft);   color: var(--text-muted); }
.gx-kind-export { background: var(--brand-soft);  color: var(--brand-text); }

/* The header's live match count — the number itself carries the weight. */
.gx-match { font-weight: 800; color: var(--brand-text); font-variant-numeric: tabular-nums; }

/* Empty state inside the builder. */
.gx-filter-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 7px;
    padding: 22px 20px 18px;
    text-align: center;
}
.gx-filter-empty-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 13px;
    background: var(--brand-soft);
    color: var(--brand);
}
.gx-filter-empty-icon svg { width: 20px; height: 20px; }
.gx-filter-empty-title { font-size: 12.5px; font-weight: 700; color: var(--text); }
.gx-filter-empty-sub { font-size: 11.5px; color: var(--text-muted); line-height: 1.5; }

/* An active filter row now leads with its kind roundel. */
.gx-filterrow-head { gap: 7px; }
.gx-filterrow-chev { width: 11px; height: 11px; color: var(--text-faint); }

/* "Add filter" is the builder's add-a-row affordance: a full-width dashed slot,
   not a heavy filled block sitting in the corner. */
.gx-addfilter-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    flex: 1 1 auto;
    padding: 9px 12px;
    border: 1.5px dashed var(--brand-line);
    border-radius: 10px;
    background: transparent;
    color: var(--brand-text);
    font-size: 12.5px;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.gx-addfilter-btn:hover { background: var(--brand-soft); border-color: var(--brand); }
.gx-addfilter-btn svg { width: 14px; height: 14px; }

/* The add-a-filter picker rows: roundel + name, colour instead of a type word. */
.gx-addrow { gap: 9px; padding: 7px 8px; }
.gx-addrow-name { font-size: 12.5px; font-weight: 600; color: var(--text); flex: 1 1 auto; }

/* The way back to the page's shipped setup, in the Views panel footer. */
.gx-reset-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    padding: 7px 10px;
    border: none;
    border-radius: 9px;
    background: transparent;
    color: var(--text-muted);
    font-size: 11.5px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.gx-reset-link:hover { background: var(--line-soft); color: var(--text); }
.gx-reset-link svg { width: 13px; height: 13px; }

/* ═══════════════════════════════════════════════════════════════════════════
   GridNext — datagrid rollout additions (modes, tag filter, lively header menu,
   export menu, server-backed view chrome). Tokens only; dark-safe.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Entity glyph roundel before the grid title. */
.gx-title-ico { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
                border-radius: 10px; background: var(--brand-soft); color: var(--brand-text); flex-shrink: 0; }
.gx-title-ico svg { width: 19px; height: 19px; }

/* ── Mode switch (list ⇄ board ⇄ calendar) in the page bar ─────────────────── */
.gx-modes { display: inline-flex; align-items: center; gap: 2px; padding: 3px; margin-left: 4px;
            background: var(--surface-muted); border: 1px solid var(--line-soft); border-radius: 10px; }
.gx-mode { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 11px;
           border: 0; background: transparent; border-radius: 7px; font-family: inherit; font-size: 12.5px;
           font-weight: 600; color: var(--text-muted); cursor: pointer; transition: background .12s ease, color .12s ease; }
.gx-mode svg { width: 15px; height: 15px; }
/* A shape that is a ROUTE is an anchor, not a button (the board and the calendar are pages, not states) —
   without this it wears the browser's underline while the grid's own buttons don't. */
a.gx-mode { text-decoration: none; }
.gx-mode:hover:not(.is-on) { color: var(--text); background: var(--surface); }
.gx-mode.is-on { background: var(--surface); color: var(--brand-text); box-shadow: 0 1px 2px rgba(15,23,42,.08); cursor: default; }

/* ── Tag filter editor (op segmented + coloured tag chips) ─────────────────── */
.gx-tagops { margin-bottom: 8px; flex-wrap: wrap; }
.gx-taglist { display: flex; flex-direction: column; gap: 2px; }
.gx-tagcheck { border-radius: 8px; }
.gx-tagcheck.is-on { background: var(--brand-soft); }
.gx-tagchip { display: inline-flex; align-items: center; padding: 2px 10px; border-radius: 9999px;
              background: var(--line-soft); color: var(--text); font-size: 12px; font-weight: 600; }
.gx-tagcheck.is-on .gx-tagchip { background: var(--brand); color: #fff; }

/* ── Lively header/kebab menu + export menu ───────────────────────────────── */
.gx-hm-head { display: flex; align-items: center; gap: 10px; padding: 11px 12px 10px; }
.gx-hm-head-body { display: flex; flex-direction: column; min-width: 0; }
.gx-hm-head-name { font-size: 13px; font-weight: 700; color: var(--text); line-height: 1.2;
                   white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gx-hm-head-sub { font-size: 10.5px; font-weight: 600; color: var(--text-faint);
                  text-transform: uppercase; letter-spacing: .04em; margin-top: 1px; }
.gx-hm-sep { height: 1px; background: var(--line-soft); margin: 5px 10px; }
.gx-hm-item { display: flex; align-items: center; gap: 11px; width: 100%; padding: 8px 12px;
              border: 0; background: transparent; font-family: inherit; text-align: left; cursor: pointer;
              border-radius: 9px; transition: background .1s ease; }
/* A menu line that NAVIGATES is a real <a> — middle-click, open-in-new-tab and copy-link are the whole
   reason to spend an anchor on it, and they die the moment it becomes a button plus NavigateTo. */
a.gx-hm-item { text-decoration: none; box-sizing: border-box; }
.gx-hm-item:hover { background: var(--surface-muted); }
.gx-hm-item .gx-pop-item-main { flex: 1; font-size: 12.5px; font-weight: 600; color: var(--text); }
.gx-hm-item.is-on { background: var(--brand-soft); }
.gx-hm-item.is-on .gx-pop-item-main { color: var(--brand-text); }
.gx-hm-hint { font-size: 10.5px; font-weight: 700; color: var(--text-faint);
              font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.gx-hm-ico { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
             border-radius: 8px; flex-shrink: 0; }
.gx-hm-ico svg { width: 15px; height: 15px; }
.gx-hm-ico-sort   { background: var(--info-soft);   color: var(--info); }
.gx-hm-ico-group  { background: var(--accent-soft); color: var(--accent); }
.gx-hm-ico-filter { background: var(--brand-soft);  color: var(--brand-text); }
.gx-hm-ico-pin    { background: var(--warn-soft);   color: var(--warn); }
.gx-hm-ico-hide   { background: var(--danger-soft); color: var(--danger); }
.gx-hm-ico-mute   { background: var(--line-soft);   color: var(--text-muted); }
.gx-hm-ico-csv    { background: var(--ok-soft);     color: var(--ok-strong); }
.gx-hm-ico-pdf    { background: var(--danger-soft); color: var(--danger); }

/* ── Server-backed view chrome (tags, favourite, default, confirm) ────────── */
.gx-view-tag { display: inline-flex; align-items: center; padding: 1px 7px; margin-left: 6px;
               border-radius: 9999px; font-size: 9.5px; font-weight: 800; text-transform: uppercase;
               letter-spacing: .04em; vertical-align: middle; }
.gx-view-tag-std    { background: var(--line-soft);  color: var(--text-muted); }
.gx-view-tag-shared { background: var(--info-soft);  color: var(--info); }
.gx-view-tag-def    { background: var(--brand-soft); color: var(--brand-text); }
.gx-view-confirm { display: flex; align-items: center; gap: 8px; padding: 8px 10px;
                   background: var(--danger-soft); border-radius: 10px; }
.gx-view-confirm-q { flex: 1; font-size: 12px; font-weight: 600; color: var(--text); }
.gx-pop-act-danger { background: var(--danger); color: #fff; border-color: var(--danger); }
.gx-pop-act-danger:hover { filter: brightness(.95); }
.gx-share-check { margin-top: 6px; }
.gx-notice-danger { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.gx-notice-danger .gx-notice-sub { color: var(--danger); }
.gx-pop-item-sub { display: block; font-size: 11px; font-weight: 500; color: var(--text-muted); margin-top: 1px; }

/* The last column's resize handle overhangs the table's right edge by 4px (right:-4), which pushed a
   spurious few-pixel horizontal scrollbar. Keep the last one inside the cell. */
.gx-th:last-child .gx-resize { right: 0; }

/* Bulk-bar export: the bar is pinned to the bottom, so its menu EXPANDS above the button in place
   (no measured/floating anchor). It's a normal surface card even though the bar is inverse. */
.gx-bulk-export { position: relative; display: inline-flex; }
/* Click-away catcher: over the grid, under the bar (z 20) so the bar's own buttons still work. */
.gx-bulk-backdrop { position: fixed; inset: 0; z-index: 15; background: transparent; }
.gx-bulk-export-chev { width: 12px; height: 12px; margin-left: 1px; opacity: .85; transition: transform .12s ease; }
.gx-bulk-export-menu {
    position: absolute; bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%);
    min-width: 230px; padding: 6px; border-radius: 14px; z-index: 60; animation: gx-pop-in .12s ease;
    /* Same inverse surface as the bulk bar, so the card reads as an extension of it in both themes. */
    background: var(--text); color: var(--surface);
    border: 1px solid rgba(148, 163, 184, .22); box-shadow: 0 14px 34px rgba(15, 23, 42, .32);
}
.gx-bulk-export-menu .gx-hm-item { color: var(--surface); }
.gx-bulk-export-menu .gx-pop-item-main { color: var(--surface); }
.gx-bulk-export-menu .gx-hm-hint { color: var(--surface); opacity: .5; }
.gx-bulk-export-menu .gx-hm-item:hover { background: rgba(148, 163, 184, .2); }

/* ── Colour-tinted leading roundel for the Views / Group / Columns panel rows (matches the
      filter kind roundels + header-menu tiles — gives the "boring" menus the same life). ──── */
.gx-row-ico { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px;
              border-radius: 9px; flex-shrink: 0; }
.gx-row-ico svg { width: 16px; height: 16px; }
.gx-row-ico-brand   { background: var(--brand-soft);  color: var(--brand-text); }
.gx-row-ico-accent  { background: var(--accent-soft); color: var(--accent); }
.gx-row-ico-info    { background: var(--info-soft);   color: var(--info); }
.gx-row-ico-ok      { background: var(--ok-soft);     color: var(--ok-strong); }
.gx-row-ico-warn    { background: var(--warn-soft);   color: var(--warn); }
.gx-row-ico-neutral { background: var(--line-soft);   color: var(--text-muted); }
/* When a view/group row is active the roundel fills brand. */
.gx-viewrow.is-on .gx-row-ico { background: var(--brand); color: #fff; }
.gx-viewrow-check { margin-left: auto; display: inline-flex; color: var(--brand-text); }
.gx-viewrow-check svg { width: 17px; height: 17px; }
/* The row now leads with a roundel, so it gets a touch more gap + the body fills the middle. */
.gx-viewrow { gap: 11px; }
.gx-viewrow .gx-viewrow-body { flex: 1; min-width: 0; }
/* The columns panel row leads with a section roundel — slightly smaller to sit beside the grip. */
.gx-colrow-ico { width: 26px; height: 26px; }
.gx-colrow-ico svg { width: 14px; height: 14px; }

/* ── A LENS's QUERY PLAN (/lenses/{id}) ───────────────────────────────────────────
      Two panes: the plan canvas and the editor for whatever stage is selected. One column on a
      narrow viewport — a 330px editor beside a chain the width of the screen is not a layout, and
      the canvas is the thing that has to breathe. Media queries can't live in the inline styles the
      config canvases use, so the pair lives here. */
.plan-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: start; }
@media (min-width: 1024px) { .plan-layout { grid-template-columns: minmax(0, 1fr) 330px; } }

/* The editor follows the canvas down a tall plan, but only where there's a column to follow. */
.plan-side { position: static; }
@media (min-width: 1024px) { .plan-side { position: sticky; top: 12px; } }


/* ── ROW ACTIONS (2026-08-20) ──────────────────────────────────────────────────────────────────
      EVERY grid's row actions are one floating CHIP at the row's right edge, revealed on hover.
      They used to be whatever each page felt like — three brand tiles here, a row of ghost buttons
      there, a 520px column of live <select>s somewhere else — so the same gesture looked different on
      every page and the widest of them spent half the grid on controls nobody was using.

      Two shapes, one chip, and the count of actions picks between them:
        · RowAction — one verb ("Open", "Manage", "Triage"). Fires straight away; burying a page's only
          action behind a menu would just charge a click for it.
        · RowMenu   — two or more, or anything destructive. Opens a menu, which extends by gaining a
          line where a row of buttons extends by getting wider.

      The chip is only on the row you're pointing at, which is what keeps the grid quiet — a brand tile
      repeated down every row is a column of colour competing with the data. Because it's transient it
      can afford a WORD, where a permanent one would have repeated "Actions" down the whole grid.
      Revealed by row hover, by keyboard focus, while the row is selected/active, and while its own menu
      is open; ALWAYS shown where there is no hover to reveal it (touch), because a button that never
      appears is not a button. */

/* The wrap is the floater: pinned to the right edge of the actions cell, so the chip costs the grid
   only a narrow gutter and spills over the empty tail of its neighbour instead of demanding width of
   its own. It stays a positioned ancestor, which is what the un-anchored menu fallback hangs off.

   NO `transform` here, ever: a transformed element becomes the containing block for its position:FIXED
   descendants, and the menu panel is fixed precisely so it can escape .gx-scroller's overflow. Centring
   with translateY(-50%) silently re-anchored every panel to this zero-width box and threw it off-screen.
   Stretching top-to-bottom and centring with flex does the same job with no transform.

   The inset is the grid chrome's own right margin (.gx-toolrow and .gx-foot both pad 16px), so the
   chip lands on the SAME vertical line as Export above it and the row count below it. Flush at right:0
   it sat on the card's edge, out of line with every other control on the page. */
.gx-rowmenu-wrap { position: absolute; right: 16px; top: 0; bottom: 0;
                   display: inline-flex; align-items: center; gap: 6px; }
/* The cell is the frame the chip pins to, and it must NOT clip — clipping is what gives the other
   columns their ellipsis, and here it would cut the chip off at the gutter's edge. It's the LAST cell,
   so it paints over its neighbour. */
.gx-td.gx-td-actions { position: relative; overflow: visible; }
/* On a phone card there is no gutter and no hover: the chip is a normal-flow control on its own
   labelled row, like every other field on the card. */
.gx-mcard-v .gx-rowmenu-wrap { position: relative; right: auto; top: auto; bottom: auto; }

/* The chip itself. Soft brand tile, filling solid on hover — the gx-mini-btn doctrine, one tile
   instead of three. Same shape whether it acts or opens; the glyph is what says which. */
.gx-rowchip {
    display: inline-flex; align-items: center; gap: 5px;
    height: 26px; padding: 0 8px 0 10px; white-space: nowrap;
    border: none; border-radius: 8px; text-decoration: none;
    background: var(--brand-soft); color: var(--brand-text); cursor: pointer;
    font-family: inherit; font-size: 11.5px; font-weight: 700;
    opacity: 0;
    transition: opacity .12s ease, background .12s ease, color .12s ease, box-shadow .12s ease;
}
.gx-tr:hover .gx-rowchip,
.gx-tr.is-selected .gx-rowchip,
.gx-tr.is-active .gx-rowchip,
.gx-mcard .gx-rowchip,
.gx-rowchip:focus-visible,
.gx-rowchip.is-open { opacity: 1; }
@media (hover: none) { .gx-rowchip { opacity: 1; } }
.gx-rowchip:hover, .gx-rowchip.is-open {
    background: var(--brand); color: var(--on-brand);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--brand) 35%, transparent);
}
.gx-rowchip:active { transform: scale(.94); }
.gx-rowchip:disabled { opacity: .45; cursor: default; box-shadow: none; }
/* A destructive single action reads danger from the start — there is no menu line to explain it in. */
.gx-rowchip.is-danger { background: var(--danger-soft); color: var(--danger); }
.gx-rowchip.is-danger:hover { background: var(--danger); color: #fff;
    box-shadow: 0 2px 8px color-mix(in srgb, var(--danger) 35%, transparent); }
/* The chevron flips while the menu is open — the trigger reports its own state for free. */
.gx-rowchip.gx-rowchip-menu svg { transition: transform .14s ease; }
.gx-rowchip.is-open svg { transform: rotate(180deg); }

/* The panel is .gx-pop (position:fixed) so it escapes .gx-scroller's overflow — a dropdown rendered
   inside the cell would be clipped by the grid's own scroll box. The scrim closes it on any outside
   click without needing a document listener. */
.gx-rowmenu-scrim { position: fixed; inset: 0; z-index: 9998; background: transparent; border: none; padding: 0; cursor: default; }
/* Degradation when the anchor measurement fails: sit under the trigger. Can be clipped by the grid's
   scroller, but a clipped menu beside its button beats a perfect one in the top-left corner. */
.gx-pop.gx-rowmenu-loose { position: absolute; right: 0; left: auto; top: calc(100% + 6px); }

/* A menu that carries a small FORM rather than a list of verbs — the escalate/decide/apply flows that
   used to sit inline in a 320px column, on every row, whether or not you were acting on it. */
.gx-hm-form { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px 11px; }
.gx-hm-form-label { font-size: 11px; font-weight: 700; letter-spacing: .02em; color: var(--text-muted); }
.gx-hm-form-row { display: flex; align-items: center; gap: 7px; }
.gx-hm-note { padding: 2px 12px 9px; font-size: 11.5px; color: var(--text-muted); line-height: 1.45; }

/* An armed destructive item — the first tap of the double-tap. It says what the second tap does,
   which a 30px tile never had room to. */
.gx-hm-item.is-arming { background: var(--danger-soft); align-items: flex-start; }
.gx-hm-item.is-arming .gx-pop-item-main { color: var(--danger); font-weight: 700; }
.gx-hm-item.is-danger .gx-pop-item-main { color: var(--danger); }
/* The armed item's icon keeps the first line's optical baseline once the item grows to two. */
.gx-hm-item.is-arming > svg, .gx-hm-item.is-arming > .lucide { margin-top: 2px; }

/* A menu item that carries a second line (a consequence, a hint). min-width:0 is what actually stops
   the overflow — without it a flex child refuses to shrink below its content and the text runs past the
   panel — and the wrap rules make sure a long word breaks instead of pushing the panel wider. */
.gx-hm-item-stack { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; text-align: left; }
.gx-hm-item-stack .gx-pop-item-main,
.gx-hm-item-stack .gx-pop-item-sub { white-space: normal; overflow-wrap: anywhere; line-height: 1.4; }


/* Visually hidden, still announced — for a header whose column is an icon gutter. */
.gx-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
              clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }


/* ── The curriculum report (/fields-of-study) ────────────────────────────────
   A dumbbell plot of two SHARES on one axis (share of catalogue vs share of the student body), a ranked
   named-field list with one mini-bar per measure, and the findings panel. Page-authored chart chrome, so
   it is tokens only — no raw surface/text/line hex below the token block (#581/#590). Built in CSS rather
   than SVG on purpose: a viewBox-scaled chart magnifies like an image on a 4K display (the origin
   widget's lesson), and these rows have to stay 13px text at any width. */

.fos-row { position: relative; display: grid; grid-template-columns: minmax(0, 240px) 1fr auto;
           align-items: center; column-gap: 14px; padding: 5px 0; border-radius: 8px;
           transition: background .12s, opacity .12s; }
.fos-row:hover { background: var(--line-soft); }

.fos-row-label { display: flex; align-items: center; gap: 9px; min-width: 0; }
.fos-row-name { font-size: 12.5px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The identity dot — AcademicGlyphs' colour, decorative only. The bars never wear it. */
.fos-dot { width: 24px; height: 24px; border-radius: 8px; flex-shrink: 0;
           display: inline-flex; align-items: center; justify-content: center; }

/* The plot track. Position is the encoding; the rule underneath is the axis, hairline and recessive. */
.fos-track { position: relative; height: 20px; min-width: 0; }
.fos-track-line { position: absolute; left: 0; right: 0; top: 50%; height: 1px;
                  background: var(--line); transform: translateY(-50%); }

/* The connector between the two pips IS the mismatch — 2px, round, the neutral ink so neither series
   claims it. Inset by the pip's radius so it meets the dots rather than poking past them. */
.fos-connect { position: absolute; top: 50%; height: 3px; border-radius: 9999px;
               background: var(--text-faint); transform: translateY(-50%); }

/* Each pip carries a 2px ring in the surface colour so the two stay legible where they overlap. */
.fos-pip { position: absolute; top: 50%; width: 11px; height: 11px; border-radius: 9999px;
           transform: translate(-50%, -50%); box-shadow: 0 0 0 2px var(--surface); }

.fos-row-values { display: flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 700;
                  font-variant-numeric: tabular-nums; min-width: 62px; justify-content: flex-end; }

/* The axis: three ticks under the plot, on the SAME grid as the rows so they line up with the pips.
   The right-hand cell names what the two direct-labelled numbers are, which no tick can say. */
.fos-axis { display: grid; grid-template-columns: minmax(0, 240px) 1fr auto; column-gap: 14px;
            align-items: center; margin-top: 6px; padding-top: 7px; border-top: 1px solid var(--line-soft); }
.fos-axis-ticks { position: relative; height: 14px; min-width: 0; }
.fos-tick { position: absolute; top: 0; transform: translateX(-50%); font-size: 10.5px;
            color: var(--text-faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
.fos-tick:first-child { transform: none; }
.fos-tick:last-child { transform: translateX(-100%); }
.fos-axis-key { min-width: 62px; text-align: right; font-size: 10.5px; color: var(--text-faint); white-space: nowrap; }

/* The hover panel. Anchored under its own row (over the one below), flipping above for the last few so
   it never falls out of the card. */
.fos-tip { position: absolute; right: 0; top: calc(100% - 2px); z-index: 30; min-width: 190px;
           background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
           padding: 10px 12px; box-shadow: 0 12px 28px rgba(15, 23, 42, .14); pointer-events: none; }
.fos-tip.is-above { top: auto; bottom: calc(100% - 2px); }
.fos-tip-title { font-size: 12.5px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
.fos-tip-grid { display: grid; grid-template-columns: 1fr auto; gap: 3px 16px; font-size: 12px; }
.fos-tip-grid span { color: var(--text-muted); }
.fos-tip-grid strong { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── The named-field ranking ── */

/* The name column is CAPPED, not elastic: a field of study is two or three words, and letting it take the
   slack left the two bars pinned to the right edge with 900px of nothing between them and the labels. The
   bars take the slack instead, which is also where the extra resolution is worth having. */
.fos-named-head, .fos-named { display: grid;
                              grid-template-columns: 24px minmax(0, 300px) minmax(0, 1fr) minmax(0, 1fr);
                              align-items: center; column-gap: 16px; }
.fos-named-head { padding: 0 0 7px; margin-bottom: 4px; border-bottom: 1px solid var(--line-soft);
                  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
                  color: var(--text-muted); }
/* Each measure column states its OWN maximum — the two bars are separate scales, and saying so is what
   keeps them from reading as one. */
.fos-named-head em { display: block; font-style: normal; font-weight: 400; text-transform: none;
                     letter-spacing: 0; color: var(--text-faint); }

.fos-named { padding: 6px 0; border-radius: 8px; transition: background .12s; }
.fos-named:hover { background: var(--line-soft); }
.fos-named-name { display: block; font-size: 12.5px; font-weight: 600; color: var(--text);
                  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fos-named-name.is-empty { color: var(--text-faint); font-weight: 400; font-style: italic; }
.fos-named-sub { display: block; font-size: 11px; color: var(--text-muted);
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.fos-mini { display: flex; align-items: center; gap: 8px; }
.fos-mini-track { flex: 1; min-width: 0; height: 8px; border-radius: 4px; background: var(--line-soft); overflow: hidden; }
.fos-mini-fill { height: 100%; border-radius: 4px; }
.fos-mini-val { font-size: 12px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums;
                min-width: 22px; text-align: right; }
.fos-mini-val.is-zero { color: var(--text-faint); font-weight: 400; }

.fos-more { display: inline-flex; align-items: center; gap: 6px; margin-top: 12px; padding: 6px 12px;
            border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
            font-size: 12px; font-weight: 600; color: var(--text-muted); cursor: pointer;
            transition: background .12s, color .12s; }
.fos-more:hover { background: var(--surface-muted); color: var(--text); }

/* ── The findings panel ── */

.fos-gap { display: flex; align-items: flex-start; gap: 11px; padding: 12px 14px; border-radius: 12px;
           background: var(--surface-muted); border: 1px solid var(--line-soft); }
/* A finding that is CLEAR still shows — "nothing wrong here" is the answer to the same question. */
.fos-gap.is-clear { background: var(--surface); }
.fos-gap-ico { width: 28px; height: 28px; border-radius: 9px; flex-shrink: 0;
               display: inline-flex; align-items: center; justify-content: center; }
.fos-gap-title { font-size: 13px; font-weight: 600; color: var(--text); }
.fos-gap-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.fos-gap-door { flex-shrink: 0; padding: 6px 12px; border: 1px solid var(--line); border-radius: 10px;
                background: var(--surface); font-size: 12px; font-weight: 600; color: var(--text);
                text-decoration: none; transition: background .12s; }
.fos-gap-door:hover { background: var(--surface-muted); }

/* One off-catalogue course, with how many students are on it. */
.fos-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 9999px;
            background: var(--surface); border: 1px solid var(--line); font-size: 11.5px; color: var(--text); }
.fos-chip em { font-style: normal; font-weight: 700; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* Phones: the label column stops competing with the plot, and the two mini-bar columns narrow. */
@media (max-width: 640px) {
  .fos-row { grid-template-columns: minmax(0, 1fr) auto; row-gap: 4px; }
  .fos-row-label { grid-column: 1; }
  .fos-row-values { grid-column: 2; }
  .fos-track { grid-column: 1 / -1; }
  .fos-axis { grid-template-columns: 1fr; }
  .fos-axis > span:first-child, .fos-axis-key { display: none; }   /* the key collides with the last tick */
  .fos-named-head, .fos-named { grid-template-columns: 24px minmax(0, 1fr) 74px 74px; column-gap: 8px; }
  .fos-named-head em { display: none; }
  .fos-gap { flex-wrap: wrap; }
}

/* The actions GUTTER's header. Not a column: no kebab, no drag handle, no resize grip, so nothing in it
   reacts to the pointer. It keeps .gx-th's own --surface-page band and gets NO hover rule — an earlier
   `background: inherit` here resolved to the transparent row behind it, so the cell flashed white on hover
   and read as a hole in the header band. */
.gx-th.gx-th-actions { cursor: default; }

/* ── The fields-of-study management grid ─────────────────────────────────────
   Strips that sit ABOVE the grid card: a confirmation of the last bulk edit (a rename can move dozens of
   courses under rows you can't see, so the count is the receipt), and the adopt-the-loose-wordings prompt.
   Tokens only — page-authored chrome still lives under the white-label rule. */

.fos-notice { display: flex; align-items: center; gap: 9px; margin-bottom: 12px; padding: 9px 12px;
              border-radius: 12px; background: var(--ok-soft); border: 1px solid var(--ok);
              font-size: 12.5px; color: var(--ok-strong); }
.fos-notice span { flex: 1; min-width: 0; }
.fos-notice-x { flex-shrink: 0; width: 22px; height: 22px; border: none; border-radius: 7px;
                background: transparent; color: var(--ok-strong); cursor: pointer;
                display: inline-flex; align-items: center; justify-content: center; }
.fos-notice-x:hover { background: var(--surface); }

.fos-adopt { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; padding: 11px 14px;
             border-radius: 14px; background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 32%, var(--surface)); }
.fos-adopt-ico { flex-shrink: 0; width: 28px; height: 28px; border-radius: 9px; background: var(--surface);
                 color: var(--warn); display: inline-flex; align-items: center; justify-content: center; }
.fos-adopt .fos-gap-title { color: var(--warn); }

/* ── One field of study, on its own page (/fields-of-study/{id}) ─────────────
   A form whose every save is a BULK edit, so it shows the courses it is about to rewrite and warns before
   the two edits that can't be taken back by repeating them (a merge, and a reclassify). */

/* The pre-save warning. is-merge is the louder one: renaming back afterwards does not un-merge. */
.fosf-warn { display: flex; align-items: flex-start; gap: 9px; margin-top: 12px; padding: 10px 12px;
             border-radius: 12px; background: var(--info-soft); border: 1px solid var(--brand-line);
             font-size: 12px; color: var(--info); line-height: 1.45; }
.fosf-warn.is-merge { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 32%, var(--surface)); color: var(--warn); }

/* One course this field is filed on — a door into the course, not just a name. */
.fosf-course { display: flex; align-items: center; gap: 11px; padding: 9px 12px; border-radius: 12px;
               background: var(--surface-muted); border: 1px solid var(--line-soft);
               text-decoration: none; transition: background .12s, border-color .12s; }
.fosf-course:hover { background: var(--line-soft); border-color: var(--line); }
.fosf-course-ico { flex-shrink: 0; width: 28px; height: 28px; border-radius: 9px;
                   display: inline-flex; align-items: center; justify-content: center; }
.fosf-course-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.fosf-course-name { font-size: 13px; font-weight: 600; color: var(--text);
                    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fosf-course-meta { font-size: 11.5px; color: var(--text-muted);
                    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Retire / bring back — a state with a consequence, so it gets a sentence and a button, not a bare switch. */
.fosf-toggle { display: flex; align-items: center; gap: 14px; padding: 12px 14px; border-radius: 12px;
               background: var(--surface-muted); border: 1px solid var(--line-soft); }
.fosf-toggle-title { font-size: 13px; font-weight: 600; color: var(--text); }
.fosf-toggle-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; line-height: 1.45; }

@media (max-width: 640px) {
  .fos-adopt { flex-wrap: wrap; }
  .fosf-toggle { flex-direction: column; align-items: stretch; }
}

/* ── DOCUMENT CATEGORIES: the drag-to-rearrange drop gaps ─────────────────────
   The gap above each row is a re-ORDER target; the row itself is a NEST target. Two intents need two
   targets, or a drag lands somewhere the user didn't mean. The gap is 0-high at rest so the tree keeps its
   density, opens only while a drag is live, and fills brand when it's the one that would receive the drop
   — the tags board's slot idiom, laid out vertically. */
.dcx-slot { height: 0; border-radius: 999px; transition: height .12s ease, background .12s ease; }
.dcx-slot.is-live { height: 6px; background: var(--line-soft); margin: 2px 0; }
.dcx-slot.is-live.is-over { height: 10px; background: var(--brand); }

/* The inventory tree's TOP-LEVEL drop strip — the way a nested place comes back out. It only exists while a
   drag is live (the page renders it conditionally), so it never takes space at rest. */
.invx-toplevel { display: flex; align-items: center; justify-content: center; gap: 7px; margin-bottom: 8px;
  padding: 9px 12px; border: 1px dashed var(--line); border-radius: 12px; background: var(--surface-muted);
  font-size: 11.5px; font-weight: 700; color: var(--text-muted);
  transition: background .12s ease, border-color .12s ease, color .12s ease; }
.invx-toplevel.is-over { border-color: var(--brand); border-style: solid; background: var(--brand-soft); color: var(--brand-text); }
/* ── THE PAGE HEADER (2026-08-21) ─────────────────────────────────────────────
   One treatment on every page: a 3px TENANT-PRIMARY rule across the top of a white card, a solid brand
   roundel with the area's icon in white, then the title.

   Tokens only, so a white-labelled college's own colour lands here automatically — which is the point of
   the change. It used to tint with the owning nav group's accent, so the top of every page was a different
   colour and a college's own brand appeared nowhere on it. The group colour still does its job in the nav,
   where it tells one door from another. */
.ui-pagehead {
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-top: 3px solid var(--brand);
    border-radius: 16px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
    overflow: hidden;
    margin-bottom: 20px;
    /* NEVER SHRINK. On a grid page the header is a flex item of the scaffold's min-h-0 column, next to a
       card whose content is a whole list — and a flex item's default shrink is 1. `overflow:hidden` above
       zeroes the automatic minimum size that would otherwise hold the box open, so a list long enough to
       overflow squashed the header and clipped its own title (2026-08-21: "the header is bugged" on
       courses/fields of study/applications, and on students the moment the filters were cleared and the
       row count went up). It reproduces only under a constrained viewport — which is why every screenshot,
       taken on an unbounded page, looked perfect. It collapsed to 8px against a natural 65px.
       Pinned by GridViewportLayoutTests. */
    flex: 0 0 auto;
}
/* COMPACT, AND ALIGNED WITH THE ROW BELOW (2026-08-21 — "where is the alignment? this must be compact!").
   The 16px side padding is the grid tool row's, so the roundel's left edge lines up with the search box and
   the Actions slot's right edge lines up with Export. A header is a line, not a band. */
.ui-pagehead-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 12px 16px; }
.ui-pagehead-ico {
    width: 38px;
    height: 38px;
    border-radius: 12px;
    background: var(--brand);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    box-shadow: 0 3px 9px var(--brand-ring);
}

/* A record hub's hero opens the same way as an ordinary page — the tenant rule across the top — while
   keeping its own body (avatar, status chips, stat strip), because it identifies a RECORD, not a page. */
.ui-pagehead-record { border-top: 3px solid var(--brand); }

/* The count belongs WITH the title, in the header — a grid reports it up rather than rendering a second
   title for it to sit beside, which is what made every grid page say its own name twice. */
.ui-pagehead-count {
    font-size: 11px;
    font-weight: 800;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--brand-soft);
    color: var(--brand-text);
}

/* ── ONE BLOCK: the header welded to the card beneath it (2026-08-21) ─────────
   His "merge those… one single block". Done on ADJACENCY rather than by rendering the header inside the
   body, because a body decides for itself what to render and several don't render their card until
   something happens — a header living inside one disappears with it. On adjacency the weld also corrects
   itself: when an error banner or an upload panel genuinely sits between them, they stop being one block,
   which is true.

   It welds to a GRID (.gx-card) or to a card that opts in (.ui-weld) — not to whatever happens to be next.
   A page that opens with a filter panel (giving, engagement) or a tab strip (notifications) keeps a header
   card of its own, because there the two really are separate things. */
/* BOTH HALVES OF THE WELD HANG OFF THE SAME `:has()`. They used to differ — the header flattened behind
   `:has()`, the card behind a plain `+` — so anything that killed the `:has()` rule (an unsupported engine
   drops the whole selector list) left a HALF-WELD: a rounded header with its margin and shadow intact, a
   gap, then a card whose top edge had lost its border and its corners. Written this way the pair either
   both apply or neither does, and "neither" is just two ordinary cards. */
.ui-pagestack:has(> .ui-pagehead + .gx-card) > .ui-pagehead,
.ui-pagestack:has(> .ui-pagehead + .ui-weld) > .ui-pagehead {
    margin-bottom: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    /* NO SEAM (2026-08-21 — "do we need that additional divider?"). It doesn't: the tool row already draws
       the one line that matters, between the chrome and the data. A second hairline a few pixels above it
       was drawing the join of two things we had just spent the effort making into one. The card below casts
       the shadow for the whole block. */
    border-bottom: 0;
    box-shadow: none;
}
.ui-pagestack:has(> .ui-pagehead + .gx-card) > .ui-pagehead + .gx-card,
.ui-pagestack:has(> .ui-pagehead + .ui-weld) > .ui-pagehead + .ui-weld {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    border-top: 0;
}

/* TWO ROWS, NOT THREE. With the header carrying the title, the count and the page's actions, the grid's
   page bar has nothing left to say and isn't rendered — so the tool row is now the card's first child and
   needs the top padding the bar used to provide — tight, since with no divider between them the title line
   and the tool row are one zone of chrome. */
.gx-card.has-pagehead > .gx-toolrow { padding-top: 4px; }

/* A welded Card carries the GRID's side padding, not the Card's, so its first control lines up with the
   header's roundel directly above it. Two selectors deep so it beats Tailwind's p-5/sm:p-6 on the body. */
.ui-weld > .ui-weld-body { padding: 4px 16px 16px; }

/* WELD THROUGH A ONE-COLUMN SHELL (2026-08-26 — "we have 2 separate blocks on the interviews page, and
   that's inconsistent with the calendar page"). The interviews page lays its card out in a flex shell so
   the "still to arrange" rail can sit beside it, which put the card one level below the header and broke
   the sibling weld: a rounded header, a gap, then a rounded card — two blocks for one page.

   It welds only when the shell holds a SINGLE column, which is the case whenever the rail is absent. With
   the rail present there genuinely are two cards side by side and the weld correctly stays off — the same
   self-correcting principle as the banner/upload case above: they stop being one block when they are not
   one block. Both halves hang off the same `:has()`, for the half-weld reason given there. */
.ui-pagestack:has(> .ui-pagehead + .ui-weld-shell > :only-child > .ui-weld) > .ui-pagehead {
    margin-bottom: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    border-bottom: 0;
    box-shadow: none;
}
.ui-pagestack:has(> .ui-pagehead + .ui-weld-shell > :only-child > .ui-weld) > .ui-pagehead + .ui-weld-shell > :only-child > .ui-weld {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    border-top: 0;
}

/* ── The KANBAN BOARD's movable cards (2026-08-21) ────────────────────────────
   "Cards on the board shouldn't display a dropdown, but should be draggable instead." A card that can be
   moved has to LOOK like it can be picked up, and the column you're about to drop it in has to be obvious
   while your hand is on the mouse — a drop that lands somewhere you didn't expect is worse than the
   dropdown was. The card keeps its grab cursor and a lift on hover; the target column tints and outlines.
   Boards that don't pass OnMove get none of this and stay read-only. */
.kb-col { border-radius: 14px; padding: 2px; transition: background .12s ease, box-shadow .12s ease; }
/* A card that leads somewhere is clickable ANYWHERE ("I think the entire card should take to the page"):
   the title stays the link — one link, correctly labelled for a screen reader — and this stretches its hit
   area across the whole card. Anything the card needs to keep clickable in its own right must sit above
   the overlay (position:relative + z-index:2), which is why nothing else in an activity card is a link. */
.kb-card { position: relative; }
.kb-card-link::after { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: 12px; }
.kb-card-link:focus-visible { outline: none; }
.kb-card:has(.kb-card-link:focus-visible) { box-shadow: 0 0 0 3px var(--brand-ring); border-color: var(--brand-line); }
.kb-col.is-target { background: var(--brand-soft); box-shadow: inset 0 0 0 2px var(--brand-line); }
.kb-card.is-movable { cursor: grab; transition: box-shadow .12s ease, transform .12s ease; }
.kb-card.is-movable:hover { box-shadow: 0 3px 10px rgba(15, 23, 42, .10); transform: translateY(-1px); }
.kb-card.is-movable:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); border-color: var(--brand-line); }
.kb-card.is-movable:active { cursor: grabbing; }
/* The original stays in place while the browser drags its ghost — dimming it is what says "this one is
   in the air", so an interrupted drag reads as unfinished rather than as a card that failed to move. */
.kb-card.is-dragging { opacity: .45; }

/* ── ANOTHER SHAPE IN THE GRID'S CARD (2026-08-21) ────────────────────────────
   When a GridNext renders a Body instead of its table — the activity board, the activity calendar — the
   shape takes the same box the table had: it fills what's left of the card and scrolls inside it, so the
   page never scrolls as a whole. The side padding matches .gx-toolrow's, so the first control of the
   shape lines up with the search box above it. */
.gx-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 12px 16px 16px; }


/* ── THE ACTIVITY EDITOR (2026-08-21) ─────────────────────────────────────────
   The dense composer idiom, done as classes rather than a wall of inline styles. The editor itself rides
   `.compact-form` (the kit's 30px/caps-micro-label treatment); these are the three things that treatment
   doesn't cover: the type chip, the inset panel that groups a block of fields, and the ✕ on a chip. */
.act-type-chip {
    display: inline-flex; align-items: center; gap: 5px; height: 27px; padding: 0 10px;
    border: 1px solid var(--line); border-radius: 9999px; background: var(--surface);
    font-family: inherit; font-size: 12px; font-weight: 600; color: var(--text-muted); cursor: pointer;
    transition: border-color .12s ease, background .12s ease, color .12s ease;
}
.act-type-chip svg { width: 13px; height: 13px; }
.act-type-chip:hover:not(.is-on) { border-color: var(--text-faint); color: var(--text); }
.act-type-chip:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }

/* An inset block of fields — the composer's "Options" panel, which is what makes a dense form scannable:
   the fields that belong together sit on the recessed tone instead of running on down the card. */
.act-edit-panel {
    margin-top: 14px; padding: 12px 14px; border: 1px solid var(--line-soft); border-radius: 12px;
    background: var(--surface-muted);
}
.act-edit-check {
    display: flex; align-items: center; gap: 8px; margin-top: 12px;
    font-size: 12.5px; color: var(--text-muted); cursor: pointer;
}
.act-chip-x {
    display: inline-flex; align-items: center; justify-content: center; margin-left: 2px;
    border: none; background: transparent; color: var(--text-faint); cursor: pointer; padding: 0; line-height: 1;
}
.act-chip-x svg { width: 11px; height: 11px; }
.act-chip-x:hover { color: var(--danger); }


/* THE FORM GRID, in real CSS. `sm:grid-cols-12` / `sm:col-span-*` are used all over the form pages and
   NONE of them exist in the built tailwind.css — it is a hand-rebuilt artifact (npm run build:css) and it
   carries no `sm:` variants at all, so every one of those layouts has been silently falling back to a
   single stacked column. Rather than depend on an artifact this component can't verify, it brings its own:
   one row on a phone, twelve columns from 640px, `c4`/`c5`/`c6`/`c7`/`c12` to claim a span. */
.act-fgrid { display: grid; grid-template-columns: 1fr; gap: 12px; align-items: start; }
.act-fgrid > .c3, .act-fgrid > .c4, .act-fgrid > .c5,
.act-fgrid > .c6, .act-fgrid > .c7, .act-fgrid > .c12 { min-width: 0; }
@media (min-width: 640px) {
    .act-fgrid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
    .act-fgrid > .c3 { grid-column: span 3; }
    .act-fgrid > .c4 { grid-column: span 4; }
    .act-fgrid > .c5 { grid-column: span 5; }
    .act-fgrid > .c6 { grid-column: span 6; }
    .act-fgrid > .c7 { grid-column: span 7; }
    .act-fgrid > .c12 { grid-column: span 12; }
}

/* ── THE ACTIVITY PAGE (2026-08-21) — a MANAGEMENT page, in the academic-year page's language ──────────
   Every control on it commits by itself; there is no view mode, no edit mode and no Save button. These are
   the pieces that idiom needs and the kit doesn't already name. */

/* The one line IS the record's name, so it is the h1 — and it is typed in place. It reads as a heading
   until you touch it, which is what tells you it can be changed at all. */
.actp-title {
    width: 100%; border: 1px solid transparent; border-radius: 8px; margin: -3px -7px 0; padding: 1px 7px;
    background: transparent; font-family: inherit; font-size: 18px; font-weight: 700; letter-spacing: -.02em;
    color: var(--text); transition: background .12s ease, border-color .12s ease;
}
.actp-title:hover { background: var(--surface-muted); }
.actp-title:focus { outline: none; background: var(--surface); border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }

.actp-flag {
    display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 9999px;
    background: var(--surface-muted); border: 1px solid var(--line-soft); color: var(--text-muted);
    font-size: 10.5px; font-weight: 800; letter-spacing: .05em;
}
.actp-flag.is-alarm { background: var(--danger-soft); border-color: var(--danger-soft); color: var(--danger); }

.actp-del {
    display: inline-flex; align-items: center; gap: 6px; padding: 6px 11px; border: 1px solid var(--line);
    border-radius: 9px; background: var(--surface); font-family: inherit; font-size: 12px; font-weight: 600;
    color: var(--text-muted); cursor: pointer;
}
.actp-del:hover { border-color: var(--danger); color: var(--danger); }
.actp-del.is-armed { border: none; background: var(--danger); color: #fff; font-weight: 700; }

/* THE STAGE STRIP — the lifecycle as tiles. Click one and the work has moved; that is the whole point of
   the archetype. Where it is now is filled, everything behind it is ticked, the rest are outlines. */
.actp-stage {
    display: flex; align-items: center; gap: 7px; flex: 1; min-width: 124px; padding: 7px 10px;
    border: 1px solid var(--line-soft); border-radius: 12px; background: var(--surface);
    font-family: inherit; cursor: pointer; text-align: left;
    transition: border-color .12s ease, background .12s ease;
}
.actp-stage:hover:not(:disabled) { border-color: var(--text-faint); background: var(--surface-muted); }
.actp-stage:disabled { cursor: default; }
.actp-stage-dot {
    width: 19px; height: 19px; border-radius: 6px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
}
.actp-stage-label { font-size: 12px; font-weight: 700; color: var(--text-muted); }
.actp-stage.is-done .actp-stage-label { color: var(--text); }

/* A section card and its header — 26px tinted roundel, uppercase tracked label, count, one-line rule. */
.actp-card { background: var(--surface); border: 1px solid var(--line-soft); border-radius: 16px; box-shadow: 0 1px 2px rgba(15, 23, 42, .05); overflow: hidden; }
.actp-head { display: flex; align-items: center; gap: 8px; padding: 9px 14px 8px; border-bottom: 1px solid var(--line-soft); }
.actp-head-title { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }
.actp-head-count { font-size: 10.5px; font-weight: 800; padding: 1px 7px; border-radius: 9999px; }
.actp-head-rule { margin: 2px 0 0; font-size: 11px; color: var(--text-faint); }
.actp-head-btn { height: 28px; padding: 0 11px; border: 1px solid; border-radius: 9px; font-family: inherit; font-size: 11.5px; font-weight: 700; cursor: pointer; white-space: nowrap; }
.actp-body { padding: 12px 14px 13px; }
.actp-none { margin: 0; font-size: 12.5px; color: var(--text-faint); }
.actp-mini { display: block; font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint); }
.actp-field { margin-bottom: 9px; }
.actp-field:last-of-type { margin-bottom: 0; }
.actp-check { display: flex; align-items: center; gap: 8px; margin-top: 11px; font-size: 12px; color: var(--text-muted); cursor: pointer; }

/* A priority is a small closed set, so it is a row of toggles rather than a dropdown — one click, committed. */
.actp-pri { height: 26px; padding: 0 10px; border: 1px solid var(--line); border-radius: 9999px; background: var(--surface); font-family: inherit; font-size: 11.5px; font-weight: 600; color: var(--text-muted); cursor: pointer; }
.actp-pri:hover:not(.is-on) { border-color: var(--text-faint); color: var(--text); }

/* The dashed reveal-on-demand add row — the pipeline's "+ Checklist item" idiom, never a parked form. */
.actp-add { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px; margin-top: 10px; padding: 9px 11px; border: 1px dashed var(--brand-line); border-radius: 11px; background: var(--brand-soft); }

/* WHO IT'S ABOUT, as a record rather than a run of grey words. "Task · Priya Garcia · Student" put the
   person — the thing you actually want to click — in the middle of a sentence. */
.actp-subject {
    display: inline-flex; align-items: center; gap: 8px; padding: 3px 10px 3px 4px;
    border: 1px solid var(--line-soft); border-radius: 9999px; background: var(--surface-muted);
    text-decoration: none; transition: border-color .12s ease, background .12s ease;
}
.actp-subject:hover { border-color: var(--brand-line); background: var(--brand-soft); }
.actp-subject.is-flat { cursor: default; }
.actp-subject-body { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.actp-subject-name { font-size: 12.5px; font-weight: 700; color: var(--text); }
.actp-subject-kind { font-size: 10.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.actp-followup {
    display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 600;
    color: var(--brand); text-decoration: none;
}

/* ── THE TIMELINE — rich, and read at a glance ("a rich timeline, impactful") ──────────────────────────
   A rail with a RINGED ICON per entry saying what sort of change it was, a headline naming the event, the
   time in words, the sentence, then who did it with their face beside their name. */
.actp-tl { list-style: none; margin: 0; padding: 0; position: relative; }
.actp-tl::before { content: ""; position: absolute; left: 11px; top: 16px; bottom: 10px; width: 2px; background: var(--line-soft); }
.actp-tl-item { position: relative; display: flex; gap: 10px; padding-bottom: 13px; }
.actp-tl-item:last-child { padding-bottom: 0; }
.actp-tl-badge {
    position: relative; z-index: 1; flex-shrink: 0;
    width: 24px; height: 24px; border-radius: 9999px; border: 1.5px solid;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 0 0 3px var(--surface);
}
.actp-tl-badge svg { width: 12px; height: 12px; }
.actp-tl-body { min-width: 0; flex: 1; padding-top: 1px; }
.actp-tl-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.actp-tl-title { font-size: 12.5px; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
.actp-tl-when { font-size: 11px; color: var(--text-faint); }
.actp-tl-line { margin: 2px 0 0; font-size: 12px; color: var(--text-muted); overflow-wrap: anywhere; }
.actp-tl-line strong { font-weight: 700; color: var(--text); }
.actp-tl-who { display: flex; align-items: center; gap: 5px; margin-top: 5px; font-size: 11px; font-weight: 600; color: var(--text-muted); }
.actp-tl-system { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 9999px; background: var(--surface-muted); color: var(--text-faint); font-weight: 600; }
/* CANCELLED IS AN EXIT, NOT A STAGE. It stands apart from the numbered flow behind a divider, wears an ✕
   instead of a number, and never puts a tick on the steps it skipped. When the work IS cancelled the flow
   tiles drain, because none of them happened. */
.actp-stage-sep { align-self: stretch; width: 1px; margin: 2px 4px; background: var(--line-soft); flex-shrink: 0; }
.actp-stage.is-exit { flex: 0 1 auto; min-width: 118px; }
.actp-stage.is-exit:hover:not(:disabled) { border-color: var(--danger); }
.actp-stage.is-off { opacity: .5; }

/* ── THE ESTATE MAP (/accommodation/dashboard) ────────────────────────────────
   The inventory tree draws the estate as a LIST, with indentation standing in for containment. The map
   draws containment as containment: a campus is a box, and the buildings inside it are boxes inside that
   box, down to the room, and the room names who sleeps in it. A shape you can read at a glance instead
   of a hierarchy you reconstruct from indents. The map only shows: it moves, renames and deletes nothing. */
.emap-wrap { display: flex; flex-direction: column; gap: 10px; min-height: 0; flex: 1; }
.emap-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* The canvas can scroll both axes, but only as the escape hatch (one place genuinely too wide): by
   default cards shrink and bodies wrap, so the estate reflows to the width it is given rather than
   running off the right edge. Content centres in the canvas — a map sits in the middle of its table. */
.emap-canvas {
    flex: 1; min-height: 0; overflow: auto; padding: 12px;
    background: var(--surface-page); border: 1px solid var(--line-soft); border-radius: 14px;
    scrollbar-width: thin;
}
.emap-canvas-inner { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: center; gap: 12px; }

/* ── One card per place. The level's accent rides the header, so containment reads as colour nesting. ── */
.emap-card {
    --accent: var(--text-muted);
    border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
    position: relative; flex: 0 1 auto; max-width: 100%; align-self: flex-start;
}
.emap-card.is-archived { opacity: .62; border-style: dashed; }

.emap-head { display: flex; align-items: center; gap: 7px; padding: 7px 9px; border-radius: 11px 11px 0 0;
             background: color-mix(in srgb, var(--accent) 12%, var(--surface)); }
/* A room is a small tile, not a box: its name on top and its seats under it, the kind in the tooltip.
   Rooms are the estate's bulk, so the leaf spends nothing on chrome — no icon tile, no second header row. */
.emap-card.is-leaf { display: inline-flex; flex-direction: column; align-items: stretch;
                     background: color-mix(in srgb, var(--accent) 9%, var(--surface)); }
.emap-card.is-leaf .emap-head { background: transparent; padding: 4px 8px 2px; gap: 6px; }
.emap-card.is-leaf .emap-code { font-size: 11px; }
.emap-ico { width: 22px; height: 22px; border-radius: 7px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
            background: color-mix(in srgb, var(--accent) 22%, var(--surface)); color: color-mix(in srgb, var(--accent) 62%, var(--text)); }
.emap-ico svg { width: 13px; height: 13px; }
.emap-code { font-size: 12px; font-weight: 800; color: var(--text); white-space: nowrap; }
.emap-name { font-size: 11px; font-weight: 600; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 170px; }

/* ── Permission catalogue (the role builder) ────────────────────────────────
   Each module block carries its accent as --pa; every colour below derives from it through
   color-mix against the tokens, so the same rules re-skin in dark. An area is ONE LEVEL BAR: its
   stops (Viewer / Officer / Admin) in ladder order, filled up to the level the role holds. */
/* The module score tiles: --cols balances the rows (set from the module count); narrow screens fall
   back to as many 150px tiles as fit. */
.role-tiles-grid { display: grid; grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr)); gap: 10px; padding: 13px 16px; }
@media (max-width: 900px) { .role-tiles-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); } }
.role-identity { display: grid; grid-template-columns: minmax(220px, 1fr) minmax(0, 2fr) auto; gap: 14px; }
@media (max-width: 720px) { .role-identity { grid-template-columns: 1fr; } }

/* One row per area: the area's name (module caption under it) and its level band — the stage band's
   shape: a 9px pill per level with its name under it, 6px apart. Levels are cumulative: every level up
   to the held one is lit, in three progressing shades of the module colour (Viewer lightest, Admin
   full); the held one wears a ring; the levels above it stay unlit. */
.perm-rows { display: flex; flex-direction: column; }
.perm-row { display: flex; align-items: center; gap: 18px; padding: 7px 0; border-bottom: 1px solid var(--line-soft); min-width: 0; }
.perm-row:last-of-type { border-bottom: 0; }
.perm-area { flex: 0 0 200px; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.perm-area-label { font-size: 12.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.perm-area-module { display: inline-flex; align-items: center; gap: 5px; font-size: 10.5px; font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.perm-area-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--pa); flex-shrink: 0; }
.perm-bar { display: flex; gap: 6px; width: calc(var(--stops) * 150px); max-width: 100%; }
.perm-seg { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 4px; padding: 2px 0;
            font-family: inherit; background: transparent; border: 0; cursor: pointer; border-radius: 6px; }
.perm-seg-pill { display: block; height: 9px; border-radius: 999px; background: var(--line); opacity: .7; transition: background .13s, opacity .13s, box-shadow .13s; }
.perm-seg-label { font-size: 10.5px; font-weight: 600; color: var(--text-muted); text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.perm-seg:hover .perm-seg-pill { background: var(--pa); opacity: .35; }
.perm-seg.is-on .perm-seg-pill { opacity: 1; }
.perm-seg-1.is-on .perm-seg-pill { background: color-mix(in srgb, var(--pa) 45%, var(--surface)); }
.perm-seg-2.is-on .perm-seg-pill { background: color-mix(in srgb, var(--pa) 72%, var(--surface)); }
.perm-seg-3.is-on .perm-seg-pill { background: var(--pa); }
.perm-seg.is-on .perm-seg-label { color: var(--text); }
.perm-seg.is-top .perm-seg-pill { box-shadow: 0 0 0 2px color-mix(in srgb, var(--pa) 27%, transparent); }
.perm-seg.is-top .perm-seg-label { font-weight: 800; }
.perm-seg:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
span.perm-seg { cursor: default; }
/* The roles grid's per-module cell: the same band, pills only, the held level named once under it. */
.role-band { display: flex; flex-direction: column; gap: 3px; width: 104px; }
.role-band-pills { display: flex; gap: 4px; }
.role-band .perm-seg { padding: 0; gap: 0; }
.role-band .perm-seg-pill { height: 7px; }
.role-band-level { font-size: 10.5px; font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.role-band:has(.is-on) .role-band-level { color: var(--text); }
@media (max-width: 640px) {
    .perm-row { flex-direction: column; align-items: stretch; gap: 6px; }
    .perm-area { flex-basis: auto; }
}
.emap-roll { margin-left: auto; display: flex; align-items: center; gap: 5px; flex-shrink: 0; }
.emap-pill { font-size: 9.5px; font-weight: 800; letter-spacing: .02em; padding: 1px 6px; border-radius: 999px;
             background: var(--surface); border: 1px solid var(--line-soft); color: var(--text-muted); white-space: nowrap; }
.emap-pill.is-warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 40%, var(--surface)); color: var(--warn); }
.emap-twisty { width: 18px; height: 18px; border: none; background: transparent; padding: 0; cursor: pointer;
               color: var(--text-muted); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.emap-twisty svg { width: 12px; height: 12px; }

.emap-body { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: center; gap: 8px; padding: 8px; min-width: 120px; }
/* An empty branch keeps a labelled floor rather than collapsing away, so it still reads as a place. */
.emap-empty { font-size: 10.5px; color: var(--text-faint); padding: 10px 12px; border: 1px dashed var(--line-soft);
              border-radius: 9px; width: 100%; text-align: center; }

/* ── The room is the leaf: its beds are its content. A taken bed is a seat wearing its resident's face
   AND name; an empty one is a dashed pip. Seats stack one per line so every name reads in full, and the
   empty pips sit in a row of their own beneath them. ── */
.emap-beds { display: flex; flex-wrap: wrap; align-items: center; gap: 3px 6px; padding: 0 8px 6px; max-width: 200px; }
.emap-bed { width: 18px; height: 18px; border-radius: 6px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
            background: var(--surface-muted); border: 1px dashed var(--line); color: var(--text-faint); }
.emap-bed svg { width: 10px; height: 10px; }
.emap-seat { flex: 1 0 100%; min-width: 0; display: flex; align-items: center; gap: 6px; }
.emap-seat img { flex-shrink: 0; }
.emap-seat-name { font-size: 11px; font-weight: 600; color: var(--text); white-space: nowrap;
                  overflow: hidden; text-overflow: ellipsis; }
.emap-nobeds { font-size: 9.5px; font-weight: 800; letter-spacing: .04em; padding: 2px 7px; border-radius: 999px;
               background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 40%, var(--surface)); color: var(--warn); }

/* A place that threw while drawing. It occupies the slot its card would have, so the estate keeps its shape
   and the failure is local and NAMED instead of being an empty page. */
.emap-broken {
    display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; align-self: flex-start;
    padding: 7px 10px; border-radius: 10px; font-size: 11.5px; font-weight: 700;
    background: var(--danger-soft); border: 1px dashed var(--danger); color: var(--danger);
}
.emap-broken svg { width: 13px; height: 13px; flex-shrink: 0; }

@media (prefers-reduced-motion: reduce) {
    .emap-card { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════════
   THE PERSONAL + ADMIN DESKS (2026-08-25) — the notification inbox, the impersonation console, the
   settings desk and the integration hub.

   These four pages were rebuilt together because they shared one fault: they were assembled from
   Tailwind slate literals (`text-slate-800`, `divide-slate-100`, a raw `#cbd5e1` dot) at a time when
   that was allowed, so on the near-black ramp they were light-theme colours painted on a dark page.
   Everything below is TOKENS ONLY, which is what makes them re-theme for free — and what makes them a
   white-labelled college's own colour rather than indigo.
   ═════════════════════════════════════════════════════════════════════════════════════════════════ */

/* ── The inbox (/notifications) ─────────────────────────────────────────────────────────────────────
   The bell panel's language, given room: the typed roundel, the unread rail, one heading per day. */
/* The inbox is ONE panel: the scope strip across the top, the day-grouped rows beneath — the search
   results page's shape, because narrowing a list of records is the same job in both places. The rows ARE
   the panel's edges (a day heading is a full-bleed band, an unread tint runs to the border), so nothing
   here has body padding. */
.inbox-panel { margin-top: 16px; background: var(--surface); border: 1px solid var(--line-soft);
    border-radius: 14px; overflow: hidden; }

.inbox-day { display: flex; align-items: center; gap: 10px; padding: 11px 18px 9px;
    background: var(--surface-page); border-bottom: 1px solid var(--line-soft); }
.inbox-day + .inbox-row { border-top: none; }
/* --text, not --text-muted: this band is the RECESSED tone, where muted measures 4.4:1 and fails the
   axe gate — the same trap the grid's header band hit. */
.inbox-day-label { font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text); white-space: nowrap; }
.inbox-day-rule { flex: 1; height: 1px; background: var(--line-soft); }
.inbox-day-n { font-size: 10.5px; font-weight: 700; color: var(--text-muted); }

.inbox-row { position: relative; display: flex; align-items: flex-start; gap: 12px; padding: 13px 18px 13px 20px;
    border-top: 1px solid var(--line-soft); transition: background .12s ease; }
.inbox-row:first-of-type { border-top: none; }
.inbox-row:hover { background: var(--line-soft); }
/* THE RAIL IS THE STATE. A bold title alone is not a state — it is a font weight, and a read row two
   pixels lighter than an unread one is a difference nobody sees. The rail is the same 3px leading edge
   the grid uses for a selected row, so "this one still wants you" reads down the whole list at a glance. */
.inbox-rail { position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: transparent; }
.inbox-row.is-unread { background: color-mix(in srgb, var(--brand-soft) 45%, var(--surface)); }
.inbox-row.is-unread:hover { background: color-mix(in srgb, var(--brand-soft) 70%, var(--surface)); }
.inbox-row.is-unread .inbox-rail { background: var(--brand); }
.inbox-row.is-unread .inbox-title { font-weight: 700; color: var(--text); }

.inbox-ico { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px;
    border-radius: 10px; flex-shrink: 0; margin-top: 1px; }
.inbox-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; padding: 0;
    border: none; background: none; font-family: inherit; text-align: left; cursor: pointer; }
.inbox-title { font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.35; }
.inbox-text { font-size: 12px; color: var(--text-muted); line-height: 1.45;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.inbox-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 2px;
    font-size: 10.5px; color: var(--text-muted); }
/* --text-muted, NOT --text-faint. The faint tone is 2.8:1 on the card and the axe gate caught it the
   moment these pages were added to it: 10.5px uppercase is small text, and small text needs 4.5:1.
   (The same is true of every --text-faint label on --surface — see .desksec-rule and .desklabel.) */
.inbox-kind { font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--text-muted); }
.inbox-sev { padding: 1px 7px; border-radius: 9999px; font-size: 10px; font-weight: 800; }
.inbox-when { color: var(--text-muted); }
/* The "Open" affordance appears on the row you are pointing at — a permanent one would print the word
   down the whole list, and most rows go somewhere. */
.inbox-go { display: inline-flex; align-items: center; gap: 3px; margin-left: auto; padding: 1px 7px;
    border-radius: 9999px; font-weight: 700; color: var(--brand-text); background: var(--brand-soft);
    opacity: 0; transition: opacity .12s ease; }
.inbox-row:hover .inbox-go, .inbox-body:focus-visible .inbox-go { opacity: 1; }

.inbox-tick { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
    flex-shrink: 0; border: 1px solid var(--line); border-radius: 9px; background: var(--surface);
    color: var(--text-muted); cursor: pointer; transition: background .12s ease, color .12s ease, border-color .12s ease; }
.inbox-tick:hover { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 32%, var(--surface)); color: var(--ok-strong); }

/* The row's two actions sit together at its end: the tick (unread rows only) and dismiss. Dismiss is the
   same quiet square, warming to the danger hue on hover — removing a message is small, but it is final. */
.inbox-acts { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; }
.inbox-dismiss { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
    flex-shrink: 0; border: 1px solid var(--line); border-radius: 9px; background: var(--surface);
    color: var(--text-muted); cursor: pointer; transition: background .12s ease, color .12s ease, border-color .12s ease; }
.inbox-dismiss:hover { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 32%, var(--surface)); color: var(--danger); }

/* The foot of a paged list: the sentinel that pulls the next page in as it scrolls into view (and offers a
   button for the same), or the line that says the oldest message is in. */
.inbox-more { display: flex; justify-content: center; padding: 14px 18px; border-top: 1px solid var(--line-soft); }
.inbox-end { margin: 0; padding: 12px 18px; text-align: center; font-size: 12px; color: var(--text-muted);
    border-top: 1px solid var(--line-soft); }

.inbox-empty { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 56px 24px; }
.inbox-empty-ico { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px;
    border-radius: 16px; margin-bottom: 14px; }
.inbox-empty-title { margin: 0; font-size: 14px; font-weight: 700; color: var(--text); }
.inbox-empty-sub { margin: 4px 0 0; font-size: 12.5px; color: var(--text-muted); max-width: 40ch; }

@media (hover: none) {
    /* A control that never appears is not a control — on touch there is no pointer to reveal it with. */
    .inbox-go { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .inbox-row, .inbox-go, .inbox-tick { transition: none; }
}

/* ── The section rail shared by the settings desk and the integration hub ───────────────────────────
   A settings page is a set of UNRELATED subjects that happen to be saved together — a colour, an
   address, a domain. A single scroll makes you hunt; a rail names all of them at once and shows which
   one you are in. It is the same two-column management shape the document-categories page uses, with
   the fixed list on the left instead of the selection on the right, because here the LIST is the
   navigation rather than the substance. */
.deskgrid { display: grid; gap: 16px; align-items: start; grid-template-columns: 232px minmax(0, 1fr); }
@media (max-width: 900px) { .deskgrid { grid-template-columns: minmax(0, 1fr); } }

.desknav { display: flex; flex-direction: column; gap: 2px; padding: 8px;
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 16px;
    box-shadow: 0 1px 2px rgba(15,23,42,.05); position: sticky; top: 8px; }
@media (max-width: 900px) { .desknav { position: static; flex-direction: row; flex-wrap: wrap; } }
.desknav-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px; border: none;
    border-radius: 11px; background: transparent; font-family: inherit; font-size: 12.5px; font-weight: 600;
    color: var(--text); text-align: left; cursor: pointer; transition: background .12s ease, color .12s ease; }
@media (max-width: 900px) { .desknav-item { width: auto; } }
.desknav-item:hover { background: var(--line-soft); }
.desknav-item.is-on { background: var(--brand-soft); color: var(--brand-text); }
.desknav-ico { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px;
    border-radius: 8px; flex-shrink: 0; background: var(--surface-muted); color: var(--text-muted); }
.desknav-item.is-on .desknav-ico { background: var(--brand); color: #fff; }
.desknav-ico svg { width: 14px; height: 14px; }
/* A dot, not a word: the rail says WHICH section needs attention without a sentence that would wrap
   every row onto two lines. The section itself explains. */
.desknav-flag { margin-left: auto; width: 7px; height: 7px; border-radius: 9999px; flex-shrink: 0; }

/* A section card: 26px tinted roundel, uppercase tracked label, one-line rule — the management-page
   section header, lifted whole so these desks read like the pages they sit beside. */
.desksec { background: var(--surface); border: 1px solid var(--line-soft); border-radius: 16px;
    box-shadow: 0 1px 2px rgba(15,23,42,.05); overflow: hidden; }
.desksec + .desksec { margin-top: 16px; }
.desksec-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px 11px;
    border-bottom: 1px solid var(--line-soft); }
.desksec-ico { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px;
    border-radius: 8px; flex-shrink: 0; }
.desksec-ico svg { width: 14px; height: 14px; }
.desksec-label { font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-muted); }
.desksec-rule { margin: 2px 0 0; font-size: 11px; color: var(--text-muted); }
.desksec-body { padding: 16px; }
.desksec-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    padding: 12px 16px; border-top: 1px solid var(--line-soft); background: var(--surface-page); }

/* The field grid every desk section lays its inputs on — a fixed minimum, so two short fields sit
   side by side and a long one takes the row, without a per-page flex incantation. */
.deskfields { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.deskfield-wide { grid-column: 1 / -1; }
.desklabel { display: block; margin: 0 0 5px; font-size: 10.5px; font-weight: 800; letter-spacing: .05em;
    text-transform: uppercase; color: var(--text-faint); }
.deskhint { margin: 5px 0 0; font-size: 11px; color: var(--text-muted); }

/* A row of switchable things (feature flags, connectors) — the label carries the explanation, the
   switch carries the state, and the whole row is a hairline-separated band rather than a card each. */
.deskrow { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 13px 16px;
    border-top: 1px solid var(--line-soft); }
.deskrow:first-of-type { border-top: none; }
.deskrow-text { flex: 1; min-width: 200px; }
.deskrow-title { margin: 0; font-size: 13px; font-weight: 600; color: var(--text); }
.deskrow-sub { margin: 3px 0 0; font-size: 11.5px; color: var(--text-muted); line-height: 1.45; }

/* ── The integration hub (/integrations) ───────────────────────────────────────────────────────────
   A connector's STATUS is a colour and a word in the same place on every card, because the whole point
   of the page is answering "is anything wrong" without reading six paragraphs. */
.conn-dot { width: 8px; height: 8px; border-radius: 9999px; flex-shrink: 0; }
.conn-status { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 9999px;
    font-size: 10.5px; font-weight: 800; letter-spacing: .02em; white-space: nowrap; }

/* ── The impersonation console (/impersonate) ──────────────────────────────────────────────────────
   A person you might BECOME is a person: the picker rows wear the same tinted roundel + name + detail
   shape the search preview and the student cells use, never a bare radio beside plain text. */
.deskpick { display: flex; align-items: center; gap: 11px; width: 100%; padding: 9px 11px; border: 1px solid transparent;
    border-radius: 12px; background: transparent; font-family: inherit; text-align: left; cursor: pointer;
    transition: background .12s ease, border-color .12s ease; }
.deskpick:hover { background: var(--line-soft); }
.deskpick.is-on { background: var(--brand-soft); border-color: var(--brand-line); }
.deskpick-ico { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px;
    border-radius: 10px; flex-shrink: 0; background: var(--surface-muted); color: var(--text-muted); font-size: 12px; font-weight: 800; }
.deskpick.is-on .deskpick-ico { background: var(--brand); color: #fff; }
.deskpick-name { display: block; font-size: 13px; font-weight: 600; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deskpick-detail { display: block; font-size: 11px; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deskpick-tick { margin-left: auto; flex-shrink: 0; color: var(--brand-text); }

/* THE ACTIVE SESSION IS A WARNING SURFACE. While one runs, every other page in the Lodge is resolving
   somebody else's identity — that has to look like a state you are IN, not a card you happen to be on. */
.imp-live { border: 1px solid color-mix(in srgb, var(--warn) 40%, var(--surface)); border-radius: 16px;
    background: var(--warn-soft); padding: 18px 20px; }
.imp-live-title { margin: 0; font-size: 15px; font-weight: 800; color: var(--warn); }
.imp-live-sub { margin: 5px 0 0; font-size: 12.5px; color: var(--text); line-height: 1.5; }

/* A numbered step head — impersonation is a SEQUENCE (pick who, say why, how long), and numbering it is
   honest structure rather than decoration: you cannot do step 3 before step 1. */
.imp-step { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px;
    border-radius: 7px; flex-shrink: 0; background: var(--brand-soft); color: var(--brand-text);
    font-size: 11px; font-weight: 800; }

/* ── THE UNHANDLED-ERROR BAR (#blazor-error-ui) ───────────────────────────────
   Blazor unhides this element by ID when a render or event handler throws past
   every ErrorBoundary. Without it such a failure is SILENT — rendering stops,
   the already-committed DOM stays on screen, and a half-drawn page is
   indistinguishable from a slow one (the estate map's blank canvas, 2026-08-22:
   three rounds of wrong theories because the only evidence was the console).

   DELIBERATELY THEME-INDEPENDENT. The element sits at BODY level, outside the
   `.theme-dark` wrapper MainLayout puts the tokens on, so it cannot read them.
   One high-contrast treatment instead — near-black with a red edge, legible over
   any page in either theme. Being unmissable IS the job.

   z-index clears 9999 (the app's highest) because nothing may cover the last
   signal that the page can no longer be trusted. Shown as `display: block` by
   the framework, so the row inside does the layout. */
#blazor-error-ui {
    display: none;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 10000;
    background: #0c0c0f; border-top: 3px solid #ff5257; color: #f8fafc;
    box-shadow: 0 -6px 24px rgba(15, 23, 42, .35);
    font-size: 13.5px; line-height: 1.45;
}
#blazor-error-ui .bar {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    max-width: 1200px; margin: 0 auto; padding: 11px 16px;
}
#blazor-error-ui .reload {
    margin-left: auto; flex-shrink: 0; cursor: pointer;
    padding: 5px 12px; border-radius: 8px; font-weight: 700; text-decoration: none;
    background: rgba(248, 250, 252, .12); border: 1px solid rgba(248, 250, 252, .28); color: #f8fafc;
}
#blazor-error-ui .reload:hover { background: rgba(248, 250, 252, .2); }
#blazor-error-ui .dismiss {
    flex-shrink: 0; cursor: pointer; padding: 4px 8px; border-radius: 8px;
    color: #cbd5e1; font-weight: 700; text-decoration: none;
}
#blazor-error-ui .dismiss:hover { color: #f8fafc; background: rgba(248, 250, 252, .12); }
#blazor-error-ui .reload:focus-visible, #blazor-error-ui .dismiss:focus-visible {
    outline: 2px solid #f8fafc; outline-offset: 2px;
}

/* ── THE SHELF's editor panel (2026-08-26) ────────────────────────────────────
   Defining a shelf document and changing one are the SAME job, so they wear the same panel: the add
   form at the top of /shelf and the inline edit that replaces a slot's header. Before this the edit
   was a strip of naked inputs with "from"/"until" as placeholders — placeholder-as-label, which the
   style guide forbids, and unstyled besides (it named a `.compact-form-input` class that never existed).
   The fields themselves ride `.compact-form`, the kit's dense treatment; these are the two things that
   treatment doesn't cover — the panel it sits in, and a label that reads at rest. */
.shelf-form {
    padding: 14px 16px;
    border: 1px dashed color-mix(in srgb, var(--brand) 32%, var(--surface));
    border-radius: 14px;
    background: var(--brand-soft);
}

/* Right-aligned action pair, separated from the fields by the panel's own hairline. */
.shelf-form-actions {
    display: flex; justify-content: flex-end; gap: 8px;
    margin-top: 12px; padding-top: 12px;
    border-top: 1px solid color-mix(in srgb, var(--brand) 18%, transparent);
}

/* The field label. `--text-muted`, NOT `--text-faint`: faint is ~2.8:1 and fails the contrast gate on
   anything anyone actually reads (2026-08-25). */
.shelf-label {
    display: block; margin-bottom: 4px;
    font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-muted);
}

/* ═══════════════════════════════════════════════════════════════════════════════
   THE DASHBOARD'S INSIGHT CARDS (.ins-*) — the home page's analytics.
   ───────────────────────────────────────────────────────────────────────────────
   A 12-column grid of section cards (the desk section's head: a 26px roundel in the card's own accent,
   an uppercase tracked label, a one-line rule), each holding one chart drawn by the chart interop plus
   the stat strips, capacity tracks and lists that give the chart its numbers. Tokens only; a card's
   accent rides in --ins so one recipe serves every module's colour. */
.ins-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 16px; margin-bottom: 24px; }
.ins-card {
    grid-column: span 12; display: flex; flex-direction: column; min-width: 0;
    /* The card is the containing block for its hidden data table (.ins-sr, position:absolute): without
       this the table's static position falls through the scrolling <main> to the document and stretches
       the page past the shell — a light band under the app on every dashboard scroll. */
    position: relative;
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 16px;
    box-shadow: 0 1px 2px rgba(15,23,42,.05); overflow: hidden;
}
@media (min-width: 1024px) {
    .ins-span-3 { grid-column: span 3; } .ins-span-4 { grid-column: span 4; } .ins-span-5 { grid-column: span 5; }
    .ins-span-6 { grid-column: span 6; } .ins-span-7 { grid-column: span 7; } .ins-span-8 { grid-column: span 8; }
    .ins-span-9 { grid-column: span 9; }
}
@media (min-width: 640px) and (max-width: 1023.98px) {
    .ins-span-3, .ins-span-4, .ins-span-5, .ins-span-6 { grid-column: span 6; }
}
.ins-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px 11px; border-bottom: 1px solid var(--line-soft); }
.ins-ico {
    display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
    background: color-mix(in srgb, var(--ins) 14%, var(--surface)); color: var(--ins);
}
.ins-ico svg { width: 14px; height: 14px; }
.ins-titles { min-width: 0; flex: 1 1 auto; }
.ins-title { margin: 0; font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); }
.ins-rule { margin: 2px 0 0; font-size: 11px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ins-aside { flex-shrink: 0; }
.ins-door {
    display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; padding: 5px 9px; border-radius: 8px;
    font-size: 11.5px; font-weight: 600; color: var(--text); text-decoration: none; border: 1px solid var(--line);
    background: var(--surface); transition: background .15s, border-color .15s;
}
.ins-door:hover { background: var(--surface-muted); border-color: var(--ins); }
.ins-door svg { width: 13px; height: 13px; }
.ins-body { padding: 14px 16px 16px; flex: 1 1 auto; min-width: 0; }
.ins-foot { padding: 10px 16px; border-top: 1px solid var(--line-soft); background: var(--surface-page); font-size: 11.5px; color: var(--text); }

/* The chart host: the interop draws into it; before that (and without JavaScript) it holds its height. */
.ins-chart { width: 100%; min-width: 0; }
.ins-chart .apexcharts-canvas { margin: 0 auto; }
.ins-chart .apexcharts-tooltip { font-family: inherit !important; border-radius: 10px !important; box-shadow: 0 8px 24px rgba(15,23,42,.14) !important; }
.ins-chart .apexcharts-tooltip.apexcharts-theme-light { background: var(--surface) !important; border-color: var(--line) !important; color: var(--text) !important; }
.ins-chart .apexcharts-tooltip-title { background: var(--surface-muted) !important; border-color: var(--line-soft) !important; font-weight: 700 !important; }
.ins-chart .apexcharts-legend-text { color: var(--text) !important; }

/* A row of stats under a chart: label over value, hairline-ruled from the chart. */
.ins-stats { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.ins-stat { display: flex; flex-direction: column; min-width: 68px; }
.ins-stat-label { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); }
.ins-stat-value { font-size: 18px; font-weight: 800; color: var(--text); line-height: 1.2; }
.ins-stat-value small { font-size: 11.5px; font-weight: 600; color: var(--text-muted); margin-left: 3px; }
.ins-stat .ins-dot { display: inline-block; width: 8px; height: 8px; border-radius: 3px; margin-right: 6px; vertical-align: 1px; }

/* A capacity TRACK: the forecast's committed places against next year's beds — one segment per source,
   a 2px surface gap between fills, the free remainder as the track itself. */
.ins-track { display: flex; gap: 2px; height: 14px; border-radius: 7px; overflow: hidden; background: var(--line-soft); margin: 14px 0 10px; }
.ins-seg { height: 100%; min-width: 2px; transition: width .4s ease; }
.ins-seg:first-child { border-radius: 7px 0 0 7px; }
.ins-track.is-over { box-shadow: 0 0 0 2px var(--danger-soft); }
.ins-legend { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px 16px; margin: 0; padding: 0; list-style: none; }
.ins-legend li { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text); }
.ins-legend .ins-dot { width: 9px; height: 9px; border-radius: 3px; flex-shrink: 0; }
.ins-legend b { margin-left: auto; font-weight: 800; font-variant-numeric: tabular-nums; }
.ins-legend .is-free { color: var(--text-muted); }
.ins-over { margin: 8px 0 0; font-size: 11.5px; font-weight: 600; color: var(--danger); }

/* A gauge beside its numbers. */
.ins-split { display: grid; grid-template-columns: minmax(180px, 220px) minmax(0, 1fr); gap: 8px 18px; align-items: center; }
@media (max-width: 639.98px) { .ins-split { grid-template-columns: 1fr; } }

/* The quiet lines: nothing to draw yet, or the chart could not be drawn. */
.ins-quiet { margin: 0; padding: 22px 0; font-size: 12.5px; color: var(--text-muted); text-align: center; }

/* The origins map and its ranked places. */
.ins-mapwrap { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 16px; }
@media (max-width: 1023.98px) { .ins-mapwrap { grid-template-columns: 1fr; } }
.ins-map { min-height: 380px; border-radius: 12px; overflow: hidden; background: var(--surface-muted); border: 1px solid var(--line-soft); }
.ins-map-off { display: flex; align-items: center; justify-content: center; min-height: 200px; border-radius: 12px; border: 1px dashed var(--line); font-size: 12.5px; color: var(--text-muted); text-align: center; padding: 20px; }
.ins-places { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 4px; max-height: 380px; overflow-y: auto; }
.ins-place { display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 9px; border: 1px solid var(--line-soft); }
.ins-place-rank { font-size: 10.5px; font-weight: 800; color: var(--text-muted); text-align: center; }
.ins-place-name { font-size: 12.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ins-place-count { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.ins-place-count .is-applicants { color: var(--text-muted); font-weight: 600; }
.ins-place-note { margin: 8px 0 0; font-size: 11px; color: var(--text-muted); }

/* The map's own pins (built by maps.js). The map is the provider's surface, so the pin carries its own
   ring and shadow rather than borrowing the card's. */
.tmap-pin {
    display: inline-flex; align-items: center; justify-content: center; min-width: 26px; height: 26px; padding: 0 7px;
    border-radius: 9px; background: var(--brand); color: #fff; font: 700 12px/1 ui-sans-serif, system-ui, sans-serif;
    box-shadow: 0 2px 8px rgba(15,23,42,.35), 0 0 0 2px #fff; transform: translateY(-4px);
}
.tmap-pin.is-top { background: var(--danger); }
.tmap-info { display: flex; flex-direction: column; gap: 2px; font: 12px/1.4 ui-sans-serif, system-ui, sans-serif; color: #1e293b; }
.tmap-info strong { font-size: 13px; }

/* The accessible twin of every chart: the same numbers as a table, visually hidden. */
.ins-sr { position: absolute; top: 0; left: 0; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* The hero strip above the cards. */
.ins-heroes { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin-bottom: 20px; }

/* Loading: a card-shaped placeholder that holds the chart's height. */
.ins-skel { min-height: 300px; }
.ins-skel .ins-body { display: flex; flex-direction: column; gap: 10px; }
.ins-skel-bar { height: 12px; border-radius: 6px; background: var(--line-soft); }
@media (prefers-reduced-motion: no-preference) {
    .ins-skel-bar { animation: ins-pulse 1.4s ease-in-out infinite; }
    @keyframes ins-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
}

/* The recent-record tables inside a card share the grid's tonal ladder: header band recessed, rows hairline-ruled. */
.ins-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.ins-table th { text-align: left; padding: 8px 12px; font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text); background: var(--surface-page); }
.ins-table td { padding: 9px 12px; border-top: 1px solid var(--line-soft); color: var(--text); vertical-align: middle; }
.ins-table tr:hover td { background: var(--line-soft); }
.ins-table .is-muted { color: var(--text-muted); }
.ins-table a { color: inherit; text-decoration: none; }
.ins-body.is-flush { padding: 0; }
.ins-feed { display: flex; flex-direction: column; gap: 4px; max-height: 320px; overflow-y: auto; }
.ins-feed-row { display: flex; align-items: flex-start; gap: 10px; padding: 7px 8px; border-radius: 9px; text-decoration: none; color: inherit; }
.ins-feed-row:hover { background: var(--line-soft); }
.ins-feed-ico { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 9px; flex-shrink: 0; }
.ins-feed-ico svg { width: 14px; height: 14px; }
.ins-feed-title { display: block; font-size: 12.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ins-feed-body { display: block; font-size: 11.5px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ins-feed-when { display: block; font-size: 11px; color: var(--text-muted); margin-top: 2px; }

/* ═══════════════════════════════════════════════════════════════════════════
   QUAD — the consumer shell. Everything above is the shared kit; what follows is the home's brand
   band, the action card, the door tile, the checklist state dot, the progress timeline and the
   college's front door. Tokens only, bar the front door's saturated brand fill with white text.
   ═══════════════════════════════════════════════════════════════════════════ */
.qd-hero {
    position: relative; overflow: hidden; flex-shrink: 0; margin-bottom: 20px; padding: 22px 24px; border-radius: 16px; color: #fff;
    background: radial-gradient(700px 300px at 90% -20%, rgba(255,255,255,.18), transparent 60%), linear-gradient(135deg, var(--brand) 0%, var(--brand-strong) 100%);
    box-shadow: 0 12px 32px var(--brand-ring);
}
.qd-hero-mark { position: absolute; right: -22px; bottom: -30px; width: 170px; height: 170px; opacity: .13; }
.qd-hero-row { position: relative; display: flex; align-items: center; gap: 18px; }
.qd-hero-avatar {
    width: 60px; height: 60px; border-radius: 14px; overflow: hidden; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: 800;
    background: rgba(255,255,255,.16); border: 1px solid rgba(255,255,255,.28);
}
.qd-hero-avatar img { width: 100%; height: 100%; object-fit: cover; }
.qd-hero-kicker { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; opacity: .78; margin: 0; }
.qd-hero-title { font-size: 24px; font-weight: 800; letter-spacing: -.02em; line-height: 1.15; margin: 2px 0 0; color: #fff; }
.qd-hero-line { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 8px; }
.qd-hero-sub { font-size: 13px; opacity: .88; }
.qd-hero-chip {
    display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px;
    background: rgba(255,255,255,.16); border: 1px solid rgba(255,255,255,.32); font-size: 11px; font-weight: 700; letter-spacing: .02em;
}
.qd-hero-chip svg { width: 13px; height: 13px; }
@media (max-width: 640px) {
    .qd-hero { padding: 18px 18px; }
    .qd-hero-title { font-size: 20px; }
}

.qd-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-items: start; }
@media (min-width: 1024px) { .qd-grid { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); } }
.qd-col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

/* The action card: a 4px rule in the card's own accent (--qd), a 34px tinted roundel, kicker, title, text, and its acts. */
.qd-card {
    --qd: var(--brand); flex-shrink: 0; overflow: hidden; border-radius: 16px; background: var(--surface);
    border: 1px solid var(--line-soft); box-shadow: 0 1px 2px rgba(15,23,42,.05);
}
.theme-dark .qd-card { border-color: var(--line); box-shadow: none; }
.qd-card-rule { height: 4px; background: var(--qd); }
.qd-card-body { padding: 18px 20px; }
.qd-card-head { display: flex; align-items: flex-start; gap: 12px; }
.qd-card-ico {
    width: 34px; height: 34px; border-radius: 10px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--qd) 14%, var(--surface)); color: var(--qd);
}
.qd-card-ico svg { width: 18px; height: 18px; }
.qd-card-kicker { font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted); margin: 0; }
.qd-card-title { font-size: 15px; font-weight: 800; letter-spacing: -.01em; color: var(--text); margin: 1px 0 0; }
.qd-card-text { font-size: 13px; line-height: 1.5; color: var(--text-muted); margin: 4px 0 0; }
.qd-card-text strong { color: var(--text); font-weight: 600; }
.qd-card-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; }

/* A door tile: icon, two lines, a chevron. */
.qd-doors { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.qd-door {
    display: flex; align-items: center; gap: 12px; padding: 11px 13px; border-radius: 12px; text-decoration: none; color: var(--text);
    border: 1px solid var(--line-soft); background: var(--surface); transition: background .15s, border-color .15s;
}
.qd-door:hover { background: var(--surface-muted); border-color: var(--line); }
.qd-door-ico {
    width: 34px; height: 34px; border-radius: 10px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--qd, var(--brand)) 14%, var(--surface)); color: var(--qd, var(--brand));
}
.qd-door-ico svg { width: 18px; height: 18px; }
.qd-door-title { font-size: 13px; font-weight: 600; }
.qd-door-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 1px; }
.qd-door-chev { margin-left: auto; width: 16px; height: 16px; flex-shrink: 0; color: var(--text-muted); }

/* The progress ring on a draft. */
.qd-ring { position: relative; width: 64px; height: 64px; flex-shrink: 0; }
.qd-ring svg { width: 64px; height: 64px; transform: rotate(-90deg); }
.qd-ring-track { stroke: var(--line); }
.qd-ring-fill { stroke: var(--brand); stroke-linecap: round; transition: stroke-dashoffset .5s ease; }
.qd-ring-pct { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; }

/* The state dot at the head of a checklist row. */
.qd-state { width: 22px; height: 22px; border-radius: 999px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 800; }
.qd-state svg { width: 12px; height: 12px; }
.qd-state.is-done { background: var(--ok-soft); color: var(--ok-strong); }
.qd-state.is-wait { background: var(--warn-soft); color: var(--warn); }
.qd-state.is-bad { background: var(--danger-soft); color: var(--danger); }
.qd-state.is-todo { background: var(--surface-muted); color: var(--text-muted); }

/* The photo on the record page. */
.qd-photo { width: 72px; height: 72px; border-radius: 16px; overflow: hidden; flex-shrink: 0; display: block; }
.qd-photo img { width: 100%; height: 100%; object-fit: cover; border-radius: 16px; }
.qd-photo-empty { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; border-radius: 16px; background: var(--surface-muted); border: 1px dashed var(--line); color: var(--text-muted); }
.qd-photo-empty svg { width: 26px; height: 26px; }

/* The progress timeline. */
.qd-timeline { list-style: none; margin: 0; padding: 0; }
.qd-tl { position: relative; display: flex; gap: 12px; padding-bottom: 14px; }
.qd-tl:last-child { padding-bottom: 0; }
.qd-tl-line { position: absolute; left: 5px; top: 16px; bottom: 0; width: 1px; background: var(--line); }
.qd-tl-dot { position: relative; margin-top: 5px; width: 11px; height: 11px; border-radius: 999px; background: var(--line); flex-shrink: 0; }
.qd-tl.is-latest .qd-tl-dot { background: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.qd-tl-title { margin: 0; font-size: 13px; font-weight: 500; color: var(--text-muted); }
.qd-tl.is-latest .qd-tl-title { font-weight: 600; color: var(--text); }
.qd-tl-when { margin: 0; font-size: 11px; color: var(--text-muted); }

/* The college's front door — the one surface a visitor sees before signing in. A saturated brand
   fill carrying white text reads on either ground. */
.qd-front {
    min-height: 100vh; display: flex; flex-direction: column; color: #fff;
    background: radial-gradient(900px 480px at 88% -12%, rgba(255,255,255,.16), transparent 60%), radial-gradient(700px 420px at -10% 40%, rgba(255,255,255,.10), transparent 55%), linear-gradient(160deg, var(--brand) 0%, var(--brand-strong) 100%);
}
.qd-front-bar { display: flex; align-items: center; justify-content: space-between; padding: 22px 28px; }
.qd-front-mark { display: inline-flex; align-items: center; gap: 10px; font-size: 20px; font-weight: 800; letter-spacing: -.02em; }
.qd-front-mark svg { width: 36px; height: 36px; padding: 7px; border-radius: 11px; background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.22); }
.qd-front-ghost {
    padding: 8px 16px; border-radius: 11px; font-family: inherit; font-size: 13px; font-weight: 600; color: #fff; cursor: pointer;
    background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.28);
}
.qd-front-ghost:hover { background: rgba(255,255,255,.2); }
.qd-front-hero { flex: 1; display: flex; flex-direction: column; justify-content: center; max-width: 720px; width: 100%; margin: 0 auto; padding: 24px 28px 48px; }
.qd-front-logo { height: 56px; width: auto; align-self: flex-start; margin-bottom: 20px; padding: 6px 12px; border-radius: 12px; background: rgba(255,255,255,.92); object-fit: contain; }
.qd-front-kicker { font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; opacity: .75; margin: 0 0 12px; }
.qd-front-title { font-size: 40px; font-weight: 800; letter-spacing: -.03em; line-height: 1.05; margin: 0; color: #fff; }
.qd-front-lead { font-size: 16px; line-height: 1.55; opacity: .88; margin: 16px 0 0; max-width: 520px; }
.qd-front-doors { display: flex; flex-direction: column; gap: 10px; margin-top: 28px; max-width: 520px; }
.qd-front-door {
    display: flex; align-items: center; gap: 14px; padding: 14px 16px; border-radius: 14px; text-align: left; cursor: pointer; font-family: inherit; color: #1e293b;
    background: #fff; border: 0; box-shadow: 0 12px 32px rgba(0,0,0,.18); transition: transform .15s;
}
.qd-front-door:hover { transform: translateY(-1px); }
.qd-front-door-ico { width: 38px; height: 38px; border-radius: 11px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; background: #eef2ff; color: #4f46e5; }
.qd-front-door-ico svg { width: 19px; height: 19px; }
.qd-front-door-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.qd-front-door-title { font-size: 14px; font-weight: 700; }
.qd-front-door-sub { font-size: 12px; color: #64748b; margin-top: 1px; }
.qd-front-door-chev { width: 18px; height: 18px; flex-shrink: 0; color: #94a3b8; }
.qd-front-foot { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; padding: 18px 28px; font-size: 12px; opacity: .72; }
.qd-front-foot a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
@media (max-width: 640px) {
    .qd-front-bar, .qd-front-hero, .qd-front-foot { padding-left: 18px; padding-right: 18px; }
    .qd-front-title { font-size: 30px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE FILL KIT (.fx-*) — how a staff-built form's question looks and behaves wherever a person answers it:
   Quad's fill page, the Lodge's preview and the builder's live canvas. One control per field kind, every one
   drawn from the tokens so a college's own colour lands on it. Motion is short and fades out under
   prefers-reduced-motion. Sits inside .compact-form (the 30px input scale); the shapes below are what give
   the form its life — tiles, cards, stars, calendars — never a bigger input.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The page a step shows: a numbered kicker, the title, the intro; then the questions rising in ── */
.fx-page { display: flex; flex-direction: column; gap: 4px; margin-bottom: 18px; }
.fx-page-kicker { display: inline-flex; align-items: center; gap: 8px; font-size: 10.5px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--brand-text); }
.fx-page-kicker i { width: 18px; height: 2px; border-radius: 2px; background: var(--brand); display: inline-block; }
.fx-page-title { font-size: 18px; font-weight: 800; letter-spacing: -.02em; color: var(--text); margin: 0; line-height: 1.2; }
.fx-page-sub { font-size: 12.5px; color: var(--text-muted); margin: 2px 0 0; line-height: 1.5; white-space: pre-line; }
.fx-grid { display: grid; grid-template-columns: 1fr; gap: 16px 18px; }
@media (min-width: 640px) { .fx-grid { grid-template-columns: 1fr 1fr; } .fx-wide { grid-column: 1 / -1; } }
/* The entrance fills BACKWARDS only: once it has played the field carries no transform or opacity, so it
   never stays a stacking layer that would trap a popover beneath a later sibling. A field whose popover
   or suggestion panel is open rises above its siblings for as long as it is open. */
.fx-field { position: relative; animation: fx-rise .32s cubic-bezier(.22,.61,.36,1) backwards; animation-delay: calc(var(--fx-i, 0) * 45ms); }
.fx-field:has(.fx-cal-pop), .fx-field:has(.fx-sugg), .enr-field:has(.fx-cal-pop), .enr-field:has(.fx-sugg) { position: relative; z-index: 40; }
/* The native radio/checkbox inside an option card or a Yes/No tile: real for the keyboard and assistive tech, invisible to the eye. */
.fx-native { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.fx-opt, .fx-yn-btn { position: relative; }
.fx-opt:has(.fx-native:focus-visible), .fx-yn-btn:has(.fx-native:focus-visible) { box-shadow: 0 0 0 3px var(--brand-ring); }
@keyframes fx-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ── The label row: the question, a "required" spark, and an "answered" tick that lights when it is ── */
.fx-field .enr-label { display: flex; align-items: center; gap: 6px; }
.fx-done { position: absolute; top: 0; right: 0; width: 14px; height: 14px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; background: var(--ok-soft); color: var(--ok-strong); opacity: 0; transform: scale(.6); transition: opacity .18s, transform .18s; }
.fx-done svg { width: 9px; height: 9px; }
.fx-field.is-answered .fx-done { opacity: 1; transform: scale(1); }

/* ── Text-like inputs: a leading kind glyph, a trailing state glyph, and a live count ─────────────── */
.fx-inwrap { position: relative; display: flex; align-items: center; }
.fx-inwrap > input, .fx-inwrap > textarea { width: 100%; }
.fx-ico { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); width: 14px; height: 14px; color: var(--text-faint); pointer-events: none; transition: color .15s; }
.fx-ico svg { width: 14px; height: 14px; }
.fx-inwrap:focus-within .fx-ico { color: var(--brand-text); }
.fx-tail { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); width: 14px; height: 14px; display: inline-flex; align-items: center; justify-content: center; pointer-events: none; }
.fx-tail svg { width: 14px; height: 14px; }
.fx-tail.is-ok { color: var(--ok-strong); animation: fx-pop .25s cubic-bezier(.2,.9,.3,1.4); }
.fx-tail.is-bad { color: var(--danger); }
@keyframes fx-pop { from { transform: translateY(-50%) scale(.4); opacity: 0; } to { transform: translateY(-50%) scale(1); opacity: 1; } }
.compact-form .fx-inwrap.has-ico > input, .compact-form .fx-inwrap.has-ico > textarea { padding-left: 30px !important; }
.compact-form .fx-inwrap.has-tail > input { padding-right: 28px !important; }
.fx-inwrap.has-ico > input, .fx-inwrap.has-ico > textarea { padding-left: 30px; }
.fx-inwrap.has-tail > input { padding-right: 28px; }
.fx-inwrap > textarea { min-height: 88px; resize: vertical; line-height: 1.5; }
.fx-inwrap.has-ico .fx-ico.at-top { top: 12px; transform: none; }
.fx-count { display: flex; align-items: center; gap: 6px; font-size: 10.5px; color: var(--text-muted); margin-top: 2px; font-variant-numeric: tabular-nums; }
.fx-count-bar { flex: 1; height: 3px; border-radius: 2px; background: var(--line-soft); overflow: hidden; }
.fx-count-bar i { display: block; height: 100%; background: var(--brand); transition: width .2s; border-radius: 2px; }
.fx-count.is-near .fx-count-bar i { background: var(--warn); }
.fx-count.is-full .fx-count-bar i { background: var(--danger); }

/* ── Number: a stepper cluster, and a slider when the range is small enough to be a dial ─────────── */
.fx-num { display: flex; align-items: center; gap: 6px; }
.fx-num > .fx-inwrap { width: 140px; }
.fx-step { width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--line); background: var(--surface); color: var(--text-muted); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; font-family: inherit; transition: background .12s, border-color .12s, color .12s, transform .1s; }
.fx-step svg { width: 13px; height: 13px; }
.fx-step:hover { border-color: var(--brand-line); background: var(--brand-soft); color: var(--brand-text); }
.fx-step:active { transform: scale(.94); }
.fx-step:disabled { opacity: .4; cursor: default; }
.fx-num-hint { font-size: 10.5px; color: var(--text-muted); margin-left: 4px; }
.fx-slider { position: relative; padding: 8px 0 4px; }
.fx-slider input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; border-radius: 2px; margin: 0;
    background: linear-gradient(to right, var(--brand) 0 var(--fx-pct, 0%), var(--line) var(--fx-pct, 0%) 100%); outline: none; cursor: pointer; }
.fx-slider input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--surface); border: 2px solid var(--brand); box-shadow: 0 1px 4px rgba(15,23,42,.18); transition: transform .12s, box-shadow .12s; }
.fx-slider input[type="range"]:focus-visible::-webkit-slider-thumb, .fx-slider input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.15); box-shadow: 0 0 0 4px var(--brand-ring); }
.fx-slider input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--surface); border: 2px solid var(--brand); box-shadow: 0 1px 4px rgba(15,23,42,.18); }
.fx-slider-ends { display: flex; justify-content: space-between; font-size: 10px; color: var(--text-faint); margin-top: 2px; font-variant-numeric: tabular-nums; }
.fx-slider-val { position: absolute; top: -14px; left: var(--fx-pct, 0%); transform: translateX(-50%); font-size: 10.5px; font-weight: 800; padding: 1px 7px; border-radius: 999px; background: var(--brand); color: #fff; pointer-events: none; white-space: nowrap; }

/* ── Yes / No: two tiles, a tick and a cross ──────────────────────────────────────────────────────── */
.fx-yn { display: inline-flex; gap: 8px; }
.fx-yn-btn { display: inline-flex; align-items: center; gap: 8px; min-width: 96px; padding: 7px 14px 7px 10px; border-radius: 11px; border: 1.5px solid var(--line); background: var(--surface); color: var(--text-muted); font-family: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; transition: border-color .15s, background .15s, color .15s, transform .1s, box-shadow .15s; }
.fx-yn-btn i { width: 20px; height: 20px; border-radius: 999px; border: 1.5px solid var(--line); display: inline-flex; align-items: center; justify-content: center; color: transparent; transition: background .15s, border-color .15s, color .15s; }
.fx-yn-btn i svg { width: 11px; height: 11px; }
.fx-yn-btn:hover { border-color: var(--brand-line); color: var(--text); }
.fx-yn-btn:active { transform: scale(.97); }
.fx-yn-btn.is-on { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-text); box-shadow: 0 0 0 1px var(--brand); }
.fx-yn-btn.is-on i { background: var(--brand); border-color: var(--brand); color: #fff; }
.fx-yn-btn.is-on.is-no { border-color: var(--text-muted); box-shadow: 0 0 0 1px var(--text-muted); background: var(--surface-muted); color: var(--text); }
.fx-yn-btn.is-on.is-no i { background: var(--text-muted); border-color: var(--text-muted); }

/* ── Choices: option cards with a key badge, a radio dot or a check square ────────────────────────── */
.fx-opts { display: grid; grid-template-columns: 1fr; gap: 6px; }
@media (min-width: 640px) { .fx-opts.is-many { grid-template-columns: 1fr 1fr; } }
.fx-opt { display: flex; align-items: center; gap: 10px; padding: 7px 10px 7px 8px; border-radius: 11px; border: 1.5px solid var(--line); background: var(--surface); color: var(--text); font-family: inherit; font-size: 12.5px; font-weight: 500; text-align: left; cursor: pointer; transition: border-color .15s, background .15s, box-shadow .15s, transform .1s; }
.fx-opt:hover { border-color: var(--brand-line); background: var(--surface-muted); }
.fx-opt:active { transform: scale(.985); }
.fx-opt:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.fx-opt-key { width: 20px; height: 20px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 800; letter-spacing: .02em; background: var(--surface-muted); border: 1px solid var(--line); color: var(--text-muted); flex-shrink: 0; transition: background .15s, color .15s, border-color .15s; }
.fx-opt-label { flex: 1; min-width: 0; }
.fx-opt-mark { width: 18px; height: 18px; border-radius: 999px; border: 1.5px solid var(--line); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: transparent; transition: background .15s, border-color .15s, color .15s, transform .15s; }
.fx-opt-mark svg { width: 10px; height: 10px; }
.fx-opt.is-multi .fx-opt-mark { border-radius: 5px; }
.fx-opt.is-on { border-color: var(--brand); background: var(--brand-soft); box-shadow: 0 0 0 1px var(--brand); font-weight: 600; color: var(--brand-text); }
.fx-opt.is-on .fx-opt-key { background: var(--brand); border-color: var(--brand); color: #fff; }
.fx-opt.is-on .fx-opt-mark { background: var(--brand); border-color: var(--brand); color: #fff; transform: scale(1.05); }
.fx-opts-foot { font-size: 10.5px; color: var(--text-muted); margin-top: 2px; }

/* ── Dropdown: the kit select with a leading glyph ────────────────────────────────────────────────── */
.fx-select { position: relative; display: flex; align-items: center; }
.fx-select > select { width: 100%; }
.compact-form .fx-select.has-ico > select { padding-left: 30px !important; }
.fx-select.has-ico > select { padding-left: 30px; }

/* ── Rating: stars up to five, numbered tiles beyond ──────────────────────────────────────────────── */
.fx-stars { display: inline-flex; align-items: center; gap: 2px; }
.fx-star { width: 30px; height: 30px; border: none; background: transparent; padding: 0; cursor: pointer; color: var(--line); display: inline-flex; align-items: center; justify-content: center; transition: color .12s, transform .12s; font-family: inherit; }
.fx-star svg { width: 24px; height: 24px; fill: currentColor; stroke: none; }
.fx-star.is-on { color: var(--warn); }
.fx-stars:hover .fx-star { color: var(--line); }
.fx-stars .fx-star:hover, .fx-stars .fx-star:has(~ .fx-star:hover) { color: var(--warn); transform: scale(1.12); }
.fx-star:active { transform: scale(.9); }
.fx-star:focus-visible { outline: none; border-radius: 8px; box-shadow: 0 0 0 3px var(--brand-ring); }
.fx-stars-word { margin-left: 8px; font-size: 11.5px; font-weight: 600; color: var(--text-muted); min-width: 60px; }
.fx-scale { display: flex; flex-wrap: wrap; gap: 5px; }
.fx-scale-btn { min-width: 34px; height: 34px; padding: 0 8px; border-radius: 10px; border: 1.5px solid var(--line); background: var(--surface); color: var(--text-muted); font-family: inherit; font-size: 13px; font-weight: 800; cursor: pointer; transition: border-color .12s, background .12s, color .12s, transform .1s; }
.fx-scale-btn:hover { border-color: var(--brand-line); color: var(--text); }
.fx-scale-btn:active { transform: scale(.94); }
.fx-scale-btn.is-lit { background: var(--brand-soft); border-color: var(--brand-line); color: var(--brand-text); }
.fx-scale-btn.is-on { background: var(--brand); border-color: var(--brand); color: #fff; box-shadow: 0 2px 8px var(--brand-ring); }
.fx-scale-ends { display: flex; justify-content: space-between; font-size: 10px; color: var(--text-faint); margin-top: 4px; }

/* ── Calendars: a date or a range in a popover, the days inline ───────────────────────────────────── */
.fx-datebtn { display: inline-flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px 0 8px; border-radius: 8px; border: 1px solid var(--line); background: var(--surface-muted); color: var(--text); font-family: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; transition: border-color .15s, background .15s, box-shadow .15s; min-width: 160px; }
.fx-datebtn svg { width: 14px; height: 14px; color: var(--text-faint); }
.fx-datebtn:hover { border-color: var(--text-faint); }
.fx-datebtn.is-open, .fx-datebtn:focus-visible { outline: none; background: var(--surface); border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.fx-datebtn.is-empty { color: var(--text-muted); }
.fx-datebtn .fx-datebtn-x { margin-left: auto; width: 16px; height: 16px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; color: var(--text-faint); }
.fx-datebtn .fx-datebtn-x:hover { background: var(--line-soft); color: var(--text); }
.fx-datebtn .fx-datebtn-x svg { width: 10px; height: 10px; color: inherit; }
.fx-datewrap { position: relative; display: inline-flex; flex-direction: column; gap: 6px; max-width: 100%; }
.fx-cal-pop { position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; padding: 12px; border-radius: 14px; background: var(--surface); border: 1px solid var(--line); box-shadow: 0 14px 34px rgba(15,23,42,.14); animation: fx-rise .18s ease; }
.fx-cal { display: flex; flex-direction: column; gap: 8px; user-select: none; -webkit-user-select: none; }
.fx-cal-nav { display: flex; align-items: center; gap: 8px; }
.fx-cal-nav b { flex: 1; text-align: center; font-size: 12px; font-weight: 800; color: var(--text); }
.fx-cal-arrow { width: 26px; height: 26px; border-radius: 8px; border: 1px solid var(--line); background: var(--surface); color: var(--text-muted); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-family: inherit; transition: background .12s, color .12s; }
.fx-cal-arrow svg { width: 12px; height: 12px; }
.fx-cal-arrow:hover { background: var(--brand-soft); color: var(--brand-text); }
.fx-cal-months { display: flex; gap: 16px; flex-wrap: wrap; }
.fx-cal-month { min-width: 210px; }
.fx-cal-title { font-size: 11px; font-weight: 800; color: var(--text); margin-bottom: 6px; letter-spacing: .01em; }
.fx-cal-grid { display: grid; grid-template-columns: repeat(7, 28px); gap: 2px; }
.fx-cal-dow { font-size: 9.5px; font-weight: 700; color: var(--text-muted); text-align: center; padding-bottom: 2px; }
.fx-cal-day { width: 28px; height: 26px; border: none; border-radius: 8px; background: transparent; color: var(--text); font-size: 11.5px; font-family: inherit; cursor: pointer; padding: 0; position: relative; transition: background .1s, color .1s, transform .1s; }
.fx-cal-day:hover:not(:disabled) { background: var(--brand-soft); color: var(--brand-text); }
.fx-cal-day:active:not(:disabled) { transform: scale(.92); }
.fx-cal-day:disabled { color: var(--text-faint); opacity: .45; cursor: default; }
.fx-cal-day.is-today::after { content: ""; position: absolute; left: 50%; bottom: 3px; width: 4px; height: 4px; border-radius: 999px; background: var(--brand); transform: translateX(-50%); }
.fx-cal-day.is-on { background: var(--brand); color: #fff; font-weight: 700; }
.fx-cal-day.is-on.is-today::after { background: #fff; }
.fx-cal-day.is-in { background: var(--brand-soft); color: var(--brand-text); border-radius: 0; }
.fx-cal-day.is-start { border-radius: 8px 0 0 8px; }
.fx-cal-day.is-end { border-radius: 0 8px 8px 0; }
.fx-cal-day.is-start.is-end { border-radius: 8px; }
.fx-cal-day.is-paint { background: var(--brand-soft); color: var(--brand-text); font-weight: 700; }
.fx-cal-day.is-unpaint { background: var(--surface-muted); color: var(--text-faint); }
.fx-cal-foot { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--text-muted); }
.fx-cal-foot b { color: var(--text); font-weight: 700; }
.fx-cal-clear { margin-left: auto; border: none; background: transparent; color: var(--text-muted); cursor: pointer; font-size: 11px; font-family: inherit; text-decoration: underline; padding: 0; }
.fx-cal-clear:hover { color: var(--danger); }
.fx-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.fx-chip { padding: 3px 11px; border: 1px dashed var(--line); border-radius: 999px; background: transparent; font-family: inherit; font-size: 11px; font-weight: 600; color: var(--text-muted); cursor: pointer; transition: background .12s, color .12s, border-color .12s; }
.fx-chip:hover { border-style: solid; border-color: var(--brand-line); background: var(--brand-soft); color: var(--brand-text); }
.fx-range-sum { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--text-muted); }
.fx-range-sum b { color: var(--text); font-weight: 700; }
.fx-range-sum .fx-nights { padding: 1px 8px; border-radius: 999px; background: var(--brand-soft); color: var(--brand-text); font-size: 10.5px; font-weight: 800; }
/* A typeable date: the input reads the date in words, the button at its tail opens the calendar. */
.fx-tail-btn { position: absolute; right: 3px; top: 50%; transform: translateY(-50%); width: 24px; height: 24px; border-radius: 7px; border: none; background: transparent; color: var(--text-faint); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-family: inherit; padding: 0; transition: background .12s, color .12s; }
.fx-tail-btn svg { width: 14px; height: 14px; }
.fx-tail-btn:hover, .fx-tail-btn.is-on { background: var(--brand-soft); color: var(--brand-text); }
.fx-tail-btn:disabled { opacity: .4; cursor: default; }
.fx-range { display: flex; align-items: center; gap: 8px; width: 100%; }
.fx-range > .fx-inwrap { flex: 1; min-width: 0; }
.fx-range-arrow { color: var(--text-faint); font-size: 12px; flex-shrink: 0; }
/* The calendar zooms: tap the month title and the days give way to a grid of years, then months, then days —
   a date of birth is three taps, never three hundred, and never a dropdown. */
.fx-cal-title-btn { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px; height: 26px; padding: 0 10px; border: 1px solid transparent; border-radius: 8px; background: transparent; font-family: inherit; font-size: 12px; font-weight: 800; color: var(--text); cursor: pointer; transition: background .12s, border-color .12s; }
.fx-cal-title-btn svg { width: 12px; height: 12px; color: var(--text-faint); }
.fx-cal-title-btn:hover, .fx-cal-title-btn.is-open { background: var(--brand-soft); border-color: var(--brand-line); color: var(--brand-text); }
.fx-cal-title-btn.is-open svg { color: var(--brand-text); }
.fx-cal-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; min-width: 210px; animation: fx-rise .18s ease; }
.fx-cal-tile { height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); font-family: inherit; font-size: 12px; font-weight: 600; color: var(--text); cursor: pointer; position: relative; transition: background .12s, border-color .12s, color .12s, transform .1s; }
.fx-cal-tile:hover { border-color: var(--brand-line); background: var(--brand-soft); color: var(--brand-text); }
.fx-cal-tile:active { transform: scale(.95); }
.fx-cal-tile.is-on { background: var(--brand); border-color: var(--brand); color: #fff; }
.fx-cal-tile.is-now::after { content: ""; position: absolute; right: 6px; top: 6px; width: 5px; height: 5px; border-radius: 999px; background: var(--brand); }
.fx-cal-tile.is-on.is-now::after { background: #fff; }

/* ── Files: a drop zone that breathes, chips with a glyph, a stripe while uploading ───────────────── */
.fx-drop { position: relative; display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 14px; border: 1.5px dashed var(--line); background: var(--surface-muted); transition: border-color .15s, background .15s, transform .15s; cursor: pointer; }
.fx-drop:hover, .fx-drop.is-over { border-color: var(--brand); background: var(--brand-soft); }
.fx-drop.is-over { transform: scale(1.01); }
.fx-drop input[type="file"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.fx-drop-ico { width: 36px; height: 36px; border-radius: 11px; background: var(--surface); border: 1px solid var(--line); display: inline-flex; align-items: center; justify-content: center; color: var(--brand-text); flex-shrink: 0; transition: transform .2s; }
.fx-drop-ico svg { width: 17px; height: 17px; }
.fx-drop:hover .fx-drop-ico, .fx-drop.is-over .fx-drop-ico { transform: translateY(-2px); }
.fx-drop-t { font-size: 12.5px; font-weight: 700; color: var(--text); }
.fx-drop-t u { text-decoration: none; color: var(--brand-text); }
.fx-drop-s { font-size: 10.5px; color: var(--text-muted); margin-top: 1px; }
.fx-drop.is-busy { pointer-events: none; }
.fx-drop-bar { position: absolute; left: 12px; right: 12px; bottom: 6px; height: 3px; border-radius: 2px; overflow: hidden; background: var(--line-soft); }
.fx-drop-bar i { display: block; width: 40%; height: 100%; border-radius: 2px; background: var(--brand); animation: fx-stripe 1s ease-in-out infinite; }
@keyframes fx-stripe { 0% { transform: translateX(-100%); } 100% { transform: translateX(260%); } }
.fx-files { display: flex; flex-direction: column; gap: 5px; }
.fx-file { display: flex; align-items: center; gap: 9px; padding: 6px 8px 6px 6px; border-radius: 10px; border: 1px solid var(--line-soft); background: var(--surface); animation: fx-rise .2s ease; }
.fx-file-ico { width: 28px; height: 28px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; font-size: 8.5px; font-weight: 800; letter-spacing: .04em; color: #fff; background: var(--text-muted); flex-shrink: 0; }
.fx-file-ico.is-img { background: #0d9488; }
.fx-file-ico.is-pdf { background: #dc2626; }
.fx-file-ico.is-doc { background: #2563eb; }
.fx-file-name { flex: 1; min-width: 0; font-size: 12px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fx-file-meta { font-size: 10.5px; color: var(--text-muted); }
.fx-x { width: 22px; height: 22px; border-radius: 7px; border: none; background: transparent; color: var(--text-faint); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-family: inherit; transition: background .12s, color .12s; flex-shrink: 0; }
.fx-x svg { width: 12px; height: 12px; }
.fx-x:hover { background: var(--danger-soft); color: var(--danger); }

/* ── Photo: the studio wears a frame ──────────────────────────────────────────────────────────────── */
.fx-photo { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; }
.fx-photo-frame { width: 96px; height: 96px; border-radius: 22px; background: var(--surface-muted); border: 1.5px dashed var(--line); display: inline-flex; align-items: center; justify-content: center; color: var(--text-faint); overflow: hidden; flex-shrink: 0; position: relative; }
.fx-photo-frame svg { width: 30px; height: 30px; }
.fx-photo-frame img { width: 100%; height: 100%; object-fit: cover; }
.fx-photo-frame.has-photo { border-style: solid; border-color: var(--brand); box-shadow: 0 6px 18px var(--brand-ring); }
.fx-photo-frame .fx-photo-ok { position: absolute; right: 4px; bottom: 4px; width: 20px; height: 20px; border-radius: 999px; background: var(--ok); color: #fff; display: inline-flex; align-items: center; justify-content: center; border: 2px solid var(--surface); }
.fx-photo-frame .fx-photo-ok svg { width: 10px; height: 10px; }
.fx-photo-body { flex: 1; min-width: 220px; }

/* ── Payment: the amount, the lock, the paid state ────────────────────────────────────────────────── */
.fx-pay { border-radius: 14px; border: 1px solid var(--line); background: var(--surface); overflow: hidden; }
.fx-pay-head { display: flex; align-items: center; gap: 12px; padding: 12px 14px; background: var(--surface-muted); border-bottom: 1px solid var(--line-soft); }
.fx-pay-ico { width: 36px; height: 36px; border-radius: 11px; background: var(--brand); color: #fff; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.fx-pay-ico svg { width: 17px; height: 17px; }
.fx-pay-label { flex: 1; min-width: 0; font-size: 12.5px; font-weight: 700; color: var(--text); }
.fx-pay-amt { font-size: 18px; font-weight: 800; letter-spacing: -.02em; color: var(--text); font-variant-numeric: tabular-nums; }
.fx-pay-amt small { font-size: 10.5px; font-weight: 700; color: var(--text-muted); margin-left: 4px; letter-spacing: .04em; }
.fx-pay-body { padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
.fx-pay-note { display: inline-flex; align-items: center; gap: 6px; font-size: 10.5px; color: var(--text-muted); }
.fx-pay-note svg { width: 11px; height: 11px; }
.fx-pay-paid { display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px 6px 6px; border-radius: 999px; background: var(--ok-soft); color: var(--ok-strong); font-size: 12px; font-weight: 700; }
.fx-pay-paid i { width: 20px; height: 20px; border-radius: 999px; background: var(--ok); color: #fff; display: inline-flex; align-items: center; justify-content: center; animation: fx-pop-in .3s cubic-bezier(.2,.9,.3,1.4); }
.fx-pay-paid i svg { width: 11px; height: 11px; }
@keyframes fx-pop-in { from { transform: scale(.3); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* ── List: numbered lines, Enter adds the next ───────────────────────────────────────────────────── */
.fx-list { display: flex; flex-direction: column; gap: 5px; }
.fx-list-row { display: flex; align-items: center; gap: 8px; animation: fx-rise .18s ease; }
.fx-list-n { width: 22px; height: 22px; border-radius: 7px; background: var(--brand-soft); color: var(--brand-text); font-size: 10.5px; font-weight: 800; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.fx-list-row > .fx-inwrap { flex: 1; }
.fx-list-row .fx-x { opacity: .35; }
.fx-list-row:hover .fx-x, .fx-list-row:focus-within .fx-x { opacity: 1; }
/* The add button is an invitation, not a footnote: a brand-tinted pill with a solid plus roundel that fills on hover. */
.fx-add { display: inline-flex; align-items: center; gap: 8px; padding: 5px 14px 5px 5px; border-radius: 999px; border: 1px solid var(--brand-line); background: var(--brand-soft); color: var(--brand-text); font-family: inherit; font-size: 11.5px; font-weight: 700; cursor: pointer; transition: background .14s, color .14s, border-color .14s, box-shadow .14s, transform .1s; width: fit-content; }
.fx-add svg { width: 20px; height: 20px; padding: 5px; border-radius: 999px; background: var(--brand); color: #fff; box-sizing: border-box; flex-shrink: 0; transition: background .14s, color .14s, transform .14s; }
.fx-add:hover { background: var(--brand); border-color: var(--brand); color: #fff; box-shadow: 0 4px 12px var(--brand-ring); }
.fx-add:hover svg { background: #fff; color: var(--brand); transform: rotate(90deg); }
.fx-add:active { transform: scale(.97); }
.fx-add.is-wide { width: 100%; justify-content: center; padding: 8px; border-radius: 12px; }
.fx-list-hint { font-size: 10.5px; color: var(--text-muted); }

/* ── Group: each row a card with a crest ─────────────────────────────────────────────────────────── */
.fx-group { display: flex; flex-direction: column; gap: 10px; }
.fx-row { border-radius: 14px; border: 1px solid var(--line); background: var(--surface); padding: 12px 14px 14px; animation: fx-rise .2s ease; position: relative; }
.fx-row-head { display: flex; align-items: center; gap: 9px; margin-bottom: 10px; }
.fx-row-crest { width: 26px; height: 26px; border-radius: 9px; background: var(--brand); color: #fff; font-size: 11px; font-weight: 800; display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 2px 6px var(--brand-ring); }
.fx-row-title { font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); flex: 1; }
.fx-row .fx-grid { gap: 10px 14px; }
.fx-row-checks { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-top: 10px; }
.fx-check { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text); cursor: pointer; }

/* ── Address: the parts in a grid, suggestions in a kit panel ─────────────────────────────────────── */
.fx-sugg { position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 25; border-radius: 12px; background: var(--surface); border: 1px solid var(--line); box-shadow: 0 12px 30px rgba(15,23,42,.14); overflow: hidden; max-height: 260px; overflow-y: auto; animation: fx-rise .15s ease; }
.fx-sugg-item { display: flex; align-items: center; gap: 9px; width: 100%; padding: 8px 10px; border: none; background: transparent; text-align: left; font-family: inherit; font-size: 12px; color: var(--text); cursor: pointer; }
.fx-sugg-item svg { width: 13px; height: 13px; color: var(--text-faint); flex-shrink: 0; }
.fx-sugg-item:hover { background: var(--brand-soft); color: var(--brand-text); }
.fx-sugg-item small { color: var(--text-muted); }
.fx-addr-line { display: inline-flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--text-muted); margin-top: 2px; padding: 4px 10px; border-radius: 999px; background: var(--surface-muted); }
.fx-addr-line svg { width: 12px; height: 12px; color: var(--brand-text); }

/* ── Statement: a note with a roundel ────────────────────────────────────────────────────────────── */
.fx-note { display: flex; gap: 12px; padding: 12px 14px; border-radius: 14px; background: var(--surface-muted); border: 1px solid var(--line-soft); border-left: 3px solid var(--brand); }
.fx-note-ico { width: 30px; height: 30px; border-radius: 9px; background: var(--brand-soft); color: var(--brand-text); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.fx-note-ico svg { width: 15px; height: 15px; }
.fx-note-t { font-size: 13px; font-weight: 700; color: var(--text); margin: 0; }
.fx-note-b { font-size: 12px; color: var(--text-muted); margin: 3px 0 0; line-height: 1.5; white-space: pre-line; }

/* ── Review: every answer, grouped by page, each with a way back ─────────────────────────────────── */
.fx-review { display: flex; flex-direction: column; gap: 12px; }
.fx-rv-group { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
.fx-rv-head { display: flex; align-items: center; gap: 9px; padding: 9px 14px; background: var(--surface-muted); border-bottom: 1px solid var(--line-soft); }
.fx-rv-head .fx-row-crest { width: 22px; height: 22px; border-radius: 7px; font-size: 10px; }
.fx-rv-head h4 { flex: 1; font-size: 12.5px; font-weight: 700; color: var(--text); margin: 0; }
.fx-rv-edit { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 700; color: var(--brand-text); background: none; border: none; cursor: pointer; padding: 3px 8px; border-radius: 7px; font-family: inherit; transition: background .12s; }
.fx-rv-edit svg { width: 11px; height: 11px; }
.fx-rv-edit:hover { background: var(--brand-soft); }
.fx-rv-body { display: grid; grid-template-columns: 1fr; gap: 10px 20px; padding: 12px 14px; }
@media (min-width: 560px) { .fx-rv-body { grid-template-columns: 1fr 1fr; } .fx-rv-full { grid-column: 1 / -1; } }
.fx-rv-k { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin-bottom: 2px; }
.fx-rv-v { font-size: 12.5px; color: var(--text); font-weight: 500; white-space: pre-line; word-break: break-word; }
.fx-rv-v.is-empty { color: var(--text-faint); font-style: italic; font-weight: 400; }
.fx-rv-v .fx-rv-pill { display: inline-block; padding: 1px 8px; border-radius: 999px; background: var(--brand-soft); color: var(--brand-text); font-size: 11px; font-weight: 700; margin: 1px 3px 1px 0; }
.fx-missing { display: flex; gap: 10px; align-items: flex-start; padding: 10px 14px; border-radius: 12px; background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 32%, var(--surface)); color: var(--warn); font-size: 12px; }
.fx-missing svg { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; }
.fx-missing b { display: block; font-weight: 700; margin-bottom: 2px; }
.fx-ready { display: flex; gap: 10px; align-items: center; padding: 10px 14px; border-radius: 12px; background: var(--ok-soft); color: var(--ok-strong); font-size: 12px; font-weight: 600; }
.fx-ready svg { width: 15px; height: 15px; }

/* ── The ending: the animated tick, confetti, the college's words ─────────────────────────────────── */
.fx-end { position: relative; text-align: center; padding: 30px 16px 26px; overflow: hidden; }
.fx-end h2 { font-size: 20px; font-weight: 800; letter-spacing: -.02em; color: var(--text); margin: 0; }
.fx-end p { font-size: 13px; color: var(--text-muted); margin: 8px auto 0; max-width: 480px; line-height: 1.55; white-space: pre-line; }
.fx-end-note { font-size: 11px; color: var(--text-muted); margin-top: 18px; }
.fx-end-acts { display: flex; justify-content: center; gap: 8px; margin-top: 18px; }

/* ── Errors under a control ───────────────────────────────────────────────────────────────────────── */
.fx-err { display: flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; color: var(--danger); margin-top: 2px; animation: enr-shake .3s; }
.fx-err svg { width: 12px; height: 12px; }

@media (prefers-reduced-motion: reduce) {
    .fx-field, .fx-file, .fx-list-row, .fx-row, .fx-cal-pop, .fx-sugg, .fx-tail.is-ok, .fx-pay-paid i, .fx-err { animation: none; }
    .fx-drop-bar i { animation-duration: 2s; }
}

/* ── The PHOTO STUDIO (.ps-*): a picture comes in by drop, paste, file or camera, is framed on a pan/zoom/
   straighten stage that always stays covered, and leaves as one finished square ── */
.ps { display: flex; flex-direction: column; gap: 14px; }
.ps-error { display: flex; align-items: center; gap: 8px; margin: 0; padding: 8px 12px; border-radius: 10px; background: color-mix(in srgb, var(--danger) 10%, var(--surface)); color: var(--danger); font-size: 12.5px; font-weight: 600; }
.ps-error svg { width: 15px; height: 15px; flex-shrink: 0; }
.ps-entry { display: flex; flex-direction: column; align-items: center; gap: 14px; text-align: center; }
.ps-drop { position: relative; width: 100%; max-width: 520px; padding: 26px 22px 22px; border-radius: 22px; border: 1.5px dashed var(--line); background: var(--surface-muted); display: flex; flex-direction: column; align-items: center; gap: 5px; transition: border-color .18s, background .18s, transform .18s; }
.ps-drop.is-over { border-color: var(--brand); background: var(--brand-soft); transform: scale(1.01); }
.ps-drop-art { position: relative; width: 100px; height: 78px; margin-bottom: 8px; }
.ps-drop-art i { position: absolute; left: 50%; top: 50%; width: 58px; height: 44px; margin: -22px 0 0 -29px; border-radius: 9px; background: linear-gradient(135deg, var(--brand-soft), var(--brand-line)); border: 1px solid var(--brand-line); box-shadow: 0 6px 16px rgba(0, 0, 0, .08); transition: transform .35s cubic-bezier(.2, .8, .2, 1); }
.ps-drop-art i:nth-child(1) { transform: rotate(-12deg) translate(-16px, 4px); }
.ps-drop-art i:nth-child(2) { transform: rotate(9deg) translate(14px, 2px); }
.ps-drop-art i:nth-child(3) { transform: translateY(-6px); background: linear-gradient(135deg, var(--brand), var(--brand-strong, var(--brand))); border-color: transparent; }
.ps-drop:hover .ps-drop-art i:nth-child(1), .ps-drop.is-over .ps-drop-art i:nth-child(1) { transform: rotate(-18deg) translate(-26px, 6px); }
.ps-drop:hover .ps-drop-art i:nth-child(2), .ps-drop.is-over .ps-drop-art i:nth-child(2) { transform: rotate(14deg) translate(24px, 4px); }
.ps-drop-art svg { position: absolute; left: 50%; top: 50%; width: 22px; height: 22px; transform: translate(-50%, -50%) translateY(-6px); color: var(--on-brand, #fff); }
.ps-drop-title { margin: 0; font-size: 15px; font-weight: 800; color: var(--text); letter-spacing: -.01em; }
.ps-drop-sub { margin: 0; font-size: 12px; color: var(--text-muted); }
.ps-drop-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 12px; }
.ps-btn { position: relative; display: inline-flex; align-items: center; gap: 7px; padding: 9px 16px; border-radius: 999px; font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer; border: 1px solid var(--line); background: var(--surface); color: var(--text); transition: transform .15s, box-shadow .15s, background .15s; }
.ps-btn svg { width: 16px; height: 16px; }
.ps-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(0, 0, 0, .08); }
.ps-btn:disabled { opacity: .6; cursor: default; transform: none; box-shadow: none; }
.ps-btn-primary { background: var(--brand); border-color: var(--brand); color: var(--on-brand, #fff); }
.ps-btn-primary:hover { box-shadow: 0 8px 20px var(--brand-ring); }
.ps-native { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.ps-stage { position: relative; width: 100%; max-width: 420px; aspect-ratio: 1; margin: 0 auto; border-radius: 22px; overflow: hidden; background: #0b0f19; touch-action: none; user-select: none; -webkit-user-select: none; cursor: grab; outline: none; box-shadow: 0 14px 40px rgba(0, 0, 0, .22), 0 0 0 1px rgba(255, 255, 255, .06) inset; }
.ps-stage:focus-visible { box-shadow: 0 0 0 3px var(--brand-ring), 0 14px 40px rgba(0, 0, 0, .22); }
.ps-stage.is-dragging { cursor: grabbing; }
.ps-stage canvas { display: block; width: 100%; height: 100%; }
.ps-stage video { display: block; width: 100%; height: 100%; object-fit: cover; }
.ps-stage video.is-mirrored { transform: scaleX(-1); }
.ps-grid { position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity .18s; }
.ps-grid i { position: absolute; background: rgba(255, 255, 255, .55); box-shadow: 0 0 0 .5px rgba(0, 0, 0, .25); }
.ps-grid i:nth-child(1) { left: 33.333%; top: 0; width: 1px; height: 100%; }
.ps-grid i:nth-child(2) { left: 66.666%; top: 0; width: 1px; height: 100%; }
.ps-grid i:nth-child(3) { top: 33.333%; left: 0; height: 1px; width: 100%; }
.ps-grid i:nth-child(4) { top: 66.666%; left: 0; height: 1px; width: 100%; }
.ps-stage.is-dragging .ps-grid, .ps-stage.is-tilting .ps-grid { opacity: 1; }
.ps-guide { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.ps-guide::before { content: ""; position: absolute; left: 50%; top: 45%; width: 54%; height: 68%; transform: translate(-50%, -50%); border-radius: 50%; border: 2px dashed rgba(255, 255, 255, .8); box-shadow: 0 0 0 9999px rgba(6, 9, 16, .3); }
.ps-count { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(6, 9, 16, .35); }
.ps-count span { font-size: 96px; font-weight: 900; color: #fff; text-shadow: 0 6px 30px rgba(0, 0, 0, .5); animation: ps-pop .65s ease-out both; }
@keyframes ps-pop { 0% { transform: scale(1.7); opacity: 0; } 35% { transform: scale(1); opacity: 1; } 100% { opacity: .9; } }
.ps-flash { position: absolute; inset: 0; background: #fff; opacity: 0; pointer-events: none; }
.ps-flash.is-on { animation: ps-flash .5s ease-out both; }
@keyframes ps-flash { 0% { opacity: .95; } 100% { opacity: 0; } }
.ps-cam-bar { display: flex; align-items: center; justify-content: center; gap: 24px; margin-top: 14px; }
.ps-round { width: 44px; height: 44px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--text); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: transform .15s, background .15s; }
.ps-round svg { width: 18px; height: 18px; }
.ps-round:hover { transform: scale(1.06); background: var(--surface-muted); }
.ps-round:disabled { opacity: .5; cursor: default; transform: none; }
.ps-shutter { width: 72px; height: 72px; border-radius: 999px; border: 4px solid var(--brand); background: var(--surface); padding: 4px; cursor: pointer; transition: transform .12s; }
.ps-shutter span { display: block; width: 100%; height: 100%; border-radius: 999px; background: var(--brand); transition: transform .12s; }
.ps-shutter:hover span { transform: scale(.92); }
.ps-shutter:active span { transform: scale(.8); }
.ps-shutter:disabled { opacity: .5; cursor: default; }
.ps-hint { text-align: center; font-size: 12px; color: var(--text-muted); margin: 10px 0 0; }
.ps-tools { display: flex; flex-direction: column; gap: 10px; max-width: 420px; margin: 14px auto 0; width: 100%; }
.ps-tools .ps-hint { margin-top: 0; }
.ps-tool { display: flex; align-items: center; gap: 10px; }
.ps-tool > svg { width: 16px; height: 16px; color: var(--text-muted); flex-shrink: 0; }
.ps-tool-btn { width: 32px; height: 32px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; transition: background .15s, transform .15s; }
.ps-tool-btn svg { width: 15px; height: 15px; }
.ps-tool-btn:hover { background: var(--surface-muted); transform: scale(1.06); }
.ps-range { flex: 1; accent-color: var(--brand); height: 4px; cursor: pointer; margin: 0; }
.ps-deg { width: 44px; text-align: right; font-variant-numeric: tabular-nums; font-size: 12px; color: var(--text-muted); }
.ps-tool-row { display: flex; align-items: center; gap: 8px; }
.ps-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--text-muted); font-family: inherit; font-size: 12px; font-weight: 700; cursor: pointer; transition: background .15s, color .15s; }
.ps-chip svg { width: 14px; height: 14px; }
.ps-chip:hover { background: var(--surface-muted); }
.ps-chip.is-on { background: var(--brand-soft); border-color: var(--brand-line); color: var(--brand-text); }
.ps-spacer { flex: 1; }
.ps-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; margin-top: 16px; }
.ps-actions svg { width: 15px; height: 15px; margin-right: 6px; }
