/* ==========================================================================
   EXXAT DESIGN SYSTEM — globals.css
   Brands: Exxat One (Lavender · hue 270) · Exxat Prism (Rose · hue 343)
   Standard: WCAG 2.1 Level AA
     • Text contrast ≥ 4.5 : 1  (SC 1.4.3)
     • UI control contrast ≥ 3 : 1  (SC 1.4.11)
     • Focus ring ≥ 3 : 1  (SC 2.4.11)
     • Touch targets ≥ 44 × 44 px  (SC 2.5.5 / mobile)
   ========================================================================== */

@import "tailwindcss";
@import "tw-animate-css";
/* Inter is loaded via next/font/google in the consuming app's layout — no @import needed here. */

/* --------------------------------------------------------------------------
   Self-describing Tailwind sources — see docs/migrations/0003-globals-css-canonical.md
   --------------------------------------------------------------------------
   Tailwind v4 only emits a utility when it sees the class string in a scanned
   file. The package's component classes live in two different places depending
   on how the package is consumed:

     • Published npm install:  node_modules/@exxatdesignux/ui/dist/  (compiled JS)
     • Monorepo workspace dev: packages/ui/src/                     (.tsx sources)

   @source paths resolve relative to THIS file, and both forms below resolve
   under both consumption modes. Since `exxat-ui eject` needs a source to fork
   from, the tarball ships `src/` too, so a published install scans the same
   classes twice — once compiled, once as .tsx. Tailwind emits each utility
   once, so the only cost is scan time, and dropping the `./` branch would
   break workspace dev where the package resolves to `packages/ui/src`.

   Consumer apps never need to know any of this: their globals.css needs
   `@import "@exxatdesignux/ui/globals.css";` plus their own folders.
   -------------------------------------------------------------------------- */
@source "../dist";
@source "./components";
@source "./hooks";
@source "./lib";
@source "./product-framework";

/* RTL layout direction support */
@custom-variant dark (&:is(.dark *));

/* High-contrast variant — in-app High or Windows (JSON) contrast */
@custom-variant hc (&:is([data-contrast="high"] *, [data-contrast="windows"] *));

/* Radix tabs use data-state=active; sidebar rows use a data-active attribute. */
@custom-variant data-active (&:is([data-state=active], [data-active]));

/* --------------------------------------------------------------------------
   Metric-compatible fallback for Inter
   --------------------------------------------------------------------------
   Inter loads with `font-display: swap`, so text paints in the fallback first.
   Arial is ~7% narrower than Inter with a different ascent, so without these
   overrides every text node reflows the moment the real face lands. The
   overrides scale the local face to Inter's metrics, making the swap
   invisible. Values match the Inter/Arial pairing used by next/font.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Liberation Sans");
  size-adjust: 107.12%;
  ascent-override: 90.20%;
  descent-override: 22.48%;
  line-gap-override: 0%;
}

/* --------------------------------------------------------------------------
   Tailwind v4 theme bridge — maps CSS custom-props → utility classes
   -------------------------------------------------------------------------- */
@theme inline {
  /* Typography — use Tailwind utilities (`font-sans`, `font-heading`, `text-*`), not raw px / inline font-family. */
  --font-heading: "ivypresto-text", Georgia, serif;
  --font-sans:    "Inter Variable", "Inter", "Inter Fallback", ui-sans-serif, system-ui, sans-serif;
  /* Sizes are rem so Settings → Appearance → Text size (root font-size step)
     actually scales body copy, not just the headings. `max()` keeps the hard
     px floors intact when the root steps down at `compact`. Line heights are
     unitless for the same reason. */
  /* Minimum product text: 12px — use `text-xs` or larger. Never use raw px in components. */
  --text-xs: max(12px, 0.75rem);
  --text-xs--line-height: 1.3333;
  /* Dense count badges / "New" pills — same 12px floor as `--text-xs`; tighter line-height only. */
  --text-2xs: max(12px, 0.75rem);
  --text-2xs--line-height: 1.1667;
  /* Body copy floor — Card, CardDescription, and other `text-sm` slots must not shrink below 14px when root rem scales. */
  --text-sm: max(14px, 0.875rem);
  --text-sm--line-height: 1.4286;
  --text-base: max(16px, 1rem);
  --text-base--line-height: 1.5;
  /* Dense FA icons + secondary-panel chrome (between xs and sm). */
  --text-icon: max(12px, 0.8125rem);
  --text-icon--line-height: 1;
  /* Command palette input + nav rows that sit between body (14px) and base (16px). */
  --text-md: max(14px, 0.9375rem);
  --text-md--line-height: 1.4667;

  /* Semantic color map */
  --color-background:              var(--background);
  --color-foreground:              var(--foreground);
  --color-card:                    var(--card);
  --color-card-foreground:         var(--card-foreground);
  --color-popover:                 var(--popover);
  --color-popover-foreground:      var(--popover-foreground);
  --color-primary:                 var(--primary);
  --color-primary-foreground:      var(--primary-foreground);
  --color-auth-primary:            var(--auth-primary);
  --color-auth-primary-foreground: var(--auth-primary-foreground);
  --color-auth-ink:                var(--auth-ink);
  --color-secondary:               var(--secondary);
  --color-secondary-foreground:    var(--secondary-foreground);
  --color-muted:                   var(--muted);
  --color-muted-foreground:        var(--muted-foreground);
  --color-accent:                  var(--accent);
  --color-accent-foreground:       var(--accent-foreground);
  --color-destructive:             var(--destructive);
  --color-destructive-foreground:  var(--destructive-foreground);
  --color-destructive-ink:         var(--destructive-ink);
  --color-slab:                    var(--slab);
  --color-slab-foreground:         var(--slab-foreground);
  --color-slab-border:             var(--slab-border);
  --color-destructive-subtle-foreground: var(--destructive-subtle-foreground);
  --color-border:                  var(--border);
  --color-border-control:          var(--border-control);
  --color-input:                   var(--input);
  --color-input-background:        var(--input-background);
  --color-ring:                    var(--ring);
  /* Modal / sheet / drawer scrim */
  --color-overlay:                 var(--overlay);

  /* Chart tokens */
  --color-chart-1:  var(--chart-1);
  --color-chart-2:  var(--chart-2);
  --color-chart-3:  var(--chart-3);
  --color-chart-4:  var(--chart-4);
  --color-chart-5:  var(--chart-5);

  /* Chip / badge tokens (AA-compliant on light bg: ≥ 4.5:1) */
  --color-chip-1:            var(--chip-1);
  --color-chip-2:            var(--chip-2);
  --color-chip-3:            var(--chip-3);
  --color-chip-4:            var(--chip-4);
  --color-chip-5:            var(--chip-5);
  --color-chip-destructive:  var(--chip-destructive);

  /* Semantic status badge pairs — locked L recipe (fill ≈95%, fg ≈38.5%) */
  --color-status-badge-success-fill: var(--status-badge-success-fill);
  --color-status-badge-success-fg:   var(--status-badge-success-fg);
  --color-status-badge-info-fill:    var(--status-badge-info-fill);
  --color-status-badge-info-fg:      var(--status-badge-info-fg);
  --color-status-badge-warning-fill: var(--status-badge-warning-fill);
  --color-status-badge-warning-fg:   var(--status-badge-warning-fg);
  --color-status-badge-danger-fill:  var(--status-badge-danger-fill);
  --color-status-badge-danger-fg:    var(--status-badge-danger-fg);
  --color-status-badge-neutral-fill: var(--status-badge-neutral-fill);
  --color-status-badge-neutral-fg:   var(--status-badge-neutral-fg);

  /* Brand accent + Exxat One tint scale (see :root --brand-*) */
  --color-brand:              var(--brand-color);
  --color-brand-dark:         var(--brand-color-dark);
  --color-brand-foreground:   var(--brand-foreground);
  --color-brand-ink:          var(--brand-ink);
  --color-brand-tint:         var(--brand-tint);
  --color-brand-tint-light:   var(--brand-tint-light);
  --color-brand-tint-subtle:  var(--brand-tint-subtle);
  --color-brand-color-light:  var(--brand-color-light);
  --color-brand-deep:         var(--brand-color-deep);

  /* Prism banner highlight */
  --color-banner-prism:  var(--banner-prism-bg);

  /* Sidebar */
  --color-sidebar:                    var(--sidebar);
  --color-sidebar-foreground:         var(--sidebar-foreground);
  --color-sidebar-primary:            var(--sidebar-primary);
  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
  --color-sidebar-accent:             var(--sidebar-accent);
  --color-sidebar-accent-foreground:  var(--sidebar-accent-foreground);
  --color-sidebar-border:             var(--sidebar-border);
  --color-sidebar-ring:               var(--sidebar-ring);
  /* WCAG 1.4.3 — always mixed against real --sidebar (lavender / rose / dark) */
  --color-sidebar-section-label:      var(--sidebar-section-label-foreground);

  /* Interactive hover — single source for ghost controls, lists, table chrome */
  --color-interactive-hover:                    var(--interactive-hover);
  --color-interactive-hover-foreground:         var(--interactive-hover-foreground);
  --color-interactive-hover-subtle:             var(--interactive-hover-subtle);
  --color-interactive-hover-soft:               var(--interactive-hover-soft);
  --color-interactive-hover-medium:             var(--interactive-hover-medium);
  --color-interactive-hover-strong:             var(--interactive-hover-strong);
  --color-interactive-hover-row:                var(--interactive-hover-row);

  /* Icon-only controls — SC 1.4.11 (≥3:1 UI) on canvas / sidebar */
  --color-icon-button-foreground:               var(--icon-button-foreground);
  --color-icon-button-foreground-on-sidebar:    var(--icon-button-foreground-on-sidebar);
  --color-icon-button-foreground-on-secondary-panel: var(--icon-button-foreground-on-secondary-panel);
  --color-secondary-action-foreground:          var(--secondary-action-foreground);
  --color-placeholder-foreground:                 var(--placeholder-foreground);

  /* DataTable — opaque surfaces for pinned/sticky cells and row states */
  --color-dt-row-bg:             var(--dt-row-bg);
  --color-dt-row-hover:          var(--dt-row-hover);
  --color-dt-row-selected:       var(--dt-row-selected);
  --color-dt-row-selected-fg:    var(--dt-row-selected-fg);
  --color-dt-row-open-marker:    var(--dt-row-open-marker);
  --color-dt-header-bg:          var(--dt-header-bg);
  --color-dt-group-bg:           var(--dt-group-bg);
  --color-dt-new-row-bg:         var(--dt-new-row-bg);
  --color-dt-new-row-border:     var(--dt-new-row-border);
  --color-dt-new-row-dot:        var(--dt-new-row-dot);

  /*
   * Border-radius scale (4 px base → 8 px default).
   *
   * Rebound onto the L0 names rather than restating the pixels, which is what
   * the token taxonomy asks of an L1 name and which the values already agreed
   * with exactly. It also makes the scale themeable: `@theme inline` substitutes
   * the *declaration* into each utility, so a literal `12px` bakes into
   * `.rounded-lg` and no later override can reach it, whereas a `var()` leaves
   * the reference intact and resolves per subtree. That is what lets the compact
   * shell halve every corner from one place.
   */
  --radius-sm:  var(--exxat-radius-1);
  --radius-md:  var(--exxat-radius-2);
  --radius-lg:  var(--exxat-radius-3);
  --radius-xl:  var(--exxat-radius-4);
  --radius-2xl: var(--exxat-radius-5);
  --radius-3xl: var(--exxat-radius-6);

  /* ============================================================
     Exxat L0 — Tailwind utility bridges (additive)
     ------------------------------------------------------------
     Short utility forms backed by --exxat-* canonical primitives.
     They coexist with the shadcn-named bridges above. Prefer
     these for new code; see apps/web/docs/token-taxonomy.md.
     ============================================================ */
  --color-surface-1:           var(--exxat-color-surface-1);
  --color-surface-2:           var(--exxat-color-surface-2);
  --color-surface-3:           var(--exxat-color-surface-3);
  --color-surface-muted:       var(--exxat-color-surface-muted);
  --color-surface-accent:      var(--exxat-color-surface-accent);
  --color-surface-sidebar:     var(--exxat-color-surface-sidebar);
  --color-ink-1:               var(--exxat-color-ink-1);
  --color-ink-2:               var(--exxat-color-ink-2);
  --color-ink-on-brand:        var(--exxat-color-ink-on-brand);
  --color-brand-1:             var(--exxat-color-brand-1);
  --color-brand-2:             var(--exxat-color-brand-2);
  --color-brand-3:             var(--exxat-color-brand-3);
  --color-brand-tint-1:        var(--exxat-color-brand-tint-1);
  --color-brand-tint-2:        var(--exxat-color-brand-tint-2);
  --color-brand-tint-3:        var(--exxat-color-brand-tint-3);
  --color-border-1:            var(--exxat-color-border-1);
  --color-focus-ring:          var(--exxat-color-focus-ring);

  /* Elevation ramp → shadow-xs … shadow-xl.
     These MUST live in `@theme inline`, not `:root`. Declared in `:root` they
     are inert: Tailwind emits its own default shadows for the utilities and
     nothing reads the token. Purpose-built sheet shadows stay in `:root`
     because they are consumed as `var(--shadow-sheet-*)`, not as utilities. */
  --shadow-xs: oklch(0 0 0 / 0.05) 0px 1px 1px 0px;
  --shadow-sm: oklch(0 0 0 / 0.08) 0px 1px 2px 0px;
  --shadow-md: oklch(0 0 0 / 0.08) 0px 2px 4px -1px, oklch(0 0 0 / 0.06) 0px 1px 2px;
  --shadow-lg: oklch(0 0 0 / 0.10) 0px 4px 8px -2px, oklch(0 0 0 / 0.06) 0px 2px 4px;
  --shadow-xl: oklch(0 0 0 / 0.12) 0px 8px 16px -4px, oklch(0 0 0 / 0.07) 0px 4px 8px -2px;

  /* Exxat L0 radius scale → rounded-1 … rounded-6 */
  --radius-1: var(--exxat-radius-1);
  --radius-2: var(--exxat-radius-2);
  --radius-3: var(--exxat-radius-3);
  --radius-4: var(--exxat-radius-4);
  --radius-5: var(--exxat-radius-5);
  --radius-6: var(--exxat-radius-6);
}

/* --------------------------------------------------------------------------
   Text size (Settings → Appearance)
   Pattern: root `font-size` steps like iOS Larger Text / Android font scale /
   browser zoom-at-root so rem-based components track together. We keep steps
   modest; `compact` keeps `--text-xs` / `--text-2xs` at the 12px floor and `--text-sm` / `--text-base` pinned when root rem scales down.
   -------------------------------------------------------------------------- */
html[data-text-size="compact"] {
  font-size: 94%;
  /* No --text-* overrides here: `@theme inline` inlines the scale into each
     utility, so redeclaring the variables at this level never reached them.
     The 12px / 14px floors now live in the `max()` calls on the scale itself. */
}
html[data-text-size="large"] {
  font-size: 112.5%;
}

/* --------------------------------------------------------------------------
   Shell density (Settings → Appearance → Shell layout → Compact)

   Tighter corners, everywhere. Squaring the shell chrome but leaving cards,
   buttons, chips, and menus on the default 8/12/16 makes the mismatch worse,
   not better — the flush edges read as the odd ones out. Compact halves both
   radius scales so the whole variant shares one language. Rebinding tokens
   rather than restyling components means every primitive follows, including
   ones added later, and `rounded-full` is untouched, so avatars stay round.

   On `html`, not on `SidebarShell`, because Radix portals menus, dialogs,
   popovers, and tooltips into `document.body`, outside the shell — a rounded
   dropdown over a squared surface is exactly the mismatch this removes. Set in
   `ShellLayoutProvider`, alongside `data-text-size` / `data-contrast`.

   Unlayered, and here rather than with the other compact rules in
   `@layer utilities`, because `--radius` is declared by the unlayered `:root`
   block below: unlayered CSS beats any layer regardless of specificity, so a
   layered override reaches the `@theme` scale but never `--radius` itself.
   -------------------------------------------------------------------------- */
html[data-shell-density="compact"] {
  /* The L0 scale is the only thing that has to move: both `rounded-1…6` and
     `rounded-sm…3xl` resolve through these six names. */
  --exxat-radius-1: 2px;
  --exxat-radius-2: 4px;
  --exxat-radius-3: 6px;
  --exxat-radius-4: 8px;
  --exxat-radius-5: 10px;
  --exxat-radius-6: 12px;

  /* Shadcn primitives that compute their own corners off the base. */
  --radius: 0.25rem;
}

/*
 * Controls keep their normal corners.
 *
 * The halved scale reads right on surfaces — cards, panels, menus — where the
 * corner is describing an edge. On a control it reads as damage: the shape is
 * small enough that 4px is nearly square, and a toolbar of near-square buttons
 * looks like unstyled controls rather than a denser variant. 8px is the size
 * they were drawn at.
 *
 * Buttons and fields move together. Splitting them puts an 8px button next to a
 * 4px input in the same filter bar, which is the same odd-one-out problem the
 * halved scale was introduced to avoid, only one level down.
 *
 * A rebind rather than a `border-radius` override, so one rule reaches every
 * corner a control draws: `rounded-md` at rest and grouped end caps both resolve
 * `var(--exxat-radius-2 / -3)` against the element,
 * and the element's own declaration beats the inherited compact one. Setting
 * `border-radius` would fix the base, miss the group, and flatten the group's
 * per-side rounding on the way past.
 *
 * The `group/button` hook is there because `data-slot` is not dependable on a
 * `Button`: with `asChild`, a trigger's props are merged into the child and
 * spread after the child's own, so `DialogTrigger`, `SheetTrigger`, and
 * `HoverCardTrigger` all replace `data-slot="button"` with their own name.
 * (`TooltipTrigger` and `DropdownMenuTrigger` used to blank it entirely; they
 * now omit the prop under `asChild`.) `group/button` comes from the
 * `buttonVariants` base and className is merged rather than replaced, so it
 * survives all of them.
 *
 * Controls only. Sidebar rows, menu items, and the other button-shaped things
 * that are not controls stay on the compact scale, because they sit inside
 * surfaces and take those surfaces' corners.
 */
html[data-shell-density="compact"]
  :is(
    [data-slot="button"],
    [class~="group/button"],
    [data-slot="toggle"],
    [data-slot="toggle-group"],
    [data-slot="toggle-group-item"],
    [data-slot="view-segmented-toolbar"],
    [data-slot="view-segmented-item"],
    [data-slot="input"],
    [data-slot="textarea"],
    [data-slot="select-trigger"],
    [data-slot="input-group"],
    /* Not controls: the Ask Leo halo and the wrapper carrying its arrival wash,
       both of which trace one. They are a sibling and an ancestor of the chip
       rather than children, so neither can inherit the corner the chip resolves —
       and a decoration that keeps the compact corner while the button takes the
       roomy one shows up as a square glow around a rounded button. Listing them
       here is what keeps all three in step at either density. */
    [data-ask-leo-utility-glow],
    [data-ask-leo-utility-glow-wrap]
  ) {
  --exxat-radius-2: 8px;
  --exxat-radius-3: 12px;
}

/*
 * Raised surfaces keep their normal corners too.
 *
 * Same argument as controls, one size up. The flush treatment is about the
 * shell — canvas, rail, panel, bar — meeting the viewport with no gutter and no
 * card. A `Card` is the opposite of that: it is a thing sitting *on* the canvas,
 * and its corner is what says so. Halved to 8px it stops reading as a raised
 * surface and starts reading as a rectangle someone forgot to style, which is
 * the one thing the flush shell needs it not to do.
 *
 * Dialogs and alert dialogs come along because they are the same object one
 * layer up: a panel raised off the page, differing only in whether a scrim sits
 * under it. Splitting them would mean a dialog full of cards where the dialog's
 * own corner is tighter than the cards inside it. `Sheet` is listed for the
 * same reason even though it renders flush today, so it lands correctly if it
 * ever grows a corner.
 *
 * `--exxat-radius-4` rather than `border-radius` so the `rounded-t-xl` /
 * `rounded-b-xl` on edge-to-edge media inside a card follows its parent, and so
 * per-side rounding anywhere in the family survives.
 */
html[data-shell-density="compact"]
  :is(
    [data-slot="card"],
    [data-slot="dialog-content"],
    [data-slot="alert-dialog-content"],
    [data-slot="sheet-content"]
  ) {
  --exxat-radius-4: 16px;
}

/*
 * Dialogs and sheets keep their normal corners.
 *
 * The same argument as the button, one scale up. Halved corners read as density
 * on chrome that meets an edge — canvas, rail, secondary panel — because
 * squaring those is what removes the boxed-in look. A dialog and a floating
 * sheet do the opposite: they are cards lifted off the page, and 8px across a
 * surface that large reads as an unfinished rectangle rather than a tighter one.
 * 16px is the radius they were drawn at.
 *
 * A rebind of `--exxat-radius-4` rather than a `border-radius` override,
 * because the corner is drawn in more than one place: `DialogContent`,
 * `AlertDialogContent`, and the floating sheet chrome carry `rounded-xl`, while
 * their footers carry `rounded-b-xl` on a nested element. Overriding
 * `border-radius` on the root alone would fix the top corners and leave the
 * footer clipped 8px inside them, so inheritance is the point here.
 *
 * `[data-sheet-content]` rather than `[data-slot="sheet-content"]`:
 * `FloatingSheetPanelContent` renames the slot to identify its drawer
 * (`export-drawer`, `table-properties-drawer`, …), so the slot is not a stable
 * hook. `sheet.tsx` stamps the marker after its prop spread, where a consumer
 * cannot displace it.
 */
html[data-shell-density="compact"]
  :is([data-slot="dialog-content"], [data-slot="alert-dialog-content"], [data-sheet-content]) {
  --exxat-radius-4: 16px;
}

/* ==========================================================================
   :root — Exxat One · Light Mode (brand hue 286.1)
   All contrast ratios verified against oklch(1 0 0) white background.
   ========================================================================== */
