/*
 * Aurora — OCM 2026 design tokens + base components.
 *
 * Mockup-only; not wired into the production app yet. The token names are
 * the contract that every Aurora component will consume. Light + dark are a
 * pure token swap on the same component CSS.
 */

/*
 * The Inter @font-face declarations used to arrive here as
 * `@import url("https://rsms.me/inter/inter.css")`. Two problems with
 * that: it was a fifth third-party origin on the render path, and a CSS
 * @import cannot be seen by the browser's preload scanner, so it only
 * starts downloading after aurora.css has arrived and parsed. First
 * paint waited on a serial round trip to a host nobody here controls.
 *
 * Inter is now vendored at js/vendor/inter/ and pulled in as a real
 * <link> ahead of this file, which the preload scanner can start
 * immediately and in parallel. The three places that load aurora.css
 * emit it: pl_aurora_asset_tags() in pika-danio.php, login-form.html and
 * enroll-mfa.html. Add the link alongside aurora.css in any new one.
 */

:root {
    color-scheme: light;

    /* --- Neutral ramp (OKLCH) --- */
    --gray-0:  oklch(99% 0.002 270);
    --gray-1:  oklch(97% 0.003 270);
    --gray-2:  oklch(94% 0.004 270);
    --gray-3:  oklch(89% 0.005 270);
    --gray-4:  oklch(82% 0.006 270);
    --gray-5:  oklch(70% 0.008 270);
    --gray-6:  oklch(58% 0.010 270);
    --gray-7:  oklch(46% 0.012 270);
    --gray-8:  oklch(36% 0.013 270);
    --gray-9:  oklch(26% 0.012 270);
    --gray-10: oklch(18% 0.010 270);
    --gray-11: oklch(12% 0.008 270);

    /* --- Brand accent --- one color, one job: action.
     * Steps 2/4/8 were referenced by component sheets but never
     * defined (second alias sweep, 2026-08-12) — interpolated
     * between their neighbours. */
    --accent-1:  oklch(96% 0.04 250);
    --accent-2:  oklch(90% 0.07 250);
    --accent-3:  oklch(82% 0.10 250);
    --accent-4:  oklch(73% 0.13 250);
    --accent-5:  oklch(64% 0.16 250);
    --accent-6:  oklch(56% 0.18 250);
    --accent-7:  oklch(48% 0.18 250);
    --accent-8:  oklch(41% 0.16 250);
    --accent-9:  oklch(34% 0.14 250);

    /* --- Semantic --- (warning-1/2/8 and danger-7: same story) */
    --success-3: oklch(86% 0.10 150);
    --success-6: oklch(58% 0.16 150);
    --success-9: oklch(35% 0.12 150);
    --warning-1: oklch(96% 0.05 80);
    --warning-2: oklch(92% 0.09 80);
    --warning-3: oklch(88% 0.12 80);
    --warning-6: oklch(70% 0.16 80);
    --warning-8: oklch(52% 0.14 80);
    --warning-9: oklch(45% 0.13 80);
    --danger-3:  oklch(86% 0.10 25);
    --danger-6:  oklch(58% 0.20 25);
    --danger-7:  oklch(52% 0.19 25);
    --danger-9:  oklch(40% 0.16 25);
    --info-3:    oklch(88% 0.06 220);
    --info-6:    oklch(60% 0.14 220);
    /* info-8/-9 (#374). The ramp stopped at -6, which is 3.63:1 on white —
     * fine as a fill behind white text, but below AA as *text*. custom.css's
     * .btn-outline-info therefore could not move onto the info ramp without
     * undoing the #247 contrast fix, and kept its own hard-coded #0769a3
     * (5.90:1) / #055880 (7.74:1). These two rungs land on the same numbers
     * through the token layer: -8 = 5.92:1, -9 = 10.23:1 on white. */
    --info-8:    oklch(48% 0.13 220);
    --info-9:    oklch(35% 0.12 220);

    /* --- Surfaces --- */
    --surface-0: var(--gray-0);   /* page background */
    --surface-1: white;            /* card / panel */
    --surface-2: var(--gray-1);    /* subtle inset */
    --surface-3: var(--gray-2);    /* hover */

    --text-1: var(--gray-11);     /* primary */
    --text-2: var(--gray-8);      /* secondary */
    --text-3: var(--gray-7);      /* tertiary / placeholder.
                                   * #233: was gray-6 (4.29:1 on white,
                                   * sub-AA). Bumped to gray-7 (~5.7:1)
                                   * so .nav-label / .sidebar .count /
                                   * .palette li .kind / .palette-foot
                                   * (which is wired to text-2 already)
                                   * all clear AA without each component
                                   * having to opt into a darker token. */
    --text-on-accent: white;

    --border-subtle: var(--gray-2);
    --border-default: var(--gray-3);
    --border-strong: var(--gray-5);

    /* --- Accent tints (#233) ---
     * Selection / "active row" tints in components that need a soft
     * accent wash behind text. Keeping these as semantic tokens means
     * dark + high-contrast can pick a wash that actually reads against
     * their darker / pure-white surfaces — which `--accent-1` (a near-
     * white pale-blue) cannot do on either. The contract is the same
     * for every theme: bg pairs with fg at AA contrast or better.
     *
     * Light theme: very pale accent wash + deep accent text.
     * Dark theme:  semi-transparent accent-6 wash + light accent text.
     * HC theme:    deep-accent inverse for unmistakable selection.
     */
    --accent-soft-bg: var(--accent-1);
    --accent-soft-fg: var(--accent-9);

    /* Link color (#233). Was raw `var(--accent-7)` baked into the `a {}`
     * rule. Promoting it to a token means dark mode can lighten the link
     * without having to bump the whole accent ramp (which would knock
     * over button + selection contrast). The companion --link-decoration
     * token toggles the always-on underline. We default to "underline"
     * across every theme — the WCAG 1.4.1 "link distinguishable from
     * surrounding text" check needs 3:1 colour separation OR a non-
     * colour cue, and our --link (accent-7) sits at only ~1.7:1 against
     * --text-1 (gray-11) on white. Underline is the cheapest non-colour
     * cue and reads as a link affordance everywhere. */
    --link: var(--accent-7);
    --link-hover: var(--accent-9);
    --link-decoration: underline;

    /* Semantic TEXT colours (#374). Distinct from both the raw --success-9 /
     * --danger-9 rungs and the --badge-*-fg pair, and it is worth being
     * explicit about why, because both of the obvious choices are wrong:
     *
     *   - the -9 rung is a fixed dark green/red. Right on light, but it does
     *     not lighten for dark, where --surface-1 is gray-10 and the text
     *     lands at ~1.8:1.
     *   - --badge-*-fg assumes the matching --badge-*-bg is painted behind
     *     it. High-contrast sets those fg tokens to #ffffff (HC badges are
     *     solid blocks of colour), so borrowing one for *unfilled* text puts
     *     white on HC's white surface: 1:1, invisible.
     *
     * These two are for coloured text sitting directly on a page surface —
     * an open/closed badge, an inline error. Each theme picks the rung that
     * reads against its own --surface-1: -9 on light, -3 on dark, and the
     * AA-darkened -6 on high-contrast. */
    --text-success: var(--success-9);
    --text-danger:  var(--danger-9);
    /* --text-info / --text-warning (#374). Same reason as the two above:
     * the -8 rungs are correct as *text on a light surface* and as the
     * *fill behind white text*, but those two jobs need opposite moves in
     * dark theme. --info-8 on dark's --surface-1 is 3.18:1, below AA. The
     * fills must stay on -8 (white text needs a dark fill), so the text
     * role gets its own token that swaps to the light end of the ramp on
     * dark. Light 5.92:1 / 5.58:1, dark 13.32:1 / 12.91:1, HC 7.94:1 /
     * 7.54:1. */
    --text-info:    var(--info-8);
    --text-warning: var(--warning-8);

    /* Focus ring (#374). custom.css has painted the keyboard focus outline
     * #ffbf47 (the GDS yellow) since before Aurora, in four places. It is
     * deliberately non-semantic — a thick offset outline that has to read
     * against the dark sidebar and the white content area alike — so it is
     * its own token rather than a rung on the warning ramp. The value is
     * unchanged in all three themes; promoting it to a token is what lets
     * the polish pass (#372) retune it in one place. */
    --focus-ring: #ffbf47;

    /* Scrim — overlay/backdrop tint behind modals. Stays "darker than
     * the page" in every theme so the layering always reads. */
    --scrim: color-mix(in oklch, var(--gray-11) 50%, transparent);

    /* Toast — inverse of body. Light → dark pill; dark → light pill;
     * HC → black-on-white for max attention. Component-agnostic so the
     * future "snackbar" / "command-bar tray" can reuse the same pair. */
    --toast-bg: var(--gray-11);
    --toast-fg: var(--gray-1);

    /* Semantic badge tints (#233). The original .badge.* rules
     * `color-mix(in oklch, var(--success-3) 50%, var(--surface-1))`
     * give the right pastel on light, but on dark surface-1 the same
     * mix produces a murky colour with poor text contrast. Promote
     * each tint pair to a token so dark + HC can override the BG and
     * FG independently of the parent surface. */
    --badge-success-bg: color-mix(in oklch, var(--success-3) 50%, var(--surface-1));
    --badge-success-fg: var(--success-9);
    --badge-warning-bg: color-mix(in oklch, var(--warning-3) 50%, var(--surface-1));
    --badge-warning-fg: var(--warning-9);
    --badge-danger-bg:  color-mix(in oklch, var(--danger-3)  50%, var(--surface-1));
    --badge-danger-fg:  var(--danger-9);
    --badge-info-bg:    color-mix(in oklch, var(--info-3)    50%, var(--surface-1));
    --badge-info-fg:    var(--gray-10);
    /* Case-note highlighter marks (#374). `.yh` / `.ph` in custom.css are
     * the marker-pen highlights that `pl.php` writes as <span class="yh">
     * from the [y] / [p] note tags. They are NOT badges: a badge is a pale
     * wash behind small label text, a highlight has to shout. Mapping them
     * onto --badge-*-bg would have faded #ffff44 to #ffe7bb and made a
     * highlighted note harder to spot than an unhighlighted one, so they
     * get their own token pair. Light keeps the historic marker colours
     * exactly; dark dims the fill so it does not glare; both keep black
     * text, because a marker is dark ink on bright colour.
     * Contrast on the paired fg: light 19.64:1 / 8.16:1, dark 11.34:1 /
     * 7.93:1, high-contrast 19.59:1 / 8.76:1. */
    --mark-yellow-bg:   #ffff44;
    --mark-yellow-fg:   #000000;
    --mark-pink-bg:     #ff7777;
    --mark-pink-fg:     #000000;

    /* Inline <code> foreground (#545). Bootstrap's own #d63384 measures
     * 4.26:1 on the page surface and 3.5:1 inside an .alert — both under
     * the 4.5:1 WCAG AA floor, and the alert case is where OCM actually
     * shows code (file paths and setting values on System Settings).
     * Keeping the magenta family so inline code still reads as code, but
     * dark enough to clear the floor on the worst background we render it
     * on: measured 7.19:1 on the .alert-secondary tint (#e2e3e5) and
     * 8.76:1 on the page surface. */
    --code-fg: oklch(42% 0.16 350);

    /* Destructive outline-button label (#545). Bootstrap's resting
     * .btn-outline-danger colour (#dc3545) measures 4.15:1 on the pop-up
     * timer's whitesmoke page and ~4.5:1 on the Aurora page surface, so on the
     * one button in the product that discards work it sits at or under the AA
     * floor. Two steps darker in the same hue: 5.58:1 on whitesmoke, 5.77:1 on
     * the Aurora page surface, 6.08:1 on a card. A token and not a literal
     * because dark mode has to go the other way -- see the dark block. */
    --danger-outline-fg: var(--danger-7);

    /* ── Legacy token aliases (#273 / #277) ──────────────────────────
     *
     * The Aurora sub-stylesheets (aurora-saved-views.css,
     * aurora-case-list-v2.css, aurora-calendar-v2.css, aurora-case-
     * three-pane.css, aurora-quick-time-entry.css, aurora-form-
     * components.css …) authored before the canonical token system
     * landed referenced names like `--ocm-color-surface`, `--aurora-
     * surface-alt`, `--ocm-bg-elevated`, `--aurora-text-muted`. None
     * of those tokens were defined in :root, so every consumer fell
     * through to the hard-coded light hex fallback (`#ffffff`,
     * `#f6f7f9`, etc.). The end result was that saved-views
     * toolbars, case-list rows, calendar grids, and quick-time-entry
     * sheets all rendered with a permanent white background even
     * when [data-theme="dark"] was active — illegible.
     *
     * Aliases here point each legacy name at the canonical token
     * that actually theme-swaps. Light/dark/high-contrast all flow
     * through automatically because --surface-* / --text-* /
     * --border-* are the source of truth.
     */
    --ocm-color-surface:        var(--surface-1);
    --ocm-color-surface-2:      var(--surface-2);
    --ocm-color-surface-hover:  var(--surface-3);
    --ocm-color-accent-soft:    var(--accent-soft-bg);
    --aurora-surface:           var(--surface-1);
    --aurora-surface-alt:       var(--surface-2);
    --aurora-surface-hover:     var(--surface-3);
    --aurora-border:            var(--border-subtle);
    --aurora-border-soft:       var(--border-subtle);
    --aurora-text:              var(--text-1);
    --aurora-text-muted:        var(--text-3);
    --aurora-accent:            var(--accent-7);
    --aurora-focus:             var(--accent-6);
    --aurora-danger:            var(--danger-9);
    --aurora-muted:             var(--text-3);
    --aurora-fg:                var(--text-1);
    --ocm-bg-default:           var(--surface-1);
    --ocm-bg-emphasis:          var(--surface-2);
    --ocm-bg-elevated:          var(--surface-1);
    --ocm-fg-default:           var(--text-1);
    --ocm-fg-muted:             var(--text-2);
    --ocm-border-default:       var(--border-default);
    --ocm-accent-default:       var(--accent-7);
    --ocm-accent-hover:         var(--accent-6);
    --ocm-success-default:      var(--success-9);
    --ocm-danger-default:       var(--danger-9);

    /* Second alias sweep (SCLO dark-mode report, 2026-08-12). The
     * #273/#277 sweep above missed a batch of legacy names — a full
     * consumed-vs-defined diff of cms/css found 39 names referenced
     * only through their hard-coded light fallbacks, so those spots
     * (the v2 calendar shell being the visible one) stayed white
     * under [data-theme="dark"]. Every alias resolves to a canonical
     * token so all three themes flow through. If you add a component
     * stylesheet that consumes a NEW token name, define it here —
     * a light-hex fallback is not a definition. */
    --aurora-bg:                var(--surface-0);
    --aurora-fg-muted:          var(--text-3);
    --aurora-font:              var(--font-sans);
    --aurora-hover:             var(--surface-3);
    --aurora-primary:           var(--accent-7);
    --aurora-accent-fg:         var(--text-on-accent);
    --aurora-accent-soft:       var(--accent-soft-bg);
    --aurora-border-light:      var(--border-subtle);
    --aurora-danger-soft:       var(--badge-danger-bg);
    --aurora-row-active:        var(--accent-soft-bg);
    --aurora-success:           var(--success-9);
    --aurora-surface-fg:        var(--text-1);
    --aurora-surface-muted:     var(--surface-2);
    --aurora-surface-soft:      var(--surface-2);
    --aurora-warn-bg:           var(--badge-warning-bg);
    --border-1:                 var(--border-subtle);
    --ocm-color-accent:         var(--accent-6);
    --ocm-color-border:         var(--border-default);
    --ocm-color-border-strong:  var(--border-strong);
    --ocm-color-danger:         var(--danger-9);
    --ocm-color-on-accent:      var(--text-on-accent);
    --ocm-color-success:        var(--success-9);
    --ocm-color-surface-3:      var(--surface-3);
    --ocm-color-surface-alt:    var(--surface-2);
    --ocm-color-text:           var(--text-1);
    --ocm-color-text-muted:     var(--text-3);

    /* Third alias sweep (#374). A consumed-vs-defined diff of custom.css
     * turned up five more colour names that were only ever reached through
     * their light-hex fallback — all of them on the intake-flow AI triage
     * panel (#513), which therefore stayed a white card with grey text
     * under [data-theme="dark"]. Same defect and same fix as the two
     * sweeps above. Note that --ocm-cbh / --ocm-cbw / --ocm-flh / --ocm-flw
     * are deliberately NOT listed: those four fall back to `auto` and are
     * set per-element from an inline style, so a :root definition would
     * override the default they are supposed to have. */
    --ocm-border:               var(--border-default);
    /* --text-danger, not --danger-9 and not --badge-danger-fg:
     * --ocm-danger is only ever unfilled error text on the triage panel.
     * The -9 rung does not lighten on dark; --badge-danger-fg is #ffffff in
     * high-contrast, invisible on that theme's white surface. */
    --ocm-danger:               var(--text-danger);
    --ocm-primary:              var(--accent-7);
    --ocm-surface:              var(--surface-1);
    --ocm-text-muted:           var(--text-3);

    /* Scrollbar. custom.css has painted the page scrollbar since before
     * Aurora and still names its own --thumbBG/--scrollbarBG, which held
     * the pre-Aurora #7386D5 purple and a fixed light #CFD8DC track. Both
     * now resolve through these two aliases so the scrollbar follows the
     * theme like everything else. Track stays on --surface-2 in all three
     * themes; the thumb re-points to a lighter accent rung under dark
     * below, the same way --link does. */
    --scrollbar-track:          var(--surface-2);
    --scrollbar-thumb:          var(--accent-7);

    /* Static aliases from the same sweep — sizing/radius/spacing
     * names some component sheets consume. Mapped to the canonical
     * scale where values match; --ocm-font-size-lg keeps its
     * fallback value (1.25rem) rather than --text-lg (1.125rem) so
     * nothing visibly changes. */
    --ocm-font-size-sm:         var(--text-base);
    --ocm-font-size-lg:         1.25rem;
    --ocm-radius:               var(--radius-2);
    --ocm-radius-sm:            var(--radius-1);
    --ocm-radius-md:            var(--radius-2);
    --ocm-radius-lg:            var(--radius-3);
    --ocm-space-1:              var(--space-1);
    --ocm-space-2:              var(--space-2);
    --ocm-space-3:              var(--space-3);
    --ocm-space-4:              var(--space-4);
    --ocm-space-5:              var(--space-5);

    /* Avatar — initials chip in the topbar. Was --accent-5 (3.35:1
     * with white) which fell below AA for the 12-px initials. Bumped
     * to --accent-6 (~4.6:1 on white) so all themes start AA-clean
     * before their own dark/HC overrides kick in. */
    --avatar-bg: var(--accent-6);
    --avatar-fg: white;

    /* --- Typography --- */
    --font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;

    /* Type scale. Six steps: 12 / 14 / 18 / 22 / 28 / 36 px at the default
     * 16px root.
     *
     * #548 removed two steps. The scale used to run 12 / 13 / 14 / 16 / 18 /
     * 22 / 28 / 36, and 12-13-14 is not a scale — a 1px difference does not
     * read as hierarchy, it only adds another distinct value to every screen
     * that mixes secondary text with body text. The census on the four
     * screens in that story counted 13px on 42-52 elements and 14px on
     * 110-494, side by side, doing the same job.
     *
     *   --text-sm  was 0.8125rem (13px). Kept as a NAME, not a value: 46
     *              rules use it to mean "secondary text", and that intent is
     *              worth keeping in the source even though the size now
     *              matches body copy. Weight and colour carry the
     *              distinction instead.
     *   --text-md  was 1rem (16px) and is GONE. Its three call sites (the
     *              command-palette input, .ocm-doc-preview-name and the form
     *              demo card heading) all wanted "a step above body", which
     *              is --text-lg. Bootstrap's own 1rem defaults — .form-control
     *              and .h6 — are retargeted in the base block below.
     *
     * rem throughout, never px: custom.css scales the root for the per-user
     * text-size preference (html.ocm-font-small / -large / -xl), and px
     * would freeze that accessibility setting. */
    --text-xs:   0.75rem;
    --text-base: 0.875rem;
    --text-sm:   var(--text-base);
    --text-lg:   1.125rem;
    --text-xl:   1.375rem;
    --text-2xl:  1.75rem;
    --text-3xl:  2.25rem;

    --leading-tight: 1.2;
    --leading-snug:  1.4;
    --leading-base:  1.5;

    /* --- Spacing (4-px base) --- */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;
    --space-8: 4rem;

    /* --- Radius --- */
    --radius-1: 4px;
    --radius-2: 8px;
    --radius-3: 12px;
    --radius-full: 9999px;

    /* #548 — Bootstrap's radius scale, retargeted onto Aurora's.
     *
     * Bootstrap ships its own four-step scale (6 / 4 / 8 / 16 / 32 px and a
     * 50rem pill) and every component reads it through these variables. It
     * shares exactly one value with Aurora's, so any screen that mixes a
     * Bootstrap control with an Aurora one showed both scales at once: the
     * case screen rendered 6px inputs beside 8px nav items, 6px 6px 0 0 tabs,
     * a 5px 5px 0 0 card header inside a 12px card, and an 800px pill badge
     * next to a 9999px one. Fourteen distinct radii, from two scales that
     * disagree by 1-2px everywhere.
     *
     * Pointing the Bootstrap variables at the Aurora tokens fixes every
     * Bootstrap component at once and needs no per-component overrides.
     * Aurora declares :root after the Bootstrap link, so these win at equal
     * specificity.
     *
     * The card pair is set on `.card` further down this file rather than
     * here: Bootstrap declares --bs-card-*-border-radius as local custom
     * properties inside its own `.card` rule, so a :root value never
     * reaches them. */
    --bs-border-radius:            var(--radius-2);
    --bs-border-radius-sm:         var(--radius-1);
    --bs-border-radius-lg:         var(--radius-2);
    --bs-border-radius-xl:         var(--radius-3);
    --bs-border-radius-xxl:        var(--radius-3);
    --bs-border-radius-2xl:        var(--radius-3);
    --bs-border-radius-pill:       var(--radius-full);

    /* --- Elevation: borders first, shadows for floating only --- */
    --shadow-1: 0 1px 0 rgb(0 0 0 / 0.04);
    --shadow-2: 0 4px 12px rgb(0 0 0 / 0.06), 0 1px 2px rgb(0 0 0 / 0.04);
    --shadow-3: 0 16px 48px rgb(0 0 0 / 0.12), 0 2px 8px rgb(0 0 0 / 0.06);

    /* #548: the fourth elevation. Not a neutral drop shadow — a tinted
     * halo used only on the two brand-presence marks in the chrome, the
     * sidebar brand square and the topbar avatar. It existed already, but
     * written out twice in aurora-polish.css with slightly different blur
     * radii (14px and 6px), so it counted as two distinct shadows on every
     * screen. One token, one value, both call sites. */
    --shadow-accent:
        0 0 0 1px color-mix(in oklch, var(--accent-7) 25%, transparent),
        0 2px 10px color-mix(in oklch, var(--accent-7) 30%, transparent);

    /* --- Motion --- */
    --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
    --dur-fast: 120ms;
    --dur-base: 180ms;
    --dur-slow: 280ms;

    /* --- Layout --- */
    --sidebar-w: 240px;
    --sidebar-w-collapsed: 56px;
    --topbar-h: 48px;
    --right-rail-w: 320px;
}

