/* Dalton Baseball Club — design tokens.
 *
 * First sheet on every page, and the only one with NO media queries at all:
 * a token is a value, not a layout. Width-dependent values live in mobile.css
 * / desktop.css, which load after this one and after site.css.
 */

/* ---------------------------------------------------------------------------
 * The type, served from this origin.
 *
 * These six faces used to come from fonts.googleapis.com: a render-blocking
 * third-party stylesheet whose woff2 URLs are only knowable after it parses.
 * Measured on a phone (W9 perf L9): first contentful paint 1 304 ms, first
 * font request 1 496 ms, last font done 2 024 ms — every headline and nav
 * label set in the fallback for ~720 ms and then snapped, because plain
 * sans-serif is 34 % wider than Anton and 33 % wider than Barlow Condensed at
 * the same size.
 *
 * So: latin-subset woff2 committed under assets/fonts/ (vercel.json gives
 * /assets/*.woff2 a year, immutable), declared here in the first stylesheet on
 * every page, with <link rel="preload"> in each <head> for the two faces the
 * first screen paints with. Two third-party origins, two DNS+TLS handshakes
 * and one render-blocking stylesheet gone.
 *
 * The "… Fallback" faces are the swap made invisible: local Arial with its
 * advance widths and its line box scaled onto the real face's. Every number
 * below was measured, not guessed, twice over —
 * <scratch>/w9/perf/c2-fontmetrics.mjs renders this site's own strings (in the
 * case the site renders them in: every --display heading is uppercased by CSS)
 * in both faces at 100px, and c2-swap3.mjs loads each real page twice, once
 * with the woff2 served and once with every woff2 blocked, and compares the
 * text bounds of every element on it. Results, phone, five pages, ~450 pieces
 * of text, size-weighted, before → after:
 *
 *      Anton             +22.7 %  →  −11.6 %      (hero headlines: +46…+52 % → ±1.6 %)
 *      Barlow Condensed  +23.8 %  →   +0.2 %
 *      Barlow             +3.0 %  →   −1.4 %
 *
 * and the line boxes match to the pixel, so nothing moves vertically at all.
 * Anton keeps a residual because one size-adjust cannot serve both halves of a
 * face: its letters are ~32 % narrower than Arial's and its digits only ~12 %,
 * so the value here is tuned on the headlines — the text whose width carries
 * layout — and leaves the big numerals on /team and /scores a little narrow
 * until the face lands. Those sit centred inside their own cards, where a
 * width change moves nothing else on the page.
 *
 * (Tuned against Liberation Sans, which is
 * metric-compatible with Arial by design and is what the measuring container
 * has.) The local() chain is Arial first — the face the numbers are tuned to,
 * and the one iOS, macOS and Windows all have — then Helvetica, then the two
 * platforms that have neither: Liberation Sans on Linux, which is Arial's
 * metric clone, and Roboto on Android, which is 2-3 % off it and still an
 * order of magnitude closer than sans-serif. Where NONE of them exists the
 * face simply does not match, the stack falls through to sans-serif, and the
 * swap is what it was before — a floor, not a regression.
 *
 * font-display:swap stays: FOUT with matched metrics costs nothing, FOIT would
 * cost a blank headline on ballpark wi-fi.
 *
 * The six woff2 files are HINTED subsets (fpgm/prep/cvt kept, the same Latin
 * codepoints as before) cut from the upstream TTFs in google/fonts with
 * fontTools' subsetter. The first set was unhinted — the shape Google's CSS
 * API hands a Mac or Linux browser, where hinting is ignored — and on a
 * Windows PC Chrome and Edge rasterised every face soft at text sizes; the
 * owner read the whole desktop site as blurry (2026-09-26).
 * tests/fonts-hinted.test.mjs fails if a re-subset ever drops the tables.
 * The third segment of each filename is the first eight hex digits of the
 * file's SHA-256: the URLs are immutable for a year (vercel.json) and are not
 * stamped like the .js/.css (scripts/stamp-assets.mjs says why), so different
 * bytes MUST get a different name — the same test enforces that too. Changing
 * a face means: new file, new name, and this block plus the preload tags that
 * name it follow: every page preloads Anton 400 and Barlow Condensed 600, and
 * index.html and live.html also Barlow 400 (tests/harness-fonts-ready.test.mjs
 * pins them against this block, and scripts/stamp-assets.mjs refuses a build
 * whose preload names a file that is not there).
 * ------------------------------------------------------------------------ */
@font-face{font-family:"Anton";font-style:normal;font-weight:400;font-display:swap;
  src:url(/assets/fonts/anton-400.63e229df.woff2) format("woff2")}
