/* ============================================================================
   tokens.css — elite-ui canonical token sheet (light + dark, OKLCH)
   ----------------------------------------------------------------------------
   Load it first and UNLAYERED, so its :root values beat Tailwind's @layer theme
   defaults:  <link rel="stylesheet" href="tokens.css">  or  @import "./tokens.css";
   Pair it with <meta name="color-scheme" content="light dark"> and the no-flash
   head script from references/02-tokens-and-theming.md §11.

   Host resets: the base element styles at the end sit in @layer base, so ANY
   unlayered rule beats them, a host's own reset included (a Claude Artifact
   wraps the page in body { margin: 0; font: 14px system-ui }). In a fragment,
   restate body type and colour unlayered: page-starter.html's HOST-RESET GUARD.
   Dark-only pages use the DARK-ONLY block below; :root[data-motion="reduce"]
   forces the reduced-motion clamp (02 §7.1, §9.5).

   Three tiers. Components read tiers 2 and 3 only, never raw oklch():
     1 primitives  hue / chroma / depth knobs          --brand-h, --depth …
     2 semantic    shadcn roles + elite additions       --primary, --surface-2 …
     3 recipe      composed treatments                  --shadow-card …

   Re-skin in five moves, with no component edits:
     1 --neutral-h / --neutral-c    hue the greys lean toward, and how much
     2 --brand-h / --brand-c        the one signal hue; keep --brand-c inside the
                                    sRGB gamut table (02-tokens-and-theming.md §4)
     3 --brand-l / --brand-l-dark   fill lightness per theme. White labels need
       --on-brand / --on-brand-dark  L <= .54; ink labels need L >= .62; nothing
                                    passes in between, so pick a side
     4 --font-*, --display-*, --radius, --depth   type, shape, flat (0) to deep (1.5)
     5 re-verify contrast in both themes at 390 / 768 / 1440 (assets/ui-audit.js)

   Gotcha: derived tokens resolve on :root. Changing a knob on a subtree does NOT
   recolour that subtree; redeclare the semantic tokens on it instead.
   ========================================================================= */

/* ── Scheme: native controls, scrollbars, canvas and UA defaults follow it ─ */
:root                     { color-scheme: light dark; }
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

:root {
  /* ── Tier 1 · primitives (the only numbers a re-skin touches) ───────────── */
  --neutral-h: 75;                      /* warm stone; 250–260 reads cool graphite  */
  --neutral-c: 0.012;                   /* 0 pure grey · .006 crisp · .02 paper     */

  --brand-h: 160;                      /* jade: the single signal hue              */
  --brand-c: 0.11;                      /* sRGB max at L .52 for hue 160 ≈ .118     */
  --brand-l: 0.52;                      /* fill lightness, light theme              */
  --brand-l-dark: 0.74;                 /* fill lightness, dark theme               */
  --on-brand: oklch(0.99 0 0);                       /* label on the fill (light)   */
  --on-brand-dark: oklch(0.18 0.02 var(--brand-h));  /* label on the fill (dark)    */

  --chart-c: 0.11;                      /* sRGB-safe at L .6 except cyan h 180–220 (max ≈ .10, clips slightly) */
  --depth: 1;                           /* 0 flat brand · 1 default · 1.5 deep      */

  --display-weight: 600;                /* serif display faces: 400                 */
  --display-tracking: -0.022em;         /* h1 gets ×1.5; serif display: -0.01em     */
}

/* Tiers 2–3 and the scales. Everything here is derived, so it re-resolves on
   any element the selector matches: widen it to `:root, [data-scheme]` to get
   scoped light/dark bands (02-tokens-and-theming.md §2.4).                   */
