/* ============================================================================
   DCC DESIGN TOKENS  ·  v2.0  ·  11 Sep 2026
   ----------------------------------------------------------------------------
   The single source of truth for colour, type, space, radius and elevation.
   Loaded BEFORE styles.css. Every value in styles.css and every colour
   interpolated into markup in app.js resolves through a token defined here.

   WHY THIS FILE EXISTS
   The previous token layer defined 24 custom properties in one flat :root
   block. The stylesheet and app.js between them referenced 38 — the extra 14
   were never defined anywhere, and 45 of those references carried no fallback,
   so they silently resolved to the property's initial value. That is why the
   dashboard sparkline, the dashboard activity-feed dots and the selected
   basemap button all render invisible today. See DCC-UI-REVIEW §1.

   THE RULES
   1. Two layers. PRIMITIVES are the raw palette and are never used directly by
      a component. SEMANTIC tokens name a role and are the only thing components
      may reference. Changing a theme means re-pointing semantic tokens at
      different primitives — never editing a component.
   2. No component may contain a hex literal. The two documented exceptions are
      map-renderer paint options (Google/MapLibre cannot resolve CSS custom
      properties) and documents generated into a foreign document context
      (the job sheet, the signature print window), which carry their own :root.
   3. Every token referenced must be defined here. `scripts/ui_audit.py`
      Rule 8 enforces this — see the review, Track 1.
   4. Dark is the product. Light is a setting. The dark block is live and swept;
      the light block is complete and correct but not yet swept through
      styles.css (≈300 literals still hardcode dark surfaces). See §LIGHT below.

   COLOUR PROVENANCE
   Surfaces and brand come from DCC Brand Pack/colours/dcc-colours.json —
   Carbon, Graphite, DCC Orange, Orange Deep, Paper, Ink, Slate, Mist. The app
   had drifted off the brand pack (it shipped #E86828, the pack says #ED6C2D;
   it shipped #0B0D10, the pack says Carbon #15181B). This realigns.
   Status and chart colours are NOT the raw Signal values: the pack's Signal
   set was drawn for ink on paper and falls below the legibility floor on a
   dark surface. They are re-stepped for each surface and validated — every
   value below carries its measured WCAG ratio against its own surface.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1 · PRIMITIVES — the raw palette. Components never reference these.
   ------------------------------------------------------------------------ */
