/* KRAFTful design tokens.
   Source of truth: the colour spec written for the EVE → KRAFTful rebrand,
   2026-08-20. Where a value here differs from that document the reason is
   recorded inline and the deviation is contrast, never taste.

   ⚠️ ONE COPY, SERVED BY BOTH SIDES — the same rule as shared/public/recorder.js.
   This used to be two byte-identical files, unified/public/tokens.css and
   coach/public/tokens.css. That was survivable while there was one palette. With
   two it is not: a light mode maintained in two places drifts, and the way it
   drifts is "the coach's light mode is a different colour from the client's" —
   two people looking at the same number on differently-coloured cards, which is
   exactly what C17 exists to prevent. Both servers mount shared/public at
   /shared, so both link /shared/tokens.css and there is one palette.

   ⚠️ THERE IS A SECOND PALETTE, AND IT IS NOT THIS FILE. dash/public/dashboard.html
   carries its own :root — it predates this vocabulary and is served by three
   things (dash :3000, guest/server.js for Frederik, coach/server.js inside the
   portal iframe). It has been moved to these values in the same pass. If you
   change a colour here and not there, the coach portal shows a differently
   coloured card inside its own chrome, which is the one failure this file exists
   to prevent. Change both or neither.

   ── The one rule ────────────────────────────────────────────────────────────
   Colour means something or it isn't there. Four jobs, four channels, and
   nothing borrows another channel's meaning:

     compliance   did this hit its target?      --compliant / --noncompliant
     verdict      is this reading good or bad?  --compliant / --miss / --neutral
     brand        this is KRAFTful              --brand
     everything else                            neutrals

   Periwinkle is retired. It was carrying brand AND data AND every action at
   once, which is the conflict the rebrand exists to remove.

   ── Why amber is --brand and not --accent ───────────────────────────────────
   The spec names the amber token `--accent`. We do not, and the reason is
   mechanical rather than aesthetic: `--accent` ALREADY EXISTS in this codebase
   and means the opposite thing. It is the alias for periwinkle — the single
   action colour — across ~67 call sites in app.css, coach.css and the dash
   screens, including screens that are out of scope for this pass. Redefining
   `--accent` to amber would have turned every button, link, active tab and
   focus ring in the product amber on the next deploy, silently and everywhere,
   including in files nobody opened.

   So amber is `--brand`, which is a name nothing has ever used, and which
   therefore cannot be reached by accident. Amber appears only where somebody
   typed `--brand` on purpose: the logo, the leading-edge dot on a progress bar,
   and the today marker. It works because it is rare, and giving it an
   unreachable-by-default name is what keeps it rare.

   `--accent` and `--acc` survive as the ACTION colour, re-pointed at the
   neutral. Every existing call site keeps working and silently acquires the
   correct new meaning, which is the outcome a rename could not have produced.

   ── Why the light palette is derived, not lifted ─────────────────────────────
   The spec is dark-only; it gives one palette and one extra value for bone
   backgrounds. Day/night was asked for directly on 2026-08-13 and is live in
   three states, so the light side is derived here the same way the previous
   palette derived its own — by holding the SEMANTICS fixed and re-deriving the
   values, which is not the same as inverting them:

     * Elevation flips direction. In dark, a card is LIGHTER than the page. In
       light, a card is WHITE and the page is bone — the card still reads as
       floating, but by being brighter than its surround in one case and cleaner
       in the other. A literal inversion gives dark-grey cards on a black page,
       which reads as sunken.
     * The semantic colours cannot be inverted. --compliant #4ADE9B on white is
       1.6:1 and --miss #F2555A is 3.3:1 — one invisible, one not text. Their
       light counterparts are darkened to clear 4.5:1 on bone, the darkest
       surface they can land on. They stop being neon and start being ink,
       because on a white card that is what a colour has to be.
     * The gradient survives into light but nearly closes. #FFFFFF → #FAFAFD is
       almost nothing, and it should be: on a dark page the gradient is what
       stops a card reading as unfinished, and on a white one the border already
       does that job.

   ── Where these values differ from the spec, and why ─────────────────────────
   Three deviations, all of them contrast failures found by measuring rather
   than by eye. The spec's own Quality floor section asks for exactly this check
   and says to confirm in the build.

     --text-tertiary   #7A7A88 → #8A8A98
     --text-quaternary #5C5C68 → #6E6E7C
       4.05:1 and 2.60:1 against --surface-card-top, the lightest surface either
       can sit on. Tertiary carries units and eyebrows and quaternary carries
       axis labels; both are real text at small sizes. Nudged until tertiary
       clears 4.5:1 (5.03) and quaternary clears 3.0:1 (3.41). The RATIO between
       the four text tiers is what the spec is actually specifying, and it is
       preserved — this is the same ladder, moved up two rungs at the bottom.

     --brand light     #A86F14 → #8F5C0A
       The spec's bone-background amber is 4.24:1 on a white card, which is fine
       for the logo and the progress dot (both graphics, 3:1) but fails as text,
       and the today marker is a text label. #8F5C0A is hue 37.0° against the
       spec's 36.9° — the same hue one tone deeper, which is what the spec says
       to do when a value reads wrong: "Same hue, two tones. Don't change hue."
       Clears 5.67:1 on white and 4.86:1 on bone.

   Verified with a WCAG 2.1 relative-luminance check, worst-case surface for
   each token, not the most flattering one.

   ── How a theme is selected ──────────────────────────────────────────────────
   shared/public/theme.js ALWAYS stamps a resolved data-theme on <html>, so this
   file needs no @media block: dark is the base and light is an override. If the
   script never runs — JS off, an offline miss — the page is dark, which is what
   it was before any of this existed. That is the right way for it to fail. */