:root {
  --neutral-c-lo:  calc(var(--neutral-c) * 0.35);  /* page + ink: tint fades at extremes */
  --neutral-c-mid: calc(var(--neutral-c) * 0.7);   /* surfaces, hairlines               */
  /* Scheme knobs: light values here, dark blocks override them. */
  --chart-l: 0.6;                                  /* categorical series lightness */
  --shadow-lch: 0.3 calc(var(--neutral-c) * 3) var(--neutral-h);  /* tinted shadow ink */
  --shadow-strength: 0.2;                          /* light: shadows whisper       */
  --highlight-strength: 4;                         /* light: bevels need tinted fills */
  --_s: calc(var(--shadow-strength) * var(--depth));     /* private alpha scales  */
  --_h: calc(var(--highlight-strength) * var(--depth));

  /* ── Tier 2 · semantic colour (shadcn contract + elite additions) ───────── */
  --background:             oklch(0.978 var(--neutral-c-lo) var(--neutral-h));
  --foreground:             oklch(0.205 var(--neutral-c-mid) var(--neutral-h));
  /* Elevation = lighter, in both themes. Light mode lets shadows do the work. */
  --surface-1:              oklch(0.99 calc(var(--neutral-c-lo) * 0.5) var(--neutral-h));
  --surface-2:              oklch(0.995 0 0);
  --surface-3:              oklch(1 0 0);
  --card:                   var(--surface-1);
  --card-foreground:        var(--foreground);
  --popover:                var(--surface-2);
  --popover-foreground:     var(--foreground);
  --primary:                oklch(var(--brand-l) var(--brand-c) var(--brand-h));
  --primary-foreground:     var(--on-brand);
  --secondary:              oklch(0.94 var(--neutral-c-mid) var(--neutral-h));
  --secondary-foreground:   var(--foreground);
  --muted:                  oklch(0.955 var(--neutral-c-mid) var(--neutral-h));
  --muted-foreground:       oklch(0.49 var(--neutral-c) var(--neutral-h));  /* ≥5.1:1 on bg, muted, accent */
  /* shadcn's --accent is a QUIET hover/selected surface, not the brand colour.
     The brand colour is --primary; its small-text twin is --accent-text.      */
  --accent:                 oklch(0.935 var(--neutral-c-mid) var(--neutral-h));
  --accent-foreground:      var(--foreground);
  --accent-text:            oklch(0.47 min(var(--brand-c) * 0.85, 0.11) var(--brand-h)); /* ≥5.7:1, any hue */
  --destructive:            oklch(0.55 0.2 27);       /* passes as error text AND as a fill */
  --destructive-foreground: oklch(0.99 0 0);
  --border:                 oklch(0.905 var(--neutral-c-mid) var(--neutral-h));  /* decorative hairline */
  --input:                  oklch(0.64 var(--neutral-c) var(--neutral-h));      /* ≥3:1: a field's only edge */
  --ring:                   oklch(0.55 min(var(--brand-c), 0.12) var(--brand-h)); /* focus, ≥4.3:1 */
  --overlay:                oklch(0.2 var(--neutral-c-mid) var(--neutral-h) / 0.42);  /* modal scrim */
  --chart-1: oklch(var(--chart-l) var(--chart-c) var(--brand-h));
  --chart-2: oklch(var(--chart-l) var(--chart-c) calc(var(--brand-h) + 72));
  --chart-3: oklch(var(--chart-l) var(--chart-c) calc(var(--brand-h) + 144));
  --chart-4: oklch(var(--chart-l) var(--chart-c) calc(var(--brand-h) + 216));
  --chart-5: oklch(var(--chart-l) var(--chart-c) calc(var(--brand-h) + 288));

  /* ── Type ─────────────────────────────────────────────────────────────────
     System stacks so nothing ships or phones home. Swap in self-hosted OFL
     families (02 §12); never proprietary brand faces.                         */
  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: var(--font-sans);
  --font-mono: ui-monospace, "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  /* Fluid steps, 320 → 1440px viewport. Minor third (1.2) at 16px up to
     perfect fourth (1.333) at 18px. rem floors keep zoom working; every
     max ≤ 2.5 × min, so 500% zoom still doubles the text (WCAG 1.4.4).       */
  --step--2: clamp(0.75rem, 0.736rem + 0.071vw, 0.8rem);    /* 12 → 12.8px  labels    */
  --step--1: clamp(0.844rem, 0.824rem + 0.098vw, 0.913rem); /* 13.5 → 14.6  meta, UI  */
  --step-0:  clamp(1rem, 0.964rem + 0.179vw, 1.125rem);     /* 16 → 18      body      */
  --step-1:  clamp(1.2rem, 1.114rem + 0.429vw, 1.5rem);     /* 19.2 → 24    lead, h4  */
  --step-2:  clamp(1.44rem, 1.28rem + 0.799vw, 2rem);       /* 23 → 32      h3        */
  --step-3:  clamp(1.728rem, 1.46rem + 1.338vw, 2.666rem);  /* 27.6 → 42.6            */
  --step-4:  clamp(2.074rem, 1.651rem + 2.112vw, 3.553rem); /* 33.2 → 56.8  h2        */
  --step-5:  clamp(2.488rem, 1.846rem + 3.209vw, 4.736rem); /* 39.8 → 75.8  h1, hero  */

  /* ── Space · 4px base (= Tailwind's p-4 at the default 16px root) ──────── */
  --space-1: 4px;   --space-2: 8px;   --space-3: 12px;  --space-4: 16px;
  --space-5: 20px;  --space-6: 24px;  --space-8: 32px;  --space-10: 40px;
  --space-12: 48px; --space-16: 64px; --space-24: 96px;
  --space-section: clamp(4rem, 2.857rem + 5.714vw, 8rem);   /* 64 → 128px */

  /* ── Radius · one knob. Nested corners: inner = outer − padding ───────── */
  --radius: 10px;
  --radius-sm: calc(var(--radius) - 4px);   /* negative results clamp to 0 */
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);
  --radius-full: 9999px;

  /* ── Depth · three layers per tier: top highlight, contact, ambient (1:2) ─
     xs chip/dot · sm resting button, card · md hover · lg popover · xl dialog.
     Alphas = knob × factor; dark theme raises shadows and lowers highlights.  */
  --shadow-xs: inset 0 1px 0 oklch(1 0 0 / calc(var(--_h) * 0.03)),
               0 1px 1px oklch(var(--shadow-lch) / calc(var(--_s) * 0.35)),
               0 1px 2px oklch(var(--shadow-lch) / calc(var(--_s) * 0.15));
  --shadow-sm: inset 0 1px 0 oklch(1 0 0 / calc(var(--_h) * 0.05)),
               0 1px 2px oklch(var(--shadow-lch) / calc(var(--_s) * 0.4)),
               0 2px 4px oklch(var(--shadow-lch) / calc(var(--_s) * 0.2));
  --shadow-md: inset 0 1px 0 oklch(1 0 0 / calc(var(--_h) * 0.08)),
               0 2px 4px oklch(var(--shadow-lch) / calc(var(--_s) * 0.5)),
               0 4px 8px oklch(var(--shadow-lch) / calc(var(--_s) * 0.25));
  --shadow-lg: inset 0 1px 0 oklch(1 0 0 / calc(var(--_h) * 0.12)),
               0 4px 8px oklch(var(--shadow-lch) / calc(var(--_s) * 0.6)),
               0 8px 16px oklch(var(--shadow-lch) / calc(var(--_s) * 0.3));
  --shadow-xl: inset 0 1px 0 oklch(1 0 0 / calc(var(--_h) * 0.18)),
               0 8px 16px oklch(var(--shadow-lch) / calc(var(--_s) * 0.7)),
               0 16px 32px oklch(var(--shadow-lch) / calc(var(--_s) * 0.35));

  /* ── Tier 3 · recipes. Themes change how things FEEL here, not in components.
     Rings are drawn with box-shadow (no layout shift). Under forced-colors,
     shadows vanish: give such elements `outline: 1px solid transparent`.       */
  --shadow-button: 0 0 0 1px oklch(var(--shadow-lch) / calc(var(--_s) * 0.55)),
                   inset 0 1px 0 oklch(1 0 0 / calc(var(--_h) * 0.05)),
                   0 1px 2px oklch(var(--shadow-lch) / calc(var(--_s) * 0.45));
  --shadow-card:   0 0 0 1px var(--border), var(--shadow-sm);
  --shadow-pop:    0 0 0 1px var(--border), var(--shadow-lg);

  /* ── Motion · exits ≈ 60% of the matching entrance: calc(var(--dur-3) * .6) */
  --dur-1: 100ms;   /* micro: press, toggle, colour                  */
  --dur-2: 160ms;   /* hover, small state changes                    */
  --dur-3: 240ms;   /* base: menus, popovers, tabs                   */
  --dur-4: 400ms;   /* panels, dialogs, section reveals              */
  --dur-5: 640ms;   /* the one hero sequence                         */
  --ease-out: cubic-bezier(.22, 1, .36, 1);        /* entrances                    */
  --ease-in: cubic-bezier(.4, 0, 1, 1);            /* exits                        */
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);     /* moves between on-screen spots */
  --ease-overshoot: cubic-bezier(.34, 1.56, .64, 1); /* playful pops; never errors  */
  /* linear: marquees, spinners, scroll-linked progress only. */
}