:root {
  /* font-size intentionally NOT overridden here.
     Shadcn components assume 1rem = 16px (browser default).
     Overriding to 87.5% / 14px breaks rem-based spacing on all devices,
     and compounds incorrectly at Windows 150% system zoom.
     If a compact 14px density is needed, apply it at the component/page level
     via a scoped class, not globally on :root. */
  /* Minimum readable UI copy: 12px (`text-xs` / `text-2xs`). Never ship visible copy below 12px. */

  /* ── Layout ─────────────────────────────────────────────────── */
  /* SiteHeader / breadcrumb height. Mirrored on the SidebarProvider for
     descendants; declared here too so JS that reads from documentElement
     (e.g. DataTable sticky-head offset) can resolve it. Prefer rem tokens;
     `getStickyTableHeaderOffset` resolves rem → px via root font-size.
     Keep in sync with `headerHeight` in components/sidebar-shell.tsx for the
     non-compact default (`3rem` / 48px). Compact overrides via
     `--shell-utility-bar-height`. */
  --header-height: 3rem;
  /* Compact utility bar chrome — single source for bar height, rail top, and
     sticky header math under `data-shell-density="compact"`. */
  --shell-utility-bar-height: 2.625rem;

  /* ── Typography ─────────────────────────────────────────────── */
  /* Ivy Presto loaded via Adobe Fonts Kit wuk5wqn (use.typekit.net) */
  --font-heading: "ivypresto-text";

  /* ── Brand — Exxat One (hue 286.1) ─────────────────────────── */
  /* Primary surface — requested token */
  --brand-tint:        oklch(0.9676 0.016 286.1);
  /* Lighter / darker washes (same hue) for nested UI & hovers */
  --brand-tint-light:  oklch(0.993 0.007 286.1);
  --brand-tint-subtle: oklch(0.935 0.024 286.1);
  /* Interactive accent + scale (readable on white; pairs with --brand-tint) */
  --brand-color:       oklch(0.50 0.14 286.1);
  --brand-color-light: oklch(0.78 0.09 286.1);
  --brand-color-dark:  oklch(0.38 0.11 286.1);
  --brand-color-deep:  oklch(0.28 0.085 286.1);
  --brand-foreground:  oklch(0.985 0 0);
  /* Brand as INK on the page canvas (links, accented labels). --brand-color is a
     fill: it assumes brand-foreground sits on top, so it stays mid-lightness in
     both modes and fails contrast as text once the canvas goes dark. This is the
     same split as --destructive (fill) vs --destructive-ink (text). */
  --brand-ink:         var(--brand-color);
  /* Fixed swatches for brand picker (Exxat One vs Prism), independent of active theme */
  --brand-preview-one:   var(--brand-tint);
  --brand-preview-prism: oklch(0.57 0.24 342);

  /**
   * Ask Leo panel tints (same mixes as the vertical wash). Use for blobs, cards, etc.
   * `--leo-surface-gradient` — full-screen Leo landing / search blobs only;
   * panel mode uses `--secondary-panel-bg` via `NestedSecondaryPanelShell`.
   */
  /* Mixed in oklab, not oklch: oklch interpolates hue on the polar arc, and
     `oklch(1 0 0)` carries hue 0, so a lavender brand at 286deg drifted toward
     360deg and these tints came out pinker than the brand. Oklab is the same
     perceptual space with rectangular coordinates, so the hue holds. */
  --leo-surface-tint-a: color-mix(in oklab, var(--brand-color) 4%, var(--background));
  --leo-surface-tint-b: color-mix(in oklab, var(--brand-color) 8%, var(--background));
  --leo-surface-gradient: linear-gradient(180deg, var(--leo-surface-tint-a) 0%, var(--leo-surface-tint-b) 100%);

  /* KeyMetrics — card tiles preferred; flat band has no glow. */
  --key-metrics-flat-cell-bg: transparent;
  --key-metrics-flat-divider: color-mix(in oklch, var(--sidebar-border) 55%, transparent);
  /* Kept for catalog-landing / legacy CSS that still references the token —
     value is transparent (no brand wash under KPI strips). */
  --key-metrics-flat-band-radial: none;
  --key-metrics-flat-band-shadow: none;
  --key-metrics-card-glow-radial: none;

  /* Ask Leo utility-bar glow: a halo *around* the launcher, never a wash across
     it. An outward `box-shadow` is what buys that — a non-inset shadow paints
     only outside the border box, so the chip's own face stays the bar's colour
     however bright the glow gets. A radial-gradient background cannot make that
     promise: it fills the element it sits on, and under a translucent chip it
     read as haze trapped inside the button.

     Three stops: a hairline that keeps the edge defined, a tight bloom, and a
     wide falloff that carries past the 42px bar without a visible cutoff. */
  --ask-leo-utility-glow-shadow:
    0 0 0 1px color-mix(in oklch, var(--brand-color) 26%, transparent),
    0 0 8px 0 color-mix(in oklch, var(--brand-color) 30%, transparent),
    0 0 20px 4px color-mix(in oklch, var(--brand-color) 16%, transparent);

  /* Specular highlight for the arrival sheen. On a light bar the read comes from
     saturation, not brightness: mixing the brand toward white here made the
     travelling arc lighter than the surface it crossed, so it disappeared. Near-
     full brand is what shows. The dark-mode value inverts this. */
  --ask-leo-sheen-color: color-mix(in oklch, var(--brand-color) 88%, transparent);

  /* The glow the chip keeps once the halo has played: light gathered behind the
     star and falling off across the label, rather than a flat tint edge to edge.
     A flat wash was the thing that read as a disabled control; a gradient with a
     centre reads as the same light source the halo comes from.

     Held at 14% at the centre so label ink still clears 4.5:1 on the bar. */
  --ask-leo-chip-glow: radial-gradient(
    120% 140% at 18% 50%,
    color-mix(in oklch, var(--brand-color) 14%, transparent) 0%,
    color-mix(in oklch, var(--brand-color) 7%, transparent) 55%,
    transparent 100%
  );

  /* How much of the Leo bar wash the launcher chip is allowed to show
     (`AskLeoLauncherWash`). The lobes themselves are the search bar's, painted
     at the opacities the composer uses, and the composer can afford them because
     an input surface sits between them and its text. On the chip they pass
     directly behind the label, so the layer is what holds the contrast budget:
     at 55% the lightest lobe (brand mixed halfway to white) leaves near-black
     label ink above 12:1 on the light bar. */
  --ask-leo-chip-blob-opacity: 0.55;

  /* ── Surfaces ────────────────────────────────────────────────── */
  --background:         oklch(1 0 0);
  --foreground:         oklch(0.145 0 0);           /* near-black — 17:1 on white ✓ */
  --card:               oklch(1 0 0);
  --card-foreground:    oklch(0.145 0 0);
  --popover:            oklch(1 0 0);
  --popover-foreground: oklch(0.145 0 0);

  /* ── Primary ─────────────────────────────────────────────────── */
  /* Default / ghost / solid primary actions — neutral charcoal (style guide) */
  --primary:            oklch(0.3457 0.0052 286.13);
  --primary-foreground: oklch(0.985 0 0);

  /* ── Auth primary ────────────────────────────────────────────────
     Sign-in is the one surface whose primary button belongs to Exxat rather
     than to the workspace: it is the front door, seen before any product is
     chosen, so it wears the corporate indigo instead of the neutral charcoal
     every in-app primary uses. Sampled from the production sign-in page
     (#4152b4), which this value reproduces exactly. White ink measures 6.8:1.

     A named pair rather than a class on the button, so the fork is one
     reviewable token instead of a literal repeated per step, and so the auth
     surface can remap `--primary` inside itself once and every primary in
     every step inherits it. See `[data-auth-surface]` below. */
  --auth-primary:            oklch(0.481 0.156 272);
  --auth-primary-foreground: oklch(1 0 0);
  /* Indigo as INK on the canvas — the sign-up links and the Forgot Password
     link, which the production page also renders in the blue family rather than
     in the workspace brand hue. Same fill/ink split as `--destructive` and
     `--brand-color`: the fill above assumes white sits on top, so used as text
     on a dark canvas it lands at 3.5:1 and fails 1.4.3. On white one value does
     both jobs (6.8:1 either way), so they only diverge in `.dark`. */
  --auth-ink:                var(--auth-primary);

  /* The promo slide's canvas: the pastel wash the production sign-in rail sits
     on, which the slide artwork is drawn to sit *on top of* rather than to
     replace. Values are the reference's own stops (Tailwind `yellow-100`,
     `pink-100`, `pink-200`), named here so the rail reads a token instead of
     three palette utilities the token lint would (rightly) reject.

     One token for the whole gradient rather than three colour stops: the stops
     are meaningless apart, and registering three theme colours to spell one
     background invites them being reused as if they were semantic.

     Theme-invariant on purpose. This is printed artwork, not chrome — the slide
     is the same picture in dark mode, the way an illustration or a photograph
     is, and re-mixing it per theme would leave the vector's own colours sitting
     on a canvas that no longer matches them. */
  --auth-promo-canvas: linear-gradient(
    to bottom right,
    oklch(0.973 0.071 103.193),
    oklch(0.948 0.028 342.258),
    oklch(0.899 0.061 343.231)
  );

  /* ── Secondary / Muted / Accent ──────────────────────────────── */
  --secondary:             oklch(0.95 0.0058 264.53);
  --secondary-foreground:  oklch(0.082 0 0);
  --muted:                 oklch(0.945 0.002 270);
  --muted-foreground:      oklch(0.40 0.014 270);   /* ≥4.5:1 on white/muted/accent — darker than prior 0.46 for readable helper copy ✓ */
  --accent:                oklch(0.925 0.005 260);
  --accent-foreground:     oklch(0.082 0 0);

  /* ── Destructive ─────────────────────────────────────────────── */
  /* Two jobs, two tokens, because in dark mode they pull opposite ways:
     `--destructive` is a *fill* and has to stay dark enough for white ink,
     while `--destructive-ink` is red *text* and has to stay light enough to
     read on the canvas behind it. On white, one value does both (5.4:1 either
     way), so they only diverge in `.dark`. */
  --destructive:            oklch(0.55 0.22 25);
  --destructive-foreground: oklch(1 0 0);
  --destructive-ink:        var(--destructive);
  --destructive-subtle-foreground: var(--destructive);

  /* ── Inverse slab ────────────────────────────────────────────────
     A band that has to be impossible to miss and has to host accents of any
     hue: the bulk selection bar puts a red Delete next to outline actions.
     Bound to the canvas pair rather than fixed, so it inverts with the theme
     and always sits at maximum contrast against whatever is behind it. Unlike
     `--primary`, which also inverts, the ink here inverts *with* the surface,
     so anything placed on it stays legible in both modes. */
  --slab:            var(--foreground);
  --slab-foreground: var(--background);
  /* Luminance alone barely separates the slab from a dark canvas, so it carries
     an edge rather than relying on the fill. */
  --slab-border: color-mix(in oklab, var(--slab) 82%, var(--slab-foreground));

  /* ── Borders ─────────────────────────────────────────────────── */
  /* Decorative: cards, dividers — no AA contrast requirement        */
  --border: oklch(0.92 0.002 270);

  /* Form-field boundary — WCAG 1.4.11 requires 3:1 against bg.
     #8E9095 → oklch(0.6533 0.0077 268.51) — lighter neutral field outline. */
  --border-control:    oklch(0.86 0.004 268.51); /* subtle (layout use only) */
  --border-control-3:  oklch(0.6533 0.0077 268.51); /* default field border */
  --border-control-35: oklch(0.6533 0.0077 268.51); /* same rung — inputs, chips */
  --control-border:    var(--border-control-3);  /* default form-field border */

  /* ── Input ───────────────────────────────────────────────────── */
  /* Field outline — matches --border-control-3 (#8E9095 → OKLCH). */
  --input:            oklch(0.6533 0.0077 268.51);
  --input-background: oklch(0.97 0.002 270);

  /* ── Focus ring ──────────────────────────────────────────────── */
  /* 3:1+ contrast on both light & dark backgrounds (WCAG 2.4.11)  */
  --ring: oklch(0.25 0 0);

  /* ── Charts ──────────────────────────────────────────────────── */
  --chart-1: oklch(0.55 0.22 264.116);
  --chart-2: oklch(0.48 0.15 184.704);
  --chart-3: oklch(0.32 0.08 227.392);
  --chart-4: oklch(0.65 0.18 84.429);
  --chart-5: oklch(0.58 0.18 70.08);

  /* ── Chip / Badge ────────────────────────────────────────────── */
  /* AA-compliant on white: all ≥ 4.5:1 (WCAG 1.4.3)              */
  --chip-1:           oklch(0.38 0.18 264);   /* indigo   */
  --chip-2:           oklch(0.35 0.14 184);   /* teal     */
  --chip-3:           oklch(0.32 0.08 227);   /* slate    */
  --chip-4:           oklch(0.42 0.12 84);    /* amber    */
  --chip-5:           oklch(0.42 0.14 70);    /* orange   */
  --chip-destructive: oklch(0.40 0.18 25);    /* red      */

  /* ── Semantic status badges (StatusBadge / StatusCell) ───────── */
  /* Locked lightness so every tone has equal visual weight:
       fill L ≈ 0.95, text L ≈ 0.385 — only hue + chroma shift.
     Map domain labels onto success / info / warning / danger / neutral
     (enterprise DS cadence: success · info · caution · danger · neutral).
     Draft/neutral uses chroma 0 (true grey, no warm carryover). */
  --status-badge-success-fill: oklch(0.952 0.029 126);
  --status-badge-success-fg:   oklch(0.386 0.107 135.9);
  --status-badge-info-fill:    oklch(0.953 0.018 245.4);
  --status-badge-info-fg:      oklch(0.385 0.110 253.2);
  --status-badge-warning-fill: oklch(0.953 0.029 80.7);
  --status-badge-warning-fg:   oklch(0.386 0.084 62.5);
  --status-badge-danger-fill:  oklch(0.954 0.019 17.5);
  --status-badge-danger-fg:    oklch(0.387 0.124 25.1);
  --status-badge-neutral-fill: oklch(0.952 0 0);
  --status-badge-neutral-fg:   oklch(0.386 0 0);

  /* ── Prism promo banner ──────────────────────────────────────── */
  /* Rose hue 343 — used universally regardless of active theme    */
  --banner-prism-bg: oklch(0.97 0.02 343);

  /* ── Sidebar — Exxat One brand tint ───────────────────────────── */
  --sidebar:                    var(--brand-tint);
  --sidebar-foreground:         oklch(0.145 0 0);
  --sidebar-primary:            oklch(0.082 0 0);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent:             oklch(0.945 0.025 286.1);
  --sidebar-accent-foreground:  oklch(0.3457 0.0052 286.13);
  --sidebar-border:             oklch(0.92 0.025 286.1);
  --sidebar-ring:               oklch(0.25 0 0);
  /* Nav section titles — ≥4.5:1 vs --sidebar (not vs page white) */
  --sidebar-section-label-foreground: color-mix(in oklch, var(--sidebar-foreground) 72%, var(--sidebar));
  /* Nested secondary rail — elevation 1: brand wash, lighter than `--sidebar` / `--brand-tint`. */
  --secondary-panel-bg:         color-mix(in oklch, var(--background) 40%, var(--brand-tint-light) 60%);

  /* Browser UI (meta theme-color) — aligned with --brand-tint */
  --theme-color-chrome: oklch(0.9635 0.0071 295.8);

  /* ── Border radius ───────────────────────────────────────────── */
  --radius: 0.5rem; /* 8px base */

  /* ── Density / touch targets ─────────────────────────────────── */
  --scaling:              1;
  --control-height:       calc(40px * var(--scaling));
  --control-height-sm:    calc(32px * var(--scaling));
  /* WCAG 2.5.5 — minimum 44×44 px touch target (mobile only)     */
  --control-height-touch: 44px;
  --control-padding-y:    calc(8px  * var(--scaling));
  --control-padding-x:    calc(12px * var(--scaling));
  --table-row-height:     calc(48px * var(--scaling));

  /* ── Interactive hover (ties to --muted / --accent — theme overrides apply) ─ */
  --interactive-hover:            var(--muted);
  --interactive-hover-foreground: var(--foreground);
  --interactive-hover-subtle:     color-mix(in oklch, var(--muted) 50%, var(--background));
  --interactive-hover-soft:       color-mix(in oklch, var(--muted) 40%, var(--background));
  --interactive-hover-medium:     color-mix(in oklch, var(--muted) 60%, var(--background));
  --interactive-hover-strong:     color-mix(in oklch, var(--muted) 70%, var(--background));
  --interactive-hover-row:        color-mix(in oklch, var(--accent) 50%, var(--background));

  /* Icon + secondary chrome — ≥4.5:1 on --background / --card (slightly darker than --muted-foreground) */
  --icon-button-foreground: color-mix(in oklch, var(--foreground) 68%, var(--background));
  --secondary-action-foreground: var(--icon-button-foreground);
  --placeholder-foreground: color-mix(in oklch, var(--foreground) 58%, var(--background));
  /* Sidebar / Ask Leo rail — mixed on --sidebar (not opacity /70 hacks) */
  --icon-button-foreground-on-sidebar: color-mix(in oklch, var(--sidebar-foreground) 82%, var(--sidebar));
  /* Library / Tokens nested rail — mixed on --secondary-panel-bg (brand-tinted wash) */
  --icon-button-foreground-on-secondary-panel: color-mix(in oklch, var(--foreground) 82%, var(--secondary-panel-bg));

  /* ── DataTable — opaque row/cell surfaces (pinned cells MUST be opaque) ── */
  --dt-row-bg:             var(--background);
  --dt-row-hover:          oklch(0.972 0.001 270);
  --dt-row-selected:       oklch(0.962 0.003 260);
  --dt-row-selected-fg:    var(--foreground);
  --dt-header-bg:          oklch(0.972 0.001 270);
  /* Group divider neutral fill — light grey when no semantic tone. Status /
     badge groups override inline with --status-badge-*-fill (same as the chip). */
  --dt-group-bg:           oklch(0.972 0.001 270);
  /* Open-row marker: the leading bar on the row a detail rail is describing.
     Brand ink, not a row fill, because "open" and "selected" are different
     answers and must not look like the same one. Selection tints the whole row
     (it feeds bulk actions); open marks one edge (it answers "which one am I
     reading?"), so a row can be both at once and still be legible. */
  --dt-row-open-marker:    var(--brand-ink);
  /* New-row signal: the washed-out blue of the Info banner. Both the row tint
     and the TableNewRowDot come off --chip-1 so "new" reads as one signal in
     the same family the Info LocalBanner already uses.

     Mixed `in srgb`, never `in oklch`: oklch interpolates hue on the polar arc
     and `oklch(1 0 0)` carries hue 0, so mixing a blue into white swung 264deg
     toward 360deg and this wash rendered pink (#fef3f7) for as long as it used
     an oklch mix. sRGB also matches how the browser composites the banner's
     `bg-chip-1/5`, so the two surfaces stay in the same family.

     Mix stays at the banner's 5%: the TableNewRowDot (and optional leading
     gutter) carries the "new" signal so the wash can stay soft. A heavier
     mix (8%+) reads as a selected/filled band rather than a hint. */
  --dt-new-row-dot:        var(--chip-1);
  --dt-new-row-bg:         color-mix(in srgb, var(--chip-1) 5%, var(--background));
  --dt-new-row-border:     transparent;

  /* ── Transitions ─────────────────────────────────────────────── */
  --transition-fast:   0.15s ease;
  --transition-normal: 0.25s ease-in-out;
  --transition-colors: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;

  /* ── Shadows ─────────────────────────────────────────────────── */
  /* The shadow-xs … shadow-xl ramp lives in the `@theme inline` block above so
     the Tailwind utilities actually emit it. Only the purpose-built sheet
     shadows below belong here, because they are read as `var(--shadow-*)`. */
  /* Nav flyouts (primary + secondary) and floating product sheets (Export, Properties, …) */
  --shadow-sheet-flyout: oklch(0 0 0 / 0.11) 0px 4px 14px -2px, oklch(0 0 0 / 0.07) 0px 2px 6px;
  --shadow-sheet-panel: oklch(0 0 0 / 0.14) 0px 14px 32px -8px, oklch(0 0 0 / 0.09) 0px 6px 14px -4px;

  /* ── Sticky column edge fade (data tables) ───────────────────── */
  --sticky-edge-fade: oklch(0 0 0 / 0.08);

  /* ── Overlay scrim (Sheet / Drawer / Dialog) ─────────────────── */
  /* Softer than raw bg-black/10; follows foreground hue (not pure black) */
  --overlay: color-mix(in oklch, var(--foreground) 5%, transparent);

  /* ── Avatar initials (table) ───────────────────────────────────── */
  /* Soft brand wash + brand ink — no black fills; ≥4.5:1 on white rows */
  --avatar-initials-bg: color-mix(in oklch, var(--brand-color) 14%, oklch(1 0 0));
  --avatar-initials-fg: var(--brand-color-dark);

  /* ── KPI insight severity (KeyMetrics) ────────────────────────── */
  --insight-severity-warning-bg: color-mix(in oklch, var(--chart-4) 15%, transparent);
  --insight-severity-warning-fg: color-mix(in oklch, var(--chart-4) 75%, var(--foreground));
  --insight-severity-info-bg: color-mix(in oklch, var(--chart-1) 14%, transparent);
  --insight-severity-info-fg: color-mix(in oklch, var(--chart-1) 75%, var(--foreground));

  /* ── Tinted icon discs — soft wash (≈14% tint like legacy inline styles); contrast from --chip-* / brand-dark on fg */
  --icon-disc-chart-2-bg: color-mix(in oklch, var(--chart-2) 14%, transparent);
  --icon-disc-chart-2-fg: var(--chip-2);
  --icon-disc-chart-4-bg: color-mix(in oklch, var(--chart-4) 14%, transparent);
  --icon-disc-chart-4-fg: var(--chip-4);
  --icon-disc-brand-bg: color-mix(in oklch, var(--brand-color) 12%, transparent);
  --icon-disc-brand-fg: var(--brand-color-dark);
  --icon-disc-danger-bg: color-mix(in oklch, var(--destructive) 14%, transparent);
  --icon-disc-danger-fg: var(--chip-destructive);

  /* ── Conditional formatting rule backgrounds (table drawer) ─── */
  --conditional-rule-green: color-mix(in oklch, var(--chart-2) 22%, transparent);
  --conditional-rule-yellow: color-mix(in oklch, var(--chart-4) 30%, transparent);
  --conditional-rule-blue: color-mix(in oklch, var(--chart-1) 22%, transparent);
  --conditional-rule-red: color-mix(in oklch, var(--destructive) 22%, transparent);
  --conditional-rule-purple: color-mix(in oklch, var(--brand-color) 22%, transparent);
  --conditional-rule-orange: color-mix(in oklch, var(--chart-5) 25%, transparent);

  /* ── Switch (ToggleSwitch) — "on" track ──────────────────────── */
  /* iOS-style affirmative green so the on/off semantic reads at a glance.
     Knob keeps --primary-foreground (white in light mode) for ≥3:1 contrast
     against the green track per WCAG 1.4.11. The high-contrast mode override
     in the [data-contrast="high"] block continues to use --accent — green
     stays constant across brand themes (Prism rose / One lavender) since it
     encodes "on", not brand identity. */
  --switch-on: oklch(0.62 0.16 150);

  /* ============================================================
     Exxat L0 — canonical namespace (additive aliases)
     ------------------------------------------------------------
     SLDS-style flat namespace. These are the official names the
     DS scales on — components SHOULD migrate to these over time.
     Existing shadcn-style names above are L1 aliases that resolve
     to the same values. The L0 names use var(...) so they inherit
     every theme override (.dark / .theme-one / .theme-prism /
     [data-contrast="high"]) automatically — no per-theme block
     needed.
     Reference: apps/web/docs/token-taxonomy.md (§ L0 Exxat).
     ============================================================ */

  /* ── Surfaces (canvas → muted → input) ──────────────────────── */
  --exxat-color-surface-1:         var(--background);
  --exxat-color-surface-2:         var(--card);
  --exxat-color-surface-3:         var(--popover);
  --exxat-color-surface-muted:     var(--muted);
  --exxat-color-surface-accent:    var(--accent);
  --exxat-color-surface-secondary: var(--secondary);
  --exxat-color-surface-sidebar:   var(--sidebar);
  --exxat-color-surface-input:     var(--input-background);
  /* Inverts with the theme, unlike every surface above it. */
  --exxat-color-surface-slab:      var(--slab);

  /* ── Ink (foreground / on-surface text) ─────────────────────── */
  --exxat-color-ink-1:              var(--foreground);
  --exxat-color-ink-2:              var(--muted-foreground);
  --exxat-color-icon-button-1:      var(--icon-button-foreground);
  --exxat-color-icon-button-sidebar: var(--icon-button-foreground-on-sidebar);
  --exxat-color-icon-button-secondary-panel: var(--icon-button-foreground-on-secondary-panel);
  --exxat-color-ink-on-surface-2:   var(--card-foreground);
  --exxat-color-ink-on-surface-3:   var(--popover-foreground);
  --exxat-color-ink-on-brand:       var(--brand-foreground);
  --exxat-color-ink-on-primary:     var(--primary-foreground);
  --exxat-color-ink-on-secondary:   var(--secondary-foreground);
  --exxat-color-ink-on-accent:      var(--accent-foreground);
  --exxat-color-ink-on-destructive: var(--destructive-foreground);
  /* Danger as *text*, not as a fill. Distinct from `-on-destructive`, which is
     the ink you put on a red button. */
  --exxat-color-ink-danger:         var(--destructive-ink);
  /* Brand as *text*, the same split as danger above: `-on-brand` is the ink you
     put on a brand fill, this is brand used as ink on the canvas. */
  --exxat-color-ink-brand:          var(--brand-ink);
  --exxat-color-ink-on-slab:        var(--slab-foreground);

  /* ── Brand (interactive accent + scale) ─────────────────────── */
  --exxat-color-brand-1:        var(--brand-color);
  --exxat-color-brand-2:        var(--brand-color-dark);
  --exxat-color-brand-3:        var(--brand-color-light);
  --exxat-color-brand-deep:     var(--brand-color-deep);
  --exxat-color-brand-tint-1:   var(--brand-tint);
  --exxat-color-brand-tint-2:   var(--brand-tint-subtle);
  --exxat-color-brand-tint-3:   var(--brand-tint-light);

  /* ── Wordmark ink (Exxat product logo SVG + HTML prefix) ─────
     Brand-frozen slate/cool-grey pair used by the "Exxat" SVG
     letters and the HTML wordmark prefix. Held outside the
     `--foreground` family because the wordmark stays at brand
     ink even when the rest of the surface inverts. Replaces
     the bare light/dark wordmark literals in
     `apps/web/components/exxat-product-logo.tsx` and
     `product-wordmark.tsx`. */
  --exxat-color-wordmark-ink-light: oklch(0.301 0.022 252);
  --exxat-color-wordmark-ink-dark:  oklch(0.755 0.012 230);

  /* IvyPresto product suffix — WCAG AA on `--sidebar` / `--brand-tint` (≥4.5:1).
     Matches EXXAT_LOGO_PINK_SHADOW (#BE1E6D); custom products override via
     `--brand-color-deep` in `.theme-custom`. */
  --product-wordmark-suffix: #BE1E6D;

  /* ── Action surfaces ────────────────────────────────────────── */
  --exxat-color-action-primary:     var(--primary);
  --exxat-color-action-secondary:   var(--secondary);
  --exxat-color-action-destructive: var(--destructive);

  /* ── Borders (decorative + form-field ladder) ──────────────── */
  --exxat-color-border-1:              var(--border);
  --exxat-color-border-control-subtle: var(--border-control);
  --exxat-color-border-control-1:      var(--border-control-3);
  --exxat-color-border-control-2:      var(--border-control-35);

  /* ── Focus + overlay ────────────────────────────────────────── */
  --exxat-color-focus-ring:  var(--ring);
  --exxat-color-overlay:     var(--overlay);

  /* ── Chart slots ────────────────────────────────────────────── */
  --exxat-color-chart-1: var(--chart-1);
  --exxat-color-chart-2: var(--chart-2);
  --exxat-color-chart-3: var(--chart-3);
  --exxat-color-chart-4: var(--chart-4);
  --exxat-color-chart-5: var(--chart-5);

  /* ── Chip / badge slots ─────────────────────────────────────── */
  --exxat-color-chip-1:           var(--chip-1);
  --exxat-color-chip-2:           var(--chip-2);
  --exxat-color-chip-3:           var(--chip-3);
  --exxat-color-chip-4:           var(--chip-4);
  --exxat-color-chip-5:           var(--chip-5);
  --exxat-color-chip-destructive: var(--chip-destructive);

  /* Semantic status badge pairs (StatusBadge / StatusCell) */
  --exxat-color-status-badge-success-fill: var(--status-badge-success-fill);
  --exxat-color-status-badge-success-fg:   var(--status-badge-success-fg);
  --exxat-color-status-badge-info-fill:    var(--status-badge-info-fill);
  --exxat-color-status-badge-info-fg:      var(--status-badge-info-fg);
  --exxat-color-status-badge-warning-fill: var(--status-badge-warning-fill);
  --exxat-color-status-badge-warning-fg:   var(--status-badge-warning-fg);
  --exxat-color-status-badge-danger-fill:  var(--status-badge-danger-fill);
  --exxat-color-status-badge-danger-fg:    var(--status-badge-danger-fg);
  --exxat-color-status-badge-neutral-fill: var(--status-badge-neutral-fill);
  --exxat-color-status-badge-neutral-fg:   var(--status-badge-neutral-fg);

  /* ── Radius scale ──────────────────────────────────────────── */
  --exxat-radius-1: 4px;
  --exxat-radius-2: 8px;
  --exxat-radius-3: 12px;
  --exxat-radius-4: 16px;
  --exxat-radius-5: 20px;
  --exxat-radius-6: 24px;

  /* ── Spacing scale ─────────────────────────────────────────── */
  --exxat-spacing-1:  0.25rem;
  --exxat-spacing-2:  0.5rem;
  --exxat-spacing-3:  0.75rem;
  --exxat-spacing-4:  1rem;
  --exxat-spacing-5:  1.25rem;
  --exxat-spacing-6:  1.5rem;
  --exxat-spacing-8:  2rem;
  --exxat-spacing-12: 3rem;

  /* ── Sidebar / outline tree vertical guides ─────────────────── */
  /* Primary nav: px-2 row padding + half of size-4 icon column. */
  --sidebar-tree-guide-x: var(--exxat-spacing-4);
  /* Half of min-h-7 (1.75rem) primary row — line meets icon vertical center. */
  --sidebar-tree-guide-overhang: 0.875rem;
  /* Folder tree: px-2 + half of size-6 chevron hit target. */
  --outline-tree-guide-x: calc(var(--outline-tree-row-padding-x) + var(--outline-tree-chevron-half));
  --outline-tree-guide-overhang: var(--outline-tree-chevron-half);
  /* Per nesting level — flat sub-lists; indent lives on the row only. */
  --outline-tree-row-padding-x: var(--exxat-spacing-2);
  --outline-tree-depth-step: var(--exxat-spacing-3);
  --outline-tree-chevron-half: 0.75rem;

  /* ── Control height scale ──────────────────────────────────── */
  --exxat-control-height-1: var(--control-height-sm);
  --exxat-control-height-2: var(--control-height);
  --exxat-control-height-3: var(--control-height-touch);
}

/* ==========================================================================
   Dark Mode — Exxat One base (neutral surfaces; brand uses --brand-*)
   ========================================================================== */
