/* ============================================================================
   Cascade layer order — declared ONCE, before any other stylesheet.
   ----------------------------------------------------------------------------
   For NORMAL declarations, later layers win regardless of selector specificity.
   `legacy` is therefore deliberately FIRST (lowest priority): it holds every
   pre-token sheet as-is, so new component CSS in primitives/composites/domain
   wins without having to match light-mode.css's 233-rule `:root.light .x`
   specificity floor, and without a single new !important.

   Two cascade facts this design depends on:

   1. UNLAYERED CSS BEATS EVERY LAYERED RULE. Every existing sheet must be
      wrapped in `@layer legacy` — an unwrapped sheet silently outranks the
      whole design system. This is why App.tsx imports the legacy bundle via
      styles/legacy/index.css rather than importing the sheets directly.

   2. FOR `!important`, LAYER ORDER IS REVERSED. A legacy `!important` rule
      still beats a normal declaration in a higher layer. Layers neutralise
      ordinary specificity (the overwhelming majority of the problem) but not
      the 73 existing !important declarations — those must still be deleted
      surface-by-surface as each one migrates.
   ========================================================================== */
@layer legacy, tokens, base, primitives, composites, domain, pages;
/* ============================================================================
   LAYER 1 — PRIMITIVES
   ----------------------------------------------------------------------------
   Raw values. Theme-INDEPENDENT: nothing here changes between light and dark.
   This is the ONLY file in the codebase permitted to contain literal values
   (hex, px, rem, ms). Everything else reads --c-* (semantic) or --k-* (component).

   Every scale below was derived from measured usage, not invented:
   31 radii -> 6 steps, 118 shadows -> 5, 69 font sizes -> 10,
   238 paddings + 52 gaps -> one 4px scale, 20+ breakpoints -> 4,
   6 greens + 6 reds -> one ramp each.
   ========================================================================== */
@layer tokens {
  :root {
    /* ---- Space: single 4px base. The only sanctioned padding/gap/margin ---- */
    --p-space-0: 0;
    --p-space-05: 0.125rem;   /* 2px  — icon nudges only */
    --p-space-1: 0.25rem;     /* 4px  */
    --p-space-2: 0.5rem;      /* 8px  */
    --p-space-3: 0.75rem;     /* 12px */
    --p-space-4: 1rem;        /* 16px */
    --p-space-5: 1.5rem;      /* 24px */
    --p-space-6: 2rem;        /* 32px */
    --p-space-7: 3rem;        /* 48px */
    --p-space-8: 4rem;        /* 64px */

    /* ---- Radius: collapses all 31 measured values by frequency ---- */
    --p-radius-sm: 4px;       /* was 4px x54 + 6px x71 */
    --p-radius-md: 8px;       /* was 8px x98 */
    --p-radius-lg: 12px;      /* was 10px x75 + 12px x75 */
    --p-radius-xl: 16px;      /* was 20px (Admin.css:368) */
    --p-radius-2xl: 24px;     /* was 24px (theme.css:23) */
    --p-radius-full: 999px;   /* was 999px x26 + 50% x40 */

    /* ---- Elevation: 5 steps for 118 measured shadows, per theme ---- */
    --p-shadow-dark-0: none;
    --p-shadow-dark-1: 0 1px 2px rgba(1, 4, 22, 0.45);
    --p-shadow-dark-2: 0 4px 12px rgba(1, 4, 22, 0.5);
    /* Steps 3-4 tightened: at 28px/60px blur on a dark UI the shadow read as a
       page vignette rather than as a lifted surface. */
    --p-shadow-dark-3: 0 8px 20px rgba(1, 4, 22, 0.5);
    --p-shadow-dark-4: 0 16px 40px rgba(1, 4, 22, 0.6);

    --p-shadow-light-0: none;
    --p-shadow-light-1: 0 1px 2px rgba(16, 24, 40, 0.06);
    --p-shadow-light-2: 0 4px 12px rgba(16, 24, 40, 0.08);
    --p-shadow-light-3: 0 12px 28px rgba(16, 24, 40, 0.1);
    --p-shadow-light-4: 0 24px 48px rgba(16, 24, 40, 0.12);

    /* ---- Type: 10 steps for 69 measured sizes ---- */
    --p-text-2xs: 0.6875rem;  /* 11px — absorbs the 0.65-0.70 cluster */
    --p-text-xs: 0.75rem;     /* 12px — absorbs 0.72/0.75 */
    --p-text-sm: 0.8125rem;   /* 13px — absorbs 0.78/0.80/0.82 */
    --p-text-md: 0.875rem;    /* 14px — absorbs 0.85/0.875 */
    --p-text-base: 1rem;
    --p-text-lg: 1.125rem;
    --p-text-xl: 1.25rem;
    --p-text-2xl: 1.5rem;
    --p-text-3xl: 2rem;
    --p-text-4xl: 2.5rem;

    --p-leading-tight: 1.2;
    --p-leading-snug: 1.35;
    --p-leading-normal: 1.5;
    --p-leading-relaxed: 1.6;

    --p-weight-regular: 400;
    --p-weight-medium: 500;
    --p-weight-semibold: 600;
    --p-weight-bold: 700;

    --p-tracking-tight: -0.01em;
    --p-tracking-normal: 0;
    --p-tracking-caption: 0.06em;   /* replaces 0.04 / 0.05 / 0.1em */

    --p-font-sans: 'Inter', 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
    --p-font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, monospace;
    /* Tabular + slashed zero, so live price/pnl columns stop reflowing per tick */
    --p-numeric-tnum: 'tnum' 1, 'zero' 1;

    /* ---- Dark surface ramp.
       Seeded from the values that ACTUALLY RENDER today, which are the ones in
       styles/layout.css:5-34 — not the ones in index.css. Both files declare a
       bare `:root` block for the same variable names, and App.tsx imports
       layout.css after main.tsx imports index.css, so layout.css wins.
       (index.css's deeper navy survives only in --c-page-gradient, because
       --gradient-bg is defined nowhere else. That is why the app currently
       paints a deep-navy body behind slate-navy cards.)
       Anchoring the ramp here means migrated and unmigrated components look
       identical side by side during the migration. ---- */
    --p-navy-950: #010416;   /* index.css --bg-primary; page gradient origin */
    --p-navy-900: #0a0e1a;   /* layout.css --bg-primary  — rendered page bg */
    --p-navy-850: #12161f;   /* layout.css --bg-secondary */
    --p-navy-800: #1a1f2e;   /* layout.css --bg-elevated */
    --p-navy-750: #232a3b;   /* one rung ABOVE the card fill, for overlays.
                                Added because --c-surface-overlay pointed at
                                navy-800, which is DARKER than the navy-700 card
                                it covers — a modal read as recessed. */
    --p-navy-700: #1e2433;   /* layout.css --bg-card     — rendered card fill */
    --p-navy-600: #16224a;
    --p-navy-500: #24325f;

    /* ---- Neutral / light surfaces + all neutral text ---- */
    --p-slate-50: #fafbff;
    --p-slate-100: #f4f6fa;
    --p-slate-200: #eef1f7;
    --p-slate-300: #e2e8f2;
    --p-slate-400: #cbd5e1;
    --p-slate-500: #94a3b8;   /* absorbs #94a3b8 x53 */
    --p-slate-600: #64748b;
    --p-slate-700: #475569;
    --p-slate-800: #334155;
    --p-slate-900: #1a202c;

    /* ---- Alpha ladders: the sanctioned washes. Replaces 164 raw rgba() ---- */
    --p-white-a02: rgba(255, 255, 255, 0.02);
    --p-white-a04: rgba(255, 255, 255, 0.04);
    --p-white-a06: rgba(255, 255, 255, 0.06);
    --p-white-a08: rgba(255, 255, 255, 0.08);
    --p-white-a12: rgba(255, 255, 255, 0.12);
    --p-white-a16: rgba(255, 255, 255, 0.16);

    --p-black-a20: rgba(1, 4, 22, 0.2);
    --p-black-a30: rgba(1, 4, 22, 0.3);
    --p-black-a45: rgba(1, 4, 22, 0.45);
    --p-black-a60: rgba(1, 4, 22, 0.6);

    /* ---- ONE green ramp. Retires #4ade80 x69, #34d399 x35, #43e6b1 x25,
            #22c55e x22, #10b981, #51cf66, #059669 ---- */
    --p-green-200: #86efac;
    --p-green-400: #4ade80;
    --p-green-500: #22c55e;
    --p-green-600: #16a34a;
    --p-green-700: #15803d;

    /* ---- ONE red ramp. Retires #f87171 x77, #ef4444 x36, #dc2626 x32,
            #ff6b6b x22, #ff8b8b, #991b1b ---- */
    --p-red-200: #fecaca;
    --p-red-400: #f87171;
    --p-red-500: #ef4444;
    --p-red-600: #dc2626;
    --p-red-700: #b91c1c;

    --p-amber-200: #fde68a;
    --p-amber-400: #fbbf24;
    --p-amber-500: #f59e0b;
    --p-amber-600: #d97706;
    --p-amber-700: #b45309;

    --p-blue-200: #bfdbfe;
    --p-blue-400: #60a5fa;
    --p-blue-500: #38bdf8;
    --p-blue-600: #2563eb;
    --p-blue-700: #1d4ed8;

    --p-indigo-300: #a5b4fc;
    --p-indigo-400: #818cf8;
    --p-indigo-500: #6c63ff;   /* existing dark brand accent, x34 */
    --p-indigo-600: #6366f1;   /* existing light brand accent */
    --p-indigo-700: #4f46e5;
    --p-indigo-800: #4338ca;   /* light-theme pressed state: one rung below the
                                  indigo-700 hover, so `active` stays same-hue */

    --p-violet-500: #a76bff;
    --p-violet-600: #7c3aed;

    /* ---- Categorical chart series. Ordered for hue separation, NOT brand
            order. Never used for sign/PnL — that is --c-pnl-*. ---- */
    /* Resequenced so the first slots hold NO semantic hue: amber and green used
       to be slots 3-4, so a 4-series chart drew a "stale" amber and a "profit"
       green next to real P&L colours in the same viewport. Both are pushed to
       the tail; hue separation between adjacent slots is preserved. */
    --p-chart-cat-1: var(--p-indigo-500);
    --p-chart-cat-2: #38bdf8;
    --p-chart-cat-3: #a78bfa;
    --p-chart-cat-4: #f472b6;
    --p-chart-cat-5: #2dd4bf;
    --p-chart-cat-6: #fb923c;
    --p-chart-cat-7: #f59e0b;
    --p-chart-cat-8: #22c55e;

    /* ---- Z-index. Replaces 16 magic numbers (1, 2, 3, 5, 10, 30, 50, 80, 99,
            100, 900, 1000, 1001, 1190, 1200, 9999) including the 1190/1200 pair
            that existed only to sit above a 1000, and the two independent
            `z-index: 100` owners (.sidebar and .search-results-dropdown) whose
            relative order was decided by DOM position rather than intent.

            EVERY SCRIM SITS DIRECTLY BELOW THE SURFACE IT DIMS. That pairing is
            the whole point of the scale: a scrim must cover page content but
            never its own panel. Getting this backwards makes the panel
            unclickable, so the pairs are adjacent and named together.

            Page chrome needs two ranks because the mobile sidebar is a drawer
            that must cover the topbar. ---- */
    --p-z-base: 0;
    --p-z-raised: 10;          /* in-card affordances: collapse button, sticky th */
    --p-z-sticky: 100;         /* topbar, sticky table headers, sticky sidebars */
    --p-z-sticky-above: 110;   /* desktop sidebar: same plane, must beat topbar */
    --p-z-dropdown-scrim: 190; /* invisible click-catcher under an open menu */
    --p-z-dropdown: 200;       /* menus, popovers, comboboxes, search results */
    --p-z-drawer-scrim: 280;
    --p-z-drawer: 300;         /* mobile nav drawer, detail drawers */
    --p-z-modal-scrim: 480;
    --p-z-modal: 500;          /* dialogs, command palette */
    --p-z-toast: 600;
    --p-z-tooltip: 700;        /* always last: may be triggered from inside a modal */

    /* ---- Motion. Measured usage already clustered here
            (0.2s x149, 0.3s x32, 0.15s x25). ---- */
    /* Retuned DOWN for a trading UI: a screen whose numbers change every tick
       must not also be easing. Overlays fade at -base, everything else -fast.
       ~150 literal durations in unmigrated sheets still bypass these. */
    --p-dur-instant: 0ms;
    --p-dur-fast: 100ms;
    --p-dur-base: 160ms;
    --p-dur-slow: 240ms;

    --p-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
    --p-ease-out: cubic-bezier(0, 0, 0.2, 1);
    --p-ease-in: cubic-bezier(0.4, 0, 1, 1);

    /* ---- Misc ---- */
    --p-border-hairline: 1px;
    --p-border-thin: 1px;
    --p-border-thick: 2px;

    --p-opacity-disabled: 0.45;
    --p-opacity-muted: 0.65;
    --p-opacity-stale: 0.55;   /* the literal already at AdminPriceFreshnessPage.css:238 */

    --p-blur-sm: 6px;
    --p-blur-overlay: 12px;

    /* ---- Breakpoints. Documented here for reference; the enforceable form is
            the postcss custom-media block in media.css, because CSS variables
            are not valid inside a @media query. ---- */
    --p-bp-sm: 480px;   /* was 400/440/480/500 */
    --p-bp-md: 768px;   /* was 600/640/680/700/720/760/768 */
    --p-bp-lg: 1024px;  /* was 860/900/960/1024 */
    --p-bp-xl: 1280px;  /* was 1100/1200 */
  }
}
/* ============================================================================
   LAYER 2 — SEMANTIC ROLES, DARK THEME (the default)
   ----------------------------------------------------------------------------
   RULE: this file may contain ONLY `--c-*` declarations.
   A `:root[data-theme] .some-class { ... }` selector in a theme file is a bug
   by definition — that pattern is exactly the 335 component-property overrides
   this architecture replaces.

   Applied to :root (not :root[data-theme="dark"]) so dark is the fallback when
   no theme attribute has been set yet — avoids an unstyled first paint.
   ========================================================================== */