[data-theme="dark"] {
    color-scheme: dark;

    /* #233 dark tuning — surface ramp re-spaced so consecutive
     * surfaces are visually distinct without leaving the dark range:
     *  surface-0 (page)  → gray-11 (12% L) — unchanged
     *  surface-1 (panel) → gray-10 (18% L) — unchanged
     *  surface-2 (inset) → was gray-9 (26% L) = 8 L step on top of a
     *                                         6 L step. Now at gray-8
     *                                         (36%) for an 18 L step,
     *                                         so cards on insets read
     *                                         as actual cards.
     *  surface-3 (hover) → gray-7 (46% L) — was gray-8; bumped one step
     *                                         to keep the hover lift
     *                                         readable against the new
     *                                         surface-2.
     */
    --surface-0: var(--gray-11);
    --surface-1: var(--gray-10);
    --surface-2: var(--gray-8);
    --surface-3: var(--gray-7);

    --text-1: var(--gray-1);
    --text-2: var(--gray-3);     /* was gray-4 — pushed one step lighter
                                  * (89% vs 82% L) so secondary text
                                  * clears AA against surface-1. */
    --text-3: var(--gray-4);     /* was gray-6 (58% L → 3.2:1 on surface-1
                                  * = below AA). gray-5 (70%) only got us
                                  * to 3.7:1 against the new surface-2
                                  * (gray-8); gray-4 (82%) lands at ~4.6:1
                                  * which clears AA for the topbar
                                  * search-trigger / nav-label / palette
                                  * footer that all use --text-3 against
                                  * --surface-2. */

    --border-subtle:  var(--gray-8);   /* was gray-9 — bumped so panel
                                        * edges actually appear against
                                        * the new surface-2 (which is
                                        * also gray-8). */
    --border-default: var(--gray-7);   /* was gray-8 — same reason */
    --border-strong:  var(--gray-5);   /* was gray-6 — focus ring needs
                                        * to read against everything */

    --shadow-1: 0 1px 0 rgb(0 0 0 / 0.3);
    --shadow-2: 0 4px 12px rgb(0 0 0 / 0.35), 0 1px 2px rgb(0 0 0 / 0.2);
    --shadow-3: 0 16px 48px rgb(0 0 0 / 0.5), 0 2px 8px rgb(0 0 0 / 0.3);
    /* #548: stronger mix in dark — a 30% accent glow disappears against a
     * dark surface. */
    --shadow-accent:
        0 0 0 1px color-mix(in oklch, var(--accent-6) 40%, transparent),
        0 2px 10px color-mix(in oklch, var(--accent-6) 45%, transparent);

    /* Selection tints: --accent-1 (oklch 96% L) is a near-white wash
     * that fades into surface-1 in dark mode. Use a transparent
     * accent-6 mix instead so the wash is visible but not garish, and
     * pair with accent-3 text for AA. Replaces the three
     * `[data-theme="dark"] .foo.active` per-component overrides we used
     * to ship — the architectural rule is "every variant comes from
     * a token swap", and these are the tokens. */
    --accent-soft-bg: color-mix(in oklch, var(--accent-6) 22%, transparent);
    --accent-soft-fg: var(--accent-3);

    /* Backdrop in dark mode: keep the dark scrim, but lean into pure
     * black for a stronger blur edge. */
    --scrim: color-mix(in oklch, #000 60%, transparent);

    /* Toast inverts vs the page — dark theme → light pill. */
    --toast-bg: var(--gray-1);
    --toast-fg: var(--gray-11);

    /* #545: the light-mode --code-fg is a dark magenta, invisible on a dark
     * surface. Lift it to the same hue at high lightness — measured 10.3:1
     * on surface-1. */
    --code-fg: oklch(82% 0.10 350);

    /* Darkening the danger label the way light mode does takes it from 4.16:1
     * to 3.10:1 on this surface -- the wrong direction. Lift it instead:
     * danger-3 measures 11.43:1 on surface-1 and 6.60:1 on the surface-2
     * hover fill. */
    --danger-outline-fg: var(--danger-3);

    /* Semantic text (#374) — the -3 rungs are the light end of each ramp,
     * which is what reads against dark's gray-10 --surface-1. */
    --text-success: var(--success-3);
    --text-danger:  var(--danger-3);
    --text-info:    var(--info-3);
    --text-warning: var(--warning-3);

    /* Link colour — accent-7 in dark is too dim against surface-0
     * (~3.18:1 fail). Lift to accent-3 (a light pastel blue) for
     * AAA against the page. Hover then drops to accent-1 for the
     * "I'm acting on this" cue. Underline always-on so the lifted
     * link colour (which sits close to --text-1 at high L) still
     * passes WCAG 1.4.1 link-distinguishable-from-surrounding-text. */
    --link: var(--accent-3);
    --link-hover: var(--accent-1);
    --link-decoration: underline;

    /* Badge tints — invert the convention in dark: dark wash + light
     * text. The pastel `--success-3 50%` mix above produces a near-
     * black murky colour on surface-1 in dark; replace with the same
     * recipe but anchored on the dark accent + light text instead. */
    --badge-success-bg: color-mix(in oklch, var(--success-6) 30%, var(--surface-1));
    --badge-success-fg: var(--success-3);
    --badge-warning-bg: color-mix(in oklch, var(--warning-6) 30%, var(--surface-1));
    --badge-warning-fg: var(--warning-3);
    --badge-danger-bg:  color-mix(in oklch, var(--danger-6)  30%, var(--surface-1));
    --badge-danger-fg:  var(--danger-3);
    --badge-info-bg:    color-mix(in oklch, var(--info-6)    30%, var(--surface-1));
    --badge-info-fg:    var(--info-3);
    /* Highlighter marks (#374) — dimmed fills, still dark ink. */
    --mark-yellow-bg:   oklch(80% 0.17 100);
    --mark-pink-bg:     oklch(72% 0.15 25);

    /* Avatar — accent-7 (oklch 48%) brings the white initials to ~6:1. */
    --avatar-bg: var(--accent-7);
    --avatar-fg: white;

    /* Scrollbar thumb. --accent-7 is 48% L and dark's --surface-2 track is
     * gray-8 at 36% L, so the light-theme thumb would sit ~12 L off its own
     * track and read as a smudge. Re-point to the rung --link uses here.
     * The track alias needs no override: it rides --surface-2. */
    --scrollbar-thumb: var(--accent-3);
}

/* --- High-contrast theme (#206) ----------------------------------------
 * Pure-white surfaces with near-black text + borders so every component
 * still passes WCAG AAA on the primary text pair (21:1) and AA on the
 * accent / semantic ramps. Same token contract as light + dark — no
 * component CSS changes required. The toggle in cms/js/aurora.js cycles
 * light → dark → high-contrast, and `@media (prefers-contrast: more)`
 * below opts users into this theme automatically when their OS asks.
 *
 * Pure black/white are kept as hex (#000 / #fff) — they are truly named
 * colors that don't benefit from OKLCH's perceptual uniformity. Accent +
 * semantic ramps stay in OKLCH so they sit in the same color space as
 * the rest of the file.
 */
[data-theme="high-contrast"] {
    color-scheme: light;

    /* Surfaces: pure white. Subtle inset / hover stay white but rely on
     * the heavier borders below for separation. */
    --surface-0: #ffffff;
    --surface-1: #ffffff;
    --surface-2: #ffffff;
    --surface-3: #f0f0f0;

    /* Text: pure black primary, near-black secondary + tertiary. */
    --text-1: #000000;
    --text-2: #1a1a1a;
    --text-3: #333333;
    --text-on-accent: #ffffff;

    /* Borders: black / dark-gray ramp so every panel edge is visible. */
    --border-subtle:  #666666;
    --border-default: #333333;
    --border-strong:  #000000;

    /* Accent: deep blue, ~12:1 against white (AA Large + AAA Normal). */
    --accent-1: oklch(95% 0.04 255);
    --accent-3: oklch(75% 0.12 255);
    --accent-5: oklch(45% 0.18 255);
    --accent-6: oklch(34% 0.18 255);    /* ≈ #003a99 */
    --accent-7: oklch(28% 0.18 255);
    --accent-9: oklch(20% 0.14 255);

    /* Semantic ramps: darken every -6 to AA-passing on white (≥ 4.5:1). */
    --success-6: oklch(40% 0.16 150);
    --success-9: oklch(28% 0.12 150);
    --warning-6: oklch(45% 0.16 80);
    --warning-9: oklch(35% 0.13 80);
    --danger-6:  oklch(40% 0.20 25);
    --danger-9:  oklch(30% 0.16 25);
    --info-6:    oklch(40% 0.16 220);

    /* Shadows down — high-contrast users rely on borders, not shadows. */
    --shadow-1: 0 0 0 1px #000000;
    --shadow-2: 0 0 0 1px #000000;
    --shadow-3: 0 0 0 2px #000000;
    /* #548: no accent glow in high contrast — a tinted halo is exactly the
     * cue these users cannot see. A solid 1px ring instead. */
    --shadow-accent: 0 0 0 1px #000000;

    /* #233 HC tuning — selection tint. The default `--accent-1`
     * (`oklch(95% 0.04 255)` ≈ #e6ecff) on white is invisible: a
     * sighted HC user sees no "this row is selected" cue. Flip the
     * convention: dark accent-7 background with white text is an
     * unambiguous selection state at any contrast level. AA-passing
     * pair on every selected row / nav-item / palette match. */
    --accent-soft-bg: var(--accent-7);
    --accent-soft-fg: #ffffff;

    /* Scrim must be opaque-enough to read against the pure-white
     * surfaces under it. 70% black instead of the light-mode 50%. */
    --scrim: color-mix(in oklch, #000 70%, transparent);

    /* Semantic text (#374) — the -6 rungs above are already darkened to
     * >= 4.5:1 on HC's pure-white surface, so they are the right rung for
     * unfilled coloured text here. NOT --badge-*-fg, which is #ffffff in
     * this theme and would vanish. */
    --text-success: var(--success-6);
    --text-danger:  var(--danger-6);
    --text-info:    var(--info-6);
    --text-warning: var(--warning-6);

    /* Toast: black on white with a heavy border, same convention as
     * every other surface in HC. The component CSS already ships the
     * `--shadow-3` ring so the toast still floats visually. */
    --toast-bg: #ffffff;
    --toast-fg: #000000;

    /* #545: HC keeps the code hue but pushes it to 13.6:1 on white. */
    --code-fg: oklch(30% 0.16 350);
    /* 13.22:1 on white. */
    --danger-outline-fg: var(--danger-9);

    /* Link colour. The light-mode default of accent-7 sits at ~12:1
     * against pure white in HC (we already darkened the accent ramp
     * for HC above), so just keep it pointing at the same token —
     * but make hover go to pure-black for that "I-am-clicking-this"
     * cue HC users rely on. Underline always-on per HC convention
     * (link colour vs --text-1 = #000 only gets us ~2.5:1 distinct,
     * below WCAG 1.4.1's 3:1 colour-only floor). */
    --link: var(--accent-7);
    --link-hover: #000000;
    --link-decoration: underline;

    /* Badge tints. The pastel `--*-3 50%` mix collapses to almost-
     * white in HC (surface-1 is pure white). Use the AA-darkened
     * `--*-6` ramp directly on the white surface with white text —
     * solid blocks of colour, not a tint. */
    --badge-success-bg: var(--success-6);
    --badge-success-fg: #ffffff;
    --badge-warning-bg: var(--warning-6);
    --badge-warning-fg: #ffffff;
    --badge-danger-bg:  var(--danger-6);
    --badge-danger-fg:  #ffffff;
    --badge-info-bg:    var(--info-6);
    --badge-info-fg:    #ffffff;
    /* Highlighter marks (#374) — maximum separation from body text. */
    --mark-yellow-bg:   oklch(97% 0.20 110);
    --mark-pink-bg:     oklch(78% 0.18 25);

    /* Avatar — HC accent-7 (oklch 28%) gives white initials AAA. */
    --avatar-bg: var(--accent-7);
    --avatar-fg: #ffffff;
}

/* OS-level high-contrast preference → opt into the same token swap.
 * Scoped to `:root:not([data-theme])` so an explicit user selection in
 * the toggle (which writes data-theme=...) always wins over the OS hint. */
@media (prefers-contrast: more) {
    :root:not([data-theme]),
    [data-theme="auto"] {
        color-scheme: light;

        --surface-0: #ffffff;
        --surface-1: #ffffff;
        --surface-2: #ffffff;
        --surface-3: #f0f0f0;

        --text-1: #000000;
        --text-2: #1a1a1a;
        --text-3: #333333;
        --text-on-accent: #ffffff;

        --border-subtle:  #666666;
        --border-default: #333333;
        --border-strong:  #000000;

        --accent-1: oklch(95% 0.04 255);
        --accent-3: oklch(75% 0.12 255);
        --accent-5: oklch(45% 0.18 255);
        --accent-6: oklch(34% 0.18 255);
        --accent-7: oklch(28% 0.18 255);
        --accent-9: oklch(20% 0.14 255);

        --success-6: oklch(40% 0.16 150);
        --success-9: oklch(28% 0.12 150);
        --warning-6: oklch(45% 0.16 80);
        --warning-9: oklch(35% 0.13 80);
        --danger-6:  oklch(40% 0.20 25);
        --danger-9:  oklch(30% 0.16 25);
        --info-6:    oklch(40% 0.16 220);

        --shadow-1: 0 0 0 1px #000000;
        --shadow-2: 0 0 0 1px #000000;
        --shadow-3: 0 0 0 2px #000000;
        --shadow-accent: 0 0 0 1px #000000;   /* #548 — see above */

        /* #233 — match the explicit `[data-theme="high-contrast"]`
         * block above so OS-level prefers-contrast users get the
         * same selection / scrim / toast pairing without needing to
         * pick a theme manually. */
        --accent-soft-bg: var(--accent-7);
        --accent-soft-fg: #ffffff;
        --scrim: color-mix(in oklch, #000 70%, transparent);
        --toast-bg: #ffffff;
        --toast-fg: #000000;
        --code-fg: oklch(30% 0.16 350);
        --danger-outline-fg: var(--danger-9);
        --link: var(--accent-7);
        --link-hover: #000000;
        --link-decoration: underline;
        --badge-success-bg: var(--success-6);
        --badge-success-fg: #ffffff;
        --badge-warning-bg: var(--warning-6);
        --badge-warning-fg: #ffffff;
        --badge-danger-bg:  var(--danger-6);
        --badge-danger-fg:  #ffffff;
        --badge-info-bg:    var(--info-6);
        --badge-info-fg:    #ffffff;
        /* Highlighter marks (#374) — match the explicit HC block above. */
        --mark-yellow-bg:   oklch(97% 0.20 110);
        --mark-pink-bg:     oklch(78% 0.18 25);
        --avatar-bg: var(--accent-7);
        --avatar-fg: #ffffff;
    }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

/* --- Reset / base --- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
    font-family: var(--font-sans);
    font-feature-settings: "cv02", "cv03", "cv04", "cv11";
    font-size: var(--text-base);
    line-height: var(--leading-base);
    color: var(--text-1);
    background: var(--surface-0);
    -webkit-font-smoothing: antialiased;
}
@supports (font-variation-settings: normal) {
    body { font-family: "Inter var", var(--font-sans); }
}

/* #233: links carry a token-controlled --link-decoration so dark + HC
 * can force the underline on (since their lifted link colours can't
 * stay 3:1 distinct from the lifted body text, WCAG 1.4.1 requires a
 * non-colour cue). Light defaults to underline too — the `--link`
 * (accent-7) sits at only ~1.7:1 against `--text-1` so it can't
 * stand on colour alone either. The tinted underline-color (60%
 * mix toward transparent) keeps the rule visually weighted toward
 * the link text rather than the rule itself. */
a {
    color: var(--link);
    text-decoration: var(--link-decoration);
    text-underline-offset: 3px;
    text-decoration-color: color-mix(in oklch, var(--link) 60%, transparent);
}
a:hover { color: var(--link-hover); text-decoration: underline; }

button { font: inherit; cursor: pointer; }
input, select, textarea { font: inherit; color: inherit; }

/* ─────────────────────────────────────────────────────────────────────
 * #548 — every element that carries a size names a step on the scale.
 *
 * The four screens the story measured rendered 11 to 15 distinct font
 * sizes each. Most of those sizes were not authored anywhere: they were
 * relative declarations multiplying through nested elements. On
 * `admin-settings`, six of fifteen came out of three rules:
 *
 *   15.4    = 14 x 1.1      custom.css `p { font-size: 1.1em }`  (removed)
 *   12.25   = 14 x 0.875    Bootstrap `small`, `.small`, `.form-text`
 *   13.475  = 15.4 x 0.875  `small` inside a `p`
 *   12.6    = 14 x 0.9      `code, kbd, .mono` (this rule, was 0.9em)
 *   11.025  = 12.25 x 0.9   `code` inside `small`
 *   12.1275 = 13.475 x 0.9  `code` inside `small` inside `p`
 *   10.5    = 14 x 0.75     Bootstrap `sub`, `sup`   (on cal-adv)
 *
 * An absolute token cannot compound however deeply it nests, which is
 * why these are tokens and not smaller `em` values. The rules live here
 * rather than in custom.css because the --text-* tokens are defined in
 * this file; custom.css also loads with aurora_chrome off, where they
 * would not resolve.
 *
 * Equal specificity to Bootstrap's own bare-element rules is enough:
 * pl_aurora_asset_tags() always emits aurora.css after the Bootstrap
 * link, and aurora.css can never load without it.
 * ───────────────────────────────────────────────────────────────────── */
small, .small, .form-text { font-size: var(--text-xs); }
sub, sup                  { font-size: var(--text-xs); }

/* Headings were straight Bootstrap: 40 / 32 / 28 / 24 / 20 / 16 px, a
 * scale of its own that shares only one value with Aurora's. h1 at 40px
 * also outranked .aurora-page-header__title (28px), the element that is
 * actually the page title on every Aurora screen. h4-h6 land on body
 * size and separate themselves by weight, which is what the section
 * headings in this file already do. */
h1, .h1 { font-size: var(--text-2xl); }
h2, .h2 { font-size: var(--text-xl); }
h3, .h3 { font-size: var(--text-lg); }
h4, .h4,
h5, .h5,
h6, .h6 { font-size: var(--text-base); }

/* Bootstrap sizes .form-control and .form-select at 1rem (16px), which
 * beat this file's `input, select, textarea { font: inherit }` on
 * specificity — so every dropdown on a form rendered 2px larger than
 * its own label. 87 of the 91 elements at 16px on the case screen were
 * `option` elements inheriting it. */
.form-control, .form-select { font-size: var(--text-base); }

/* Bootstrap rounds a checkbox with `border-radius: .25em`, which is em-based,
 * so it tracked the font size instead of the radius scale — 3.5px at 14px,
 * on 120 checkboxes on the System Settings screen. The radio's 50% is a
 * circle only because the control is square; --radius-full says that
 * outright and matches every other pill in the product. */
.form-check-input[type="checkbox"] { border-radius: var(--radius-1); }
.form-check-input[type="radio"]    { border-radius: var(--radius-full); }

code, kbd, .mono { font-family: var(--font-mono); font-size: var(--text-xs); }
/* #545: overrides Bootstrap's --bs-code-color (#d63384), which fails AA
 * on both surfaces we render inline code on. kbd keeps its own treatment
 * below. */
code { color: var(--code-fg); }

/* Neutralise Bootstrap's inverted <kbd>.
 *
 * bootstrap.min.css ships `kbd { color: var(--bs-body-bg);
 * background-color: var(--bs-body-color); }` — a white-on-near-black chip.
 * Nothing in this app ever sets data-bs-theme, so --bs-body-bg stays #fff
 * in light, dark AND high-contrast. Any component rule that restyles a
 * kbd's *background* to a light surface but forgets `color` therefore
 * inherits white text and renders white-on-white. Measured live:
 * .aurora-search-overlay kbd was 1.19:1 (light) / 1.14:1 (high-contrast),
 * .ocm-quick-time-entry__hint kbd was 1.09:1 / 1.00:1. AA needs 4.5:1.
 *
 * That is the same bug for the fourth time — #256 already patched
 * .palette-foot kbd and .topbar .search-trigger kbd one at a time — so fix
 * the class of bug here rather than chasing each new component. Component
 * rules that set their own colour still win on specificity.
 *
 * Both properties are declared deliberately: dropping only `color` would
 * leave Bootstrap's dark background under dark text, which is the same
 * failure upside down. Equal specificity to Bootstrap's bare `kbd` is
 * enough because pl_aurora_asset_tags() emits aurora.css after the
 * Bootstrap CDN link, and aurora.css can never load without it. */
kbd {
    color: var(--text-1);
    background: var(--surface-2);
}

::selection { background: var(--accent-3); color: var(--gray-11); }
::-moz-selection { background: var(--accent-3); color: var(--gray-11); }
/* Force the high-contrast selection text colour onto links and their
   children. Without an explicit a::selection, a selected link keeps its
   own colour — e.g. Documents-tab file-name links are --accent-6 (blue),
   which lands blue text on the light-blue (--accent-3) selection
   background = unreadable when a user highlights a document name. */
a::selection, a *::selection { color: var(--gray-11); }
a::-moz-selection, a *::-moz-selection { color: var(--gray-11); }

:focus-visible {
    outline: 2px solid var(--accent-6);
    outline-offset: 2px;
    border-radius: var(--radius-1);
}

/* --- App shell --- */
.app {
    display: grid;
    grid-template-columns: var(--sidebar-w) 1fr;
    grid-template-rows: var(--topbar-h) 1fr;
    grid-template-areas:
        "sidebar topbar"
        "sidebar main";
    height: 100vh;
}

.topbar {
    grid-area: topbar;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 0 var(--space-5);
    background: var(--surface-1);
    border-bottom: 1px solid var(--border-subtle);
}
.topbar .search-trigger {
    flex: 1;
    max-width: 480px;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 6px var(--space-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    background: var(--surface-2);
    color: var(--text-3);
    font-size: var(--text-sm);
    transition: background var(--dur-fast) var(--ease-out);
}
.topbar .search-trigger:hover { background: var(--surface-3); }
.topbar .search-trigger kbd {
    margin-left: auto;
    padding: 2px 6px;
    background: var(--surface-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-1);
    font-size: var(--text-xs);   /* #548: was 11px, off the scale */
    color: var(--text-2);
}
/* a11y (#210) — the topbar's real search form. It ships
 * .visually-hidden-focusable, so Bootstrap clips it to 1px until it or a
 * child takes focus; everything below only describes how it looks once
 * revealed. Nothing here fights the hidden state (Bootstrap sets that
 * with !important), so the pill above stays the only visible search
 * affordance for mouse users. */
.topbar .aurora-topbar-search {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 1;
    max-width: 480px;
}
.topbar .aurora-topbar-search-label {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--text-2);
    white-space: nowrap;
}
.topbar .aurora-topbar-search-input {
    flex: 1;
    min-width: 0;
    font-size: var(--text-sm);
}
.topbar .aurora-topbar-search-submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.topbar .spacer { flex: 1; }
.topbar .icon-btn {
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-2);
    color: var(--text-2);
    transition: background var(--dur-fast) var(--ease-out);
}
.topbar .icon-btn:hover { background: var(--surface-2); color: var(--text-1); }
.topbar .avatar {
    width: 28px; height: 28px;
    border-radius: var(--radius-full);
    /* #233: was raw `--accent-5` + `white`, which gave 3.35:1 in dark
     * (well below AA for the small initials). Pull from the per-theme
     * `--avatar-bg` / `--avatar-fg` pair so each theme picks an
     * AA-passing combo for white initials text. */
    background: var(--avatar-bg);
    color: var(--avatar-fg);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600;
    font-size: var(--text-xs);
}

.sidebar {
    grid-area: sidebar;
    background: var(--surface-1);
    border-right: 1px solid var(--border-subtle);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
}
.sidebar .brand {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    height: var(--topbar-h);
    border-bottom: 1px solid var(--border-subtle);
    font-weight: 600;
    letter-spacing: -0.01em;
}
.sidebar .brand-mark {
    width: 28px; height: 28px;
    border-radius: var(--radius-2);
    background: linear-gradient(135deg, var(--accent-6), var(--accent-9));
    color: var(--text-on-accent, #fff);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}
.sidebar .brand-mark svg {
    width: 16px;
    height: 16px;
}
/* PNG/SVG-image brand mark: drop the gradient backplate (the image
 * supplies its own background) and let the image fill the 28×28
 * frame rounded by the parent's border-radius. object-fit:cover
 * crops a non-square asset to a square; switch to "contain" if the
 * org's logo needs whitespace around it. */
/* When an image logo replaces the SVG mark, drop the gradient backplate
 * (the logo asset supplies its own background) and let the image sit
 * inside the rounded 28×28 chip. object-fit: contain preserves the
 * whole mark; if the asset is full-bleed and you'd rather crop, switch
 * to cover. */
.sidebar .brand-mark.brand-mark-img {
    background: transparent;
    overflow: hidden;
}
.sidebar .brand-mark.brand-mark-img img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}
/* Org logo (#365): wordmark-shaped org logos need more than the 28×28
 * chip — let the mark grow to the sidebar's usable width at a fixed
 * height, and drop the chip rounding (the asset brings its own shape).
 * In the collapsed rail the logo shrinks back to the chip footprint. */
.sidebar .brand-mark.brand-mark-org {
    width: auto;
    max-width: 160px;
    height: 34px;
    border-radius: 0;
}
.sidebar .brand-mark.brand-mark-org img {
    width: auto;
    max-width: 100%;
}
.aurora-app.collapsed .aurora-sidebar .brand-mark.brand-mark-org {
    max-width: 28px;
    height: 28px;
}
/* #261: the entire brand block is now a clickable link to /cms/. The
 * anchor needs to inherit the brand row's typography and lose the
 * default link underline so it reads as a logo, not a hyperlink.
 * Hover lifts the surface for affordance. */
.sidebar .brand-link {
    color: inherit;
    text-decoration: none;
    display: block;
    border-radius: var(--radius-2);
    transition: background var(--dur-fast) var(--ease-out);
}
.sidebar .brand-link:hover,
.sidebar .brand-link:focus {
    text-decoration: none;
    background: var(--surface-2);
}
.sidebar .brand-link:focus-visible {
    outline: 2px solid var(--accent-6);
    outline-offset: 2px;
}
.sidebar .nav-section {
    padding: var(--space-3) var(--space-2);
    border-bottom: 1px solid var(--border-subtle);
}
.sidebar .nav-section:last-of-type { border-bottom: 0; }
.sidebar .nav-label {
    padding: 0 var(--space-2) var(--space-2);
    font-size: var(--text-xs);   /* #548: was 11px, off the scale */
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-3);
}
.sidebar .nav-item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 6px var(--space-3);
    margin: 1px 0;
    color: var(--text-2);
    font-size: var(--text-sm);
    border-radius: var(--radius-2);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out);
}
.sidebar .nav-item:hover { background: var(--surface-2); color: var(--text-1); }
.sidebar .nav-item.active {
    /* #233: was a hard-coded var(--accent-1) + var(--accent-9) pair
     * with a separate [data-theme="dark"] override. Now both come
     * from --accent-soft-bg / --accent-soft-fg so dark + HC swap
     * the wash via tokens instead of per-component CSS. */
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}
.sidebar .nav-item .count {
    margin-left: auto;
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
    font-size: var(--text-xs);
}
/* #233: when the parent nav-item is .active the soft-bg is a colored
 * wash (deep accent in HC; mid-tone elsewhere). Inherit so the count
 * pill stays legible against that wash instead of staying gray. */
.sidebar .nav-item.active .count { color: inherit; }
.sidebar .icon { width: 16px; height: 16px; flex: 0 0 auto; }

.main {
    grid-area: main;
    overflow-y: auto;
}

.page {
    max-width: 1280px;
    padding: var(--space-5) var(--space-6);
    margin: 0 auto;
}
.page-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}
.page-title {
    font-size: var(--text-2xl);
    font-weight: 600;
    line-height: var(--leading-tight);
    letter-spacing: -0.015em;
    margin: 0;
}
.page-subtitle {
    color: var(--text-2);
    font-size: var(--text-sm);
    margin: var(--space-1) 0 0;
}