/* ── Dark · explicit choice ────────────────────────────────────────────────
   A remap, not an inversion: elevation by lightness (+.035 L per level),
   brand chroma ×.88, alpha hairlines, black shadows with faint highlights.
   KEEP IN SYNC with the media-query block below.                            */
:root[data-theme="dark"] {
  --background: oklch(0.155 var(--neutral-c-mid) var(--neutral-h));
  --foreground: oklch(0.955 var(--neutral-c-lo) var(--neutral-h));
  --surface-1: oklch(0.19 var(--neutral-c-mid) var(--neutral-h));
  --surface-2: oklch(0.225 var(--neutral-c-mid) var(--neutral-h));
  --surface-3: oklch(0.26 var(--neutral-c-mid) var(--neutral-h));
  --primary: oklch(var(--brand-l-dark) calc(var(--brand-c) * 0.88) var(--brand-h));
  --primary-foreground: var(--on-brand-dark);
  --secondary: oklch(0.27 var(--neutral-c-mid) var(--neutral-h));
  --muted: oklch(0.235 var(--neutral-c-mid) var(--neutral-h));
  --muted-foreground: oklch(0.72 var(--neutral-c-mid) var(--neutral-h));
  --accent: oklch(0.285 var(--neutral-c-mid) var(--neutral-h));
  --accent-text: oklch(0.8 min(var(--brand-c) * 0.85, 0.11) var(--brand-h));
  --destructive: oklch(0.68 0.19 25);
  --destructive-foreground: oklch(0.17 0.01 25);
  --border: oklch(1 0 0 / 0.09);
  --input: oklch(0.5 var(--neutral-c-mid) var(--neutral-h));
  --ring: oklch(0.74 min(var(--brand-c), 0.12) var(--brand-h));
  --overlay: oklch(0.08 0 0 / 0.66);
  --chart-l: 0.72;
  --shadow-lch: 0 0 0;
  --shadow-strength: 1;
  --highlight-strength: 1;
}