:root {
  color-scheme: dark;

  /* ── Surfaces ──────────────────────────────────────────────────────────────
     --surface-page deliberately equals the app-icon tile ink, so the launch
     screen and the icon are the same colour and the tile edge disappears on
     open. Do not "improve" it to a rounder number without changing the icon. */
  --surface-page:      #0B0B0D;
  --surface-card-top:  #1B1B23;
  --surface-card-bot:  #121218;
  --surface-inset:     #14141A;
  --surface-control:   #0E0E13;
  --surface-track:     #1E1E27;
  --hairline:          #24242E;

  /* Cards are a gradient with a hairline border. The gradient is doing real
     work — it was the main thing missing when the palette went neutral, and
     flat cards read as unfinished rather than austere. */
  --card-grad: linear-gradient(180deg, var(--surface-card-top), var(--surface-card-bot));

  /* ── Text ─────────────────────────────────────────────────────────────────
     Four tiers. See the deviation note above for tertiary and quaternary. */
  --text-primary:      #F4F4F8;
  --text-secondary:    #C8C8D4;
  --text-tertiary:     #8A8A98;
  --text-quaternary:   #6E6E7C;

  /* ── Semantic ─────────────────────────────────────────────────────────────
     --noncompliant is deliberately lighter than a conventional "off" grey: a
     month of missed targets must still render as a chart with body, not an
     empty card.

     ⚠️ --noncompliant is a GRAPHICS colour. It is 1.77:1 on a card, which is
     correct for a bar and unreadable as a word. In text a missed target is
     --miss and an in-progress one is --neutral. Never set body text to it. */
  --compliant:         #4ADE9B;
  --noncompliant:      #3E3E4A;
  --miss:              #F2555A;
  --neutral:           #F4F4F8;

  --compliant-border:  rgba(74,222,155,0.35);
  --compliant-tint:    rgba(74,222,155,0.10);
  --miss-border:       rgba(242,85,90,0.35);
  --miss-tint:         rgba(242,85,90,0.10);

  /* ── Brand ────────────────────────────────────────────────────────────────
     The logo, the leading-edge dot on a progress bar, the today marker.
     Nothing else. Read the header note before adding a fourth use. */
  --brand:             #D9962B;
  --brand-tint:        rgba(217,150,43,0.10);   /* today-column wash */
  --brand-halo:        rgba(217,150,43,0.35);   /* soft halo under the dot */

  /* ── Chart ────────────────────────────────────────────────────────────────
     Baseline metrics are readings with no pass or fail. The line is neutral,
     the normal range is a band, the baseline is dashed. No green anywhere —
     if you cannot say what the target is, it is a baseline metric, and you do
     not invent one to get colour. */
  --data-line:         #F4F4F8;
  --baseline-band:     #1E1E27;
  --baseline-line:     #585866;
  --grid:              #24242E;

  /* ── Action ───────────────────────────────────────────────────────────────
     Retiring periwinkle removed the product's only affordance colour, so
     actions are now carried by weight, fill and position instead of hue.
     --action is the neutral that replaces it; --action-on is what sits on top
     of a filled one. Both are defined in terms of the text and surface tokens,
     so they invert correctly in light without a second rule.

     ⚠️ Links lost their colour along with periwinkle. A link that is only
     "primary text" is not a link — anything using --action as inline text
     needs an underline or a border to say so. Colour is never the only
     signal here; that property must survive new screens. */
  --action:            var(--text-primary);
  --action-on:         var(--surface-page);
  --action-tint:       rgba(244,244,248,0.10);
  --action-border:     rgba(244,244,248,0.30);
  --action-border-strong: rgba(244,244,248,0.45);

  /* ── Structure ────────────────────────────────────────────────────────────
     Hairlines stay alpha rather than solid so they work over both the card
     gradient and the page. */
  --canvas:      #060608;                  /* page behind everything */
  --bg:          var(--surface-page);
  --card:        var(--surface-card-bot);
  --card-line:   rgba(255,255,255,0.05);
  --sub-line:    rgba(255,255,255,0.07);   /* set-row sub-cards */
  --chip-line:   rgba(255,255,255,0.09);   /* hairline chips */
  --chip-line-2: rgba(255,255,255,0.10);
  --field:       rgba(255,255,255,0.05);   /* search fields, quiet chips */
  --val:         rgba(255,255,255,0.06);   /* value chips (load × reps) */
  --track:       rgba(255,255,255,0.07);   /* progress-bar track */
  --track-2:     rgba(255,255,255,0.08);
  --hatch:       rgba(255,255,255,0.14);   /* dashed drop zones */
  --grip:        rgba(255,255,255,0.18);   /* sheet grab handle */
  --wash:        rgba(255,255,255,0.03);   /* faintest fill on a card */

  --ink:      var(--text-primary);
  --ink-2:    var(--text-secondary);
  --ink-3:    #AAB1C0;                     /* header pill text */
  --dim:      var(--text-secondary);
  --faint:    var(--text-tertiary);
  --ghost:    #454C5B;                     /* drag handles, off dock items */
  --dock-dim: var(--text-quaternary);      /* inactive dock labels */

  --sheet:     #151A26;                    /* bottom sheets */
  --dock-bg:   rgba(24,29,42,0.94);
  --scrim:     rgba(5,7,11,0.5);
  /* ⚠️ --bg AT ZERO ALPHA, and it has to be a token rather than `transparent`.
     The dock fade is linear-gradient(var(--bg) → nothing); `transparent` is
     transparent BLACK, so on a light page the gradient interpolates through
     grey and leaves a dirty haze above the dock. This is the same colour as
     --bg with alpha 0, per theme, so it fades to nothing visibly. */
  --bg-0:           rgba(11,11,13,0);
  --shadow:         rgba(0,0,0,0.4);       /* floating dock button */
  --shadow-drawer:  rgba(0,0,0,0.45);      /* coach chat drawer, narrow screens */

  --r-card:  24px;
  --r-sub:   18px;                         /* set-row sub-cards, switcher rows */
  --r-btn:   16px;                         /* Start session, Finish, Discard */
  --r-chip:  10px;                         /* RIR chips, back buttons */
  --r-val:   12px;                         /* value chips in session */
  --r-sheet: 28px;
  --r-pill:  999px;

  --ease:    cubic-bezier(.2, .7, .2, 1);
  --tap-min: 44px;
}