/* --- Buttons --- */
.btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 6px var(--space-3);
    border-radius: var(--radius-2);
    border: 1px solid var(--border-default);
    background: var(--surface-1);
    color: var(--text-1);
    font-size: var(--text-sm);
    font-weight: 500;
    transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.btn:hover { background: var(--surface-2); border-color: var(--border-strong); }
.btn:active { background: var(--surface-3); }
/* Outline buttons (.btn-outline-*). Bootstrap 5.3 paints their hover /
   active state as white text on a filled brand background
   (--bs-btn-hover-color: #fff). The two rules above replace that fill
   with --surface-2 / --surface-3 but leave Bootstrap's white text in
   place, so the label vanishes on mouseover — first reported on the
   outcome-goal category picker (system-adv_outcomes.php). Pin the text
   to the theme's primary colour; the vars flip with light/dark/high-
   contrast themes, and the fallbacks cover a page that lost the token
   block. The legacy (non-Aurora) layout never loads this file, so
   Bootstrap's filled hover stays intact there. */
.btn[class*="btn-outline-"]:hover,
.btn[class*="btn-outline-"]:focus-visible,
.btn[class*="btn-outline-"]:active {
    color: var(--text-1, #1f2933);
    background: var(--surface-2, #f1f3f5);
}
.btn[class*="btn-outline-"]:active { background: var(--surface-3, #e6e8eb); }
.btn[class*="btn-outline-"]:hover .badge,
.btn[class*="btn-outline-"]:active .badge { color: var(--text-on-accent, #fff); }

/* #545: pin the resting .btn-outline-danger label to --danger-outline-fg,
 * which is per-theme (see the palette blocks for the measured ratios). This
 * is the one button in the product that discards work ("End Timer" /
 * "Delete"), and Bootstrap's own #dc3545 fails AA on both page backgrounds we
 * render it on. The hover/active pair above already pins the label to
 * --text-1, so only the resting state needed changing. */
.btn.btn-outline-danger {
    --bs-btn-color: var(--danger-outline-fg);
    --bs-btn-border-color: var(--danger-outline-fg);
    color: var(--danger-outline-fg);
    border-color: var(--danger-outline-fg);
}
.btn.btn-primary {
    background: var(--accent-7);
    border-color: var(--accent-7);
    color: var(--text-on-accent);
}
.btn.btn-primary:hover { background: var(--accent-6); border-color: var(--accent-6); }
.btn.btn-ghost { background: transparent; border-color: transparent; }
.btn.btn-ghost:hover { background: var(--surface-2); }
.btn.btn-sm { padding: 3px var(--space-2); font-size: var(--text-xs); }
.btn.btn-icon { padding: 5px; }

/* --- Inputs --- */
.input, .select {
    padding: 6px var(--space-3);
    background: var(--surface-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    color: var(--text-1);
    font-size: var(--text-sm);
    transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.input:hover, .select:hover { border-color: var(--border-strong); }
.input:focus, .select:focus {
    outline: none;
    border-color: var(--accent-6);
    box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent-6) 25%, transparent);
}

/* --- Cards / panels --- */
.card {
    /* #548: Bootstrap derives its card corner radii from --bs-border-radius
     * as calc(radius - border-width), which knew nothing about the
     * border-radius below — so a card header rounded at 5px inside a 12px
     * card, and at 7px once the Bootstrap radius bridge moved the base to
     * 8px. Both now name the same token as the card itself. Declared on
     * `.card` because that is where Bootstrap declares them; a :root
     * override is shadowed. */
    --bs-card-border-radius:       var(--radius-3);
    --bs-card-inner-border-radius: var(--radius-3);
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-3);
    padding: var(--space-5);
}
.card-title {
    font-size: var(--text-base);
    font-weight: 600;
    margin: 0 0 var(--space-3);
    letter-spacing: -0.005em;
}

/* --- Badges + tags --- */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    font-weight: 500;
    line-height: 1.4;
    border: 1px solid var(--border-default);
    background: var(--surface-2);
    color: var(--text-2);
}
/* #233: badge tints come from theme-aware tokens so dark + HC pick a
 * legible BG/FG pair instead of the light-mode `--*-3 50%` mix that
 * goes murky on dark surfaces and washes out on pure-white in HC. */
.badge.success { background: var(--badge-success-bg); border-color: var(--success-3); color: var(--badge-success-fg); }
.badge.warning { background: var(--badge-warning-bg); border-color: var(--warning-3); color: var(--badge-warning-fg); }
.badge.danger  { background: var(--badge-danger-bg);  border-color: var(--danger-3);  color: var(--badge-danger-fg); }
.badge.info    { background: var(--badge-info-bg);    border-color: var(--info-3);    color: var(--badge-info-fg); }
.badge .dot {
    width: 6px; height: 6px;
    border-radius: var(--radius-full);
    background: currentColor;
    flex: 0 0 auto;
}

/* --- Tables --- */
.table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-3);
    overflow: hidden;
    font-variant-numeric: tabular-nums;
}
.table thead th {
    text-align: left;
    padding: 8px var(--space-3);
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--text-2);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border-subtle);
}
.table tbody td {
    padding: 10px var(--space-3);
    border-bottom: 1px solid var(--border-subtle);
    font-size: var(--text-sm);
    vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr {
    transition: background var(--dur-fast) var(--ease-out);
    cursor: pointer;
}
.table tbody tr:hover { background: var(--surface-2); }
.table tbody tr.selected {
    /* #233: token-driven so dark + HC pick a visible wash. */
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}
.table .case-num {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--text-2);
}
/* #233: same story as .nav-item.active .count — when the row is
 * .selected the wash is opaque enough to swallow the gray. Inherit. */