/* ── Dark · OS preference, unless the user pinned light. KEEP IN SYNC ────── */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --background: oklch(0.155 var(--neutral-c-mid) var(--neutral-h));
    --foreground: oklch(0.955 var(--neutral-c-lo) var(--neutral-h));
    --surface-1: oklch(0.19 var(--neutral-c-mid) var(--neutral-h));
    --surface-2: oklch(0.225 var(--neutral-c-mid) var(--neutral-h));
    --surface-3: oklch(0.26 var(--neutral-c-mid) var(--neutral-h));
    --primary: oklch(var(--brand-l-dark) calc(var(--brand-c) * 0.88) var(--brand-h));
    --primary-foreground: var(--on-brand-dark);
    --secondary: oklch(0.27 var(--neutral-c-mid) var(--neutral-h));
    --muted: oklch(0.235 var(--neutral-c-mid) var(--neutral-h));
    --muted-foreground: oklch(0.72 var(--neutral-c-mid) var(--neutral-h));
    --accent: oklch(0.285 var(--neutral-c-mid) var(--neutral-h));
    --accent-text: oklch(0.8 min(var(--brand-c) * 0.85, 0.11) var(--brand-h));
    --destructive: oklch(0.68 0.19 25);
    --destructive-foreground: oklch(0.17 0.01 25);
    --border: oklch(1 0 0 / 0.09);
    --input: oklch(0.5 var(--neutral-c-mid) var(--neutral-h));
    --ring: oklch(0.74 min(var(--brand-c), 0.12) var(--brand-h));
    --overlay: oklch(0.08 0 0 / 0.66);
    --chart-l: 0.72;
    --shadow-lch: 0 0 0;
    --shadow-strength: 1;
    --highlight-strength: 1;
  }
}