@layer tokens {
  :root {
    /* ---- Surfaces ---- */
    --c-surface-base: var(--p-navy-900);
    --c-surface-sunken: var(--p-black-a30);
    --c-surface-raised: var(--p-navy-700);      /* THE one card/panel fill */
    /* Must sit ABOVE --c-surface-raised, never below it. This pointed at
       navy-800 (#1a1f2e), darker than the navy-700 card it covers, so every
       modal/drawer/dropdown/tooltip read as recessed into the page. */
    --c-surface-overlay: var(--p-navy-750);     /* modals, drawers, dropdowns, toasts */
    --c-surface-hover: var(--p-white-a04);
    --c-surface-active: var(--p-white-a08);
    --c-scrim: var(--p-black-a60);

    /* ---- Text ---- */
    /* Three DISTINCT ranks. --c-text-secondary was #dbe7ff, only ~2 steps of
       lightness off primary — and base.css sets :root { color: secondary }, so
       most of the app rendered at near-primary brightness with no usable middle
       rank. On the #1e2433 card: ~15.8:1 / ~10.5:1 / ~5.4:1.
       Also retires the only raw hex in a semantic file. */
    --c-text-primary: var(--p-slate-50);
    --c-text-secondary: var(--p-slate-400);
    --c-text-muted: var(--p-slate-500);
    --c-text-inverse: var(--p-slate-900);
    --c-text-on-accent: #ffffff;                /* home of the #fff x132 on buttons */

    /* ---- Borders ---- */
    --c-border-subtle: var(--p-white-a08);
    --c-border-medium: var(--p-white-a12);
    --c-border-strong: var(--p-white-a16);

    /* ---- Brand / interactive ---- */
    /* STATE MOVES DEPTH, NEVER HUE: hover steps one rung toward the viewer
       (lighter on dark), active one rung further away. Hover used to jump to
       violet, so the brand hue changed on mouseover while the comment below
       claimed "deeper". Violet stays a chart-level role (--p-violet-*). */
    --c-accent: var(--p-indigo-500);
    --c-accent-hover: var(--p-indigo-400);
    --c-accent-active: var(--p-indigo-700);     /* pressed: deeper than hover */
    --c-accent-soft: rgba(108, 99, 255, 0.18);
    --c-accent-glow: rgba(108, 99, 255, 0.35);

    /* Inner 2px is the surface the focused element ACTUALLY sits on, which for
       almost every button/input/select in the app is a card, not the page.
       Bound to --c-surface-base it painted a dark halo on every card. */
    --c-focus-ring: 0 0 0 2px var(--c-surface-raised), 0 0 0 4px var(--c-accent);

    --c-disabled-fg: var(--c-text-muted);
    --c-disabled-bg: var(--p-white-a04);
    --c-disabled-opacity: var(--p-opacity-disabled);

    /* ---- Elevation: the single indirection that makes shadows theme-aware.
            Previously --shadow-* existed ONLY inside :root.light, so dark mode
            had no shadow tokens at all and wrote 118 shadows longhand. ---- */
    --c-elevation-0: var(--p-shadow-dark-0);
    --c-elevation-1: var(--p-shadow-dark-1);
    --c-elevation-2: var(--p-shadow-dark-2);
    --c-elevation-3: var(--p-shadow-dark-3);
    --c-elevation-4: var(--p-shadow-dark-4);

    /* ---- Feedback. For confirmations and destructive actions ONLY.
            Never PnL — that is --c-pnl-* below. ---- */
    --c-danger-fg: var(--p-red-400);
    --c-danger-bg: rgba(239, 68, 68, 0.12);
    --c-danger-border: rgba(239, 68, 68, 0.35);

    --c-warning-fg: var(--p-amber-400);
    --c-warning-bg: rgba(245, 158, 11, 0.12);
    --c-warning-border: rgba(245, 158, 11, 0.35);

    --c-success-fg: var(--p-green-400);
    --c-success-bg: rgba(34, 197, 94, 0.12);
    --c-success-border: rgba(34, 197, 94, 0.35);

    --c-info-fg: var(--p-blue-400);
    --c-info-bg: rgba(56, 189, 248, 0.12);
    --c-info-border: rgba(56, 189, 248, 0.35);

    /* ---- PnL. DOMAIN tier, owned by Money/Delta. Deliberately separate from
            --c-success/--c-danger so "saved OK" and "up 2%" can diverge
            without re-forking six greens. ---- */
    --c-pnl-positive: var(--p-green-400);
    --c-pnl-positive-bg: rgba(34, 197, 94, 0.14);
    --c-pnl-positive-border: rgba(34, 197, 94, 0.32);

    --c-pnl-negative: var(--p-red-400);
    --c-pnl-negative-bg: rgba(239, 68, 68, 0.14);
    --c-pnl-negative-border: rgba(239, 68, 68, 0.32);

    /* Zero/unchanged. Currently renders as an accidental green app-wide. */
    --c-pnl-flat: var(--c-text-muted);
    --c-pnl-flat-bg: var(--p-white-a04);
    --c-pnl-flat-border: var(--c-border-subtle);

    /* Separate names so order side can be recoloured independently of PnL.
       They alias the PnL steps TODAY, but a BUY is not a profit — the aliasing is
       a starting value, not a definition, and the badge tones read these names so
       changing them here is enough. */
    --c-side-buy: var(--c-pnl-positive);
    --c-side-buy-bg: var(--c-pnl-positive-bg);
    --c-side-buy-border: var(--c-pnl-positive-border);
    --c-side-sell: var(--c-pnl-negative);
    --c-side-sell-bg: var(--c-pnl-negative-bg);
    --c-side-sell-border: var(--c-pnl-negative-border);

    /* ---- Strategy phase. Generalises the only existing phase palette — the
            `.phase-waiting`/-scanning/-monitoring/-ready/-in-position/-done set
            that used to live in VWAPEMAMomentumStrategyStatus.css, deleted in
            PR-7 once that widget moved onto domain/StepTrack — to all 10
            widgets. Consumed by domain/StepTrack/StepTrack.css. ---- */
    --c-phase-idle-fg: var(--c-text-muted);
    --c-phase-idle-bg: var(--p-white-a04);
    --c-phase-idle-border: var(--c-border-subtle);

    --c-phase-done-fg: var(--c-text-muted);
    --c-phase-done-bg: var(--p-white-a04);
    --c-phase-done-border: var(--c-border-subtle);

    --c-phase-warming-fg: var(--p-amber-400);
    --c-phase-warming-bg: rgba(245, 158, 11, 0.12);
    --c-phase-warming-border: rgba(245, 158, 11, 0.3);

    --c-phase-scanning-fg: var(--p-blue-400);
    --c-phase-scanning-bg: rgba(56, 189, 248, 0.12);
    --c-phase-scanning-border: rgba(56, 189, 248, 0.3);

    /* Armed states get a SOLID border so "about to spend money" is visually
       distinct from "in profit". */
    --c-phase-armed-long-fg: var(--p-green-400);
    --c-phase-armed-long-bg: rgba(34, 197, 94, 0.14);
    --c-phase-armed-long-border: var(--p-green-500);

    --c-phase-armed-short-fg: var(--p-red-400);
    --c-phase-armed-short-bg: rgba(239, 68, 68, 0.14);
    --c-phase-armed-short-border: var(--p-red-500);

    /* Accent, NOT green/red, so live position chrome never fights the PnL
       sign rendered inside it. */
    --c-phase-in-position-fg: var(--p-indigo-400);
    --c-phase-in-position-bg: var(--c-accent-soft);
    --c-phase-in-position-border: var(--c-accent);

    /* ---- Entry gates. "gate" appears in 166 TSX locations with only 3 CSS
            files behind it. ---- */
    --c-gate-pass-fg: var(--p-green-500);
    --c-gate-pass-bg: rgba(34, 197, 94, 0.12);
    --c-gate-pass-border: rgba(34, 197, 94, 0.3);

    --c-gate-fail-fg: var(--p-red-500);
    --c-gate-fail-bg: rgba(239, 68, 68, 0.12);
    --c-gate-fail-border: rgba(239, 68, 68, 0.3);

    --c-gate-waiting-fg: var(--p-amber-400);
    --c-gate-waiting-bg: rgba(245, 158, 11, 0.1);
    --c-gate-waiting-border: rgba(245, 158, 11, 0.28);

    --c-gate-skipped-fg: var(--c-text-muted);
    --c-gate-skipped-bg: var(--p-white-a04);
    --c-gate-skipped-border: var(--c-border-subtle);

    /* ---- Execution mode. Live is bound to the DANGER hue on purpose:
            real-money surfaces must be unmistakable. ---- */
    --c-mode-paper-fg: var(--p-blue-400);
    --c-mode-paper-bg: rgba(56, 189, 248, 0.12);
    --c-mode-paper-border: rgba(56, 189, 248, 0.35);

    /* SOLID fill, not a tint. Live was red-400 on a 0.14 red wash — pixel-for-pixel
       the same treatment as --c-pnl-negative, so "real money armed" and "down
       Rs 2k" were indistinguishable in the same row. Paper stays a tint, so the
       two modes now differ in FILL WEIGHT rather than only in hue. */
    --c-mode-live-fg: var(--p-red-200);
    --c-mode-live-bg: var(--p-red-700);
    --c-mode-live-border: var(--p-red-500);

    /* ---- Data freshness / connection ---- */
    --c-stale-fg: var(--p-amber-400);
    --c-stale-bg: rgba(245, 158, 11, 0.1);
    --c-stale-opacity: var(--p-opacity-stale);

    --c-conn-live: var(--p-green-500);
    --c-conn-degraded: var(--p-amber-500);
    --c-conn-down: var(--p-red-500);

    /* ---- Loading + meters ---- */
    --c-skeleton-base: var(--p-white-a04);
    --c-skeleton-shimmer: var(--p-white-a08);

    --c-meter-track: var(--p-white-a06);
    --c-meter-fill: var(--c-accent);
    --c-meter-marker: var(--c-text-primary);

    /* ---- Page background gradient (role, so themes can differ) ---- */
    --c-page-gradient:
      radial-gradient(circle at top, rgba(47, 72, 159, 0.45), transparent 55%),
      linear-gradient(135deg, #010416 0%, #030a20 50%, #050c28 100%);
  }
}
/* ============================================================================
   LAYER 2 — SEMANTIC ROLES, LIGHT THEME
   ----------------------------------------------------------------------------
   RULE: this file may contain ONLY `--c-*` declarations. Value swaps, never
   component selectors. This single rule is what retires styles/light-mode.css
   (936 lines, 233 `:root.light .x` rules) plus the 102 scattered overrides —
   including the 53 blocks that were inside WeightedLeadersStrategyStatus.css,
   whose widget moved onto the kit in PR-7 and whose sheet is now deleted.

   The dual selector is the migration window: SettingsContext sets BOTH
   data-theme and the legacy .light class, so this file applies either way and
   the old light-mode.css keeps working until each surface is migrated.

   `:root:not([data-theme])` + prefers-color-scheme handles a future "system"
   option without needing JS.
   ========================================================================== */
@layer tokens {
  :root[data-theme='light'],
  :root.light {
    /* ---- Surfaces ---- */
    --c-surface-base: var(--p-slate-50);
    /* slate-200, not slate-100: #f4f6fa against a #ffffff card is ~1.02:1, so
       wells, table heads and inputs read as flat. --k-control-bg and
       --k-table-head-bg both inherit this, so one swap fixes all three. */
    --c-surface-sunken: var(--p-slate-200);
    --c-surface-raised: #ffffff;
    --c-surface-overlay: #ffffff;
    --c-surface-hover: var(--p-slate-100);
    --c-surface-active: var(--p-slate-200);
    --c-scrim: rgba(26, 32, 44, 0.4);

    /* ---- Text ---- */
    --c-text-primary: var(--p-slate-900);
    --c-text-secondary: var(--p-slate-800);
    --c-text-muted: var(--p-slate-600);
    --c-text-inverse: #ffffff;
    --c-text-on-accent: #ffffff;

    /* ---- Borders ---- */
    /* 0.16, was 0.12: at density 8 a dense table's gridlines all but vanish on
       white. --k-table-border and --k-card-border both inherit this. */
    --c-border-subtle: rgba(100, 116, 139, 0.16);
    --c-border-medium: rgba(100, 116, 139, 0.2);
    --c-border-strong: var(--p-slate-400);

    /* ---- Brand / interactive ---- */
    /* Same rule as dark: state moves depth, never hue. `active` was violet-600,
       so pressing a button changed the brand colour. */
    --c-accent: var(--p-indigo-600);
    --c-accent-hover: var(--p-indigo-700);
    --c-accent-active: var(--p-indigo-800);      /* pressed: deeper than hover */
    --c-accent-soft: rgba(99, 102, 241, 0.12);
    --c-accent-glow: rgba(99, 102, 241, 0.15);

    --c-focus-ring: 0 0 0 2px var(--c-surface-raised), 0 0 0 4px var(--c-accent);

    --c-disabled-fg: var(--c-text-muted);
    --c-disabled-bg: var(--p-slate-200);
    --c-disabled-opacity: var(--p-opacity-disabled);

    /* ---- Elevation ---- */
    --c-elevation-0: var(--p-shadow-light-0);
    --c-elevation-1: var(--p-shadow-light-1);
    --c-elevation-2: var(--p-shadow-light-2);
    --c-elevation-3: var(--p-shadow-light-3);
    --c-elevation-4: var(--p-shadow-light-4);

    /* ---- Feedback. Darker steps for contrast on white. ---- */
    --c-danger-fg: var(--p-red-600);
    --c-danger-bg: rgba(220, 38, 38, 0.08);
    --c-danger-border: rgba(220, 38, 38, 0.28);

    --c-warning-fg: var(--p-amber-700);
    --c-warning-bg: rgba(245, 158, 11, 0.1);
    --c-warning-border: rgba(217, 119, 6, 0.28);

    --c-success-fg: var(--p-green-700);
    --c-success-bg: rgba(22, 163, 74, 0.08);
    --c-success-border: rgba(22, 163, 74, 0.28);

    --c-info-fg: var(--p-blue-600);
    --c-info-bg: rgba(37, 99, 235, 0.08);
    --c-info-border: rgba(37, 99, 235, 0.28);

    /* ---- PnL ---- */
    --c-pnl-positive: var(--p-green-600);
    --c-pnl-positive-bg: rgba(22, 163, 74, 0.1);
    --c-pnl-positive-border: rgba(22, 163, 74, 0.26);

    --c-pnl-negative: var(--p-red-600);
    --c-pnl-negative-bg: rgba(220, 38, 38, 0.1);
    --c-pnl-negative-border: rgba(220, 38, 38, 0.26);

    --c-pnl-flat: var(--c-text-muted);
    --c-pnl-flat-bg: var(--p-slate-100);
    --c-pnl-flat-border: var(--c-border-subtle);

    --c-side-buy: var(--c-pnl-positive);
    --c-side-buy-bg: var(--c-pnl-positive-bg);
    --c-side-buy-border: var(--c-pnl-positive-border);
    --c-side-sell: var(--c-pnl-negative);
    --c-side-sell-bg: var(--c-pnl-negative-bg);
    --c-side-sell-border: var(--c-pnl-negative-border);

    /* ---- Strategy phase ---- */
    --c-phase-idle-fg: var(--c-text-muted);
    --c-phase-idle-bg: var(--p-slate-100);
    --c-phase-idle-border: var(--c-border-subtle);

    --c-phase-done-fg: var(--c-text-muted);
    --c-phase-done-bg: var(--p-slate-100);
    --c-phase-done-border: var(--c-border-subtle);

    --c-phase-warming-fg: var(--p-amber-700);
    --c-phase-warming-bg: rgba(245, 158, 11, 0.1);
    --c-phase-warming-border: rgba(217, 119, 6, 0.26);

    --c-phase-scanning-fg: var(--p-blue-600);
    --c-phase-scanning-bg: rgba(37, 99, 235, 0.08);
    --c-phase-scanning-border: rgba(37, 99, 235, 0.26);

    --c-phase-armed-long-fg: var(--p-green-700);
    --c-phase-armed-long-bg: rgba(22, 163, 74, 0.1);
    --c-phase-armed-long-border: var(--p-green-600);

    --c-phase-armed-short-fg: var(--p-red-700);
    --c-phase-armed-short-bg: rgba(220, 38, 38, 0.1);
    --c-phase-armed-short-border: var(--p-red-600);

    --c-phase-in-position-fg: var(--p-indigo-700);
    --c-phase-in-position-bg: var(--c-accent-soft);
    --c-phase-in-position-border: var(--c-accent);

    /* ---- Entry gates ---- */
    --c-gate-pass-fg: var(--p-green-600);
    --c-gate-pass-bg: rgba(22, 163, 74, 0.1);
    --c-gate-pass-border: rgba(22, 163, 74, 0.26);

    --c-gate-fail-fg: var(--p-red-600);
    --c-gate-fail-bg: rgba(220, 38, 38, 0.1);
    --c-gate-fail-border: rgba(220, 38, 38, 0.26);

    --c-gate-waiting-fg: var(--p-amber-700);
    --c-gate-waiting-bg: rgba(245, 158, 11, 0.1);
    --c-gate-waiting-border: rgba(217, 119, 6, 0.24);

    --c-gate-skipped-fg: var(--c-text-muted);
    --c-gate-skipped-bg: var(--p-slate-100);
    --c-gate-skipped-border: var(--c-border-subtle);

    /* ---- Execution mode. Live stays bound to danger. ---- */
    --c-mode-paper-fg: var(--p-blue-600);
    --c-mode-paper-bg: rgba(37, 99, 235, 0.08);
    --c-mode-paper-border: rgba(37, 99, 235, 0.28);

    /* SOLID, for the same reason as dark: a live badge must not be mistakable
       for a red number sitting next to it. */
    --c-mode-live-fg: #ffffff;
    --c-mode-live-bg: var(--p-red-600);
    --c-mode-live-border: var(--p-red-700);

    /* ---- Freshness / connection ---- */
    --c-stale-fg: var(--p-amber-700);
    --c-stale-bg: rgba(245, 158, 11, 0.1);
    --c-stale-opacity: var(--p-opacity-stale);

    --c-conn-live: var(--p-green-600);
    --c-conn-degraded: var(--p-amber-600);
    --c-conn-down: var(--p-red-600);

    /* ---- Loading + meters ---- */
    --c-skeleton-base: var(--p-slate-200);
    --c-skeleton-shimmer: var(--p-slate-100);

    --c-meter-track: var(--p-slate-200);
    --c-meter-fill: var(--c-accent);
    --c-meter-marker: var(--c-text-primary);

    /* ---- Page background ---- */
    --c-page-gradient: linear-gradient(180deg, #fafbff 0%, #f0f3fa 50%, #e8ecf4 100%);
  }

  /* "System" theme: no explicit data-theme attribute, OS prefers light.
     Duplicated values rather than @extend because CSS has no mixins; kept
     minimal by re-declaring only what differs from the dark defaults. */
  @media (prefers-color-scheme: light) {
    :root:not([data-theme]):not(.dark):not(.light) {
      --c-surface-base: var(--p-slate-50);
      --c-surface-sunken: var(--p-slate-200);
      --c-surface-raised: #ffffff;
      --c-surface-overlay: #ffffff;
      --c-surface-hover: var(--p-slate-100);
      --c-surface-active: var(--p-slate-200);
      --c-scrim: rgba(26, 32, 44, 0.4);

      --c-text-primary: var(--p-slate-900);
      --c-text-secondary: var(--p-slate-800);
      --c-text-muted: var(--p-slate-600);
      --c-text-inverse: #ffffff;

      --c-border-subtle: rgba(100, 116, 139, 0.16);
      --c-border-medium: rgba(100, 116, 139, 0.2);
      --c-border-strong: var(--p-slate-400);

      --c-accent: var(--p-indigo-600);
      --c-accent-hover: var(--p-indigo-700);
      --c-accent-active: var(--p-indigo-800);
      --c-accent-soft: rgba(99, 102, 241, 0.12);

      /* Live mode must stay legible on white, and the dark block's solid
         red-700/red-200 pair is not. Mirrored from the explicit light theme. */
      --c-mode-live-fg: #ffffff;
      --c-mode-live-bg: var(--p-red-600);
      --c-mode-live-border: var(--p-red-700);

      --c-elevation-1: var(--p-shadow-light-1);
      --c-elevation-2: var(--p-shadow-light-2);
      --c-elevation-3: var(--p-shadow-light-3);
      --c-elevation-4: var(--p-shadow-light-4);

      --c-pnl-positive: var(--p-green-600);
      --c-pnl-negative: var(--p-red-600);

      --c-skeleton-base: var(--p-slate-200);
      --c-skeleton-shimmer: var(--p-slate-100);
      --c-meter-track: var(--p-slate-200);

      --c-page-gradient: linear-gradient(180deg, #fafbff 0%, #f0f3fa 50%, #e8ecf4 100%);
    }
  }
}
/* ============================================================================
   LAYER 3 — COMPONENT TOKENS (--k-*)
   ----------------------------------------------------------------------------
   Per-component knobs. Always resolve to a --p-* (geometry) or --c-* (colour)
   token, never to a literal. These exist so a component can be re-skinned
   from one place instead of by overriding its CSS from another sheet — the
   pattern that produced `.admin-brand-icon { ... !important }`.

   Theme-independent: colour knobs point at semantic roles, which are what
   actually flip.
   ========================================================================== */
@layer tokens {
  :root {
    /* ---- Shell. Hardcoded in styles/layout.css today and read by BOTH
            UserLayout and AdminLayout; the only way the collapse transition
            and the sticky offsets stay in sync. ---- */
    --k-sidebar-w: 260px;
    --k-sidebar-w-collapsed: 72px;
    /* 56px: every consumer derives from this token (AppShell height, the two
       dropdown offsets, the toast offset, Breadcrumbs' sticky top), so the
       sticky offsets stay in sync by construction. No literal 64px remains. */
    --k-topbar-h: 56px;
    /* The drawer is WIDER than the docked sidebar: 280px in the old sheet's
       `@media (max-width: 768px)` block, because a drawer overlays content
       instead of competing with it for the grid. */
    --k-sidebar-w-drawer: 280px;
    --k-shell-bg: var(--c-surface-base);
    --k-shell-chrome-bg: var(--c-surface-sunken);
    --k-shell-border: var(--c-border-subtle);
    /* The content column's reading measure. Was a bare `max-width: 1400px`. */
    --k-content-max: 1400px;
    --k-content-pad: var(--p-space-5);
    --k-content-pad-md: var(--p-space-4);
    --k-content-pad-sm: var(--p-space-3);

    /* ---- Nav. Admin currently forces .admin-brand-icon with !important
            purely because these knobs did not exist. ---- */
    --k-nav-item-bg-hover: var(--c-surface-hover);
    --k-nav-item-bg-active: var(--c-accent-soft);
    --k-nav-indicator: var(--c-accent);
    --k-brand-accent: var(--c-accent);
    --k-nav-item-fg: var(--c-text-secondary);
    --k-nav-item-fg-active: var(--c-text-primary);
    --k-nav-item-pad: var(--p-space-3) var(--p-space-4);
    --k-nav-item-radius: var(--p-radius-md);
    --k-nav-item-gap: var(--p-space-1);
    --k-nav-item-text: var(--p-text-sm);
    /* Width of the active-item edge marker. The old sheet drew this as a
       ::before with `transform: scaleY()`, which animates but cannot be read by
       anything but a sighted user — the kit pairs it with aria-current. */
    --k-nav-indicator-w: 3px;

    /* ---- Controls. One source for the three parallel field systems
            (.config-field / .scf-field / .admin-input) and ~600 inputs. ---- */
    /* Compact: a config panel with 20 fields should not need scrolling. */
    --k-control-h-sm: 26px;
    --k-control-h-md: 32px;
    --k-control-h-lg: 38px;
    --k-control-pad-x: var(--p-space-3);
    --k-control-radius: var(--p-radius-sm);
    --k-control-bg: var(--c-surface-sunken);
    --k-control-border: var(--c-border-medium);
    --k-control-fg: var(--c-text-primary);
    --k-control-placeholder: var(--c-text-muted);

    /* ---- Table density ---- */
    /* Density: ~8 more rows of trades visible per screen at 32px vs 40px.
       Horizontal padding is UNCHANGED — columns still need separation. */
    --k-table-row-h: 32px;
    --k-table-cell-x: var(--p-space-3);
    --k-table-cell-y: var(--p-space-1);
    --k-table-cell-y-compact: 2px;
    --k-table-head-bg: var(--c-surface-sunken);
    --k-table-border: var(--c-border-subtle);
    --k-table-row-hover: var(--c-surface-hover);

    /* ---- Card. The single knob set behind what the audit found as
            Card / Panel / SectionCard / StatCard / MetricCard / KPI-tile
            (.section-card is defined in 5 live sheets, radius 10/12/20/24px). ---- */
    --k-card-pad: var(--p-space-4);
    --k-card-pad-compact: var(--p-space-2);
    --k-card-radius: var(--p-radius-md);
    --k-card-gap: var(--p-space-3);
    --k-card-bg: var(--c-surface-raised);
    --k-card-border: var(--c-border-subtle);
    /* BORDER-LED, not shadow-led: a card is defined by its 1px border and its
       fill. Shadow is reserved for things that genuinely float above the page
       (popover 2 / drawer 3 / modal+toast 4). Dozens of drop-shadowed cards on
       one dashboard is what made the old screens read as marketing rather than
       as an instrument panel. */
    --k-card-elevation: var(--c-elevation-0);

    /* ---- Badge/pill: GEOMETRY ONLY. Colour always comes from a --c-* role
            (phase / gate / mode / status / pnl). ---- */
    --k-badge-pad: 2px var(--p-space-2);
    /* Squared, not a pill: a status chip is data, a pill is a marketing tag.
       --p-radius-full stays available for the StatusDot and meter fills. */
    --k-badge-radius: var(--p-radius-sm);
    --k-badge-text: var(--p-text-2xs);
    --k-badge-tracking: var(--p-tracking-caption);
    --k-badge-weight: var(--p-weight-semibold);

    /* ---- Numeric: applied by Money/Delta and every numeric table cell ---- */
    --k-numeric-font: var(--p-font-mono);
    --k-numeric-feature: var(--p-numeric-tnum);

    /* ---- Modal / drawer ---- */
    --k-modal-w-sm: 420px;
    --k-modal-w-md: 640px;
    --k-modal-w-lg: 880px;
    --k-drawer-w: 480px;
    --k-modal-radius: var(--p-radius-lg);
    --k-modal-pad: var(--p-space-4);
    --k-modal-bg: var(--c-surface-overlay);
    --k-modal-elevation: var(--c-elevation-4);

    /* ---- Toast ---- */
    --k-toast-w: 360px;
    --k-toast-offset: var(--p-space-4);
    --k-toast-gap: var(--p-space-2);

    /* ---- Tooltip / popover. Deliberately SHARED with the recharts tooltip
            (see theme/chartTheme.ts) so DOM and chart tooltips cannot drift. ---- */
    --k-tooltip-bg: var(--c-surface-overlay);
    --k-tooltip-fg: var(--c-text-primary);
    --k-tooltip-border: var(--c-border-medium);
    --k-tooltip-radius: var(--p-radius-md);
    --k-tooltip-pad: var(--p-space-2) var(--p-space-3);

    /* ---- StepTrack / phase rail: real geometry reused by every status widget ---- */
    --k-step-dot: 10px;
    --k-step-dot-active: 14px;
    --k-step-connector: 2px;
    --k-step-gap: var(--p-space-3);

    /* ---- Chart geometry. Replaces the literal height={240|280|300} passed to
            ResponsiveContainer across 11 recharts files. ---- */
    --k-chart-h-sm: 200px;
    --k-chart-h-md: 280px;
    --k-chart-h-lg: 360px;
    --k-chart-pad-x: var(--p-space-3);

    /* ---- Meter ---- */
    --k-meter-h: 8px;
    --k-meter-h-sm: 4px;
    --k-meter-radius: var(--p-radius-full);

    /* ---- Tabs. GEOMETRY plus the selected/idle pair, because the three
            existing tab families disagreed on which channel even carries
            selection: .eq-tab dimmed the idle tabs with `opacity: .6` and tinted
            the active one, .scalper-tab swapped a hardcoded #6366f1 fill, and
            .monitor-domain-tab used `.is-active`. Binding both states here is
            what lets the primitive draw one tab bar everywhere. ---- */
    --k-tab-pad: var(--p-space-2) var(--p-space-4);
    --k-tab-radius: var(--p-radius-md) var(--p-radius-md) 0 0;
    --k-tab-text: var(--p-text-sm);
    --k-tab-weight: var(--p-weight-medium);
    --k-tab-gap: var(--p-space-1);
    --k-tab-bg: transparent;
    --k-tab-bg-hover: var(--c-surface-hover);
    --k-tab-bg-selected: var(--c-accent-soft);
    --k-tab-fg: var(--c-text-secondary);
    --k-tab-fg-selected: var(--c-text-primary);
    --k-tab-border: var(--c-border-subtle);
    --k-tab-border-selected: var(--c-accent);
    /* The selected tab's underline. A 2px edge rather than opacity alone: at
       `opacity: .6` vs 1 the only difference between selected and idle was
       lightness, which is exactly the distinction that disappears for a
       low-vision or greyscale reader. */
    --k-tab-indicator-h: 2px;
  }
}
/* ============================================================================
   COMPAT ALIASES — the day-0 unblocking. Temporary by design.
   ----------------------------------------------------------------------------
   Every legacy variable name is re-pointed at a semantic role, so the ~4,200
   existing rules keep working untouched while surfaces migrate one at a time.
   Delete an alias only when grep shows zero remaining uses of that name.

   Three groups, all lossless:

   1. NAMES THAT EXISTED AND WORKED  — index.css / layout.css / light-mode.css
      / analytics.css each declared their own `:root` block. Values here are
      anchored to whichever block ACTUALLY RENDERED (layout.css wins over
      index.css today, because App.tsx imports it later), so migrated and
      unmigrated components match pixel-for-pixel during the transition.

   2. NAMES THAT WERE USED BUT NEVER DEFINED — `var(--border)` is referenced 45
      times and declared nowhere, so those borders silently do not render. The
      research pages theme themselves through 8 more phantom names. Aliasing
      them makes ~7 pages themeable without touching their markup — and makes
      45 invisible borders appear. That is a deliberate visual change; see the
      PR-1 review checklist.

   3. NAMES THAT ONLY EXISTED IN ONE THEME — `--shadow-*` was declared solely
      inside `:root.light`, so every `var(--shadow-sm)` in dark mode resolved
      to nothing. Now theme-aware via --c-elevation-*.
   ========================================================================== */
@layer tokens {
  :root {
    /* ---- 1. Surfaces / text / borders (index.css + layout.css) ---- */
    --bg-primary: var(--c-surface-base);
    --bg-card: var(--c-surface-raised);

    /* These two are bound to a FIXED navy primitive rather than a --c-* role,
       because 108 call sites depend on their exact dark appearance and rebinding
       them to a semantic role would shift every one. The light values are
       supplied in the theme block below instead — see the warning there.
       (--bg-panel and --bg-dark were removed: zero uses in any css or tsx.) */
    --bg-secondary: var(--p-navy-850);
    --bg-elevated: var(--p-navy-800);

    --text-primary: var(--c-text-primary);
    --text-secondary: var(--c-text-secondary);
    --text-muted: var(--c-text-muted);

    --border-subtle: var(--c-border-subtle);
    --border-medium: var(--c-border-medium);

    --accent: var(--c-accent);
    --accent-strong: var(--c-accent-hover);
    --accent-hover: var(--c-accent-hover);
    --accent-glow: var(--c-accent-glow);

    --success: var(--c-success-fg);
    --danger: var(--c-danger-fg);
    --error: var(--c-danger-fg);
    --warning: var(--c-warning-fg);
    --info: var(--c-info-fg);
    --success-glow: var(--c-success-border);
    --error-glow: var(--c-danger-border);

    --gradient-bg: var(--c-page-gradient);
    --transition-smooth: var(--p-ease-standard);

    /* Shell geometry — layout.css names, now sharing one source with --k-* */
    --sidebar-width: var(--k-sidebar-w);
    --sidebar-collapsed-width: var(--k-sidebar-w-collapsed);
    --topbar-height: var(--k-topbar-h);

    /* ---- 2. Phantom names: used, never declared ---- */

    /* var(--border): 45 uses, zero declarations. e.g. styles/components.css:123,
       pages/StrategyPositionPage.css, pages/AboutPage.css */
    --border: var(--c-border-subtle);

    /* The 8 names the 7 research pages theme themselves with. All resolved to
       their inline fallback hex until now, which is why light mode never
       reached pages/BacktestPage.css et al. */
    --bg-surface: var(--c-surface-raised);
    --bg-surface-2: var(--c-surface-sunken);
    --bg-input: var(--k-control-bg);
    --border-light: var(--c-border-medium);
    --green: var(--c-pnl-positive);
    --red: var(--c-pnl-negative);
    --yellow: var(--c-warning-fg);

    /* ---- 3. Light-only names, now theme-aware ---- */
    --shadow-sm: var(--c-elevation-1);
    --shadow-md: var(--c-elevation-2);
    --shadow-lg: var(--c-elevation-3);
    --shadow-xl: var(--c-elevation-4);
    --shadow-2xl: var(--c-elevation-4);
    --shadow-accent: 0 8px 24px var(--c-accent-glow);
    --shadow-success: 0 8px 24px var(--c-success-border);

    /* ---- Page-local tokens that duplicated a role the theme already names ---- */
    /* styles/analytics.css:10-11 invented these for exactly --c-pnl-* */
    --stat-positive: var(--c-pnl-positive);
    --stat-negative: var(--c-pnl-negative);
    --glass-bg: var(--c-surface-raised);
    --glass-border: var(--c-border-subtle);
    --glass-shadow: var(--c-elevation-3);
    --card-hover-bg: var(--c-surface-hover);
    --chart-grid-color: var(--c-border-subtle);
  }

  /* ==========================================================================
     LIGHT VALUES FOR THE TWO FIXED-NAVY ALIASES
     --------------------------------------------------------------------------
     WARNING — do not delete this block while --bg-secondary / --bg-elevated
     still have call sites.

     These two are the only compat aliases bound to a fixed dark primitive
     instead of a --c-* role, so unlike every other name above they do NOT
     become theme-aware for free. index.css used to carry a `:root.light` block
     that gave them light values (--bg-secondary #F4F6FA, --bg-elevated #FFFFFF)
     which out-specified its own dark `:root`; PR-1 folded the dark pair into the
     block above and that light pair had nowhere to land.

     That matters well beyond the aliases themselves, because light-mode.css
     uses these two names as its LIGHT surfaces in ~50 rules — cards, table
     headers, sidebar, inputs, admin. Without a light value every one of those
     rules paints dark navy in light mode, and because `:root.light .admin-card`
     (0,3,0) out-specifies the tokenised `.admin-card` (0,1,0) inside the same
     `legacy` layer, it wins.

     Bound to the surface ROLES rather than to the old hex so they track the
     theme from here on. The values are identical to what index.css shipped:
     --p-slate-100 is #f4f6fa and --c-surface-raised is #ffffff.
     ====================================================================== */
  :root[data-theme='light'],
  :root.light {
    --bg-secondary: var(--c-surface-sunken);
    --bg-elevated: var(--c-surface-raised);
  }

  @media (prefers-color-scheme: light) {
    :root:not([data-theme]):not(.dark):not(.light) {
      --bg-secondary: var(--c-surface-sunken);
      --bg-elevated: var(--c-surface-raised);
    }
  }
}
/* ============================================================================
   LAYER `base` — element defaults.
   ----------------------------------------------------------------------------
   No class selectors. Elements only, reading tokens exclusively.
   Sits above `legacy` so these defaults win over the old global element rules,
   but below `primitives` so components can always override.
   ========================================================================== */
@layer base {
  :root {
    font-family: var(--p-font-sans);
    /* Density: 1.6 is a reading measure for prose, and this app is dashboards
       and tables. Stepped ONE rank to 1.5 rather than to --p-leading-snug (1.35)
       because line-height is inherited app-wide and 1.35 would tighten every
       legacy surface at once; components that want tighter set it locally.
       The root font-size is deliberately left at the UA default — changing it
       rescales every rem in the app. */
    line-height: var(--p-leading-normal);
    font-weight: var(--p-weight-regular);
    color: var(--c-text-secondary);
    background-color: var(--c-surface-base);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    /* Tells the UA to theme scrollbars, form controls and the canvas to match.
       Previously absent, which is why native controls stayed light in dark mode. */
    color-scheme: dark;
  }

  :root[data-theme='light'],
  :root.light {
    color-scheme: light;
  }

  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  body {
    margin: 0;
    min-height: 100vh;
    background: var(--c-page-gradient);
    color: var(--c-text-primary);
    transition: background var(--p-dur-slow) var(--p-ease-standard),
      color var(--p-dur-slow) var(--p-ease-standard);
  }

  #root {
    min-height: 100vh;
  }

  a {
    color: var(--c-accent);
    transition: color var(--p-dur-base) var(--p-ease-standard);
  }

  a:hover {
    color: var(--c-accent-hover);
  }

  /* ---- Accessibility foundation ------------------------------------------
     Both of these previously existed exactly ONCE each, inside
     admin/Admin.css (:2784 and :3190), scoped to `.admin-layout`. So the
     other ~330 buttons in the app had no visible focus ring, and all motion
     ignored the user's reduced-motion preference.

     :where() keeps specificity at zero so any component can still override.
     ---------------------------------------------------------------------- */
  :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: none;
    box-shadow: var(--c-focus-ring);
    border-radius: var(--p-radius-sm);
  }

  /* Because durations are tokens, one override silences motion app-wide. */
  @media (prefers-reduced-motion: reduce) {
    :root {
      --p-dur-fast: 0ms;
      --p-dur-base: 0ms;
      --p-dur-slow: 0ms;
    }

    *,
    *::before,
    *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }

  /* Numeric text should never reflow as prices tick.
     Extended to table cells: a P&L or LTP column re-laid out on every tick is
     the most visible instance of this, and covering td/th here reaches all ~40
     table surfaces without touching a single page sheet. Affects only the
     ADVANCE WIDTH of digit glyphs — no colour, size or spacing change — and
     :where() keeps specificity at zero so any cell can still override. */
  :where(input[type='number']) {
    font-variant-numeric: tabular-nums;
  }

  :where(td, th) {
    font-variant-numeric: tabular-nums;
  }
}
/* ============================================================================
   Cross-cutting utilities.
   ----------------------------------------------------------------------------
   Classes that no single component owns, loaded from the entry point so they are
   available to every page regardless of which components it imports.

   base.css cannot hold these: it is element-selectors-only by contract, so that
   nothing there can accidentally outrank a component. They sit in `primitives`
   instead, which is where the ui/ library lives.

   Keep this file SHORT. It is not a utility framework — anything that recurs
   with real styling belongs in a component in ui/.
   ========================================================================== */
@layer primitives {
  /**
   * Screen-reader-only text: a table <caption>, a "skip to content" link, the
   * word "Loading" beside a spinner.
   *
   * Clip-based, NOT `display: none` — display:none removes the node from the
   * accessibility tree entirely, which defeats the entire purpose of the class.
   *
   * This lived in Feedback.css, which meant a page that used the class WITHOUT
   * importing DataState or Spinner got no rule at all, and the hidden text
   * rendered visibly in the middle of the layout. Loading it from the entry
   * point removes that coupling.
   */
  .k-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  /* Same, until focused — for skip links, which must appear when tabbed to. */
  .k-sr-only-focusable:not(:focus):not(:focus-within) {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}
/* ============================================================================
   index.css — now almost empty, on purpose.
   ----------------------------------------------------------------------------
   This file used to hold all 43 custom properties (`:root, :root.dark` plus a
   `:root.light` override block) AND the base element styles. Both have moved:

     base element styles  ->  styles/base.css        (@layer base)
     the 43 variables     ->  styles/tokens/*.css    (@layer tokens)

   Why they could not stay here: this sheet is imported UNLAYERED from main.tsx,
   and unlayered CSS outranks every layered rule. A `:root { --bg-primary: ... }`
   block here would therefore beat the entire token layer, and no theme flip
   would work.

   Note the variables here were also NOT the ones that rendered — styles/layout.css
   declared the same names later in the import order and won. The token layer is
   seeded from layout.css's values so nothing moves visually. See
   styles/tokens/compat.css for the full alias list.

   What is left is the one component block that had nowhere better to live yet;
   it is in `legacy` so the future ui/ primitives can supersede it.
   ========================================================================== */
@layer legacy {

  /* ===== Access Restricted Fallback ===== */
  .access-restricted {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: var(--p-space-8) var(--p-space-6);
    min-height: 50vh;
    gap: var(--p-space-4);
  }

  .access-restricted__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 96px;
    height: 96px;
    border-radius: var(--p-radius-full);
    background: var(--c-danger-bg);
    border: var(--p-border-thin) solid var(--c-danger-border);
    color: var(--c-danger-fg);
    margin-bottom: var(--p-space-2);
  }

  .access-restricted__title {
    font-size: var(--p-text-2xl);
    font-weight: var(--p-weight-bold);
    margin: 0;
    color: var(--c-text-primary);
  }

  .access-restricted__message {
    max-width: 420px;
    margin: 0;
    color: var(--c-text-muted);
    font-size: var(--p-text-md);
    line-height: var(--p-leading-relaxed);
  }
}
/* Wrapped in @layer legacy by the token-layer migration (PR-1).
   Lowest cascade layer, so new component CSS in ui/ wins WITHOUT matching
   this sheet's specificity and without any new !important.
   Do not add rules here. Migrate them out, surface by surface. */
@layer legacy {
/* Component Styles */

/* Auth Chips */
.auth-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.35rem 0.9rem;
    border-radius: 999px;
    background: rgba(108, 99, 255, 0.1);
    border: 1px solid rgba(108, 99, 255, 0.4);
    font-size: 0.95rem;
}

.auth-chip.ghost {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.1);
}