/* ── DAY ────────────────────────────────────────────────────────────────────
   Only the colours change. Radii, easing and the 44px tap minimum are the same
   design in both themes; a light mode that also changed the geometry would be a
   second design to maintain rather than a second palette.

   ⚠️ `color-scheme` is not decoration. This app is full of <input type="date">
   and <input type="number">, and the native date picker, the number spinners
   and the scrollbars take their appearance from this property alone — not from
   any colour we set. Without it a light page opens a black date picker. */
:root[data-theme="light"] {
  color-scheme: light;

  /* Bone, per the brand: the coach lockup is ink on bone and this is that
     bone. The inversion is an identity device, not a theme — a coach on light
     and a coach on dark are the same product, and this palette is what makes
     the light one still look like it. */
  --surface-page:      #EDEDF2;
  --surface-card-top:  #FFFFFF;
  --surface-card-bot:  #FAFAFD;
  --surface-inset:     #F2F2F7;
  --surface-control:   #E7E7EE;
  --surface-track:     #E2E2EA;
  --hairline:          #D8D8E2;

  --text-primary:      #0B0B0D;
  --text-secondary:    #3A3A46;   /* 9.61:1 on bone */
  --text-tertiary:     #63636F;   /* 5.08:1 */
  --text-quaternary:   #7E7E8B;   /* 3.43:1 — tertiary and decorative only */

  --compliant:         #0E7A55;   /* 4.58:1 on bone, the darkest it can land on */
  /* Matched to dark by CONTRAST, not by lightness: 1.82:1 on a white card
     against dark's 1.77:1, so a month of missed targets has the same visual
     weight in both themes. Picked by measuring — #C9C9D4 looked right and
     measured 1.64:1, which read as a washed-out chart next to the dark one. */
  --noncompliant:      #BFBFCE;   /* graphics only, as in dark */
  --miss:              #C42F35;   /* 4.73:1 */
  --neutral:           #0B0B0D;

  --compliant-border:  rgba(14,122,85,0.35);
  --compliant-tint:    rgba(14,122,85,0.10);
  --miss-border:       rgba(196,47,53,0.35);
  --miss-tint:         rgba(196,47,53,0.10);

  --brand:             #8F5C0A;   /* see the deviation note — hue 37.0°, 5.67:1 on white */
  --brand-tint:        rgba(143,92,10,0.10);
  --brand-halo:        rgba(143,92,10,0.35);

  --data-line:         #0B0B0D;
  --baseline-band:     #E9E9F0;
  --baseline-line:     #9A9AA8;
  --grid:              #E4E4EC;

  /* --action / --action-on derive from the text and surface tokens above and
     invert for free: bone-on-ink becomes ink-on-bone. Only the alphas restate. */
  --action-tint:          rgba(11,11,13,0.08);
  --action-border:        rgba(11,11,13,0.28);
  --action-border-strong: rgba(11,11,13,0.45);

  --canvas:      #E4E4EB;
  --card-line:   rgba(15,20,35,0.09);
  --sub-line:    rgba(15,20,35,0.10);
  --chip-line:   rgba(15,20,35,0.12);
  --chip-line-2: rgba(15,20,35,0.14);
  --field:       rgba(15,20,35,0.05);
  --val:         rgba(15,20,35,0.06);
  --track:       rgba(15,20,35,0.08);
  --track-2:     rgba(15,20,35,0.10);
  --hatch:       rgba(15,20,35,0.18);
  --grip:        rgba(15,20,35,0.20);
  --wash:        rgba(15,20,35,0.03);

  --ink-3:    #444B5C;
  --ghost:    #A3AAB8;

  --sheet:     #FFFFFF;
  --dock-bg:   rgba(250,251,253,0.94);
  --scrim:     rgba(15,18,26,0.32);
  --bg-0:      rgba(237,237,242,0);
  /* Shadows stay black in both themes — they are absence of light, not a
     colour — but a 40% black shadow that reads as depth on a dark page reads
     as dirt on a white one. Same shadow, less of it. */
  --shadow:        rgba(15,20,35,0.18);
  --shadow-drawer: rgba(15,20,35,0.22);
}