.dark {
  --sticky-edge-fade: oklch(0 0 0 / 0.32);

  /* Brand-tint scale for dark mode — same hue family as :root but at dark-mode
     lightness so derived surfaces (`--secondary-panel-bg`, `--sidebar` fallback
     via `var(--brand-tint)`, etc.) read as dark brand surfaces, not pale
     pastel washes. Themed `.dark` variants (`.theme-one.dark`,
     `.theme-prism.dark`, …) override these with their own hue.

     Chroma note: at L≈0.27 the OKLCH hue-discrimination threshold is much
     higher than at L≈0.97 (Helmholtz–Kohlrausch). The PRIMARY brand surface
     (`--brand-tint`) needs ~3–4× the chroma of its light-mode counterpart so
     different products read as visibly different in dark mode. The lighter /
     subtler scale steps stay low-chroma — they're elevation hints, not the
     brand carrier. */
  --brand-tint:         oklch(0.30 0.05 286.1);
  --brand-tint-light:   oklch(0.34 0.04 286.1);
  --brand-tint-subtle:  oklch(0.26 0.025 286.1);

  /* Brand ink lifts to the light step of the ramp so accented text clears AA on
     the dark canvas (8.5:1+ for every product hue). Resolved per element, so a
     themed `.dark` block that redefines --brand-color-light gets its own hue for
     free. Fills keep --brand-color: they carry --brand-foreground, not canvas
     ink, so lifting them would wash out the brand. */
  --brand-ink:          var(--brand-color-light);

  /* Canvas — charcoal grey with a perceptible whisper of brand chroma (still
     ≥ 12:1 against --foreground). Was 0.008 — below the OKLCH hue-perception
     threshold at L 0.20, so the canvas read as dead-neutral grey. 0.014 ties
     the canvas to the lavender-blue family (286.1), not cyan-green (270). */
  --background:         oklch(0.20 0.014 286.1);
  --foreground:         oklch(0.985 0 0);
  /* Dark surface elevation ladder (see commentary at line ~605 for the full
     stack across themed blocks):
       --background          L=0.20    canvas
       --secondary-panel-bg  L=0.22    nested chrome (between sidebar & canvas)
       --card                L=0.225   subtle inline elevation
       --sidebar /input-bg   L=0.245   outer chrome
       --popover             L=0.275   floating menus / dropdowns

     Card stays at +0.025 above canvas so it reads as the same surface family.
     Popover lifts further (+0.075) so it visibly floats above the canvas it
     covers — a too-close popover blends into the page and a dropdown loses
     its boundary. */
  --card:               oklch(0.225 0.017 286.1);
  --card-foreground:    oklch(0.985 0 0);
  --popover:            oklch(0.275 0.022 286.1);
  --popover-foreground: oklch(0.985 0 0);
  --primary:            oklch(0.985 0 0);
  --primary-foreground: oklch(0.3457 0.0052 286.13);
  /* Lifted off the light value rather than reused. At L 0.481 the indigo fill
     reaches only 2.9:1 against this canvas, so the button's own edge falls under
     the 3:1 floor for a graphical object (1.4.11) and it reads as a dark smudge.
     L 0.52 measures 3.5:1 on the canvas and still 5.7:1 under white ink, so both
     the boundary and the label clear their thresholds. */
  --auth-primary:            oklch(0.52 0.15 272);
  --auth-primary-foreground: oklch(1 0 0);
  /* Lifted well past the fill: as text this has to clear 4.5:1 on both the deep
     canvas and the lighter card behind it. L 0.64 measures 5.8:1 and 5.2:1. */
  --auth-ink:                oklch(0.64 0.14 272);
  --secondary:          oklch(0.31 0.04 286.1);
  --secondary-foreground: oklch(0.985 0 0);
  --muted:              oklch(0.31 0.04 286.1);
  /* Helper copy + bg-muted labels — lifted from --muted so contrast holds on
     both the canvas (--background) and muted chips (AvatarFallback, Kbd, badges).
     Fixed L=0.72 failed the bg-muted + text-muted-foreground pairing app-wide. */
  --muted-foreground:   oklch(from var(--muted) calc(l + 0.50) calc(c * 0.30) h);
  --accent:             oklch(0.33 0.06 286.1);
  --accent-foreground:  oklch(0.985 0 0);
  /* Fill: white ink reaches 5.38:1 on this red, and the red itself clears the
     3:1 non-text floor against the canvas (3.37:1) and a card (3.19:1). The
     previous value brightened the red to L 0.65 to serve red *text* as well, at
     which point white ink only reached 3.56:1 and the fill had to take near-black
     instead, which is why a Delete button read black on red in dark mode. Red
     text is now `--destructive-ink`, so this value answers only to white. */
  --destructive:        oklch(0.55 0.20 25);
  --destructive-foreground: oklch(1 0 0);
  /* Red text on a dark surface: 6.8:1 on the canvas, 6.4:1 on a card. */
  --destructive-ink:    oklch(0.72 0.17 25);
  /* Text on destructive/20 button wash — ≥4.5:1 vs mixed dark tint */
  --destructive-subtle-foreground: oklch(0.93 0.045 25);

  /* KeyMetrics — no glow (matches light theme). */
  --key-metrics-flat-cell-bg: transparent;
  --key-metrics-flat-divider: color-mix(in oklch, var(--sidebar-border) 55%, transparent);
  --key-metrics-flat-band-radial: none;
  --key-metrics-flat-band-shadow: none;
  --key-metrics-card-glow-radial: none;

  /* Ask Leo utility-bar glow: same halo, carried a little further. Light emitted
     onto a dark surface reads dimmer than the same mix on a light one, so the
     bloom stops go up rather than the hairline, which is already legible. */
  --ask-leo-utility-glow-shadow:
    0 0 0 1px color-mix(in oklch, var(--brand-color) 30%, transparent),
    0 0 8px 0 color-mix(in oklch, var(--brand-color) 38%, transparent),
    0 0 22px 5px color-mix(in oklch, var(--brand-color) 22%, transparent);

  /* Specular highlight, inverted from light mode: against a dark bar the arc has
     to be brighter than the surface, so the brand is lifted toward white. Pure
     white read as a chrome spinner rather than the brand catching light. */
  --ask-leo-sheen-color: color-mix(in oklch, oklch(1 0 0) 42%, var(--brand-color));

  /* Resting chip glow, carried further for the same reason the halo is: emitted
     light on a dark bar reads dimmer than the same mix on a light one. */
  --ask-leo-chip-glow: radial-gradient(
    120% 140% at 18% 50%,
    color-mix(in oklch, var(--brand-color) 22%, transparent) 0%,
    color-mix(in oklch, var(--brand-color) 11%, transparent) 55%,
    transparent 100%
  );

  /* Held back rather than carried further, which is the opposite of what the halo
     and the resting glow do — and for the same reason. Those are brand mixed
     toward transparent over a dark bar, so more of them is more visible. The bar
     lobes are brand mixed toward *black* in dark mode, which lands them near the
     lightness of light-mode label ink; the label above them is near-white. 50%
     keeps the composite dark enough that white ink stays past 7:1. */
  --ask-leo-chip-blob-opacity: 0.5;

  /* Borders — visible but not washed out on dark surfaces */
  --border:            oklch(0.38 0.010 286.1);
  --border-control:    oklch(0.76 0.008 286.1);
  --border-control-3:  oklch(0.82 0.008 286.1);
  --border-control-35: oklch(0.82 0.008 286.1);
  --control-border:    var(--border-control-3);

  --input:             oklch(0.82 0.008 286.1);
  --input-background:  oklch(0.212 0.009 286.1);

  /* Focus ring — 3:1+ on dark bg */
  --ring: oklch(0.85 0 0);

  /* Switch "on" track — slightly lighter L so the green still reads as
     vibrant against the dark canvas; knob picks up --primary-foreground
     (charcoal) for clear knob/track separation in dark mode. */
  --switch-on: oklch(0.66 0.17 150);

  /* Charts — higher lightness for dark surfaces */
  --chart-1: oklch(0.70 0.155 264.376); /* 0.155 is the sRGB chroma ceiling at this L/H */
  --chart-2: oklch(0.75 0.15 162.48);
  --chart-3: oklch(0.78 0.12 227.392);
  --chart-4: oklch(0.80 0.18 84.429);
  --chart-5: oklch(0.75 0.18 70.08);

  /* Chips — AA-compliant on dark bg (L raised) */
  --chip-1:           oklch(0.72 0.18 264);
  --chip-2:           oklch(0.72 0.14 184);
  --chip-3:           oklch(0.78 0.12 227);
  --chip-4:           oklch(0.78 0.14 84);
  --chip-5:           oklch(0.78 0.16 70);
  --chip-destructive: oklch(0.72 0.18 25);

  /* Status badges — same hues; invert L (fill ≈0.28, fg ≈0.86) for equal weight on dark */
  --status-badge-success-fill: oklch(0.28 0.04 126);
  --status-badge-success-fg:   oklch(0.86 0.09 135.9);
  --status-badge-info-fill:    oklch(0.28 0.035 245.4);
  --status-badge-info-fg:      oklch(0.86 0.09 253.2);
  --status-badge-warning-fill: oklch(0.28 0.04 80.7);
  --status-badge-warning-fg:   oklch(0.86 0.08 62.5);
  --status-badge-danger-fill:  oklch(0.28 0.04 17.5);
  --status-badge-danger-fg:    oklch(0.86 0.1 25.1);
  --status-badge-neutral-fill: oklch(0.28 0 0);
  --status-badge-neutral-fg:   oklch(0.86 0 0);

  /* Tinted icon discs — same soft transparency wash; fg stays readable on dark canvas */
  --icon-disc-chart-2-bg: color-mix(in oklch, var(--chart-2) 14%, transparent);
  --icon-disc-chart-2-fg: color-mix(in oklch, var(--foreground) 10%, var(--chart-2));
  --icon-disc-chart-4-bg: color-mix(in oklch, var(--chart-4) 14%, transparent);
  --icon-disc-chart-4-fg: color-mix(in oklch, var(--foreground) 10%, var(--chart-4));
  --icon-disc-brand-bg: color-mix(in oklch, var(--brand-color) 12%, transparent);
  --icon-disc-brand-fg: color-mix(in oklch, var(--foreground) 8%, var(--brand-color));
  --icon-disc-danger-bg: color-mix(in oklch, var(--destructive) 14%, transparent);
  --icon-disc-danger-fg: color-mix(in oklch, var(--foreground) 8%, var(--destructive));

  /* Sidebar — same chroma-bump principle as `--brand-tint`: at L≈0.245 the
     OKLCH hue-perception threshold is ~0.04, so 0.015 read as dead grey
     regardless of brand. 0.04 reads as the brand without competing with the
     content panel. `.theme-*.dark` blocks restate this scale at their hue. */
  --sidebar:                    oklch(0.245 0.04 286.1);
  --sidebar-foreground:         oklch(0.985 0 0);
  --sidebar-primary:            oklch(0.488 0.243 264.376);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent:             oklch(0.30 0.035 286.1);
  --sidebar-accent-foreground:  oklch(0.985 0 0);
  --sidebar-border:             oklch(0.38 0.025 286.1);
  --sidebar-ring:               oklch(0.85 0 0);
  /* Nav section titles — ≥4.5:1 vs --sidebar (12px labels; dark sidebar is low-L) */
  --sidebar-section-label-foreground: color-mix(in oklch, var(--sidebar-foreground) 76%, var(--sidebar));
  /* Lighter mark-gradient end — readable on dark `--sidebar` (L≈0.245). */
  --product-wordmark-suffix: #EF609D;
  /* Nested secondary rail — nested chrome wedged between the outer sidebar
     (L=0.245) and the page canvas (L=0.20). Previously this resolved to
     `var(--brand-tint)` at L=0.30, which made the panel *brighter* than the
     sidebar — it popped out instead of nestling in. Now L=0.22 places it just
     above canvas with a whisper of brand chroma; each themed dark block below
     overrides with the brand hue so it still reads as Exxat One vs Prism vs
     Assessment vs custom. */
  --secondary-panel-bg:         oklch(0.22 0.025 286.1);
  --theme-color-chrome:         oklch(0.2950 0.0143 286.1);

  /* Lifted scrim on dark — white-tinted veil, not heavy black */
  --overlay: color-mix(in oklch, var(--foreground) 10%, transparent);

  /* Mid lavender chip + light text — no black circle; contrasts on dark table rows */
  --avatar-initials-bg: color-mix(in oklch, var(--brand-color) 38%, oklch(0.40 0.05 286.1));
  --avatar-initials-fg: oklch(0.99 0.01 286.1);

  --icon-button-foreground: color-mix(in oklch, var(--foreground) 78%, var(--background));
  --secondary-action-foreground: var(--icon-button-foreground);
  --placeholder-foreground: color-mix(in oklch, var(--foreground) 64%, var(--background));
  --icon-button-foreground-on-sidebar: color-mix(in oklch, var(--sidebar-foreground) 88%, var(--sidebar));
  --icon-button-foreground-on-secondary-panel: color-mix(in oklch, var(--foreground) 88%, var(--secondary-panel-bg));

  /* DataTable — opaque surfaces for dark mode */
  --dt-row-bg:             var(--background);
  --dt-row-hover:          oklch(0.24 0.012 286.1);
  --dt-row-selected:       oklch(0.27 0.02 286.1);
  --dt-row-selected-fg:    var(--foreground);
  --dt-header-bg:          oklch(0.24 0.012 286.1);
  --dt-group-bg:           oklch(0.24 0.012 286.1);
  /* Matches the Info banner's dark step (`dark:bg-chip-1/10`). Soft wash;
     the TableNewRowDot carries the distinct "new" signal. */
  --dt-new-row-dot:        var(--chip-1);
  --dt-new-row-bg:         color-mix(in srgb, var(--chip-1) 10%, var(--background));
  --dt-new-row-border:     transparent;
}

/* ==========================================================================
   Theme: Exxat One · Lavender (explicit — also applied as :root default)
   Usage: <html class="theme-one"> or <html class="theme-lavender">
   ========================================================================== */
.theme-one,
.theme-lavender {
  --brand-tint:         oklch(0.9676 0.016 286.1);
  --brand-tint-light:   oklch(0.993 0.007 286.1);
  --brand-tint-subtle:  oklch(0.935 0.024 286.1);
  --brand-color:        oklch(0.50 0.14 286.1);
  --brand-color-light:  oklch(0.78 0.09 286.1);
  --brand-color-dark:   oklch(0.38 0.11 286.1);
  --brand-color-deep:   oklch(0.28 0.085 286.1);
  --ring:               var(--brand-color-dark);
}

/* Light surfaces only — must NOT override .dark (otherwise muted/accent stay “paper white” in dark mode) */
.theme-one:not(.dark),
.theme-lavender:not(.dark) {
  --sidebar:        var(--brand-tint);
  --sidebar-accent: oklch(0.945 0.025 286.1);
  --sidebar-border: oklch(0.92 0.025 286.1);
  --secondary:      oklch(0.95 0.012 286.1);
  --accent:         oklch(0.925 0.015 286.1);
  --muted:          oklch(0.945 0.008 286.1);
}

.theme-one.dark,
.dark.theme-one,
.theme-lavender.dark,
.dark.theme-lavender {
  /* Focus ring uses the light brand step on dark surfaces. --brand-color is
     the mid step and only reaches 1.97:1 to 2.88:1 here, under the 3:1 floor
     for non-text contrast. */
  --ring:             var(--brand-color-light);
  --background:       oklch(0.20 0.014 286.1);
  --sidebar:          oklch(0.245 0.04 286.1);
  --sidebar-accent:   oklch(0.30 0.035 286.1);
  --sidebar-border:   oklch(0.38 0.025 286.1);
  /* Restore dark surfaces (base .dark is overridden by :not(.dark) rules above when both classes apply) */
  --secondary:        oklch(0.31 0.04 286.1);
  --muted:            oklch(0.31 0.04 286.1);
  --accent:           oklch(0.33 0.06 286.1);
  /* Surface elevation ladder — dark mode.
     Input fields (L=0.212) nest just above canvas (L=0.20). Card is a small
     inline step (L=0.225). Popover is a *floating* surface — lifts further so a
     dropdown over canvas reads as clearly above it instead of blending in.
     Secondary panel is nested chrome — wedged between canvas and sidebar
     (L=0.245), so it nestles into the shell rather than popping out.
     Brand chroma stays gentle on all three; the saturated brand expression
     lives on --brand-tint / --sidebar. */
  --secondary-panel-bg: oklch(0.22  0.025 286.1);
  --card:               oklch(0.225 0.017 286.1);
  --popover:            oklch(0.275 0.022 286.1);
  --input-background:   oklch(0.212 0.009 286.1);
  /* Brand-tint scale — same hue (286.1 — Exxat One lavender), dark lightness.
     PRIMARY (`--brand-tint`) carries enough chroma to read as visibly lavender
     against the page canvas; LIGHT / SUBTLE steps are elevation aides only. */
  --brand-tint:        oklch(0.30 0.05 286.1);
  --brand-tint-light:  oklch(0.34 0.04 286.1);
  --brand-tint-subtle: oklch(0.26 0.025 286.1);
}

/* ==========================================================================
   Theme: Exxat Prism · Rose · hue 343
   Usage: <html class="theme-prism"> or <html class="theme-rose">
   ========================================================================== */
.theme-prism,
.theme-rose {
  /* Prism rose washes — base tint sampled from the approved Prism shell
     reference. Keep the scale pale; Prism should read as a soft
     rose canvas, not a saturated pink sidebar. */
  --brand-tint:         oklch(0.9695 0.0158 341.4);
  --brand-tint-light:   oklch(0.9834 0.0100 345.4);
  --brand-tint-subtle:  oklch(0.9465 0.0247 348.5);
  --brand-color:        oklch(0.57 0.24 342);   /* Prism rose */
  --brand-color-light:  oklch(0.78 0.14 342);
  /* Chroma clamped to the sRGB maximum for each L/H. Authored at 0.24 / 0.20
     these fell outside the gamut, and the browser's silent gamut mapping
     pulled --brand-color-dark to L 0.452 H 343.6, collapsing the intended
     0.150 lightness step to 0.118 and drifting the hue. */
  --brand-color-dark:   oklch(0.42 0.18 342);
  --brand-color-deep:   oklch(0.32 0.135 342);
  --ring:               var(--brand-color-dark);
}

.theme-prism:not(.dark),
.theme-rose:not(.dark) {
  --sidebar:          var(--brand-tint);
  --sidebar-accent:   oklch(0.9465 0.0247 348.5);
  --sidebar-border:   oklch(0.9152 0.0394 347.1);
  --secondary:        oklch(0.9601 0.0120 345);
  --accent:           oklch(0.9465 0.0180 348);
  --muted:            oklch(0.9560 0.0080 345);
  --banner-prism-bg:  var(--brand-tint);
  --theme-color-chrome: oklch(0.9695 0.0158 341.4);
}

.theme-prism.dark,
.dark.theme-prism,
.theme-rose.dark,
.dark.theme-rose {
  /* See .theme-one.dark — mid brand step is under 3:1 on dark surfaces. */
  --ring:             var(--brand-color-light);
  --background:       oklch(0.20 0.014 342);
  --sidebar:          oklch(0.245 0.045 342);
  --sidebar-accent:   oklch(0.30 0.04 342);
  --sidebar-border:   oklch(0.38 0.028 342);
  --secondary:        oklch(0.31 0.04 342);
  --muted:              oklch(0.31 0.04 342);
  --accent:             oklch(0.33 0.06 342);
  --theme-color-chrome: oklch(0.2657 0.0107 353.7);
  /* Surface elevation ladder — see commentary in .theme-one.dark. Chroma is a
     touch higher than lavender at every step because rose reads slightly
     dimmer at equal chroma at these lightnesses. */
  --secondary-panel-bg: oklch(0.22  0.030 342);
  --card:               oklch(0.225 0.020 342);
  --popover:            oklch(0.275 0.026 342);
  --input-background:   oklch(0.212 0.010 342);
  /* Brand-tint scale — same hue (342 — Prism rose), dark lightness. Rose can
     carry slightly higher chroma without warming-shift on dark surfaces. */
  --brand-tint:        oklch(0.30 0.055 342);
  --brand-tint-light:  oklch(0.34 0.045 342);
  --brand-tint-subtle: oklch(0.26 0.028 342);
}

/* ==========================================================================
   Theme: Dynamic accent override · user-selected hue
   Usage: <html class="theme-custom" style="--custom-product-brand-color: …">

   Activated by `ProductProvider` whenever the user sets a per-product brand
   color override in Settings → Appearance (across any of the three built-in
   products). The default look for each product still flows from
   `theme-one` / `theme-prism`; this block kicks in only when an override is
   present. The `exxat-custom` product was removed in a prior cleanup, so
   this is now a generic accent-override mechanism.
   ========================================================================== */
/*
 * Tint chroma is **derived from the source's chroma** so neighbouring hues
 * (e.g. Blue h=252 vs Indigo h=280 vs Purple h=286) read as distinct pale
 * tints instead of collapsing to the same near-white. The earlier formula
 * pinned chroma to a fixed `0.018`, which is below the hue-discrimination
 * threshold at L≈0.96 for closely-spaced hues — that's why Blue / Indigo /
 * Purple looked identical in the sidebar.
 *
 * `max(<floor>, calc(c * <fraction>))` rules:
 *   - vibrant brand colours (c ≈ 0.10–0.23) get a meaningfully tinted
 *     sidebar / accent / muted scale → product chrome visibly changes per
 *     pick
 *   - low-chroma custom-hex picks (greys) fall back to the floor so they
 *     don't render as pure white
 *   - lightness + hue stay pinned to the original mock-up values, so the
 *     overall "very pale background" feel is unchanged
 */
.theme-custom {
  /* Match Prism cadence — pale wash, not saturated pink sidebar. Chroma is
     capped well below vibrant source picks so Clinical Education reads like
     Prism rose, not a hot magenta rail. */
  --brand-tint:         oklch(from var(--custom-product-brand-color) 0.9695 min(0.018, max(0.008, calc(c * 0.07))) h);
  --brand-tint-light:   oklch(from var(--custom-product-brand-color) 0.9834 min(0.012, max(0.006, calc(c * 0.04))) h);
  --brand-tint-subtle:  oklch(from var(--custom-product-brand-color) 0.9465 min(0.026, max(0.012, calc(c * 0.11))) h);
  --brand-color:        var(--custom-product-brand-color);
  --brand-color-light:  oklch(from var(--custom-product-brand-color) 0.82 min(c, 0.14) h);
  --brand-color-dark:   oklch(from var(--custom-product-brand-color) 0.48 c h);
  --brand-color-deep:   oklch(from var(--custom-product-brand-color) 0.34 c h);
  --ring:               var(--brand-color-dark);
}

.theme-custom:not(.dark) {
  --product-wordmark-suffix: var(--brand-color-deep);
  --sidebar:            var(--brand-tint);
  --sidebar-accent:     oklch(from var(--custom-product-brand-color) 0.9465 min(0.026, max(0.012, calc(c * 0.11))) h);
  --sidebar-border:     oklch(from var(--custom-product-brand-color) 0.915 min(0.030, max(0.014, calc(c * 0.12))) h);
  --secondary:          oklch(from var(--custom-product-brand-color) 0.960 min(0.014, max(0.006, calc(c * 0.07))) h);
  --accent:             oklch(from var(--custom-product-brand-color) 0.946 min(0.018, max(0.008, calc(c * 0.09))) h);
  --muted:              oklch(from var(--custom-product-brand-color) 0.956 min(0.010, max(0.004, calc(c * 0.05))) h);
  --theme-color-chrome: var(--brand-tint);
}

.theme-custom.dark,
.dark.theme-custom {
  --product-wordmark-suffix: var(--brand-color-light);
  /* See .theme-one.dark — mid brand step is under 3:1 on dark surfaces. */
  --ring:               var(--brand-color-light);
  --background:         oklch(from var(--custom-product-brand-color) 0.20  max(0.014, calc(c * 0.12)) h);
  --sidebar:            oklch(from var(--custom-product-brand-color) 0.245 max(0.035, calc(c * 0.28)) h);
  --sidebar-accent:     oklch(from var(--custom-product-brand-color) 0.30  max(0.030, calc(c * 0.24)) h);
  --sidebar-border:     oklch(from var(--custom-product-brand-color) 0.38  max(0.022, calc(c * 0.18)) h);
  --secondary:          oklch(from var(--custom-product-brand-color) 0.31  max(0.04,  calc(c * 0.40)) h);
  --muted:              oklch(from var(--custom-product-brand-color) 0.31  max(0.04,  calc(c * 0.40)) h);
  --accent:             oklch(from var(--custom-product-brand-color) 0.33  max(0.06,  calc(c * 0.50)) h);
  --theme-color-chrome: color-mix(in oklch, var(--custom-product-brand-color) 12%, black);
  /* Surface elevation ladder — derived from the custom picked hue. See
     commentary in .theme-one.dark for the full ladder. Chroma scales with
     the source colour (~13% for inline cards / 16% for floating popovers /
     20% for secondary panel) with floors that keep near-grey picks from
     collapsing back to neutral. */
  --secondary-panel-bg: oklch(from var(--custom-product-brand-color) 0.22  max(0.020, calc(c * 0.20)) h);
  --card:               oklch(from var(--custom-product-brand-color) 0.225 max(0.015, calc(c * 0.13)) h);
  --popover:            oklch(from var(--custom-product-brand-color) 0.275 max(0.018, calc(c * 0.16)) h);
  --input-background:   oklch(from var(--custom-product-brand-color) 0.212 max(0.007, calc(c * 0.08)) h);
  /* Brand-tint scale — derived from the picked hue, dark lightness. PRIMARY
     carries chroma high enough for the secondary panel to read as the brand
     hue; floor 0.04 prevents low-chroma picks (greys) from collapsing back to
     a flat surface. */
  --brand-tint:         oklch(from var(--custom-product-brand-color) 0.30 max(0.04,  calc(c * 0.30)) h);
  --brand-tint-light:   oklch(from var(--custom-product-brand-color) 0.34 max(0.03,  calc(c * 0.24)) h);
  --brand-tint-subtle:  oklch(from var(--custom-product-brand-color) 0.26 max(0.022, calc(c * 0.20)) h);
}

/* ==========================================================================
   HIGH CONTRAST MODE
   ──────────────────────────────────────────────────────────────────────────
   Three guiding principles (WCAG 1.4.6 Enhanced + research-backed UX):

   1. FORCED-COLORS APPROACH
      Strip gradients, shadows, and background images.
      Use borders to define shape and elevation — not fills.
      Limited palette: 4–6 named tokens, nothing decorative.

   2. DON'T RELY ON COLOR ALONE
      Error/warning states get a thick border AND an icon differentiator.
      Charts and badges use luminance separation (grayscale steps), not hue.
      Interactive state (hover / focus / active) is communicated via outline,
      not background-color change alone.

   3. DARK MODE ≠ HIGH CONTRAST
      Dark mode  = aesthetic; uses subtle greys.
      High-contrast dark = functional; uses near-black grey canvas, pure white
      (white), and a "hot" neon-yellow focus accent for maximum visibility.
   ========================================================================== */