.table tbody tr.selected .case-num { color: inherit; }
.table .truncate {
    max-width: 280px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- Filter bar --- */
.filter-bar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) 0;
    flex-wrap: wrap;
}
.filter-bar .filter-group {
    display: flex;
    align-items: center;
    gap: 1px;
    padding: 2px;
    background: var(--surface-2);
    border-radius: var(--radius-2);
}
.filter-bar .filter-group .btn {
    border: 0;
    background: transparent;
    border-radius: var(--radius-1);
}
.filter-bar .filter-group .btn.active {
    background: var(--surface-1);
    box-shadow: var(--shadow-1);
}

/* --- Stat tiles --- */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}
.stat {
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-3);
    padding: var(--space-4);
}
.stat .label {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-2);
    font-weight: 600;
}
.stat .value {
    font-size: var(--text-3xl);
    font-weight: 600;
    letter-spacing: -0.025em;
    line-height: var(--leading-tight);
    margin: var(--space-2) 0 var(--space-1);
    font-variant-numeric: tabular-nums;
}
.stat .delta { font-size: var(--text-xs); color: var(--text-2); }
.stat .delta.up { color: var(--success-9); }
.stat .delta.down { color: var(--danger-9); }

/* --- Two-pane case detail --- */
.detail {
    display: grid;
    grid-template-columns: 220px 1fr var(--right-rail-w);
    gap: 0;
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-3);
    overflow: hidden;
    min-height: 720px;
}
.detail .section-nav {
    border-right: 1px solid var(--border-subtle);
    padding: var(--space-4);
    background: var(--surface-2);
}
.detail .section-nav h4 {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-3);
    margin: var(--space-4) 0 var(--space-2);
    font-weight: 600;
}
.detail .section-nav h4:first-child { margin-top: 0; }
.detail .section-nav .nav-item {
    display: flex;
    align-items: center;
    padding: 5px var(--space-2);
    border-radius: var(--radius-2);
    color: var(--text-2);
    font-size: var(--text-sm);
    margin: 1px 0;
    cursor: pointer;
}
.detail .section-nav .nav-item:hover { background: var(--surface-3); color: var(--text-1); }
.detail .section-nav .nav-item.active {
    background: var(--surface-1);
    color: var(--text-1);
    font-weight: 500;
    box-shadow: var(--shadow-1);
}

.detail .content {
    padding: var(--space-5) var(--space-6);
    overflow-y: auto;
}
.detail .content h2 {
    font-size: var(--text-xl);
    font-weight: 600;
    margin: 0 0 var(--space-4);
    letter-spacing: -0.01em;
}

.detail .right-rail {
    border-left: 1px solid var(--border-subtle);
    padding: var(--space-4);
    background: var(--surface-2);
    overflow-y: auto;
}
.detail .right-rail h4 {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-3);
    margin: 0 0 var(--space-3);
    font-weight: 600;
}

/* --- Field grid --- */
.field-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-4) var(--space-5);
    margin-bottom: var(--space-5);
}
.field { display: flex; flex-direction: column; gap: 2px; }
.field .field-label {
    font-size: var(--text-xs);
    color: var(--text-2);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.field .field-value {
    font-size: var(--text-sm);
    padding: 4px 0;
    cursor: text;
    border-bottom: 1px solid transparent;
    transition: border-color var(--dur-fast) var(--ease-out);
}
.field .field-value:hover { border-bottom-color: var(--border-default); }
.field .field-value.editable::after {
    content: " ";
    color: var(--text-3);
}

/* --- Activity timeline --- */
.timeline { display: flex; flex-direction: column; gap: var(--space-3); }
.timeline-item {
    position: relative;
    padding-left: var(--space-5);
    font-size: var(--text-sm);
}
.timeline-item::before {
    content: "";
    position: absolute;
    left: 6px; top: 8px;
    width: 8px; height: 8px;
    background: var(--accent-6);
    border-radius: var(--radius-full);
    box-shadow: 0 0 0 3px var(--surface-2);
}
.timeline-item:not(:last-child)::after {
    content: "";
    position: absolute;
    left: 9px; top: 16px;
    width: 2px; height: calc(100% - 4px);
    background: var(--border-default);
}
.timeline-item .who { font-weight: 500; }
.timeline-item .when { font-size: var(--text-xs); color: var(--text-3); }
.timeline-item .what { color: var(--text-2); margin-top: 2px; }

/* --- Command palette overlay --- */
.palette-overlay {
    position: fixed;
    inset: 0;
    /* #233: was hard-coded gray-11/50%. The scrim now comes from a
     * theme-aware token so dark mode can lean on pure-black for a
     * stronger blur edge and HC can use a 70% scrim that's still
     * readable against pure-white surfaces underneath. */
    background: var(--scrim);
    backdrop-filter: blur(4px);
    display: flex;
    justify-content: center;
    padding-top: 12vh;
    z-index: 100;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-base) var(--ease-out);
}
.palette-overlay.open { opacity: 1; pointer-events: auto; }

.palette {
    width: min(560px, 92vw);
    max-height: 60vh;
    background: var(--surface-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-3);
    box-shadow: var(--shadow-3);
    display: flex;
    flex-direction: column;
    transform: translateY(-8px);
    transition: transform var(--dur-base) var(--ease-out);
}
.palette-overlay.open .palette { transform: translateY(0); }
.palette input {
    border: 0;
    border-bottom: 1px solid var(--border-subtle);
    background: transparent;
    padding: var(--space-4) var(--space-5);
    font-size: var(--text-lg);   /* #548: was --text-md, which is gone */
    outline: none;
    color: var(--text-1);
}
.palette ul { list-style: none; margin: 0; padding: var(--space-2); overflow-y: auto; }
.palette li {
    padding: 8px var(--space-3);
    border-radius: var(--radius-2);
    font-size: var(--text-sm);
    display: flex;
    align-items: center;
    gap: var(--space-3);
    cursor: pointer;
    color: var(--text-1);
}
.palette li.active {
    /* #233: token-driven so dark + HC pick a visible wash. */
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}
.palette li .kind { color: var(--text-3); font-size: var(--text-xs); margin-left: auto; }
/* #233: same inherit-when-active pattern as .nav-item / .table .selected. */
.palette li.active .kind { color: inherit; }
.palette li .icon { color: var(--text-2); }

/* #298 palette polish: each row now has a small breadcrumb crumb
 * above the action label so the user sees the destination structure
 * ("Cases ▸ My Open Cases") at a glance. */
