/* ================================================================
   tokens.css — StatMason design tokens (single source of truth)

   Every page links this ONE file. Nothing below should ever be
   copy-pasted into a page's <style> block again.

   Three layers, in order:

     1. LOCKED CHROME   identical for every gym, forever. Surfaces,
                        ink, truth colors, event palette, type,
                        radius, spacing, motion, z-index.

     2. TENANT SLOT     the only tokens a tenant may override.
                        Exactly three. Today every tenant ships the
                        same values — the slot exists so a future
                        brand is a values swap, not a refactor.

     3. DERIVED         computed from the tenant slot via color-mix().
                        NEVER hand-authored. This layer is what makes
                        swapping one variable actually change the
                        whole UI instead of 3% of it.

   See DESIGN.md for the rules that govern all three.
   ================================================================ */


/* ================================================================
   LAYER 1 — LOCKED CHROME
   Do not vary these per tenant. This is the ~95% of the interface
   that makes every StatMason league recognizably the same product.
   ================================================================ */

:root {
  /* — surfaces: tonal depth, dark → light (The Tonal-Depth Rule) —
       Warm ink, anchored on statmason.com's #1C1B19 (oklch hue 85,
       chroma 0.004). A warm near-black that reads like ink on paper
       rather than a screen. Adopted as locked chrome 2026-08-06: warm
       ink is the StatMason fingerprint that survives into franchise
       branding, including one whose own neutral is pure #000.
       For the cold blue-black ramp this replaced, see LAYER 1b.

       The ramp is COMPRESSED to 0.62x the original cold steps. Warm
       ink is lighter than a cold near-black (L 0.222 vs 0.158), which
       eats contrast headroom; at full steps --loss falls to 4.47:1 on
       --surface-2 and every legal accent fails its 4.5:1 gate.
       Chroma climbs 0.004 -> 0.010 up the ramp so the upper surfaces
       stay warm instead of going dead grey as they lighten.

       The ramp hue (85) is 3 deg off --gold. Safe ONLY because chroma
       is 0.004 — it is a neutral, not a hue. Do not raise its chroma. */
  --bg:            #1C1B19;
  --surface:       #22201E;
  --surface-2:     #292825;
  --surface-3:     #302F2B;
  --border:        #35332F;
  --border-strong: #423F39;

  /* — ink: cream, not blue-white — the other half of the warm feel — */
  --ink:           #F4EFE4;   /* 15.01:1 on --bg */
  --ink-secondary: #CFC7B6;   /* 10.24:1 on --bg */
  --ink-muted:     #978F81;   /*  4.61:1 on --surface-2; large / de-emphasized only */
  --placeholder:   #A39B8C;   /*  5.35:1 on --surface-2, 4.87:1 on --surface-3 */

  /* — vendor mark —
     StatMason's own brand orange, verbatim. Locked chrome, never a
     tenant accent: it sits far outside the legal accent lane (hue 42)
     and collides with three event colors. Its only jobs are the footer
     attribution, the login screen, admin chrome, and the share-card
     watermark — surfaces where it never sits beside a calendar chip.

     CONTRAST CEILING on the warm ramp — it does not clear 4.5:1
     everywhere, and the brand value is not negotiable, so the usage is:
       as TEXT   on --bg 5.22:1 and --surface 4.92:1 only.
       on --surface-2 it is 4.47:1 — below AA. Use it there as a MARK
                 or FILL (the footer chip, a swatch, an icon) where the
                 3:1 non-text gate applies, never as a text color.
       as a FILL it carries dark text at 5.74:1 (cf. #1C0D05). */
  --statmason: #E8662A;

  /* — truth colors: win / loss / honor (The Truth-Color Rule) —
     Locked for every tenant. A tenant accent may never be green,
     red, or gold — those hues carry fixed meaning site-wide. */
  --win:  #38DD8A;
  --loss: #FF5D6B;
  --gold: #FFC54D;

  /* — calendar event palette —
     Semantic, not decorative. Each hue is a fixed meaning.
     --event-game intentionally follows the tenant accent so the
     "live game" color and the site's one voice never diverge. */
  --event-practice: #38DD8A;   /* oklch hue 156 */
  --event-clinic:   #FFB454;   /* oklch hue  70 */
  --event-camp:     #B589FF;   /* oklch hue 299 */
  --event-season:   #FFC54D;   /* oklch hue  82 */
  --event-closed:   #FF5D6B;   /* oklch hue  19 */

  /* — type (The Two-Silhouette Rule) — */
  --font-display: 'Archivo', system-ui, sans-serif;
  --font-body:    'Hanken Grotesk', system-ui, sans-serif;
  --font-mono:    'Spline Sans Mono', ui-monospace, monospace;

  /* — display voice: the DEFAULT values for the brand-voice slot.
       The three faces never vary per tenant (that is the Two-
       Silhouette Rule). How Archivo is *set* does — see LAYER 2b. */
  --display-width:    100;        /* Archivo wdth axis, 62–125 */
  --display-tracking: -0.01em;
  --display-case:     none;

  /* — shape group: ONE decision ("does this brand have an angular
       motif?"), three values that must move together. A cut corner
       on a pill reads as a rendering bug, and a cut edge with no
       extra padding crowds the label — so radius and pad are
       mechanical consequences of --slant, not separate choices. — */
  --slant:      none;
  --slant-pad:  0px;              /* extra padding-left, clears the cut */
  --cta-radius: var(--r-pill);    /* flattens when a slant is present */

  /* — radius — */
  --r-sm:   6px;
  --r-md:   12px;
  --r-lg:   20px;
  --r-pill: 999px;

  /* — spacing — */
  --sp-xs:  4px;
  --sp-sm:  8px;
  --sp-md:  16px;
  --sp-lg:  24px;
  --sp-xl:  40px;
  --sp-2xl: 64px;

  /* — motion: ease-out only, no bounce — */
  --ease:      cubic-bezier(0.22, 1, 0.36, 1);    /* out-quint */
  --ease-soft: cubic-bezier(0.33, 1, 0.68, 1);    /* out-cubic */
  --dur-fast:  120ms;
  --dur:       200ms;
  --dur-slow:  380ms;

  /* — z-index scale (semantic; never arbitrary 9999) — */
  --z-sticky:  100;
  --z-overlay: 900;
  --z-week:    950;
  --z-modal:   1000;
  --z-toast:   1100;

  /* — legacy aliases: consumed by older inline styles.
       Do not use in new code. — */
  --text-primary:   var(--ink);
  --text-secondary: var(--ink-secondary);
  --text-muted:     var(--ink-muted);
}