@font-face{font-family:"Barlow Condensed";font-style:normal;font-weight:500;font-display:swap;
  src:url(/assets/fonts/barlow-condensed-500.0405f575.woff2) format("woff2")}
@font-face{font-family:"Barlow Condensed";font-style:normal;font-weight:600;font-display:swap;
  src:url(/assets/fonts/barlow-condensed-600.67768967.woff2) format("woff2")}
@font-face{font-family:"Barlow";font-style:normal;font-weight:400;font-display:swap;
  src:url(/assets/fonts/barlow-400.027fbeb5.woff2) format("woff2")}
@font-face{font-family:"Barlow";font-style:normal;font-weight:600;font-display:swap;
  src:url(/assets/fonts/barlow-600.8eebf7d1.woff2) format("woff2")}
@font-face{font-family:"Barlow";font-style:normal;font-weight:700;font-display:swap;
  src:url(/assets/fonts/barlow-700.511c5e60.woff2) format("woff2")}

@font-face{font-family:"Anton Fallback";font-style:normal;font-weight:400;
  src:local("Arial"),local("Helvetica Neue"),local("Helvetica"),local("Liberation Sans"),local("Roboto");
  size-adjust:68.2%;ascent-override:173%;descent-override:48.4%;line-gap-override:0%}
@font-face{font-family:"Barlow Cond Fallback";font-style:normal;font-weight:500;
  src:local("Arial"),local("Helvetica Neue"),local("Helvetica"),local("Liberation Sans"),local("Roboto");
  size-adjust:76.4%;ascent-override:130.9%;descent-override:26.2%;line-gap-override:0%}
@font-face{font-family:"Barlow Cond Fallback";font-style:normal;font-weight:600;
  src:local("Arial"),local("Helvetica Neue"),local("Helvetica"),local("Liberation Sans"),local("Roboto");
  size-adjust:78.5%;ascent-override:127.4%;descent-override:25.5%;line-gap-override:0%}
@font-face{font-family:"Barlow Fallback";font-style:normal;font-weight:400;
  src:local("Arial"),local("Helvetica Neue"),local("Helvetica"),local("Liberation Sans"),local("Roboto");
  size-adjust:94.5%;ascent-override:105.8%;descent-override:21.2%;line-gap-override:0%}
@font-face{font-family:"Barlow Fallback";font-style:normal;font-weight:600;
  src:local("Arial"),local("Helvetica Neue"),local("Helvetica"),local("Liberation Sans"),local("Roboto");
  size-adjust:95.9%;ascent-override:104.3%;descent-override:20.9%;line-gap-override:0%}
@font-face{font-family:"Barlow Fallback";font-style:normal;font-weight:700;
  src:local("Arial"),local("Helvetica Neue"),local("Helvetica"),local("Liberation Sans"),local("Roboto");
  size-adjust:98.9%;ascent-override:101.1%;descent-override:20.2%;line-gap-override:0%}