/* Buttons */
.btn {
    border: 0;
    border-radius: 999px;
    padding: 0.65rem 1.3rem;
    font-weight: 600;
    font-size: 0.95rem;
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.2s;
    color: #fff;
}

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn:not(:disabled):hover {
    transform: translateY(-1px);
}

.btn-primary {
    background: linear-gradient(135deg, var(--accent), var(--accent-strong));
    box-shadow: 0 10px 30px rgba(108, 99, 255, 0.35);
}

.btn-secondary {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.15);
}

.btn-danger {
    background: linear-gradient(135deg, #ff4f6d, #ff8b8b);
    box-shadow: 0 10px 30px rgba(255, 79, 109, 0.35);
}

.btn-ghost {
    background: transparent;
    color: var(--accent);
    border: 1px dashed rgba(255, 255, 255, 0.2);
}

.btn-warning {
    background: linear-gradient(135deg, #BFC9D1, #a3a6a8);
    color: #fff;
}

.btn-warning:hover {
    background: linear-gradient(135deg, #BFC9D1, #a3a6a8);
}

.btn-warning:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-danger {
    background: linear-gradient(135deg, #ef4444, #dc2626);
    color: #fff;
}

.btn-danger:hover {
    background: linear-gradient(135deg, #f87171, #ef4444);
}

.btn-danger:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Notices */
.notice {
    border-radius: 14px;
    padding: 1rem 1.25rem;
    margin-bottom: 1.5rem;
    border: 1px solid rgba(255, 255, 255, 0.15);
    background: rgba(4, 8, 35, 0.9);
}

.notice.success {
    border-color: rgba(67, 230, 177, 0.6);
    color: var(--success);
}

.notice.error {
    border-color: rgba(255, 139, 139, 0.6);
    color: var(--danger);
}

.notice.info {
    border-color: rgba(108, 99, 255, 0.6);
}

.notice.idle-notice {
    margin-bottom: 1rem;
}

/* Cards & Sections */
.section-card {
    background: var(--bg-card);
    border-radius: 24px;
    padding: 1.75rem;
    margin-bottom: 1.5rem;
    border: 1px solid var(--border);
    box-shadow: 0 35px 65px rgba(1, 4, 22, 0.55);
}

.section-card.highlight {
    border: 1px solid rgba(108, 99, 255, 0.4);
    box-shadow: 0 40px 80px rgba(108, 99, 255, 0.15);
}

.section-header {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    align-items: center;
    margin-bottom: 3rem;
}

.header-controls {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.section-header h2 {
    font-size: 3rem;
    margin: 0 0 0.25rem;
}

.section-header p {
    margin: 0;
    color: var(--text-muted);
}

/* Forms */
.form-card {
    background: rgba(255, 255, 255, 0.02);
    border-radius: 18px;
    padding: 1.2rem;
    border: 1px solid rgba(255, 255, 255, 0.05);
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    width: 40%;
}

.form-card h3 {
    margin: 0;
}

.form-card.wide {
    margin-top: 0.5rem;
}

.form-row {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-end;
}

label {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    font-size: 0.9rem;
    color: var(--text-muted);
}

input,
textarea,
select {
    border-radius: 12px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    background: rgba(2, 8, 28, 0.9);
    padding: 0.75rem;
    color: var(--text-primary);
    font-size: 1rem;
    font-family: inherit;
}

textarea {
    resize: vertical;
    min-height: 120px;
}

.input-small {
    width: 60px;
}

.hint {
    color: var(--text-muted);
}

.broker-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}

/* Strategy Selection */
.strategy-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.strategy-card {
    border-radius: 18px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    padding: 1rem;
    background: rgba(6, 11, 32, 0.8);
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}

.strategy-card header {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
}

.strategy-card.active {
    border-color: var(--accent);
    box-shadow: 0 12px 30px rgba(108, 99, 255, 0.2);
}

.strategy-key {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
}

/* Status Pills */
.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.8rem;
    border-radius: 999px;
    font-size: 0.85rem;
    border: 1px solid rgba(255, 255, 255, 0.1);
}

.status-pill.success {
    color: var(--success);
    border-color: rgba(67, 230, 177, 0.4);
}

.status-pill.muted {
    color: var(--text-muted);
}

/* Tables */
.table-wrapper {
    overflow-x: auto;
}

table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.92rem;
}

th,
td {
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    padding: 0.6rem 0.4rem;
    text-align: left;
    vertical-align: top;
}

th {
    color: var(--text-muted);
    font-weight: 600;
}

td pre {
    margin: 0;
    background: rgba(255, 255, 255, 0.03);
    border-radius: 10px;
    padding: 0.5rem;
    font-size: 0.8rem;
    max-height: 120px;
    overflow: auto;
}

code {
    font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
    font-size: 0.85em;
    color: var(--accent-strong);
}

/* Generic Actions & Lists */
.actions {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.empty-placeholder {
    text-align: center;
    color: var(--text-muted);
    margin: 0;
}

.empty-placeholder.success {
    color: #4ade80;
    background: rgba(74, 222, 128, 0.08);
    border: 1px solid rgba(74, 222, 128, 0.2);
    padding: 1rem;
    border-radius: 8px;
    text-align: center;
}

.hint-list {
    margin: 0.5rem 0 0;
    padding-left: 1.2rem;
    color: var(--text-muted);
}

.hint-list li {
    margin-bottom: 0.35rem;
}

/* Side Badges */
.side-badge {
    display: inline-block;
    padding: 0.15rem 0.5rem;
    border-radius: 6px;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
}

.side-badge.buy {
    background: rgba(67, 230, 177, 0.15);
    color: var(--success);
    border: 1px solid rgba(67, 230, 177, 0.3);
}

.side-badge.sell {
    background: rgba(255, 139, 139, 0.15);
    color: var(--danger);
    border: 1px solid rgba(255, 139, 139, 0.3);
}

/* Mixed utility */
h4 {
    margin: 1rem 0 0.5rem;
    font-size: 1rem;
    color: var(--text-muted);
    font-weight: 600;
}

.danger-zone {
    border: 1px solid rgba(239, 68, 68, 0.25);
    background: rgba(239, 68, 68, 0.06);
}

.danger-controls {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 0.75rem;
}

.checkbox-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--text-muted);
    font-size: 0.9rem;
}

.danger-note {
    margin-top: 0.75rem;
}

/* ===== Mobile Responsive Styles ===== */

/* Tablet and smaller */
@media (max-width: 768px) {
    .btn {
        padding: 0.6rem 1.1rem;
        font-size: 0.9rem;
    }

    .section-card {
        padding: 1.25rem;
        border-radius: 18px;
        margin-bottom: 1.25rem;
    }

    .section-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.75rem;
    }

    .header-controls {
        width: 100%;
        flex-wrap: wrap;
    }

    .form-row {
        flex-direction: column;
        gap: 0.75rem;
    }

    .form-row label {
        width: 100%;
    }

    .strategy-grid {
        grid-template-columns: 1fr;
    }

    .notice {
        padding: 0.875rem 1rem;
        border-radius: 12px;
    }

    .broker-actions {
        flex-direction: column;
    }

    .broker-actions .btn {
        width: 100%;
        text-align: center;
    }
}

/* Mobile phones */
@media (max-width: 480px) {
    .btn {
        padding: 0.55rem 1rem;
        font-size: 0.85rem;
    }

    .section-card {
        padding: 1rem;
        border-radius: 14px;
        margin-bottom: 1rem;
    }

    .section-header h2 {
        font-size: 1.25rem;
    }

    .form-card {
        padding: 1rem;
        border-radius: 14px;
        width: 100%;
    }

    .strategy-card {
        padding: 0.875rem;
        border-radius: 14px;
    }

    .auth-chip {
        padding: 0.3rem 0.75rem;
        font-size: 0.85rem;
    }

    .status-pill {
        padding: 0.2rem 0.65rem;
        font-size: 0.8rem;
    }

    .notice {
        padding: 0.75rem;
        font-size: 0.9rem;
    }

    input,
    textarea,
    select {
        padding: 0.65rem;
        font-size: 0.95rem;
        border-radius: 10px;
    }

    .table-wrapper {
        margin: 0 -0.5rem;
        padding: 0 0.5rem;
    }

    table {
        font-size: 0.85rem;
    }

    th,
    td {
        padding: 0.5rem 0.3rem;
    }

    .danger-controls {
        gap: 0.5rem;
    }
}

/* Splash Screen Styles */
.splash-screen {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: var(--gradient-bg);
    /* Use theme background */
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 9999;
    /* Ensure it stays on top */
    transition: opacity 0.5s ease-in-out;
}

.splash-screen.visible {
    opacity: 1;
}

.splash-screen.hidden {
    opacity: 0;
    pointer-events: none;
    /* Allow clicks to pass through after fade out */
}

.splash-logo-container {
    width: 200px;
    /* Adjust as needed */
    height: auto;
}

.splash-logo {
    width: 100%;
    height: auto;
    animation: pulse 2s infinite ease-in-out;
}

@keyframes pulse {
    0% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.05);
    }

    100% {
        transform: scale(1);
    }
}
}
/* Wrapped in @layer legacy by the token-layer migration (PR-1).
   Lowest cascade layer, so new component CSS in ui/ wins WITHOUT matching
   this sheet's specificity and without any new !important.
   Do not add rules here. Migrate them out, surface by surface. */
