/* ==========================================================================
   THEME — NAVIGATION  (2 of 3)
   ==========================================================================
   Nav pills, disclosure menus, the user avatar, narrow-viewport behaviour,
   and the hairline/centring rules that keep edges consistent.

   ⛔ SPLIT FROM ONE 2,708-LINE FILE (2026-09-04) so no stylesheet is too long to
   review. Cut at DEPTH-0 BOUNDARIES — points where no rule, media query or
   comment was open — and reconstruction was verified byte-identical.

   ⛔ THE LOAD ORDER IN base.html IS THE CASCADE. These three are separate
   `<link>`s that load AFTER styles.css, so a rule here beats an equally specific
   rule in any @import'ed module on source order. Measured twice while fixing an
   icon leak: a rule in _10-components.css could not win against one here.

   Every rule is scoped `body.laser-theme`, gated by LASER_THEME_ENABLED — which
   defaults to False, so this whole layer is inert unless the flag is on.
   ========================================================================== */


/* Brand: holographic on HOVER only, exactly as LASER's `Laser HQ` Typography does. A
   permanently gradient wordmark competes with the active nav pill for attention. */
body.laser-theme .topbar-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-1);
    font-size: 0.9375rem /* 15px */;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--laser-text-primary);
    text-decoration: none;
    white-space: nowrap;
    transition: opacity var(--laser-transition);
}

body.laser-theme .topbar-brand:hover .topbar-brand-name {
    background: var(--laser-holographic);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

@media (forced-colors: active) {
    body.laser-theme .topbar-brand:hover .topbar-brand-name {
        background: none;
        -webkit-text-fill-color: currentColor;
        color: CanvasText;
    }
}

body.laser-theme .topbar-brand-logo {
    max-height: 28px;
    width: auto;
    border-radius: 4px;
}

/* ─── Nav pills (LaserButton) ───────────────────────────────────────────── */

body.laser-theme .topbar-nav {
    display: flex;
    align-items: center;
    gap: var(--s-1);
    min-width: 0;
    overflow-x: auto;           /* the STRIP scrolls, never the page */
    scrollbar-width: none;
}

/* ⛔ #2167: `overflow-x: auto` CLIPS BOTH AXES. Setting it computes `overflow-y: auto`
   too, so the `More` panel — an absolutely-positioned child of a `<details>` inside this
   strip — was cut to the strip's own height. MEASURED at 1920x1080: nav 35px tall, panel
   473px, **492px clipped away, 0 of 10 links reachable**. Every one of those ten had a
   valid href and NO other door: Refund Approvals and Review Queue are work queues an
   approver could not open at all, and Payment Settings is where processor credentials
   live. The chevron flipped, so the operator concluded the button was dead.

   ★ The fix keeps the horizontal scroll — it is load-bearing when the pills outgrow the
   bar — and lifts the clip ONLY while a menu is open. `:has()` makes that a pure-CSS
   state query; no JS, and the `<details>` element keeps the keyboard, Escape and focus
   behaviour the markup comment says it was chosen for.

   ⚠️ Do NOT "simplify" this to `overflow: visible` on the base rule. That reintroduces
   the page-level horizontal scroll this strip exists to contain — which is why the
   original author reached for `overflow-x` in the first place. */
body.laser-theme .topbar-nav:has(details[open]) {
    overflow: visible;
}

body.laser-theme .topbar-nav::-webkit-scrollbar { display: none; }

body.laser-theme .topbar-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    padding: var(--s-2) var(--s-3);
    border: 1px solid transparent;
    border-radius: var(--border-radius-sm);
    color: var(--laser-text-secondary);
    font-size: var(--font-size-sm);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    list-style: none;           /* it is a <summary> on the overflow item */
    transition: background-color var(--laser-transition), color var(--laser-transition),
                border-color var(--laser-transition), box-shadow var(--laser-transition);
}

body.laser-theme .topbar-pill::-webkit-details-marker { display: none; }

body.laser-theme .topbar-pill:hover {
    border-color: var(--laser-primary);
    color: var(--laser-primary);
}

/* ⛔ Styled from `aria-current`, not a separate `.active` class: the attribute is what a
   screen reader announces, so binding the visual to it makes the two impossible to
   disagree. Fill + glow are LASER's own active-pill values. */
body.laser-theme .topbar-pill[aria-current="page"] {
    background: var(--laser-primary);
    border-color: var(--laser-primary);
    color: var(--laser-bg-page);
    box-shadow: 0 0 12px rgba(0, 229, 255, 0.4);
}

/* ─── Disclosure menus (MUI <Menu>, as <details>) ───────────────────────── */

body.laser-theme .topbar-more,
body.laser-theme .topbar-user {
    position: relative;
}