/* ================================================================
   LAYER 1b — COLD SURFACE VARIANT  (opt-in)

   Set data-surface="cold" on <html> for the original blue-black
   ramp. Everything else — tenant slot, derived layer, truth
   colors — is untouched.

   This WAS the default until warm ink was adopted as locked chrome
   (decided 2026-08-06). Kept because it is a genuine comparison
   baseline: mock-b-rail.html runs cold deliberately so warm vs cold
   can be judged on identical structure.

   It is anchored at #0A0D12, oklch hue 260 — the same hue family
   as the accent, which is what makes it read cold and slightly
   electric. That is exactly why it lost: the surface competed with
   the one voice instead of receding behind it, and against a
   franchise whose brand neutral is pure #000 it was also the less
   differentiated choice.

   Note this ramp runs at FULL steps, not the warm ramp's 0.62x
   compression — a cold near-black is darker (L 0.158 vs 0.222) and
   has the contrast headroom to afford them.
   ================================================================ */

:root[data-surface="cold"] {
  --bg:            #0A0D12;
  --surface:       #11151C;
  --surface-2:     #19202A;
  --surface-3:     #222B38;
  --border:        #283240;
  --border-strong: #3A4656;

  --ink:           #F2F6FB;   /* ~17.0:1 on --bg */
  --ink-secondary: #A6B2C2;   /*  ~8.5:1 on --bg */
  --ink-muted:     #6B7787;   /*  4.03:1 on --surface-2 — large text only */
  --placeholder:   #8A95A6;   /*  meets 4.5:1 on --surface-2 */
}


