/* ============================================================================
   Nova.tokens.css — minimal runtime token file for Blazor apps.
   Import this ONCE from App.razor or _Host.cshtml before any component CSS.
   Derived from colors_and_type.css — this is the handoff subset Claude Code
   should consume and extend. See tokens/nova.tokens.json for the full source.

   v2 additions (22 Apr 2026):
     - Chart-series palette (--nova-chart-1 … --nova-chart-6)
     - Container tints for severity banners (--nova-container-*)
     - Tertiary accents (--nova-accent-teal, --nova-accent-violet)
     - Neutral interstitials (--nova-canvas-rail, --nova-canvas-sunken)

   v2.12 additions (25 Apr 2026):
     - Tone-keyed value text (--nova-value-tone-*) — primary surface
       for stat-card / hero-metric Sub+Tone (SUB-GAP-039)
     - Chart axes / gridlines (--nova-chart-axis, --nova-chart-grid,
       --nova-chart-grid-strong, --nova-chart-tooltip-*)
     - Glass surface chrome shared by NovaGlassPopup + NovaFilterPopup
       (--nova-glass-surface, --nova-glass-border, --nova-glass-blur)
     - Segmented-control track + thumb (--nova-segmented-*)
     - Treemap text-flip threshold + ring track (--nova-ring-track)
     - SUB-GAP-033 reconciliation: --nova-text-secondary,
       --nova-text-muted, --nova-text-primary now have ONE canonical
       value. app.css MUST stop redefining these at :root / dark.
   ============================================================================ */