.palette li .palette-label {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.palette li .palette-crumb {
    color: var(--text-3);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.01em;
    line-height: 1;
}
.palette li .palette-name {
    color: inherit;
    font-size: var(--text-sm);
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.palette li.active .palette-crumb { color: inherit; opacity: 0.7; }

/* --- Hint footer for the palette ---
   a11y (#256): bumped from --text-3 (gray-6, 4.29:1) to --text-2 (gray-8,
   AA-passing 8.5:1) so axe's color-contrast rule passes against the
   #ffffff palette background. Same fix on the inline kbd: explicit
   var(--text-1) so the user-agent default white-on-light kbd colour
   (1.09:1) doesn't leak through. */
.palette-foot {
    border-top: 1px solid var(--border-subtle);
    padding: 6px var(--space-4);
    display: flex;
    gap: var(--space-4);
    font-size: var(--text-xs);
    color: var(--text-2);
}
.palette-foot kbd {
    padding: 1px 5px;
    border: 1px solid var(--border-default);
    /* #548: were 3px / 10px, both off the scale. */
    border-radius: var(--radius-1);
    font-size: var(--text-xs);
    background: var(--surface-2);
    color: var(--text-1);
}

/* --- Toast --- */
.toast {
    position: fixed;
    bottom: var(--space-5);
    left: 50%;
    transform: translateX(-50%);
    /* #233: was hard-coded gray-11 / gray-1 which inverted wrongly in
     * dark + had no contrast in HC. Theme-aware tokens swap the pair
     * so the toast always looks "different from the page". */
    background: var(--toast-bg);
    color: var(--toast-fg);
    padding: 10px var(--space-4);
    border-radius: var(--radius-2);
    font-size: var(--text-sm);
    box-shadow: var(--shadow-3);
}

/* --- Utility --- */
/* #271: removed `.row { display: flex; align-items: center; gap: ... }`
 * because it overrode Bootstrap's `.row` class and broke the multi-
 * column layout used by megareport.php and other Bootstrap forms.
 * Bootstrap's column padding plus this rule's gap added up to >100%
 * width, forcing two col-6 cells onto separate rows. Aurora-specific
 * row utilities now use `.aurora-row`. */
.aurora-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.muted { color: var(--text-2); }
.subtle { color: var(--text-3); }
/* #233: when these helpers sit inside a row/item that has an opaque
 * accent wash applied (.active / .selected → --accent-soft-bg), the
 * grayed text fights the wash and drops below AA. Inherit so the
 * wash's --accent-soft-fg flows through to the helper text too. */
.active .muted, .active .subtle,
.selected .muted, .selected .subtle { color: inherit; }
.tabular { font-variant-numeric: tabular-nums; }
.divider {
    height: 1px;
    background: var(--border-subtle);
    margin: var(--space-4) 0;
}
.spacer { flex: 1; }

/* #202 — supervisor dashboard tiles
 * The same markup is also styled by the Bootstrap fallback in custom.css
 * for the legacy chrome (which doesn't load aurora.css). Keep selector
 * names in sync between the two so a future Aurora-only refactor can
 * delete the fallback cleanly.
 */
.ocm-supervisor-dashboard {
    padding: var(--space-4);
}
.ocm-sup-heading {
    font-size: var(--text-xl, 1.25rem);
    color: var(--text-1, #111);
    margin-bottom: var(--space-3);
}
.ocm-sup-intro {
    color: var(--text-2);
    margin-bottom: var(--space-4);
}
.ocm-sup-tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--space-3);
}
.ocm-sup-tile {
    background: var(--surface-2, #f5f7fa);
    border: 1px solid var(--border-default, #d0d7de);
    border-radius: var(--radius-2, 6px);
    padding: var(--space-3, 12px);
    box-shadow: var(--shadow-1, 0 1px 2px rgba(0,0,0,0.05));
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.ocm-sup-tile:hover {
    box-shadow: var(--shadow-2, 0 4px 8px rgba(0,0,0,0.1));
    transform: translateY(-1px);
}
.ocm-sup-tile-link {
    display: block;
    text-decoration: none;
    color: inherit;
}
.ocm-sup-tile-link:hover,
.ocm-sup-tile-link:focus {
    text-decoration: none;
    color: inherit;
}
.ocm-sup-tile-count {
    font-size: 2rem;
    font-weight: 600;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}
.ocm-sup-tile-label {
    margin-top: 4px;
    font-size: var(--text-sm, 0.875rem);
    color: var(--text-2);
}
.ocm-sup-tone-info    { border-left: 4px solid #3182ce; }
.ocm-sup-tone-warning { border-left: 4px solid #d69e2e; }
.ocm-sup-tone-danger  { border-left: 4px solid #c53030; }
.ocm-sup-staff-table {
    margin-top: var(--space-3);
}
.ocm-sup-footnote {
    margin-top: var(--space-4);
}
/* --- Aurora chrome (#211 + #212) ----------------------------------------
 * Production rules layered on top of the mockup base. The mockup CSS
 * already ships .app / .sidebar / .topbar / .nav-item — these rules
 * cover the production-only concerns: the sidebar-collapse state, the
 * topbar avatar/notifications dropdowns, the empty-state placeholder
 * inside Saved views, and the pieces of legacy Bootstrap we need to
 * neutralise (`.wrapper` takes flex by default in custom.css; the
 * Aurora variant overrides to grid via the `.aurora-app` class). */

/* The body modifier keeps the legacy Bootstrap reset from interfering
 * with the grid app shell. */
.aurora-body { background: var(--surface-0); }

.aurora-app {
    /* mirror .app — the Aurora chrome wrapper carries both classes */
    display: grid;
    grid-template-columns: var(--sidebar-w) 1fr;
    grid-template-rows: var(--topbar-h) 1fr;
    grid-template-areas:
        "sidebar topbar"
        "sidebar main";
    height: 100vh;
    /* #298: cubic-bezier on the column transition gives the collapse
     * a subtle physics curve. Default linear ease feels mechanical
     * for a 240→56px slide; this pairs an ease-out start with a
     * slight overshoot tail. */
    transition: grid-template-columns 220ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
.aurora-app.collapsed {
    grid-template-columns: var(--sidebar-w-collapsed) 1fr;
}

/* #298: when collapsed, give the active nav item a 3px left stripe
 * so users can still see "where am I" without the label. The stripe
 * uses the accent ramp so it inherits theme color. */
.aurora-app.collapsed .aurora-sidebar .nav-item.active {
    position: relative;
}
.aurora-app.collapsed .aurora-sidebar .nav-item.active::before {
    content: "";
    position: absolute;
    left: -2px;
    top: 4px;
    bottom: 4px;
    width: 3px;
    border-radius: 2px;
    background: var(--accent-7);
}
.aurora-app.collapsed .aurora-sidebar .nav-label,
.aurora-app.collapsed .aurora-sidebar .nav-label-text,
.aurora-app.collapsed .aurora-sidebar .brand-label,
.aurora-app.collapsed .aurora-sidebar .count,
.aurora-app.collapsed .aurora-sidebar .aurora-empty-saved {
    display: none;
}
.aurora-app.collapsed .aurora-sidebar .nav-item {
    justify-content: center;
}

/* Sidebar nav links (anchors wrap each .nav-item) */
.aurora-sidebar .aurora-nav-link {
    color: inherit;
    text-decoration: none;
    display: block;
}
.aurora-sidebar .aurora-nav-link:hover { text-decoration: none; }

.aurora-sidebar .aurora-nav-button {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 6px var(--space-3);
    margin: 1px 0;
    width: 100%;
    color: var(--text-2);
    background: none;
    border: 0;
    border-radius: var(--radius-2);
    font: inherit;
    font-size: var(--text-sm);
    text-align: left;
    cursor: pointer;
}
.aurora-sidebar .aurora-nav-button:hover {
    background: var(--surface-2);
    color: var(--text-1);
}

.aurora-sidebar .aurora-empty-saved {
    padding: 4px var(--space-3);
    color: var(--text-3);
    font-size: var(--text-xs);
    font-style: italic;
}

.aurora-sidebar .aurora-bottom-nav {
    margin-top: auto;
}

/* Topbar additions */
.aurora-topbar .aurora-sidebar-toggle {
    margin-right: var(--space-2);
    width: 32px;
    height: 32px;
}
.aurora-topbar .search-trigger-label {
    flex: 1;
    text-align: left;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Topbar dropdowns (notifications + account menu).
 *
 * #267: the wrapper stretches to the topbar's full height so the
 * dropdown panel anchors below the topbar's bottom border instead of
 * overlapping it. Previously align-items: center on .topbar collapsed
 * the wrapper to the button's height (32px), and "top: calc(100% + 6px)"
 * positioned the panel relative to the wrapper rather than the topbar
 * — meaning the panel started above the topbar's bottom edge. */
.aurora-dropdown {
    position: relative;
    display: inline-flex;
    align-items: center;
    align-self: stretch;
}
.aurora-dropdown .aurora-avatar-button {
    border: 0;
    cursor: pointer;
}
.aurora-dropdown-panel {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    min-width: 240px;
    background: var(--surface-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    box-shadow: var(--shadow-2);
    z-index: 80;
    padding: var(--space-2) 0;
}
.aurora-dropdown-panel[hidden] { display: none; }
.aurora-dropdown-head {
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-xs);
    color: var(--text-2);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.aurora-dropdown-name {
    text-transform: none;
    font-size: var(--text-sm);
    color: var(--text-1);
    font-weight: 600;
    letter-spacing: 0;
}
.aurora-dropdown-sub {
    text-transform: none;
    font-size: var(--text-xs);
    color: var(--text-3);
    font-weight: 400;
    letter-spacing: 0;
}
.aurora-dropdown-body {
    padding: var(--space-3);
    font-size: var(--text-sm);
    color: var(--text-2);
}
/* #267: bumped vertical padding so menu items have visible breathing
 * room between rows. At 6px y-padding the items ran together as one
 * block of text. */
.aurora-dropdown-item {
    display: block;
    padding: var(--space-2) var(--space-4);
    color: var(--text-1);
    font-size: var(--text-sm);
    text-decoration: none;
    line-height: 1.4;
}
.aurora-dropdown-item + .aurora-dropdown-item {
    margin-top: 1px;
}
.aurora-dropdown-item:hover {
    background: var(--surface-2);
    text-decoration: none;
}
.aurora-dropdown-item--danger { color: var(--danger-9); }
.aurora-dropdown-item--danger:hover { background: color-mix(in oklch, var(--danger-3) 30%, var(--surface-1)); }
.aurora-dropdown-sep {
    height: 1px;
    background: var(--border-subtle);
    margin: var(--space-2) 0;
}

/* #293 — Aurora inline SVG icon set (replaces literal emoji glyphs).
 *
 * Default sizing tuned for sidebar nav rows + topbar icon-buttons.
 * Authors can compose `aurora-icon--lg` / `aurora-icon--sm` /
 * `aurora-icon--inline` for context-specific sizing.
 *
 * Stroke width is set on the <svg> itself; everything else (color,
 * transition) inherits from the surrounding element so no rule needs
 * to know about the icon underneath. */
.aurora-icon {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
    vertical-align: middle;
    transition: color var(--dur-fast) var(--ease-out);
}
.aurora-icon--sm     { width: 14px; height: 14px; }
.aurora-icon--lg     { width: 22px; height: 22px; }
.aurora-icon--xl     { width: 28px; height: 28px; }
.aurora-icon--inline { width: 16px; height: 16px; }

/* Sidebar nav rows give the icon its own slot (16x16). */
.sidebar .nav-item .icon .aurora-icon {
    width: 16px;
    height: 16px;
}

/* Topbar icon-buttons (32x32 outer) center an 18x18 icon. */
.topbar .icon-btn .aurora-icon {
    width: 18px;
    height: 18px;
}

.aurora-badge-dot {
    width: 8px;
    height: 8px;
    border-radius: var(--radius-full);
    background: var(--danger-6);
    position: absolute;
    top: 4px;
    right: 4px;
}

/* Page padding inside the Aurora <main>.
 *
 * #266: <main> is a flex column with the page-wrap flexed to grow
 * (`flex: 1 0 auto`) so the breadcrumb that follows naturally lands
 * at the bottom of the viewport — even on short pages like the home
 * dashboard. Previously the breadcrumb sat immediately under the
 * dashboard content mid-page.
 *
 * #262 / #269: bumped horizontal padding from --space-6 (32px) to
 * --space-7 (48px) so headings on short pages don't crowd the sidebar
 * divider on narrower viewports.
 *
 * The bare `.aurora-breadcrumb` has `min-width: 0` to prevent the
 * grid track collapsing on narrow viewports. */
.aurora-main {
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    min-height: 0;
}
/* Page-wrap width: the original 1280px cap left ~360px of dead margin
 * on either side of content on a 1920px desktop, while data-dense
 * tables (case_list, case detail) ran out of room and truncated
 * column headers. Bump the cap to 1680px so the content uses the
 * available horizontal real estate; on viewports narrower than ~1830px
 * the cap is irrelevant because viewport-minus-sidebar is already
 * smaller. Side padding shrinks from space-7 → space-6 to give back
 * another ~16px each side. Forms-heavy pages stay readable because
 * line length is naturally bounded by the widest field. */
.aurora-page-wrap {
    max-width: 1680px;
    padding: var(--space-5) var(--space-6);
    margin: 0 auto;
    width: 100%;
    flex: 1 0 auto;
}
.aurora-breadcrumb {
    padding: 0 var(--space-6) var(--space-5);
    max-width: 1680px;
    margin: 0 auto;
    width: 100%;
    flex: 0 0 auto;
    min-width: 0;
}
.aurora-breadcrumb .breadcrumb {
    background: transparent;
    color: var(--text-3);
    font-size: var(--text-xs);
    padding: 0;
    margin: 0;
    list-style: none;
}
/* #225 — documents grid ----------------------------------------------------
 *
 * Aurora Phase 5: card grid + preview pane that replaces the legacy
 * file_list table when `aurora_documents` is on. Tokens come from the
 * existing aurora.css ramp so light / dark / high-contrast themes pick
 * the right surfaces, borders, and text colors automatically.
 *
 * Layout: 1fr cards + a sticky right-side preview pane. Falls back to
 * a single column under 720px so phones get a stack of cards and the
 * preview pane re-flows below the grid.
 */
.ocm-doc-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-4);
    color: var(--text-1);
    font-family: var(--font-sans);
    position: relative;
}
.ocm-doc-toolbar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding-bottom: var(--space-3);
    border-bottom: 1px solid var(--border-subtle);
}
.ocm-doc-title {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--text-1);
}
.ocm-doc-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
    flex-wrap: wrap;
}
.ocm-spacer { flex: 1 1 var(--space-4); }
.ocm-doc-count {
    font-size: var(--text-sm);
    color: var(--text-2);
    min-width: 6ch;
}

/* Buttons — minimal, scoped to the documents grid so the existing
 * Bootstrap .btn ladder elsewhere is not perturbed. */
.ocm-doc-page .ocm-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    background: var(--surface-1);
    color: var(--text-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    padding: 6px var(--space-3);
    font-size: var(--text-sm);
    font-family: inherit;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out);
}
.ocm-doc-page .ocm-btn:hover:not(:disabled) {
    background: var(--surface-3);
    border-color: var(--border-strong);
}
.ocm-doc-page .ocm-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
.ocm-doc-page .ocm-btn--primary {
    background: var(--accent-6);
    color: var(--text-on-accent);
    border-color: var(--accent-7);
}
.ocm-doc-page .ocm-btn--primary:hover:not(:disabled) {
    background: var(--accent-7);
    border-color: var(--accent-9);
}
.ocm-doc-page .ocm-btn--danger {
    color: var(--danger-9);
    border-color: var(--danger-3);
}
.ocm-doc-page .ocm-btn--danger:hover:not(:disabled) {
    background: var(--danger-3);
    color: var(--gray-11);
}

.ocm-doc-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--right-rail-w);
    gap: var(--space-4);
    align-items: start;
}
@media (max-width: 880px) {
    .ocm-doc-layout {
        grid-template-columns: 1fr;
    }
}

.ocm-doc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: var(--space-3);
    min-height: 200px;
}
.ocm-doc-grid-empty {
    grid-column: 1 / -1;
    padding: var(--space-6);
    text-align: center;
    color: var(--text-2);
    background: var(--surface-2);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-2);
}

.ocm-doc-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3);
    background: var(--surface-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    cursor: pointer;
    transition: transform var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
.ocm-doc-card:hover {
    border-color: var(--accent-5);
    box-shadow: var(--shadow-2);
}
.ocm-doc-card:focus-visible {
    outline: 2px solid var(--accent-6);
    outline-offset: 2px;
}
.ocm-doc-card--selected {
    border-color: var(--accent-6);
    background: var(--accent-1);
    box-shadow: var(--shadow-2);
}

.ocm-doc-thumb {
    margin: 0;
    height: 120px;
    background: var(--surface-2);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-1);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.ocm-doc-thumb-img,
.ocm-doc-thumb-pdf {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border: 0;
}
.ocm-doc-thumb-icon {
    font-size: var(--text-lg);
    font-weight: 600;
    letter-spacing: 0.05em;
    color: var(--text-2);
    background: var(--surface-1);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-1);
    border: 1px solid var(--border-default);
}