/* ================================================================
   LAYER 1c — NAVY SURFACE VARIANT  (opt-in)

   Set data-surface="navy" on <html>. This is the "tint the FIELD,
   not the accent" option, and it exists because of what the official
   brand package showed:

     PANTONE 274 C   #201751   oklch(0.26 0.101 283.8)

   That is the franchise's dominant colour — 53% of the logo's pixels
   — and it is unusable as an accent for two independent reasons. It
   sits at hue 283.8, outside the open lane and 15° from
   --event-camp; and at 1.07:1 against --bg it is the SAME LIGHTNESS
   as the background. It was never an accent. It is a field.

   So this ramp puts their brand where it actually belongs. The
   accent stays lane-legal blue, red stays chrome, and the franchise
   is carried by the surface everything else sits on.

   HOW IT IS BUILT
   Lightness steps are copied verbatim from the warm ramp, so this is
   a temperature swap rather than a redesign — warm vs navy is a
   like-for-like comparison (see mock-a-deck vs mock-d-navy, which
   are byte-identical below <body>).

   Chroma runs 0.045 → 0.075, deliberately FAR below the brand navy's
   0.101. The constraint is not contrast (chroma barely moves
   luminance — --event-camp holds 6.58:1 on --bg at every chroma
   tested). It is hue proximity: the field is 15.5° from
   --event-camp, so the two stay legible only because the chip is
   3.9x more saturated than the field. Push the field past ~0.08 and
   a purple chip on a purple field stops reading. Do not saturate it
   toward #201751 to be "more accurate" — accuracy here costs a
   semantic colour.

   INK is PANTONE Cool Gray 1 C verbatim (#DAD9D7), stepped down for
   the secondary and muted roles. Their grey is oklch hue 84.6 —
   WARM — even though the field is indigo, which is exactly the
   relationship the logo itself uses: warm grey type on a cold field.
   Deriving the ink ramp from scratch landed on #DBD9D7, one unit off
   their Pantone, so their value is used directly.
   ================================================================ */

:root[data-surface="navy"] {
  --bg:            #19182F;   /* hue 283.8, C 0.045 */
  --surface:       #1E1D38;
  --surface-2:     #252442;
  --surface-3:     #2B2A4D;
  --border:        #2F2D54;
  --border-strong: #3B3965;

  --ink:           #DAD9D7;   /* PANTONE Cool Gray 1 C — 12.27:1 on --bg */
  --ink-secondary: #B3B1AD;   /*  8.09:1 on --bg */
  --ink-muted:     #93908B;   /*  4.68:1 on --surface-2 */
  --placeholder:   #9A9893;   /*  5.17:1 on --surface-2 */
}