:root {
  /* Brand — DCC Brand Pack */
  --p-orange:        #ED6C2D;   /* DCC Orange   — the mark, primary action    */
  --p-orange-lift:   #F5854A;   /* hover/active on dark                       */
  --p-orange-deep:   #C2551C;   /* Orange Deep  — orange as text on light     */
  --p-orange-tint:   #FBEADD;   /* Orange Tint  — soft fill on light          */

  /* Neutrals, dark end — Carbon/Graphite extended into a 3–6% elevation ramp */
  --p-carbon:        #15181B;   /* Brand Carbon — app canvas                  */
  --p-graphite:      #1D2125;   /* Brand Graphite — cards, panels             */
  --p-slate-900:     #101317;   /* recessed: sidebar, wells                   */
  --p-slate-800:     #262B31;   /* raised: hover, inputs                      */
  --p-slate-700:     #2F353C;   /* raised-2: pressed, selected                */
  --p-slate-600:     #3A424B;   /* strong rule                                */
  --p-slate-500:     #2C333A;   /* default rule                               */
  --p-slate-450:     #232930;   /* soft rule (inside cards)                   */

  /* Neutrals, light end — Ink/Slate/Mist/Paper */
  --p-ink:           #23272C;
  --p-slate:         #565D64;
  --p-mist:          #8A9199;
  --p-paper:         #F7F4EF;
  --p-white:         #FFFFFF;
  --p-paper-2:       #FFFFFF;
  --p-paper-sunk:    #EFECE6;
  --p-rule-light:    #DDD8D0;
  --p-rule-light-2:  #E8E4DD;

  /* Text steps, dark surfaces. Body sits at ~92% white, not 100% — pure white
     on a dark ground haloes and fatigues over a shift. */
  --p-dk-text-1:     #F5F8FB;   /* hero figures only                          */
  --p-dk-text-2:     #E8ECF1;   /* body            13.7:1 on Graphite         */
  --p-dk-text-3:     #A8B2BD;   /* secondary        7.5:1 on Graphite         */
  --p-dk-text-4:     #828C99;   /* muted / meta     4.8:1 on Graphite         */

  /* Status, dark surfaces. Re-stepped from the Signal set for Graphite; each
     sits inside OKLCH L 0.48–0.67 and clears 3:1. Pairs that matter under
     deuteranopia (ok↔bad) cannot be separated by hue at all, which is why
     status in this product is ALWAYS colour + icon + word, never colour alone.
     `scripts/ui_audit.py` Rule 9 enforces that. */
  --p-dk-ok:         #32A069;   /* mark  4.9:1  */
  --p-dk-ok-text:    #57C98E;   /* label 7.8:1  */
  --p-dk-warn:       #BD8C12;   /* mark  5.3:1  */
  --p-dk-warn-text:  #E0A93A;   /* label 7.6:1  */
  --p-dk-bad:        #D84F49;   /* mark  4.0:1  */
  --p-dk-bad-text:   #F07B74;   /* label 6.0:1  */
  --p-dk-info:       #4A90D9;   /* mark  4.9:1  */
  --p-dk-info-text:  #74AEE8;   /* label 6.9:1  */

  /* Status, light surfaces. The Signal set, with text steps darkened to clear
     4.5:1 on Paper — the raw Signal Green is 3.9:1 and fails as label text. */
  --p-lt-ok:         #2E8B57;
  --p-lt-ok-text:    #20744A;
  --p-lt-warn:       #B07A18;
  --p-lt-warn-text:  #7A5610;
  --p-lt-bad:        #B4423A;
  --p-lt-bad-text:   #98352E;
  --p-lt-info:       #2F6FA8;
  --p-lt-info-text:  #255A8A;

  /* Categorical chart hues. Six slots, fixed order, assigned by entity and
     never cycled — a 7th series folds into "Other" or becomes small multiples.
     The ORDER is the colour-blind-safety mechanism: candidate orderings were
     enumerated and only those clearing every adjacent gate kept. This one is
     worst-adjacent CVD ΔE 8.4, normal-vision ΔE 19.3 on Graphite.
     Brand orange is deliberately NOT a slot — it means "action" and "now", and
     a chart series wearing it would collide with every button on the screen. */
  --p-cat-dk-1:      #3987e5;   /* blue    */
  --p-cat-dk-2:      #199e70;   /* aqua    */
  --p-cat-dk-3:      #c98500;   /* yellow  */
  --p-cat-dk-4:      #d55181;   /* magenta */
  --p-cat-dk-5:      #9085e9;   /* violet  */
  --p-cat-dk-6:      #e66767;   /* red     */
  --p-cat-lt-1:      #2a78d6;
  --p-cat-lt-2:      #1baf7a;
  --p-cat-lt-3:      #eda100;
  --p-cat-lt-4:      #e87ba4;
  --p-cat-lt-5:      #4a3aa7;
  --p-cat-lt-6:      #e34948;
}

/* ---------------------------------------------------------------------------
   2 · SEMANTIC — DARK (the default). This is what components reference.
   ------------------------------------------------------------------------ */
