/* Default token layer. Every colour, size, radius and font in this deployment resolves
   through one of these custom properties, so an agency's branding is an override of this
   file — never a fork of the markup that uses it.

   The palette is owner-decided (#57, 2 Aug 2026), derived from zig.ai: a warm off-white ground,
   grey-green ink, one accent and three state tints. It replaces the stackai defaults
   (white / grey / #0099ff blue) this file shipped with. It is still a NEUTRAL base rather than
   the saulera Sunrise brand, so the README's "one deployment per agency, the tool sits inside
   the agency's own client relationship" decision is untouched — this is a warmer neutral, not
   saulera's colours.

   Where a decided value and a contrast gate disagreed, the gate won (#58 AC2). That happened
   exactly once, at --accent, and --accent-strong below is the result.

   The --font-* families now have real .woff2 files behind them (fonts.css, on disk since #8).
   test/tokens.test.js measures every pairing here and fails the suite under the floor,
   including for an agency that swaps a colour. */

:root {
  /* colour. Ratios below are quoted against the three grounds text renders on, in the order
     --background / --surface / --surface-signature, which is the order test/tokens.test.js
     checks them in. */
  --background:   #fdfafa;
  --surface:      #f4efee;   /* one warm step down from the ground: 1.10:1, a tone not an edge */
  --text-primary: #2e3332;   /* 12.37 / 11.27 / 11.59 */
  /* Kept at the 4.5:1 body-text floor through the repalette, for the same reason it was darkened
     from stackai's #8c8c8c in #5: row meta and the note scaffold both sit on --surface, so this
     is body text and not a decorative grey. The warm-neutral equivalent measures 5.65 / 5.15 /
     5.29 — comfortably clear, and it must stay that way through any agency swap.
     NOT usable on --tint-info: 3.86:1 there. See the tints below. */
  --text-muted:   #5c6764;
  /* Domino's blue (their site's theme-color, 17 Sep 2026), replacing the zig green. Still
     DECORATIVE by contract: the focus ring, the link and aria-current underlines, .client-row's
     marker bar and the dragover edge, every one a non-text use at the 3:1 floor, which it
     clears at 6.63 / 6.04 / 6.12. It would also carry a white label (6.88:1), but the label
     sites keep spending --accent-strong so a future accent that fails that floor breaks only
     one token. It is 4.53:1 on --tint-info, and :focus-visible still keeps a --text-primary
     hairline inside the ring (app.css) rather than trusting the accent alone. */
  --accent:       #00608c;
  /* The accent a step darker, for a white label: 7.62:1. This exists because #58 AC2 says the
     gates win over a raw reference value, and .btn-primary puts a label directly on its fill.
     One consumer: .btn-primary. */
  --accent-strong: #005983;
  /* The label colour on --accent-strong. A token rather than a literal so app.css needs no raw
     hex — which test/chrome.test.js now enforces. */
  --on-accent:    #ffffff;
  --border:       #78827f;   /* 3.82 / 3.48 / 3.58 — the 3:1 non-text floor, held */
  /* Decorative only: card edges, the top bar's rule, dividers whose grouping the surface
     change already carries. Never the sole boundary of a meaningful control — inputs and
     textareas keep --border, which is what holds the 3:1 non-text floor. That is why this
     token is deliberately NOT in test/tokens.test.js's PAIRINGS. 1.25:1 on --background. */
  --border-hairline: #e7e1e0;
  /* Its width, tokenised so component files can draw a hairline without a raw px. */
  --hairline: 1px;
  --danger:       #78350f;   /* unchanged: 8.74 / 7.96 / 8.19 on the new grounds */

  /* The signature surface: one card per candidate page renders on this accent-tinted ground.
     Mint under the zig green; a light blue under Domino's accent (17 Sep 2026). Every text
     token holds its floor on it: ink 11.42, muted 5.22, verified 7.09, unverified 5.63, failed
     7.13, danger 8.07, border 3.52. The third column of every ratio in this file is this
     surface, and test/tokens.test.js checks it rather than trusting this comment. The border is
     decorative, same licence as --border-hairline: 1.20:1 on its own ground. prep.css:69-74 is
     the consumer. */
  --surface-signature: #eaf3f8;
  --border-signature:  #cfe0ea;

  /* The three state tints, named for their ROLE and not their colour (mint / powder / blush),
     because that is what keeps an agency's branding a value swap. These are BACKGROUNDS: #60
     builds the claim chips on them. A chip carries --text-primary or its own mark colour, never
     --text-muted, which is 3.86:1 on --tint-info and under the floor. The verified and info
     tints moved to light blues with the accent (17 Sep 2026); the warn tint is unchanged.
       --tint-verified  ink 9.39, --verified 5.83
       --tint-info      ink 8.45   (--border is 2.61:1 on it: a chip here needs a darker edge, or
                                    a border that is genuinely decorative because the chip also
                                    carries a word, this product's existing pattern)
       --tint-warn      ink 11.34, --unverified 5.59, --failed 7.08, --danger 8.01
     --tint-info has two consumers, both spending it as the info ground #58 promised it for.
     #60 gives it to act 3's open-questions panel (.pack-questions-panel), the one block of the
     pack that is neither sourced nor unsourced, the recruiter's own list of things to confirm
     before sending. #62 gives it to the notice a dead magic link lands on (login.html). Both
     carry --text-primary (8.45:1) and never --text-muted (3.86:1, under the body-text floor),
     and both draw NO border: --border is 2.61:1 on this tint and an edge in it would sit under
     the 3:1 non-text floor, so each panel is found by its tone and by the words it carries.
     test/tokens.test.js's TINTS table measures all three. */
  --tint-verified: #c4e1ee;
  --tint-info:     #b3d7ea;
  --tint-warn:     #fbeeec;

  /* stackai's soft layered card shadow, one token so every card lifts identically. Alpha hex
     is fine here: the contrast gate reads single whole-value hex tokens only, and a shadow is
     not a colour pairing. The two tinted layers carry the ink's own hue, so they moved with it
     from #1d1d1d to #2e3332 — nothing measures this, so it is a manual correspondence. */
  --shadow-card: 0 1px 1px #0000000a, 0 4px 6px #2e333205, 0 20px 60px #2e33320a,
                 0 2px 4px #0000000a;

  /* type. One sans, one mono (#58): Geist carries the whole UI. Both names
     stay pointing at the same stack rather than collapsing into one --font-sans, because they
     have 14 + 3 call sites across app.css and prep.css and prep.css belongs to #62 — a rename
     is a cross-ticket edit for no functional gain. Deliberate, not an oversight.
     Geist ships 400 and 600 only. Nothing may ask for 500: CSS font-matching resolves a request
     between 400 and 500 DOWNWARD, so a 500 heading would silently render at regular weight. */
  --font-ui:   "Geist", system-ui, -apple-system, sans-serif;
  --font-body: "Geist", system-ui, -apple-system, sans-serif;
  --font-mono: "DM Mono", ui-monospace, SFMono-Regular, monospace;

  /* Type ramp: a ~1.2 step from a 14px UI base. The note is 16px because it is prose a person
     writes and rereads, which CRAFT.md asks for on the pack and the note is the same kind of
     text. Nothing anywhere goes below 12px. */
  --text-caption: 12px;   /* row meta, scaffold items */
  --text-body:    14px;   /* UI chrome, labels, buttons */
  --text-note:    16px;   /* the textarea and any rendered note prose */
  --text-h4:      17px;
  --text-h3:      20px;   /* the selected client's name */
  --text-h2:      24px;   /* what the page h1 was until #59 */
  --text-h1:      29px;   /* the next 1.2 step up: the candidate pages' role title */
  /* 29 × 1.2 = 34.8 → 35. The ramp's top step, and the page h1 on every recruiter screen from
     #59 — it was --text-h2 (24px), which put the page title four points above a subheading and
     is not a ramp. The epic's decided direction is big type; this is the step #58's Out of
     Scope reserved for #59 by name, spent on the one element it named. Disciplined rather than
     round: 35, not 40. */
  --text-display: 35px;

  /* provenance status. Whether a claim is sourced is this product's core distinction, so these
     are text colours and are held to the 4.5:1 body-text floor rather than to the 3:1 a
     decorative state colour could take — the mark carries the WORD ("Unverified"), and a word
     is text. Measured on --background / --surface / --surface-signature:
       --verified   7.68:1 / 7.00:1 / 7.20:1
       --unverified 6.10:1 / 5.55:1 / 5.71:1
       --failed     7.72:1 / 7.03:1 / 7.23:1
     They replace aliases of --success (#22c55e, 2.28:1) and --warning (#c68a0b, 2.98:1), which
     both failed the floor. Those two tokens are gone rather than left orphaned: nothing else
     referenced them. test/tokens.test.js is the gate that keeps these numbers true.
     --failed is its own red rather than var(--danger) so it separates from --unverified at
     12px; --danger stays the state-line error colour.
     --verified moved from #166534 to #0b5c46 in #58, and that one is NOT gate-forced: the old
     value cleared every floor (6.87 / 6.26). It moved so the "sourced" green sits inside the
     accent's hue family rather than beside it — a hue-harmony judgment, cheap to revert. */
  --verified:   #0b5c46;
  --unverified: #8a5300;
  --failed:     #9f1239;

  /* 4px grid */
  --space-1: 4px;  --space-2: 8px;  --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-12: 48px;
  /* The next step up, added by #59 for the rhythm BETWEEN zones: the gap under the page head
     and between the acts. CRAFT.md asks that rhythm between zones be visibly larger than
     spacing within them, and --space-12 could not deliver that while it was also the common
     large gap — it separated the acts AND the page head AND the .visibility fieldset AND
     .editor-remove. 64px is the zone break; 48px goes back to being an ordinary large step. */
  --space-16: 64px;

  /* Decided and settled in #59, closing #58's open question 5, which asked #59 or #60 to pick
     once because the two references disagree — zig.ai is soft, forcanopy tighter. 9px stays:
     the epic assigns zig.ai the FEEL of the build-a-pack flow and gives forcanopy only the
     pattern language of the pack view, so soft is the right answer for the surfaces #59 owns,
     and 9px is already that. If #60 wants a tighter corner for the Canopy claim chips it adds
     a --radius-sm rather than moving this one — this one is on the card, the rail, the pack,
     every input and every button. */
  --radius: 9px;

  /* The tighter corner #60 was told to add rather than move --radius. Its consumers are the
     claim chips and the two counts in the pack's summary, and nothing else: a chip is roughly
     22px tall, where 9px reads as a pill and 4px reads as a corner. 4px because the spacing
     grid is 4px and this is the one number in the file that can sit on it for free. */
  --radius-sm: 4px;

  --max-width: 1024px;

  /* Off the 4px grid deliberately: CRAFT.md's hit-area floor is 44px on touch, and that is not
     a spacing decision that should move when the grid does. --space-8 is 32px and was standing
     in for it, which put every button under the floor. */
  --tap-target: 44px;

  /* The client note's height, taken from the viewport rather than from a row count. The floor is
     what rows="16" already renders — 16 rows at --text-note (16px) and line-height 1.5 is 384px
     of text plus padding — so a window shorter than about 770px is no worse than today, and a
     tall one gets a real writing surface instead of the same 400px box. The ceiling stops the
     box outgrowing the reader on a 27" monitor. Nothing measures this: it is a comfort judgment,
     stated so it can be argued with.
     vh rather than dvh on purpose: a dynamic viewport unit re-lays-out the note box as a phone's
     URL bar hides, which is motion nobody asked for.
     Neither this nor --sticky-save has an entry in test/tokens.test.js, and neither needs one:
     that gate reads `--name: #hex;` declarations only, because what it measures is contrast. */
  --note-height: clamp(400px, 52vh, 640px);

  /* The sticky save row's own height, reserved as scroll padding so a control focused below it
     is not scrolled underneath it. --tap-target (44) + --space-3 above and below (12 + 12) + the
     hairline (1) = 69px, rounded up to the next 4px step. CHECKLIST.md names the top-side
     version of this trap (scroll-padding-top under sticky chrome); this is the bottom-side twin.
     Keep the two in step: change the save row's padding and this moves with it. */
  --sticky-save: 72px;

  /* Motion. One curve, defined once. Ease-out on everything entering or interactive and never
     ease-in, because ease-in delays the first frame — the one the user is watching. */
  --ease-out:   cubic-bezier(0.23, 1, 0.32, 1);
  --duration-1: 150ms;   /* press and hover response */
  --duration-2: 200ms;   /* the one authored moment: the saved-state line arriving */

  /* Focus. Two concentric lines rather than one. --accent clears the 3:1 non-text floor on all
     three grounds (6.04:1 at worst) and on --tint-info (4.53:1), but #60 puts focusable
     controls on the tints and a future accent may not, so a --text-primary hairline sits inside
     the accent ring and guarantees the indicator is visible on any surface in this palette. */
  --focus-width:  2px;
  --focus-inner:  1px;
}