/* ── Light High Contrast ─────────────────────────────────────────────────── */
/*    Fluent 2 / Microsoft 6-color strategy.                                 */
/*    Highlight = saturated bright brand color (follows theme hue).           */
html[data-contrast="high"]:not(.dark) {
  /* Chroma is the sRGB ceiling at L 0.35 for the tightest brand hue (342). */
  --hc-highlight: oklch(from var(--brand-color) 0.35 0.15 h);
  --hc-highlight-text: oklch(1 0 0);

  --background:         oklch(1 0 0);
  --foreground:         oklch(0.14 0 0);
  --card:               oklch(1 0 0);
  --card-foreground:    oklch(0.14 0 0);
  --popover:            oklch(1 0 0);
  --popover-foreground: oklch(0.14 0 0);
  --primary:            oklch(0.14 0 0);
  --primary-foreground: oklch(1 0 0);
  /* High contrast gives up the indigo, the same way `--primary` gives up brand
     charcoal here: the theme's job is maximum separation, and no indigo beats
     near-black on white. Written as literals rather than `var(--primary)`
     because `[data-auth-surface]` remaps `--primary` to this pair, and pointing
     back at `--primary` would be a cycle that resolves to nothing. */
  --auth-primary:            oklch(0.14 0 0);
  --auth-primary-foreground: oklch(1 0 0);
  --auth-ink:                oklch(0.14 0 0);
  --secondary:            oklch(1 0 0);
  --secondary-foreground: oklch(0.14 0 0);
  --muted:            oklch(0.94 0 0);
  --muted-foreground: oklch(0.14 0 0);
  --accent:            var(--hc-highlight);
  --accent-foreground: var(--hc-highlight-text);
  --destructive:            oklch(0.45 0.24 25);
  --destructive-foreground: oklch(1 0 0);
  --destructive-ink:        var(--destructive);
  --border:            oklch(0.14 0 0);
  --border-control:    oklch(0.14 0 0);
  --border-control-3:  oklch(0.14 0 0);
  --border-control-35: oklch(0.14 0 0);
  --control-border:    oklch(0.14 0 0);
  --input:            oklch(0.14 0 0);
  --input-background: oklch(1 0 0);
  --ring: var(--hc-highlight);
  --sidebar:                    oklch(1 0 0);
  --sidebar-foreground:         oklch(0.14 0 0);
  --sidebar-primary:            oklch(0.14 0 0);
  --sidebar-primary-foreground: oklch(1 0 0);
  --sidebar-accent:             oklch(0.90 0 0);
  --sidebar-accent-foreground:  oklch(0.14 0 0);
  --sidebar-border:             oklch(0.14 0 0);
  --sidebar-ring:               var(--hc-highlight);
  --sidebar-section-label-foreground: oklch(0.40 0 0);
  /* Every step has to clear 3:1 against the canvas: a chart series is a
     graphical object (1.4.11), so the ramp cannot spend its bottom half on
     values that only read as "light grey on white". The old 0.72 / 0.86 tail
     measured 2.49:1 and 1.53:1 — series four and five were invisible. The ramp
     now stops at L 0.60 (≈3.9:1) and takes smaller steps to keep five levels
     distinguishable inside the narrower band. */
  --chart-1: oklch(0.14 0 0);
  --chart-2: oklch(0.29 0 0);
  --chart-3: oklch(0.40 0 0);
  --chart-4: oklch(0.50 0 0);
  --chart-5: oklch(0.60 0 0);
  --chip-1:           oklch(0.14 0 0);
  --chip-2:           oklch(0.14 0 0);
  --chip-3:           oklch(0.14 0 0);
  --chip-4:           oklch(0.14 0 0);
  --chip-5:           oklch(0.14 0 0);
  --chip-destructive: oklch(0.45 0.24 25);
  --status-badge-success-fill: oklch(1 0 0);
  --status-badge-success-fg:   oklch(0.14 0 0);
  --status-badge-info-fill:    oklch(1 0 0);
  --status-badge-info-fg:      oklch(0.14 0 0);
  --status-badge-warning-fill: oklch(1 0 0);
  --status-badge-warning-fg:   oklch(0.14 0 0);
  --status-badge-danger-fill:  oklch(1 0 0);
  --status-badge-danger-fg:    oklch(0.14 0 0);
  --status-badge-neutral-fill: oklch(1 0 0);
  --status-badge-neutral-fg:   oklch(0.14 0 0);
  --interactive-hover:            oklch(0.88 0 0);
  --interactive-hover-foreground: oklch(0.14 0 0);
  --interactive-hover-subtle:     oklch(0.92 0 0);
  --interactive-hover-soft:       oklch(0.92 0 0);
  --interactive-hover-medium:     oklch(0.86 0 0);
  --interactive-hover-strong:     oklch(0.82 0 0);
  --interactive-hover-row:        oklch(0.88 0 0);
  --overlay: color-mix(in oklch, var(--foreground) 18%, transparent);
  --dt-row-bg:          oklch(1 0 0);
  --dt-row-hover:       oklch(0.92 0 0);
  --dt-row-selected:    var(--accent);
  --dt-row-selected-fg: var(--accent-foreground);
  /* The high-contrast themes flatten brand hues, so the marker takes plain ink
     rather than a brand tint that would land a step off the page's own text. */
  --dt-row-open-marker: var(--foreground);
  --dt-header-bg:       oklch(1 0 0);
  --dt-group-bg:        oklch(0.92 0 0);
  --dt-new-row-bg:      oklch(1 0 0);
  --dt-new-row-border:  oklch(0.14 0 0);
  /* High contrast strips chroma: the row keeps a border instead of a wash, and
     the dot goes full-contrast rather than blue. */
  --dt-new-row-dot:     oklch(0.14 0 0);
}

/* ── Dark High Contrast ──────────────────────────────────────────────────── */
/*    Same 6-color strategy inverted for dark canvas.                        */
/*    Highlight = very bright saturated brand (like MS cyan but brand hue).  */
html[data-contrast="high"].dark {
  /* Chroma is the sRGB ceiling at L 0.78 for the tightest brand hue (286.1). */
  --hc-highlight: oklch(from var(--brand-color) 0.78 0.115 h);
  --hc-highlight-text: oklch(0.08 0 0);

  --background:         oklch(0.14 0 0);
  --foreground:         oklch(0.96 0 0);
  --card:               oklch(0.14 0 0);
  --card-foreground:    oklch(0.96 0 0);
  --popover:            oklch(0.20 0 0);
  --popover-foreground: oklch(0.96 0 0);
  --primary:            oklch(0.96 0 0);
  --primary-foreground: oklch(0.10 0 0);
  /* Mirror of the light high-contrast decision: near-white fill, dark ink. */
  --auth-primary:            oklch(0.96 0 0);
  --auth-primary-foreground: oklch(0.10 0 0);
  --auth-ink:                oklch(0.96 0 0);
  --secondary:            oklch(0.14 0 0);
  --secondary-foreground: oklch(0.96 0 0);
  --muted:            oklch(0.22 0 0);
  --muted-foreground: oklch(0.96 0 0);
  --accent:            var(--hc-highlight);
  --accent-foreground: var(--hc-highlight-text);
  /* High contrast keeps the bright red with dark ink: on this fill, near-black
     reaches ~8:1 where white would manage ~2.4:1, and maximum contrast is the
     whole point of the theme. Normal dark mode makes the opposite trade. */
  --destructive:            oklch(0.72 0.18 18);
  --destructive-foreground: oklch(0.10 0 0);
  --destructive-ink:        var(--destructive);
  --border:            oklch(0.75 0 0);
  --border-control:    oklch(0.75 0 0);
  --border-control-3:  oklch(0.75 0 0);
  --border-control-35: oklch(0.75 0 0);
  --control-border:    oklch(0.75 0 0);
  --input:            oklch(0.75 0 0);
  --input-background: oklch(0.14 0 0);
  --ring: var(--hc-highlight);
  --sidebar:                    oklch(0.14 0 0);
  --sidebar-foreground:         oklch(0.96 0 0);
  --sidebar-primary:            oklch(0.96 0 0);
  --sidebar-primary-foreground: oklch(0.10 0 0);
  --sidebar-accent:             oklch(0.24 0 0);
  --sidebar-accent-foreground:  oklch(0.96 0 0);
  --sidebar-border:             oklch(0.75 0 0);
  --sidebar-ring:               var(--hc-highlight);
  --sidebar-section-label-foreground: oklch(0.65 0 0);
  /* Mirror of the light ramp — floor at L 0.56 (≈4.2:1 on the 0.14 canvas).
     The old 0.38 / 0.22 tail measured 1.98:1 and 1.16:1. */
  --chart-1: oklch(0.96 0 0);
  --chart-2: oklch(0.85 0 0);
  --chart-3: oklch(0.75 0 0);
  --chart-4: oklch(0.65 0 0);
  --chart-5: oklch(0.56 0 0);
  --chip-1:           oklch(0.96 0 0);
  --chip-2:           oklch(0.96 0 0);
  --chip-3:           oklch(0.96 0 0);
  --chip-4:           oklch(0.96 0 0);
  --chip-5:           oklch(0.96 0 0);
  --chip-destructive: oklch(0.72 0.18 18);
  --status-badge-success-fill: oklch(0.14 0 0);
  --status-badge-success-fg:   oklch(0.96 0 0);
  --status-badge-info-fill:    oklch(0.14 0 0);
  --status-badge-info-fg:      oklch(0.96 0 0);
  --status-badge-warning-fill: oklch(0.14 0 0);
  --status-badge-warning-fg:   oklch(0.96 0 0);
  --status-badge-danger-fill:  oklch(0.14 0 0);
  --status-badge-danger-fg:    oklch(0.96 0 0);
  --status-badge-neutral-fill: oklch(0.14 0 0);
  --status-badge-neutral-fg:   oklch(0.96 0 0);
  --interactive-hover:            oklch(0.26 0 0);
  --interactive-hover-foreground: oklch(0.96 0 0);
  --interactive-hover-subtle:     oklch(0.22 0 0);
  --interactive-hover-soft:       oklch(0.22 0 0);
  --interactive-hover-medium:     oklch(0.28 0 0);
  --interactive-hover-strong:     oklch(0.32 0 0);
  --interactive-hover-row:        oklch(0.26 0 0);
  --overlay: color-mix(in oklch, var(--foreground) 22%, transparent);
  --dt-row-bg:          oklch(0.14 0 0);
  --dt-row-hover:       oklch(0.22 0 0);
  --dt-row-selected:    var(--accent);
  --dt-row-selected-fg: var(--accent-foreground);
  --dt-row-open-marker: var(--foreground);
  --dt-header-bg:       oklch(0.14 0 0);
  --dt-group-bg:        oklch(0.22 0 0);
  --dt-new-row-bg:      oklch(0.14 0 0);
  --dt-new-row-border:  oklch(0.96 0 0);
  --dt-new-row-dot:     oklch(0.96 0 0);
}

/* ── Horizontal scroll viewports fade at the edge, they do not cut ───────
   A clipped row ends mid-card, mid-label, or mid-chart. That reads as a
   rendering fault, not as "there is more this way", and on the products home it
   left a What's new card sliced down the middle at both window edges.

   A **mask**, not a gradient overlay in a background colour. These rows sit on
   white cards, on brand-tinted washes, and on the page canvas, so any colour
   painted over the content would be right on one surface and visibly wrong on
   the next two. A mask fades the content itself to transparent and lets whatever
   is behind show through, in every theme, including both high-contrast ones.

   Only the side that has content behind it fades. `data-fade` comes from the
   viewport's own scroll position (`HorizontalScrollViewport`), so a row that has
   not been scrolled keeps its first card crisp against the page margin. */
[data-slot="horizontal-scroll-viewport"] {
  --hsv-fade: 2rem;
  -webkit-mask-image: var(--hsv-mask, none);
  mask-image: var(--hsv-mask, none);
  /* A mask paints nothing outside its box, and these rows hold cards and KPI
     tiles whose shadows sit a few pixels below them. `no-clip` keeps the
     horizontal ramp and stops it trimming those shadows flat. Where it is
     unsupported the declaration drops and the row falls back to its own box. */
  mask-clip: no-clip;
}

