/* site/system/tokens.contract.css — layer 1 of 3, the semantic token CONTRACT (brand-agnostic).
 *
 * The interface between this system and any client pack. It declares every semantic token that
 * site/system/components.css and any page CSS may reference, with NEUTRAL defaults: greyscale, one
 * plain blue accent, a generic scale. No brand lives in this file.
 *
 * A pack (site/system/tokens.fin.css) is loaded AFTER this file and overrides these values with
 * the client's own. With no pack loaded the site still renders as a coherent unbranded theme, which
 * is what proves this layer carries structure rather than aesthetics. A slot the pack does not bind
 * falls through to the default below, and that fall-through is a real state: gate group 8 resolves
 * it rather than treating it as an error.
 *
 * S3, and it is enforced: a token referenced anywhere outside the pack must be declared HERE first.
 * Gate group 6 reddens on any var() in components.css, page CSS or an inline <style> that this file
 * does not declare. Adding a token to this file is therefore step one of using one, not a tidy-up
 * afterwards.
 *
 * HAND-AUTHORED. site/tokens.json is generated FROM this file by tooling/gen-tokens-json.mjs —
 * the opposite direction to ux-factory, which generates its contract CSS from a JSON source (d-007).
 * Edit this file; never edit site/tokens.json.
 */

:root {
  /* ---- Colour: foreground / surface (light context) ---- */
  --color-fg:            #1a1a1a;
  --color-fg-muted:      #656b76;   /* darkened from ux-factory's #6b7280, which is 4.40:1 on --color-bg-surface */
  --color-bg:            #ffffff;
  --color-bg-surface:    #f4f4f5;
  --color-border:        #767d85;   /* 3:1 against --color-bg (WCAG 1.4.11), not a hairline tint */
  --color-border-strong: #1a1a1a;
  --color-white:         #ffffff;   /* self-referenced by --color-on-dark-border; not a page colour */

  /* ---- Colour: accent ---- */
  --color-accent:            #2563eb;
  --color-accent-hover:      #1d4ed8;
  --color-accent-active:     #1e40af;
  --color-accent-fg:         #ffffff;   /* text and icons ON an accent fill */
  --color-accent-secondary:  #15803d;   /* the quiet second signal: live dots, status marks */
  --color-accent-on-inverse: #93b4ff;   /* the accent lifted to stay legible as text on the dark ground */

  /* ---- Colour: inverse (content sitting on a dark surface) ---- */
  --color-bg-inverse:           #1a1a1a;
  --color-fg-on-inverse:        #f4f4f5;
  --color-fg-on-inverse-strong: #ffffff;
  /* OPAQUE, and it is the only one of these four that carries text. #f4f4f5 at 55% over the inverse
     ground, composited here rather than left to the browser, so gate group 8 can measure it: 5.59:1
     against the neutral --color-bg-inverse above. As a color-mix() it was unmeasurable, and the
     pack's binding of it shipped the footer's legal line at 4.29:1 from #3 to #17. A pack rebinding
     this slot owes group 8 an opaque colour too, since the pair is now enumerated and a value the
     group cannot resolve is a failure rather than a skip. */
  --color-fg-on-inverse-muted:  #929292;
  /* The two derived values below are computed at runtime by the browser. They carry no static
     value, which is why tooling/gen-tokens-json.mjs excludes them from the DTCG export and why gate
     group 8 excludes them from the contrast pairs. Never bind a TEXT slot to one of these: group 8
     fails a text pair it cannot reduce to an opaque colour rather than skipping it. */
  --color-inverse-line:         color-mix(in srgb, var(--color-fg-on-inverse) 12%, transparent);
  --color-on-dark-border:       color-mix(in srgb, var(--color-white) 50%, transparent);

  /* ---- Fonts (neutral system stack; a pack picks real faces) ---- */
  --font-display: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-body:    ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono:    ui-monospace, SFMono-Regular, Menlo, monospace;   /* numerals, machine-adjacent text */
  --font-weight-ui: 500;   /* UI weight: the neutral theme reads medium; a pack may rebind it (Fin sets 400) */

  /* ---- Spacing (4px scale, 8 steps) ----
     The scale IS the answer. Gate group 6 rejects a bare px value on padding, margin, gap,
     border-radius and font-size, so a one-off spacing value has to snap to a step here or the rule
     has to earn a new step. Eight steps plus fourteen exceptions is not a scale. */
  --spacing-xs:  4px;
  --spacing-sm:  8px;
  --spacing-md:  16px;
  --spacing-lg:  24px;
  --spacing-xl:  32px;
  --spacing-2xl: 48px;
  --spacing-3xl: 64px;
  --spacing-4xl: 96px;

  /* ---- Radius (non-zero default: radius is a brand choice, not a base rule) ---- */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg:     16px;   /* raised in-screen panels: a step past --radius-md for phone-scale cards */
  --radius-pill:   999px;  /* fully rounded chips and pills */
  --radius-phone:  44px;   /* the prototype device bezel */
  --radius-screen: 36px;   /* the screen inside the bezel: bezel radius less its padding */

  /* ---- Shadow ---- */
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.08);

  /* ---- Motion: durations, distances and easings ----
     Nine tokens, derived from what the vendored component band actually references rather than
     copied wholesale. #5's replay player will want more, and S3 says it declares them here first. */
  --motion-fast:   160ms;   /* micro feedback: hovers, colour and opacity */
  --motion-base:   200ms;   /* structural: lifts, slides, panels */
  --motion-slow:   480ms;   /* entrances and reveals */
  --motion-bounce: 300ms;   /* micro-interaction release, pairs with --motion-ease-bounce */
  --motion-rise:   20px;    /* reveal translate offset: how far an element travels in */
  --motion-ease:   ease;    /* default curve */
  --motion-ease-spring: linear(0, 0.004, 0.016, 0.035, 0.062, 0.096, 0.138, 0.187, 0.242, 0.304, 0.371, 0.443, 0.519, 0.598, 0.68, 0.763, 0.846, 0.918, 0.973, 1.007, 1.019, 1.016, 1.007, 1.001, 1);   /* baked spring, one ~1.9% overshoot */
  --motion-ease-bounce: linear(0, 0.063, 0.223 3.9%, 0.605 8.7%, 0.882 12.7%, 1.017 15.7%, 1.092 18.7%, 1.126 21.6%, 1.13 24.6%, 1.111 28%, 1.028 36.3%, 0.99 41.6%, 0.973 46.9%, 0.972 52.5%, 0.996 68.8%, 1.003 79.4%, 1);   /* ~13% overshoot, one rebound; things you touch, never entrances */
  --motion-ease-settle: linear(0, 0.102, 0.199, 0.29, 0.373, 0.449, 0.518, 0.581, 0.638, 0.689, 0.735, 0.775, 0.811, 0.843, 0.871, 0.895, 0.916, 0.934, 0.949, 0.962, 0.972, 0.98, 0.987, 0.992, 0.995, 0.998, 0.999, 1);   /* critically damped glide, no overshoot */

  /* ---- Layout ---- */
  --maxw:   1200px;
  --gutter: 24px;

  /* ---- Type ramp ----
     The four clamp() steps are fluid, so they have no single static value. tooling/gen-tokens-json.mjs
     excludes them from the DTCG export by name for the same reason it excludes color-mix(): DTCG
     dimension is one measurement, and emitting the clamp string would be a false claim. */
  --type-display: clamp(40px, 6vw, 76px);
  --type-h1:      clamp(32px, 4vw, 56px);
  --type-h2:      clamp(24px, 2.5vw, 34px);
  --type-lead:    clamp(18px, 1.5vw, 22px);
  --type-h3:      20px;
  --type-body:    16px;
  --type-caption: 13px;
  --type-eyebrow: 12px;
}
