/* Patroller AI — design tokens. CANONICAL.
   Colour: the Tracer palette adopted whole, measured from tracerml.ai live CSS 2026-07-30.
   Type:   Saira display · Pretendard Std body · IBM Plex Mono data.
   Scale:  re-derived 2026-08-11 on a double-stranded modular scale (Bringhurst §8.3) —
           display strand a perfect fourth (4:3, seed 40px); text strand the traditional
           foundry sizes 10-11-12-14-16-18-21-24 (geometric mean 1.133 ≈ a major second).
   Layers: PRIMITIVE (raw, the only place a literal appears) → SEMANTIC (aliased, meaning-bound).
   tokens/tokens.json mirrors this file exactly. Change both together. */

@import url('https://fonts.googleapis.com/css2?family=Saira:wght@300;400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap');
/* Pretendard Std (Latin), self-hosted — a remote @import is not guaranteed to
   resolve in every render sandbox, and a silently-substituted body face is the
   kind of drift that is hard to spot later. */
@font-face {
  font-family: 'Pretendard Std';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('./fonts/PretendardStd-Regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Pretendard Std';
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  src: url('./fonts/PretendardStd-SemiBold.woff2') format('woff2');
}

:root {

  /* ========================================================================
     PRIMITIVES — raw values. The only place a literal colour appears.
     Never reference a --hue-*/--paper-*/--ink-* directly from component CSS.
     ======================================================================== */

  /* paper ramp — measured (Tracer) */
  --paper-0:    #FFFDF7;   /* brightest, raised */
  --paper-100:  #F2EFE7;   /* the ground */
  --paper-200:  #E8E3D8;   /* recessed */
  --paper-400:  #B4B1A8;   /* hairline rule */
  --paper-600:  #68665F;   /* muted type / strong rule */

  /* ink ramp — measured (Tracer ink + paper), plus three steps promoted from
     the .on-ink scope where they previously lived as unnamed literals. */
  --ink-950:    #11110F;   /* the ink ground */
  --ink-900:    #1A1A17;   /* recessed on ink */
  --ink-800:    #262622;   /* raised on ink   (was hardcoded in .on-ink) */
  --ink-600:    #3A3A34;   /* hairline on ink */
  --ink-300:    #96948B;   /* muted on ink    (was hardcoded in .on-ink) */
  --ink-200:    #C4C1B6;   /* soft on ink     (was hardcoded in .on-ink) */
  --ink-100:    #282824;   /* soft type on paper */

  /* accent — measured (Tracer) */
  --acid-500:   #C9FF5A;

  /* signal hues — measured (Tracer); the meanings are Patroller's own */
  --hue-red:    #F04444;
  --hue-orange: #FF7A38;
  --hue-green:  #72A778;
  --hue-blue:   #38BDF8;
  --hue-violet: #A78BFA;

  /* ========================================================================
     SEMANTIC — colour. Meaning-bound. This is what components consume.
     ======================================================================== */

  /* ---- ground: paper. deck, website, documents, reports ---- */
  --surface:            var(--paper-100);
  --surface-alt:        var(--paper-200);
  --surface-bright:     var(--paper-0);
  --on-surface:         var(--ink-950);
  --on-surface-soft:    var(--ink-100);
  --on-surface-muted:   var(--paper-600);

  /* ---- rules. --outline is DECORATIVE (1.60:1) — container edges, dividers,
     the fine grid that is a large part of what this system looks like. It is
     unchanged. --outline-strong (5.00:1) is for a rule that CARRIES MEANING:
     a selected edge, a focus ring, a meter track, a match boundary. ---- */
  --outline:            var(--paper-400);
  --outline-strong:     var(--paper-600);

  /* ---- ground: ink. console, video, evidence ---- */
  --surface-inverse:    var(--ink-950);
  --surface-inverse-alt:var(--ink-900);
  --on-surface-inverse: var(--paper-100);
  --outline-inverse:    var(--ink-600);

  /* ---- accent. FILL on paper (1.02:1 as text — never type). Text on ink is fine (16.14:1).
     Inside product UI it means MATCH — and nothing else. ---- */
  --primary:            var(--acid-500);
  --on-primary:         var(--ink-950);

  /* ---- semantic. meaning only, never decoration ---- */
  --signal-red:         var(--hue-red);     /* alarm, critical            */
  --signal-orange:      var(--hue-orange);  /* caution, needs review      */
  --signal-green:       var(--hue-green);   /* clear, confirmed, dismissed*/
  --signal-blue:        var(--hue-blue);    /* system, informational      */
  --signal-violet:      var(--hue-violet);  /* secondary, reserved        */
  /* all five FAIL as text on --surface (1.86-3.25:1). Fills on paper; text on ink only.
     On --surface-alt even red drops to 2.92:1 — no signal is legal as a meaningful
     graphic inside a recessed band. Use --surface or --surface-bright for state rules. */

  /* ---- data-visualisation series. Order is a SEQUENCE, not a set of meanings:
     red sits last so a neutral series never opens on alarm.
     BINDING: on paper these run 1.86-3.25:1 and adjacent pairs are below 3:1, so
     hue alone must never carry series identity there — pair every series with its
     --series-N-dash pattern, or scope the chart to the ink ground where all five
     clear 5.05-8.82:1. ---- */
  --series-1: var(--hue-blue);
  --series-2: var(--hue-orange);
  --series-3: var(--hue-green);
  --series-4: var(--hue-violet);
  --series-5: var(--hue-red);
  --series-1-dash: none;
  --series-2-dash: 6 3;
  --series-3-dash: 2 3;
  --series-4-dash: 10 4;
  --series-5-dash: 6 3 2 3;

  /* ---- product-UI semantics. acid = MATCH, and only match. ---- */
  --match-tag-bg:        var(--primary);          /* fill; ink type on it, 16.14:1 */
  --match-tag-fg:        var(--on-primary);
  --match-box-border:    var(--outline-strong);   /* on paper: acid is 1.02:1 and cannot carry a boundary */
  --match-strength-fill: var(--outline-strong);
  --match-strength-track:var(--surface-alt);

  /* ========================================================================
     TYPE — three faces, three jobs.
     ======================================================================== */

  --font-display: 'Saira', system-ui, sans-serif;
  --font-body:    'Pretendard Std', system-ui, sans-serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, monospace;

  /* weights — only what is loadable. There is no 700; do not synthesise one. */
  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;

  /* ---- display strand · Saira · perfect fourth (1.333). Projected surfaces. ---- */
  --display-2xl: 500 169px/1.02 var(--font-display);
  --display-xl:  500 126px/1.03 var(--font-display);
  --display-lg:  500 95px/1.04  var(--font-display);
  --display-md:  500 71px/1.06  var(--font-display);
  --display-sm:  500 53px/1.08  var(--font-display);

  /* ---- headline · Saira · the band that did not exist. Section headings,
     card titles, stats — the sizes that were being invented every time. ---- */
  --headline-lg: 600 40px/1.12 var(--font-display);
  --headline-md: 600 30px/1.18 var(--font-display);
  --headline-sm: 600 24px/1.25 var(--font-display);

  /* ---- title · Pretendard · sub-headings inside prose ---- */
  --title-lg:    600 21px/1.35 var(--font-body);
  --title-md:    600 18px/1.45 var(--font-body);

  /* ---- body · Pretendard · traditional foundry sizes ---- */
  --body-xl:     400 24px/1.50 var(--font-body);  /* the deck-legal prose step */
  --body-lg:     400 18px/1.62 var(--font-body);
  --body-md:     400 16px/1.70 var(--font-body);
  --body-sm:     400 14px/1.72 var(--font-body);
  --body-xs:     400 12px/1.75 var(--font-body);

  /* ---- data · IBM Plex Mono ---- */
  --mono-lg:     400 16px/1.50 var(--font-mono);
  --mono-md:     400 14px/1.55 var(--font-mono);
  --mono-sm:     400 12px/1.60 var(--font-mono);
  --mono-xs:     400 11px/1.65 var(--font-mono);

  /* ---- labels · uppercase, tracked. Legal ONLY at these weights + tracking. ---- */
  --label-lg:    600 14px/1.30 var(--font-body);
  --label-md:    600 12px/1.35 var(--font-body);
  --label-sm:    600 10px/1.40 var(--font-body);

  /* ---- eyebrows · uppercase mono ---- */
  --eyebrow-lg:  500 24px/1.20 var(--font-mono);
  --eyebrow-md:  500 12px/1.35 var(--font-mono);
  --eyebrow-sm:  500 10px/1.40 var(--font-mono);

  /* ---- tracking. The font shorthand cannot carry letter-spacing; pair these.
     Display tracking is POSITIVE (+0.01em) — shipped doctrine. Tight negative
     display tracking is the generic-SaaS signature and is not this brand. ---- */
  --tracking-display:  0.01em;
  --tracking-headline: 0.006em;
  --tracking-title:    0em;
  --tracking-body:     0em;
  --tracking-label-lg: 0.10em;
  --tracking-label:    0.14em;
  --tracking-label-sm: 0.18em;
  --tracking-eyebrow:  0.12em;
  --tracking-eyebrow-sm: 0.16em;
  --tracking-mono:     0.02em;
  --tracking-mono-sm:  0.04em;
  --tracking-mono-xs:  0.06em;

  /* ========================================================================
     SPACE — a 4px base unit. Raw steps are primitives; use the SEMANTIC roles.
     ======================================================================== */

  --space-4:   4px;    --space-8:   8px;    --space-12:  12px;
  --space-16:  16px;   --space-20:  20px;   --space-24:  24px;
  --space-32:  32px;   --space-40:  40px;   --space-48:  48px;
  --space-56:  56px;   --space-64:  64px;   --space-80:  80px;
  --space-96:  96px;   --space-128: 128px;  --space-160: 160px;

  /* inset — padding inside a container */
  --space-inset-2xs: var(--space-4);
  --space-inset-xs:  var(--space-8);
  --space-inset-sm:  var(--space-12);
  --space-inset-md:  var(--space-16);   /* THE DEFAULT card / panel padding */
  --space-inset-lg:  var(--space-24);
  --space-inset-xl:  var(--space-32);
  --space-inset-2xl: var(--space-48);

  /* squish — asymmetric vertical/horizontal padding. Buttons, chips, cells. */
  --space-squish-xs: var(--space-4) var(--space-12);
  --space-squish-sm: var(--space-8) var(--space-16);
  --space-squish-md: var(--space-12) var(--space-24);

  /* stack — vertical gap between siblings */
  --space-stack-3xs: var(--space-4);
  --space-stack-2xs: var(--space-8);
  --space-stack-xs:  var(--space-12);
  --space-stack-sm:  var(--space-16);
  --space-stack-md:  var(--space-24);   /* THE DEFAULT paragraph gap */
  --space-stack-lg:  var(--space-32);
  --space-stack-xl:  var(--space-48);

  /* inline — horizontal gap between siblings */
  --space-inline-2xs: var(--space-4);
  --space-inline-xs:  var(--space-8);
  --space-inline-sm:  var(--space-12);
  --space-inline-md:  var(--space-16);  /* THE DEFAULT button-group gap */
  --space-inline-lg:  var(--space-24);
  --space-inline-xl:  var(--space-32);

  /* section — between major regions */
  --space-section-sm: var(--space-48);
  --space-section-md: var(--space-64);  /* THE DEFAULT region break */
  --space-section-lg: var(--space-96);
  --space-section-xl: var(--space-128);

  --space-gutter:  var(--space-32);
  --container:     1240px;
  --measure-body:  68ch;   /* prose max-width */
  --measure-lead:  56ch;   /* lede / intro */

  /* legacy aliases — the eight original steps, kept so existing markup still
     resolves. Prefer the semantic roles above in anything new. */
  --space-xs: var(--space-8);   --space-sm: var(--space-12);
  --space-md: var(--space-16);  --space-lg: var(--space-24);
  --space-xl: var(--space-32);  --space-2xl: var(--space-48);
  --space-3xl: var(--space-64); --space-4xl: var(--space-96);

  /* ========================================================================
     SHAPE — effectively square. Nothing between 1px and a full pill.
     Division is done with RULES and a change of ground, never with shadow.
     There are deliberately no elevation/shadow tokens.
     ======================================================================== */

  --radius-sm:   1px;      /* cards, panels, inputs, buttons, chips, thumbnails,
                              modals, meters, bounding boxes, avatars — everything */
  --radius-full: 9999px;   /* status dots and round mark chips ONLY, and only where
                              computed width equals computed height */

  /* rule weights — the 1px hairline is the identity and was untokenised until now */
  --rule-hairline: 1px;    /* every container edge and divider. The default. */
  --rule-emphasis: 2px;    /* a rule that is CONTENT, not a container edge */
  --rule-signal:   3px;    /* a coloured state marker on the edge of a surface */
  --rule-accent:   6px;    /* the accent rule beside a pulled quote or a lede */

  /* ========================================================================
     MOTION — for legibility, not delight. No ambient, nothing continuous.
     ======================================================================== */

  --duration-instant: 0ms;    /* @kind other */ /* evidence frames never animate */
  --duration-fast:   120ms;   /* @kind other */ /* state change */
  --duration-base:   240ms;   /* @kind other */ /* entering or leaving */
  --duration-slow:   560ms;   /* @kind other */ /* full surface transition */
  --ease:            cubic-bezier(.19, 1, .22, 1);  /* @kind other */
  --ease-out:        cubic-bezier(.19, 1, .22, 1);  /* @kind other */ /* entering */
  --ease-in:         cubic-bezier(.55, 0, 1, .45);  /* @kind other */ /* leaving, faster */
  --stagger-step:    40ms;    /* @kind other */ /* between items in a result list */
  --stagger-cap:     240ms;   /* @kind other */ /* total sequence never exceeds this */
}

/* numbers align everywhere: timestamps, camera IDs, durations, confidence */
body { font-variant-numeric: tabular-nums lining-nums; }

/* the ink ground as a scope, so one system covers both.
   Every value here is now a named primitive — nothing hardcoded. */
.on-ink {
  --surface: var(--ink-950);
  --surface-alt: var(--ink-900);
  --surface-bright: var(--ink-800);
  --on-surface: var(--paper-100);
  --on-surface-soft: var(--ink-200);
  --on-surface-muted: var(--ink-300);
  --outline: var(--ink-600);
  --outline-strong: var(--ink-300);
  --match-box-border: var(--primary);      /* acid reaches 16.14:1 on ink */
  --match-strength-fill: var(--primary);
  --match-strength-track: var(--ink-600);
  background: var(--surface);
  color: var(--on-surface);
}

/* Reduced motion: kill travel and ambient, keep the state change legible.
   A blanket `animation:none` also removes transitions that aid comprehension,
   so durations collapse toward instant rather than being deleted outright. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast: 1ms;
    --duration-base: 1ms;
    --duration-slow: 1ms;
    --stagger-step: 0ms;
    --stagger-cap: 0ms;
  }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}