@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root {
  /* Tell the UA which scheme to paint native controls + their popups in, so
     the browser's own calendar/time/select popups and scrollbars render in the
     right theme from the first frame. Without this the native date-picker popup
     flashes black for a frame before repainting (dark-mode "black blink" on
     open). Flipped to dark under [data-theme="dark"] below. */
  color-scheme: light;

  /* ===== CANONICAL BREAKPOINTS (mobile-first, min-width only) ===== */
  --nova-bp-sm: 480px;   /* large phone */
  --nova-bp-md: 768px;   /* small tablet — sidebar returns as 64px rail */
  --nova-bp-lg: 1024px;  /* small laptop — sidebar expands to 260px full */
  --nova-bp-xl: 1280px;  /* desktop */

  /* Palette — raw. Never reference directly from component CSS; use semantic tokens below. */
  --nova-blue-500: #146EF4;
  --nova-blue-600: #0a4bc7;
  --nova-blue-300: #8ab4f8;

  --nova-neutral-0:   #FFFFFF;
  --nova-neutral-50:  #F5F7FA;
  --nova-neutral-100: #FAFBFC;
  --nova-neutral-200: #EBF2FE;
  --nova-neutral-300: #E0E0E0;
  --nova-neutral-500: #555555;
  --nova-neutral-700: #2c2c30;
  --nova-neutral-800: #19191d;
  --nova-neutral-900: #0e0e11;

  --nova-sev-critical: #DC2626;
  --nova-sev-severe:   #ea580c;
  --nova-sev-warning:  #F59E0B;
  --nova-sev-success:  #16A34A;

  --nova-ink-900: #111111;

  /* Semantic — light mode */
  --nova-primary:          var(--nova-blue-500);
  --nova-primary-hover:    var(--nova-blue-600);
  /* v2.6 · SUB-GAP-010 — ink to pair with --nova-primary (fg on blue chips,
     active tabs, filled buttons). Paired with severity families so that
     colored backgrounds never use raw #fff. */
  --nova-on-accent:            #FFFFFF;
  --nova-on-severity-critical: #FFFFFF;
  --nova-on-severity-severe:   #FFFFFF;
  --nova-on-severity-warning:  #111111;
  --nova-on-severity-success:  #FFFFFF;
  /* SUB-GAP-047 §16 / v2.13a §1 — three orphan tokens declared canonically.
     Components reference these; without declarations CSS falls back silently. */
  --nova-inverse-surface:  #111111;                   /* toasts / dark spinners on light canvas */
  --nova-on-accent-border: rgba(255, 255, 255, 0.32); /* 1px hairline inside filled --nova-primary */
  --nova-on-accent-muted:  rgba(255, 255, 255, 0.64); /* second-tier ink on filled --nova-primary */
  --nova-canvas:           #FFFFFF;
  --nova-canvas-panel:     #FFFFFF;
  --nova-canvas-panel-alt: var(--nova-neutral-100);
  --nova-canvas-border:    #E5E7EB;
  --nova-border-subtle:    rgba(0,0,0,0.06);

  /* GAP-038 NovaGeoMap — self-contained world basemap (light): near-white land on a pale
     sea, hairline coastline, faint graticule. Marker tones reuse --nova-severity-*. */
  --nova-geo-ocean:        #e9eff6;
  --nova-geo-land:         #f8fafc;
  --nova-geo-land-stroke:  #c2ccd8;
  --nova-geo-graticule:    rgba(0,0,0,0.05);
  --nova-geo-link:         var(--nova-accent);
  --nova-geo-place:        #6b7280;

  --nova-chrome-surface:   var(--nova-neutral-50);
  --nova-chrome-divider:   #EFF1F4;

  /* v2 — neutral interstitials. Sub-surfaces between panel and border.
     Use for: progress-bar rails, chart gridlines, sunken track fills. */
  --nova-canvas-rail:    #EEF1F5;
  --nova-canvas-sunken:  #F0F3F7;

  --nova-text-primary:   var(--nova-ink-900);
  /* SUB-GAP-033 reconciliation — text tier values are CANONICAL here.
     app.css MUST NOT redefine these. Light --nova-text-secondary tightens
     from #555555 → #424655 (4.95:1 → 7.96:1 contrast on canvas-panel-alt
     #FAFBFC). Light --nova-text-muted holds at #727786. */
  --nova-text-secondary: #424655;
  --nova-text-muted:     #727786;
  /* SUB-GAP-048 §3 — third text tier, sits between secondary (#424655) and
     muted (#727786) on the shipped ladder. Used by NovaTextInput /
     NovaSelect / NovaDatePicker chrome (icons, hover-borders, helper text,
     placeholders, counters). 5.7:1 on canvas-panel — passes AA for
     non-text UI; passes AA Large for body. PRE-v2.12.1 this token was
     referenced in 16 sites without a declaration — chrome rendered with
     inherited currentColor (theme-incorrect). */
  --nova-text-tertiary:  #6b7280;

  --nova-severity-critical: var(--nova-sev-critical);
  --nova-severity-severe:   var(--nova-sev-severe);
  --nova-severity-warning:  var(--nova-sev-warning);
  --nova-severity-success:  var(--nova-sev-success);
  --nova-severity-info:     var(--nova-primary);

  --nova-severity-critical-text: #b91c1c;
  --nova-severity-warning-text:  #a16207;
  --nova-severity-success-text:  #15803d;

  /* GAP-013: NovaProgressRowList / NovaProgressBar over-capacity (>100%) treatment.
     Replaces the fill colour on a bar whose value exceeds 100% and feeds the hatched
     overflow tip past the 100% reference line, so an over-capacity bar reads as a
     distinct measurement regime without relying on the % text.
     Deeper than --nova-severity-critical on purpose: over-capacity is a distinct
     measurement fact (a hard limit crossed), not a generic critical state. */
  --nova-over-capacity:        #b42318;
  --nova-over-capacity-hatch:  #7a1c12;
  --nova-over-capacity-text:   #b42318;

  /* v2 — container tints. Only valid use: the body fill of a status banner
     that itself is NOT a row. Row-level severity is still 3px left border
     + colored text + dot — do not tint rows. These are for standalone
     inline notices (e.g. form submission banner, empty-state-with-cause). */
  --nova-container-success:  rgba(22, 163, 74, 0.08);
  --nova-container-warning:  rgba(245, 158, 11, 0.08);
  --nova-container-severe:   rgba(234, 88, 12, 0.08);
  --nova-container-critical: rgba(220, 38, 38, 0.08);
  --nova-container-info:     rgba(20, 110, 244, 0.08);

  /* v2 — tertiary accents. Use ONLY for secondary data series and chart
     categoricals. Never for brand, never for severity. */
  --nova-accent-teal:   #0EA5A3;
  --nova-accent-violet: #7C5CE0;

  /* v2 — chart-series palette. Qualitative 6-color set, ordered by
     scan-priority. Use in index order: first category = chart-1, etc.
     These are the ONLY allowed colors for bars/lines/donut segments.
     Dark-mode values re-point below. */
  --nova-chart-1: #146EF4;  /* brand blue */
  --nova-chart-2: #0EA5A3;  /* teal */
  --nova-chart-3: #7C5CE0;  /* violet */
  --nova-chart-4: #F59E0B;  /* amber */
  --nova-chart-5: #E1643F;  /* coral */
  --nova-chart-6: #475569;  /* slate */

  --nova-signal-surface: var(--nova-ink-900);
  --nova-signal-ink:     #FFFFFF;

  /* §SUB-GAP-001 — muted text tiers for the always-opposite-of-canvas signal
     surface. These tokens invert with --nova-signal-surface (not with canvas),
     so consumers don't hand-roll rgba() on hex values. */
  --nova-signal-text-muted:  rgba(255, 255, 255, 0.72);
  --nova-signal-text-subtle: rgba(255, 255, 255, 0.56);

  /* v2.11 · SUB-GAP-021/022 — canvas-inverted focal surface family for
     NovaSignalCard + NovaPredictorCard. Separate from --nova-signal-surface
     because signal-surface inverts with canvas (dark ↔ light), whereas
     these go DEEPER than canvas in dark mode so the focal surface still
     contrasts against dark chrome. Flat — no gradient, no colored shadow. */
  --nova-signal-canvas:     #0e1016;
  --nova-signal-canvas-alt: #1a1d25;
  --nova-signal-hairline:   rgba(255, 255, 255, 0.08);

  /* The canvas is ALWAYS dark (both modes), so its text is always light. These
     are mode-invariant on purpose (no dark override) and exist because
     --nova-signal-ink / -text-muted / -text-subtle invert with
     --nova-signal-surface, which would collapse to dark-on-dark on the canvas in
     dark mode. NovaPredictorCard / NovaSignalCard use these. */
  --nova-signal-canvas-ink:         #FFFFFF;
  --nova-signal-canvas-text-muted:  rgba(255, 255, 255, 0.72);
  --nova-signal-canvas-text-subtle: rgba(255, 255, 255, 0.56);

  /* v2.11 · SUB-GAP-026 — 5th-tier aging severity. One step deeper than
     --nova-severity-critical for 5-band aging palettes (0-7d / 8-30d /
     31-60d / 61-90d / 90d+). Not a replacement for --nova-severity-critical. */
  --nova-severity-critical-deep: #991b1b;

  /* §SUB-GAP-005 — promoted tokens. Legacy names (--nova-occ-press,
     --nova-canvas-inset-shadow) live here under canonical semantic names. */
  --nova-state-pressed:        rgba(0, 0, 0, 0.06);
  --nova-canvas-inset-shadow:  inset 0 1px 2px rgba(0, 0, 0, 0.04);

  /* Type */
  --nova-font-sans: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --nova-font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Spacing / radii / motion */
  --nova-space-xs: 4px;
  --nova-space-sm: 8px;
  --nova-space-md: 16px;
  --nova-space-lg: 24px;
  --nova-space-xl: 32px;

  --nova-radius-sm: 4px;
  --nova-radius-md: 8px;
  --nova-radius-pill: 9999px;

  --nova-shadow-sm: 0 1px 3px rgba(0,0,0,0.07);
  /* v2.13b · SUB-GAP-033 / roadmap B2 — migrated from app.css to canonical token source. */
  --nova-shadow-lg: 0 4px 16px rgba(0,0,0,0.08);
  --nova-shadow-xl: 0 24px 48px rgba(25,28,30,0.12), 0 4px 12px rgba(25,28,30,0.06);

  --nova-transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);

  /* Runtime-set inline by NovaRiskHighlightCard — declared with a default
     so the validator's token-completeness check passes and the CSS has a
     sensible fallback if a consumer omits the inline style. */
  --nova-risk-tone: var(--nova-severity-severe);

  /* Runtime-set inline by NovaTable (declarative mode) — defaults so the
     CSS resolves without inline style and the validator passes. The
     declarative path always sets these inline; values here are fallback. */
  --nova-tablev2-grid: auto;
  --nova-tablev2-pad: 20px;
  --nova-tablev2-rowpad: 12px;
  --nova-tablev2-maxheight: none;

  /* v2.13b · SUB-GAP-049 §3 — single source of truth for the icon-only sidebar
     rail width. Used by both `.nova-shell--sidebar-collapsed` (manual collapse)
     and the `@media (max-width: 900px)` auto-collapse so widths don't double-jump
     on resize. Layout token, not theme-driven (same value light + dark). */
  --nova-sidebar-collapsed-width: 64px;

  /* v2.11 · SUB-GAP-022 — LIVE indicator on NovaPredictorCard. Slower than
     spinner cadence (900ms) so it reads as breathing, not spinning. */
  --nova-motion-predictor-pulse-duration: 1800ms;

  --nova-content-max-width: 1400px;

  /* ===== SHELL TOKENS — three-tier responsive ===== */
  --nova-appbar-height: 56px;       /* phone */
  --nova-sidebar-width: 0;           /* phone — drawer is overlay, no inline rail */
  --nova-sidebar-collapsed-width: 64px; /* retained for the rail at md tier */

  /* v2.7 · SUB-GAP-015 — Phase B motion durations. Long-running
     indeterminate animations that don't fit --nova-transition (150ms).
     Same value in light and dark. */
  --nova-motion-spinner-duration: 900ms;
  --nova-motion-progress-indeterminate-duration: 1400ms;
  --nova-motion-skeleton-duration: 1600ms;

  /* v2.7 · SUB-GAP-015 — Scrim + z-scale. Scrim is the NovaOverlay
     fill; z-scale orders popover (70) → scrim (80) → dialog (90), with
     10-point gaps leaving room for toasts / drawer content without
     renumbering. */
  --nova-scrim:         rgba(0, 0, 0, 0.38);
  --nova-scrim-strong:  rgba(0, 0, 0, 0.60);
  --nova-z-popover:     70;
  --nova-z-scrim:       80;
  --nova-z-dialog:      90;

  /* ───────── v2.12 (25 Apr 2026) ───────── */

  /* SUB-GAP-039 — tone-keyed value text. The focal number in a stat card
     or hero metric inherits one of these instead of the generic text-primary,
     so callers can encode "open repairs is bad and rising" via Tone="Critical"
     without per-page CSS. NEVER use these for body text — value-class only.
     Pairs visually with --nova-severity-*-text but slightly darker so the
     focal number reads as gravity, not a chip. */
  --nova-value-tone-neutral:  var(--nova-text-primary);
  --nova-value-tone-good:     #15803d;
  --nova-value-tone-warning:  #92400e;
  --nova-value-tone-severe:   #c2410c;
  --nova-value-tone-critical: #991b1b;
  --nova-value-tone-info:     var(--nova-blue-600);

  /* SUB-GAP-035 — chart axes / gridlines / tooltip chrome. Quieter than
     canvas-border because they live BEHIND data. Tooltip is shared with
     NovaGlassPopup family so chart hover-cards feel native. */
  --nova-chart-axis:           rgba(0, 0, 0, 0.18);
  --nova-chart-grid:           rgba(0, 0, 0, 0.06);
  --nova-chart-grid-strong:    rgba(0, 0, 0, 0.10);
  --nova-chart-tooltip-bg:     rgba(255, 255, 255, 0.96);
  --nova-chart-tooltip-border: rgba(0, 0, 0, 0.08);
  --nova-chart-empty-stroke:   rgba(0, 0, 0, 0.10);

  /* SUB-GAP-027/030 — glass-family chrome shared by NovaGlassPopup
     (hover tooltip) and NovaFilterPopup (click-open filter panel). Both
     resolve to ONE source of truth so the surface family stays unified. */
  --nova-glass-surface:        rgba(255, 255, 255, 0.88);
  --nova-glass-surface-strong: rgba(255, 255, 255, 0.94);
  --nova-glass-border:         rgba(0, 0, 0, 0.08);
  --nova-glass-blur:           blur(12px) saturate(140%);

  /* SUB-GAP-037 — segmented control. Track is sunken; thumb is canvas-panel
     so the active segment reads as "lifted out" of the track. */
  --nova-segmented-track:      var(--nova-canvas-sunken);
  --nova-segmented-thumb:      var(--nova-canvas-panel);
  --nova-segmented-thumb-ring: var(--nova-canvas-border);

  /* SUB-GAP-040 — radial-progress ring track (the un-filled portion of
     a NovaRadialProgress arc). Quieter than canvas-rail because rings sit
     INSIDE cards and need to recede further. */
  --nova-ring-track:           var(--nova-canvas-rail);

  /* SUB-GAP-036 — treemap ink-flip threshold. Tiles whose primary-tint
     opacity exceeds this value flip foreground from --nova-text to
     --nova-on-accent. Mode-invariant; consumers read this and branch. */
  --nova-treemap-ink-flip-at:  0.55;

  /* SUB-GAP-035 — chart series stroke widths + dash patterns. Tokenized
     so all chart primitives draw lines at the same weight without page-CSS. */
  --nova-chart-stroke:         2px;
  --nova-chart-stroke-strong:  2.5px;
  --nova-chart-dash:           6 3;

  /* ───────── v2.14 (GAP-003…006) ───────── */

  /* GAP-005: shared control heights. The input family (NovaTextInput,
     NovaNumberInput, NovaSelect) sizes off these so a row of controls aligns.
     Ported from Nova v2.9 (SUB-GAP-018); NovaTextInput already references them
     via var(--nova-control-h-md, 36px) fallbacks; now canonical. Mode-invariant. */
  --nova-control-h-sm: 28px;
  --nova-control-h-md: 36px;
  --nova-control-h-lg: 44px;

  /* GAP-005: fourth control radius. The checkbox/grid-cell tier: 2px, one
     step tighter than --nova-radius-sm (4px, inputs/buttons). INPUTS.md caps
     the checkbox box at "no rounded > 2px"; this is the canonical value so
     NovaCheckbox / NovaBulkGrid cells never hardcode it. Mode-invariant. */
  --nova-radius-xs: 2px;

  /* GAP-004: NovaLightbox immersive chrome. A photo viewer is always a
     dark, immersive surface REGARDLESS of app theme (the photo, not the UI,
     is the figure), so these are mode-invariant, no dark override. The
     backdrop is near-opaque, far heavier than --nova-scrim (0.38) which is
     for dialogs that keep page context visible underneath. */
  --nova-lightbox-scrim:         rgba(0, 0, 0, 0.92);
  --nova-lightbox-surface:       rgba(255, 255, 255, 0.10); /* nav buttons, caption bar */
  --nova-lightbox-surface-hover: rgba(255, 255, 255, 0.18);
  --nova-lightbox-ink:           #FFFFFF;                   /* captions, controls */
  --nova-lightbox-ink-muted:     rgba(255, 255, 255, 0.64); /* counter, EXIF meta */

  /* GAP-006: NovaBulkGrid cell selection. The range / multi-cell selection
     fill + its rectangle outline (also the fill-handle anchor). Theme-keyed
     (re-pointed in dark below) so the translucent brand wash reads on both
     canvases. The sticky header reuses --nova-canvas-rail via the alias. */
  --nova-grid-selection:        rgba(20, 110, 244, 0.12);
  --nova-grid-selection-border: rgba(20, 110, 244, 0.40);
  --nova-grid-header-bg:        var(--nova-canvas-rail);    /* alias: mode-invariant */

  /* GAP-009: NovaHorizonBoard resting voyage-bar fill. A horizon board is ONE
     calm field where the SELECTED voyage must pop, so every resting bar shares
     a single translucent brand wash and selection flips to solid --nova-primary
     + a lift (not six chart hues competing). Theme-keyed (re-pointed in dark so
     the wash registers on the dark lane); the bar radius reuses --nova-radius-sm. */
  --nova-horizon-bar:           rgba(20, 110, 244, 0.14);

  /* ===== FLUID TYPE RAMP (mobile-first via clamp) ===== */
  --nova-text-h1: clamp(1.5rem, 1rem + 2vw, 2.25rem);
  --nova-text-h2: clamp(1.25rem, 0.9rem + 1.5vw, 1.75rem);
  --nova-text-h3: clamp(1.125rem, 0.95rem + 0.75vw, 1.375rem);
  --nova-text-body: clamp(0.875rem, 0.8rem + 0.25vw, 1rem);
  --nova-text-caption: 0.8125rem; /* legibility floor; fixed */

  /* ===== FLUID PAGE SPACING ===== */
  --nova-space-page: clamp(12px, 2vw + 4px, 32px);
  --nova-space-section: clamp(16px, 1vw + 12px, 24px);
  --nova-space-stack: clamp(8px, 0.5vw + 6px, 12px);

  /* ===== TOUCH TARGET FLOOR ===== */
  --nova-touch-min: 44px;
}