/* ================================================================
   LAYER 2 — TENANT SLOT

   The tokens a tenant may set. Everything else in this file is
   locked.

   ⚠ THE SLOT WAS WIDENED, 3 → 6. Read this before adding a 7th.

   It was three (accent, accent-deep, on-accent) on the theory that
   a tenant is a color and nothing more. The PickUp USA evaluation
   broke that: their brand is only ~40% color. The rest is a
   geometric uppercase display voice and one angular cut. Ship only
   their blue and you have a StatMason site with their hue — not a
   site their members recognize.

   So the slot now carries two groups:

     COLOR   --accent, --accent-deep, --on-accent
     VOICE   --display-width, --display-tracking, --display-case,
             --slant

   The shape group (--slant, --slant-pad, --cta-radius) is three
   values but ONE decision — the latter two are mechanical
   consequences of the first, not independent knobs. Count it as
   one when you are deciding whether the slot has grown too wide.

   The line that has NOT moved, and must not: a tenant may restyle
   the three faces but may never REPLACE them (The Two-Silhouette
   Rule), and may never touch surfaces, ink, truth colors, the
   event palette, spacing, radius, or motion. Those are the ~95%
   that make every StatMason league the same product. A tenant
   varies the voice; it does not vary the instrument.

   ── ON THE NAVY RAMP, which looks like a violation ──────────────
   The navy ramp in LAYER 1c is derived from a franchise Pantone,
   and surfaces are locked chrome. That is a real tension, and it is
   resolved by WHERE the choice lives, not by relaxing the rule:

     A tenant SELECTS a ramp. It cannot AUTHOR one.

   The ramps are a fixed, StatMason-owned set — cold, warm, navy —
   each one validated here against every truth color, every event
   hue and the whole ink scale before it ships. data-surface is a
   picker, not an escape hatch. A tenant that wants a fourth field
   gets it the way this one did: someone measures it, proves the
   event palette survives on it, and adds it to this file.

   That keeps the guarantee that matters — no tenant can produce a
   surface that quietly breaks a semantic colour — while admitting
   the thing the brand package made obvious, which is that for some
   franchises the field IS the identity and the accent is a detail.

   A tenant accent is legal only if it satisfies all four:
     · oklch hue within 186–269  (the one lane the locked event
       palette leaves open — see The Open-Lane Rule in DESIGN.md)
     · ≥4.5:1 against --surface-2  (it is used as text on chips)
     · ≥4.5:1 against its own --on-accent  (it is a button fill)
     · not green / red / gold      (The Truth-Color Rule)

   design-preview.html checks all four live. Use it.
   ================================================================ */

:root {
  /* Concord Blue — oklch(0.78 0.14 227).
     Dead center of the open lane, 71° from the nearest event hue,
     9.96:1 on --bg. This is the default for every tenant today. */
  --accent:      #19C8FF;
  --accent-deep: #00A6E5;   /* hover / pressed; small-size accent */
  --on-accent:   #04161D;   /* text + icons ON the accent fill */
}

/* ---- PickUp USA Fitness — the first real tenant ----------------
   Colour authority is the official brand package (the .ai file's
   spot definitions, corroborated by sampling the rendered PNGs).
   NOT their websites, which disagree with the package and with
   each other. See brand/pickup-usa/README.md.

     PANTONE 274 C           #201751   hue 283.8   53% of the mark
     PANTONE Cool Gray 1 C   #DAD9D7   hue  84.6   12%
     PANTONE 179 C           #E53E30   hue  29.2    7%
     PANTONE 1815 C          #802629   hue  22.7   <1%

   Display face is ITC Avant Garde Gothic Pro 700, uppercase on h1
   and brand buttons only. One clip-path in the whole stylesheet.

   COLOR — no colour in the MARK can be the accent. Their red is
   10.3° from --loss and --event-closed; their navy is outside the
   open lane and only 1.07:1 against --bg, which makes it a field,
   not an accent (that is what LAYER 1c is for).

   So the accent below comes from their WEBSITE, #2D62FF, hue 265.0
   — inside the lane with 4° to spare. It fails contrast as shipped
   (3.53:1 on the warm --bg), so lightness lifts while the hue is
   held EXACTLY at 265. Be honest about this in review: it is the
   least brand-grounded token here, and that is a consequence of
   their palette rather than an oversight.

   Their red lives in chrome instead — masthead, rules, the slant —
   never beside a win-loss column.

   VOICE — Avant Garde is geometric and WIDE, so Archivo's wdth
   axis goes UP to 115, not down. Tracking tightens to -0.02em
   because Archivo's caps run looser than Avant Garde's. This
   echoes the posture, not the letterforms: Archivo is a grotesque
   and will never have Avant Garde's circular bowls. Closing that
   gap means licensing a geometric — a real cost, not a tweak. */