@layer legacy {

.pnl-value {
    font-size: 1.35rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.pnl-value.profit {
    color: var(--success);
}

.pnl-value.loss {
    color: var(--danger);
}

.pnl-value.neutral {
    color: var(--text-primary);
}

.profit {
    color: var(--success);
}

.loss {
    color: var(--danger);
}

/* `.flat-position` removed (PR-5): it dimmed a closed position in the positions
   table and is now `.pos-row--flat` in PositionsPage.css, which uses
   `--p-opacity-muted` instead of a magic 0.5. No other page emitted it. */

.instrument-cell {
    font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
    font-size: 0.85rem;
}

/* `.side-badge.flat` and `.timestamp` removed (PR-5). Both were emitted only by
   PositionsPage: the flat side is now `<Badge tone="neutral">` (Badge has no
   side-neutral tone, and 'neutral' is the honest choice — a FLAT position is
   neither a buy nor a sell), and the "Updated" cell is `<Text variant="caption">`
   plus `.pos-table__ts` for the nowrap.

   Only the `.flat` VARIANT went: plain `.side-badge` is still emitted by
   RejectionsTable, StrategyPositionPage and TradeReportsPage, and its base rules
   live in components.css. `side-badge ${side.toLowerCase()}` at those three sites
   can only produce `buy` / `sell`. */

/* `.trades-scroll-container`, `.trades-actions`, `.btn.load-more` and
   `.btn.collapse-btn` removed with the TradesPage conversion (PR-2) — nothing
   referenced them afterwards. The scroll region is now `.trades-scroll` in
   TradesPage.css, tokenised (the old one hardcoded an indigo scrollbar thumb and
   an rgba(11,23,56,.95) sticky header that stayed navy in light mode), and the
   footer row belongs to SectionCard's `footer` slot. */

.trades-info {
    font-size: 0.8rem;
    color: var(--text-muted);
    font-style: italic;
}

@media (max-width: 720px) {
    .section-card {
        padding: 1.3rem;
    }

    /* The `.pnl-summary` / `.pnl-card.total` narrow-viewport overrides went with
       the PR-5 conversion. StatGrid does its own responsive column count via
       `repeat(auto-fit, minmax(var(--k-grid-min), 1fr))`, so there is no
       breakpoint to maintain here. */
}

/* Strategy Status Display */
.strategy-status-card {
    font-size: 0.8rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 0.5rem;
    background: rgba(11, 23, 56, 0.5);
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.06);
}

.strategy-status-card .status-section {
    padding: 0.5rem;
    background: rgba(0, 0, 0, 0.2);
    border-radius: 6px;
}

.strategy-status-card .status-section h4 {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--accent);
    margin: 0 0 0.5rem 0;
    padding-bottom: 0.35rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.strategy-status-card .status-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.2rem 0;
}