@media (min-width: 768px) {
  :root {
    --nova-appbar-height: 64px;
    --nova-sidebar-width: 64px;     /* md — compact rail */
  }
}

@media (min-width: 1024px) {
  :root {
    --nova-sidebar-width: 260px;    /* lg+ — full sidebar */
  }
}

[data-theme="dark"],
.mud-theme-dark {
  color-scheme: dark;   /* native controls + popups paint dark from frame one (no black blink) */
  --nova-primary:          var(--nova-blue-300);
  /* v2.6 · SUB-GAP-010 — on-accent families (dark theme). Dark-tint blue
     is pale enough that near-black ink reads better than white. Severity
     backgrounds stay black-ink on warning, white-ink elsewhere. */
  --nova-on-accent:            #0e0e11;
  --nova-on-severity-critical: #111111;
  --nova-on-severity-severe:   #111111;
  --nova-on-severity-warning:  #111111;
  --nova-on-severity-success:  #111111;
  /* SUB-GAP-047 §16 / v2.13a §1 — orphan tokens (dark). inverse-surface flips
     to white in dark mode; on-accent-* flip to dark hairline / dark muted. */
  --nova-inverse-surface:  #FFFFFF;
  --nova-on-accent-border: rgba(0, 0, 0, 0.18);
  --nova-on-accent-muted:  rgba(0, 0, 0, 0.56);
  --nova-canvas:           var(--nova-neutral-900);
  --nova-canvas-panel:     var(--nova-neutral-800);
  --nova-canvas-panel-alt: #131316;
  --nova-canvas-border:    rgba(255,255,255,0.07);
  --nova-border-subtle:    rgba(255,255,255,0.05);

  /* GAP-038 NovaGeoMap — self-contained world basemap (dark): deep sea, raised land,
     low-contrast coastline + graticule so vessel markers carry the signal. */
  --nova-geo-ocean:        #0b1018;
  --nova-geo-land:         #232f44;
  --nova-geo-land-stroke:  #3c4a63;
  --nova-geo-graticule:    rgba(255,255,255,0.05);
  --nova-geo-link:         var(--nova-accent);
  --nova-geo-place:        #9ca3af;

  --nova-chrome-surface:   var(--nova-neutral-900);
  --nova-chrome-divider:   rgba(255,255,255,0.07);

  /* v2 — neutral interstitials (dark) */
  --nova-canvas-rail:    rgba(255,255,255,0.06);
  --nova-canvas-sunken:  rgba(255,255,255,0.03);

  --nova-text-primary:   #e8eaed;
  /* SUB-GAP-033 reconciliation — dark tier values CANONICAL here.
     --nova-text-secondary lifts from #9aa0a6 → #b3b8c0 (3.8:1 → 5.9:1 on
     canvas-panel #19191d, AA threshold). --nova-text-muted lifts from
     #6b7280 → #9ca3af (failed AA at 3.4:1 → passes at 5.0:1). */
  --nova-text-secondary: #b3b8c0;
  --nova-text-muted:     #9ca3af;
  /* SUB-GAP-048 §3 (dark) — third text tier. 5.0+ headroom on canvas-panel
     because consumer sites include NovaDatePicker helper text and
     NovaTextInput hover-border (body-text use, not just icon/border). */
  --nova-text-tertiary:  #8a909c;

  --nova-severity-critical: #f28b82;
  --nova-severity-severe:   #fb923c;
  --nova-severity-warning:  #fbbf24;
  --nova-severity-success:  #4ade80;
  --nova-severity-info:     var(--nova-blue-300);

  /* GAP-013 (dark): over-capacity treatment. Fill + text lift to a hot pale red
     so they read on the dark lane; the hatch goes deep red so the diagonal stripe
     still registers over the light-red overflow tip. */
  --nova-over-capacity:        #ff8a80;
  --nova-over-capacity-hatch:  #b42318;
  --nova-over-capacity-text:   #ff8a80;

  /* v2 — container tints (dark). Higher alpha so they register against
     dark panels but never fight the colored text. */
  --nova-container-success:  rgba(74, 222, 128, 0.14);
  --nova-container-warning:  rgba(251, 191, 36, 0.14);
  --nova-container-severe:   rgba(251, 146, 60, 0.14);
  --nova-container-critical: rgba(242, 139, 130, 0.14);
  --nova-container-info:     rgba(138, 180, 248, 0.14);

  /* v2 — tertiary accents (dark — lightened for contrast on dark panel) */
  --nova-accent-teal:   #2DD4BF;
  --nova-accent-violet: #A78BFA;

  /* v2 — chart-series (dark). Hue-shifted lighter to clear dark surface. */
  --nova-chart-1: #8ab4f8;
  --nova-chart-2: #2DD4BF;
  --nova-chart-3: #A78BFA;
  --nova-chart-4: #fbbf24;
  --nova-chart-5: #F98A6A;
  --nova-chart-6: #94A3B8;

  --nova-signal-surface: #FFFFFF;
  --nova-signal-ink:     var(--nova-ink-900);

  /* §SUB-GAP-001 (dark) */
  --nova-signal-text-muted:  rgba(17, 17, 17, 0.72);
  --nova-signal-text-subtle: rgba(17, 17, 17, 0.56);

  /* v2.11 · SUB-GAP-021/022 (dark) — goes DEEPER than default canvas, not
     lighter. Focal surface must still contrast against dark chrome. */
  --nova-signal-canvas:     #050608;
  --nova-signal-canvas-alt: #101217;
  /* --nova-signal-hairline stays mode-invariant. */

  /* v2.11 · SUB-GAP-026 (dark) — lighter than --nova-severity-critical
     (#f28b82) to preserve the "deepest" semantic. In dark mode the screen
     is already dark; lightness = intensity. */
  --nova-severity-critical-deep: #fca5a5;

  /* §SUB-GAP-005 (dark) */
  --nova-state-pressed:        rgba(255, 255, 255, 0.06);
  --nova-canvas-inset-shadow:  inset 0 1px 2px rgba(0, 0, 0, 0.25);

  /* v2.13b · SUB-GAP-033 / roadmap B2 — dark shadow steps up to 0.4 alpha so
     the drop reads through the darkened canvas. Migrated from app.css. */
  --nova-shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.4);

  /* v2.7 · SUB-GAP-015 (dark) — scrim steps up to ~56% alpha so the
     darkened dark-mode scrim still registers visually against
     --nova-canvas (neutral-900). --nova-scrim-strong is identical in
     both themes — "strong" is a fixed 60% alpha regardless of mode. */
  --nova-scrim:  rgba(0, 0, 0, 0.56);

  /* ───────── v2.12 (dark) ───────── */

  /* SUB-GAP-039 (dark) — tone-keyed value text. Lighter so the focal
     number stays readable on dark canvas. Mirrors severity-*-text-dark
     intentions. */
  --nova-value-tone-good:     #4ade80;
  --nova-value-tone-warning:  #fbbf24;
  --nova-value-tone-severe:   #fb923c;
  --nova-value-tone-critical: #f28b82;
  --nova-value-tone-info:     var(--nova-blue-300);

  /* SUB-GAP-035 (dark) — chart axes / grids / tooltip. Inverted via
     white-alpha. Tooltip uses dark-glass surface. */
  --nova-chart-axis:           rgba(255, 255, 255, 0.20);
  --nova-chart-grid:           rgba(255, 255, 255, 0.06);
  --nova-chart-grid-strong:    rgba(255, 255, 255, 0.10);
  --nova-chart-tooltip-bg:     rgba(19, 19, 22, 0.92);
  --nova-chart-tooltip-border: rgba(255, 255, 255, 0.10);
  --nova-chart-empty-stroke:   rgba(255, 255, 255, 0.12);

  /* SUB-GAP-027/030 (dark) — glass-family chrome */
  --nova-glass-surface:        rgba(19, 19, 22, 0.82);
  --nova-glass-surface-strong: rgba(19, 19, 22, 0.92);
  --nova-glass-border:         rgba(255, 255, 255, 0.08);
  /* --nova-glass-blur stays mode-invariant. */

  /* SUB-GAP-037 (dark) — segmented control */
  --nova-segmented-track:      rgba(255, 255, 255, 0.04);
  --nova-segmented-thumb:      var(--nova-neutral-700);
  --nova-segmented-thumb-ring: rgba(255, 255, 255, 0.10);

  /* SUB-GAP-040 (dark) — ring track quieter on dark canvas */
  --nova-ring-track:           rgba(255, 255, 255, 0.08);

  /* --nova-treemap-ink-flip-at, --nova-chart-stroke, --nova-chart-dash
     stay mode-invariant. */

  /* ───────── v2.14 (dark) ───────── */

  /* GAP-006 (dark): grid selection re-points to the pale-blue dark primary
     and steps the alpha up so the wash registers on the dark canvas.
     --nova-radius-xs and the --nova-lightbox-* family are mode-invariant,
     no dark override. --nova-grid-header-bg follows --nova-canvas-rail. */
  --nova-grid-selection:        rgba(138, 180, 248, 0.18);
  --nova-grid-selection-border: rgba(138, 180, 248, 0.45);
  /* GAP-009 (dark): resting voyage-bar wash re-points to the pale-blue dark
     primary and steps the alpha up so it registers on the dark lane. */
  --nova-horizon-bar:           rgba(138, 180, 248, 0.22);
}