.ocm-doc-meta {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.ocm-doc-name {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text-1);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ocm-doc-name--editing {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    background: var(--surface-2);
    outline: 2px solid var(--accent-6);
    border-radius: var(--radius-1);
    padding: 1px 4px;
    cursor: text;
}
.ocm-doc-sub {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ocm-doc-select {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    width: 18px;
    height: 18px;
    accent-color: var(--accent-6);
    cursor: pointer;
}

/* Preview pane — sticky on wide screens so it stays visible while the
 * grid scrolls; flows into a card below the grid on narrow viewports. */
.ocm-doc-preview {
    background: var(--surface-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    padding: var(--space-3);
    min-height: 320px;
    position: sticky;
    top: var(--space-4);
    max-height: calc(100vh - var(--topbar-h) - var(--space-6));
    overflow: auto;
}
.ocm-doc-preview[data-empty] {
    display: flex;
    align-items: center;
    justify-content: center;
}
.ocm-doc-preview-empty {
    margin: 0;
    color: var(--text-3);
    font-size: var(--text-sm);
    text-align: center;
}
.ocm-doc-preview-head {
    border-bottom: 1px solid var(--border-subtle);
    padding-bottom: var(--space-2);
    margin-bottom: var(--space-2);
}
.ocm-doc-preview-name {
    margin: 0 0 2px 0;
    font-size: var(--text-lg);   /* #548: was --text-md, which is gone */
    font-weight: 600;
    color: var(--text-1);
    word-break: break-word;
}
.ocm-doc-preview-sub {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-2);
}
.ocm-doc-preview-body {
    display: flex;
    align-items: center;
    justify-content: center;
}
.ocm-doc-preview-pdf {
    width: 100%;
    height: 70vh;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-1);
}
.ocm-doc-preview-img {
    max-width: 100%;
    max-height: 70vh;
    border-radius: var(--radius-1);
}
.ocm-doc-preview-fallback {
    text-align: center;
    color: var(--text-2);
    padding: var(--space-5) var(--space-3);
}

/* Drop overlay — shown via data-doc-drop-overlay (the JS toggles
 * `hidden` on the host node so we don't need an inline display:none). */
.ocm-drop-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: color-mix(in oklch, var(--accent-9) 55%, transparent);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
.ocm-drop-overlay-inner {
    background: var(--surface-1);
    border: 2px dashed var(--accent-6);
    border-radius: var(--radius-3);
    padding: var(--space-7);
    color: var(--text-1);
    font-size: var(--text-lg);
    font-weight: 500;
    box-shadow: var(--shadow-3);
}

/* Confirmation modal */
.ocm-doc-confirm {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: rgb(0 0 0 / 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
}
.ocm-doc-confirm-card {
    background: var(--surface-1);
    border-radius: var(--radius-3);
    padding: var(--space-5);
    max-width: 440px;
    width: 90%;
    box-shadow: var(--shadow-3);
    color: var(--text-1);
}
.ocm-doc-confirm-title {
    margin: 0 0 var(--space-2) 0;
    font-size: var(--text-lg);
    font-weight: 600;
}
.ocm-doc-confirm-msg {
    margin: 0 0 var(--space-4) 0;
    color: var(--text-2);
    font-size: var(--text-sm);
}
.ocm-doc-confirm-buttons {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* Document Understanding modal — case-scoped analysis and proposal review. */
.ocm-doc-preview-ai {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border-subtle);
}
.ocm-doc-preview-ai p {
    margin: var(--space-2) 0 0;
    color: var(--text-2);
    font-size: var(--text-xs);
}
.ocm-doc-ai-modal {
    position: fixed;
    inset: 0;
    z-index: 10020;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    background: rgb(0 0 0 / 0.52);
    backdrop-filter: blur(3px);
}
.ocm-doc-ai-modal[hidden] { display: none; }
.ocm-doc-ai-card {
    width: min(760px, 96vw);
    max-height: min(820px, 92vh);
    overflow: auto;
    padding: var(--space-5);
    background: var(--surface-1);
    color: var(--text-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-3);
    box-shadow: var(--shadow-3);
}
.ocm-doc-ai-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    padding-bottom: var(--space-3);
    border-bottom: 1px solid var(--border-subtle);
}
.ocm-doc-ai-eyebrow,
.ocm-doc-ai-label,
.ocm-doc-ai-proposal-type {
    margin: 0 0 var(--space-1);
    color: var(--accent-7);
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.ocm-doc-ai-title { margin: 0; font-size: var(--text-xl); }
.ocm-doc-ai-status {
    margin: var(--space-3) 0;
    padding: var(--space-3);
    border-radius: var(--radius-2);
    background: var(--surface-2);
    color: var(--text-2);
}
.ocm-doc-ai-status--working { border-left: 4px solid var(--accent-6); }
.ocm-doc-ai-status--ready { border-left: 4px solid var(--success-6, #287a52); }
.ocm-doc-ai-status--error { border-left: 4px solid var(--danger-6, #b42318); }
.ocm-doc-ai-summary {
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-2);
    background: var(--surface-2);
}
.ocm-doc-ai-classification {
    display: inline-block;
    padding: 3px var(--space-2);
    border-radius: 999px;
    background: var(--accent-2);
    color: var(--accent-8);
    font-weight: 600;
    text-transform: capitalize;
}
.ocm-doc-ai-proposals h3 { margin: var(--space-4) 0 var(--space-2); }
.ocm-doc-ai-proposal {
    display: flex;
    justify-content: space-between;
    gap: var(--space-4);
    margin-top: var(--space-2);
    padding: var(--space-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
}
.ocm-doc-ai-proposal p { margin: 0 0 var(--space-1); }
.ocm-doc-ai-proposal-actions { display: flex; gap: var(--space-2); align-items: center; }
.ocm-doc-ai-proposal--decided { background: var(--surface-2); }
.ocm-doc-ai-empty { color: var(--text-2); }
.ocm-doc-ai-inline-error { color: var(--danger-7, #b42318); font-size: var(--text-xs); }
@media (max-width: 620px) {
    .ocm-doc-ai-card { padding: var(--space-3); }
    .ocm-doc-ai-proposal { flex-direction: column; }
}

/* Semantic conflict review embedded in the sidebar's New Intake path. */
.ocm-semantic-intake {
    margin-bottom: var(--space-4);
    padding: var(--space-4);
    border: 1px solid var(--warning-6, #b7791f);
    border-radius: var(--radius-2);
    background: var(--warning-1, #fff8e6);
    color: var(--text-1);
}
.ocm-semantic-intake__head {
    display: flex;
    justify-content: space-between;
    gap: var(--space-3);
    align-items: flex-start;
}
.ocm-semantic-intake__head h2 { margin: 0; font-size: var(--text-lg); }
.ocm-semantic-intake__eyebrow {
    margin: 0 0 var(--space-1);
    color: var(--warning-8, #7a4b00);
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}
.ocm-semantic-intake__status { color: var(--text-2); font-size: var(--text-sm); }
.ocm-semantic-intake__hit {
    display: grid;
    grid-template-columns: minmax(220px, .8fr) minmax(320px, 1.2fr);
    gap: var(--space-4);
    margin-top: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    background: var(--surface-1);
}
.ocm-semantic-intake__candidate { display: flex; flex-direction: column; gap: var(--space-1); }
.ocm-semantic-intake__candidate p { margin: 0; }
.ocm-semantic-intake__name { font-weight: 700; }
.ocm-semantic-intake__score {
    align-self: flex-start;
    padding: 2px var(--space-2);
    border-radius: 999px;
    background: var(--warning-2, #ffedbd);
    font-size: var(--text-xs);
    font-weight: 700;
}
.ocm-semantic-intake__review { display: flex; flex-direction: column; gap: var(--space-2); }
.ocm-semantic-intake__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.ocm-semantic-intake__decision-status { margin: 0; color: var(--text-2); font-size: var(--text-xs); }
.ocm-semantic-intake__hit--decided { border-color: var(--accent-5); background: var(--surface-2); }
@media (max-width: 720px) {
    .ocm-semantic-intake__hit { grid-template-columns: 1fr; }
}

/* AI builder modal mounted directly on the Megareport form. */
.ocm-megareport-ai-launch {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: 0 0 var(--space-4);
    padding: var(--space-3);
    border: 1px solid var(--accent-4);
    border-radius: var(--radius-2);
    background: var(--accent-1);
}
.ocm-megareport-ai-launch span { color: var(--text-2); }
.ocm-megareport-ai-modal {
    position: fixed;
    inset: 0;
    z-index: 10030;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    background: rgb(0 0 0 / .52);
    backdrop-filter: blur(3px);
}
.ocm-megareport-ai-modal[hidden] { display: none; }
.ocm-megareport-ai-card {
    width: min(760px, 96vw);
    max-height: 92vh;
    overflow: auto;
    padding: var(--space-5);
    border-radius: var(--radius-3);
    background: var(--surface-1);
    color: var(--text-1);
    box-shadow: var(--shadow-3);
}
.ocm-megareport-ai-head {
    display: flex;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}
.ocm-megareport-ai-head p { margin: 0; color: var(--accent-7); font-weight: 700; }
.ocm-megareport-ai-head h2 { margin: 0; }
.ocm-megareport-ai-actions { margin-top: var(--space-3); }
.ocm-megareport-ai-status { margin: var(--space-3) 0; color: var(--text-2); }
.ocm-megareport-ai-review {
    padding: var(--space-4);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    background: var(--surface-2);
}

/* ── Aurora site-map / Browse OCM (#288) ────────────────────────────
 *
 * Replaces the legacy 6-column dense link-list site map with a
 * card grid grouped by domain. The Admin card spans two columns and
 * has its own 3-column sub-grid for sub-grouping the dense system
 * screens.
 */
.ocm-sitemap {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
}
@media (max-width: 1100px) { .ocm-sitemap { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .ocm-sitemap { grid-template-columns: 1fr; } }
.ocm-sitemap__card {
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-3);
    padding: var(--space-4);
    transition: border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
.ocm-sitemap__card:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-1);
}
.ocm-sitemap__card--wide {
    grid-column: span 3;
}
@media (max-width: 1100px) { .ocm-sitemap__card--wide { grid-column: span 2; } }
@media (max-width: 700px)  { .ocm-sitemap__card--wide { grid-column: span 1; } }
.ocm-sitemap__card-title {
    margin: 0 0 var(--space-3);
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--text-1);
    letter-spacing: -0.005em;
}
.ocm-sitemap__group {
    margin: var(--space-3) 0 var(--space-2);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-3);
}
.ocm-sitemap__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.ocm-sitemap__list li {
    margin: 0;
}
.ocm-sitemap__list a {
    display: block;
    padding: 6px var(--space-2);
    color: var(--text-1);
    text-decoration: none;
    font-size: var(--text-sm);
    border-radius: var(--radius-1);
    transition: background var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
.ocm-sitemap__list a:hover {
    background: var(--surface-2);
    color: var(--accent-7);
    text-decoration: none;
}
.ocm-sitemap__columns {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
}
@media (max-width: 800px) { .ocm-sitemap__columns { grid-template-columns: 1fr; } }
.ocm-sitemap__reports ul,
.ocm-sitemap__reports ol {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.ocm-sitemap__reports li {
    margin: 0;
}
.ocm-sitemap__reports a {
    display: block;
    padding: 6px var(--space-2);
    color: var(--text-1);
    text-decoration: none;
    font-size: var(--text-sm);
    border-radius: var(--radius-1);
}
.ocm-sitemap__reports a:hover {
    background: var(--surface-2);
    color: var(--accent-7);
}

/* ── Aurora empty-state pattern (#296) ──────────────────────────────
 *
 * Extend the existing `.ocm-empty-state` to support an icon, a
 * 1-line description, and an inline CTA pointing to the action that
 * fills the void. Pages opt in by composing markup like:
 *
 *     <div class="ocm-empty-state">
 *       <div class="ocm-empty-state__icon">{lucide svg}</div>
 *       <p class="ocm-empty-state__title">No saved views yet</p>
 *       <p class="ocm-empty-state__hint">Pin one to surface it in the sidebar.</p>
 *       <a class="ocm-empty-state__cta" href="...">+ Save a view</a>
 *     </div>
 *
 * The bare-string variant `<div class="ocm-empty-state">No foo
 * yet</div>` keeps working — the new sub-elements are additive.
 */
.ocm-empty-state {
    text-align: center;
    color: var(--text-2);
    font-size: var(--text-sm);
    line-height: 1.55;
    padding: var(--space-5) var(--space-4);
}
.ocm-empty-state__icon {
    color: var(--text-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    margin: 0 auto var(--space-3);
    background: var(--surface-2);
    border-radius: var(--radius-full);
}
.ocm-empty-state__icon .aurora-icon {
    width: 22px;
    height: 22px;
}
.ocm-empty-state__title {
    margin: 0 0 var(--space-1);
    color: var(--text-1);
    font-size: var(--text-base);
    font-weight: 600;
}
.ocm-empty-state__hint {
    margin: 0 0 var(--space-3);
    color: var(--text-2);
    font-size: var(--text-sm);
}
.ocm-empty-state__cta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 6px var(--space-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    background: var(--surface-1);
    color: var(--text-1);
    text-decoration: none;
    font-size: var(--text-sm);
    font-weight: 500;
}
.ocm-empty-state__cta:hover {
    background: var(--surface-2);
    border-color: var(--border-strong);
    text-decoration: none;
}

/* Inline empty-state used in dashboard panels — same tokens, smaller
 * footprint so it doesn't dominate a half-column card. */
.ocm-empty-state--inline {
    padding: var(--space-4) var(--space-3);
}
.ocm-empty-state--inline .ocm-empty-state__icon {
    width: 36px; height: 36px; margin-bottom: var(--space-2);
}

/* ── Calendar action bar + section hierarchy (#289, #290) ──────────
 *
 * Calendar pages render a top pagination row (Previous Day / Go to
 * date / Next Day) and four section cards (Today's Pending,
 * Actions, To Do List, Time Slip Log). Without help, every section
 * uses an h2 with its own card, so the page reads as a stack of
 * equal-weight blocks. Polish:
 *
 *   - Pagination becomes a clean toolbar with chip-shaped Prev/Next
 *     and a flush-right inline date input.
 *   - Section h2 (.hdt) becomes a smaller, lighter sub-section
 *     heading so the page-header stays the visual top.
 *   - The "Actions" sidebar gets a subtle inset background so it
 *     reads as a sidebar, not as the canvas.
 */
.aurora-page-wrap .pagination {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-2);
    padding: var(--space-2);
    margin: 0 0 var(--space-4) 0;
    list-style: none;
}
.aurora-page-wrap .pagination .page-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 4px var(--space-3);
    border-radius: var(--radius-1);
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-1);
    font-size: var(--text-sm);
    text-decoration: none;
    transition: background var(--dur-fast) var(--ease-out);
}
.aurora-page-wrap .pagination .page-link:hover {
    background: var(--surface-2);
    color: var(--text-1);
    border-color: var(--border-subtle);
}
.aurora-page-wrap .pagination .form-inline {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 1 1 auto;
    justify-content: center;
}
.aurora-page-wrap .pagination .form-inline label {
    font-size: var(--text-sm);
    color: var(--text-2);
}
.aurora-page-wrap .pagination .ml-md-auto { margin-left: auto; }
.aurora-page-wrap .pagination .ml-auto    { margin-left: auto; }

/* Section h2 inside a calendar / activity / dashboard becomes a
 * smaller "section" heading that reads below the page header. */
.aurora-page-wrap h2.hdt {
    margin: var(--space-4) 0 var(--space-2);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--text-1);
    letter-spacing: -0.005em;
}

/* Calendar Actions card — give it an inset background so it visually
 * reads as a sidebar / aside, not another primary content block. */
.aurora-page-wrap .col-sm:has(> .hdt:first-child) {
    /* no-op fallback for browsers without :has */
}
.aurora-page-wrap .col-sm > h2.hdt + .fas,
.aurora-page-wrap .col-sm > h2.hdt + form {
    margin-top: var(--space-2);
}

/* Intake form differentiation (#280): a subtle info-tinted top border
 * + a hint banner below the search form so users know intake creates
 * a NEW record vs the addressbook which finds an existing one. */
.aurora-page-wrap form[action*="intake2.php"] {
    border-top: 3px solid var(--success-6);
    padding-top: var(--space-3);
    border-radius: var(--radius-1);
}
.aurora-page-wrap form[action*="intake2.php"]::before {
    content: "Search first, then create a new record only if no existing contact matches.";
    display: block;
    font-size: var(--text-xs);
    color: var(--text-3);
    margin-bottom: var(--space-2);
}
.aurora-page-wrap form[action*="addressbook.php"] {
    border-top: 3px solid var(--accent-6);
    padding-top: var(--space-3);
    border-radius: var(--radius-1);
}
.aurora-page-wrap form[action*="addressbook.php"]::before {
    content: "Find an existing contact by name, phone, or SSN.";
    display: block;
    font-size: var(--text-xs);
    color: var(--text-3);
    margin-bottom: var(--space-2);
}

/* ── Aurora settings tabs (#302) ────────────────────────────────────
 *
 * System Settings split into 10 tabs grouped by configuration
 * category. Bootstrap 5's nav-tabs power the click-to-switch; this
 * stylesheet just gives the tab strip + content area Aurora-token-
 * native chrome instead of Bootstrap's defaults.
 *
 * The save bar at the bottom is sticky so a user changing settings
 * deep in a long tab can save without scrolling all the way back.
 */
.aurora-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    margin-bottom: var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
    padding-bottom: 0;
}
.aurora-tabs .nav-link {
    padding: 8px var(--space-3);
    border: 1px solid transparent;
    border-bottom: 0;
    border-radius: var(--radius-2) var(--radius-2) 0 0;
    background: transparent;
    color: var(--text-2);
    font-size: var(--text-sm);
    font-weight: 500;
    line-height: 1.4;
    transition: background var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
.aurora-tabs .nav-link:hover {
    background: var(--surface-2);
    color: var(--text-1);
}
.aurora-tabs .nav-link.active {
    background: var(--surface-1);
    color: var(--accent-7);
    border-color: var(--border-subtle);
    border-bottom-color: var(--surface-1);
    margin-bottom: -1px;
    position: relative;
    z-index: 1;
}
.aurora-tab-content {
    min-height: 60vh;
}
.aurora-save-bar {
    position: sticky;
    bottom: 0;
    background: var(--surface-0);
    padding: var(--space-3) 0;
    margin-top: var(--space-5);
    border-top: 1px solid var(--border-subtle);
    z-index: 5;
}
/* Settings-search target flash — when the user navigates to a
 * specific setting via #setting-foo the target gets a soft pulse so
 * they can find it within a busy tab. */
.aurora-tabs-target-flash {
    animation: aurora-settings-flash 1.4s ease-out;
}
@keyframes aurora-settings-flash {
    0%   { box-shadow: 0 0 0 4px var(--accent-soft-bg); }
    100% { box-shadow: 0 0 0 0 transparent; }
}

/* ── Aurora collapse / disclosure (#286) ────────────────────────────
 *
 * <details> / <summary> styled to match the design system. Used by
 * System Settings to hide the 8-input "Branding & icons" panel
 * behind a one-click expander, plus any other "advanced / optional"
 * surfaces.
 *
 * Replaces the implicit triangle marker with a chevron that rotates
 * when open. */
.ocm-collapse {
    cursor: pointer;
    padding: var(--space-3) var(--space-4);
}
.ocm-collapse > .ocm-collapse__summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding: 0;
    color: var(--text-1);
    font-weight: 500;
}
.ocm-collapse > .ocm-collapse__summary::-webkit-details-marker { display: none; }
.ocm-collapse > .ocm-collapse__summary::before {
    content: "▸";
    color: var(--text-3);
    font-size: var(--text-xs);
    transition: transform var(--dur-fast) var(--ease-out);
    flex: 0 0 auto;
}
.ocm-collapse[open] > .ocm-collapse__summary::before {
    transform: rotate(90deg);
}
.ocm-collapse__title {
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--text-1);
}
.ocm-collapse__hint {
    color: var(--text-3);
    font-size: var(--text-sm);
    font-weight: 400;
}
.ocm-collapse__body {
    margin-top: var(--space-3);
}

/* ── Form polish (#297) ─────────────────────────────────────────────
 *
 * Visual upgrade to the legacy two-input phone control on
 * addressbook.php / intake2.php / contact.php. The underlying
 * <input name="area_code"> + <input name="phone"> stays (so the
 * backend still receives two separate values) but the visual
 * treatment merges them into a single rounded shell with an
 * inline divider, getting rid of the cramped pre-1995 look.
 *
 * The .maskt span wraps the whole control in the legacy markup;
 * we use that as the styling hook. */
.aurora-page-wrap .maskt {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-2);
    background: var(--surface-1);
    font-size: var(--text-sm);
    color: var(--text-2);
    transition: border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
.aurora-page-wrap .maskt:focus-within {
    border-color: var(--accent-6);
    box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent-6) 25%, transparent);
}
.aurora-page-wrap .maskt input.form-control,
.aurora-page-wrap .maskt input.form-control-sm {
    border: 0 !important;
    box-shadow: none !important;
    background: transparent !important;
    padding: 4px 2px !important;
    font-size: var(--text-sm) !important;
    color: var(--text-1) !important;
    min-width: 0;
}
.aurora-page-wrap .maskt input[name="area_code"] {
    width: 36px !important;
    text-align: center;
}
.aurora-page-wrap .maskt input[name="phone"] {
    width: 80px !important;
}

/* ── Aurora semantic colour utilities (#299) ────────────────────────
 *
 * Aurora ships full success / warning / danger / info OKLCH ramps
 * but most components only ever pull from the accent ramp. These
 * utility classes expose the semantic tokens for the sites that
 * actually want to communicate meaning (saved-toast → success,
 * approaching-deadline row tint → warning, destructive button →
 * danger, hint banner → info).
 *
 * Each utility composes the appropriate background-tint, foreground,
 * and border tokens so the surface reads as a coherent semantic
 * card without any per-component per-theme overrides.
 */
.ocm-tint-success {
    background: var(--badge-success-bg);
    color: var(--badge-success-fg, var(--success-9));
    border-color: var(--success-6);
}
.ocm-tint-warning {
    background: var(--badge-warning-bg);
    color: var(--badge-warning-fg, var(--warning-9));
    border-color: var(--warning-6);
}
.ocm-tint-danger {
    background: var(--badge-danger-bg);
    color: var(--badge-danger-fg, var(--danger-9));
    border-color: var(--danger-6);
}
.ocm-tint-info {
    background: var(--badge-info-bg);
    color: var(--badge-info-fg, var(--info-6));
    border-color: var(--info-6);
}

/* Compact pill badges using the same semantic tokens (drop-in for
 * Bootstrap's .badge.bg-success / .bg-warning / .bg-danger which
 * stay light-only). */
.ocm-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 10px;
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    font-weight: 500;
    line-height: 1.5;
    border: 1px solid transparent;
}
.ocm-pill--success { background: var(--badge-success-bg); color: var(--badge-success-fg, var(--success-9)); }
.ocm-pill--warning { background: var(--badge-warning-bg); color: var(--badge-warning-fg, var(--warning-9)); }
.ocm-pill--danger  { background: var(--badge-danger-bg);  color: var(--badge-danger-fg,  var(--danger-9));  }
.ocm-pill--info    { background: var(--badge-info-bg);    color: var(--badge-info-fg,    var(--info-6));    }

/* Bootstrap semantic backgrounds that we DO want to surface — make
 * the dark theme honour the same hue family rather than leaking
 * white. .bg-success / .bg-warning / .bg-danger / .bg-info rendered
 * via Bootstrap had stark hex values that read badly in dark. */
[data-theme="dark"] .bg-success { background-color: color-mix(in oklch, var(--success-6) 60%, var(--surface-1)) !important; color: var(--success-3); }
[data-theme="dark"] .bg-warning { background-color: color-mix(in oklch, var(--warning-6) 60%, var(--surface-1)) !important; color: var(--gray-11); }
[data-theme="dark"] .bg-danger  { background-color: color-mix(in oklch, var(--danger-6)  60%, var(--surface-1)) !important; color: var(--danger-3); }
[data-theme="dark"] .bg-info    { background-color: color-mix(in oklch, var(--info-6)    60%, var(--surface-1)) !important; color: var(--info-3); }

/* Light + high-contrast themes: Bootstrap's .bg-danger paints a bright
 * red background but sets NO text colour, so .badge.bg-danger (case
 * flags, "Past due", "over budget", "cap reached", …) inherited the
 * body's dark text from .badge { color: var(--text-2) } above —
 * black-on-red, hard to read (LSNM/Ken pre-cutover report). Pin it
 * white. Placed after .badge so it wins by source order in light/HC;
 * the [data-theme="dark"] .bg-danger rule above is more specific
 * (0,2,0) so dark mode keeps its muted danger-3 treatment untouched. */
.bg-danger { color: #fff; }

/* #545: .bg-secondary has exactly the same defect, and it is the worst
 * measured contrast pair in the product. Bootstrap paints #6c757d and sets
 * no foreground, so .badge.bg-secondary inherited var(--text-2) (#3a3d44)
 * from .badge above — dark grey on mid grey, measured 2.31:1 against the
 * 4.5:1 WCAG AA floor. That one pair accounted for 13 of the 21 failing
 * nodes (12 of them the outcome codes on system-adv_outcomes.php, which
 * staff read to tell one outcome from another).
 *
 * White on #6c757d measures 4.69:1, so pinning it fixes every instance in
 * one rule. Unconditional, like .bg-danger: Bootstrap's .bg-secondary
 * background is the same #6c757d in every theme, so the same foreground is
 * correct in every theme. */
.bg-secondary { color: #fff; }

/* Activity compose form-action separator (#291). The Save / Save and
 * Close / Cancel button row gets a hairline divider above + tighter
 * grouping so it reads as the action affordance, not "another row of
 * fields". */
.aurora-page-wrap form .ocm-form-actions,
.aurora-page-wrap form .form-actions {
    margin-top: var(--space-5);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border-subtle);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* Inline "Text Highlighting" link inside Activity compose was a
 * power-user feature exposed at peer with primary form fields. Make
 * it look like a small textarea-toolbar control rather than a body
 * link. */
.aurora-page-wrap a[href*="text_highlighting"],
.aurora-page-wrap a.ocm-textarea-toolbar-link {
    display: inline-block;
    margin-top: var(--space-1);
    font-size: var(--text-xs);
    color: var(--text-3);
    text-decoration: none;
    padding: 2px var(--space-2);
    border-radius: var(--radius-1);
    background: var(--surface-2);
}
.aurora-page-wrap a[href*="text_highlighting"]:hover,
.aurora-page-wrap a.ocm-textarea-toolbar-link:hover {
    background: var(--surface-3);
    color: var(--text-1);
    text-decoration: none;
}

/* Case-detail activity rail — combine "+ New" with the empty-state
 * card so the empty state reads as intentional rather than lopsided.
 * (#275) The static-#219 rail wraps "+ New" link inside the rail's
 * default markup; rebalance the visual weight by giving the empty
 * <p> a centred icon + caption + button. */
.ocm-case-three-pane__rail .ocm-case-tl-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-3);
    color: var(--text-2);
    font-size: var(--text-sm);
    background: var(--surface-2);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-2);
    margin: var(--space-2) 0;
}

/* ── Aurora microinteractions (#300) ────────────────────────────────
 *
 * Apply a consistent hover + focus ring + transition treatment to
 * every interactive element. Three rules:
 *
 *   1. Every clickable element gets a 150ms ease-out background-color
 *      transition (subtle but perceptible, no layout shift).
 *   2. :focus-visible always shows the 2px outline + 2px offset
 *      pattern with the accent token. Not :focus, so mouse users
 *      don't see a ring after click.
 *   3. <main> content fades in 180ms on load — perceptual smoothness
 *      without faking SPA navigation.
 */

/* Universal smooth-hover for the most common click targets. The list
 * is intentionally narrow so we don't over-transition things like
 * <body> or <input> backgrounds. */
.btn,
.aurora-nav-link,
.aurora-nav-button,
.aurora-dropdown-item,
.aurora-sidebar .nav-item,
.topbar .icon-btn,
.aurora-page-header__action,
.ocm-home__kpi,
.ocm-home__list-link {
    transition:
        background-color var(--dur-fast) var(--ease-out),
        border-color var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out),
        transform var(--dur-fast) var(--ease-out),
        box-shadow var(--dur-fast) var(--ease-out);
}

/* Unified focus ring. Mouse users see no outline (focus alone),
 * keyboard users see the ring (focus-visible). */
*:focus-visible {
    outline: 2px solid var(--accent-6);
    outline-offset: 2px;
    border-radius: var(--radius-1);
}
.btn:focus,
.aurora-nav-link:focus,
.aurora-dropdown-item:focus,
.topbar .icon-btn:focus {
    outline: none;
}

/* No on-page-load animation on the main content column.
 *
 * There used to be a 220ms `aurora-fade-in` here (opacity 0 -> 1 plus a
 * 6px translateY) on `.aurora-main > .aurora-page-wrap`. It ran on every
 * navigation, which is what made ordinary page loads look like the
 * content was bouncing into place rather than simply being there.
 *
 * Two reasons it is gone rather than shortened. It animated the whole
 * content column, so it fought with the browser's own incremental paint
 * instead of hiding it. And it used `both` fill-mode, which pins the
 * column at opacity 0 before the animation starts -- so any delay in
 * applying the stylesheet showed an empty page, not an unstyled one.
 *
 * The `prefers-reduced-motion` block stays: `.aurora-app` still has a
 * transition on it for the sidebar collapse. */
@media (prefers-reduced-motion: reduce) {
    .aurora-app { transition: none; }
}

/* ── Aurora page-header component (#294) ────────────────────────────
 *
 * Every Aurora page should open with this same header bar:
 *
 *   <eyebrow>            <primary action>  <secondary action>
 *   Title
 *   Subtitle (optional)
 *   ────────────────────────────────────────────────────────
 *
 * The PHP helper pl_aurora_page_header() in cms/app/lib/
 * plAuroraComponents.php emits the markup. The hairline divider at
 * the bottom is rendered as a border, not a stroke, so the header
 * has the same height regardless of whether actions are present.
 */
.aurora-page-header {
    margin: 0 0 var(--space-5) 0;
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
}
.aurora-page-header__row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}
.aurora-page-header__lead {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
}
.aurora-page-header__eyebrow {
    font-size: var(--text-xs);
    color: var(--text-3);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.aurora-page-header__title {
    margin: 0;
    font-size: var(--text-2xl);
    font-weight: 600;
    color: var(--text-1);
    line-height: var(--leading-tight);
    letter-spacing: -0.015em;
}
.aurora-page-header__subtitle {
    margin: 0;
    color: var(--text-2);
    font-size: var(--text-sm);
}
.aurora-page-header__actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
}
.aurora-page-header__action {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

/* ── Aurora home dashboard (#295) ───────────────────────────────────
 *
 * Replaces the empty "Message Board" home with: greeting + 4 KPI
 * tiles + 2-column "Recent activity" / "Today's calendar" panels.
 * Pinned admin messages slide below the dashboard so they're still
 * surfaced but no longer dominate the most-visited page.
 *
 * Token usage:
 *   --surface-1   panel + KPI background
 *   --border-subtle   tile borders
 *   --space-*    inter-element rhythm
 *   --accent-*   KPI icon tints + hover lift
 *
 * Layout: a single CSS grid with three rows (greeting, KPIs, panels).
 * KPIs collapse to 2-up under 720px; panels stack under 900px. */
.ocm-home {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}
.ocm-home__greeting {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.ocm-home__title {
    font-size: var(--text-3xl);
    font-weight: 600;
    line-height: var(--leading-tight);
    letter-spacing: -0.02em;
    color: var(--text-1);
    margin: 0;
}
.ocm-home__subtitle {
    color: var(--text-2);
    font-size: var(--text-base);
    margin: 0;
}

/* KPI tiles ---------------------------------------------------------- */
.ocm-home__kpis {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-4);
    /* A <ul> since #573. role="listitem" on the <a> tiles was invalid --
       an <a href> is already a link and cannot be relabelled -- so the row
       is native list markup and the tiles carry no roles at all. */
    margin: 0;
    padding: 0;
    list-style: none;
}
/* The <li> is the grid item now, so it has to stretch and hand its full
   height to the tile. Without this the tiles stop being equal height as
   soon as one label wraps. */
.ocm-home__kpi-item {
    display: flex;
    min-width: 0;
}
.ocm-home__kpi-item > .ocm-home__kpi {
    flex: 1 1 auto;
    min-width: 0;
}
@media (max-width: 1100px) {
    .ocm-home__kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Feature L adds a fifth KPI tile ("Awaiting your approval"), but only on
   orgs that turn the approvals badge on. The base rule is a hard
   repeat(4), so a fifth tile drops onto a second row on its own and reads
   as a mistake. The modifier is emitted by cms/index.php only when the
   fifth tile is actually rendered, so a dashboard with four tiles keeps
   the layout it has always had. 1400px is where five tiles stop fitting
   without the value text wrapping; below it they go 3 + 2. */
.ocm-home__kpis--five { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 1400px) {
    .ocm-home__kpis--five { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 1100px) {
    .ocm-home__kpis--five { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.ocm-home__kpis--six { grid-template-columns: repeat(6, minmax(0, 1fr)); }
@media (max-width: 1400px) {
    .ocm-home__kpis--six { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 1100px) {
    .ocm-home__kpis--six { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Seven tiles: approvals + compliance + "Closed today" all on. Seven in a
   row only fits on wide screens; below 1400px they go 4 + 3. */
.ocm-home__kpis--seven { grid-template-columns: repeat(7, minmax(0, 1fr)); }
@media (max-width: 1400px) {
    .ocm-home__kpis--seven { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 1100px) {
    .ocm-home__kpis--seven { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.ocm-home__kpi {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4);
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-3);
    text-decoration: none;
    color: inherit;
    transition: transform var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
a.ocm-home__kpi:hover,
a.ocm-home__kpi:focus-visible {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-1);
    transform: translateY(-1px);
    text-decoration: none;
    color: inherit;
}
a.ocm-home__kpi:focus-visible {
    outline: 2px solid var(--accent-6);
    outline-offset: 2px;
}
.ocm-home__kpi-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-2);
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}
.ocm-home__kpi-value {
    font-size: var(--text-2xl);
    font-weight: 600;
    color: var(--text-1);
    line-height: 1;
    letter-spacing: -0.015em;
}
.ocm-home__kpi-label {
    font-size: var(--text-sm);
    color: var(--text-2);
}

/* Panels (Recent activity + Today's calendar + optional Message Board) */
.ocm-home__panels {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
}
@media (max-width: 900px) {
    .ocm-home__panels { grid-template-columns: 1fr; }
}
.ocm-home__panel {
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-3);
    overflow: hidden;
}
.ocm-home__panel--motd {
    grid-column: 1 / -1;
}
.ocm-home__panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
}
.ocm-home__panel-title {
    margin: 0;
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--text-1);
    letter-spacing: -0.005em;
}
.ocm-home__panel-link {
    font-size: var(--text-sm);
    color: var(--link);
    text-decoration: none;
}
.ocm-home__panel-link:hover { text-decoration: underline; }

/* List rows inside panels (recent activity, today's calendar) ---------- */
.ocm-home__list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.ocm-home__list-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
}
.ocm-home__list-item:last-child { border-bottom: 0; }
.ocm-home__list-link {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--text-1);
    text-decoration: none;
    font-size: var(--text-sm);
    line-height: 1.4;
}
.ocm-home__list-link:hover { color: var(--accent-7); text-decoration: none; }
.ocm-home__list-time {
    flex: 0 0 64px;
    font-variant-numeric: tabular-nums;
    color: var(--text-2);
    font-size: var(--text-xs);
}
.ocm-home__list-time--allday {
    font-style: italic;
    color: var(--text-3);
}
.ocm-home__list-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ocm-home__list-meta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
    font-size: var(--text-xs);
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
}
.ocm-home__list-meta-link {
    color: var(--text-2);
    text-decoration: none;
}
.ocm-home__list-meta-link:hover { color: var(--accent-7); text-decoration: underline; }
.ocm-home__list-date { color: var(--text-3); }

/* Empty states -------------------------------------------------------- */
.ocm-home__empty {
    padding: var(--space-5) var(--space-4);
    text-align: center;
    color: var(--text-2);
    font-size: var(--text-sm);
}
.ocm-home__empty p { margin: 0 0 var(--space-2) 0; color: inherit; }
.ocm-home__empty-cta {
    display: inline-block;
    color: var(--link);
    font-size: var(--text-sm);
    text-decoration: none;
}
.ocm-home__empty-cta:hover { text-decoration: underline; }

/* Tip of the day ------------------------------------------------------ */
.ocm-home__tip {
    position: relative;
    padding: var(--space-4);
    padding-right: var(--space-8);
    background: var(--surface-1);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-3);
    color: var(--text-1);
}
.ocm-home__tip--locked {
    border-color: var(--accent-5);
    background: var(--accent-soft-bg);
}
.ocm-home__tip-label {
    margin: 0 0 var(--space-2) 0;
    color: var(--text-2);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.ocm-home__tip-text {
    margin: 0;
    color: var(--text-1);
    font-size: var(--text-sm);
    line-height: 1.5;
}
.ocm-home__tip-note {
    margin: var(--space-2) 0 0 0;
    color: var(--text-2);
    font-size: var(--text-sm);
}
.ocm-home__tip-link {
    display: inline-block;
    margin-top: var(--space-2);
    color: var(--link);
    font-size: var(--text-sm);
    text-decoration: none;
}
.ocm-home__tip-link:hover { text-decoration: underline; }
.ocm-home__tip-dismiss {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    padding: var(--space-1) var(--space-2);
    border: 0;
    background: transparent;
    color: var(--text-2);
    cursor: pointer;
    font: inherit;
    font-size: var(--text-xs);
}
.ocm-home__tip-dismiss:hover,
.ocm-home__tip-dismiss:focus-visible {
    color: var(--text-1);
    text-decoration: underline;
}

/* Embedded message board content keeps the panel padding consistent
 * with the list rows above it. */
.ocm-home__motd {
    padding: var(--space-4);
    color: var(--text-1);
    font-size: var(--text-sm);
    line-height: 1.55;
}
.ocm-home__motd p,
.ocm-home__motd blockquote { margin: 0 0 var(--space-3) 0; }
.ocm-home__motd blockquote {
    padding-left: var(--space-4);
    border-left: 3px solid var(--border-subtle);
    color: var(--text-2);
    font-style: italic;
}

/* ── Dark + high-contrast Bootstrap component overrides (#272) ─────────
 *
 * The Aurora token system covers Aurora-native components, but the
 * legacy Bootstrap classes baked into many existing pages (`.card`,
 * `.bg-light`, `.text-muted`, `.table`, `.list-group-item`, …) ship
 * with hard-coded near-white backgrounds and dark text. In dark mode
 * those panels stay light and read as bright cards floating in a
 * dark page; many also lose contrast when the surrounding page text
 * shifts to light. The block below remaps the most-visible Bootstrap
 * surfaces to Aurora's theme-aware tokens so dark + high-contrast
 * users get a coherent, readable page without rewriting every
 * page-specific stylesheet. Light mode is untouched.
 */
[data-theme="dark"] body,
[data-theme="dark"] html {
    background: var(--surface-0) !important;
    color: var(--text-1);
}

/* Bootstrap surface utilities — the cards / wells / panels that
 * megareport.php, system-*.php and other forms use. */
[data-theme="dark"] .bg-light,
[data-theme="dark"] .bg-white {
    background-color: var(--surface-2) !important;
    color: var(--text-1);
}
[data-theme="dark"] .bg-dark {
    background-color: var(--surface-0) !important;
    color: var(--text-1);
}

/* Cards (#272). Bootstrap defaults: white bg, dark text, light border. */
[data-theme="dark"] .card {
    background-color: var(--surface-1);
    color: var(--text-1);
    border-color: var(--border-default);
}
[data-theme="dark"] .card .card-body {
    color: var(--text-1);
}
[data-theme="dark"] .card .card-body.bg-light {
    background-color: var(--surface-2) !important;
}
[data-theme="dark"] .card-header,
[data-theme="dark"] .card-footer {
    background-color: var(--surface-2);
    border-color: var(--border-subtle);
    color: var(--text-1);
}
[data-theme="dark"] .card-title { color: var(--text-1); }

/* Bootstrap text utilities. .text-muted's #6c757d sits at ~3:1 against
 * dark surfaces (below AA); remap to --text-3 which is the AA-clean
 * tertiary token in dark. */
[data-theme="dark"] .text-muted { color: var(--text-3) !important; }
[data-theme="dark"] .text-dark  { color: var(--text-1) !important; }
[data-theme="dark"] .text-body  { color: var(--text-1) !important; }
[data-theme="dark"] .text-secondary { color: var(--text-2) !important; }

/* Tables. Bootstrap's default is dark text on white. Map to surface-1
 * + text-1 in dark; alternating rows (.table-striped) pick up surface-2;
 * borders use the subtle token. */
[data-theme="dark"] .table {
    color: var(--text-1);
    background-color: var(--surface-1);
    border-color: var(--border-subtle);
    --bs-table-bg: var(--surface-1);
    --bs-table-color: var(--text-1);
    --bs-table-border-color: var(--border-subtle);
    --bs-table-striped-bg: var(--surface-2);
    --bs-table-striped-color: var(--text-1);
    --bs-table-hover-bg: var(--surface-3);
    --bs-table-hover-color: var(--text-1);
}
[data-theme="dark"] .table > :not(caption) > * > * {
    background-color: transparent;
    color: inherit;
    border-bottom-color: var(--border-subtle);
}
[data-theme="dark"] .table-light,
[data-theme="dark"] .table-light > th,
[data-theme="dark"] .table-light > td {
    background-color: var(--surface-2) !important;
    color: var(--text-1) !important;
}

/* List groups. */
[data-theme="dark"] .list-group {
    background-color: transparent;
    border-color: var(--border-subtle);
}
[data-theme="dark"] .list-group-item {
    background-color: var(--surface-1);
    color: var(--text-1);
    border-color: var(--border-subtle);
}
[data-theme="dark"] .list-group-item-action:hover,
[data-theme="dark"] .list-group-item-action:focus {
    background-color: var(--surface-2);
    color: var(--text-1);
}

/* Form controls outside the aurora-form-components gate (so legacy
 * pages with the Aurora chrome but without the form-components
 * toggle still render readable inputs in dark mode). The
 * aurora-form-components.css rules already use tokens; these are a
 * narrower fallback that matches by selector but only when in dark. */
[data-theme="dark"] input[type="text"],
[data-theme="dark"] input[type="search"],
[data-theme="dark"] input[type="email"],
[data-theme="dark"] input[type="password"],
[data-theme="dark"] input[type="number"],
[data-theme="dark"] input[type="url"],
[data-theme="dark"] input[type="tel"],
[data-theme="dark"] input[type="date"],
[data-theme="dark"] input[type="time"],
[data-theme="dark"] input[type="datetime-local"],
[data-theme="dark"] textarea,
[data-theme="dark"] select {
    background-color: var(--surface-1);
    color: var(--text-1);
    border-color: var(--border-default);
}
[data-theme="dark"] input::placeholder,
[data-theme="dark"] textarea::placeholder {
    color: var(--text-3);
    opacity: 1;
}

/* Headings inherit text colour but enforce the token here so legacy
 * pages with their own h1/h2 colour rules still flip in dark. */
[data-theme="dark"] h1, [data-theme="dark"] h2,
[data-theme="dark"] h3, [data-theme="dark"] h4,
[data-theme="dark"] h5, [data-theme="dark"] h6,
[data-theme="dark"] blockquote,
[data-theme="dark"] p,
[data-theme="dark"] label,
[data-theme="dark"] li,
[data-theme="dark"] dt, [data-theme="dark"] dd,
[data-theme="dark"] th, [data-theme="dark"] td {
    color: var(--text-1);
}

/* Borders + horizontal rules. */
[data-theme="dark"] hr {
    border-color: var(--border-subtle);
    color: var(--border-subtle);
    opacity: 1;
}
[data-theme="dark"] .border,
[data-theme="dark"] .border-dark {
    border-color: var(--border-default) !important;
}

/* Bootstrap dropdowns + alerts (used in legacy pages). */
[data-theme="dark"] .dropdown-menu {
    background-color: var(--surface-1);
    color: var(--text-1);
    border-color: var(--border-default);
}
[data-theme="dark"] .dropdown-item {
    color: var(--text-1);
}
[data-theme="dark"] .dropdown-item:hover,
[data-theme="dark"] .dropdown-item:focus {
    background-color: var(--surface-2);
    color: var(--text-1);
}
[data-theme="dark"] .alert {
    background-color: var(--surface-2);
    color: var(--text-1);
    border-color: var(--border-default);
}

/* Modals. */
[data-theme="dark"] .modal-content {
    background-color: var(--surface-1);
    color: var(--text-1);
    border-color: var(--border-default);
}
[data-theme="dark"] .modal-header,
[data-theme="dark"] .modal-footer {
    border-color: var(--border-subtle);
}

/* Megareport filter group labels (#279). Surface the AND-joined
 * group structure (Filter group A AND Filter group B AND …) so
 * users can see what they're configuring at a glance. */
.ocm-megareport-group {
    position: relative;
}
.ocm-megareport-group-label {
    position: absolute;
    top: -10px;
    left: 16px;
    padding: 0 var(--space-2);
    background: var(--surface-0);
    color: var(--text-3);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    z-index: 1;
}

/* Megareport sticky toolbar (#278). Lift Run Report into a sticky
 * toolbar so users don't have to scroll through every filter group
 * to submit. Keyed on the .ocm-megareport-run-sticky class that
 * reports/megareport/index.php applies, NOT on structure: the earlier
 * `.card.mb-4:last-of-type` selector pinned the bar regardless of the
 * per-user megareport_sticky_options preference and the org-wide
 * enable_megareport_sticky_run_bar setting, so "dock it in place"
 * (SBNM, 2026-09-01) was impossible to honour. */
.aurora-page-wrap form#mega > .container-fluid > .card.ocm-megareport-run-sticky {
    position: sticky;
    bottom: var(--space-3);
    z-index: 5;
    box-shadow: 0 4px 16px rgb(0 0 0 / 0.08);
}