.strategy-status-card .status-row .label {
    color: var(--text-muted);
    font-weight: 500;
}

.strategy-status-card .status-row .value {
    font-weight: 600;
    /* Was `--text-default` (named without var() here so the token guard does not
       flag this comment) — a name nothing ever defined. With no fallback the
       whole declaration was dropped, so every strategy-status value inherited its
       row's muted label colour: the reading and the label it belongs to were the
       same weight of grey, on the live status cards. */
    color: var(--c-text-primary);
}

.strategy-status-card .status-row .value.success {
    color: var(--success);
}

.strategy-status-card .status-row .value.warning {
    color: #BFC9D1;
}

.strategy-status-card .status-row .value.danger {
    color: var(--danger);
}

.strategy-status-card .status-row .value.muted {
    color: var(--text-muted);
}

.option-badge {
    font-size: 0.75rem;
    color: #BFC9D1;
    font-weight: 500;
}

/* ==================== Trade Reports Page ====================

   This block was written FOR TradeReportsPage, in a sheet App.tsx imports
   globally — which is how one screen came to own `.positive`, `.negative`,
   `.stat-value` and `.text-muted { … !important }` for the whole app. There was
   also a `pages/TradeReportsPage.css` holding a near-verbatim copy of all of it,
   imported by nobody; that file is now the page's real (scoped) sheet.

   Removed with the PR-5 conversion, each having no emitter left:

     .filter-row (+ label / input / select / :focus)  -> Field + Input/Select.
        CounterBreakout's `cb-filter-row` was a DIFFERENT class token and never
        matched this selector; the whole `cb-` family went with PR-7 anyway.
     .filter-inline (+ label)     -> SectionCard's `toolbar` slot.
     .stat-value.positive/.negative -> Money. StrategyPositionPage's
        `stat-value profit` / `stat-value loss` are different tokens and keep
        their own rules further up this file.
     .stat-mini (+ strong), .stats-row  -> `.tr-meta-row` + Text variant="caption".
     .table-scroll (+ table)      -> `.tr-table-wrap`. DataTable's is
        `.k-table-scroll`, a different name.
     .row-warning (+ td)          -> `.tr-row--flagged`, on warning tokens. The
        old rgba(191,201,209,.08) was a pale grey that read as disabled.
     .text-warning                -> Text tone="warning" / SectionCard accent.
     .text-mono                   -> `.tr-table__instrument`. AdminUserDetailPage
        emits `admin-text-mono`, defined in admin.css.
     .btn-group                   -> Button inside a toolbar Row.

   KEPT, with the emitters that hold them here:
     .stats-grid / .stat-card / .stat-label / .stat-value — FailureSummaryCards,
       StrategyPositionPage, SubscriptionsPage (PR-7 / PR-8 territory).
     .text-muted — 15 files. .text-success — 4, including three admin pages that
       do not import analytics.css, so its copy there cannot cover them.
     .positive / .negative (bare) — AnalyticsSummaryCards' `metric-card positive`
       and DeltaRangeBreakout's `value positive` both still land on these.
       They go with PortfolioAnalyticsPage (PR-5) and the status kit (PR-7);
       deleting them now would silently restyle two live surfaces.          */

.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 1rem;
    margin-top: 1rem;
}

.stat-card {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 10px;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.stat-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
}

.stat-value {
    font-size: 1.25rem;
    font-weight: 700;
    color: #fff;
}

/* `.stat-value.positive` / `.stat-value.negative` deleted with the rest of the
   TradeReports block. StrategyPositionPage's `stat-value profit` /
   `stat-value loss` are different tokens and keep their own rules above. */

.text-muted {
    color: var(--text-muted) !important;
}

.text-success {
    color: #4ade80 !important;
}

/* ============================================================================
   Subscriptions Dashboard
   ============================================================================ */

.subscriptions-dashboard {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.dashboard-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: 1rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.dashboard-title h2 {
    margin: 0 0 0.25rem;
    font-size: 1.75rem;
    font-weight: 700;
    color: #fff;
}

.dashboard-subtitle {
    margin: 0;
    color: var(--text-muted);
    font-size: 0.95rem;
}

.dashboard-controls {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.auto-refresh-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: var(--text-muted);
    cursor: pointer;
}

.auto-refresh-toggle input {
    width: 16px;
    height: 16px;
    accent-color: var(--accent);
}

/* Dashboard Stats */
.dashboard-stats {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1rem;
}

@media (max-width: 600px) {
    .dashboard-stats {
        grid-template-columns: repeat(2, 1fr);
    }
}

.stat-box {
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 16px;
    padding: 1.25rem;
    text-align: center;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.stat-box:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}

.stat-box.active {
    background: linear-gradient(135deg, rgba(74, 222, 128, 0.1), rgba(74, 222, 128, 0.05));
    border-color: rgba(74, 222, 128, 0.25);
}

.stat-box.paper {
    background: linear-gradient(135deg, rgba(191, 201, 209, 0.1), rgba(191, 201, 209, 0.05));
    border-color: rgba(191, 201, 209, 0.25);
}

.stat-box.live {
    background: linear-gradient(135deg, rgba(248, 113, 113, 0.1), rgba(248, 113, 113, 0.05));
    border-color: rgba(248, 113, 113, 0.25);
}

.stat-number {
    display: block;
    font-size: 2rem;
    font-weight: 800;
    color: #fff;
    line-height: 1;
    margin-bottom: 0.35rem;
}

.stat-box.active .stat-number {
    color: #4ade80;
}

.stat-box.paper .stat-number {
    color: #BFC9D1;
}

.stat-box.live .stat-number {
    color: #f87171;
}

.stat-box .stat-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text-muted);
}

/* Empty State */
.empty-state-card {
    background: rgba(255, 255, 255, 0.02);
    border: 1px dashed rgba(255, 255, 255, 0.1);
    border-radius: 24px;
    padding: 4rem 2rem;
    text-align: center;
}

.empty-icon {
    font-size: 4rem;
    margin-bottom: 1rem;
    opacity: 0.7;
}

.empty-state-card h3 {
    margin: 0 0 0.5rem;
    font-size: 1.5rem;
    color: #fff;
}

.empty-state-card p {
    margin: 0;
    color: var(--text-muted);
    max-width: 400px;
    margin: 0 auto;
}

/* Subscription Grid */
.subscription-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
    gap: 1.25rem;
}

@media (max-width: 440px) {
    .subscription-grid {
        grid-template-columns: 1fr;
    }
}