/* Base */
body {
  font-family: var(--nova-font-sans);
  font-size: 14px;
  line-height: 1.6;
  color: var(--nova-text-primary);
  background: var(--nova-canvas);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Utility — signature Nova labels. Use as a class on spans/divs. */
.nova-label {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--nova-text-muted);
}

.nova-mono,
code {
  font-family: var(--nova-font-mono);
  font-variant-numeric: tabular-nums;
}

.tabular-nums { font-variant-numeric: tabular-nums; }

/* §SUB-GAP-005 — panel typography utilities, promoted from colors_and_type.css. */
.nova-panel-title {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--nova-text-primary);
  margin: 0;
}
.nova-panel-subtitle {
  font-size: 13px;
  font-weight: 400;
  color: var(--nova-text-secondary);
  margin: 2px 0 0;
}
.nova-panel-meta {
  font-family: var(--nova-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--nova-text-muted);
  font-variant-numeric: tabular-nums;
}

/* ============================================================================ */
/* GAP-065 — FORMS.md layout utilities. The one form layout primitive + the      */
/* action row + section rhythm. Layout only (no colour); tokens only; additive.  */
/* See specs/FORMS.md.                                                           */
/* ============================================================================ */

/* .nova-form-grid — responsive field grid. Mobile-first: 1-up → 2-up (--nova-bp-md)
   → 3-up (--nova-bp-lg). data-span widens a cell; put it on a <div> grid child. */
.nova-form-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--nova-space-md);
    align-items: start;
}
@media (min-width: 768px) {   /* --nova-bp-md → 2-up */
    .nova-form-grid { grid-template-columns: repeat(2, 1fr); }
    .nova-form-grid > [data-span="2"],
    .nova-form-grid > [data-span="3"] { grid-column: span 2; }
}
@media (min-width: 1024px) {  /* --nova-bp-lg → 3-up */
    .nova-form-grid { grid-template-columns: repeat(3, 1fr); }
    .nova-form-grid > [data-span="2"] { grid-column: span 2; }
    .nova-form-grid > [data-span="3"] { grid-column: span 3; }
}

/* .nova-form-actions — a form's button row. Wrapping, vertically centred,
   --nova-space-sm gap. Primary-first, left-aligned by default; --end right-aligns. */
.nova-form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--nova-space-sm);
}
.nova-form-actions--end { justify-content: flex-end; }

/* .nova-form — section rhythm. Stacks a form's blocks (field grid, section title,
   error row, action row) with --nova-space-lg. One wrapper, not hand-rolled divs. */
.nova-form {
    display: flex;
    flex-direction: column;
    gap: var(--nova-space-lg);
}

/* ============================================================================ */
/* Consolidated from Nova app.css during DS migration (2026-06-11).             */
/* Semantic Stitch MD3 + chrome/canvas/signal/typography tokens that lived in   */
/* the app stylesheet. Hoisted so the package token file is self-contained.    */
/* ============================================================================ */
:root {
    --nova-border: #c2c6d7;
    --nova-border-light: rgba(0, 0, 0, 0.06);
    --nova-canvas-surface: var(--nova-neutral-0);
    --nova-chrome-accent: var(--nova-blue-500);
    --nova-chrome-elevation: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 3px rgba(0, 0, 0, 0.03);
    --nova-chrome-surface-hover: rgba(0, 0, 0, 0.03);
    --nova-chrome-text: var(--nova-ink-900);
    --nova-chrome-text-muted: var(--nova-neutral-500);
    --nova-error: #ba1a1a;
    --nova-error-container: #ffdad6;
    --nova-radius-lg: 8px;
    --nova-severity-critical-bg: rgba(239, 68, 68, 0.08);
    --nova-severity-info-bg: rgba(20, 110, 244, 0.06);
    --nova-severity-info-text: var(--nova-text);
    --nova-severity-success-bg: rgba(34, 197, 94, 0.08);
    --nova-severity-warning-bg: rgba(245, 158, 11, 0.08);
    --nova-shadow-ambient: 0 4px 20px rgba(25, 28, 30, 0.06);
    --nova-shadow-md: 0 2px 8px rgba(0, 0, 0, 0.06);
    --nova-shadow-primary: none;
    --nova-signal-cta-bg: #FFFFFF;
    --nova-signal-cta-ink: var(--nova-ink-900);
    --nova-spacing-2xl: 48px;
    --nova-spacing-lg: 24px;
    --nova-spacing-md: 16px;
    --nova-spacing-sm: 8px;
    --nova-spacing-xl: 32px;
    --nova-spacing-xs: 4px;
    --nova-surface: #f7f9fc;
    --nova-surface-card: #ffffff;
    --nova-text: #191c1e;
    --nova-text-badge: 0.625rem;
    --nova-text-kpi-value: 2.25rem;
    --nova-text-kpi-value-lg: 3rem;
    --nova-text-label: 0.625rem;
    --nova-text-page-title: 1.875rem;
    --nova-text-section: 1rem;
    --nova-track-normal: 0;
    --nova-track-tight: -0.02em;
    --nova-track-wide: 0.02em;
    --nova-track-wider: 0.08em;
    --nova-track-widest: 0.15em;
    --nova-transition-normal: 250ms cubic-bezier(0.4, 0, 0.2, 1);
    --nova-warning: #a23b00;
    --nova-weight-black: 700;
    --nova-weight-bold: 700;
    --nova-weight-medium: 500;
    --nova-weight-regular: 400;
    --nova-weight-semi: 600;
}