[data-tenant="pickup-usa"] {
  --accent:      #6A94F4;   /* oklch(0.68 0.15 265) — 5.02:1 on warm --surface-2 */
  --accent-deep: #5D8BFC;   /* 4.61:1 on warm --surface-2 */
  --on-accent:   #050C22;   /* 6.61:1 under --accent */

  --display-width:    115;
  --display-tracking: -0.02em;
  --display-case:     uppercase;

  /* their one shape, verbatim. 15% is width-relative, so the angle
     steepens on narrow buttons and flattens on wide ones — matches
     how their nav CTA behaves. */
  --slant:      polygon(0 0, 100% 0, 100% 100%, 15% 100%);
  --slant-pad:  10px;
  --cta-radius: var(--r-sm);
}

/* ---- Future tenants -------------------------------------------
   Add a block per tenant, keyed off a data-tenant attribute on
   <html>. Set slot tokens and NOTHING else.

   html[data-tenant="northgate"] {
     --accent:      #4FB8FF;
     --accent-deep: #2A93E0;
     --on-accent:   #04121D;
   }
   ---------------------------------------------------------------- */


/* ================================================================
   LAYER 3 — DERIVED FROM THE ACCENT

   Hand-writing rgba(25,200,255,…) anywhere is what breaks tenant
   swapping — it hard-codes one hue into a value that is supposed
   to follow the accent. Every accent-tinted value in the system is
   computed here instead, so changing --accent changes all of them.

   color-mix() in oklab keeps tints perceptually even across hues;
   the sRGB equivalent goes muddy on warm accents.
   ================================================================ */

:root {
  /* tints — chips, hovers, low-opacity fills */
  --accent-dim:  color-mix(in oklab, var(--accent) 12%, transparent);
  --accent-mid:  color-mix(in oklab, var(--accent) 30%, transparent);
  --accent-edge: color-mix(in oklab, var(--accent) 45%, transparent);

  /* The same treatment for the two truth colours. A "you cannot make this
     session" control and a save confirmation both need a tinted field behind a
     coloured label, and every page that needed one was mixing its own literal
     rgba() — which is how --loss ended up hardcoded as rgba(255,93,107,…) in
     three places that a retint would have missed. Derived here so they follow
     --win and --loss like the accent tints follow --accent. */
  --win-dim:  color-mix(in oklab, var(--win) 12%, transparent);
  --win-edge: color-mix(in oklab, var(--win) 35%, transparent);
  --loss-dim:  color-mix(in oklab, var(--loss) 12%, transparent);
  --loss-edge: color-mix(in oklab, var(--loss) 35%, transparent);

  /* accent over a surface, pre-flattened (no alpha) — use when a
     translucent fill would let a neighbor's background bleed through */
  --accent-wash: color-mix(in oklab, var(--accent) 14%, var(--surface));

  /* the one hero glow (The Glow-Once Rule) */
  --accent-glow:
    0 0 0 1px color-mix(in oklab, var(--accent) 25%, transparent),
    0 14px 48px -12px color-mix(in oklab, var(--accent) 40%, transparent);

  /* fixed ambient wash behind heroes */
  --court-glow: radial-gradient(
    ellipse at center,
    color-mix(in oklab, var(--accent) 10%, transparent),
    transparent 68%
  );

  /* the "live game" event color IS the accent — never a separate hue */
  --event-game: var(--accent);

  /* neutral elevation — the only non-accent shadow in the system */
  --lift: 0 12px 32px -12px rgba(0, 0, 0, 0.6);
}


/* ================================================================
   REDUCED MOTION — every duration collapses, so no component has
   to special-case it. Individual transforms still need their own
   @media guard; this kills the timing globally.
   ================================================================ */

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 1ms;
    --dur:      1ms;
    --dur-slow: 1ms;
  }
}