body.laser-theme .topbar-menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 1200;
    min-width: 210px;
    display: flex;
    flex-direction: column;
    padding: var(--spacing-1);
    /* ⛔ A FLOATING MENU IS NOT A CARD, and this is why it gets its own ground rather than
       `--laser-glass-sheen`. A card sits ON the page, so a 4.5% white fill over the page
       backdrop reads as glass. This panel floats OVER arbitrary page content — a table, a
       paragraph, another card — so the same fill let that content read straight through the
       menu's own links. Owner reported it 2026-09-09 as "too much transparent".

       The blur stays (it is still glass and still separates from what is behind it), but the
       fill is now the CARD SURFACE at high opacity, so the text underneath is suppressed
       whatever it happens to be. A menu has to be readable over the worst case, not the
       average one.

       ⚠️ The cast shadow is kept SEPARATE from `--laser-glass-edge`: this panel needs a
       deeper drop than a panel sitting on the page ground, and it keeps the cyan glow that
       identifies an open menu. The inset lines match the token's own values so the lit lip
       still matches every other glass surface. */
    background: color-mix(in srgb, var(--laser-bg-card) 92%, transparent);
    -webkit-backdrop-filter: var(--laser-glass-filter);
    backdrop-filter: var(--laser-glass-filter);
    border: 1px solid var(--laser-glass-border);
    border-radius: var(--border-radius-md);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.10),
        inset 0 -1px 0 rgba(0, 0, 0, 0.20),
        0 12px 32px rgba(0, 0, 0, 0.45),
        var(--laser-glow-subtle);
    animation: laser-fade-in-scale 150ms cubic-bezier(0.4, 0, 0.2, 1) both;
}

/* ⛔ Hard-coded shadow ⇒ not swept by the token-level opt-outs; it carries its own.
   The cast shadow SURVIVES both: with the blur gone this is an opaque panel floating over
   content, and that is exactly when a drop shadow is doing real work telling the user
   which layer is on top. Only the specular insets go. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    body.laser-theme .topbar-menu {
        background: var(--laser-bg-card);
        box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), var(--laser-glow-subtle);
    }
}

@media (prefers-reduced-transparency: reduce) {
    body.laser-theme .topbar-menu {
        background: var(--laser-bg-card);
        box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), var(--laser-glow-subtle);
    }
}

/* Right-anchored, so the account menu never runs off the viewport edge. */
body.laser-theme .topbar-menu-right { left: auto; right: 0; }

body.laser-theme .topbar-menu a,
body.laser-theme .topbar-menu-signout {
    display: flex;
    align-items: center;
    padding: var(--s-2) var(--s-3);
    border: 0;
    border-radius: var(--border-radius-sm);
    background: none;
    color: var(--laser-text-primary);
    font: inherit;
    font-size: var(--font-size-sm);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

body.laser-theme .topbar-menu a:hover,
body.laser-theme .topbar-menu-signout:hover {
    background: var(--laser-wash-8);
    color: var(--laser-primary);
}

/* MuiListSubheader: cyan, upper, tracked out. */
body.laser-theme .topbar-menu-heading {
    padding: var(--s-2) var(--s-3) var(--s-1);
    color: var(--laser-primary);
    font-size: var(--font-size-caption);
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
}

/* The email row — LASER's disabled first MenuItem. Not interactive, so not a link. */
body.laser-theme .topbar-menu-meta {
    padding: var(--s-2) var(--s-3) var(--s-2);
    margin-bottom: 2px;
    border-bottom: 1px solid var(--laser-border-divider);
    color: var(--laser-text-secondary);
    font-size: var(--font-size-caption);
    overflow-wrap: anywhere;
}

body.laser-theme .topbar-menu form { margin: 0; }
body.laser-theme .topbar-menu-signout { width: 100%; }

/* ─── User avatar ───────────────────────────────────────────────────────── */

body.laser-theme .topbar-user > summary {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-1);
    padding: var(--s-1) var(--s-2);
    min-height: var(--h-touch);
    border-radius: var(--border-radius-sm);
    color: var(--laser-text-secondary);
    font-size: var(--font-size-sm);
    cursor: pointer;
    list-style: none;
}

body.laser-theme .topbar-user > summary::-webkit-details-marker { display: none; }
body.laser-theme .topbar-user > summary:hover { background: var(--laser-wash-4); }

body.laser-theme .topbar-avatar {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--laser-primary);
    /* Cyan is a LIGHT fill: the initial must be near-black. */
    color: var(--laser-bg-page);
    font-size: 13px;
    font-weight: 700;
    flex: 0 0 auto;
}

/* ─── Narrow viewports ──────────────────────────────────────────────────── */