[data-slot="horizontal-scroll-viewport"][data-fade="start"] {
  --hsv-mask: linear-gradient(to right, transparent 0, #000 var(--hsv-fade));
}

[data-slot="horizontal-scroll-viewport"][data-fade="end"] {
  --hsv-mask: linear-gradient(to left, transparent 0, #000 var(--hsv-fade));
}

[data-slot="horizontal-scroll-viewport"][data-fade="both"] {
  --hsv-mask: linear-gradient(
    to right,
    transparent 0,
    #000 var(--hsv-fade),
    #000 calc(100% - var(--hsv-fade)),
    transparent 100%
  );
}

/* A mask fades everything under it, including a focus ring, and a keyboard user
   arriving at the tile or tab nearest an edge lands inside the ramp. Focus wins:
   while it is anywhere in the row, nothing fades. Covers the row's own ring
   (`ScrollRegion` is focusable while it overflows) and every child's. */
[data-slot="horizontal-scroll-viewport"]:is(:focus-visible, :has(:focus-visible)) {
  --hsv-mask: none;
}

/* High contrast asks for the opposite trade: the ramp dims real text on the card
   at the edge, and a reader who turned contrast up did so to stop exactly that.
   These modes keep the hard edge, which costs nothing in legibility because the
   chevrons reach the same content. */
html:is([data-contrast="high"], [data-contrast="windows"])
  [data-slot="horizontal-scroll-viewport"] {
  --hsv-mask: none;
}

@media (forced-colors: active) {
  [data-slot="horizontal-scroll-viewport"] {
    --hsv-mask: none;
  }
}

/* ── Auth surface ────────────────────────────────────────────────────────
   Pre-auth pages (`AuthShell`) remap `--primary` to the auth pair for their
   whole subtree, so `Button` needs no auth-specific variant and no step needs
   an indigo class: the identifier step, the password step, the SSO handoff and
   every choice step all get it by being inside the shell.

   Custom properties resolve from the nearest matching ancestor, so this beats
   `:root`, `.dark`, and both high-contrast blocks for anything under the shell
   regardless of where it sits in the file. Each of those blocks defines its own
   `--auth-primary`, so the theme still decides the value; this only decides
   which token the buttons read. */
[data-auth-surface] {
  --primary:            var(--auth-primary);
  --primary-foreground: var(--auth-primary-foreground);
  /* `--ring` is deliberately NOT remapped. Indigo focus turned the field into a
     glowing halo that no longer read as a DS input, and the reason it stood out
     is that these pages have no product theme: the product blocks point `--ring`
     at the brand hue, so pre-auth falls back to the root's neutral
     `oklch(0.25 0 0)`, which is what a DS field looks like on an unthemed page.
     Indigo stays where it was asked for, on the primary fill and the link ink.
     The neutral also measures far better than the indigo ring did in dark mode
     (3.5:1, only just over the 3:1 floor for a focus indicator). */
}

/* The rail promo is the largest type on a sign-in page, and the DS gives banner
   titles the `font-heading` serif. Beside a sans `h1` that reads as two
   typefaces rather than as a decision, so the auth surface puts the promo in the
   same voice as the form. Scoped here: every other `MarketingBanner` in the
   product keeps the serif. Matched on the tag because the hero title takes
   `titleAs` and carries no slot of its own. */
[data-auth-surface] [data-slot="marketing-banner"] h2 {
  font-family: var(--font-sans);
}

/* The promo runs to the window edges on this surface, so its artwork clip has to
   be square too. The overlay carries the banner's own `rounded-xl`, which would
   round the bottom-right corner of the illustration against a panel that has no
   corner radius and show a sliver of gradient through it. */
[data-auth-surface] [data-slot="marketing-banner-decorative-overlay"] {
  border-radius: 0;
}

/* `--primary` is a fill, so this remap cannot carry the `link` button variant,
   which is `text-primary`. Indigo-as-text measures 3.5:1 on the dark canvas and
   fails 1.4.3, so link-variant buttons on these pages set `text-auth-ink`
   explicitly instead of inheriting. Deliberately not solved by weakening the
   remap: fills genuinely do want the darker value. */

/* Neutralize brand tint washes in HC so bg-brand-tint doesn't compete     */
html:is([data-contrast="high"], [data-contrast="windows"]):not(.dark) {
  --brand-tint:        oklch(0.94 0 0);
  --brand-tint-light:  oklch(0.97 0 0);
  --brand-tint-subtle: oklch(0.98 0 0);
}
html:is([data-contrast="high"], [data-contrast="windows"]).dark {
  --brand-tint:        oklch(0.22 0 0);
  --brand-tint-light:  oklch(0.18 0 0);
  --brand-tint-subtle: oklch(0.16 0 0);
}

/* Decorative 12-15% washes (tinted icon discs, KPI severity badges) composite
   to within 1.1:1 of the canvas once the ramp is greyscale, so the disc is in
   the DOM and invisible on screen. Drop the wash and let the glyph carry the
   meaning at full foreground ink — a shape that reads is worth more than a
   tint that doesn't. */
html:is([data-contrast="high"], [data-contrast="windows"]) {
  --icon-disc-chart-2-bg: transparent;
  --icon-disc-chart-4-bg: transparent;
  --icon-disc-brand-bg:   transparent;
  --icon-disc-danger-bg:  transparent;
  --icon-disc-chart-2-fg: var(--foreground);
  --icon-disc-chart-4-fg: var(--foreground);
  --icon-disc-brand-fg:   var(--foreground);
  --icon-disc-danger-fg:  var(--destructive-ink);

  --insight-severity-warning-bg: transparent;
  --insight-severity-warning-fg: var(--foreground);
  --insight-severity-info-bg:    transparent;
  --insight-severity-info-fg:    var(--foreground);
}

/* ── Structural rules shared by BOTH light and dark HC ──────────────────── */
/*    Pattern 1: "Borders over Fills" — strip decorative chrome              */
html:is([data-contrast="high"], [data-contrast="windows"]) {

  /* 1a. Remove ALL box-shadows (elevation via outline, not shadow) */
  & * {
    box-shadow: none !important;
    text-shadow: none !important;
  }

  /* 1b. Strip gradient fills from cards; use border to define shape.
     The background goes too: severity tiles paint a 10% destructive wash that
     composites to a murky near-canvas colour, carrying no information while
     dirtying the one surface colour HC promises. The 2px border and the ink
     inside are what tell the card apart. */
  & [data-slot="card"] {
    background-image: none !important;
    background-color: var(--card) !important;
    border: 2px solid var(--foreground) !important;
  }

  /* 1c. Thicker borders on all form controls (WCAG 1.4.11: 3:1 UI contrast) */
  & [data-slot="input"],
  & [data-slot="select-trigger"],
  & [data-slot="textarea"],
  & [data-slot="checkbox"],
  & [data-slot="switch"] {
    border-width: 2px !important;
    border-color: var(--foreground) !important;
  }

  /* Text fields carry `dark:bg-input/30`, which is a barely-there wash while
     `--input` is a dark border colour. HC dark redefines `--input` as a *light*
     grey so the border can be seen, and the same token then paints the field a
     light grey behind near-white text: 1.5:1 in a control the user has to type
     into. Pin the fill to the surface token that means "inside a field". */
  & [data-slot="input"],
  & [data-slot="textarea"],
  & [data-slot="select-trigger"] {
    background-color: var(--input-background) !important;
  }

  /* 1d. Sidebar defined by its edge border, not background colour */
  & [data-slot="sidebar"] {
    border-inline-end: 2px solid var(--sidebar-border) !important;
  }

  /* 1d-bis. Secondary panel (nested rail — Library, Tokens, …) — HC chrome.
     Normal mode reads the panel via `--secondary-panel-bg` (brand-tinted
     OKLCH) + `ring-1 ring-sidebar-border shadow-sm`. In HC mode the
     brand wash collapses to ~white/near-canvas and `box-shadow: none`
     above kills the ring, leaving the panel invisible against the
     background. Force a flat canvas fill and a 2px solid border so the
     panel reads as a distinct chrome surface — same treatment cards and
     the sidebar receive. */
  & [data-slot="secondary-panel"] {
    background-color: var(--background) !important;
    background-image: none !important;
    border: 2px solid var(--sidebar-border) !important;
  }

  /* 1d-bis2. Secondary panel — active row uses the HC --accent highlight
     (saturated brand) instead of the normal-mode --sidebar-accent grey
     wash. The Library/Tokens nav rows are raw <Link> elements (not
     SidebarMenuButton), so the existing 'Pattern 3: Active nav' rule on
     [data-slot="sidebar-menu-button"][data-active="true"] doesn't fire.

     Two selectors:
       1. `[aria-current="page"]` — the active link itself (NavRow,
          IconNavRow, the inner Link inside LibraryFolderTreeBranch).
       2. `[class~="bg-sidebar-accent"]` — the folder-tree row WRAPPER
          (a <div> that puts the active fill class on the row and has
          its own `rounded-md`). The `~=` matcher is whole-token only
          so we don't accidentally match `hover:bg-sidebar-accent/50`.

     Both targets already carry `rounded-md` (or `rounded-sm` for the
     inner folder-tree link), so the bg fill follows that radius and
     reads as a pill. We deliberately avoid `:has(> [aria-current])`
     here because that also matched <li> wrappers which don't have a
     border-radius — painting them yielded a flat-cornered fill. */
  & [data-slot="secondary-panel"] [aria-current="page"],
  & [data-slot="secondary-panel"] [class~="bg-sidebar-accent"] {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
    /* No ring/outline — `outline` ignores border-radius and would
       render a square ring on top of the rounded bg. */
    box-shadow: none !important;
  }
  & [data-slot="secondary-panel"] [aria-current="page"] *,
  & [data-slot="secondary-panel"] [class~="bg-sidebar-accent"] * {
    color: var(--accent-foreground) !important;
  }
  /* Active row count chip — invert against the highlighted row so the
     count remains legible on the saturated accent fill (same trick used
     by the SidebarMenuButton active-state rule above). */
  & [data-slot="secondary-panel"] [aria-current="page"] [data-slot="badge"],
  & [data-slot="secondary-panel"] [aria-current="page"] [data-slot="sidebar-menu-badge"],
  & [data-slot="secondary-panel"] [class~="bg-sidebar-accent"] [data-slot="badge"],
  & [data-slot="secondary-panel"] [class~="bg-sidebar-accent"] [data-slot="sidebar-menu-badge"] {
    background-color: var(--accent-foreground) !important;
    color: var(--accent) !important;
    border-color: var(--accent-foreground) !important;
  }

  /* 1e. Tab list — border not fill */
  & [data-slot="tabs-list"] {
    background-color: transparent !important;
    border: 2px solid var(--foreground) !important;
  }

  /* 1f. Badge / chip — border-based; no decorative fills */
  & [data-slot="badge"] {
    border-width: 2px !important;
    border-color: var(--foreground) !important;
    background-color: var(--background) !important;
    font-weight: 700 !important;
    box-shadow: none !important;
    outline: none !important;
    color: var(--foreground) !important;
  }

  & [data-slot="badge"] * {
    color: var(--foreground) !important;
  }

  /* Leo / Ask prompt chips — solid canvas fill; no translucent card wash */
  & [data-slot="badge"][data-variant="prompt"] {
    background-color: var(--background) !important;
    font-weight: 600 !important;
  }

  /* Status chip — same treatment as Badge (border-based, no decorative fill) */
  & [data-slot="status-badge"] {
    border-width: 2px !important;
    border-color: var(--foreground) !important;
    background-color: var(--background) !important;
    font-weight: 700 !important;
    box-shadow: none !important;
    outline: none !important;
    color: var(--foreground) !important;
  }

  /* Sidebar wrapper is positioning-only when it wraps Badge/StatusBadge — avoid double border */
  & [data-slot="sidebar-menu-badge"]:has([data-slot="badge"]),
  & [data-slot="sidebar-menu-badge"]:has([data-slot="status-badge"]) {
    border-width: 0 !important;
    background-color: transparent !important;
    box-shadow: none !important;
    outline: none !important;
  }

  & [data-slot="view-toolbar-count"] {
    border-width: 2px !important;
    border-color: currentColor !important;
    background-color: var(--background) !important;
    font-weight: 700 !important;
    color: var(--foreground) !important;
    box-shadow: none !important;
    outline: none !important;
  }

  & [data-slot="sidebar-menu-badge"]:not(:has([data-slot="badge"])):not(:has([data-slot="status-badge"])) {
    border-width: 2px !important;
    border-color: currentColor !important;
    background-color: var(--background) !important;
    font-weight: 700 !important;
    color: var(--foreground) !important;
  }

  /* Segmented tab buttons: global HC button border + count pill border = double ring */
  & button[data-slot="view-segmented-item"] {
    border-width: 0 !important;
    border-color: transparent !important;
  }

  /* Radix Tabs — active state must not rely on muted fill / pseudo underline only */
  & [data-slot="tabs-trigger"] {
    color: var(--foreground) !important;
    opacity: 1 !important;
  }

  & [data-slot="tabs-trigger"][data-state="active"] {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
    outline: none !important;
    font-weight: 600 !important;
  }

  /* KPI strip + chart card mini-metrics — force readable copy (muted grays fail in HC) */
  & [data-slot="key-metrics"] :where(span, p, button, a, i, label, div) {
    color: var(--foreground) !important;
  }

  & [data-slot="key-metrics"] :where(svg) {
    color: var(--foreground) !important;
  }

  /* List / menu rows — Highlight brand color for selection */
  & [data-slot="dropdown-menu-item"][data-highlighted],
  & [data-slot="dropdown-menu-checkbox-item"][data-highlighted],
  & [data-slot="dropdown-menu-radio-item"][data-highlighted],
  & [data-slot="dropdown-menu-sub-trigger"][data-highlighted],
  & [data-slot="select-item"][data-highlighted],
  & [data-slot="command-item"][data-selected],
  & [data-slot="command-item"][aria-selected="true"] {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
  }

  & [data-slot="dropdown-menu-item"][data-highlighted] *,
  & [data-slot="dropdown-menu-checkbox-item"][data-highlighted] *,
  & [data-slot="dropdown-menu-radio-item"][data-highlighted] *,
  & [data-slot="dropdown-menu-sub-trigger"][data-highlighted] *,
  & [data-slot="select-item"][data-highlighted] *,
  & [data-slot="command-item"][data-selected] *,
  & [data-slot="command-item"][aria-selected="true"] * {
    color: var(--accent-foreground) !important;
  }

  /* List hub views toolbar + segmented radiogroup (not Radix Tabs / tabs-list) */
  & [data-slot="view-segmented-toolbar"] {
    background-color: transparent !important;
    border: 2px solid var(--foreground) !important;
  }

  & [data-slot="view-segmented-item"][aria-pressed="true"],
  & [data-slot="view-segmented-item"][aria-checked="true"] {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
    outline: none !important;
  }

  /* Composed view-tab active shell (list-page.tsx) — wraps the label button
     and its chevron disclosure. Fill the shell so the chevron sibling sits
     on the same `--accent` highlight as the label, then cascade
     `--accent-foreground` to descendants so the chevron glyph stays legible. */
  & [data-slot="view-segmented-item-shell"] {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
    outline: none !important;
  }

  & [data-slot="view-segmented-item-shell"] * {
    color: var(--accent-foreground) !important;
  }

  /* Floating surfaces — portal menus, popovers, sheets, dialogs */
  & [data-slot="dropdown-menu-content"],
  & [data-slot="dropdown-menu-sub-content"],
  & [data-slot="popover-content"],
  & [data-slot="select-content"],
  & [data-slot="dialog-content"],
  & [data-slot="sheet-content"],
  & [data-slot="drawer-content"] {
    border: 2px solid var(--foreground) !important;
    background-color: var(--background) !important;
    color: var(--foreground) !important;
  }

  & [data-slot="command"] {
    background-color: var(--background) !important;
    color: var(--foreground) !important;
  }

  /* Strip conditional-formatting inline bg from table cells in HC */
  & td[style*="background"] {
    background: var(--dt-row-bg) !important;
  }
  & td.pinned-cell {
    background-color: var(--dt-row-bg) !important;
  }
  & tr:hover td.pinned-cell,
  & tr:hover td {
    background-color: var(--dt-row-hover) !important;
  }
  & tr[data-state="selected"] td.pinned-cell,
  & tr[data-state="selected"] td {
    background-color: var(--dt-row-selected) !important;
    color: var(--dt-row-selected-fg) !important;
  }

  /* Table selected rows — Highlight brand color (like MS Teams selection) */
  & tr[data-state="selected"] {
    background-color: var(--dt-row-selected) !important;
    color: var(--dt-row-selected-fg) !important;
  }
  & tr[data-state="selected"] *:not([data-slot="checkbox"] *):not([data-slot="badge"] *):not([data-slot="checkbox"]):not([data-slot="badge"]) {
    color: var(--accent-foreground) !important;
  }
  & tr[data-state="selected"] [data-slot="badge"] {
    background-color: var(--accent-foreground) !important;
    color: var(--accent) !important;
    border-color: var(--accent-foreground) !important;
  }
  & tr[data-state="selected"] [data-slot="badge"] * {
    color: var(--accent) !important;
  }

  & tr[data-state="selected"] [data-slot="checkbox"][data-state="checked"],
  & tr[data-state="selected"] [data-slot="checkbox"][data-state="indeterminate"] {
    background-color: var(--accent-foreground) !important;
    border-color: var(--accent-foreground) !important;
    color: var(--accent) !important;
  }
  & tr[data-state="selected"] [data-slot="checkbox"][data-state="checked"] *,
  & tr[data-state="selected"] [data-slot="checkbox"][data-state="indeterminate"] * {
    color: var(--accent) !important;
  }
  & tr[data-state="selected"] [data-slot="checkbox"]:not([data-state="checked"]):not([data-state="indeterminate"]) {
    background-color: var(--accent) !important;
    border-color: var(--accent-foreground) !important;
    color: var(--accent-foreground) !important;
  }

  /* ── Pattern 2: Stronger focus ring (WCAG 2.4.11 / 2.4.12) ── */
  & :focus-visible {
    outline: 3px solid var(--ring) !important;
    outline-offset: 3px !important;
  }

  /* ── Pattern 2: Error state — thick border + no colour-only signal ── */
  & [aria-invalid="true"],
  & [data-invalid] {
    border-width: 3px !important;
    border-color: var(--destructive) !important;
    /* Downstream: pair with a ⚠ icon in the component for non-colour signal */
  }

  /* ── Pattern 3: Active nav — Highlight brand fill (like MS Teams) ── */
  & [data-slot="sidebar-menu-button"][data-active="true"] {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
    outline: none !important;
  }
  & [data-slot="sidebar-menu-button"][data-active="true"] * {
    color: var(--accent-foreground) !important;
  }
  & [data-slot="sidebar-menu-button"][data-active="true"] [data-slot="badge"],
  & [data-slot="sidebar-menu-button"][data-active="true"] [data-slot="sidebar-menu-badge"] {
    background-color: var(--accent-foreground) !important;
    color: var(--accent) !important;
    border-color: var(--accent-foreground) !important;
  }
  & [data-slot="sidebar-menu-button"][data-active="true"] [data-slot="badge"] *,
  & [data-slot="sidebar-menu-button"][data-active="true"] [data-slot="sidebar-menu-badge"] * {
    color: var(--accent) !important;
  }
  & [data-slot="toggle-group-item"][data-state="on"] {
    outline: 2px solid var(--accent) !important;
    outline-offset: -2px !important;
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
  }

  /* ── Pattern 3b: Checked checkboxes — Highlight brand fill ── */
  & [data-slot="checkbox"][data-state="checked"],
  & [data-slot="checkbox"][data-state="indeterminate"] {
    background-color: var(--accent) !important;
    border-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
  }
  & [data-slot="checkbox"][data-state="checked"] *,
  & [data-slot="checkbox"][data-state="indeterminate"] * {
    color: var(--accent-foreground) !important;
  }

  /* ── Pattern 3c: Checked radio buttons — Highlight brand fill ── */
  & [data-slot="radio-group-item"][data-state="checked"] {
    background-color: var(--accent) !important;
    border-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
  }
  & [data-slot="radio-group-item"][data-state="checked"] [data-slot="radio-group-indicator"] span {
    background-color: var(--accent-foreground) !important;
  }

  /* ── Pattern 3d: Toggle on state — Highlight brand fill ── */
  & [data-slot="toggle"][data-state="on"] {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
    border: 2px solid var(--accent) !important;
  }

  /* ── Pattern 3e: Switch (toggle-switch) — visible track + knob ── */
  & [role="switch"] {
    border: 2px solid var(--foreground) !important;
    background-color: var(--background) !important;
  }
  & [role="switch"][aria-checked="true"] {
    background-color: var(--accent) !important;
    border-color: var(--accent) !important;
  }
  & [role="switch"] > span {
    background-color: var(--foreground) !important;
  }
  & [role="switch"][aria-checked="true"] > span {
    background-color: var(--accent-foreground) !important;
  }

  /* ── Pattern 3f: Select / dropdown item focus — Highlight brand fill ── */
  & [data-slot="select-item"]:focus,
  & [data-slot="dropdown-menu-item"]:focus,
  & [data-slot="dropdown-menu-checkbox-item"]:focus,
  & [data-slot="dropdown-menu-radio-item"]:focus,
  & [data-slot="dropdown-menu-sub-trigger"]:focus {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
  }
  & [data-slot="select-item"]:focus *,
  & [data-slot="dropdown-menu-item"]:focus *,
  & [data-slot="dropdown-menu-checkbox-item"]:focus *,
  & [data-slot="dropdown-menu-radio-item"]:focus *,
  & [data-slot="dropdown-menu-sub-trigger"]:focus * {
    color: var(--accent-foreground) !important;
  }

  /* ── Pattern 3g: Dropdown check / radio indicator — inherit from parent ── */
  & [data-slot="dropdown-menu-checkbox-item"][data-state="checked"],
  & [data-slot="dropdown-menu-radio-item"][data-state="checked"] {
    font-weight: 700 !important;
  }

  /* ── Pattern 4: Board cards — border-defined, no decorative fills ── */
  & [data-slot="board-card"] {
    border: 2px solid var(--foreground) !important;
    background-image: none !important;
  }

  /* ── Pattern 6: Semantic status badges — border not fill ── */
  & [data-slot="status-badge"][data-kind="semantic"] {
    border: 2px solid var(--foreground) !important;
    background-color: var(--background) !important;
    font-weight: 700 !important;
    color: var(--foreground) !important;
  }

  /* ── Pattern 7: Insight cards — border-based, strip gradient bg ── */
  & [aria-label="Insight"],
  & [data-slot="insight-card"] {
    border: 2px solid var(--foreground) !important;
    background-image: none !important;
    background: var(--background) !important;
  }

  /* ── Pattern 8: Coach marks — strong border + readable text ── */
  & [data-slot="coach-mark"] {
    border: 2px solid var(--foreground) !important;
    background: var(--background) !important;
    background-color: var(--background) !important;
    color: var(--foreground) !important;
    background-image: none !important;
    opacity: 1 !important;
  }
  & [data-slot="coach-mark"] *:not(button):not(button *) {
    color: var(--foreground) !important;
  }

  /* ── Pattern 9: Avatars — thicken ring, strip blend modes, force contrast ── */
  & [data-slot="avatar"] {
    border: 2px solid var(--foreground) !important;
  }
  & [data-slot="avatar"]::after {
    display: none !important;
  }
  & [data-slot="avatar-fallback"] {
    background-color: var(--background) !important;
    color: var(--foreground) !important;
  }
  & tr[data-state="selected"] [data-slot="avatar"] {
    border-color: var(--accent-foreground) !important;
  }
  & tr[data-state="selected"] [data-slot="avatar-fallback"] {
    background-color: var(--accent-foreground) !important;
    color: var(--accent) !important;
  }

  /* ── Pattern 10: Breadcrumbs — ensure separators are visible ── */
  & [data-slot="breadcrumb-separator"] {
    color: var(--foreground) !important;
  }
  & [data-slot="breadcrumb-link"] {
    color: var(--foreground) !important;
    text-decoration: underline !important;
  }
  & [data-slot="breadcrumb-page"] {
    color: var(--foreground) !important;
    font-weight: 700 !important;
  }

  /* ── Pattern 11: Tooltip / popover arrow — match border ── */
  & [data-slot="tooltip-content"] {
    border: 2px solid var(--foreground) !important;
    background-color: var(--background) !important;
    color: var(--foreground) !important;
  }

  /* ── Pattern 12: Separator — visible in HC ── */
  & [data-slot="separator"] {
    background-color: var(--foreground) !important;
    opacity: 1 !important;
  }

  /* ── Pattern 13: Strip ALL hardcoded Tailwind palette colors ──────────── */
  & [data-slot="banner"],
  & [data-slot="local-banner"] {
    background-color: var(--background) !important;
    background-image: none !important;
    border: 2px solid var(--foreground) !important;
    color: var(--foreground) !important;
  }
  & [data-slot="banner"] *,
  & [data-slot="local-banner"] * {
    color: var(--foreground) !important;
  }
  /* The promo banner layers an absolutely-positioned decorative SVG over its
     gradient. Once the gradient is gone the artwork is painting near-canvas
     shapes on the canvas: no longer decoration, just noise the probe reads as
     a 1.4:1 graphical object. It carries no information, so it goes. */
  & [data-slot="banner"] > [aria-hidden]:first-child:has(svg),
  & [data-slot="local-banner"] > [aria-hidden]:first-child:has(svg) {
    display: none !important;
  }

  /* ── Pattern 13-bis: Charts ─────────────────────────────────────────────
     Bars and trapezoids keep their canvas-coloured separator: the fill is
     already a ramp step that clears 3:1, and the gap is what stops two
     adjacent bars from reading as one block. */
  & .recharts-bar-rectangle,
  & .recharts-funnel-trapezoid {
    stroke: var(--background) !important;
    stroke-width: 2 !important;
  }

  /* Area washes are the opposite case. They are drawn at 8-20% opacity so
     stacked series can overlap, which means every one of them composites to
     roughly the canvas colour — the series exists but nothing is visible.
     Opacity can't simply go to 1 (the stack would occlude itself), so the
     wash is dropped and the curve becomes the series: a 2.5px stroke at the
     ramp colour, which is the part that already clears 3:1. */
  & .recharts-area-area {
    fill: none !important;
    fill-opacity: 0 !important;
  }
  & .recharts-area-curve,
  & .recharts-line-curve {
    stroke-width: 2.5 !important;
    stroke-opacity: 1 !important;
  }

  /* Progress / radial tracks are a 10% foreground wash in normal mode. Same
     problem, different shape: give the track an outline instead of a fill. */
  & .recharts-radial-bar-background-sector {
    fill: var(--background) !important;
    stroke: var(--foreground) !important;
    stroke-width: 1 !important;
  }
  & .recharts-cartesian-grid line {
    stroke: var(--border) !important;
    stroke-opacity: 1 !important;
  }
  & .recharts-cartesian-axis-tick-value {
    fill: var(--foreground) !important;
  }

  & [role="progressbar"] {
    border: 1px solid var(--foreground) !important;
    background-color: var(--background) !important;
  }
  & [role="progressbar"] > * {
    background-color: var(--foreground) !important;
  }

  & [class*="text-emerald"],
  & [class*="text-amber"],
  & [class*="text-red-"],
  & [class*="text-blue-"],
  & [class*="text-green-"],
  & [class*="text-yellow-"],
  & [class*="text-slate-"],
  & [class*="text-orange-"],
  & [class*="text-pink-"],
  & [class*="text-purple-"] {
    color: var(--foreground) !important;
  }

  /* Flattening a brand wash to the canvas is right for decoration and wrong
     for state. A filter chip, segment, or pill that shows "selected" by
     painting itself `bg-brand` loses the only thing distinguishing it from its
     neighbours the moment the fill becomes the canvas — the row reads as five
     identical outlines. `:where()` keeps these guards at zero specificity so
     the rules still lose to the deliberate component patterns above. */
  & [class*="text-brand"]:not(:where(
      [data-state="on"], [data-state="checked"], [data-state="active"],
      [data-state="selected"], [data-active="true"], [data-selected],
      [aria-checked="true"], [aria-pressed="true"], [aria-selected="true"],
      [aria-current]:not([aria-current="false"])
    )) {
    color: var(--accent) !important;
  }
  & [class*="bg-brand"]:not(:where(
      [data-slot="coach-mark"],
      [data-state="on"], [data-state="checked"], [data-state="active"],
      [data-state="selected"], [data-active="true"], [data-selected],
      [aria-checked="true"], [aria-pressed="true"], [aria-selected="true"],
      [aria-current]:not([aria-current="false"])
    )) {
    background-color: var(--background) !important;
  }
  & [class*="border-brand"] {
    border-color: var(--foreground) !important;
  }

  /* The other half: those same selected surfaces keep a fill, swapped from the
     brand hue (which collapses toward the ink once the palette goes greyscale)
     to the theme's Highlight, with the ink Highlight is designed to carry. */
  & [class*="bg-brand"]:is(
      [data-state="on"], [data-state="checked"], [data-state="active"],
      [data-state="selected"], [data-active="true"], [data-selected],
      [aria-checked="true"], [aria-pressed="true"], [aria-selected="true"],
      [aria-current]:not([aria-current="false"])
    ) {
    background-color: var(--accent) !important;
    color: var(--accent-foreground) !important;
    border-color: var(--foreground) !important;
  }

  & [class*="bg-emerald"],
  & [class*="bg-amber"],
  & [class*="bg-red-"],
  & [class*="bg-blue-"],
  & [class*="bg-green-"],
  & [class*="bg-yellow-"],
  & [class*="bg-slate-"],
  & [class*="bg-orange-"],
  & [class*="bg-pink-"],
  & [class*="bg-purple-"] {
    background-color: var(--background) !important;
  }

  & [class*="border-emerald"],
  & [class*="border-amber"],
  & [class*="border-red-"],
  & [class*="border-blue-"],
  & [class*="border-green-"],
  & [class*="border-yellow-"],
  & [class*="border-slate-"],
  & [class*="border-orange-"],
  & [class*="border-pink-"],
  & [class*="border-purple-"] {
    border-color: var(--foreground) !important;
  }

  /* ── Pattern 13-ter: a palette fill on an EMPTY element is the graphic ────
     The rules above assume the coloured element is a container — a tinted
     card, a badge, a banner — where flattening the wash to the canvas is
     right because the text inside still carries the meaning. An element with
     no children has nothing to fall back on: the fill IS the object. Difficulty
     meter bars, legend swatches, status dots, and severity pips all render as
     bare `<span class="bg-amber-500">`, so the container rule was painting
     them canvas-on-canvas and deleting them from the page.

     `:empty` adds specificity, so these win over the block above without
     needing to be reordered.

     Restricted to `div` / `span` / `i` (and a few text tags): an `<input>` is
     also childless and also textless, and its class list mentions `bg-muted`
     for the disabled state, so an unrestricted `:empty` rule would repaint
     every text field. */
  & :is(div, span, i, b, em):is(
      [class*="bg-emerald"], [class*="bg-amber"], [class*="bg-red-"],
      [class*="bg-blue-"], [class*="bg-green-"], [class*="bg-yellow-"],
      [class*="bg-slate-"], [class*="bg-orange-"], [class*="bg-pink-"],
      [class*="bg-purple-"], [class*="bg-brand"], [class*="bg-chart-"],
      [class*="bg-destructive"]
    ):empty {
    background-color: var(--foreground) !important;
  }

  /* SVG paints named after the brand or the muted surface. Both were picked
     for a soft canvas: brand chroma collapses toward the ink once the ramp is
     greyscale, and `--muted` is defined as barely-there. A 2px progress arc
     and its track have nothing else to survive on. */
  & [class*="stroke-muted"] {
    stroke: var(--border) !important;
  }
  & [class*="fill-muted"] {
    fill: var(--border) !important;
  }
  & [class*="stroke-brand"],
  & [class*="stroke-[var(--brand"] {
    stroke: var(--foreground) !important;
  }
  & [class*="fill-brand"],
  & [class*="fill-[var(--brand"] {
    fill: var(--foreground) !important;
  }

  /* Charts colour a series by handing Recharts a token — `--color-value:
     var(--brand-color)` — which lands on the `fill`/`stroke` *attribute*, out
     of reach of any class-based rule. Rebinding the brand tokens inside the
     chart is the only lever that reaches it. Scoped to `[data-chart]` on
     purpose: `--hc-highlight` derives its hue from `--brand-color`, so
     rebinding it globally would drain the colour out of every selected row,
     tab, and checkbox in the theme. */
  & [data-chart] {
    --brand-color:       var(--foreground);
    --brand-color-dark:  var(--foreground);
    --brand-color-light: var(--foreground);
    --brand-color-deep:  var(--foreground);
  }

  /* The unfilled half of the same meters. `--muted` sits ~1.1:1 from the
     canvas by design (it is meant to disappear), which is fine behind text and
     fatal for a 4px track. `--border` is the token that already promises a
     visible-but-quiet edge in HC, and it stays clearly weaker than the
     foreground-filled segments beside it so the meter still reads as a meter. */
  & :is(div, span, i, b, em)[class*="bg-muted"]:empty {
    background-color: var(--border) !important;
  }

  /* ── Last word: ink on an accent fill ────────────────────────────────────
     Patterns 3 and 4 fill the selected tab, toggle, row, menu item, and nav
     button with `--accent`, which in the Windows palettes is a saturated
     Highlight (yellow on black, blue on white) whose only legible partner is
     `--accent-foreground`. The palette-stripping rules further up then walk
     over any descendant carrying a hardcoded Tailwind colour and force it to
     `--foreground` — correct on the canvas, and exactly backwards here: a
     white trend arrow inside a selected tab landed on yellow at 1.07:1.

     These rules run last so the fill and its ink are decided together. The
     selector list mirrors the accent-fill patterns above; a new accent-filled
     state needs a line here too. */
  & [data-slot="tabs-trigger"][data-state="active"] *:not([class*="bg-"]),
  & [data-slot="toggle-group-item"][data-state="on"] *:not([class*="bg-"]),
  & [data-slot="toggle"][data-state="on"] *:not([class*="bg-"]),
  & [data-slot="view-segmented-item"][aria-pressed="true"] *:not([class*="bg-"]),
  & [data-slot="view-segmented-item"][aria-checked="true"] *:not([class*="bg-"]),
  & [data-slot="view-segmented-item-shell"] *:not([class*="bg-"]),
  & [data-slot="sidebar-menu-button"][data-active="true"] *:not([class*="bg-"]):not([data-slot="badge"]):not([data-slot="sidebar-menu-badge"]):not([data-slot="badge"] *):not([data-slot="sidebar-menu-badge"] *),
  & [data-slot="secondary-panel"] [aria-current="page"] *:not([class*="bg-"]),
  & [data-slot="dropdown-menu-item"]:focus *:not([class*="bg-"]),
  & [data-slot="dropdown-menu-checkbox-item"]:focus *:not([class*="bg-"]),
  & [data-slot="dropdown-menu-radio-item"]:focus *:not([class*="bg-"]),
  & [data-slot="dropdown-menu-sub-trigger"]:focus *:not([class*="bg-"]),
  & [data-slot="dropdown-menu-item"][data-highlighted] *:not([class*="bg-"]),
  & [data-slot="dropdown-menu-sub-trigger"][data-highlighted] *:not([class*="bg-"]),
  & [data-slot="select-item"][data-highlighted] *:not([class*="bg-"]),
  & [data-slot="command-item"][data-selected] *:not([class*="bg-"]),
  & [data-slot="command-item"][aria-selected="true"] *:not([class*="bg-"]),
  & [class*="bg-brand"]:is(
      [data-state="on"], [data-state="checked"], [data-state="active"],
      [data-state="selected"], [data-active="true"], [data-selected],
      [aria-checked="true"], [aria-pressed="true"], [aria-selected="true"],
      [aria-current]:not([aria-current="false"])
    ) *:not([class*="bg-"]) {
    color: var(--accent-foreground) !important;
  }

  /* The `:not([class*="bg-"])` above is the whole point of the pair: a child
     that paints its own background is not sitting on the accent, so accent ink
     would be measured against the wrong surface. The count pill on a selected
     tab is the case that proved it — `bg-muted` + accent ink came out black on
     near-black. A badge inverts, which is the treatment the active sidebar
     badge already uses. */
  & [data-slot="tabs-trigger"][data-state="active"] > :is([data-slot="badge"], [data-slot="sidebar-menu-badge"], [class*="rounded-full"])[class*="bg-"],
  & [data-slot="toggle-group-item"][data-state="on"] > :is([data-slot="badge"], [data-slot="sidebar-menu-badge"], [class*="rounded-full"])[class*="bg-"],
  & [data-slot="view-segmented-item"][aria-pressed="true"] > :is([data-slot="badge"], [data-slot="sidebar-menu-badge"], [class*="rounded-full"])[class*="bg-"],
  & [data-slot="view-segmented-item-shell"] > :is([data-slot="badge"], [data-slot="sidebar-menu-badge"], [class*="rounded-full"])[class*="bg-"] {
    background-color: var(--accent-foreground) !important;
    color: var(--accent) !important;
  }

  /* Everything else that carries a background inside an accent-filled surface
     is structure, not a pill — the segmented control wraps its button in a
     shell and both declare a fill. Inverting the wrapper hid its own contents
     behind an identical colour, so structure steps aside and lets the accent
     through instead. */
  & [data-slot="tabs-trigger"][data-state="active"] > [class*="bg-"]:not([data-slot="badge"]):not([data-slot="sidebar-menu-badge"]):not([class*="rounded-full"]),
  & [data-slot="toggle-group-item"][data-state="on"] > [class*="bg-"]:not([data-slot="badge"]):not([data-slot="sidebar-menu-badge"]):not([class*="rounded-full"]),
  & [data-slot="view-segmented-item"][aria-pressed="true"] > [class*="bg-"]:not([data-slot="badge"]):not([data-slot="sidebar-menu-badge"]):not([class*="rounded-full"]),
  & [data-slot="view-segmented-item-shell"] > [class*="bg-"]:not([data-slot="badge"]):not([data-slot="sidebar-menu-badge"]):not([class*="rounded-full"]) {
    background-color: transparent !important;
    color: var(--accent-foreground) !important;
  }
}

/* Coach mark HC override (top-level, outside nesting — bulletproof for portaled content) */
html:is([data-contrast="high"], [data-contrast="windows"]) [data-slot="coach-mark"] {
  border: 2px solid var(--foreground) !important;
  background: var(--background) !important;
  background-color: var(--background) !important;
  color: var(--foreground) !important;
  background-image: none !important;
  opacity: 1 !important;
}
html:is([data-contrast="high"], [data-contrast="windows"]) [data-slot="coach-mark"] *:not(button):not(button *) {
  color: var(--foreground) !important;
}

/* ── OS-level "Increase Contrast" — macOS / iOS / Android ───────────────── */
@media (prefers-contrast: more) {
  :root:not([data-contrast="off"]):not([data-contrast="windows"]) {
    --foreground:        oklch(0.06 0 0);
    --muted-foreground:  oklch(0.10 0 0);
    --border:            oklch(0.10 0 0);
    --border-control:    oklch(0.06 0 0);
    --border-control-35: oklch(0.06 0 0);
    --border-control-3:  oklch(0.06 0 0);
    --ring:              oklch(0.06 0 0);
  }
  .dark:not([data-contrast="off"]):not([data-contrast="windows"]) {
    --foreground:        oklch(1 0 0);
    --muted-foreground:  oklch(0.88 0 0);
    --border:            oklch(1 0 0);
    --border-control:    oklch(0.88 0 0);
    --border-control-35: oklch(0.88 0 0);
    --border-control-3:  oklch(0.88 0 0);
    --ring:              oklch(0.97 0.19 110);  /* neon yellow on dark */
  }
}

/* ── Windows Forced Colors (High Contrast Mode in OS) ───────────────────── */
/*    When `forced-colors: active`, Windows REPLACES our colours entirely.   */
/*    Our job: map semantic roles to system palette keywords so the OS       */
/*    colouring makes sense, and ensure borders/outlines remain visible.     */
@media (forced-colors: active) {
  :root {
    --border:            CanvasText;
    --border-control:    CanvasText;
    --border-control-35: CanvasText;
    --border-control-3:  CanvasText;
    --ring:              Highlight;
    --destructive:       LinkText;
  }

  /* Always show a 2px outline so focus is forced-color-safe */
  :focus-visible {
    outline: 2px solid Highlight !important;
    outline-offset: 2px !important;
  }

  body {
    background-color: Canvas;
    color: CanvasText;
  }

  a          { color: LinkText; }
  button     { border: 1px solid ButtonText !important; }

  /* Cards: OS strips bg fills — add explicit border so shape is preserved */
  [data-slot="card"],
  .card {
    border: 2px solid CanvasText !important;
  }

  /* Sidebar edge */
  [data-slot="sidebar"] {
    border-inline-end: 2px solid CanvasText !important;
  }

  /* Wrapper-only sidebar badge (no inner Badge) — rare; keep single ring */
  [data-slot="sidebar-menu-badge"]:not(:has([data-slot="badge"])) {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
    color: CanvasText !important;
    outline: none !important;
    box-shadow: none !important;
  }

  /* Positioning shell around <Badge/> — do not draw a second ring */
  [data-slot="sidebar-menu-badge"]:has([data-slot="badge"]) {
    border: none !important;
    background: transparent !important;
    outline: none !important;
    box-shadow: none !important;
  }

  [data-slot="badge"],
  [data-slot="view-toolbar-count"] {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
    color: CanvasText !important;
    outline: none !important;
    box-shadow: none !important;
  }

  [data-slot="badge"] *,
  [data-slot="view-toolbar-count"] * {
    color: CanvasText !important;
  }

  /* Avoid button chrome + inner count pill = double border */
  button[data-slot="view-segmented-item"] {
    border: none !important;
  }

  /*
   * Group dividers carry their fill as an inline style, because it is computed
   * per group (status tone, or a categorical hue keyed off the value). Forced
   * colours replace our palette wholesale, and an inline background outranks
   * anything a stylesheet declares without `!important`, so the band would keep
   * a tint the OS never sanctioned and strand `CanvasText` on it. Drop to Canvas
   * and let the bar's own border carry the boundary.
   */
  [data-dt-group-bar] {
    background: Canvas !important;
    color: CanvasText !important;
  }

  /* Radix Tabs (ChartCard, library, etc.) — gray inactive + invisible active in OS HC */
  [data-slot="tabs-list"] {
    border: 2px solid CanvasText !important;
    background-color: transparent !important;
  }

  [data-slot="tabs-trigger"] {
    color: CanvasText !important;
    background-color: transparent !important;
    opacity: 1 !important;
  }

  [data-slot="tabs-trigger"][data-state="active"] {
    background-color: Highlight !important;
    color: HighlightText !important;
    outline: none !important;
    font-weight: 600 !important;
  }

  /* Key metrics strip — labels/values/trend icons must not stay muted */
  [data-slot="key-metrics"] :where(span, p, button, a, i, label, div) {
    color: CanvasText !important;
  }

  /* Menu / select / command palette — accent fill is often low-contrast in forced mode */
  [data-slot="dropdown-menu-item"][data-highlighted]:not([data-disabled]),
  [data-slot="dropdown-menu-checkbox-item"][data-highlighted]:not([data-disabled]),
  [data-slot="dropdown-menu-radio-item"][data-highlighted]:not([data-disabled]),
  [data-slot="dropdown-menu-sub-trigger"][data-highlighted]:not([data-disabled]),
  [data-slot="select-item"][data-highlighted]:not([data-disabled]),
  [data-slot="command-item"][data-selected]:not([data-disabled]),
  [data-slot="command-item"][aria-selected="true"]:not([data-disabled]) {
    background-color: Highlight !important;
    color: HighlightText !important;
  }

  [data-slot="dropdown-menu-item"][data-highlighted] *,
  [data-slot="dropdown-menu-checkbox-item"][data-highlighted] *,
  [data-slot="dropdown-menu-radio-item"][data-highlighted] *,
  [data-slot="dropdown-menu-sub-trigger"][data-highlighted] *,
  [data-slot="select-item"][data-highlighted] *,
  [data-slot="command-item"][data-selected] *,
  [data-slot="command-item"][aria-selected="true"] * {
    color: HighlightText !important;
  }

  /* Views toolbar (list hubs) + segmented radiogroup — selection via outline, not fill */
  [data-slot="view-segmented-toolbar"] {
    border: 2px solid CanvasText !important;
    background: transparent !important;
  }

  [data-slot="view-segmented-item"] {
    color: CanvasText !important;
    background: transparent !important;
  }

  [data-slot="view-segmented-item"][aria-pressed="true"],
  [data-slot="view-segmented-item"][aria-checked="true"] {
    background-color: Highlight !important;
    color: HighlightText !important;
    outline: none !important;
  }

  /* Composed view-tab active shell — see the html[data-contrast="high"] block
     above for rationale. Fill the shell + cascade `HighlightText` so the
     chevron disclosure stays attached to the active fill in forced-colors. */
  [data-slot="view-segmented-item-shell"] {
    background-color: Highlight !important;
    color: HighlightText !important;
    outline: none !important;
  }

  [data-slot="view-segmented-item-shell"] * {
    color: HighlightText !important;
  }

  /* Sidebar active item — Highlight brand fill */
  [data-slot="sidebar-menu-button"][data-active="true"] {
    background-color: Highlight !important;
    color: HighlightText !important;
    outline: none !important;
  }
  [data-slot="sidebar-menu-button"][data-active="true"] * {
    color: HighlightText !important;
  }
  [data-slot="sidebar-menu-button"][data-active="true"] [data-slot="badge"],
  [data-slot="sidebar-menu-button"][data-active="true"] [data-slot="sidebar-menu-badge"] {
    background-color: HighlightText !important;
    color: Highlight !important;
    border-color: HighlightText !important;
  }
  [data-slot="sidebar-menu-button"][data-active="true"] [data-slot="badge"] *,
  [data-slot="sidebar-menu-button"][data-active="true"] [data-slot="sidebar-menu-badge"] * {
    color: Highlight !important;
  }

  /* Strip conditional-formatting inline bg in forced-colors */
  td[style*="background"] {
    background: Canvas !important;
  }
  td.pinned-cell {
    background-color: Canvas !important;
  }
  tr:hover td,
  tr:hover td.pinned-cell {
    background-color: Highlight !important;
    color: HighlightText !important;
  }

  /* Open-row bar — which row the detail rail is about. The brand fill it uses
     elsewhere is dropped in forced-colors, so it takes Highlight here, and
     HighlightText on a row that is itself filled with Highlight (selected or
     hovered) where a Highlight bar would vanish into the row. */
  tr[data-open] > td:first-child::before {
    background-color: Highlight !important;
  }
  tr[data-open]:hover > td:first-child::before,
  tr[data-open][data-state="selected"] > td:first-child::before {
    background-color: HighlightText !important;
  }

  /* Table selected rows — Highlight brand fill */
  tr[data-state="selected"] {
    background-color: Highlight !important;
    color: HighlightText !important;
  }
  tr[data-state="selected"] td {
    background: Highlight !important;
    background-color: Highlight !important;
    color: HighlightText !important;
  }
  tr[data-state="selected"] *:not([data-slot="checkbox"] *):not([data-slot="badge"] *):not([data-slot="checkbox"]):not([data-slot="badge"]) {
    color: HighlightText !important;
  }
  tr[data-state="selected"] [data-slot="checkbox"][data-state="checked"],
  tr[data-state="selected"] [data-slot="checkbox"][data-state="indeterminate"] {
    background-color: HighlightText !important;
    border-color: HighlightText !important;
    color: Highlight !important;
  }
  tr[data-state="selected"] [data-slot="checkbox"][data-state="checked"] *,
  tr[data-state="selected"] [data-slot="checkbox"][data-state="indeterminate"] * {
    color: Highlight !important;
  }
  tr[data-state="selected"] [data-slot="checkbox"]:not([data-state="checked"]):not([data-state="indeterminate"]) {
    background-color: Highlight !important;
    border-color: HighlightText !important;
    color: HighlightText !important;
  }
  tr[data-state="selected"] [data-slot="badge"] {
    background-color: HighlightText !important;
    color: Highlight !important;
    border-color: HighlightText !important;
  }
  tr[data-state="selected"] [data-slot="badge"] * {
    color: Highlight !important;
  }
  tr[data-state="selected"] [data-slot="avatar"] {
    border-color: HighlightText !important;
  }
  tr[data-state="selected"] [data-slot="avatar-fallback"] {
    background-color: HighlightText !important;
    color: Highlight !important;
  }
  /* Badge in forced-colors — opaque canvas bg */
  [data-slot="badge"] {
    background-color: Canvas !important;
    border-color: CanvasText !important;
    color: CanvasText !important;
  }

  /* Checked checkboxes — Highlight fill */
  [data-slot="checkbox"][data-state="checked"] *,
  [data-slot="checkbox"][data-state="indeterminate"] * {
    color: HighlightText !important;
  }

  /* Avatar in forced-colors */
  [data-slot="avatar"] {
    border: 2px solid CanvasText !important;
  }
  [data-slot="avatar"]::after {
    display: none !important;
  }
  [data-slot="avatar-fallback"] {
    background-color: Canvas !important;
    color: CanvasText !important;
  }

  /* Checked checkboxes / radios — Highlight fill */
  [data-slot="checkbox"][data-state="checked"],
  [data-slot="checkbox"][data-state="indeterminate"] {
    background-color: Highlight !important;
    border-color: Highlight !important;
    color: HighlightText !important;
  }
  [data-slot="radio-group-item"][data-state="checked"] {
    background-color: Highlight !important;
    border-color: Highlight !important;
  }

  /* Switch ON — Highlight fill */
  [role="switch"][aria-checked="true"] {
    background-color: Highlight !important;
    border-color: Highlight !important;
  }
  [role="switch"][aria-checked="true"] > span {
    background-color: HighlightText !important;
  }

  /* Toggle ON — Highlight fill */
  [data-slot="toggle"][data-state="on"],
  [data-slot="toggle-group-item"][data-state="on"] {
    background-color: Highlight !important;
    color: HighlightText !important;
    border-color: Highlight !important;
  }

  /* Progress bars — bordered track with CanvasText fill */
  [role="progressbar"] {
    border: 1px solid CanvasText !important;
    background-color: Canvas !important;
  }
  [role="progressbar"] > * {
    background-color: CanvasText !important;
  }

  /* Portal overlays — explicit Canvas pairing (OS often strips popover fills) */
  [data-slot="dropdown-menu-content"],
  [data-slot="dropdown-menu-sub-content"],
  [data-slot="popover-content"],
  [data-slot="select-content"],
  [data-slot="dialog-content"],
  [data-slot="sheet-content"],
  [data-slot="drawer-content"] {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
    color: CanvasText !important;
  }

  [data-slot="command"] {
    background-color: Canvas !important;
    color: CanvasText !important;
  }

  /* Board cards — border-defined shape when fills are stripped */
  [data-slot="board-card"] {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
    color: CanvasText !important;
  }

  /* Semantic status badges — border-only, text readable */
  [data-slot="status-badge"][data-kind="semantic"] {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
    color: CanvasText !important;
    font-weight: 700 !important;
  }

  /* Insight cards — strip gradient, add border */
  [aria-label="Insight"],
  [data-slot="insight-card"] {
    border: 2px solid CanvasText !important;
    background: Canvas !important;
    color: CanvasText !important;
  }

  /* Coach marks / guided tours */
  [data-slot="coach-mark"] {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
    color: CanvasText !important;
  }
  [data-slot="coach-mark"] * {
    color: CanvasText !important;
  }

  /* Avatars — thicken existing ::after ring */
  [data-slot="avatar"]::after {
    border-width: 2px !important;
    border-color: CanvasText !important;
  }

  /* Breadcrumbs */
  [data-slot="breadcrumb-separator"] {
    color: CanvasText !important;
  }
  [data-slot="breadcrumb-link"] {
    color: LinkText !important;
  }
  [data-slot="breadcrumb-page"] {
    color: CanvasText !important;
    font-weight: 700 !important;
  }

  /* Tooltip */
  [data-slot="tooltip-content"] {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
    color: CanvasText !important;
  }

  /* Export drawer */
  [data-slot="export-drawer"] {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
    color: CanvasText !important;
  }

  /* Separators */
  [data-slot="separator"] {
    background-color: CanvasText !important;
    opacity: 1 !important;
  }

  /* Charts — forced-colors strips SVG fills; add stroke as fallback */
  svg path, svg rect, svg circle {
    forced-color-adjust: none;
  }

  /* Recharts radial bars, lines, and area — explicit stroke fallbacks */
  .recharts-radial-bar-sector,
  .recharts-line-curve,
  .recharts-area-curve {
    forced-color-adjust: none;
    stroke: CanvasText !important;
  }

  .recharts-radial-bar-background-sector {
    forced-color-adjust: none;
    fill: Canvas !important;
    stroke: GrayText !important;
  }

  .recharts-cartesian-axis-tick-value {
    fill: CanvasText !important;
  }

  .recharts-cartesian-grid line {
    stroke: GrayText !important;
  }

  .recharts-legend-item-text {
    color: CanvasText !important;
  }

  .recharts-tooltip-wrapper {
    border: 2px solid CanvasText !important;
    background-color: Canvas !important;
  }
}

/* ==========================================================================
   Base layer — global element defaults with AA focus management
   ========================================================================== */
@layer base {
  /* Reset + border-color default */
  *, *::before, *::after {
    @apply border-border;
    box-sizing: border-box;
  }

  /* Body — outer canvas matches sidebar; main column uses bg-background (white in light) */
  body {
    @apply bg-sidebar text-foreground antialiased;
    font-family: var(--font-sans);
    /* 14px baseline on body. 1rem stays = 16px so all shadcn rem-based
       layout (spacing, heights, radii) is unaffected at any zoom level. */
    font-size: 0.875rem;
    line-height: 1.5;
    /* Font smoothing (Tailwind `antialiased` = -webkit-font-smoothing + -moz-osx):
       Industry practice per Josh Comeau’s CSS reset and 2024 cross-browser testing
       (e.g. David Bushell): the -webkit property affects macOS WebKit browsers only —
       it tones down browser-only subpixel AA (Mojave+ system UI already moved away).
       Windows/Linux/iOS/Android: no practical effect; OS font rasterizer (ClearType, etc.)
       owns rendering. Safe to apply on body for Mac parity without changing Windows output. */
  }

  html {
    @apply font-sans;
    color-scheme: light;
    /* Match sidebar canvas (body) on overscroll */
    background-color: var(--sidebar);
  }

  html.dark {
    color-scheme: dark;
  }

  /* ── Focus management (WCAG 2.4.11 / 2.4.7) ──────────────────── */
  /* Visible on keyboard navigation only; hidden for mouse users    */
  :focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
    border-radius: var(--radius-sm, 4px);
  }

  /* Suppress outline for mouse/touch (Safari workaround) */
  :focus:not(:focus-visible) {
    outline: none;
  }

  /* ── Reduced motion (WCAG 2.3.3) ────────────────────────────── */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }

  /* ── Touch targets (WCAG 2.5.5) ─────────────────────────────── */
  /* Applied via utility: min-h-[var(--control-height-touch)]      */
  /* Coarse pointer only: browser zoom (200–400%) shrinks the *layout* viewport
     in CSS px, so max-width: 767px becomes true on desktop — without pointer: coarse,
     44px min-height hits every button and [role=checkbox], stretching 16px-wide
     checkboxes into tall pills and blowing up table/toolbar controls. Real phones
     and tablets report pointer: coarse. */
  @media (max-width: 767px) and (pointer: coarse) {
    button,
    [role="button"],
    [role="radio"],
    [role="switch"],
    [role="menuitem"],
    [role="tab"],
    a {
      min-height: var(--control-height-touch, 44px);
    }

    /* Dense / authored compact targets — keep intended geometry. */
    [data-slot="checkbox"],
    [data-size="icon-xs"],
    [data-compact-touch],
    [data-slot="horizontal-scroll-controls"] button,
    .icon-button-chrome,
    button[class*="aspect-square"]:not([data-size^="icon"]),
    a[class*="aspect-square"] {
      min-height: unset;
      min-width: unset;
    }

    /* Icon-only buttons — square touch target (width + height), not a tall pill. */
    [data-size^="icon"]:not([data-size="icon-xs"]) {
      min-height: var(--control-height-touch, 44px);
      min-width: var(--control-height-touch, 44px);
      width: var(--control-height-touch, 44px);
      height: var(--control-height-touch, 44px);
    }
  }

  /* ── Heading hierarchy ───────────────────────────────────────── */
  /* Ivy Presto (font-heading) applied ONLY via explicit class/inline style
     on PageHeader <h1>. All other headings use Inter (font-sans). */
  h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-sans);
    font-weight: 600;
    line-height: 1.25;
    color: var(--foreground);
  }

  /* ── Skip to main content (WCAG 2.4.1) ─────────────────────── */
  /* Off-screen until focused — must stay focusable (no pointer-events: none). */
  .skip-to-content {
    position: fixed;
    top: 0.75rem;
    left: 0.75rem;
    z-index: 9999;
    padding: 0.75rem 1.25rem;
    background: var(--background);
    color: var(--foreground);
    border: 2px solid var(--ring);
    border-radius: var(--radius-md);
    font-weight: 600;
    font-size: 1rem;
    transform: translateY(calc(-100% - 1rem));
    opacity: 0;
  }

  .skip-to-content:focus,
  .skip-to-content:focus-visible {
    transform: translateY(0);
    opacity: 1;
  }
}