/* ── More contrast on request: hairlines become 3:1 edges, muted text firms up.
   Two selectors so this beats both dark blocks above in every theme state.  */
@media (prefers-contrast: more) {
  :root[data-theme], :root:not([data-theme]) {
    --border: var(--input);
    --muted-foreground: color-mix(in oklch, var(--foreground) 82%, var(--background));
  }
}

/* ── DARK-ONLY override (the dark-only block) · dark-first genres, reason in
   the read (02 §7.1; SKILL.md §8 item 3) ───────────────────────────────────
   Paste AFTER this sheet, unlayered; no light overrides, no theme control,
   colour-scheme meta "dark":
     :root[data-theme], :root:not([data-theme]) {
       color-scheme: dark;
       --neutral-h: …; …    the knobs, then EVERY token of the dark block above
     }
     @media (prefers-contrast: more) { …the block above, restated… }
   The pair matches every theme state at (0,2,0) and comes later: it beats the
   light :root, both dark blocks and a host's data-theme="light". A bare :root
   (0,1,0) loses to the dark blocks on a dark OS. It also beats the
   more-contrast block, hence the restatement.
   Pages using surfaces.css also restate its :root[data-theme="dark"] material
   block inside this pair (08 §1), or the layered light --spot-color, --grain-*
   and --glass-* knobs win on a light OS.                                    */

/* ── Reduced motion · global safety net ─────────────────────────────────────
   1ms, not 0: transitionend / animationend still fire, so JS state machines
   (closed → open → closing) never hang. Delays drop to 0 so staggers land at
   once; iterations drop to 1 so loops stop; scroll-driven animations fall back
   to the document timeline and finish at their end state. Content always
   reaches its final readable state.
   Choose the per-effect strategy in the component (crossfade / shrink amplitude
   / poster frame / snap / remove decor). Once an element carries its own
   reduced-motion branch (e.g. an opacity-only crossfade), add [data-motion-safe]
   to exempt it from this clamp (its children are not exempt). JS motion (WAAPI,
   Motion, GSAP, rAF) is not touched by CSS: gate it with prefersReducedMotion()
   from motion-kit.js.                                                       */