/* ── Legacy aliases ─────────────────────────────────────────────────────────
   Earlier screens were written against the first token set; these map the old
   names onto the new design's values so both vocabularies render identically.
   New code uses the new names.

   ⚠️ --acc, --accent and their tints/borders now point at the NEUTRAL action
   colour, not at amber. This is the whole reason amber is called --brand: see
   the header note. A call site that says --accent today means "this is
   actionable", and after this file it still does — it has just stopped being
   periwinkle. Nothing had to be renamed for that to be true.

   ⚠️ --warn and --warn-border are DEPRECATED and point at neutral text.
   Amber has been retired as a status colour: it is brand-only now, and a
   status amber sitting next to a brand amber is the periwinkle conflict all
   over again. The call sites have been split by hand, because --warn cannot
   be mapped to one value safely — in GRAPHICS a not-met target is
   --noncompliant, and in TEXT it is --neutral or --miss. Pointing the alias at
   neutral means anything missed in the sweep degrades to readable text rather
   than to a 1.77:1 grey word nobody can see. Do not add new uses. */
:root {
  --panel:        var(--card);
  --panel-2:      var(--field);
  --line:         var(--chip-line);
  --ink-dim:      var(--dim);
  --ink-faint:    var(--faint);

  --acc:                  var(--action);
  --acc-tint:             var(--action-tint);
  --acc-tint-2:           var(--action-tint);
  --acc-border:           var(--action-border);
  --acc-border-strong:    var(--action-border-strong);
  --accent:               var(--action);
  --accent-tint:          var(--action-tint);
  --accent-border:        var(--action-border);
  --accent-band:          var(--action-border);

  --good:               var(--compliant);
  --good-border:        var(--compliant-border);
  --good-border-strong: var(--compliant-border);
  --good-tint:          var(--compliant-tint);
  --bad:                var(--miss);
  --bad-border:         var(--miss-border);
  --bad-tint:           var(--miss-tint);

  --warn:        var(--neutral);
  --warn-border: var(--action-border);
  --warn-tint:   var(--action-tint);

  --radius-card: var(--r-card);
  --radius-btn:  var(--r-val);
  --radius-pill: var(--r-pill);
  --pad-card:    18px;
  --gap-card:    12px;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, "SF Pro Text", "Helvetica Neue", sans-serif;
}

.num { font-variant-numeric: tabular-nums; }
