/* ==========================================================================
   tokens.css — the single source of truth
   Every value in every tanghoong.com property resolves to one of these.
   Never hard-code a colour, size, radius or duration outside this file.
   ========================================================================== */

:root {
  color-scheme: light dark;

  /* --- Surfaces ---------------------------------------------------------
     Four depths only. bg is the page, sunken recedes (section bands, wells),
     elevated lifts (cards, menus, modals), glass is the sticky header. */
  --bg:          light-dark(#fbfbfd, #000000);
  --bg-sunken:   light-dark(#f5f5f7, #0a0a0c);
  --bg-elevated: light-dark(#ffffff, #1c1c1e);
  --bg-glass:    light-dark(#fbfbfdb8, #000000b8);

  /* --- Ink ---------------------------------------------------------------
     Three working weights: text for content, text-2 for supporting copy,
     text-3 for labels and metadata. Plus --text-hi above them, which is a
     display device rather than a fourth level of content — reach for it only
     where something has to lift off the page. */
  --text:   light-dark(#1d1d1f, #f5f5f7);
  --text-2: light-dark(#515154, #a1a1a6);
  --text-3: light-dark(#6e6e73, #86868b);
  /* One step ABOVE --text, for a display line that has to lift off the page.
     Used sparingly — a hero word, a headline figure. If everything is --text-hi
     then nothing is, and you have just moved the baseline. */
  --text-hi: light-dark(#000000, #ffffff);

  /* --- Lines -------------------------------------------------------------
     Alpha, not solid — they must sit correctly on any surface. */
  --hairline:      light-dark(#0000001a, #ffffff21);
  --hairline-soft: light-dark(#0000000f, #ffffff14);

  /* --- Accent ------------------------------------------------------------
     Deep green on light, mint on dark. Both clear AA on their own surface. */
  --accent:      light-dark(#03744e, #4dff9b);
  --accent-ink:  light-dark(#ffffff, #04140b);
  --accent-wash: color-mix(in oklab, var(--accent) 12%, transparent);
  --focus:       var(--accent);

  /* A second accent, for a signal that is neither the primary action nor a
     status. tanghoong.com uses it on kickers and inline marks. Optional: a
     property that does not need a second voice should leave it unused rather
     than find something to paint with it. */
  --accent-2:      light-dark(#8a5a00, #ffd38a);
  --accent-2-ink:  light-dark(#ffffff, #04140b);
  --accent-2-wash: color-mix(in oklab, var(--accent-2) 14%, transparent);

  /* --- Status ------------------------------------------------------------
     Used for validation, alerts and badges. Same wash/ink pattern as accent. */
  --ok:      light-dark(#03744e, #4dff9b);
  --warn:    light-dark(#8a5a00, #f5c451);
  --danger:  light-dark(#b3261e, #ff6b62);
  --info:    light-dark(#0a5ec2, #6bb6ff);
  --ok-wash:     color-mix(in oklab, var(--ok) 12%, transparent);
  --warn-wash:   color-mix(in oklab, var(--warn) 14%, transparent);
  --danger-wash: color-mix(in oklab, var(--danger) 12%, transparent);
  --info-wash:   color-mix(in oklab, var(--info) 12%, transparent);

  /* --- Data / chart ramp -------------------------------------------------
     The ramp is a FUNCTION, not a fixed palette: interpolate --c-from to
     --c-to and sample it at however many series you have. Regenerate with
     `node scripts/ramp.mjs <n>`, which reads these two endpoints.

     🔴 Interpolate in OKLab, NOT sRGB. sRGB is the obvious choice and it is
     wrong — the steps come out perceptually uneven and bunch at one end. The
     first version of this ramp was sRGB-interpolated and its last three steps
     sat ΔE 0.027 apart where the first three were 0.11: three series nobody
     could tell apart, in a chart whose only job is telling series apart.
     scripts/contrast.mjs caught it; no one had noticed by eye.

     --c-1..--c-6 are the six-sample case, because it covers most charts and
     because CSS cannot interpolate. --c-1 is the largest series.
     Light runs dark→pale; dark runs bright→deep. */
  --c-from: light-dark(#03744e, #4dff9b);
  --c-to:   light-dark(#7ee6c3, #107f40);
  --c-1: light-dark(#03744e, #4dff9b);
  --c-2: light-dark(#268a64, #41e488);
  --c-3: light-dark(#3da07b, #35ca75);
  --c-4: light-dark(#53b792, #29b063);
  --c-5: light-dark(#69ceaa, #1d9751);
  --c-6: light-dark(#7ee6c3, #107f40);
  /* Fill under a radar or area line. Slightly stronger than --accent-wash
     because it sits over gridlines rather than over a flat surface. */
  --chart-fill: 0.16;
  --chart-grid: light-dark(#0000000f, #ffffff14);

  /* --- Type --------------------------------------------------------------
     System stack. No webfont, so no FOUT, no layout shift, no extra request. */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display",
               "Segoe UI", Roboto, system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono",
               Menlo, Consolas, monospace;

  /* Headings are fluid; body sizes are fixed so measure stays stable. */
  --fs-h1:   clamp(2rem, 1.45rem + 2.6vw, 3.25rem);
  --fs-h2:   clamp(1.5rem, 1.25rem + 1.2vw, 2.25rem);
  --fs-h3:   clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
  --fs-lead: clamp(1.125rem, 1.02rem + 0.5vw, 1.375rem);
  --fs-stat: clamp(1.25rem, 0.95rem + 1.5vw, 1.9375rem);
  /* Editorial reading size, for short high-value copy: a landing page lead, a
     pull paragraph. Bigger than --fs-lead and set at a looser line-height.
     Not for body text — at this size the measure has to come down with it. */
  --fs-read: clamp(1.3125rem, 1.15rem + 0.8vw, 1.625rem);
  --fs-body: 1.0625rem;   /* 17px — the baseline */
  --fs-sm:   0.9375rem;   /* 15px */
  --fs-xs:   0.8125rem;   /* 13px */

  /* --- Space -------------------------------------------------------------
     A 4px scale. Named by step, not by px, so the ramp can be retuned once. */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 20px;  --s-6: 24px;  --s-8: 32px;  --s-10: 40px;
  --s-12: 48px; --s-16: 64px; --s-20: 80px; --s-24: 96px; --s-32: 128px;

  /* --- Measure -----------------------------------------------------------  */
  --container:       1120px;
  --container-prose: 720px;
  --container-mid:   940px;
  --container-wide:  1320px;
  --gutter:    clamp(20px, 5vw, 40px);
  --section-y: clamp(72px, 9vw, 128px);
  --nav-h:     56px;

  /* --- Radius ------------------------------------------------------------
     chip is a pill; everything else steps with the size of the box. */
  --r-chip: 980px;
  --r-sm: 10px;  --r-md: 16px;  --r-lg: 20px;  --r-xl: 28px;

  /* --- Shadow ------------------------------------------------------------
     Light theme only — see the dark override below. */
  --sh-sm: 0 1px 2px #0000000a, 0 1px 1px #00000008;
  --sh-md: 0 4px 16px #0000000f;
  --sh-lg: 0 16px 48px #0000001a;

  /* --- Motion ------------------------------------------------------------  */
  --ease:      cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in:   cubic-bezier(0.4, 0, 1, 1);
  --dur-fast:  0.15s;
  --dur:       0.26s;
  --dur-slow:  0.42s;

  /* --- Layers ------------------------------------------------------------  */
  /* There is no --z-modal on purpose: a native <dialog> opened with
     showModal() lives in the top layer, above every z-index on the page. */
  --z-head: 40;
  --z-sheet: 50;
  --z-toast: 70;
  --z-skip: 100;
}

/* The toggle wins over the system preference in both directions. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* On black, a drop shadow reads as grime. Depth comes from --bg-elevated. */
:root[data-theme="dark"] { --sh-sm: none; --sh-md: none; --sh-lg: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --sh-sm: none; --sh-md: none; --sh-lg: none; }
}

/* ==========================================================================
   PROPERTY SKINS

   A property may re-skin the system by redefining tokens. That is the whole
   point of a token contract, and tanghoong.com is the proof: it runs this
   exact token set, then overrides the palette to a deep petrol ground with a
   sage accent and a gold second voice — and every component still works,
   because not one of them names a colour.

   A skin MAY redefine:
     the four surfaces · the four inks · both hairlines
     --accent and its ink/wash · --accent-2 and its pair
     --fs-read and the container widths
     color-scheme, if the property is single-purpose and deliberately one theme

   A skin MUST NOT touch, or it stops being the same system:
     the space scale · the radius scale · --ease and the durations
     the status colours · the focus treatment · the mark

   Two rules for anyone writing one:
     1. Re-run scripts/contrast.mjs against the skinned values. A new palette
        is exactly where contrast quietly breaks.
     2. On a coloured ground, tint the hairlines to match. A neutral white
        alpha over petrol reads as grey scum; tanghoong.com uses #dcf1f52e,
        which carries the ground's own hue.

   Put the override in its own file, loaded after tokens.css. Never edit
   tokens.css in a consuming repo — that is a fork, not a skin.

   :root {
     color-scheme: dark;
     --bg: #071a24;  --bg-sunken: #051620;  --bg-elevated: #0b2733;
     --text: #f5fafb; --text-2: #e6f2f5d1; --text-3: #a2b3b8; --text-hi: #fff;
     --hairline: #dcf1f52e; --hairline-soft: #dcf1f51a;
     --accent: #b8d9c9; --accent-2: #ffd38a;
     --sh-sm: none; --sh-md: none; --sh-lg: none;
   }
   ========================================================================== */