@media (prefers-reduced-motion: reduce) {
  *:not([data-motion-safe]),
  *:not([data-motion-safe])::before,
  *:not([data-motion-safe])::after {
    animation-duration: 1ms !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    animation-timeline: auto !important;
    transition-duration: 1ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
}
/* Forced: :root[data-motion="reduce"] (?motion=reduce in page-starter, rm-sim.js;
   motion-kit.js and gl-canvas.js read it too) shows the RM branches without OS
   emulation. A selector can't join a media query: KEEP IN SYNC.             */
:root[data-motion="reduce"],
:root[data-motion="reduce"] *:not([data-motion-safe]),
:root[data-motion="reduce"] *:not([data-motion-safe])::before,
:root[data-motion="reduce"] *:not([data-motion-safe])::after {
  animation-duration: 1ms !important;
  animation-delay: 0s !important;
  animation-iteration-count: 1 !important;
  animation-timeline: auto !important;
  transition-duration: 1ms !important;
  transition-delay: 0s !important;
  scroll-behavior: auto !important;
}
/* Separate rules: an unknown pseudo-element would void the rules above. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation-duration: 1ms !important; }
}
:root[data-motion="reduce"]::view-transition-group(*),
:root[data-motion="reduce"]::view-transition-old(*),
:root[data-motion="reduce"]::view-transition-new(*) { animation-duration: 1ms !important; }

/* ── Theme swap: freeze transitions for the two frames of a switch (02 §11) */
:root[data-theme-switching] *,
:root[data-theme-switching] *::before,
:root[data-theme-switching] *::after { transition: none !important; }

/* ── Base elements. Layered, so utilities and component classes win, and so
   does ANY unlayered host reset: see "Host resets" in the header. ────────── */
@layer base {
  *, *::before, *::after { box-sizing: border-box; }

  html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    accent-color: var(--primary);          /* native checkbox, radio, range, progress */
    caret-color: var(--accent-text);
    hanging-punctuation: first allow-end last;   /* Safari today; others ignore it */
  }

  body {
    margin: 0;
    background: var(--background);
    color: var(--foreground);
    font-family: var(--font-sans);
    font-size: var(--step-0);
    line-height: 1.55;
    font-kerning: normal;
    font-optical-sizing: auto;
    font-synthesis: none;                  /* no faux bold/italic: ship real styles */
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;   /* macOS only: thinner light-on-dark text */
    -moz-osx-font-smoothing: grayscale;
    overflow-wrap: break-word;             /* long URLs never force sideways scroll at 320px */
  }

  h1, h2, h3, h4 {
    font-family: var(--font-display);
    font-weight: var(--display-weight);
    text-wrap: balance;                    /* even ragged lines on headings */
  }
  h1 { font-size: var(--step-5); line-height: 1.02; letter-spacing: calc(var(--display-tracking) * 1.5); }
  h2 { font-size: var(--step-4); line-height: 1.06; letter-spacing: calc(var(--display-tracking) * 1.25); }
  h3 { font-size: var(--step-2); line-height: 1.15; letter-spacing: var(--display-tracking); }
  h4 { font-size: var(--step-1); line-height: 1.25; letter-spacing: calc(var(--display-tracking) * 0.5); }

  p, li, dd, figcaption, blockquote { text-wrap: pretty; }   /* no one-word last lines */
  small { font-size: var(--step--1); }
  code, kbd, samp, pre { font-family: var(--font-mono); font-size: 0.92em; }
  /* Inline code never splits an identifier or flag at a hyphen ("--ease-/in-out").
     .wrap-anywhere opts out (long paths, URLs, hashes); a long command keeps
     each token whole in <span class="tok"> and wraps only between them. Check
     320 px: a nowrap run longer than the line overflows.                    */
  :where(code, kbd):not(pre *) { white-space: nowrap; }
  :where(code, kbd):not(pre *):is(.wrap-anywhere, :has(.tok)) { white-space: normal; }
  .wrap-anywhere { overflow-wrap: anywhere; }
  .tok { white-space: nowrap; }
  table { font-variant-numeric: tabular-nums; }              /* columns of digits align */
  input, button, select, textarea { font: inherit; color: inherit; }
  img, video { max-inline-size: 100%; block-size: auto; }

  /* Prose links only: component links bring their own class and styling. */
  a:not([class]) {
    color: var(--accent-text);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
  }
  a:not([class]):hover { text-decoration-thickness: 2px; }

  [id] { scroll-margin-block-start: var(--space-16); }       /* anchors clear a sticky header */

  /* Outline, not box-shadow: it survives forced-colors mode and follows the
     border-radius. 2px at ≥3:1 against the page meets WCAG 2.4.13.          */
  :focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

  ::selection {
    background: color-mix(in oklch, var(--primary) 28%, transparent);
    color: var(--foreground);
  }
}