/* ==========================================================================
   Utility layer — design-system helpers
   ========================================================================== */
@layer utilities {
  /* Visually hidden but accessible to screen readers (WCAG 1.3.1) */
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
  }

  /* Alias — same token as text-muted-foreground (kept for legacy call sites) */
  .text-aa-muted {
    color: var(--muted-foreground);
  }

  /* Touch-target wrapper for icon buttons (mobile only) */
  .touch-target {
    min-height: var(--control-height-touch, 44px);
    min-width:  var(--control-height-touch, 44px);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Floating sheets (Properties, export, invite) use z-80 — portaled menus/tooltips must sit above. */
  .z-properties-sheet-portal {
    z-index: 90;
  }

  /*
   * App shell stacking — main inset card above inline nav rails so rounded corners
   * are not painted over by sidebar / secondary panel / Ask Leo. Flyout overlays
   * keep component-level z-40–z-60 (mobile / high-zoom).
   */
  [data-app-shell-row] {
    isolation: isolate;
    --app-shell-inset-y: 0.375rem;
  }

  /*
   * Flyout sheets must paint over the full-width utility bar (sibling `z-50`).
   * Component z-index alone cannot escape this row's stacking context — raise the
   * row while any overlay flyout is open so primary / secondary / Ask Leo cover
   * the header (viewport inset sheets, not docked under the bar).
   */
  [data-app-shell-row]:has(
    [data-slot="sidebar"][data-nav-flyout="true"][data-state="expanded"]
  ),
  [data-app-shell-row]:has(
    [data-slot="secondary-panel"][data-overlay][data-state="open"]
  ),
  [data-app-shell-row]:has(
    [data-slot="ask-leo-panel"][data-overlay][data-state="open"]
  ) {
    z-index: 60;
  }

  /*
   * Full-width utility bar — bar is a flex row above `[data-app-shell-row]`.
   * Pin fixed sidebar below the bar (and the optional full-bleed system
   * banner above it); drop workspace top padding so rails + main sit flush
   * under the bar (no pink band).
   */
  [data-shell-utility-bar-full] [data-app-shell-workspace] {
    padding-top: 0;
  }

  /*
   * Compact shell density.
   *
   * The compact bar is `var(--shell-utility-bar-height)` (2.625rem / 42px);
   * rail offset and `--header-height` read the same token. Density also comes
   * from dropping the SiteHeader row (`components/compact-header-slot.tsx`).
   *
   * Icon buttons stay size-8 inside the bar (above the 24px target floor).
   */
  [data-shell-density="compact"] [data-slot="sidebar-group"] {
    padding-block: var(--exxat-spacing-1);
  }

  /*
   * No cards in this variant — docked rails / inset only.
   *
   * `SidebarInset` and docked secondary / Ask Leo are flush at the source
   * (no radius / gutter / shadow). These rules stay as belt-and-suspenders for
   * any leftover utility classes or consumer forks, and they still own the
   * canvas↔rail hairline once gutters are gone.
   *
   * Overlay flyouts (primary mobile sheet, secondary sheet, Ask Leo sheet) keep
   * radius + shadow so they still read as sheets over the canvas. Compact must
   * not flatten those.
   */
  [data-shell-density="compact"] [data-slot="sidebar-inset"],
  [data-shell-density="compact"] [data-slot="secondary-panel"]:not([data-overlay]),
  [data-shell-density="compact"] [data-slot="ask-leo-panel"]:not([data-overlay]) {
    margin-inline: 0;
    margin-block: 0;
    border-radius: 0;
    box-shadow: none;
  }

  [data-shell-density="compact"] [data-slot="secondary-panel"][data-overlay],
  [data-shell-density="compact"] [data-slot="ask-leo-panel"][data-overlay],
  [data-shell-density="compact"]
    [data-slot="sidebar"][data-nav-flyout="true"]
    [data-slot="sidebar-container"] {
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-sheet-flyout);
  }

  /*
   * Icon rail only — expanded secondary / Ask Leo use `ResizableHandle`
   * (1px `bg-border` + optional grip). Applying a border on expanded rails
   * doubles the separator next to the handle.
   */
  [data-shell-density="compact"]
    [data-slot="secondary-panel"][data-layout="icon"]:not([data-overlay]) {
    border-inline-end: 1px solid var(--sidebar-border);
  }

  [data-shell-density="compact"]
    [data-slot="ask-leo-panel"][data-own-size="true"]:not([data-overlay]) {
    border-inline-start: 1px solid var(--sidebar-border);
  }

  /*
   * The bar sits on the page, not on the rail.
   *
   * `bg-sidebar` (brand tint) is right for the other variants, where the bar is
   * chrome wrapped around a floating canvas. Compact has no gutter and no card,
   * so the bar is the top edge of the page itself and takes the page's own
   * surface. It also carries the breadcrumb, which belongs to the page rather
   * than to the shell.
   *
   * `--background`, not literal white, so dark mode and the contrast modes
   * follow. Overriding here rather than branching the `bg-sidebar` utility in
   * `UtilityBarSlot` keeps every compact-only treatment in one block; (0,2,0)
   * beats the utility's (0,1,0) in the same layer.
   *
   * The bottom border matters more now. The other variants never needed one:
   * the inset card's gutter opens a visible gap under the bar, and `SiteHeader`
   * draws its own `border-b` below that. Compact has neither, and now the bar
   * and canvas share a colour, so this rule is the only thing separating them.
   */
  [data-shell-density="compact"] [data-slot="utility-bar"] {
    background-color: var(--background);
    border-block-end: 1px solid var(--sidebar-border);
  }

  /* Compact chrome is the utility bar only — pin sticky headers to the bar token. */
  html[data-shell-density="compact"] {
    --header-height: var(--shell-utility-bar-height);
  }

  /*
   * The last gutters. Zeroing the inset card's own margin is not enough: the
   * shell keeps a 0.5rem gutter on `[data-app-shell-main]` (trailing always,
   * leading whenever the secondary rail is closed or the primary rail is a
   * flyout), and the fixed rail is inset from the bottom by
   * `--app-shell-inset-y`. Compact is edge-to-edge, so all three go.
   *
   * `!important` because the leading-gutter rules qualify on `:has()` state and
   * reach (0,5,0) specificity — the same reason the `sidebar-inset` margin
   * override above uses it. Setting the variable on the row rather than on this
   * ancestor is deliberate: `[data-app-shell-row]` declares it on itself, and a
   * local declaration beats an inherited one whatever the ancestor says.
   */
  [data-shell-density="compact"] [data-app-shell-main] {
    padding-inline: 0 !important;
  }

  [data-shell-density="compact"] [data-app-shell-row] {
    --app-shell-inset-y: 0rem;
  }

  /*
   * Hub view tabs follow `Tabs` into the line treatment.
   *
   * They are a tab strip in everything but implementation: one row of named
   * views, one selected, per-view menus, an add affordance on the end. Leaving
   * them as a filled pill track while every `Tabs` row in the variant went to
   * an underline put two tab languages on the same page, and the pill is the
   * one the flush canvas already argues against — a raised, rounded control
   * floating on a square edge-to-edge surface.
   *
   * Qualified on `[role="toolbar"]` because that is exactly the line between
   * the two things wearing this slot. The hub strip is a toolbar: its segments
   * carry their own disclosure menus and remove buttons, so it is navigation.
   * `ViewSegmentedControl` and `ButtonSegmentedControl` are `role="radiogroup"`
   * — an exclusive choice between renderings of one thing, which is a control,
   * and keeps its pill for the same reason `Button` keeps its corners.
   *
   * CSS rather than a variant prop on `viewSegmentedButtonClass`: the hub strip
   * builds its active state from four composed pieces (shell, label button,
   * chevron, count), so a prop would have to thread through all of them, while
   * the treatment is a property of the shell either way. The high-contrast and
   * forced-colors rules for this slot all carry `!important`, so they still win.
   */
  [data-shell-density="compact"] [data-slot="view-segmented-toolbar"][role="toolbar"] {
    gap: 0.25rem;
    border-radius: 0;
    background-color: transparent;
    padding: 0;
    border-block-end: 1px solid var(--border);
  }

  /* Sticky views strip owns the full-width rule — drop the toolbar's short border.
     Sit segments on the strip bottom so the active 2px indicator meets the rule. */
  [data-slot="list-views-sticky-subheader"] [data-slot="view-segmented-toolbar"][role="toolbar"] {
    border-block-end: 0;
    align-items: flex-end;
    min-height: 100%;
  }

  [data-slot="list-views-sticky-subheader"] [data-slot="horizontal-scroll-region"] {
    align-items: flex-end;
  }

  /* `-1px` pulls the 2px indicator over the track / sticky-strip hairline so
     the selected tab sits on the rule. Pair with `items-end` on
     `list-views-sticky-subheader` (see `list-page.tsx`) — `items-center` left
     a white gap between the indicator and the full-width border. */
  [data-shell-density="compact"]
    [data-slot="view-segmented-toolbar"][role="toolbar"]
    :is([data-slot="view-segmented-item"], [data-slot="view-segmented-item-shell"]) {
    border-radius: 0;
    background-color: transparent;
    box-shadow: none;
    margin-block-end: -1px;
    border-block-end: 2px solid transparent;
  }

  [data-shell-density="compact"]
    [data-slot="view-segmented-toolbar"][role="toolbar"]
    :is(
      [data-slot="view-segmented-item-shell"],
      [data-slot="view-segmented-item"][aria-pressed="true"]
    ) {
    border-block-end-color: var(--foreground);
  }

  /* The shell already draws the indicator for the whole composed tab, so the
     label and chevron inside it must not draw a second one under themselves. */
  [data-shell-density="compact"]
    [data-slot="view-segmented-toolbar"][role="toolbar"]
    [data-slot="view-segmented-item-shell"]
    :is([data-slot="view-segmented-item"], button) {
    margin-block-end: 0;
    border-block-end: 0;
  }

  /* Compact also halves the radius scale — see `html[data-shell-density]`
     next to the `data-text-size` blocks near the top of this file. It has to
     live unlayered up there to outrank `:root`. */

  @media (min-width: 768px) {
    [data-app-shell-row] {
      --app-shell-inset-y: 0.5rem;
    }
  }

  /* System banner spans secondary rail + main — above inline panel chrome. */
  [data-app-shell-workspace] [data-slot="system-banner"] {
    position: relative;
    z-index: 40;
  }

  [data-app-shell-row] [data-app-shell-main],
  [data-app-shell-row] [data-slot="sidebar-inset"] {
    position: relative;
    z-index: 30;
  }

  /*
   * Rail ↔ main — docked columns share one workspace row. Keep inset width
   * full-bleed; vertical margin must stay 0 so panel titles line up with
   * SiteHeader.
   */
  [data-app-shell-row] [data-slot="sidebar-inset"] {
    margin-inline: 0 !important;
    width: 100%;
    max-width: 100%;
  }

  /*
   * Secondary rail + main inset share one workspace row — stray vertical margin on
   * either column drops the panel title row below SiteHeader breadcrumbs.
   */
  [data-app-shell-workspace-body] > [data-slot="secondary-panel"]:not([data-overlay]),
  [data-app-shell-workspace-body] [data-slot="sidebar-inset"] {
    margin-block-end: 0 !important;
    margin-block-start: 0 !important;
  }

  [data-app-shell-main] {
    margin-inline-start: 0;
    padding-inline-start: 0;
    padding-inline-end: 0.5rem;
  }

  /*
   * Scope rail closed (record detail, dashboards, etc.) — main column needs a
   * start gutter so the inset card is not flush to the workspace edge.
   */
  [data-app-shell-workspace-body]:not(:has(
      [data-slot="secondary-panel"][data-state="open"]:not([data-overlay])
    ))
    [data-app-shell-main] {
    padding-inline-start: 0.5rem;
  }

  /* Primary nav unmounted (focus shell) or flyout-only — no sidebar-gap spacer. */
  [data-app-shell-row]:not(:has([data-slot="sidebar"]:not([data-nav-flyout="true"])))
    [data-app-shell-main],
  [data-app-shell-row]:has([data-slot="sidebar"][data-nav-flyout="true"]) [data-app-shell-main] {
    padding-inline-start: 0.5rem;
  }

  @media (min-width: 768px) {
    [data-app-shell-main] {
      padding-inline-end: 0.5rem;
    }
  }

  [data-app-shell-workspace-body]:has(
    [data-slot="secondary-panel"][data-state="open"]:not([data-overlay])
  ) [data-app-shell-main] {
    padding-inline-start: 0;
  }

  /* Docked Ask Leo panel only — set when the right rail claims flex width.
     Floating window mode must leave this attribute unset so main does not shift. */
  [data-app-shell-row][data-ask-leo-open="true"] [data-app-shell-main] {
    padding-inline-end: 0;
  }

  [data-app-shell-row] [data-slot="sidebar"]:not([data-nav-flyout="true"]) [data-slot="sidebar-container"] {
    z-index: 10;
    /* Clip drill-in panes + resize handle — must not paint over main content. */
    overflow: hidden;
    contain: paint;
    /* Clear the full-bleed system banner above the shell row (height published
       by SystemBannerSlot). Keep the workspace vertical inset under that.
       Full-bleed utility bar overrides `top` below (banner + 3rem). */
    top: calc(var(--app-shell-inset-y) + var(--shell-system-banner-height, 0px));
    bottom: var(--app-shell-inset-y);
    height: auto;
  }

  /* After the inset rule so equal-cascade app overrides still compose with banner.
     Rail clears the utility bar token (+ optional full-bleed system banner). */
  [data-shell-utility-bar-full] [data-app-shell-row] [data-slot="sidebar"]:not([data-nav-flyout="true"]) [data-slot="sidebar-container"] {
    top: calc(var(--shell-utility-bar-height) + var(--shell-system-banner-height, 0px));
  }

  [data-app-shell-row] [data-slot="sidebar"]:not([data-nav-flyout="true"]) [data-sidebar="sidebar"] {
    overflow: hidden;
    max-width: 100%;
  }

  [data-app-shell-row] [data-slot="sidebar-drill-in"][data-open] {
    overflow: hidden;
  }

  [data-app-shell-row] [data-slot="sidebar-drill-in-section"] {
    max-width: 100%;
    overflow-x: hidden;
  }

  [data-app-shell-row] [data-slot="sidebar-drill-in-resize-handle"] {
    /* Stay inside the clipped sidebar — local z only, not above main (z-30). */
    z-index: 2;
  }

  [data-app-shell-row] [data-slot="secondary-panel"]:not([data-overlay]),
  [data-app-shell-row] [data-slot="ask-leo-panel"]:not([data-overlay]) {
    position: relative;
    z-index: 10;
  }

  [data-app-shell-row] [data-slot="sidebar-drill-in"] {
    position: relative;
    z-index: 0;
  }

  /* Drill-in open: opaque header so transparent SiteHeader does not show the rail through. */
  [data-slot="sidebar-wrapper"][data-drill-in-open] [data-site-header] {
    background-color: var(--background);
  }

  .z-properties-sheet-tooltip {
    z-index: 100 !important;
  }

  /* Floating sheet open — elevate every portaled tooltip above sheet chrome (`z-[80]`). */
  body:has([data-slot="sheet-content"][data-state="open"]) [data-slot="tooltip-content"],
  body:has([data-slot="sheet-content"][data-open]) [data-slot="tooltip-content"],
  body:has([data-slot="sheet-content"][data-state="open"]) [role="tooltip"],
  body:has([data-slot="sheet-content"][data-open]) [role="tooltip"] {
    z-index: 100 !important;
  }

  /* Secondary panel flyout (`z-[70]`) — portaled tooltips must sit above. */
  body:has([data-slot="secondary-panel"][data-overlay][data-state="open"]) [data-slot="tooltip-content"],
  body:has([data-slot="secondary-panel"][data-overlay][data-state="open"]) [role="tooltip"] {
    z-index: 100 !important;
  }

  /* Primary sidebar flyout (`z-50`, high zoom / mobile) — above panel chrome + drill-in. */
  body:has([data-slot="sidebar"][data-nav-flyout="true"][data-state="expanded"]) [data-slot="tooltip-content"],
  body:has([data-slot="sidebar"][data-nav-flyout="true"][data-state="expanded"]) [role="tooltip"] {
    z-index: 100 !important;
  }

  @media (min-width: 768px) {
    .touch-target {
      min-height: unset;
      min-width:  unset;
    }
  }

  /* Form field border — use --control-border (3:1 minimum) */
  .field-border {
    border-color: var(--control-border);
  }

  /* Exxat Prism highlight banner */
  .banner-prism {
    background-color: var(--banner-prism-bg);
  }

  /* Primary + secondary sidebar row labels — wrap; never ellipsis. */
  [data-slot="sidebar-menu-button"] [data-sidebar-nav-label],
  [data-slot="sidebar-menu-sub-button"] [data-sidebar-nav-label],
  [data-sidebar-nav-label] {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  /* Font Awesome glyphs — block + leading-none so icons center with row labels. */
  [data-slot="sidebar-menu-button"] > span[aria-hidden="true"] i,
  [data-slot="sidebar-menu-button"] > i[aria-hidden="true"],
  [data-slot="sidebar-menu-sub-button"] > span[aria-hidden="true"] i,
  [data-sidebar="menu-button"] > span[aria-hidden="true"] i,
  [data-sidebar="menu-button"] > i[aria-hidden="true"] {
    display: block;
    line-height: 1;
  }

  /* Collapsible parents: TooltipTrigger overwrites data-slot; match primary row metrics. */
  [data-sidebar="menu-button"]:not([data-size="lg"]) {
    overflow: visible;
    min-height: 1.75rem;
    align-items: center;
    padding-block: var(--exxat-spacing-1);
    padding-inline: var(--exxat-spacing-2);
  }

  /* Child sub-menu — indent only; no vertical guide (overlaps row icons). */
  [data-slot="sidebar-menu-sub"] {
    margin-inline: 0;
    margin-inline-start: var(--exxat-spacing-3);
    padding-inline-end: 0;
    padding-inline-start: 0;
    width: calc(100% - var(--exxat-spacing-3));
    box-sizing: border-box;
    border-inline-start: none;
  }

  /* Primary sidebar collapsible — guide disabled (was overlapping icon columns). */
  [data-sidebar="menu-item"].group\/collapsible {
    position: relative;
  }

  [data-sidebar="menu-item"].group\/collapsible::before {
    content: none !important;
    display: none !important;
  }

  /* Library / outline folder tree — guide spans parent row icon center → next sibling row. */
  [data-slot="outline-tree-menu-item"]:has(
      > .group\/collapsible[data-state="open"] [data-slot="outline-tree-sub"]
    ),
  li:has(> .group\/collapsible[data-state="open"] [data-slot="outline-tree-sub"]) {
    position: relative;
  }

  [data-slot="outline-tree-menu-item"]:has(
      > .group\/collapsible[data-state="open"] [data-slot="outline-tree-sub"]
    )::before,
  li:has(> .group\/collapsible[data-state="open"] [data-slot="outline-tree-sub"])::before {
    content: "";
    position: absolute;
    left: calc(
      var(--outline-tree-row-padding-x) + var(--outline-tree-depth, 0) *
        var(--outline-tree-depth-step) + var(--outline-tree-chevron-half)
    );
    top: var(--outline-tree-guide-overhang);
    bottom: calc(-1 * var(--outline-tree-guide-overhang));
    border-left: 1px solid var(--sidebar-border);
    pointer-events: none;
    z-index: 0;
  }

  /* Secondary panel folder tree — depth indent only; no vertical guide rails. */
  [data-slot="secondary-panel"] [data-slot="outline-tree-menu-item"]:has(
      > .group\/collapsible[data-state="open"] [data-slot="outline-tree-sub"]
    )::before,
  [data-slot="secondary-panel"]
    li:has(> .group\/collapsible[data-state="open"] [data-slot="outline-tree-sub"])::before {
    content: none;
    display: none;
  }

  /* Folder picker popover — same tree chrome as secondary panel, no guide rails. */
  [data-slot="folder-picker-panel"] [data-slot="outline-tree-menu-item"]:has(
      > .group\/collapsible[data-state="open"] [data-slot="outline-tree-sub"]
    )::before,
  [data-slot="folder-picker-panel"]
    li:has(> .group\/collapsible[data-state="open"] [data-slot="outline-tree-sub"])::before {
    content: none;
    display: none;
  }

  /* Flat width — depth indent is on each row, not repeated sub-list margins. */
  [data-slot="outline-tree-sub"][data-guide-layout="chevronRail"] {
    margin-inline: 0;
    padding-inline: 0;
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
    border-inline-start: none;
  }

  [data-slot="sidebar-menu-sub-item"] {
    display: flex;
    align-items: center;
    width: 100%;
  }

  [data-slot="sidebar-menu-sub-item"].group\/collapsible-sub {
    flex-direction: column;
    align-items: stretch;
  }

  [data-slot="sidebar-menu-sub-button"] {
    flex: 1 1 0%;
    min-width: 0;
    translate: none;
    align-items: center;
    padding-block: var(--exxat-spacing-1);
    padding-inline: var(--exxat-spacing-2);
  }

  [data-sidebar="nav-sub-badge"] {
    flex-shrink: 0;
    align-self: center;
    margin-inline-end: var(--exxat-spacing-2);
    pointer-events: none;
  }

  /* Sidebar nav — little pop when an item becomes active. Key swap on the
     wrapper remounts the span, so the animation replays on selection. */
  @keyframes sidebar-icon-pop {
    0%   { opacity: 0.4; transform: scale(0.7)  rotate(-6deg); }
    60%  { opacity: 1;   transform: scale(1.15) rotate(2deg); }
    100% { opacity: 1;   transform: scale(1)    rotate(0); }
  }

  /* Radix Collapsible / Accordion — slide the children open/closed using the
     `--radix-collapsible-content-height` CSS var that Radix sets on the
     content element. Mirrors the shadcn accordion pattern (Tailwind v3
     "accordion-down" / "accordion-up"); we keep our own name so it can be
     reused by both Collapsible and Accordion primitives. */
  @keyframes collapsible-down {
    from { height: 0; }
    to   { height: var(--radix-collapsible-content-height); }
  }
  @keyframes collapsible-up {
    from { height: var(--radix-collapsible-content-height); }
    to   { height: 0; }
  }

  /* Ask Leo suggestion chips — staggered lift on first render (no opacity — avoids mid-animation contrast failures). */
  @keyframes leo-chip-in {
    0%   { transform: translateY(8px) scale(0.96); }
    100% { transform: translateY(0) scale(1); }
  }

  /* DataTable bulk bar — Y-only rise into resting place (no scale / width / opacity). */
  @keyframes exxat-bulk-bar-rise {
    from { transform: translate3d(0, 1rem, 0); }
    to   { transform: translate3d(0, 0, 0); }
  }

  /* Leo typing indicator — 3 dots bouncing opacity/scale, staggered via delay. */
  @keyframes leo-typing-dot {
    0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
    40%           { opacity: 1;   transform: scale(1); }
  }

  /* Streaming status text — shadcn Marker shimmer (tokenized foreground mix). */
  @keyframes shimmer-text {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
  }

  .shimmer {
    background-image: linear-gradient(
      90deg,
      var(--muted-foreground) 0%,
      var(--foreground) 50%,
      var(--muted-foreground) 100%
    );
    background-size: 200% auto;
    background-clip: text;
    -webkit-background-clip: text;
    color: transparent;
    animation: shimmer-text 2.4s linear infinite;
  }

  /* Attachment title — `.shimmer` is not a Tailwind utility, so group-* variants
     cannot attach it. Mirror the same gradient when the chip is uploading/processing. */
  [data-slot="attachment"][data-state="uploading"] .attachment-title-shimmer,
  [data-slot="attachment"][data-state="processing"] .attachment-title-shimmer {
    background-image: linear-gradient(
      90deg,
      var(--muted-foreground) 0%,
      var(--foreground) 50%,
      var(--muted-foreground) 100%
    );
    background-size: 200% auto;
    background-clip: text;
    -webkit-background-clip: text;
    color: transparent;
    animation: shimmer-text 2.4s linear infinite;
  }

  @media (prefers-reduced-motion: reduce) {
    [data-slot="attachment"][data-state="uploading"] .attachment-title-shimmer,
    [data-slot="attachment"][data-state="processing"] .attachment-title-shimmer {
      animation: none;
      color: var(--muted-foreground);
      background-image: none;
    }
  }

  /* Whole-panel brand radial glow pulse — runs on the BG layer while Leo thinks. */
  @keyframes leo-panel-pulse {
    0%, 100% { opacity: 0.5;  transform: scale(1); }
    50%      { opacity: 0.75; transform: scale(1.01); }
  }

  /* Chart “Leo insight” marker — soft breathing halo behind the chip. */
  @keyframes leo-chart-insight-halo {
    0%, 100% { opacity: 0.45; transform: scale(1); }
    50%      { opacity: 0.72; transform: scale(1.12); }
  }

  /* Accent dot on the insight marker — gentle pulse (respect motion-reduce in markup). */
  @keyframes leo-chart-insight-dot {
    0%, 100% { opacity: 0.9; transform: scale(1); }
    50%      { opacity: 1; transform: scale(1.35); }
  }
}