/* Subscription Card */
.subscription-card {
    background: linear-gradient(145deg, rgba(15, 23, 56, 0.9), rgba(8, 14, 36, 0.95));
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 20px;
    padding: 1.25rem;
    transition: all 0.25s ease;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.subscription-card:hover {
    border-color: rgba(255, 255, 255, 0.12);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
}

.subscription-card.is-active {
    border-color: rgba(74, 222, 128, 0.3);
    box-shadow: 0 0 0 1px rgba(74, 222, 128, 0.1), 0 12px 40px rgba(74, 222, 128, 0.1);
}

.subscription-card.is-expanded {
    grid-column: span 1;
}

@media (min-width: 900px) {
    .subscription-card.is-expanded {
        grid-column: span 2;
    }
}

/* Card Header */
.card-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    flex-wrap: wrap;
}

.card-title-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.strategy-icon {
    font-size: 1.75rem;
    width: 48px;
    height: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.05);
    border-radius: 12px;
}

.card-title-info h3 {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 600;
    color: #fff;
}

.subscription-id {
    font-size: 0.75rem;
    color: var(--text-muted);
    font-family: 'SF Mono', 'Fira Code', monospace;
}

.card-badges {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

.mode-badge {
    padding: 0.3rem 0.75rem;
    border-radius: 6px;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.mode-badge.paper {
    background: rgba(191, 201, 209, 0.15);
    color: #BFC9D1;
    border: 1px solid rgba(191, 201, 209, 0.3);
}

.mode-badge.live {
    background: rgba(248, 113, 113, 0.15);
    color: #f87171;
    border: 1px solid rgba(248, 113, 113, 0.3);
}

.status-badge {
    padding: 0.3rem 0.75rem;
    border-radius: 6px;
    font-size: 0.7rem;
    font-weight: 600;
}

.status-badge.active {
    background: rgba(74, 222, 128, 0.15);
    color: #4ade80;
    border: 1px solid rgba(74, 222, 128, 0.3);
}

.status-badge.idle {
    background: rgba(100, 116, 139, 0.15);
    color: #94a3b8;
    border: 1px solid rgba(100, 116, 139, 0.2);
}

/* Card Config */
.card-config {
    background: rgba(0, 0, 0, 0.2);
    border-radius: 12px;
    padding: 0.85rem;
}

.config-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
    gap: 0.75rem;
}

.config-item {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.config-label {
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
}

.config-value {
    font-size: 0.85rem;
    font-weight: 600;
    color: #e2e8f0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Card Actions */
.card-actions {
    display: flex;
    gap: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid rgba(255, 255, 255, 0.04);
}

.action-btn {
    flex: 1;
    padding: 0.65rem 0.75rem;
    border: none;
    border-radius: 10px;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
}

.action-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.action-btn.primary {
    background: linear-gradient(135deg, #4ade80, #22c55e);
    color: #fff;
    box-shadow: 0 4px 12px rgba(74, 222, 128, 0.3);
}

.action-btn.primary:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 6px 16px rgba(74, 222, 128, 0.4);
}

.action-btn.warning {
    background: linear-gradient(135deg, #BFC9D1, #a3a6a8);
    color: #000;
    box-shadow: 0 4px 12px rgba(191, 201, 209, 0.3);
}

.action-btn.warning:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 6px 16px rgba(191, 201, 209, 0.4);
}

.action-btn.secondary {
    background: rgba(255, 255, 255, 0.08);
    color: #e2e8f0;
    border: 1px solid rgba(255, 255, 255, 0.1);
}

.action-btn.secondary:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.12);
}

.action-btn.danger {
    background: rgba(248, 113, 113, 0.15);
    color: #f87171;
    border: 1px solid rgba(248, 113, 113, 0.25);
    flex: 0 0 auto;
    min-width: 42px;
}

.action-btn.danger:hover:not(:disabled) {
    background: rgba(248, 113, 113, 0.25);
}

/* Status Panel */
.card-status-panel {
    margin-top: 0.5rem;
    padding-top: 1rem;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
    animation: slideDown 0.25s ease;
}

@keyframes slideDown {
    from {
        opacity: 0;
        transform: translateY(-10px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* BYO Credentials Page */
.broker-provider-selector {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
    gap: 0.5rem;
    width: 100%;
    min-width: 0;
    max-width: 100%;
    box-sizing: border-box;
    margin-bottom: 1.5rem;
}

.broker-provider-selector > * {
    min-width: 0;
    white-space: nowrap;
}

.credentials-setup {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.info-box {
    border-radius: 16px;
    padding: 1.25rem;
    border: 1px solid rgba(255, 255, 255, 0.1);
}

.info-box.info {
    background: rgba(108, 99, 255, 0.08);
    border-color: rgba(108, 99, 255, 0.3);
}

.info-box.warning {
    background: rgba(191, 201, 209, 0.08);
    border-color: rgba(191, 201, 209, 0.3);
    color: #BFC9D1;
}

.info-box h3,
.info-box h4 {
    margin: 0 0 0.75rem;
}

.info-box ul,
.info-box ol {
    margin: 0.5rem 0;
    padding-left: 1.5rem;
}

.info-box li {
    margin-bottom: 0.5rem;
}

.setup-steps {
    list-style-position: inside;
    color: var(--c-text-secondary);
}

.setup-steps a {
    color: var(--accent);
    text-decoration: underline;
}

.credentials-form {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.form-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.form-group label {
    font-size: 0.95rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.85);
}

.required {
    color: var(--danger);
}

.password-field {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

.password-field input {
    flex: 1;
}

.btn-icon {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 8px;
    padding: 0.75rem;
    cursor: pointer;
    font-size: 1.25rem;
    transition: background 0.2s;
}

.btn-icon:hover {
    background: rgba(255, 255, 255, 0.1);
}

.form-actions {
    display: flex;
    gap: 0.75rem;
    margin-top: 0.5rem;
}

.btn-outline {
    background: transparent;
    border: 1px solid rgba(108, 99, 255, 0.4);
    color: var(--accent);
    border-radius: 12px;
    padding: 0.65rem 1.3rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s;
    text-decoration: none;
    display: inline-block;
}

.btn-outline:hover {
    background: rgba(108, 99, 255, 0.1);
    border-color: var(--accent);
    transform: translateY(-1px);
}

}
/* Wrapped in @layer legacy by the token-layer migration (PR-1).
   Lowest cascade layer, so new component CSS in ui/ wins WITHOUT matching
   this sheet's specificity and without any new !important.
   Do not add rules here. Migrate them out, surface by surface. */
@layer legacy {
/* Theme Variables and Base Styles */

/* Apply theme-aware backgrounds and transitions */
body {
    background: var(--gradient-bg);
    color: var(--text-primary);
    transition: background 0.3s ease, color 0.3s ease;
}

/* Common component styles using CSS variables */
.shell {
    min-height: 100vh;
    width: min(1240px, 100%);
    margin: 0 auto;
    padding: 2.5rem 1.5rem 4rem;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.section-card {
    background: var(--bg-card);
    border-radius: 24px;
    padding: 1.75rem;
    margin-bottom: 1.5rem;
    border: 1px solid var(--border-subtle);
    box-shadow: 0 35px 65px rgba(0, 0, 0, 0.15);
    transition: background 0.3s ease, border-color 0.3s ease;
}

:root.light .section-card {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

.btn {
    border: 0;
    border-radius: 999px;
    padding: 0.65rem 1.3rem;
    font-weight: 600;
    font-size: 0.95rem;
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.2s;
    color: #fff;
}

.btn-primary {
    background: linear-gradient(135deg, var(--accent), var(--accent-strong));
    box-shadow: 0 10px 30px var(--accent-glow);
}

.btn-secondary {
    background: var(--bg-elevated);
    border: 1px solid var(--border-medium);
    color: var(--text-primary);
}

.btn-danger {
    background: linear-gradient(135deg, var(--danger), #dc2626);
    box-shadow: 0 10px 30px rgba(239, 68, 68, 0.3);
}

input,
textarea,
select {
    border-radius: 12px;
    border: 1px solid var(--border-medium);
    background: var(--bg-secondary);
    padding: 0.75rem;
    color: var(--text-primary);
    font-size: 1rem;
    font-family: inherit;
    transition: border-color 0.2s ease, background 0.2s ease;
}

input:focus,
textarea:focus,
select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-glow);
}

table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.92rem;
}

th,
td {
    border-bottom: 1px solid var(--border-subtle);
    padding: 0.6rem 0.4rem;
    text-align: left;
    vertical-align: top;
}

th {
    color: var(--text-muted);
    font-weight: 600;
}

.notice {
    border-radius: 14px;
    padding: 1rem 1.25rem;
    margin-bottom: 1.5rem;
    border: 1px solid var(--border-medium);
    background: var(--bg-secondary);
}

.notice.success {
    border-color: var(--success);
    color: var(--success);
    background: color-mix(in srgb, var(--success) 10%, var(--bg-secondary));
}

.notice.error {
    border-color: var(--danger);
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--bg-secondary));
}

.notice.info {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-secondary));
}
}
/* Wrapped in @layer legacy by the token-layer migration (PR-1).
   Lowest cascade layer, so new component CSS in ui/ wins WITHOUT matching
   this sheet's specificity and without any new !important.
   Do not add rules here. Migrate them out, surface by surface. */
@layer legacy {
/* ===== Enhanced Light Mode Styling ===== */
/* Inspired by modern fintech design principles and Color Hunt palettes */

:root.light {
    /* Premium shadow system for depth and elegance */
    --shadow-sm: 0 1px 3px rgba(100, 116, 139, 0.08), 0 1px 2px rgba(100, 116, 139, 0.06);
    --shadow-md: 0 4px 6px rgba(100, 116, 139, 0.07), 0 2px 4px rgba(100, 116, 139, 0.06);
    --shadow-lg: 0 10px 15px rgba(100, 116, 139, 0.1), 0 4px 6px rgba(100, 116, 139, 0.05);
    --shadow-xl: 0 20px 25px rgba(100, 116, 139, 0.1), 0 10px 10px rgba(100, 116, 139, 0.04);
    --shadow-2xl: 0 25px 50px rgba(100, 116, 139, 0.15);

    /* Accent shadows for interactive elements */
    --shadow-accent: 0 8px 20px rgba(99, 102, 241, 0.18);
    --shadow-success: 0 8px 20px rgba(5, 150, 105, 0.18);
}

/* ===== Cards & Panels =====
   `.dash-card` dropped from both groups (PR-5): the dashboard renders SectionCard
   now, which themes from --c-* and needs no light override. The other three keep
   theirs — .section-card has 10 live callers, .chart-card 2, .feature-card 1. */
:root.light .section-card,
:root.light .feature-card,
:root.light .chart-card {
    box-shadow: var(--shadow-md);
    border-color: var(--border-subtle);
    background: var(--bg-elevated);
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

:root.light .chart-card:hover,
:root.light .feature-card:hover {
    box-shadow: var(--shadow-lg);
    transform: translateY(-2px);
    border-color: var(--border-medium);
}

/* Premium accent highlights */
:root.light .section-card.highlight {
    border-color: color-mix(in srgb, var(--accent) 30%, transparent);
    box-shadow: var(--shadow-accent);
}

/* ===== Backgrounds with Subtle Depth =====
   Both rules here were ENTIRELY dashboard selectors (.broker-status, .pnl-box,
   .sub-item, .catalog-item, .dash-empty and two :hover twins), so both are gone
   with the markup. All five are now Surface level="sunken", whose fill and border
   come from --c-surface-sunken / --c-border-subtle in both themes. */

/* ===== Tables =====
   `.strategy-pnl-table th` dropped from this group in PR-10 — no emitter. The two
   remaining selectors are live (`.admin-table` across nine admin pages, and the
   bare `table thead th` element rule). */
:root.light .admin-table thead th,
:root.light table thead th {
    background: var(--bg-secondary);
    border-bottom: 2px solid var(--border-medium);
}

:root.light .admin-table tbody tr:hover,
:root.light table tbody tr:hover {
    background: color-mix(in srgb, var(--accent) 3%, transparent);
}

/* ===== Hero & Feature Elements ===== */
:root.light .hero-chart-card,
:root.light .feature-card,
:root.light .audience-item,
:root.light .value-item {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

:root.light .hero-chart-card {
    box-shadow: var(--shadow-2xl);
    border-color: var(--border-medium);
}

/* ===== Icons & Decorative Elements =====
   `.dash-card__icon` dropped (PR-5) — SectionCard's icon tile tints itself with
   --c-accent-soft, which already has a light value. `.feature-card__icon` stays. */
:root.light .feature-card__icon {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* ===== Subscription Cards ===== */
:root.light .subscription-card {
    background: var(--bg-card);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-md);
}

:root.light .subscription-card:hover {
    box-shadow: var(--shadow-lg);
    border-color: color-mix(in srgb, var(--accent) 35%, var(--border-medium));
}

:root.light .subscription-card.is-active {
    border-color: color-mix(in srgb, var(--success) 40%, transparent);
    box-shadow: var(--shadow-success);
}

/* ===== Admin Panel ===== */
:root.light .admin-card,
:root.light .admin-metric {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

/* `.admin-shell` / `.admin-shell__main` removed (PR-10) — no emitter. These
   overrode the SECOND-generation admin chrome, whose base rules have just been
   deleted from `styles/admin.css` in the same change. `.admin-shell__loading` and
   `__blocked` (App.tsx's AdminRoute gates) are different classes, owned by
   `admin/Admin.css`, and are untouched. */

/* ===== Status Badges & Pills ===== */
:root.light .mode-badge.paper {
    background: color-mix(in srgb, var(--warning) 12%, transparent);
    border-color: color-mix(in srgb, var(--warning) 28%, transparent);
    color: #B45309;
}

:root.light .mode-badge.live {
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    border-color: color-mix(in srgb, var(--danger) 28%, transparent);
    color: #B91C1C;
}

:root.light .status-badge.active,
:root.light .status-pill.success {
    background: color-mix(in srgb, var(--success) 12%, transparent);
    border-color: color-mix(in srgb, var(--success) 28%, transparent);
    color: #047857;
}

/* ===== PNL Boxes =====
   `.pnl-box.main` removed (PR-5). The dashboard's headline tile is
   <StatTile accent="accent">, whose emphasis border comes from Surface in both
   themes. Nothing else emitted `.pnl-box`. */

/* ===== Empty States =====
   `.dash-empty` dropped from this group — Surface level="sunken" themes itself.
   `.empty-state-card` has 2 live callers and keeps its override. */
:root.light .empty-state-card {
    background: var(--bg-secondary);
    border-color: var(--border-medium);
    border-style: dashed;
}

/* ===== About Page Stats =====
   `.about-section--stats` removed (PR-10) — no emitter, and this was the FIRST of
   two declarations of the same dead selector in this one sheet. See the note at
   the second site, under "About Page Overrides". */

/* ===== CTA Section ===== */
:root.light .about-cta {
    background: linear-gradient(135deg,
            color-mix(in srgb, var(--accent) 6%, var(--bg-elevated)),
            color-mix(in srgb, var(--accent) 2%, var(--bg-elevated)));
    border-color: color-mix(in srgb, var(--accent) 18%, transparent);
    box-shadow: var(--shadow-accent);
}

/* ===== Modals & Overlays ===== */
:root.light .settings-modal {
    box-shadow: var(--shadow-2xl);
    border-color: var(--border-medium);
}

:root.light .settings-backdrop {
    background: rgba(26, 32, 44, 0.35);
}

/* ===== Navigation — REMOVED (PR-10) =====
   `.shell__nav`, `.admin-shell__nav`, and the two `__nav-link:hover` twins. None
   has an emitter: the `.admin-shell*` chrome is deleted (see above), and
   `.shell__nav` never existed as a class — the shell kit emits `k-shell__nav`,
   which is a DIFFERENT token. A CSS class selector matches whole tokens, so
   `.shell__nav` never applied to `k-shell__nav` and this rule has been inert
   since the kit landed. `SideNav.css` owns nav theming now, on `--c-*`. */

/* ===== Forms ===== */
:root.light input,
:root.light textarea,
:root.light select {
    background: var(--bg-primary);
    border-color: var(--border-subtle);
}

:root.light input:focus,
:root.light textarea:focus,
:root.light select:focus {
    background: var(--bg-elevated);
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

/* ===== Trade Details — REMOVED (PR-10) =====
   `.trade-details-card` and `.details-grid section`, neither with an emitter, and
   neither with a base rule left in any sheet — so these overrode nothing in
   either theme. The trade-detail surfaces render `SectionCard` + `KeyValueList`
   as of PR-7. */

/* ===== Floating Messages ===== */
:root.light .hero-msg-bubble {
    background: var(--bg-elevated);
    border-color: color-mix(in srgb, var(--success) 30%, transparent);
    box-shadow: var(--shadow-lg);
}

/* ===== Text Readability ===== */
:root.light .pnl-value,
:root.light .stat-number,
:root.light .trading-stat__value,
:root.light h1,
:root.light h2,
:root.light h3,
:root.light h4 {
    color: var(--text-primary);
}

/* ===== Button Refinements ===== */
:root.light .btn-secondary {
    background: var(--bg-elevated);
    border-color: var(--border-medium);
    box-shadow: var(--shadow-sm);
}

:root.light .btn-secondary:hover {
    background: color-mix(in srgb, var(--text-primary) 4%, var(--bg-elevated));
    box-shadow: var(--shadow-md);
}

:root.light .btn-outline {
    border-color: var(--border-medium);
}

:root.light .btn-outline:hover {
    background: color-mix(in srgb, var(--text-primary) 3%, transparent);
    border-color: var(--text-secondary);
}

/* ===== Accent Button Glow ===== */
:root.light .btn-primary:hover {
    box-shadow: var(--shadow-accent);
}

/* ===== Shell light-mode overrides — REMOVED (PR-10) =====

   The sidebar/topbar/breadcrumb/search half of this sheet is gone, together with
   the same families in `styles/layout.css` and the `.admin-layout .sidebar`
   override block in `admin/Admin.css`. All three sheets styled the SAME global
   class names, which is why they had to move in one change: `.sidebar`,
   `.nav-item`, `.topbar`, `.breadcrumb-*`, `.user-info`, `.sidebar__*`,
   `.search-*`, `.menu-toggle`, `.btn-logout`. Every one of them now has zero
   emitters — both shells render `AppShell` / `SideNav` / `Breadcrumbs` /
   `ShellSearch` / `ShellIdentity`, whose sheets are theme-agnostic because they
   consume `--c-*` semantic tokens that already flip with the theme. A light
   override sheet is only needed by rules that hardcode a dark value.

   Three of these rules did more than re-colour, and are worth recording:

     `.brand-text h2` re-applied gradient-clipped text with
       `-webkit-text-fill-color: transparent`. The DARK sheet's version had a
       hardcoded `#fff` stop, so this block existed to stop the product name
       being white-on-white in light mode — a fix layered on a technique that
       should not have been used for a product name at all.

     `.nav-item:hover { transform: translateX(4px) }` — a light-mode-only
       geometry change (dark mode moved 2px), so the nav physically shifted by a
       different amount per theme, with no `prefers-reduced-motion` guard.

     `.topbar` set `background: rgba(255,255,255,0.9)` + `backdrop-filter:
       blur(16px)`. The dark sheet's topbar was `rgba(18,22,31,0.95)` written
       AFTER a token, so the token never applied. Both are now one opaque
       `--k-shell-chrome-bg`, and the blur is gone with them: it forced a
       compositing layer for a bar that reads as opaque either way.

   `.icon-button` and `.user-email` went in the same change, one step later.
   They were the last two selectors in this block with live emitters, held by
   `NotificationsMenu` and `UserMenu` — the two components that had to move off
   `styles/layout.css` before it could be deleted. Both now own colocated sheets
   (`components/notifications/NotificationsMenu.css`,
   `components/layout/UserMenu.css`) built on `--c-*` tokens, so neither needs a
   light override.

   Two selectors here were already dead before the shell moved:
     `.user-details .user-status` — UserMenu emitted `user-status` inside
       `.user-details-dropdown`, so the descendant combinator never matched.
     `.btn-logout` — UserMenu's button was `.btn-logout-dropdown`.
   ============================================================== */

/* ===== Subscription Page Overrides ===== */

:root.light .dashboard-header {
    background: var(--bg-elevated);
    border-color: var(--border-medium);
    box-shadow: var(--shadow-sm);
}

:root.light .stat-box {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

:root.light .stat-box:hover {
    box-shadow: var(--shadow-md);
    border-color: var(--border-medium);
}

:root.light .stat-box.active {
    background: color-mix(in srgb, var(--success) 8%, transparent);
    border-color: color-mix(in srgb, var(--success) 20%, transparent);
}

:root.light .stat-box.active .stat-number {
    color: var(--success);
}

:root.light .stat-box.paper {
    background: color-mix(in srgb, var(--accent) 8%, transparent);
    border-color: color-mix(in srgb, var(--accent) 20%, transparent);
}

:root.light .stat-box.paper .stat-number {
    color: var(--accent);
}

:root.light .stat-box.live {
    background: color-mix(in srgb, var(--danger) 8%, transparent);
    border-color: color-mix(in srgb, var(--danger) 20%, transparent);
}

:root.light .stat-box.live .stat-number {
    color: var(--danger);
}

:root.light .stat-label {
    color: var(--text-secondary);
    font-weight: 600;
}

:root.light .subscription-card {
    background: var(--bg-elevated);
    /* Ensure card background is white */
    border-color: var(--border-subtle);
}

:root.light .strategy-icon {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--accent);
}

:root.light .card-config,
:root.light .config-grid {
    background: var(--bg-secondary);
    /* Light gray background for details */
    color: var(--text-secondary);
}

:root.light .config-grid {
    border: 1px solid var(--border-subtle);
}

:root.light .config-label {
    color: var(--text-muted);
}

:root.light .config-value {
    color: var(--text-primary);
}

:root.light .action-btn.secondary {
    background: white;
    border: 1px solid var(--border-medium);
    color: var(--text-secondary);
    box-shadow: var(--shadow-sm);
}

:root.light .action-btn.secondary:hover {
    background: var(--bg-secondary);
    border-color: var(--text-muted);
    color: var(--text-primary);
}

:root.light .action-btn.quick-status {
    background: white;
    border-color: var(--border-medium);
    color: var(--text-secondary);
}

:root.light .action-btn.quick-status:hover {
    background: var(--bg-secondary);
    color: var(--text-primary);
}

:root.light .strategy-status-card {
    background: var(--bg-secondary);
    border-color: var(--border-medium);
}

:root.light .status-section {
    border-bottom-color: var(--border-medium);
}

/* ===== Positions Page Overrides ===== */

/* Section Cards
   `.positions-section` dropped from this group and its gradient-override twin
   deleted with the PositionsPage conversion (PR-5): the page is a SectionCard
   built on Surface, which reads `--c-surface-*` and is theme-aware without an
   override. `.orb-section` and `.orb-instrument-card` dropped in PR-10 with the
   rest of the ORB family below. `.section-card` (ten callers) and `.page-header`
   are still live, so this stays a group. */
:root.light .section-card,
:root.light .page-header {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

/* `.pnl-label` and `.pnl-value.neutral` removed (PR-10).
   A comment here previously asserted both were still emitted by
   StrategyPositionPage "and five strategy status widgets". They are not:
   `.pnl-label` has no emitter and no base rule left in any sheet, and
   `pnl-value` is only ever composed as `pnl-value profit` / `pnl-value loss`
   (StrategyPositionPage:397, 411, 730) — never `neutral`. The claim was written
   when `.pnl-card` went in PR-5 and was not re-checked; a class that appears in
   a sheet is not evidence that a component emits it. */

/* Tables — `.trades-scroll-container{, thead th}` removed (PR-10), no emitter. */
:root.light .trades-table th {
    color: var(--text-muted);
}

:root.light .trades-table tbody tr:hover {
    background: color-mix(in srgb, var(--accent) 5%, transparent);
}

:root.light .trades-table td {
    border-bottom-color: var(--border-subtle);
    color: var(--text-primary);
}

/* Strategy Details */
:root.light .back-btn {
    background: white;
    border-color: var(--border-medium);
    color: var(--text-secondary);
    box-shadow: var(--shadow-sm);
}

:root.light .back-btn:hover {
    background: var(--bg-secondary);
    color: var(--text-primary);
}

/* `.range-value` and `.summary-item` dropped from this group in PR-10 — neither
   has an emitter (`.summary-item`'s six call sites became `ui/KeyValueList`).
   `.stat-item` and `.config-section .config-item` are still live. */
:root.light .stat-item,
:root.light .config-section .config-item {
    background: var(--bg-secondary);
    border: 1px solid var(--border-subtle);
}

/* ===== The `.orb-*` strategy-status family — REMOVED (PR-10) =====

   Thirteen selectors: `.orb-strategy-status`, `.orb-header{, h3}`,
   `.orb-meta .meta-item`, `.orb-section{, h4, .value}` and the eight
   `.orb-section .value.<state>` colour rules. All zero-emitter — the ten strategy
   widgets moved onto `domain/StrategyStatusCard` in PR-7, which reads `--c-*`
   tokens and needs no light override.

   The two colour rules are worth recording, because they are the defect
   StrategyStatusCard was built to remove. Each grouped four unrelated meanings
   into one colour:

     .value.high, .value.profit, .value.target, .value.above   -> --success
     .value.low,  .value.loss,   .value.stop,   .value.below   -> --danger

   So "price is above the opening range" (a market OBSERVATION), "the target leg"
   (a LEVEL), and "this position is up" (a P&L OUTCOME) all rendered in the same
   green, and `stop` — the protective level that limits a loss — rendered in the
   loss colour, as though having a stop were itself bad news. The token layer
   splits these into `--c-pnl-*`, `--c-info-*` and the level roles precisely so a
   value cannot inherit a meaning it does not have. */

:root.light .strategy-status-card {
    background: var(--bg-secondary);
    border-color: var(--border-medium);
}

:root.light .strategy-status-card .status-section {
    background: var(--bg-elevated);
    border: 1px solid var(--border-subtle);
}

:root.light .strategy-status-card .status-row .label {
    color: var(--text-muted);
}

:root.light .strategy-status-card .status-row .value {
    color: var(--text-primary);
}

/* ===== Catalog Page Overrides ===== */

:root.light .catalog-card {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

:root.light .catalog-card:hover {
    border-color: var(--border-medium);
    box-shadow: var(--shadow-md);
}

:root.light .catalog-card.active {
    background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 10%, transparent), var(--bg-elevated));
    border-color: var(--accent);
    box-shadow: var(--shadow-accent);
}

:root.light .catalog-card__key {
    background: var(--bg-secondary);
    color: var(--text-secondary);
    border: 1px solid var(--border-subtle);
}

:root.light .catalog-card__header h3 {
    color: var(--text-primary);
}

:root.light .catalog-card__desc {
    color: var(--text-secondary);
}

:root.light .config-field select {
    background: var(--bg-elevated);
    border-color: var(--border-medium);
    color: var(--text-primary);
}

:root.light .config-field select:hover {
    border-color: var(--text-muted);
}

:root.light .config-field select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

:root.light .checkbox-field span {
    color: var(--text-secondary);
}

/* ===== Trade Reports Page Overrides =====

   Most of this group went with the PR-5 conversion (see the note in
   features.css). Deleted, each with no emitter left:

     .filter-row input/select/:focus, .filter-row/.filter-inline label
        -> Field + Input/Select, which read --c-* and need no light override.
     .stat-mini (+ strong), .stats-row  -> `.tr-meta-row` + Text.
     .table-scroll thead th / tbody td / tr:hover -> `.tr-table`, on --k-table-*.
     .row-warning (+ td)  -> `.tr-row--flagged`, on --c-warning-*.
     .section-card.danger-zone  -> SectionCard accent="negative". The one
        surviving `danger-zone` emitter (AccountSettings) writes
        `settings-section danger-zone`, so this compound selector — which needs
        BOTH classes on one element — already matched nothing; `styles/settings.css`
        is what actually styles that panel.
     .danger-note  -> Text variant="caption" tone="muted". No emitter anywhere.

   `.stat-card` / `.stat-label` / `.stat-value` STAY: FailureSummaryCards,
   StrategyPositionPage and SubscriptionsPage still emit them, and features.css
   still holds their dark base rules. They leave with PR-7 / PR-8. */

:root.light .stat-card {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

:root.light .stat-label {
    color: var(--text-secondary);
}

:root.light .stat-value {
    color: var(--text-primary);
}

/* ===== About Page Overrides ===== */

:root.light .about-hero__badge {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border-color: color-mix(in srgb, var(--accent) 20%, transparent);
    color: var(--accent-strong);
}

/* `.stat-item__value` dropped from this group in PR-10 — no emitter (see the
   note on the `.stat-item__*` trio further down). */
:root.light .about-hero__title,
:root.light .about-section__header h2,
:root.light .feature-card h3,
:root.light .about-cta h2,
:root.light .value-item h4,
:root.light .audience-item h4,
:root.light .hero-stat__value {
    color: var(--text-primary);
}

:root.light .about-hero__subtitle,
:root.light .about-why p,
:root.light .feature-card p,
:root.light .about-cta p,
:root.light .value-item p,
:root.light .audience-item p,
:root.light .hero-stat__label {
    color: var(--text-muted);
}

:root.light .hero-chart-card {
    background: rgba(255, 255, 255, 0.85);
    border-color: var(--border-medium);
    box-shadow: var(--shadow-2xl);
    backdrop-filter: blur(20px);
}

:root.light .hero-msg-bubble {
    background: var(--bg-elevated);
    border-color: var(--border-medium);
    color: var(--text-primary);
    box-shadow: var(--shadow-lg);
}

:root.light .hero-stat__divider {
    background: var(--border-medium);
}

:root.light .feature-card,
:root.light .audience-item,
:root.light .value-item {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

:root.light .feature-card:hover,
:root.light .audience-item:hover,
:root.light .value-item:hover {
    border-color: var(--border-medium);
    box-shadow: var(--shadow-md);
    background: var(--bg-secondary);
}

/* `.about-section--stats` removed (PR-10) — no emitter. AboutPage renders
   `.about-section` with no `--stats` modifier anywhere. This sheet declared the
   SAME dead selector TWICE, ~520 lines apart (once under "About Page Stats", once
   here), with different gradients and shadows — so even had something emitted it,
   which of the two applied depended only on source order. Both are gone.

   Its base rules still sit in `pages/AboutPage.css` (:414 and a media query at
   :1050), together with the `.stat-item__*` trio; that is a page sheet and is not
   in PR-10's scope, so they are left for the AboutPage migration. */

:root.light .about-cta {
    background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 5%, transparent), var(--bg-elevated));
    border-color: color-mix(in srgb, var(--accent) 15%, transparent);
    box-shadow: var(--shadow-lg);
}

:root.light .chart-card {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
}

:root.light .trade-log__item {
    background: var(--bg-secondary);
    border: 1px solid var(--border-subtle);
}

:root.light .trade-log__time {
    color: var(--text-secondary);
}

:root.light .trade-summary {
    border-top-color: var(--border-medium);
}

:root.light .trade-summary__divider {
    background: var(--border-medium);
}

/* `.metric-row{,:hover}` removed (PR-10) — no emitter, and no base rule in any
   sheet either. */

:root.light .hero-chart-title {
    color: var(--text-secondary);
}

/* `.stat-item__note` removed (PR-10) — the last of the `.stat-item__*` trio
   (`__value`, `__label`, `__note`). All three are unemitted; their base rules
   remain in `pages/AboutPage.css` (:433-:447), which PR-10 does not touch. */

/* ===== About Page Table Fixes ===== */
:root.light .metrics-table-wrapper {
    background: var(--bg-elevated);
    border-color: var(--border-subtle);
}

:root.light .metrics-table th {
    background: var(--bg-secondary);
    color: var(--text-muted);
    border-bottom-color: var(--border-medium);
}

:root.light .metrics-table td {
    color: var(--text-primary);
    border-bottom-color: var(--border-subtle);
}

:root.light .metrics-table tbody tr:hover {
    background: var(--bg-secondary);
}
}
/* ============================================================================
   Spinner / Skeleton / DataState
   ========================================================================== */
@layer primitives {
  /* `.k-sr-only` (used by Spinner's label below) now lives in
     styles/utilities.css, loaded from the entry point. It was here, which meant
     any page using the class without importing this component got no rule and
     rendered the hidden text visibly. */

  /* ---- Spinner ---- */
  .k-spinner {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--c-accent);
  }

  .k-spinner__ring {
    display: block;
    border-radius: var(--p-radius-full);
    border: 2px solid var(--c-border-medium);
    border-top-color: currentColor;
    animation: k-spin var(--p-dur-slow) linear infinite;
  }

  .k-spinner--sm .k-spinner__ring { width: 14px; height: 14px; }
  .k-spinner--md .k-spinner__ring { width: 22px; height: 22px; }
  .k-spinner--lg .k-spinner__ring { width: 34px; height: 34px; border-width: 3px; }

  @keyframes k-spin {
    to { transform: rotate(360deg); }
  }

  /* ---- Skeleton ---- */
  .k-skeleton {
    display: block;
    background: var(--c-skeleton-base);
    border-radius: var(--p-radius-sm);
    /* The shimmer is a moving gradient rather than an opacity pulse, which reads
       as "loading" instead of "disabled". */
    background-image: linear-gradient(
      90deg,
      var(--c-skeleton-base) 0%,
      var(--c-skeleton-shimmer) 50%,
      var(--c-skeleton-base) 100%
    );
    background-size: 200% 100%;
    animation: k-shimmer 1.4s var(--p-ease-standard) infinite;
  }

  .k-skeleton--text {
    height: 0.75em;
    margin: 0.2em 0;
    border-radius: var(--p-radius-sm);
  }

  .k-skeleton--block {
    /* Callers pass a real height; this default keeps it visible if they forget. */
    height: 100px;
    border-radius: var(--p-radius-md);
  }

  .k-skeleton--circle {
    width: 32px;
    height: 32px;
    border-radius: var(--p-radius-full);
  }

  .k-skeleton-group {
    display: block;
  }

  @keyframes k-shimmer {
    from { background-position: 200% 0; }
    to { background-position: -200% 0; }
  }

  /* Keyframes are not affected by the zeroed duration tokens, so both animated
     placeholders are stopped explicitly here. */
  @media (prefers-reduced-motion: reduce) {
    .k-spinner__ring,
    .k-skeleton {
      animation: none;
    }
  }

  /* ---- DataState ---- */
  .k-datastate {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--p-space-3);
    /* Enough height that the container does not collapse and jump when the real
       content arrives. */
    min-height: 120px;
    padding: var(--p-space-5);
    font-family: var(--p-font-sans);
    font-size: var(--p-text-sm);
    text-align: center;
  }

  .k-datastate--empty .k-datastate__message {
    color: var(--c-text-muted);
  }

  /* Error is visually distinct from empty ON PURPOSE — conflating them is the
     defect this component exists to prevent. */
  .k-datastate--error .k-datastate__message {
    color: var(--c-danger-fg);
  }

  .k-datastate__retry {
    height: var(--k-control-h-sm);
    padding: 0 var(--p-space-3);
    border: var(--p-border-thin) solid var(--c-border-medium);
    border-radius: var(--k-control-radius);
    background: var(--c-surface-raised);
    color: var(--c-text-primary);
    font-family: inherit;
    font-size: var(--p-text-xs);
    cursor: pointer;
  }

  .k-datastate__retry:hover {
    background: var(--c-surface-hover);
    border-color: var(--c-border-strong);
  }
}
/* ============================================================================
   RouteState — Suspense fallback, auth gate, catch-all
   ----------------------------------------------------------------------------
   In @layer composites, and reached from the ENTRY bundle (App.tsx imports
   RouteState.tsx statically). That is the point: the screens this styles all
   render while a lazy chunk is absent, so their rules cannot live in
   `admin/Admin.css` — which is what shipped, and why the admin gate rendered
   unstyled. See the note in RouteState.tsx.

   Every value is a token. The block this replaces used `rgba(255,255,255,0.5)`
   inline, which is invisible on the light theme's white surface.
   ========================================================================== */
@layer composites {
  .k-route-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--p-space-3);
    /* Not `100vh`: this renders INSIDE the shell's content column on the user
       side (below a 64px topbar and inside its padding), so a viewport-height box
       would overflow and add a second scrollbar. `min-height` on the available
       space, with a floor that still reads as deliberate on a short viewport. */
    min-height: min(60vh, 24rem);
    padding: var(--p-space-6) var(--p-space-4);
    color: var(--c-text-secondary);
    text-align: center;
  }

  .k-route-state__text {
    font-size: var(--p-text-sm);
    color: var(--c-text-muted);
  }

  /* ---- Notice: gate / 404 ---- */

  /* Only when the notice REPLACES a shell (the admin gate) does it own the
     viewport. Inside the shell's content column, 100dvh would be 100dvh below a
     64px topbar plus 32px of padding — the page would overflow by ~128px and gain
     a second scrollbar. The default above sizes to its content instead. */
  .k-route-state--fullscreen {
    min-height: 100vh;
    min-height: 100dvh;
  }

  .k-route-state__panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--p-space-4);
    /* `min()` not a bare max-width: at 390px a 28rem panel plus the parent's
       padding would overflow. */
    width: min(28rem, 100%);
    padding: var(--p-space-6);
    border: var(--p-border-hairline) solid var(--c-border-subtle);
    border-radius: var(--p-radius-xl);
    background: var(--c-surface-raised);
  }

  .k-route-state__title {
    margin: 0;
    font-family: var(--p-font-sans);
    font-size: var(--p-text-lg);
    font-weight: var(--p-weight-semibold);
    color: var(--c-text-primary);
  }

  .k-route-state__desc {
    margin: 0;
    font-size: var(--p-text-sm);
    line-height: var(--p-leading-relaxed);
    color: var(--c-text-secondary);
    /* Error messages and correlation ids arrive as long unbroken tokens. */
    overflow-wrap: anywhere;
  }

  .k-route-state__action {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--p-space-3);
  }
}
/* ============================================================================
   Button / IconButton
   ----------------------------------------------------------------------------
   Replaces ~30 button class families. All four interaction states are defined
   for every variant — several legacy families were missing :disabled entirely,
   so a dead button still looked live.

   Heights come from --k-control-h-*, shared with inputs and selects, which is
   what makes a Button line up with a NumberInput in the same Row without either
   one hardcoding 36px.
   ========================================================================== */