:root{
  --ink:var(--surface-0);
  /* The old surface names, now aliases onto the ladder, so a rule that still
     spells --panel is on the same four steps as one that spells --surface-2.
     --panel keeps its exact value (see --surface-2 below); --panel-2 rounds up
     by one step to --surface-3, which is 4.08:1 for --text-3 — the same figure
     --panel-3 already carries, and the same rule applies: use --text-2 there. */
  --panel:var(--surface-2);
  --panel-2:var(--surface-3);
  /* Calendar cells sit lighter than the page ink on purpose (see .cal-cell);
     --panel-4 is the same cell once it has an event on it. */
  --panel-3:#1E1E24;
  --panel-4:#22222A;
  --panel-hover:#1C1C21;
  --line:#2A2A30;
  --red:#0076e7;
  --red-deep:#8E1220;
  --red-hover:#DE1936;
  /* Channel triplet for the brand red, so translucent fills stop being typed
     out as rgba(0,118,231,...) with 17 different alphas. */
  --red-rgb:200 16 46;
  /* Three text weights, not four names for two. --text-1/2/3 are the names to
     use from here on; --white/--silver/--dim are the SAME VALUES under their
     old names, so every documented contrast ratio in this file still holds
     exactly. The audit's suggested #A8ACB3/#71757E were not taken: #71757E is
     4.26:1 on --ink and 3.98:1 on --panel, which would have quietly dropped
     the site's smallest text below AA to buy an evener luminance step. */
  --text-1:#F4F3F0;
  --text-2:#A7ABB3;
  /* AA-checked on both --ink and --panel for the small text it colors.
     NOT on the lighter calendar cells (--panel-3 4.08:1) — use --text-2 there. */
  --text-3:#7A7E87;
  --white:var(--text-1);
  --silver:var(--text-2);
  --dim:var(--text-3);
  /* Brand red for SMALL text: --red passes only for display sizes. The old
     value, #E8465F, was 5.12:1 on --ink and 4.79:1 on --panel by the numbers
     here — both AA — but an accessibility pass measured the rendered
     .eyebrow label (the most visible caller of this token, and one that
     sits on lighter surfaces those two ratios don't cover — --panel-3/
     --panel-4, e.g. .cal-next/.ro-squad-head/.ps-legend, and the page-hero
     photo veil) at 3.7:1, under AA's 4.5:1 floor for body-sized text.
     #EB5C72 = 5.91:1 on --ink, 5.52:1 on --panel, 4.98:1 on --panel-3,
     4.74:1 on --panel-4 (AA on every solid surface this token is used
     against, with headroom to spare for a photo veil in between) — same hue
     and saturation as before (350°, 78%), just lightened from 59% to 64%, so
     it reads as the same brand red, only a shade brighter. */
  --red-text:#EB5C72;
  /* The roles a coach can set (settings.colours; Coaches Corner > Logos and
     posters > Colours) are NOT declared here, on purpose. A saved set writes
     them onto <html> (assets/preloader.js, lib/colours.js), and every
     rule that reads one reads it through a fallback to the token it replaces,
     e.g. background:var(--btn-bg,var(--red)). A club with no saved colours
     therefore renders the exact bytes it did before, and a role a coach has
     not set follows the token above it. The names, so a search finds them:
       --btn-bg --btn-bg-hover --btn-glow --btn-text   primary buttons
       --link                                          small accent text, links
       --hdr-rgb --hdr-menu --hdr-text --hdr-text-hi   the header bar and its text
       --hdr-line --hdr-tint                           the bar's lines and tints, on a light bar
       --hl --hl-rgb --on-hl --hl-line                 tags, badges, progress
       --on-deep --banner-end                          text on a --red-deep chip; the live banner's far end */
  /* The metric-matched fallback sits BETWEEN the real face and sans-serif, so
     the ~400 ms before the woff2 lands is set at the real face's widths and
     line box and the swap moves nothing. */
  --display:"Anton","Anton Fallback",sans-serif;
  --cond:"Barlow Condensed","Barlow Cond Fallback",sans-serif;
  --body:"Barlow","Barlow Fallback",sans-serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  /* The faces that exist (@font-face block at the top of this file): Barlow
     400/600/700, Barlow Condensed 500/600, Anton 400. Nothing here may ask for
     a weight outside that set — the browser synthesises it and the face goes
     soft, and a synthesised weight has no matched fallback either. */

  /* Status colours. Ratios below are against --panel (#141417), the darkest
     surface any of them lands on; all clear AA for body text. */
  --ok:#5FBF7F;        /* 8.09:1 */
  --ok-rgb:95 191 127;
  --warn:#E0A13A;      /* 8.17:1 */
  --warn-rgb:224 161 58;
  --gold:#C9A13B;      /* 7.57:1 — tournaments/featured, not a warning */
  --err:#F0788C;       /* 6.80:1 */
  --err-rgb:240 120 140;
  /* Squad identity, shared by calendar chips and game-row chips. */
  --squad-6u:#1F7A3A;
  --squad-6u-line:#2A9A4A;
  --squad-10u:#1A5FB4;
  --squad-10u-line:#3B7FD4;
  --squad-5u:rgb(var(--red-rgb) / .9);

  /* Light surfaces: the site is dark-only, so these are the handful of places
     a white/near-white ground is deliberate (logos, product shots, signature). */
  --on-red:#fff;
  --surface-light:#F2F2F2;
  --ink-on-light:#111;

  /* Layout. --header-h is #app-header's RESERVED height for the DESKTOP bar;
     the stacked layout re-declares it at the top of mobile.css, and that one
     variable is the whole contract between the two sheets. --chrome-h is the
     measured height site.js/chrome-boot publish once the bar has painted.
     Sticky offsets and hero heights read var(--chrome-h,var(--header-h)) so a
     deep link never lands under the bar. */
  /* 58px was the reserve; the real desktop bar measures 63px (.nav-links a and
     .nav-more at padding:18px 13px over a 23.25px line box = a 62px row, plus
     .site-header's 1px bottom hairline). Every desktop page therefore laid
     out 5px too high until chrome-boot published the measured --chrome-h at
     ~900ms and then dropped — CLS 0.061 at 1440, all of it .hero-full moving
     58 -> 63. mobile.css keeps its own 63/107 pair; this is the desktop half
     of that same contract, and it was simply never corrected. */
  --header-h:63px;
  /* --wrap was a flat 1360px, so a 1920 screen got 280px of gutter on each
     side of a column sized for a 1440 laptop, and a 1200 laptop got the same
     column with no room to breathe. It grows with the screen now; --gutter is
     the single side padding .wrap used to spell out inline. */
  --wrap:clamp(1200px,92vw,1560px);
  --gutter:clamp(22px,3.2vw,56px);
  /* The one confirmed-short-content <select> width: Coaches Corner's scout
     filters, the innings/dues-season/schedule-type pickers, and the squad/
     recruiting/tournament action selects (admin.css says exactly which ones
     and why — a blanket rule truncates longer option text elsewhere, so this
     stays a named list). One tunable number instead of a literal repeated
     across that list and the .select-narrow class it also feeds. */
  --control-select-max:340px;
  /* The old four-step scale: 14/22/36/64, three of them off an 8px grid, and
     used in 40-odd places while 66 other paddings were typed by hand. They are
     aliases onto the ladder below now so nothing that still names them drifts
     off-grid; new rules use --s-*. */
  --space-3:var(--s-3);
  --space-4:var(--s-5);
  --space-5:var(--s-6);
  --space-6:var(--s-8);
  /* Skew survives as a brand accent on exactly three things — .na-tag, .slash
     and jersey numbers — not as the default shape of every button and chip.
     Anything else that still reads this token is a bug. */
  --skew:-8deg;

  /* One 8px ladder. Every padding, gap and margin in the sheets maps onto it,
     so a card and a section can no longer disagree about what "a gap" is. */
  --s-1:4px;
  --s-2:8px;
  --s-3:12px;
  --s-4:16px;
  --s-5:24px;
  --s-6:32px;
  --s-7:48px;
  --s-8:64px;
  --s-9:96px;
  --s-10:128px;
  /* Section rhythm: three paddings, not the six variants the audit counted.
     Applied as padding-block so a section owns the space on BOTH sides of
     itself — the old `padding:76px 0 0` made every gap depend on whatever
     happened to come next. */
  --sec-sm:var(--s-6);
  --sec-md:var(--s-7);
  --sec-lg:var(--s-8);
  --card-pad:var(--s-5);
  --card-pad-lg:var(--s-6);

  /* Type scale. The file grew to 29 distinct px sizes (eight of them half-px)
     and 23 letter-spacing values because the "kicker" idiom — condensed 600,
     uppercase, tracked — was re-declared inline in ~60 rules, each with its
     own size and tracking. New rules pick a step here; the .kicker utility
     below is the idiom itself. */
  /* Nine UI steps on a ~1.2 ratio from 13px, no half-pixels. The phone used to
     render 26 distinct sizes, nine of them on half-pixels (11.5/12.5/13.5/
     14.5/15.5/16.5), because ~60 rules each typed their own. */
  --t-2xs:11px;
  --t-xs:12px;
  --t-sm:13px;
  /* One extra step, for Coaches Corner's app skin (docs/COACHES-CORNER-
     REDESIGN.md's shell, reskinned to read like Vercel/Supabase/Claude's own
     dashboards): body text there is 14px, a size the public 9-step ramp never
     needed. Slotted between --t-sm and --t-base rather than renumbering
     anything below it. */
  --t-14:14px;
  --t-base:15px;
  --t-md:17px;
  --t-lg:20px;
  --t-xl:24px;
  --t-2xl:30px;
  --t-3xl:38px;
  /* Exactly three display ramps for the whole site. There were 21 bespoke
     clamp() ramps and seven near-identical heading curves; a section heading
     on /scores and one on /join were two different sizes for no reason. */
  --d-hero:clamp(56px,7.5vw,132px);   /* home wordmark fallback, /live score */
  --d-page:clamp(40px,5.2vw,84px);    /* every page-hero title */
  --d-sect:clamp(26px,2.6vw,42px);    /* every section h2 and card h3 */
  /* Five trackings. 47 distinct values rendered on a phone before this. */
  --tr-tight:-.01em;   /* Anton, at display sizes */
  --tr-0:0;            /* Barlow body, and sentence-case condensed UI */
  --tr-label:.12em;    /* condensed UPPERCASE labels */
  --tr-eyebrow:.18em;  /* section eyebrows only */
  --tr-splash:.16em;   /* the Splash lockup: the loading screen's name (pullout.js .pl-txt) */
  --lh-display:.94;
  --lh-tight:1.2;
  --lh-body:1.55;
  /* Two measures, not four (.section .sub was 64ch, .page-hero-lead 44ch,
     .sp-lede 56ch, .sp-profile-note 48ch). */
  --measure-lead:52ch;
  --measure-body:68ch;

  /* Legacy names, kept as aliases so the ~60 rules that still spell them stay
     on the scale instead of drifting off it. New rules use --t-*/--tr-*. */
  --fs-xs:var(--t-xs);
  --fs-sm:var(--t-sm);
  --fs-base:var(--t-base);
  --fs-md:var(--t-md);
  --fs-lg:var(--t-lg);
  --fs-xl:var(--t-xl);
  --fs-2xl:var(--t-2xl);
  --fs-3xl:var(--t-3xl);
  --track-tight:var(--tr-tight);
  --track:var(--tr-label);
  --track-wide:var(--tr-label);
  --track-xwide:var(--tr-eyebrow);

  /* Geometry ladder. The old chrome hard-coded 0/2/4/8px corners per rule, so
     a panel, a dropdown and a button never agreed. Five steps, used by new and
     rebuilt pieces; existing components keep their own radii until Wave B. */
  --r-xs:4px;
  --r-sm:6px;
  /* Coaches Corner's app skin draws cards, panels and nav rows at 8px — a
     step the public ladder didn't have between --r-sm and --r-md. */
  --r-8:8px;
  --r-md:10px;
  --r-lg:14px;
  --r-full:999px;

  /* Motion. Three durations and two curves — the audit found 11 durations and
     6 timing functions across the sheets, which is why the hamburger, the
     dropdown and the disclosures all felt like different products. */
  --dur-1:120ms;
  --dur-2:180ms;
  --dur-3:280ms;
  --ease-out:cubic-bezier(.2,.8,.2,1);
  --ease-in:cubic-bezier(.4,0,1,1);

  /* Tonal surfaces: elevation is a lighter ground, not an outline box.
     --surface-hover/-active are the overlays a row or button paints on top of
     whichever surface it already sits on, so one :hover rule works everywhere. */
  /* --surface-0 is the page ground. It is --ink's value, named for the ladder,
     so a rule can say which step it is on instead of which colour it is. */
  --surface-0:#0B0B0D;
  --surface-1:#111114;
  /* #17171B was the audit's number, and it is what Wave A landed. It is also
     1.5% lighter than --panel, which is the surface every contrast ratio in
     this file was computed against — and that 1.5% takes --text-3 from the
     documented 4.52:1 down to 4.39:1, i.e. under AA, on every small muted
     label the re-skin just moved onto a card. The ladder keeps --panel's own
     value instead. Nothing about the look changes (the two are a rounding
     error apart on screen); every ratio written above stays literally true. */
  --surface-2:#141417;
  --surface-3:#1E1E23;
  --surface-hover:rgba(255,255,255,.045);
  --surface-active:rgba(255,255,255,.075);

  /* Hairlines are a translucent white, not --line: they hold up over any of the
     surfaces above without going black-on-black or drawing a hard box. */
  --hairline:rgba(255,255,255,.07);
  --hairline-strong:rgba(255,255,255,.12);

  /* Shadows carry their own hairline ring so a detached panel needs no border. */
  --elev-1:0 1px 2px rgba(0,0,0,.4);
  --elev-2:0 8px 24px -8px rgba(0,0,0,.6), 0 0 0 1px var(--hairline);
  --elev-3:0 32px 80px -24px rgba(0,0,0,.85), 0 0 0 1px var(--hairline);

  /* One ladder, so a veil can't tie with a sticky bar again.
     --z-chrome is for page-level sticky bars that must clear the header —
     .coach-bar, Coaches Corner's own sticky strip, the /live squad toolbar —
     never for the header's OWN popovers. #app-header is position:sticky with
     its own z-index, which makes it a stacking context: nothing painted
     inside that context (the desktop "More" card, the hamburger sheet) can
     out-rank it by raising its own z-index, no matter how high — which is
     exactly how every --z-chrome bar ended up painting over an open menu.
     The header's popovers borrow --z-overlay on the HEADER ITSELF instead,
     only while one of them is open (site.css:137-139,
     `#app-header:has(.nav-more-menu.is-open), #app-header:has(.nav-links.
     is-open){z-index:var(--z-overlay)}`) — raising the whole stacking
     context clear of every --z-chrome bar, rather than the menu's own
     z-index, which a capped ancestor makes a no-op. Do not reach for a
     higher z-index on a menu itself; reach for this pattern instead. */
  --z-header:50;
  --z-chrome:60;
  --z-overlay:100;
  --z-modal:120;
  --z-toast:130;
}