@import "./styles/leo-blob-animations.css";
@import "./styles/leo-pulse.css";
@import "./styles/marketing-banner-animations.css";

@layer components {
  /*
   * Icon-only Button glyphs (ghost / outline / secondary, size icon*).
   * WCAG 1.4.11 — ≥3:1 vs surface; hover uses --interactive-hover-foreground.
   */
  [data-slot="button"][data-size^="icon"]:where([data-variant="ghost"], [data-variant="outline"]):not(
      :disabled
    ):not([disabled]):not([aria-disabled="true"]) {
    color: var(--icon-button-foreground);
  }

  [data-slot="button"][data-size^="icon"]:where([data-variant="ghost"], [data-variant="outline"]):is(
      :hover,
      :focus-visible,
      [aria-expanded="true"]
    ):not(:disabled):not([disabled]):not([aria-disabled="true"]) {
    color: var(--interactive-hover-foreground);
  }

  [data-sidebar="sidebar"]
    [data-slot="button"][data-size^="icon"]:where([data-variant="ghost"], [data-variant="outline"]):not(
      :disabled
    ):not([disabled]):not([aria-disabled="true"]):not(:hover):not(:focus-visible):not([aria-expanded="true"]) {
    color: var(--icon-button-foreground-on-sidebar);
  }

  [data-sidebar="sidebar"]
    [data-slot="button"][data-size^="icon"]:where([data-variant="ghost"], [data-variant="outline"]):is(
      :hover,
      :focus-visible,
      [aria-expanded="true"]
    ):not(:disabled):not([disabled]):not([aria-disabled="true"]) {
    color: var(--sidebar-accent-foreground);
  }

  /* Bespoke icon-only controls (no data-slot) — opt in via utility class */
  .icon-button-chrome {
    color: var(--icon-button-foreground);
  }

  .icon-button-chrome:hover,
  .icon-button-chrome:focus-visible {
    color: var(--interactive-hover-foreground);
  }

  /* Secondary text+icon actions (Add filter, Add view, dashed pills) */
  .secondary-action-chrome {
    color: var(--secondary-action-foreground);
  }

  .secondary-action-chrome:hover,
  .secondary-action-chrome:focus-visible {
    color: var(--interactive-hover-foreground);
  }

  /* Inputs / composers — placeholder copy (SC 1.4.3 ≥4.5:1) */
  .placeholder-aa::placeholder {
    color: var(--placeholder-foreground);
  }

  [data-sidebar="sidebar"] .icon-button-chrome:not(:hover):not(:focus-visible) {
    color: var(--icon-button-foreground-on-sidebar);
  }

  [data-sidebar="sidebar"] .icon-button-chrome:hover,
  [data-sidebar="sidebar"] .icon-button-chrome:focus-visible {
    color: var(--sidebar-accent-foreground);
  }

  :is([data-slot="secondary-panel"], [data-slot="ask-leo-panel"])
    [data-slot="button"][data-size^="icon"]:where([data-variant="ghost"], [data-variant="outline"]):not(
      :disabled
    ):not([disabled]):not([aria-disabled="true"]):not(:hover):not(:focus-visible):not([aria-expanded="true"]) {
    color: var(--icon-button-foreground-on-secondary-panel);
  }

  :is([data-slot="secondary-panel"], [data-slot="ask-leo-panel"])
    [data-slot="button"][data-size^="icon"]:where([data-variant="ghost"], [data-variant="outline"]):is(
      :hover,
      :focus-visible,
      [aria-expanded="true"]
    ):not(:disabled):not([disabled]):not([aria-disabled="true"]) {
    color: var(--interactive-hover-foreground);
  }

  :is([data-slot="secondary-panel"], [data-slot="ask-leo-panel"])
    .icon-button-chrome:not(:hover):not(:focus-visible) {
    color: var(--icon-button-foreground-on-secondary-panel);
  }

  :is([data-slot="secondary-panel"], [data-slot="ask-leo-panel"])
    .icon-button-chrome:hover,
  :is([data-slot="secondary-panel"], [data-slot="ask-leo-panel"])
    .icon-button-chrome:focus-visible {
    color: var(--interactive-hover-foreground);
  }

  :is([data-slot="secondary-panel"], [data-slot="ask-leo-panel"])
    .secondary-action-chrome:not(:hover):not(:focus-visible) {
    color: var(--icon-button-foreground-on-secondary-panel);
  }

  /* Shell utility bar — level-0 chrome; brand-tinted hover like sidebar rows. */
  [data-slot="utility-bar"]
    [data-slot="button"][data-size^="icon"]:where([data-variant="ghost"], [data-variant="outline"]):not(
      :disabled
    ):not([disabled]):not([aria-disabled="true"]) {
    color: var(--icon-button-foreground-on-sidebar);
  }

  [data-slot="utility-bar"]
    [data-slot="button"][data-size^="icon"]:where([data-variant="ghost"], [data-variant="outline"]):is(
      :hover,
      :focus-visible,
      [aria-expanded="true"]
    ):not(:disabled):not([disabled]):not([aria-disabled="true"]) {
    color: var(--sidebar-accent-foreground);
  }

  [data-slot="utility-bar"] .icon-button-chrome:not(:hover):not(:focus-visible) {
    color: var(--icon-button-foreground-on-sidebar);
  }

  [data-slot="utility-bar"] .icon-button-chrome:is(:hover, :focus-visible, [aria-expanded="true"]) {
    color: var(--sidebar-accent-foreground);
  }

  /* The launcher announces itself once and then holds still.
     Arrival is three passes over one box, all keyed off `data-intro` on the
     wrapper: the halo blooms outside the chip, a specular sheen crosses its
     border, and a brand wash crosses the face behind the label. Every one of them
     ends at nothing. What is left is the chip's brand border and the glow behind
     the star, and after that the launcher does not move again — hover is the
     border, focus is the ring.

     Three passes rather than one because they are three different reads and only
     one of them can be inside the chip: light gathering around it, light catching
     its edge, light moving through it. The wash is the piece borrowed from the Leo
     Assist search bar, which is where the same brand-wash language already lives
     (`leo-blob-animations.css`).

     Each pass rides its own box so the timings stay independent — one element has
     one opacity. The halo is a sibling `span` at `inset-0` carrying only an
     outward shadow, so nothing paints across the chip from behind it; the sheen is
     that span's `::after`, masked to its border band; the wash is the wrapper's
     `::before`; the resting glow is the button's `::before`.

     Not scoped to `[data-slot="utility-bar"]`: `data-ask-leo-utility-glow` is
     unique to the launcher, and scoping meant the design-system preview of that
     launcher rendered without its halo. */
  [data-ask-leo-utility-glow-wrap] {
    --ask-leo-glow-duration: 1600ms;
    /* Zero, and that is the whole resting design: the halo is an arrival, not a
       state. A launcher that keeps a lit ring around it all day is chrome
       competing with page content for attention it has already won, and it made
       the bar's other actions read as secondary when they are not. What the chip
       keeps instead is its brand border and the glow behind the star. */
    --ask-leo-glow-rest-opacity: 0;
    /* Shorter than the halo's bloom on purpose: the sheen is the thing that
       catches the eye, and it should be finished and gone by the time the chip's
       resting glow lands, so the launcher ends on stillness. */
    --ask-leo-sheen-duration: 1000ms;
    /* Starts after the halo is already up and ends before the halo has faded, so
       the three passes read as one arrival rather than a queue. */
    --ask-leo-wash-duration: 1150ms;
    --ask-leo-wash-delay: 150ms;
    /* The chip lands icon-only and opens. The label's box grows first, while the
       type is still fully transparent, which is what lets the expansion run
       without clipping anything: the text overflows a zero-width box for a beat,
       invisibly, and the chip's width follows the box. */
    --ask-leo-label-open-duration: 380ms;
    --ask-leo-label-open-delay: 140ms;
    /* The name follows the light instead of arriving with it, and it follows its
       own box: the ink starts as the expansion finishes, so nothing fades in
       while the space it needs is still moving. Finished long before the halo has
       faded, so the arrival still ends on one beat. */
    --ask-leo-label-duration: 560ms;
    --ask-leo-label-delay: 520ms;
    /* The shimmer crosses type that is already legible, not type still arriving. */
    --ask-leo-shimmer-duration: 820ms;
    --ask-leo-shimmer-delay: 620ms;
    /* The outline draws itself in under the light rather than being there before
       any of it arrives. Up before the label rises into it, so the chip has an
       edge by the time it has a name. */
    --ask-leo-border-duration: 780ms;
    --ask-leo-border-delay: 120ms;
    /* The star turns while the chip opens: in just before the box starts moving,
       settling as the name begins to ink. Overlapping rather than queued, so Leo
       arriving and the chip naming itself read as one beat. */
    --ask-leo-star-duration: 520ms;
    --ask-leo-star-delay: 100ms;
  }

  /* The wash inside the chip: the Leo bar's own drifting lobes, in their own
     element rather than a pseudo-element, because they are three boxes plus a
     connective floor and a sheen — `AskLeoLauncherWash` mounts them and owns when
     they are live. What is left here is the frame they live in.

     A real element rather than the wrapper's `::before` for the same reason the
     other passes each ride their own box: one element has one opacity, and the
     wrapper's is not free. Not the halo (its opacity is animated, and that
     multiplies down into descendants); not the chip (its `::before` is spent on
     the resting glow).

     `overflow: hidden` keeps the field inside the chip's box. The lobes are sized
     to the chip (`fit="chip"`), so this clips the ~1.5x overshoot each one needs
     for its vertical drift, not the bulk of a composer-sized lobe. It is safe
     here where it would not be on the wrapper — the halo's outward shadow is the
     wrapper's other child, and clipping this layer does not reach it.

     Tree order puts it before the halo and the chip, and the chip sits at
     `z-index: 1`, so the lobes paint behind the label. The chip's transparent
     face is what lets them show through at all. */
  [data-ask-leo-utility-wash] {
    /* Custom properties inherit, so the compact-density block above — which sets
       `--exxat-radius-2` on the wrapper — reaches this layer without listing it. */
    border-radius: var(--ask-leo-chip-radius, var(--exxat-radius-2));
    /* Under the contrast budget, not at it: the field never switches off now, and
       chrome that idles at its own ceiling reads as an alert. The full budget is
       spent only while Leo is arriving, open, or working.

       `--ask-leo-chip-blob-scale` is the user's preference (Leo appearance, Ask
       Leo button), and it multiplies the budget rather than replacing it so the
       per-theme numbers above keep deciding what the label can afford. `min()`
       caps the product: opacity clamps at 1 on its own, but keeping the cap
       explicit means the emphatic and arrival ceilings stay the same expression. */
    opacity: min(1, calc(var(--ask-leo-chip-blob-opacity) * var(--ask-leo-chip-blob-scale, 1) * 0.6));
    transition: opacity 300ms ease-out;
  }

  /* Arriving, open, or a turn in flight — `AskLeoLauncherWash` sets the flag. */
  [data-ask-leo-utility-wash][data-emphatic] {
    opacity: min(1, calc(var(--ask-leo-chip-blob-opacity) * var(--ask-leo-chip-blob-scale, 1)));
  }

  /* Hover and keyboard focus spend the same budget the working states do. The
     chip is the one control in the bar whose surface is Leo's own material, so
     answering the pointer with light is its affordance; without it the launcher
     looked identical whether or not it was about to be clicked, and the only
     hover feedback was a border step shared with every other bar action.

     In CSS rather than as a React hover state deliberately. The emphatic flag
     also picks `intensity` and the sheen, which are decided when the field
     mounts, so routing hover through it would restart the lobes' drift under
     the pointer. Opacity is the whole effect and opacity is free here. */
  [data-ask-leo-utility-glow-wrap]:not([data-disabled]):hover [data-ask-leo-utility-wash],
  [data-ask-leo-utility-glow-wrap]:not([data-disabled]):has(:focus-visible) [data-ask-leo-utility-wash] {
    opacity: min(1, calc(var(--ask-leo-chip-blob-opacity) * var(--ask-leo-chip-blob-scale, 1)));
  }

  /* On arrival the wash fades up to its resting opacity and stays: the field is
     the chip's surface for the session, so the envelope is an entrance, not a
     round trip. `both` holds the start frame through the delay, so the launcher
     lands before its wash begins rather than the two arriving together.

     After the arrival there is no envelope at all — the declared resting opacity
     above is what holds. */
  [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-wash] {
    animation: ask-leo-utility-wash var(--ask-leo-wash-duration)
      var(--ask-leo-wash-delay) ease-out both;
  }

  /* The edge light: a second copy of the field, masked to a ring that straddles
     the chip's border, then blurred.

     `overflow: hidden` on the layer above is what makes the chip read as a
     window cut into a field rather than as a control holding light — the lobes
     stop dead at the border, and the part of each one that a real light would
     spill past it is thrown away. This puts that part back: the ring covers the
     spill outside plus two pixels back into the chip, so a lobe arriving at an
     edge lights the outline there and blooms a little beyond it, and the edge
     that lights is whichever one the lobe is actually under.

     Nothing tracks lobe positions to do it. It is the same field at the same
     size with the same drift, mounted at the same moment, so the bloom is under
     its own lobe by construction rather than by a measurement that could drift
     out of step. The copy runs `lobesOnly`, which drops the field's connective
     floor: that floor is flat across the width, and read through an 8px band it
     lights every part of the outline the same amount — the even outline this is
     here to avoid.

     The ring paints over the chip (`z-[2]` on the layer, chip at `z-index: 1`).
     Under it, the light sat beneath an outline the chip draws at a flat
     `brand/45`, which held its colour all the way round however the lobes moved.
     The mask keeps this copy off the face, so being on top costs the label
     nothing.

     Blur has to come after the mask, which is why this is two elements: masking
     a blurred field cuts the glow off at a hard rounded-rect edge, and that seam
     is the artefact the whole effect exists to avoid. The band masks, the
     wrapper blurs, so both boundaries of the ring come out soft. */
  [data-ask-leo-utility-spill] {
    /* Without `mask-composite` the band cannot be cut out of the middle, and the
       fallback is a blurred brand field across the label. No edge light is the
       better failure. */
    display: none;
  }

  @supports (mask-composite: exclude) or (-webkit-mask-composite: xor) {
    [data-ask-leo-utility-spill] {
      /* How far the light reaches past the chip. Kept under the bar's own
         padding so the bloom is not clipped by the utility bar's box. */
      --ask-leo-chip-spill: 6px;
      display: block;
      inset: calc(-1 * var(--ask-leo-chip-spill));
      /* Softens both edges of the ring below and carries the outer one further
         out than the ring itself, which is what makes it read as light rather
         than as an outline. Kept under the lobes' own blur: blur this layer as
         wide as the band and the three lobes average out along it, which is the
         evenly lit outline the ring exists to break up. */
      filter: blur(3px);
      /* Read against the border, not against the bar. The chip paints its outline
         at a flat `brand/45`, and light landing on top of that has to be able to
         out-argue it or the outline stays one colour whatever the lobes do — half
         the label's budget is where the lit stretch reads as brighter than the
         plain border beside it. Still short of the inner field: this sits mostly
         outside the chip, and at the field's own level the chip reads as
         selected at rest. */
      opacity: min(1, calc(var(--ask-leo-chip-blob-opacity) * var(--ask-leo-chip-blob-scale, 1) * 0.5));
      transition: opacity 300ms ease-out;
    }

    [data-ask-leo-utility-spill][data-emphatic],
    [data-ask-leo-utility-glow-wrap]:not([data-disabled]):hover [data-ask-leo-utility-spill],
    [data-ask-leo-utility-glow-wrap]:not([data-disabled]):has(:focus-visible) [data-ask-leo-utility-spill] {
      opacity: min(1, calc(var(--ask-leo-chip-blob-opacity) * var(--ask-leo-chip-blob-scale, 1) * 0.9));
    }

    /* The outline arrives lit. Same window as the wash, so the light comes up
       inside the chip and around its edge together, and it overshoots on the way
       through: an outline that only fades in reads as a border being drawn, and
       one that peaks and settles reads as light landing on a border that was
       already there.
       Lands on the emphatic level rather than the resting one because the chip is
       still `data-emphatic` when the keyframes finish — the settle to rest is the
       300ms transition above, once the arrival lets go. */
    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-spill] {
      animation: ask-leo-utility-spill-intro var(--ask-leo-wash-duration)
        var(--ask-leo-wash-delay) ease-out both;
    }

    /* The ring. Padding is the spill plus 2px, so the visible band starts inside
       the chip's border and ends at the spill's outer edge; the two masks cover
       the whole box and the content box and exclude each other, leaving that
       band. Same technique as the arrival sheen above, at a different width. */
    [data-ask-leo-utility-spill-band] {
      border-radius: calc(
        var(--ask-leo-chip-radius, var(--exxat-radius-2)) +
          var(--ask-leo-chip-spill)
      );
      box-sizing: border-box;
      padding: calc(var(--ask-leo-chip-spill) + 2px);
      /* `-webkit-` first for Safari before 16.4, which knows only that spelling
         and spells the keyword `xor`. */
      -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
      -webkit-mask-composite: xor;
      mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
      mask-composite: exclude;
    }
  }

  /* Never transformed, only faded. The halo's job is to trace the chip's border
     box, and `transform` moves that box: a scale slides all four edges off the
     button's edges and renders the corner at a different radius than the chip's.
     Alignment is then structural rather than something to keep re-tuning. */
  [data-ask-leo-utility-glow] {
    background: none;
    /* Same variable `.rounded-md` resolves on the chip, so the halo takes the
       chip's corner at either shell density. See the density block above. */
    border-radius: var(--ask-leo-chip-radius, var(--exxat-radius-2));
    box-shadow: var(--ask-leo-utility-glow-shadow);
    opacity: var(--ask-leo-glow-rest-opacity);
    transition: opacity 200ms ease-out;
    will-change: opacity;
  }

  [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-glow] {
    animation: ask-leo-utility-glow-intro var(--ask-leo-glow-duration)
      cubic-bezier(0.16, 1, 0.3, 1) both;
  }

  /* Metallic sheen: one specular pass across the chip's edge as it arrives.
     The band is a gradient masked down to the element's own border band — two
     masks, one covering the whole box and one the content box, excluded from each
     other to leave the 1.5px padding ring. That mask is what keeps the gradient
     off the chip's face, which is the same promise the halo's outward shadow
     makes: no light paints across the button from behind.

     A linear pass rather than a conic one chasing the perimeter, for two
     reasons. An arc travelling a border is the indeterminate-progress idiom, and
     on a launcher that reads as "loading" rather than "here" — a misleading
     affordance, not merely an aesthetic miss. And on a 3:1 pill a conic sweep
     does not read as travel anyway: measured from the centre, the short edges
     subtend only ~37deg of the turn while the long edges take ~142deg each, so
     the highlight spends nearly the whole rotation sliding along the top and
     bottom and barely crosses the ends. A left-to-right pass is what the shape
     already suggests, and it interpolates `background-position`, so it needs no
     registered custom property to animate. */
  @supports (mask-composite: exclude) or (-webkit-mask-composite: xor) {
    [data-ask-leo-utility-glow]::after {
      border-radius: inherit;
      box-sizing: border-box;
      content: "";
      inset: 0;
      /* Transparent until the arrival animation drives it, so nothing shows on a
         chip that mounts without `data-intro` (route changes, or the design-
         system preview after its first paint). */
      opacity: 0;
      padding: 1.5px;
      pointer-events: none;
      position: absolute;
      /* Raked slightly off vertical so the highlight crosses the top and bottom
         edges at different moments — square-on, both lit at once and it read as a
         band rather than a travelling reflection. */
      background: linear-gradient(
        105deg,
        transparent 38%,
        var(--ask-leo-sheen-color) 50%,
        transparent 62%
      );
      background-repeat: no-repeat;
      /* Wider than the box so the highlight can start and finish off the edge.
         Position percentages resolve against (box − image), so with an image
         2.6x the width, 140% parks the highlight ~0.9 box-widths to the left and
         -60% carries it ~1.3 to the right. */
      background-size: 260% 100%;
      background-position: 140% 0;
      /* `-webkit-` first: Safari before 16.4 understands only that spelling, and
         its keyword is `xor` rather than `exclude`. Later Safari reads both and
         takes the standard one below. */
      -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
      -webkit-mask-composite: xor;
      mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
      mask-composite: exclude;
    }

    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-glow]::after {
      animation: ask-leo-utility-sheen var(--ask-leo-sheen-duration) linear both;
    }
  }

  /* Hover and focus deliberately do not bring the halo back. It is spent once the
     arrival is over, and re-lighting it on hover would put the loudest thing in
     the bar behind the pointer wherever the pointer happened to rest. Hover
     brightens the field and the edge light instead (both above), plus the border
     stepping from `brand/45` to `brand/70` (`askLeoLauncherChipClass`); focus is
     the ring, which is drawn in the same brand family the halo was — two brand
     glows at once read as one soft smear rather than as focus. */

  /* The glow the chip keeps. It rides the button's own `::before` rather than a
     sibling, which is what lets `border-radius: inherit` track the chip's corner
     for free — no second copy of the radius to fall out of step.
     `background-color` is left alone: the launcher's transparent face is what
     stops the ghost variant's dark grey from coming back, and it is asserted in
     `ask-leo-launcher-chip.test.tsx`.

     `z-index: -1` inside the chip's own stacking context (`relative z-[1]`)
     paints it over the face and under the star and label, so ink stays put. */
  /* Owns the stacking context the glow below is positioned and layered against,
     so neither the app's markup nor the design-system preview has to remember
     it. */
  [data-ask-leo-utility-glow-wrap] [data-slot="ask-leo-toggle"] {
    isolation: isolate;
    position: relative;
    z-index: 1;
  }

  /* The outline is part of the arrival too. Without this the chip's border is at
     full strength in the first frame, so light gathers around an edge that was
     already finished — and the edge light's job, which is to move brightness
     around that border, has nothing to arrive on.

     `backwards` and no forwards fill, with the landing frame left out of the
     keyframes entirely: the implicit end frame is the element's own computed
     border, so this animates up to whatever the chip declares and hands back to
     it cleanly. That is what keeps the border's value in one place — the chip's
     class — while still covering `data-leo-open` and hover, which set a different
     one. */
  [data-ask-leo-utility-glow-wrap][data-intro] [data-slot="ask-leo-toggle"] {
    animation: ask-leo-utility-border var(--ask-leo-border-duration)
      var(--ask-leo-border-delay) ease-out backwards;
  }

  [data-ask-leo-utility-glow-wrap] [data-slot="ask-leo-toggle"]::before {
    background: var(--ask-leo-chip-glow);
    border-radius: inherit;
    content: "";
    inset: 0;
    opacity: 1;
    pointer-events: none;
    position: absolute;
    transition: opacity 450ms ease-out;
    z-index: -1;
  }

  /* Clean face while the halo blooms: light arrives outside the chip first, then
     settles into it. Both at once was the wash the launcher used to have. */
  [data-ask-leo-utility-glow-wrap][data-intro] [data-slot="ask-leo-toggle"]::before {
    opacity: 0;
    transition: none;
  }

  /* The label rises 2px into place and takes one bloom of the border sheen's own
     specular colour on the way, so the type is part of the arrival rather than
     the one thing on the chip that was already finished.

     A shadow rather than a gradient clipped to the glyphs. `background-clip: text`
     is the louder version of this and it costs the label its ink: the colour has
     to go transparent for the gradient to show through, which puts every frame of
     the sweep on the smallest type in the bar with a contrast we no longer
     control, and forced-colors has nothing to substitute for it. A shadow leaves
     the glyphs exactly the colour the bar set.

     Only under `[data-intro]`, and only for the labelled chip — the Dense bar's
     launcher is icon-only and has no label to animate. `useOneShotIntro` drops
     the attribute on a ceiling as well as on the halo's `animationend`, so a
     browser that never runs the keyframes still ends with visible type.

     Two animations, not one, because they need different fills. The box opens
     with `backwards` and no forwards fill: a held `max-width` would still be
     clamping the label long after the arrival, which a longer translation would
     eventually run into. The ink keeps `both`. `min-width: 0` because a flex
     item's automatic minimum size is its content, and that alone would stop the
     box collapsing to nothing. */
  [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-label] {
    animation:
      ask-leo-utility-label-open var(--ask-leo-label-open-duration)
        var(--ask-leo-label-open-delay) ease-out backwards,
      ask-leo-utility-label var(--ask-leo-label-duration)
        var(--ask-leo-label-delay) ease-out both;
    min-width: 0;
  }

  /* One pass of light across the glyphs, once the type is already legible.
     A second copy of the label drawn from `attr()` and clipped to the glyphs,
     rather than clipping the label itself: the real text keeps the ink the bar
     gave it, and this layer paints nothing except where the band is, so the
     shimmer can only add light — it can never be the thing deciding the label's
     contrast. `background-clip: text` on the label itself needs `color:
     transparent`, and then every frame of the sweep owns the contrast of the
     smallest type in the bar.
     `linear`, and the same left-to-right direction as the border sheen at
     overlapping times, so one light crosses the whole chip rather than two
     highlights travelling independently. */
  [data-ask-leo-utility-glow-wrap][data-intro]
    [data-ask-leo-utility-label]::after {
    animation: ask-leo-utility-label-shimmer var(--ask-leo-shimmer-duration)
      var(--ask-leo-shimmer-delay) linear both;
    background-clip: text;
    /* Not the sheen at full strength: the band is painted over the ink, so an
       opaque peak replaces the glyph's colour for a frame instead of lighting it.
       At two thirds the ink still reads through the brightest glyph, which is what
       keeps a highlight from becoming this label's contrast. */
    background-image: linear-gradient(
      100deg,
      transparent 42%,
      color-mix(in oklab, var(--ask-leo-sheen-color) 66%, transparent) 50%,
      transparent 58%
    );
    background-repeat: no-repeat;
    background-size: 320% 100%;
    color: transparent;
    content: attr(data-ask-leo-utility-label);
    inset: 0;
    pointer-events: none;
    position: absolute;
    white-space: nowrap;
  }

  /* The star turns a quarter into place as the chip opens.
     `LeoIcon`'s own `invited` gesture is what greets on hover, and it is the
     right size for hover: a 6% squash and a 5deg tip. On a 20px glyph in a
     168-unit viewBox that is well under a pixel of travel, so on arrival — where
     it competes with a chip changing width, an outline drawing itself in, and a
     label inking — it was not something a user could see. The sparkles' opacity
     step is the part of that gesture which does read at this size, so the two
     compose: `LeoIcon` brightens, this turns.
     A quarter, and only a quarter, because the glyph is 4-fold symmetric — 90deg
     lands the star exactly on itself, so there is no seam to hide and no need to
     unwind. One pass and eased, never repeated: repetition is what makes a
     turning mark read as indeterminate progress, and this is a greeting.
     Transform on a wrapper span, so it composes with the inner gesture's own
     transforms instead of fighting them for the same property, and so the turn
     costs a composite rather than a layout in the shell's first paint. */
  [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-star] {
    animation: ask-leo-utility-star var(--ask-leo-star-duration)
      var(--ask-leo-star-delay) cubic-bezier(0.34, 1.4, 0.64, 1) backwards;
  }

  @media (forced-colors: active) {
    [data-ask-leo-utility-glow-wrap] [data-slot="ask-leo-toggle"]::before {
      display: none;
    }

    /* Forced-colors palettes are the user's, not ours, and a brand-tinted
       gradient is neither in that palette nor able to be substituted into it. */
    [data-ask-leo-utility-glow]::after,
    [data-ask-leo-utility-wash],
    [data-ask-leo-utility-spill] {
      display: none;
    }

    /* The border is the user's `ButtonBorder` here, not the brand, and an outline
       fading up from transparent is the one part of the arrival that could leave a
       control with no visible edge at all in a palette we do not control. */
    [data-ask-leo-utility-glow-wrap][data-intro] [data-slot="ask-leo-toggle"] {
      animation: none;
    }

    /* The label keeps its rise; the brand bloom is the part with no substitute in
       the user's palette. Held at the end frame so the type is never dimmed or
       offset by an arrival that has nothing left to show — and with the animation
       off, the box never collapses either, so the chip is simply the width of its
       own label. */
    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-label] {
      animation: none;
      opacity: 1;
      text-shadow: none;
      transform: none;
    }

    /* A brand band clipped to the glyphs is a second colour on the type that the
       user's palette did not ask for. */
    [data-ask-leo-utility-glow-wrap][data-intro]
      [data-ask-leo-utility-label]::after {
      display: none;
    }

    /* The star's turn costs the glyph nothing to skip: upright is where it lands
       anyway, and a rotated mark mid-arrival is the one frame of this that could
       be mistaken for a different icon. */
    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-star] {
      animation: none;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    [data-ask-leo-utility-glow] {
      animation: none;
      opacity: var(--ask-leo-glow-rest-opacity);
      transition: none;
      will-change: auto;
    }

    [data-ask-leo-utility-glow-wrap] [data-slot="ask-leo-toggle"]::before {
      transition: none;
    }

    /* The sheen and the wash are motion and nothing else — there is no static
       form of either worth keeping, so neither renders. Restated at the
       `[data-intro]` selectors' specificity as well: those match an extra
       attribute, so a reset on the plain selector would lose to them.

       `AskLeoLauncherWash` already declines to mount under reduced motion, so
       this is the floor rather than the mechanism — and it needs to be, because
       the bar lobes hold at full opacity when their own keyframes are stripped
       (`leo-blob-animations.css`), which is exactly the static brand field
       parked behind the label that the gate exists to prevent. */
    [data-ask-leo-utility-glow]::after,
    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-glow]::after,
    [data-ask-leo-utility-wash],
    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-wash],
    [data-ask-leo-utility-spill],
    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-spill] {
      animation: none;
      display: none;
    }

    /* Same for the border: the chip's edge is form, not flourish, so it is simply
       there from the first frame. */
    [data-ask-leo-utility-glow-wrap][data-intro] [data-slot="ask-leo-toggle"] {
      animation: none;
    }

    /* The label is the one arrival with something worth keeping when motion is
       off — the type itself — so it holds the end frame rather than not rendering.
       A 2px rise, a one-second bloom, and a chip that opens from icon-only are all
       motion, and none of them are load bearing: the label is there at its own
       width from the first frame. */
    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-label] {
      animation: none;
      opacity: 1;
      text-shadow: none;
      transform: none;
    }

    /* The shimmer is a travelling highlight and nothing else — there is no static
       form of it worth parking on the type. */
    [data-ask-leo-utility-glow-wrap][data-intro]
      [data-ask-leo-utility-label]::after {
      display: none;
    }

    /* A turning mark is the loudest thing in this arrival and the least load
       bearing: the star is upright at rest, so not turning it changes nothing
       about what the launcher says. `LeoIcon` drops its own gesture under the
       same query. */
    [data-ask-leo-utility-glow-wrap][data-intro] [data-ask-leo-utility-star] {
      animation: none;
    }
  }

  /* Shell rails glide open and closed instead of snapping.
     `react-resizable-panels` writes each panel's width as an inline `flex-grow`,
     so a rail mounting or unmounting relands every width in one frame. Animating
     that one property carries the whole row: as main's grow travels, the rail's
     share of the row travels with it. 200ms `ease-out` is the sidebar's own
     timing, so the primary nav and the rails move together.
     `useRailLayoutTransition` arms this from first render (a transition cannot
     animate a value the element never had) and sets `data-rail-transition-off`
     during separator drags and window resizes, where interpolation would only
     add lag behind the pointer. */
  [data-rail-transition="armed"]:not([data-rail-transition-off]) > [data-panel] {
    transition: flex-grow 200ms cubic-bezier(0, 0, 0.2, 1);
  }

  @media (prefers-reduced-motion: reduce) {
    [data-rail-transition="armed"] > [data-panel] {
      transition: none;
    }
  }

  /* Product suffix — tokenized fill; survives utility-bar hover foreground.
     On the utility bar the name sits next to the breadcrumb, so it uses
     primary ink (`--foreground`) rather than the brand suffix colour. The
     round mark still carries the brand. */
  [data-product-wordmark-suffix] {
    color: var(--product-wordmark-suffix);
  }

  [data-slot="utility-bar"] [data-utility-bar-product-switcher] [data-product-wordmark-suffix] {
    color: var(--foreground);
  }

  [data-slot="utility-bar"]
    [data-utility-bar-product-switcher]:is(:hover, :focus-visible, [aria-expanded="true"])
    [data-product-wordmark-suffix] {
    color: var(--foreground);
  }

  /* Utility bar product switcher: truncate the wordmark when the row is
     tight; drop it entirely under `md` so the mark + actions still fit. */
  [data-slot="utility-bar"] [data-utility-bar-product-switcher] [data-product-logo] {
    overflow: hidden;
    min-width: 0;
  }

  @media (max-width: 767px) {
    [data-slot="utility-bar"] [data-utility-bar-product-switcher] [data-product-wordmark-prefix],
    [data-slot="utility-bar"] [data-utility-bar-product-switcher] [data-product-wordmark-suffix],
    [data-slot="utility-bar"] [data-utility-bar-product-switcher] .fa-chevron-down {
      display: none;
    }
  }

  /*
   * Split hub card (`ListPageSplitHubChrome`) — outer column headers paint the
   * card's top corner radius so panel chrome does not square-cut the border.
   * Avoid `overflow:hidden` on the surface; it traps sticky headers.
   */
  [data-slot="resizable-panel-group"][aria-orientation="horizontal"]
    > [data-slot="resizable-panel"]:first-child
    [data-slot="list-page-tree-column-header"] {
    border-top-left-radius: var(--radius-xl);
  }

  [data-slot="resizable-panel-group"][aria-orientation="horizontal"]
    > [data-slot="resizable-panel"]:last-child
    [data-slot="list-page-tree-column-header"] {
    border-top-right-radius: var(--radius-xl);
  }

  [data-slot="resizable-panel-group"][aria-orientation="vertical"]
    > [data-slot="resizable-panel"]:first-child
    [data-slot="list-page-tree-column-header"] {
    border-top-left-radius: var(--radius-xl);
    border-top-right-radius: var(--radius-xl);
  }
}