@media (max-width: 900px) {
    /* Below this the pills cannot all fit beside the brand, so the rail takes over
       navigation and the bar keeps only identity + account. */
    body.laser-theme .topbar-menu-btn { display: inline-flex; align-items: center; justify-content: center; }
    body.laser-theme .topbar-nav { display: none; }
    body.laser-theme .topbar-user-name { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    body.laser-theme .topbar-menu { animation: none; }
}

/* ─── Hairlines: one colour, one edge ────────────────────────────────────────

   Owner, 2026-09-03: "lots of things have horizontal lines, small very thin ones — feels
   annoying, or they are very not aligned, that can be the reason." The second guess was
   right, and it is measurable. On the CSR dashboard at 1860px: 47 hairlines across
   **20 different left edges**, including these near-misses —

     319px  .metric-card · .filter-bar · .accounts-section · .portfolio-tab
     320px  .section-header                                    ← 1px off
     473px  .filter-input
     475px  .topbar-pill                                       ← 2px off

   A 1px offset is the worst case: too small to read as a deliberate indent, big enough
   to see. The cause is nested borders — `.accounts-section` has `border: 1px`, so its
   child `.section-header` begins 1px inside it and that child's own `border-bottom`
   lands 1px right of the parent's line. Stacking two bordered boxes always does this.

   ...and **7 different hairline colours** were in play (5 of them near-identical tints of
   the same intent), which is what makes a set of lines read as noise rather than structure.
   -------------------------------------------------------------------------- */

/* A divider INSIDE an already-bordered container spans the full inner width, so its line
   starts where the parent's does. Negative inline margins pulled back out to the parent's
   padding edge, then the padding restored — the standard full-bleed-inside-a-box idiom. */
body.laser-theme .section-header,
body.laser-theme .card-header,
body.laser-theme .filter-bar,
body.laser-theme .summary-header {
    border-left: 0;
    border-right: 0;
}

/* ⛔ The negative margin is the load-bearing half. Measured: `.accounts-section` paints its
   border at x=319 and its content box therefore begins at x=320, so the child's divider sat
   1px inside — visible, and too small to read as intentional. `margin-inline: -1px` pulls
   the child back over the parent's border so both lines share one edge; `width: calc(100% +
   2px)` restores what the margin took. Applied only where the child is a full-bleed divider
   inside a bordered surface. */
body.laser-theme .accounts-section > .section-header,
body.laser-theme .accounts-section > .filter-bar,
body.laser-theme .card > .card-header,
body.laser-theme .panel > .section-header,
body.laser-theme .section-card > .section-card-head {
    margin-inline: -1px;
    width: calc(100% + 2px);
    box-sizing: border-box;
}

/* ⛔ ONE divider colour. `--laser-border-divider` is the token for "a line separating
   content"; `--laser-border-cyan` is for "the edge of a surface". Mixing them — plus
   rgba(255,255,255,.05), a gold tint and a red tint — is what produced 7 colours where
   there should be 2. Component-specific accent borders (an alert's 4px rail, the topbar's
   2px cyan edge) are deliberate and are NOT included here. */
body.laser-theme .section-header,
body.laser-theme .card-header,
body.laser-theme .summary-header,
body.laser-theme .detail-row,
body.laser-theme .filter-bar,
body.laser-theme .select-controls,
body.laser-theme table td {
    border-bottom-color: var(--laser-border-divider);
}


/* Removed 2026-09-07: the `.org-card-stats` / `.org-card-rollup` /
   `.card-rollup-potential` theme rules. Those classes went with the dashboard
   card->table conversion and are rendered by no template. */

/* ─── Centring the things that should be centred ─────────────────────────────
   Owner, 2026-09-03: "other buttons and other stuff to have the text and other stuff in
   the center." These are the recurring cases, all the same root cause as #2046/#2009 —
   a flex parent centres on the CROSS axis via `align-items` and needs `justify-content`
   for the MAIN axis.
   ⚠️ Deliberately NOT applied to data columns: a column of values needs a shared left or
   right edge to be scannable, and the audit already found 119 `text-align: center`
   MISUSES on exactly that. Centring is for controls and short display text only.
   -------------------------------------------------------------------------- */

/* Every button centres its own label. A button whose text sits left of centre reads as
   broken, and `inline-flex` + `justify-content` is the only way to guarantee it once an
   icon or a count badge shares the box. */
body.laser-theme .btn-action,
body.laser-theme .btn-success,
body.laser-theme .btn-small,
body.laser-theme .btn-primary,
body.laser-theme .btn-secondary,
body.laser-theme .btn-danger,
body.laser-theme .btn-edit,
body.laser-theme .btn-filter,
body.laser-theme .btn-clear,
body.laser-theme .btn-verify,
body.laser-theme .btn-assign,
body.laser-theme .btn-unassign,
body.laser-theme .btn-export-csv,
body.laser-theme .btn-table-action,
body.laser-theme .btn-status-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    text-align: center;
}