@layer primitives {
  .k-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--p-space-2);
    box-sizing: border-box;
    border: var(--p-border-thin) solid transparent;
    border-radius: var(--k-control-radius);
    font-family: var(--p-font-sans);
    font-weight: var(--p-weight-medium);
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    appearance: none;
    transition:
      background-color var(--p-dur-fast) var(--p-ease-standard),
      border-color var(--p-dur-fast) var(--p-ease-standard),
      color var(--p-dur-fast) var(--p-ease-standard),
      opacity var(--p-dur-fast) var(--p-ease-standard);
  }

  /* ---- Size. Heights are the shared control heights, so a button and an input
          in the same Row are the same height by construction. ---- */
  .k-btn--sm {
    height: var(--k-control-h-sm);
    padding: 0 var(--p-space-2);
    font-size: var(--p-text-xs);
  }
  .k-btn--md {
    height: var(--k-control-h-md);
    padding: 0 var(--p-space-3);
    font-size: var(--p-text-sm);
  }
  .k-btn--lg {
    height: var(--k-control-h-lg);
    padding: 0 var(--p-space-4);
    font-size: var(--p-text-md);
  }

  .k-btn--block { width: 100%; }

  /* Square: width tracks the height token so the glyph sits in a true square. */
  .k-btn--icon-only { padding: 0; aspect-ratio: 1; }
  .k-btn--icon-only.k-btn--sm { width: var(--k-control-h-sm); }
  .k-btn--icon-only.k-btn--md { width: var(--k-control-h-md); }
  .k-btn--icon-only.k-btn--lg { width: var(--k-control-h-lg); }

  .k-btn__icon {
    display: inline-flex;
    align-items: center;
    /* Icons must not stretch when the label wraps or the button grows. */
    flex: 0 0 auto;
  }

  .k-btn__label {
    /* Lets a long label truncate inside a block button instead of forcing the
       button wider than its container. */
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* ---- Primary ---- */
  .k-btn--primary {
    background: var(--c-accent);
    color: var(--c-text-on-accent);
    border-color: var(--c-accent);
  }
  .k-btn--primary:hover:not(:disabled) {
    background: var(--c-accent-hover);
    border-color: var(--c-accent-hover);
  }
  .k-btn--primary:active:not(:disabled) { background: var(--c-accent-active); }

  /* ---- Secondary: the default. Bordered, no fill. ---- */
  .k-btn--secondary {
    background: var(--c-surface-raised);
    color: var(--c-text-primary);
    border-color: var(--c-border-medium);
  }
  .k-btn--secondary:hover:not(:disabled) {
    background: var(--c-surface-hover);
    border-color: var(--c-border-strong);
  }
  .k-btn--secondary:active:not(:disabled) { background: var(--c-surface-active); }

  /* ---- Ghost: for toolbars and table rows, where a bordered button per row
          turns the table into a grid of boxes. ---- */
  .k-btn--ghost {
    background: transparent;
    color: var(--c-text-muted);
  }
  .k-btn--ghost:hover:not(:disabled) {
    background: var(--c-surface-hover);
    color: var(--c-text-primary);
  }
  .k-btn--ghost:active:not(:disabled) { background: var(--c-surface-active); }

  /* ---- Danger: its own variant, never a recoloured primary. This app squares
          off positions and switches to live money from buttons. ---- */
  .k-btn--danger {
    background: var(--c-danger-bg);
    color: var(--c-danger-fg);
    border-color: var(--c-danger-border);
  }
  /* Hover fills with the danger hue, so the text has to invert. --c-text-inverse
     is right in BOTH themes for the same reason: dark theme's danger-fg is a
     LIGHT red (red-400) and takes dark text, light theme's is a DEEP red
     (red-600) and takes white — which is exactly what that token resolves to. */
  .k-btn--danger:hover:not(:disabled) {
    background: var(--c-danger-fg);
    color: var(--c-text-inverse);
    border-color: var(--c-danger-fg);
  }

  /* ---- Success: confirmations, arm/enable actions ---- */
  .k-btn--success {
    background: var(--c-success-bg);
    color: var(--c-success-fg);
    border-color: var(--c-success-border);
  }
  /* Same inversion argument as danger above. */
  .k-btn--success:hover:not(:disabled) {
    background: var(--c-success-fg);
    color: var(--c-text-inverse);
    border-color: var(--c-success-fg);
  }

  /* ---- Pressed (aria-pressed toggles). Selected state must differ in more than
          one channel from idle, or it disappears for a greyscale/low-vision
          reader — the same defect the tab families had. Here that is the accent
          fill PLUS the accent border, on top of the variant's own colours. ---- */
  .k-btn--pressed {
    background: var(--c-accent-soft);
    border-color: var(--c-accent);
    color: var(--c-text-primary);
  }

  /* Ghost is the common toggle variant (toolbars): without this its transparent
     background would win on specificity ties and the pressed state would vanish
     on hover-out. */
  .k-btn--ghost.k-btn--pressed:hover:not(:disabled) {
    background: var(--c-accent-soft);
    color: var(--c-text-primary);
  }

  /* ---- Disabled. Defined ONCE for all variants, which is the bug this fixes:
          several legacy families had no disabled state, so a dead button looked
          identical to a live one. ---- */
  .k-btn:disabled {
    opacity: var(--p-opacity-disabled);
    cursor: not-allowed;
  }

  /* An <a> can never match :disabled, so `as={Link}` + disabled needs its own
     hook (Button sets it next to aria-disabled). `pointer-events: none` rather
     than a cursor change, because every hover rule above is gated on
     `:not(:disabled)` — a condition an anchor ALWAYS satisfies, so a disabled
     link would otherwise still light up on hover and still navigate on click. */
  .k-btn--disabled {
    opacity: var(--p-opacity-disabled);
    pointer-events: none;
  }

  /* ---- Loading spinner. currentColor so it works on every variant without a
          per-variant rule. ---- */
  .k-btn__spinner {
    width: 1em;
    height: 1em;
    flex: 0 0 auto;
    border: 2px solid currentColor;
    /* One transparent edge is what makes the rotation visible. */
    border-top-color: transparent;
    border-radius: var(--p-radius-full);
    animation: k-btn-spin var(--p-dur-slow) linear infinite;
  }

  @keyframes k-btn-spin {
    to { transform: rotate(360deg); }
  }

  /* base.css zeroes the duration tokens under prefers-reduced-motion, but a
     keyframe animation ignores those — so it is stopped explicitly here. The
     button still reads as busy via aria-busy and the disabled state. */
  @media (prefers-reduced-motion: reduce) {
    .k-btn__spinner { animation: none; opacity: var(--p-opacity-muted); }
  }
}