/* Brightness only — the halo must not scale. Its first shadow layer is a 1px
   spread that traces the chip's border, and `transform` scales the shadow along
   with the box: at 0.88 on a ~104x32 chip the left and right edges pull ~6px
   inside the button before swinging ~3px outside it at 1.06, and the 8px corner
   renders at 7px against the chip's 8px. The ring visibly came off the border
   mid-animation. Fading a fixed box keeps every edge registered to the button,
   and the bloom still reads because opacity overshoots to full before settling. */
@keyframes ask-leo-utility-glow-intro {
  0% {
    opacity: 0;
  }

  48% {
    opacity: 1;
  }

  /* Lands on the resting halo — now zero — so dropping `data-intro` doesn't blink
     the glow. Read from the variable rather than written as `0` so the two cannot
     drift apart, and so setting a resting halo again is one edit. */
  100% {
    opacity: var(--ask-leo-glow-rest-opacity, 0);
  }
}

/* The envelope the wash arrives on. Opacity only: what crosses the chip is the
   bar lobes' own drift, so this decides when the launcher starts showing them,
   not how they move.
   It lands on the resting opacity and stays there, because the wash is the
   chip's surface for the whole session rather than a flourish on load. The
   ceiling reads that same custom property so the arrival is never louder than
   the field it hands over to. */
@keyframes ask-leo-utility-wash {
  0% {
    opacity: 0;
  }

  100% {
    opacity: min(1, calc(var(--ask-leo-chip-blob-opacity) * var(--ask-leo-chip-blob-scale, 1)));
  }
}

/* The envelope the edge light arrives on. Opacity only, like the wash: what
   moves around the outline is the copied field's own drift.
   The 46% overshoot is the whole point — it carries the ring past the level it
   will hold at, so the outline reads as catching light rather than as a border
   fading up. Both the peak and the landing are the field's budget times a
   multiplier so the arrival can never outrun the resting design: 1.15 at the peak
   is a lift over the lit level, 0.9 is the lit level itself, which is where
   `data-emphatic` holds the ring until the intro lets go. */
@keyframes ask-leo-utility-spill-intro {
  0% {
    opacity: 0;
  }

  46% {
    opacity: min(1, calc(var(--ask-leo-chip-blob-opacity) * var(--ask-leo-chip-blob-scale, 1) * 1.15));
  }

  100% {
    opacity: min(1, calc(var(--ask-leo-chip-blob-opacity) * var(--ask-leo-chip-blob-scale, 1) * 0.9));
  }
}

/* The outline, from nothing to the chip's own border, brighter on the way through.
   No `100%`: the implicit end frame is the element's computed `border-color`, so
   the resting value stays declared once, on the chip's class, and hover and
   `data-leo-open` land on theirs without a second copy here.
   The peak is hover's own level rather than a number of its own — the arrival is
   allowed to be as bright as the pointer makes it and no brighter. */
@keyframes ask-leo-utility-border {
  0% {
    border-color: transparent;
  }

  58% {
    border-color: color-mix(in oklab, var(--color-brand) 70%, transparent);
  }
}

/* The chip opens. Only the label's own box moves: the button's width is its
   content, so growing the label from nothing takes the chip from icon-only to
   icon-and-name without either value being written down twice, and without any
   layout the neighbours have to agree to.
   The negative start margin cancels the button's `gap-1.5`, or the collapsed chip
   would keep 6px of empty space where the name is going to be.
   No `overflow: hidden`, deliberately: the type sits at zero opacity until the
   box has finished opening (`ask-leo-utility-label` starts as this ends), so
   there is nothing visible to clip — and a clip here would have cut the label's
   own bloom off at the edges of a box that hugs the glyphs.
   The ceiling is generous rather than exact, and only the box is clamped by it:
   nothing holds it after the animation, so the label's real width is what governs
   from the landing frame on. */
@keyframes ask-leo-utility-label-open {
  0% {
    margin-inline-start: -0.375rem;
    max-width: 0;
  }

  100% {
    margin-inline-start: 0;
    max-width: 5rem;
  }
}

/* A quarter turn in, with the overshoot the easing carries.
   Counter-clockwise to upright: the star spins into place rather than out of it,
   which is what separates an arrival from a flourish on something already there.
   The scale starts small and passes 1 on the way, so the turn is read as one
   gesture and not as a spin plus a pop.
   Lands on `none` rather than `rotate(0) scale(1)`: the star's resting state is
   no transform at all, so dropping `data-intro` cannot leave a matrix behind on
   the wrapper for the inner gesture to compose against. */
@keyframes ask-leo-utility-star {
  0% {
    transform: rotate(-90deg) scale(0.55);
  }

  70% {
    transform: rotate(4deg) scale(1.1);
  }

  100% {
    transform: none;
  }
}

/* One band, off-edge to off-edge, on a duplicate of the glyphs. Same envelope
   idiom as the border sheen — up fast, gone before the end — so the two passes
   read as one light rather than as two effects that happen to overlap. */
@keyframes ask-leo-utility-label-shimmer {
  0% {
    background-position: 140% 0;
    opacity: 0;
  }

  14% {
    opacity: 1;
  }

  72% {
    opacity: 1;
  }

  100% {
    background-position: -60% 0;
    opacity: 0;
  }
}

/* Rise, one bloom, done. The shadow's geometry is identical in all three frames
   and only its colour changes, because `none` does not interpolate with a shadow
   — written as `none` at the ends, the bloom would pop in and out at whatever
   frame the engine decided to switch. A transparent shadow of the same blur
   interpolates cleanly and paints nothing.
   Ends on the type as the bar styles it: no transform, no shadow, full opacity —
   so dropping `data-intro` changes nothing about the label. */
@keyframes ask-leo-utility-label {
  0% {
    opacity: 0;
    text-shadow: 0 0 10px transparent;
    transform: translateY(2px);
  }

  55% {
    opacity: 1;
    text-shadow: 0 0 10px var(--ask-leo-sheen-color);
  }

  100% {
    opacity: 1;
    text-shadow: 0 0 10px transparent;
    transform: none;
  }
}

/* One pass, left to right, off-edge to off-edge. The opacity envelope fades in
   fast and leaves before the end, so the sheen is a pass of light rather than
   something that stops mid-edge and blinks out. Ends at 0 so the resting chip is
   the halo and its inner glow, nothing else. */
@keyframes ask-leo-utility-sheen {
  0% {
    background-position: 140% 0;
    opacity: 0;
  }

  14% {
    opacity: 1;
  }

  72% {
    opacity: 1;
  }

  100% {
    background-position: -60% 0;
    opacity: 0;
  }
}

/* After all @layer rules: win over Tailwind preflight / layer merge so real controls show a pointer. */
button:not(:disabled):not([disabled]),
[role="button"]:not([aria-disabled="true"]):not([data-disabled]) {
  cursor: pointer;
}

/* Selected tab: the glyph goes solid.
 *
 * Weight, not a class swap, because the design system does not own these glyphs.
 * `Tabs` takes the icon as children and `ViewSegmentedControl` takes a whole
 * class string from its consumer, so there is nothing to swap from here. Font
 * Awesome's webfont picks a cut by weight within one family (thin 100, light 300,
 * regular 400, solid 900), so promoting the weight of the glyph inside the
 * selected tab draws the solid cut wherever the icon came from, and a hub that
 * passes `fa-light fa-table` needs no edit to get it.
 *
 * The three selectors are the three ways a tab says it is the selected one: Radix
 * tabs use `data-state`, the hub's view rail uses `aria-pressed`, and
 * `ViewSegmentedControl` is a radio group, so `aria-checked`. Brands and duotone
 * are left out: each ships a single weight, so promoting them would only invite
 * the browser to fake a bold.
 *
 * Unlayered, like the cursor rule above, and for a related reason. The Font
 * Awesome kit injects its own `.fa-light { font-weight: 300 }` at runtime with no
 * cascade layer, and unlayered CSS beats layered CSS whatever the specificity, so
 * the same rule inside `@layer components` loses silently. */
[data-slot="tabs-trigger"][data-state="active"] :is(.fa-thin, .fa-light, .fa-regular),
[data-slot="view-segmented-item"][aria-pressed="true"] :is(.fa-thin, .fa-light, .fa-regular),
[data-slot="view-segmented-item"][aria-checked="true"] :is(.fa-thin, .fa-light, .fa-regular) {
  font-weight: 900;
}