/* A row of actions wraps together and sits at the END of its container.

   ⛔ WAS `justify-content: center` until 2026-09-07, against the approved samples.
   `docs/design/samples/_tokens.css:726` specifies `.modal-foot { justify-content:
   flex-end }`, and NINE templates independently asked for `flex-end` on their own
   `.form-actions` / `.modal-actions` — every one of them lost to this rule (0,2,0)
   and rendered centred.

   ★ Nine page authors reaching for the same value the sample already specifies is
   the strongest signal available that the shared rule was wrong. A Submit/Cancel
   pair centred in a 600px dialog reads as a landing-page call to action rather than
   a dialog footer; end-alignment is the convention every desktop UI uses, and it
   puts the confirming control nearest the user's pointer as it travels down the form.

   ⚠️ `align-items: center` STAYS — that is cross-axis (vertical) alignment of
   controls of differing height within the row, which is correct and unrelated. */
body.laser-theme .form-actions,
body.laser-theme .modal-actions,
body.laser-theme .card-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--spacing-2);
}

/* Status chips and badges: the label centres inside the pill. */
body.laser-theme .status-badge,
body.laser-theme .laser-badge,
body.laser-theme .verification-badge,
body.laser-theme .signnow-state-badge,
body.laser-theme .tab-count,
body.laser-theme .record-tab-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* An empty state, a metric tile and a modal header are short display text — genuinely
   centred, unlike a data column. */
body.laser-theme .empty-state,
body.laser-theme .no-data,
body.laser-theme .no-results,
body.laser-theme .laser-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* Icon buttons and avatars: a glyph must sit dead centre in its box, which `line-height`
   alone never quite achieves across fonts. */
body.laser-theme .icon-container,
body.laser-theme .status-icon,
body.laser-theme .modal-close,
body.laser-theme .topbar-menu-btn,
body.laser-theme .laser-badge-dot {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* ─── Notification bell (notifications/_bell.html) ──────────────────────────
   ⛔ This component was BUILT AND INCLUDED NOWHERE, and had no styles at all — the
   notifications app ships an inbox, per-channel settings, routing settings and this
   polling bell (#1738/#1743), and not one template linked to any of it. Mounting it in
   the top bar is the reachability fix; these are the styles it never had.
   Mirrors laser-platform's `NotificationBell` position in TopBar.tsx. */

body.laser-theme .notif-bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--border-radius-sm);
    color: var(--laser-text-secondary);
    text-decoration: none;
    transition: background-color var(--laser-transition), color var(--laser-transition);
}

body.laser-theme .notif-bell:hover {
    background: var(--laser-wash-8);
    color: var(--laser-primary);
}

/* An SVG, so it inherits `currentColor` and follows the hover state — an emoji bell
   (&#128276;) renders as a colour glyph on most platforms and would ignore it. */
body.laser-theme .notif-bell-icon {
    width: 19px;
    height: 19px;
    display: block;
}

/* The count sits on the bell's upper-right. Cyan fill ⇒ near-black digits, and tabular
   figures so 9 and 99 occupy a stable width instead of nudging the badge. */