:root,
:root[data-theme="dark"] {
  color-scheme: dark;

  /* --- surfaces. Elevation on a dark ground is a lightness step, not a
         shadow: shadows have nothing to darken against and read as smudge. --- */
  --bg:              var(--p-carbon);      /* app canvas                      */
  --bg-sunk:         var(--p-slate-900);   /* sidebar, wells, code blocks     */
  --surface:         var(--p-graphite);    /* card / panel                    */
  --surface-2:       var(--p-slate-800);   /* hover, input, raised            */
  --surface-3:       var(--p-slate-700);   /* pressed, selected               */
  --surface-overlay: #22272D;              /* drawer, modal, popover          */
  --scrim:           rgba(6,8,10,.66);

  /* --- rules --- */
  --line:            var(--p-slate-500);
  --line-soft:       var(--p-slate-450);
  --line-strong:     var(--p-slate-600);

  /* --- ink --- */
  --text-hi:         var(--p-dk-text-1);
  --text:            var(--p-dk-text-2);
  --text-2:          var(--p-dk-text-3);
  --text-3:          var(--p-dk-text-4);
  /* Ink ON the brand fill. The brand pack says "White — text on dark & orange",
     but white on #ED6C2D measures 3.10:1 — a fail for 13px button text, on a
     compliance product. Carbon on the same orange is 5.76:1, and dark-on-amber
     is the convention every high-visibility marking in aviation already uses.
     A considered departure from the pack, recorded here with the numbers. */
  --text-on-brand:   var(--p-carbon);      /* 5.76:1 on --brand              */
  --text-on-status:  #FFFFFF;              /* 4.5:1+ on ok/warn/bad marks    */

  /* --- brand / action. ONE saturated accent. Orange means "this is the
         action" or "this is now". It is not decoration, and it is not a data
         series. Everything else on the screen is neutral or status. --- */
  --brand:           var(--p-orange);
  --brand-hover:     var(--p-orange-lift);
  --brand-text:      var(--p-orange);      /* 5.2:1 on Graphite — AA         */
  --brand-tint:      rgba(237,108,45,.13);
  --brand-line:      rgba(237,108,45,.42);
  --focus-ring:      rgba(237,108,45,.55);

  /* --- status --- */
  --ok:              var(--p-dk-ok);        --ok-text:   var(--p-dk-ok-text);
  --ok-tint:         rgba(50,160,105,.14);  --ok-line:   rgba(50,160,105,.30);
  --warn:            var(--p-dk-warn);      --warn-text: var(--p-dk-warn-text);
  --warn-tint:       rgba(224,169,58,.13);  --warn-line: rgba(224,169,58,.30);
  --bad:             var(--p-dk-bad);       --bad-text:  var(--p-dk-bad-text);
  --bad-tint:        rgba(216,79,73,.14);   --bad-line:  rgba(216,79,73,.32);
  /* The mark colour is stepped for a dark surface and carries white at only
     4.09:1 — fine as a 9px bar, not as the ground under button text. Solid
     fills that text sits ON get their own step. */
  --bad-solid:       #C4423C;               /* white 5.01:1                   */
  --info:            var(--p-dk-info);      --info-text: var(--p-dk-info-text);
  --info-tint:       rgba(74,144,217,.14);  --info-line: rgba(74,144,217,.30);

  /* --- charts --- */
  --chart-surface:   var(--surface);
  --chart-grid:      #272D34;              /* one step off the surface        */
  --chart-axis:      var(--p-dk-text-4);
  --chart-label:     var(--p-dk-text-3);
  --chart-1: var(--p-cat-dk-1); --chart-2: var(--p-cat-dk-2); --chart-3: var(--p-cat-dk-3);
  --chart-4: var(--p-cat-dk-4); --chart-5: var(--p-cat-dk-5); --chart-6: var(--p-cat-dk-6);
  --chart-other:     #6B747F;              /* the folded tail                 */
  --chart-series:    var(--p-cat-dk-1);    /* the default single series       */
  --chart-fill-from: rgba(57,135,229,.22);
  --chart-fill-to:   rgba(57,135,229,0);

  /* --- elevation. Dark cards lift by lightness; shadow is used only where
         something genuinely floats above the page (drawer, modal, popover). --- */
  --shadow-pop:      0 6px 20px rgba(0,0,0,.42);
  --shadow-overlay:  0 24px 64px rgba(0,0,0,.58);
  /* Risk-matrix bands (LOW / MED / HIGH / EXTREME) as label text. Four bands,
     not three, so they are their own tokens rather than ok/warn/bad. */
  --risk-low:        var(--p-dk-ok-text);
  --risk-med:        #E0C34A;
  --risk-high:       #F0A24A;
  --risk-ext:        var(--p-dk-bad-text);
  --risk-low-tint:   rgba(76,175,80,.16);   --risk-med-tint:  rgba(255,224,58,.16);
  --risk-high-tint:  rgba(255,160,0,.16);   --risk-ext-tint:  rgba(229,57,53,.18);
  --map-today:       #ED6C2D;   /* map renderers need literals; these are the
                                   tokens' values, kept here so one edit moves
                                   the map and the legend together */
  --map-upcoming:    #4A90D9;
}

/* ---------------------------------------------------------------------------
   3 · SEMANTIC — LIGHT.  §LIGHT
   Complete and correct, but NOT yet swept: styles.css still carries ~300 hex
   literals that assume a dark ground (76 × #fff alone), so switching to light
   today gives a mostly-correct page with white-on-white patches. The sweep is
   Batch 3 in the review. Until then the Appearance setting offers Dark and
   System-follows-dark, and Light is behind the `data-theme-preview` flag.
   Nothing below needs to change when the sweep happens — the sweep is purely
   "replace literal with the token it should always have been".
   ------------------------------------------------------------------------ */