/* Megareport form panels — custom.css hardcodes white bg for the
 * field-pane and saved-reports list, which leaves them blindingly
 * bright in dark mode. Override to surface tokens. */
[data-theme="dark"] .ocm-megareport-field-pane,
[data-theme="dark"] .ocm-megareport-saved {
    background-color: var(--surface-1) !important;
    color: var(--text-1);
    border-color: var(--border-default) !important;
}
[data-theme="dark"] .ocm-megareport-and {
    color: var(--danger-3);
}

/* Misc legacy hardcoded-white panels that the Aurora chrome inherits
 * (e.g. ocm-pb-rule cards, conflict cards, etc.). Use surface-2 so
 * they read as cards without dropping dark contrast. */
[data-theme="dark"] .ocm-pb-rule-card { background-color: var(--surface-2) !important; color: var(--text-1); }
[data-theme="dark"] .ocm-pb-rule-matched { background-color: var(--surface-3) !important; color: var(--text-1); }

/* Pagination. */
[data-theme="dark"] .page-link {
    background-color: var(--surface-1);
    color: var(--text-1);
    border-color: var(--border-subtle);
}
[data-theme="dark"] .page-item.active .page-link {
    background-color: var(--accent-7);
    border-color: var(--accent-7);
    color: var(--text-on-accent, #fff);
}
[data-theme="dark"] .page-item.disabled .page-link {
    background-color: var(--surface-1);
    color: var(--text-3);
}

/* ── E-signature status panel (Phase 4 follow-up) ─────────────────────
 * The documents page renders an envelope-status section beneath the
 * docs grid (cms/documents.php). Status badges are coloured by their
 * lifecycle state so reviewers can scan the table at a glance.
 */
.ocm-esign-panel {
    margin-top: 2rem;
    padding: 1rem;
    border-top: 1px solid var(--border-1, #e0e0e0);
}
.ocm-esign-panel h2 {
    font-size: 1.15rem;
    margin-bottom: 0.75rem;
}
.ocm-esign-panel h3 {
    font-size: 1rem;
    margin-top: 1rem;
    margin-bottom: 0.5rem;
}
.ocm-esign-send-form > * {
    margin-right: 0.5rem;
}
.ocm-esign-badge {
    display: inline-block;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    font-size: 0.85em;
    font-weight: 500;
    text-transform: capitalize;
    background-color: var(--surface-2, #eee);
    color: var(--text-2, #333);
}
.ocm-esign-pending,
.ocm-esign-sent     { background-color: #fff3cd; color: #664d03; }
.ocm-esign-viewed,
.ocm-esign-started  { background-color: #cfe2ff; color: #084298; }
.ocm-esign-completed{ background-color: #d1e7dd; color: #0f5132; }
.ocm-esign-declined { background-color: #f8d7da; color: #842029; }
.ocm-esign-expired,
.ocm-esign-unknown  { background-color: #e2e3e5; color: #41464b; }

/* OCM: forgot-password link visibility on light Aurora login card (inline styles CSP-blocked) */
.ocm-login-card a[href*="resetpw"], .ocm-login-card a[href*="resetpw"] p { color:#5b4fb0 !important; text-decoration:underline; }
.ocm-login-card a[href*="resetpw"]:hover, .ocm-login-card a[href*="resetpw"]:hover p { color:#3f3690 !important; }
/* OCM MFA enrollment QR */
.ocm-enroll-qr-wrap{display:flex;justify-content:center;margin:0 0 1rem;}
#totp-enroll-qr{position:relative;width:224px;height:224px;}
#totp-enroll-qr img,#totp-enroll-qr canvas{display:block;width:224px;height:224px;}
.ocm-enroll-manual{margin-bottom:1rem;}
.ocm-case-doc-ai {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 24px;
  margin: 0 0 24px;
  padding: 22px 24px;
  border: 1px solid var(--aurora-border, #d7dee8);
  border-radius: 16px;
  background: linear-gradient(135deg, #f4f8ff, #fff);
}
.ocm-case-doc-ai h2 { margin: 2px 0 6px; }
.ocm-case-doc-ai p { margin: 0; }
.ocm-case-doc-ai__controls {
  display: grid;
  grid-template-columns: minmax(260px, 420px) auto;
  gap: 8px 12px;
  align-items: end;
}
.ocm-case-doc-ai__controls label { grid-column: 1 / -1; margin: 0; font-weight: 700; }
@media (max-width: 850px) {
  .ocm-case-doc-ai { align-items: stretch; flex-direction: column; }
  .ocm-case-doc-ai__controls { grid-template-columns: 1fr; }
}

/* ── Upload progress panel (chunked uploads) ─────────────────────────
   Rendered by cms/js/csp/aurora-documents.js while an upload is in
   flight. Bar widths are set through the CSSOM, so there are no inline
   style attributes for the strict CSP (#235 Phase 7) to reject. */
.ocm-upload-progress {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 16px 0 0;
}
.ocm-upload-progress:empty { display: none; }
.ocm-upload-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 12px;
  padding: 10px 14px;
  border: 1px solid var(--aurora-border, #d7dee8);
  border-radius: 10px;
  background: var(--aurora-surface, #fff);
}
.ocm-upload-name {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ocm-upload-note {
  color: var(--aurora-text-muted, #5b6675);
  font-size: 0.85rem;
  white-space: nowrap;
}
.ocm-upload-track {
  grid-column: 1 / -1;
  overflow: hidden;
  height: 6px;
  border-radius: 3px;
  background: var(--aurora-surface-alt, #eef2f7);
}
.ocm-upload-bar {
  width: 0;
  height: 100%;
  border-radius: 3px;
  background: var(--aurora-accent, #2b6cb0);
  transition: width 0.2s ease;
}
.ocm-upload-row--failed { border-color: var(--aurora-danger, #c53030); }
.ocm-upload-row--failed .ocm-upload-note { color: var(--aurora-danger, #c53030); }
.ocm-upload-row--failed .ocm-upload-bar { background: var(--aurora-danger, #c53030); }
@media (prefers-reduced-motion: reduce) {
  .ocm-upload-bar { transition: none; }
}

/* ── Report catalogue cards ────────────────────────────────────────────
   The grouped markup is emitted only when report_list_categories is on.
   Keep these rules in Aurora so the toggle-off legacy list is unchanged. */
.report-list-category {
  margin-block: 28px;
}
.report-list-favourites {
  margin-block: 24px 32px;
  padding: 20px;
  border: 1px solid var(--aurora-accent, #2b6cb0);
  border-radius: 14px;
  background: var(--aurora-accent-soft, #edf4ff);
}
.report-list-category > h2,
.report-list-favourites > h2 {
  margin: 0 0 12px;
  color: var(--aurora-text, #182334);
}
/* The collapse control inside a category heading. report-list.js puts it
   there; without the script the heading is plain text and these rules match
   nothing. It has to inherit the heading's type so the page looks the same
   as it did when the heading itself carried the handler (#573). */
.report-list-category-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
/* A caret, so the heading reads as something you can operate. It used to
   have no affordance at all. Rotates rather than swapping glyphs, so the
   text next to it never reflows. */
.report-list-category-toggle::before {
  content: "";
  flex: none;
  width: 0;
  height: 0;
  border-top: 5px solid currentColor;
  border-right: 4px solid transparent;
  border-left: 4px solid transparent;
  transition: transform var(--dur-fast, 120ms) var(--ease-out, ease-out);
}
.report-list-category-toggle.is-collapsed::before {
  transform: rotate(-90deg);
}
@media (prefers-reduced-motion: reduce) {
  .report-list-category-toggle::before { transition: none; }
}
.report-list-category-toggle:focus-visible {
  /* currentColor, not the accent token: the accent needs a hex fallback
     for the pre-Aurora path and that would add a colour literal to the
     census for no visual gain. The heading colour is already high
     contrast against the page. */
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 4px;
}
.report-list-category > ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.report-list-favourites > ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.report-list-category > ul > li {
  position: relative;
  min-width: 0;
  padding: 18px 18px 54px;
  border: 1px solid var(--aurora-border, #d7dee8);
  border-radius: 12px;
  background: var(--aurora-surface, #fff);
  box-shadow: var(--aurora-shadow-sm, 0 1px 3px rgba(15, 23, 42, 0.08));
  color: var(--aurora-text-muted, #5b6675);
  line-height: 1.5;
}
.report-list-favourites > ul > li {
  position: relative;
  min-width: 0;
  padding: 18px 18px 54px;
  border: 1px solid var(--aurora-border, #d7dee8);
  border-radius: 12px;
  background: var(--aurora-surface, #fff);
  color: var(--aurora-text-muted, #5b6675);
  line-height: 1.5;
}
.report-list-category > ul > li > a,
.report-list-favourites > ul > li > a {
  display: inline-block;
  margin-bottom: 6px;
  color: var(--aurora-accent, #2b6cb0);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.3;
}
.report-list-category > ul > li > br,
.report-list-favourites > ul > li > br {
  display: none;
}
.report-list-category .report-favourite-toggle,
.report-list-favourites .report-favourite-toggle {
  position: absolute;
  right: 10px;
  bottom: 10px;
  left: 10px;
  margin: 0;
  text-align: left;
}
@media (max-width: 420px) {
  .report-list-category > ul,
  .report-list-favourites > ul {
    grid-template-columns: 1fr;
  }
}