body.laser-theme .notif-bell-badge {
    position: absolute;
    top: 2px;
    right: 2px;
    min-width: 17px;
    height: 17px;
    padding: 0 var(--s-1);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: var(--laser-primary);
    color: var(--laser-bg-page);
    font-size: var(--font-size-caption);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

/* ⚠️ The template toggles the badge with the `hidden` ATTRIBUTE (`badge.hidden = …`).
   `display: inline-flex` above would override the UA's `[hidden] { display: none }`, so a
   zero count would show an empty pill. This restores it. */
body.laser-theme .notif-bell-badge[hidden] {
    display: none;
}

/* ─── The primary action is CYAN, not green ─────────────────────────────────

   Observed in a real browser (2026-09-03, org-admin session): every form's main button
   rendered `rgb(0, 230, 118)` — the SUCCESS hue. "Login", "Verify and Enable MFA",
   "Confirm", "Send LOA" — all of them.

   Cause: `.btn-success` is used as the PRIMARY-ACTION class in 100 places across the
   templates, and it paints `background-color: var(--success-color)`. That reads correctly
   on the old light theme, where the brand blue and the success green were both mid-tone
   and the distinction mattered less. On LASER it is actively wrong: cyan IS the brand, and
   green means "this already succeeded" — so a form's call-to-action was announcing success
   before the user pressed it.

   ⛔ Fixed HERE, not by renaming 100 class attributes. The class name is now inaccurate,
   but renaming it across 100 call sites is a large mechanical diff with no visual gain and
   real conflict risk against other branches — and it becomes MUI `<Button color="primary">`
   at the migration regardless. Repointing the fill is the whole fix.

   ★ The genuinely-success-coloured surfaces (a paid badge, a settled chip, a success alert)
   read `--success-color` directly and are untouched.
   -------------------------------------------------------------------------- */

body.laser-theme .btn-success,
body.laser-theme a.btn-success,
body.laser-theme button.btn-success {
    background-color: var(--laser-primary);
    /* Cyan is a LIGHT fill: near-black text, never white. */
    color: var(--laser-bg-page);
    border-color: var(--laser-primary);
}

/* ⛔ HOVER INVERTS: the fill goes dark and the LABEL becomes cyan (owner, 2026-09-10).
   It previously moved `--laser-primary` -> `--laser-primary-dark` and kept the label at
   `--laser-bg-page`, so a darker cyan still carried near-black text — the button barely
   changed and the text only got harder to read against it.

   ⚠️ The border STAYS `--laser-primary`, not the dark fill. It is what keeps the button's
   shape and its identity as the primary action while the inside empties out; matching the
   border to the background would make a hovered button read as a hole in the page.

   ★ Cyan on near-black is the theme's own highest-contrast pair — the same one the page
   ground and `--laser-primary` already use everywhere else — so this reads as the button
   turning itself inside out, not as a new colour appearing on hover. */
body.laser-theme .btn-success:hover,
body.laser-theme a.btn-success:hover,
body.laser-theme button.btn-success:hover {
    background-color: var(--laser-bg-page);
    border-color: var(--laser-primary);
    color: var(--laser-primary);
    box-shadow: var(--laser-glow);
}

/* `.btn-payment` is the same shape on the debtor flow: the one action the page exists for. */
body.laser-theme .btn-payment {
    background-color: var(--laser-primary);
    color: var(--laser-bg-page);
    border-color: var(--laser-primary);
}

body.laser-theme .btn-payment:hover {
    background-color: var(--laser-primary-dark);
    color: var(--laser-bg-page);
    box-shadow: var(--laser-glow);
}

/* The sidebar's collapse control kept a stale slate fill (rgb(15,23,42)) from the light
   theme — visible as a lighter grey square against the near-black rail. */
body.laser-theme .sidebar-toggle {
    background-color: var(--laser-bg-card);
    border: 1px solid var(--laser-border-cyan);
    color: var(--laser-text-secondary);
}

body.laser-theme .sidebar-toggle:hover {
    background-color: var(--laser-wash-8);
    border-color: var(--laser-primary);
    color: var(--laser-primary);
}

/* ═══ TOP NAV REPLACES THE SIDEBAR ══════════════════════════════════════════

   Owner, 2026-09-03: "we only want ONE — the top nav bar completely replacing the sidebar
   until the mobile view; that too will use the hamburger."

   ⚠️ A DELIBERATE DIVERGENCE FROM LASER, which keeps an AppBar AND a Drawer. The portal
   keeps only the bar; the rail survives solely as the mobile drawer. `_top_bar.html`
   therefore carries EVERY destination the rail carried, with the rail's role gating copied
   condition-for-condition — a subset would silently strip a destination from whichever role
   depended on it.

   ⛔ The rail is HIDDEN, never deleted from the DOM. Three reasons, all load-bearing:
     · it IS the mobile drawer below 900px — `toggleSidebar()` and its outside-click handler
       already work, and both read the `open` class on `#app-sidebar`;
     · `accounts/tests/test_identity_first_branding_1541.py` asserts the tenant's identity
       marker is present in the response — deleting the rail's markup would fail it, and
       that test exists because #1948 already broke it once;
     · a `display: none` rail costs nothing, while a deleted one would need every one of
       its links re-proven reachable.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (min-width: 901px) {
    body.laser-theme .app-sidebar {
        display: none;
    }

    /* The rail's own toggle and scrim belong to the drawer, so they go with it. */
    body.laser-theme .sidebar-toggle,
    body.laser-theme #sidebar-overlay {
        display: none;
    }

    /* `.app-layout` is a flex row sized around the rail. With the rail gone the content
       column takes the full width — and `min-width: 0` is what actually lets it, since a
       flex child defaults to `min-width: auto` and refuses to shrink below its content. */
    body.laser-theme .app-main-wrapper {
        width: 100%;
        min-width: 0;
    }

    /* The hamburger is the drawer's only opener, so it goes too. */
    body.laser-theme .topbar-menu-btn {
        display: none;
    }
}