:root[data-theme="light"] {
  color-scheme: light;

  --bg:              var(--p-paper);
  --bg-sunk:         var(--p-paper-sunk);
  --surface:         var(--p-white);
  --surface-2:       #F4F1EB;
  --surface-3:       #EAE6DE;
  --surface-overlay: var(--p-white);
  --scrim:           rgba(35,39,44,.38);

  --line:            var(--p-rule-light);
  --line-soft:       var(--p-rule-light-2);
  --line-strong:     #C9C3B9;

  --text-hi:         #15181B;
  --text:            var(--p-ink);
  --text-2:          var(--p-slate);
  --text-3:          #696F76;              /* Mist is 2.9:1 on Paper — far too
                                              low for meta text. 4.63:1       */
  --text-on-brand:   var(--p-carbon);      /* same reasoning as dark — 5.76:1 */
  --text-on-status:  #FFFFFF;

  --brand:           var(--p-orange);      /* fills only                      */
  --brand-hover:     #D65E23;              /* Orange Deep is 3.91:1 under
                                              Carbon — too dark to press ink
                                              onto. One step down from DCC
                                              Orange instead: 4.65:1.         */
  --brand-text:      #B84E18;              /* Orange Deep one step darker.
                                              Raw orange is 2.8:1 on Paper and
                                              Orange Deep itself is 4.15:1 —
                                              both fail. This is 4.62:1 on
                                              Paper, 5.07:1 on white. The pack's
                                              own rule stands: never raw orange
                                              as text on a light ground.      */
  --brand-tint:      var(--p-orange-tint);
  --brand-line:      rgba(194,85,28,.38);
  --focus-ring:      rgba(194,85,28,.50);

  --ok:              var(--p-lt-ok);        --ok-text:   var(--p-lt-ok-text);
  --ok-tint:         rgba(46,139,87,.10);   --ok-line:   rgba(46,139,87,.28);
  --warn:            var(--p-lt-warn);      --warn-text: var(--p-lt-warn-text);
  --warn-tint:       rgba(176,122,24,.10);  --warn-line: rgba(176,122,24,.28);
  --bad:             var(--p-lt-bad);       --bad-text:  var(--p-lt-bad-text);
  --bad-tint:        rgba(180,66,58,.09);   --bad-line:  rgba(180,66,58,.28);
  --bad-solid:       var(--p-lt-bad);       /* white 5.55:1                   */
  --info:            var(--p-lt-info);      --info-text: var(--p-lt-info-text);
  --info-tint:       rgba(47,111,168,.09);  --info-line: rgba(47,111,168,.28);

  --chart-surface:   var(--p-white);
  --chart-grid:      #EAE6DE;
  --chart-axis:      #6E757C;
  --chart-label:     var(--p-slate);
  --chart-1: var(--p-cat-lt-1); --chart-2: var(--p-cat-lt-2); --chart-3: var(--p-cat-lt-3);
  --chart-4: var(--p-cat-lt-4); --chart-5: var(--p-cat-lt-5); --chart-6: var(--p-cat-lt-6);
  --chart-other:     #98A0A8;
  --chart-series:    var(--p-cat-lt-1);
  --chart-fill-from: rgba(42,120,214,.18);
  --chart-fill-to:   rgba(42,120,214,0);

  --shadow-pop:      0 4px 14px rgba(35,39,44,.10);
  --shadow-overlay:  0 18px 48px rgba(35,39,44,.18);
  --risk-low:        var(--p-lt-ok-text);
  --risk-med:        #7A6200;
  --risk-high:       #9A4A00;
  --risk-ext:        var(--p-lt-bad-text);
  --risk-low-tint:   rgba(76,175,80,.14);   --risk-med-tint:  rgba(214,180,0,.16);
  --risk-high-tint:  rgba(255,160,0,.14);   --risk-ext-tint:  rgba(229,57,53,.12);
  --map-today:       #C2551C;
  --map-upcoming:    #2F6FA8;
}

/* System preference, when the operator has chosen "System". The guard lets an
   explicit Light stamp win over an OS set to dark. */
@media (prefers-color-scheme: light) {
  :root[data-theme="system"]:not([data-theme="dark"]) {
    color-scheme: light;
    /* Deliberately empty until the light sweep lands: following the OS into a
       half-swept light theme would be worse than staying dark. Batch 3 replaces
       this rule body with @import-equivalent of the [data-theme="light"] block. */
  }
}

/* ---------------------------------------------------------------------------
   4 · ALIASES — RETIRED in v1.191.0.
   The fourteen legacy names (--accent, --text-1, --card, --danger, --canvas,
   --panel/-2/-3, --ok-t/--warn-t/--bad-t/--info-t, --brand-2, --disp/--body/
   --mono) were renamed at every reference in styles.css and app.js. Nothing
   defines them any more, so a stray reference is caught by ui_audit.py Rule 10,
   which release_check.py now treats as a hard failure.
   ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
   5 · TYPE.  Eight steps, not thirty-two.
   The stylesheet currently uses 32 distinct font sizes including 8.5, 9.5,
   10.5, 11.5, 12.5, 13.5 and 14.5px. Half-pixel steps are invisible as
   hierarchy and expensive as consistency: 12px, 12.5px and 13px appear 108,
   104 and 87 times respectively and mean the same thing. This is the scale.
   ------------------------------------------------------------------------ */