[data-theme="dark"],
.mud-theme-dark {
    --nova-border: #48474b;
    --nova-border-light: rgba(255, 255, 255, 0.05);
    --nova-canvas-surface: var(--nova-neutral-900);
    --nova-chrome-accent: var(--nova-blue-300);
    --nova-chrome-elevation: 0 1px 2px rgba(0, 0, 0, 0.35), 0 1px 3px rgba(0, 0, 0, 0.25);
    --nova-chrome-surface-hover: rgba(255, 255, 255, 0.04);
    --nova-chrome-text: var(--nova-text-primary);
    --nova-chrome-text-muted: var(--nova-text-muted);
    --nova-error: #f28b82;
    --nova-error-container: rgba(242, 139, 130, 0.15);
    --nova-severity-critical-bg: rgba(255, 113, 108, 0.1);
    --nova-severity-info-bg: rgba(125, 175, 255, 0.1);
    --nova-severity-info-text: var(--nova-text);
    --nova-severity-success-bg: rgba(74, 222, 128, 0.1);
    --nova-severity-warning-bg: rgba(251, 191, 36, 0.1);
    --nova-shadow-md: 0 2px 8px rgba(0, 0, 0, 0.3);
    --nova-signal-cta-bg: var(--nova-ink-900);
    --nova-signal-cta-ink: #FFFFFF;
    --nova-surface: #131316;
    --nova-surface-card: #19191d;
    --nova-text: #e8eaed;
    --nova-warning: #fbbf24;
    --nova-accent: var(--nova-blue-300);
}