@media (max-width: 900px) {
    /* Below this the pills cannot fit beside the brand, so the DRAWER takes over
       navigation and the bar keeps identity + notifications + account. */
    body.laser-theme .topbar-nav {
        display: none;
    }

    body.laser-theme .topbar-menu-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    body.laser-theme .topbar-user-name {
        display: none;
    }
}

/* ─── Icons in the menus (partials/_icon.html) ─────────────────────────────── */

/* An icon column only helps if the labels still line up, so the glyph is fixed-size and
   the label takes the remaining space. `flex: 0 0 auto` stops a 14px icon being squeezed
   by a long label — which is what turns an icon column into a ragged one. */
body.laser-theme .topbar-menu a,
body.laser-theme .topbar-menu-signout {
    gap: var(--spacing-1);
}

body.laser-theme .ic {
    flex: 0 0 auto;
    /* Optical centring: a 24-unit stroke glyph sits slightly high beside 13px text. */
    vertical-align: -0.125em;
}

body.laser-theme .topbar-menu a > span,
body.laser-theme .topbar-menu-signout > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The role badge in the account menu — identity the rail used to show. */
body.laser-theme .topbar-menu-role {
    align-self: flex-start;
    margin: 0 var(--s-3) var(--spacing-1);
    padding: 2px var(--s-2);
    border: 1px solid var(--laser-border-cyan);
    border-radius: 999px;
    background: var(--laser-wash-8);
    color: var(--laser-primary);
    font-size: var(--font-size-caption);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* ─── Secondary actions: outlined cyan, not info-blue ───────────────────────

   Observed in a real browser (2026-09-03, org-admin dashboard): the Staff Tools grid
   rendered as a block of `rgb(68, 138, 255)` buttons — the **--info-color** blue — while
   the page's primary action beside it was cyan. Two different "this is a button" colours
   on one card, neither of them the brand.

   `.btn-action` is the SECONDARY-ACTION class in **97** places (as `.btn-success` is the
   primary in 100). It paints `--info-color` because on the old light theme info-blue read
   as a neutral action colour. On LASER, info-blue is the *status* hue for an informational
   message — using it for "Manage Users" says nothing true.

   ⛔ Fixed at the token, not by renaming 97 attributes: a large mechanical diff with no
   visual gain, real conflict risk, and it becomes `<Button variant="outlined">` at the
   migration anyway.

   ★ OUTLINED rather than filled, which is the substantive design call: a card holding six
   equally-weighted filled buttons has no focal point, so the eye has nowhere to start. One
   filled primary + outlined secondaries is LASER's own `LaserButton` pattern and restores
   the hierarchy the blue block destroyed.
   -------------------------------------------------------------------------- */

body.laser-theme .btn-action,
body.laser-theme a.btn-action,
body.laser-theme button.btn-action {
    background-color: transparent;
    border: 1px solid var(--laser-border-cyan);
    color: var(--laser-primary);
}

body.laser-theme .btn-action:hover,
body.laser-theme a.btn-action:hover,
body.laser-theme button.btn-action:hover {
    background-color: var(--laser-primary);
    border-color: var(--laser-primary);
    /* Cyan is a LIGHT fill — near-black text, never white. */
    color: var(--laser-bg-page);
    box-shadow: var(--laser-glow);
}

/* `.btn-report` / `.btn-edit` / `.btn-filter` / `.btn-clear` carried stale light-theme
   slate fills (rgb(30,41,59) — visible as lighter grey slabs on the near-black ground).
   Same outlined treatment, so every secondary action on a page matches. */
body.laser-theme .btn-report,
body.laser-theme .btn-edit,
body.laser-theme .btn-filter,
body.laser-theme .btn-clear,
body.laser-theme .btn-table-action,
body.laser-theme .btn-secondary {
    background-color: transparent;
    border: 1px solid var(--laser-border-cyan);
    color: var(--laser-primary);
}

body.laser-theme .btn-report:hover,
body.laser-theme .btn-edit:hover,
body.laser-theme .btn-filter:hover,
body.laser-theme .btn-clear:hover,
body.laser-theme .btn-table-action:hover,
body.laser-theme .btn-secondary:hover {
    background-color: var(--laser-primary);
    border-color: var(--laser-primary);
    color: var(--laser-bg-page);
    box-shadow: var(--laser-glow-subtle);
}

/* A card's section header took a teal-tinted fill from the old palette. The recessed
   appbar ground plus a cyan hairline is the LASER equivalent (MuiTableHead's treatment). */
body.laser-theme .staff-tools-card .section-header,
body.laser-theme .admin-section .section-header {
    background: var(--laser-bg-appbar);
    color: var(--laser-primary);
    font-size: var(--font-size-caption);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* ─── Select — MUI's Select, not the browser's ──────────────────────────────

   Owner, 2026-09-03: "the select / dropdown selection inputs are not having the exact
   design as the laser because they are, like, the standard select."

   Correct, and measurable: `appearance: auto` on every one of the **53** `<select>`s in the
   templates. The colour tokens had been applied, so the box matched — but the CONTROL was
   still the platform's: the OS arrow, the OS focus ring, the OS option list. Beside a
   LASER-styled text input it reads as a foreign widget, which is exactly what the owner saw.

   MUI's `Select` renders its own trigger with a `SelectIcon` chevron. `appearance: none`
   plus an inline-SVG chevron as a background image is the equivalent a native `<select>`
   can reach — no JS, no library, and it degrades to the native control if the property is
   unsupported.

   ⛔ THE ONE THING THIS CANNOT STYLE is the open option LIST: browsers render `<option>`
   from the OS, and no CSS reaches it (a JS listbox would — MUI is one — but that is the
   migration's job, not a hand-rolled widget here). So `option` gets explicit colours as a
   floor: without them, a dark-theme select on some platforms renders white-on-white and the
   choices are invisible. That is a real bug, and it is the reason those two rules exist.
   -------------------------------------------------------------------------- */

body.laser-theme select {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    /* The chevron, as a data URI so it needs no asset and inherits nothing it shouldn't.
       Stroke is --laser-primary; `right center` keeps it clear of the text. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2300E5FF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    /* Inset matches the field's own horizontal padding (--s-3), so the chevron lines
       up with the text on the other side rather than sitting hard against the border. */
    background-position: right var(--s-3) center;
    background-size: 16px 16px;
    /* Room for the chevron so a long option never runs under it. */
    padding-right: calc(var(--s-3) * 2 + 16px);
    cursor: pointer;
}

/* `background-image` and `background-color` are both set above, so the shorthand order
   matters: keep the colour on its own property or the chevron is painted over. */
body.laser-theme select:hover {
    border-color: var(--laser-primary);
}

body.laser-theme select:focus {
    border-color: var(--laser-primary);
    box-shadow: var(--laser-glow-subtle);
}

/* A disabled select must not look interactive: no chevron, no pointer. */
body.laser-theme select:disabled {
    background-image: none;
    color: var(--laser-text-disabled);
    cursor: not-allowed;
    opacity: 1;                 /* the UA's 0.6 on top of an alpha bg reads as broken */
}

/* ⚠️ The option LIST is drawn by the OS and mostly beyond CSS — but these two declarations
   are the difference between readable and invisible on a dark theme, because some platforms
   default the popup to the page's colours and others to white. */
body.laser-theme select option {
    background-color: var(--laser-bg-card);
    color: var(--laser-text-primary);
}

body.laser-theme select option:disabled {
    color: var(--laser-text-disabled);
}

/* A multi-select / sized select is a LIST, not a trigger — the chevron would be a lie. */
body.laser-theme select[multiple],
body.laser-theme select[size]:not([size="1"]) {
    background-image: none;
    padding-right: var(--spacing-2);
    cursor: default;
}

/* The caret on the overflow menu rotates when it opens — MUI's Select/Menu behaviour. */
body.laser-theme .topbar-more-caret {
    display: inline-flex;
    transition: transform var(--laser-transition-fast);
}

body.laser-theme .topbar-more[open] .topbar-more-caret {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    body.laser-theme .topbar-more-caret { transition: none; }
}

/* ═══ TYPE SCALE — one size per role ════════════════════════════════════════

   Owner, 2026-09-03: "some text are very big, some… I can clearly see that THIS and THIS
   text should have been same, but they are not… that just hurts."

   Correct, and the measurement is worse than it sounds. Read off the live CSR dashboard:

     h1               16px      ← the page title
     h3               18px      ← BIGGER THAN THE H1
     h2               12px AND 14px  ← the same heading level, two sizes on ONE page
     .metric-value    14px      ← a KPI number, smaller than body text

   So the hierarchy was not merely inconsistent, it was INVERTED: h3 > h1 > h2. The `h2`
   split is the exact thing the owner described — two peers that should match and don't.

   Cause: #1628 defined a real scale (`--font-size-*`, and the SEMANTIC layer on top of it),
   but each page then set heading sizes inline, so 63 templates each picked their own. This
   binds every heading role to the existing semantic tokens instead — repointing those ~6
   token lines now restyles every surface, which is what the token layer was for.

   ⚠️ Sizes come from the tokens, never literals: `--font-size-page-title` etc. already
   exist and already carry the iOS ≥16px note on body text.
   ═══════════════════════════════════════════════════════════════════════════ */

body.laser-theme h1,
body.laser-theme .page-header-title,
body.laser-theme .page-title {
    font-size: var(--font-size-page-title);   /* 24px — the largest thing on the page */
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.02em;
}

body.laser-theme h2,
body.laser-theme .section-title {
    font-size: var(--font-size-section);      /* 20px — one step down, ALWAYS this */
    font-weight: 600;
    line-height: 1.25;
    letter-spacing: -0.01em;
}

body.laser-theme h3 {
    /* ⛔ Was 18px, i.e. larger than the h1. A subsection cannot outrank its own page. */
    font-size: var(--font-size-body);         /* 16px */
    font-weight: 600;
    line-height: 1.3;
}

body.laser-theme h4,
body.laser-theme h5,
body.laser-theme h6 {
    font-size: var(--font-size-body-small);   /* 14px */
    font-weight: 600;
    line-height: 1.35;
}

/* ⛔ A section header inside a CARD is still an h2/h3 in the outline, but it is rendered as
   a LABEL — small, tracked, uppercase. That styling is why the same `h2` measured 12px in
   one place and 14px in another: two visual treatments of one level. Both are legitimate;
   what was missing is that the label treatment is chosen by the CONTAINER, not by the tag,
   so it is stated once here instead of per page. */
body.laser-theme .section-header h2,
body.laser-theme .section-header h3,
body.laser-theme .card-header h2,
body.laser-theme .card-header h3,
body.laser-theme .staff-tools-card .section-header h2 {
    font-size: var(--font-size-caption);      /* 12px */
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--laser-primary);
}

/* KPI numbers. A metric is the thing a dashboard exists to show, so it is the biggest text
   in its tile — it was 14px, i.e. smaller than the body copy beside it. */
/* ⚠️ `.stat-value` is deliberately NOT in this list. A KPI TILE exists to show one
   number, so 32px is right there. But `.stat-value` is also used for the two small
   facts on an ORGANISATION CARD -- a user count and a last-login date -- where 32px
   made the metadata shout louder than the org name beside it (measured 32px/700
   against 13px/400) and made every card twice as tall as it needed to be.
   (The organisation CARD and its `.org-card-stats` rule were removed 2026-09-07 with
   the card->table conversion; `.stat-value` stays out of this list for the other
   non-KPI surfaces that still use it.) */
/* ⛔ SCOPED 2026-09-07 — this was a BARE `body.laser-theme .metric-value`, and it
   re-broke a bug `_05-guide-and-shell.css:521` had already fixed three days earlier.

   `_05` scoped its rule to `.metrics-grid .metric-value` precisely because `dashboard`
   and `fdw_health` put `.metric-value` inside `.health-metric` — a compact
   `space-between` label/value STATUS ROW, not a KPI tile — where 32px is wrong by a
   factor of two. This rule then re-applied 32px to those same status rows at (0,2,0),
   beating both `_05`'s scoped rule and the two pages' own overrides.

   ★ The pattern to notice: `_05`'s fix REMOVED work (two pages could delete their
   duplicates); re-introducing the bare selector here silently reinstated the need for
   them. A shared rule that is wrong for some callers forces every caller to override —
   which manufactures the duplication the ownership ratchet then counts. The comment
   above already reasons this out for `.stat-value`; `.metric-value` needed the same
   care and did not get it.

   ⚠️ `.kpi-value` and `.grand-total-display .value` stay UNSCOPED on purpose: both are
   only ever a headline figure, so there is no status-row case for them. */
body.laser-theme .metrics-grid .metric-value,
body.laser-theme .kpi-value,
body.laser-theme .grand-total-display .value {
    font-size: var(--fs-metric);              /* 28px — D2 (SAMPLE): 28/12 = 2.33x */
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.025em;
    font-variant-numeric: tabular-nums lining-nums;
}

/* …and its caption is the smallest, so the pair reads as one object. */
body.laser-theme .metric-label,
body.laser-theme .stat-label,
body.laser-theme .kpi-label,
body.laser-theme .grand-total-display .label {
    font-size: var(--font-size-caption);      /* 12px */
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--laser-text-secondary);
}

/* One body size, so peer paragraphs and list items cannot disagree. */
body.laser-theme p,
body.laser-theme li,
body.laser-theme .detail-value,
body.laser-theme .summary-item .value {
    font-size: var(--font-size-body-small);   /* 14px */
    line-height: 1.55;
}

/* One label size across every form — 185 `.form-group`s make this the widest-reaching rule. */
body.laser-theme label,
body.laser-theme .detail-label,
body.laser-theme .summary-item .label,
body.laser-theme .field-label {
    font-size: var(--font-size-body-small);
    font-weight: 500;
}