:root {
  --font-display: 'Archivo', ui-sans-serif, system-ui, sans-serif;
  --font-body:    'Inter', ui-sans-serif, system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --t-micro:  10px;   /* table heads, eyebrows, keyboard hints — uppercase only */
  --t-xs:     11px;   /* pill text, chart axis labels, dense metadata           */
  --t-sm:     12px;   /* secondary text, captions, table sub-cells              */
  --t-base:   13px;   /* body, table cells, form values — the default           */
  --t-md:     14px;   /* emphasised body, primary cell text, card titles        */
  --t-lg:     16px;   /* section headings                                       */
  --t-xl:     20px;   /* page title, drawer title                               */
  --t-fig:    26px;   /* compact figures inside cards — stat tiles, totals     */
  --t-hero:   34px;   /* metric-tile figures                                    */
  --t-hero-lg:44px;   /* the single most important number on a screen           */

  --lh-tight: 1.15;   --lh-snug: 1.35;   --lh-base: 1.5;   --lh-loose: 1.65;
  --ls-caps:  .08em;  --ls-eyebrow: .14em;  --ls-tight: -.01em;

  --fw-normal: 400; --fw-medium: 500; --fw-semi: 600; --fw-bold: 700; --fw-black: 800;
}

/* ---------------------------------------------------------------------------
   6 · SPACE, RADIUS, MOTION.
   Radius: the stylesheet uses 20 distinct values (8, 10, 9, 12, 6, 20, 4, 3,
   14, 5, 16, 13, 2, 999, 7, 11, 22, 18, 100, 99). Five is enough, and a
   consistent radius is most of what separates "designed" from "assembled".
   ------------------------------------------------------------------------ */
:root {
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 20px; --s-6: 24px; --s-7: 32px; --s-8: 48px;

  --r-xs:   4px;    /* chips inside dense rows, bar ends                      */
  --r-sm:   6px;    /* inputs, small buttons, swatches                        */
  --r-md:   8px;    /* buttons, nav items, tiles inside cards                 */
  --r-lg:   12px;   /* cards, panels, drawers, modals                         */
  --r-full: 999px;  /* pills, avatars, tracks                                 */

  /* MOTION. Three durations, one curve — plus one for leaving and one for the
     single emphasised move (a switch knob, the nav marker). A product with
     fourteen durations and two easings, which is what v1.188.5 shipped, has no
     rhythm; rhythm is most of what separates "designed" from "assembled". */
  --dur-fast: 110ms;   /* hover, focus, press — must feel instant             */
  --dur-base: 170ms;   /* panels opening, rows expanding                      */
  --dur-slow: 260ms;   /* full-screen transitions                             */
  --ease:      cubic-bezier(.2,0,.2,1);   /* arriving and responding          */
  --ease-out:  cubic-bezier(.4,0,1,1);    /* leaving: no settle, just go      */
  --ease-emph: cubic-bezier(.2,0,0,1);    /* the one overshoot-free emphasis  */

  /* Travel. Short travel reads as "this belongs here"; long travel reads as
     "this flew in from somewhere". The shipping drawer travels a full panel
     height plus 96px in 280ms. */
  --rise:    10px;
  --rise-sm:  6px;

  --topbar-h: 60px;
  --sidebar-w: 236px;
}

/* Motion is a signal, not decoration: it is used to show where something came
   from, never to celebrate a hover. Anyone who has asked their OS to stop
   moving things gets a still interface. */
@media (prefers-reduced-motion: reduce) {
  :root { --dur-fast: 0ms; --dur-base: 0ms; --dur-slow: 0ms; }
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------------------
   7 · BASE RULES that belong with the tokens.
   ------------------------------------------------------------------------ */

/* Numerals. Every figure that sits in a column — table cells, axis ticks,
   spec rows, durations, registrations — gets equal-width digits so the
   columns stop shivering as data updates. Hero figures do NOT: tabular
   numerals make a large standalone number look loose. */
.mono, table td, table th, .tnum,
[class*="-val"], [class*="-v"], .spec .v, .kv {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.hero-figure, .dm-big, .dm-crew-big { font-variant-numeric: proportional-nums; }

/* One focus treatment, everywhere, visible on every surface. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* A themed scrollbar, so the one piece of chrome the browser draws stops
   being the brightest object on a dark screen. */
* { scrollbar-color: var(--line-strong) transparent; scrollbar-width: thin; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb {
  background: var(--line-strong); border-radius: var(--r-full);
  border: 3px solid transparent; background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }
