/* ═══ SPOKE SKIN v4 — DAYLIGHT ═════════════════════════════════════════════
   Loaded LAST, after spoke-skin.css, v2 and v3, on every page.

   v3's whole argument was that every generation of these pages uses the SAME
   custom-property names, so overriding :root reskins them without touching
   markup or logic. v4 takes that mechanism and turns the app over: from a warm
   near-black with an orange chrome and a jade answer, to white paper with ink
   type, hairline rules, and colour spent ONLY on a verdict.

   Why, in Ronan's words: people were struggling to read it. Measured off the
   live app before this pass:

     · 106 of 149 type sizes in the cruncher were under 13px.
     · 200+ letter-spacing rules at .12–.20em, most on UPPERCASE MONOSPACE,
       doing body-text work. That combination is the slowest thing to read.
     · Cards sat 1.07:1 above the background, hairlines 1.39:1. Nothing
       separated from anything.
     · #8b8574 and #5f5a4d carried most of the secondary text.

   ── WHAT THE COLOURS MEAN NOW ──────────────────────────────────────────────
   The rule is narrower than v3's and easier to hold:

     INK      everything. Chrome, type, the primary action, the thing you
              tapped. If it is furniture, it is ink or a grey off it.
     GREEN    good to go. You can have this.
     RED      in your way. A warning, never a discount and never a price.
     GREY     shut, or not known. Absence, not warning — a members-only
              Saturday is a closed door, not a problem.

   Colour lands on a VERDICT WORD, never on a number. A green number reads as
   a score; a green word reads as an answer. And every coloured word is a word,
   so red/green colour blindness (about 1 in 12 men) costs nothing.

   ── THE RAMP IS MEASURED, NOT PICKED ───────────────────────────────────────
   Every text node on all 38 pages was rendered and measured against its real
   computed background (audit.mjs). The first cut of this skin used #97918A
   for tertiary text, which came back at 3.12:1 — under the 4.5:1 floor, and
   the single most common failure in the app. The ramp moved:

     ink        #2A2724   14.9:1
     secondary  #5C574F    7.2:1
     tertiary   #7C746A    4.6:1   ← the AA floor for small text
     faint      #B8B2AB    2.1:1   ← RULES AND DISABLED ONLY, never type
     red        #B23A1E    6.0:1
     green      var(--key)    6.1:1

   Three visibly separate weights, all three over the line. Re-run the audit
   before changing any of them.

   The old token names all still resolve, so no page breaks: --ember, --lime,
   --jade and --gold point into the new language. Ember was chrome, so ember is
   ink. Jade meant an answer, so jade is the green. Gold meant THE one — that
   is emphasis, not hue, so gold is ink too.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  color-scheme: light;

  /* ── paper ── */
  --bg:        #FFFFFF;
  --bg-card:   #FFFFFF;
  --bg-elev:   #FFFFFF;
  --card:      #FFFFFF;

  /* ── the ink ramp ── */
  --ink:       #2A2724;
  --ink2:      #5C574F;
  --ink3:      #7C746A;
  --ink-soft:  #5C574F;
  --ink-mute:  #7C746A;
  --ink-dim:   #7C746A;   /* was the faintest tier; type cannot live at 2.1:1 */

  /* ── rules, not borders ── */
  --line:      #E0DCD7;
  --line-soft: #EEEBE7;
  --faint:     #B8B2AB;   /* rules and disabled controls only */

  /* ── chrome is ink ── */
  --ember:        #2A2724;
  --ember2:       #5C574F;
  --ember-bright: #2A2724;
  --amber:        #B23A1E;
  --gold:         #2A2724;
  --rust:         var(--key);
  --red:          #B23A1E;

  --lime:        var(--ember);
  --lime-bright: var(--ember2);
  --lime-dim:    #7C746A;

  /* ── the answer ── */
  --jade:   var(--key);
  --jade2:  var(--key);
  --jade-d: var(--key-pale);
  --lux:    var(--jade);
  --green:  var(--jade);

  /* ── peak tinge: a busy hour, shown not shouted ── */
  --tinge:      #FBEDE9;
  --tinge-line: #F2D6CE;

  --sans: 'Inter Tight', system-ui, sans-serif;
  --mono: 'Inter Tight', system-ui, sans-serif;
}

/* ── TIER HOOKS — unchanged from v3, visibility only, never colour ──────── */
body.free .pro              { display: none; }
body:not(.coach) .coachonly { display: none; }

/* ── the backdrop ───────────────────────────────────────────────────────────
   v3 painted a jade radial glow over a three-stop gradient. Paper does not
   glow, and that rule is on html,body in a sheet that loads before this one,
   so it has to be beaten outright. */
html, body {
  background: #FFFFFF !important;
  background-color: #FFFFFF !important;
  background-image: none !important;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

/* ── surfaces: no cards ─────────────────────────────────────────────────────
   The old app separated things by floating them on panels. Paper separates
   with a hairline and a left-aligned column, the way a scorecard or a draw
   sheet is set. So the card keeps its name and loses its box. */
.sheet, .modal, .panel, .card, .group, .box, .tile {
  background-color: #FFFFFF;
  border-color: var(--line);
  box-shadow: none;
}
.card, .group, .panel, .box, .tile {
  border-radius: 0;
  border-width: 0;
  border-top: 1px solid var(--line);
}
.modal, .sheet {
  border-radius: 0;
  border: 1px solid var(--line);
  box-shadow: 0 10px 40px rgba(42, 39, 36, .14);
}

/* ── the eyebrow ────────────────────────────────────────────────────────────
   v3 set these in monospace at .14em, uppercase, and half the app used the
   class for body text. Same class, quiet now: small, semibold, tertiary. */
.eyebrow, .label, .cap, .hl, .swq, .lvq, .s0q, .cascq, .bankh {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink3);
}

/* ── controls ───────────────────────────────────────────────────────────────
   One filled block for the thing you came to do, a hairline box for
   everything else. No gradients, no pills, no corners. */
.btn, .cta, .primary, .save, .wallgo, .cscdone {
  border-radius: 0;
  font-family: var(--sans);
  font-weight: 700;
  font-size: 17px;
  letter-spacing: .02em;
  text-transform: none;
}
.btn-lime, .btn-primary, .primary, .cta {
  background: #2A2724;
  background-image: none;
  color: #FFFFFF;
  border: 0;
}
.btn-ghost, .secondary {
  background: transparent;
  border: 1px solid #D5D0CA;
  color: var(--ink2);
}
/* The front door's JOIN button carries a "SYDNEY" sub-label that was a pale
   jade on a jade fill. Both ends of that pair are green now, so it reads 1:1.
   On a filled block a label is white — and this has to outrank the page's own
   .city-soon rule, hence the descendant selector. */
.city-opt .city-soon { color: #FFFFFF; opacity: .9; }

/* `.cta` is a button class in this app, but engine.html uses it for a whole
   section. A <section> is never a button — element+class outranks the button
   rule and gives the band its paper back. */
section.cta, section.primary {
  background: #FFFFFF;
  background-image: none;
  color: var(--ink);
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
}

/* Type sitting on one of our own filled blocks. The migration turns a fill and
   its text over together, but where the fill is set by class and the text
   inline (or the reverse) they can land on the same side. These put the text
   back on white. A verdict word keeps its own colour — it was chosen. */
:is([style*="background:#2A2724"], [style*="background-color:#2A2724"],
    [style*="background:#B23A1E"], [style*="background-color:#B23A1E"],
    [style*="background:var(--key)"], [style*="background-color:var(--key)"]) {
  color: #FFFFFF;
}

input, select, textarea {
  background: #FFFFFF;
  border: 0;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  border-radius: 0;
  font-family: var(--sans);
  font-size: 16px;   /* 16 stops iOS zooming the page on focus */
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-bottom-color: #2A2724;
}
input::placeholder, textarea::placeholder { color: var(--ink3); }
:focus-visible { outline: 2px solid #2A2724; outline-offset: 2px; }
[disabled], .disabled { color: var(--faint); }

/* ── the bottom nav, on every page ──────────────────────────────────────────
   The tab you are on is INK, not green. Green means "good to go — you can
   have this"; spending it on "you are here" is exactly the leak that costs
   the app its only way to point at an answer. Three different classes mark
   the active tab across the generations of these pages, so all three are
   named here — v3 only knew about .on, which is why the court tab was still
   coming up green. */
.nv.on, .nv.active, .nv.sel, .nv:active, .nav .on, .nav .active, .tabbar .on,
.tabbar .active, [aria-current="page"] {
  color: var(--ink) !important;   /* answering spoke-skin.css's own !important */
  font-weight: 700;
}
/* the active tab's icon carried a glow. Paper does not glow. */
.nv.active svg, .nv:active svg, .nv.on svg { filter: none !important; }
.nv, .nav a, .tabbar a { color: var(--ink3); }

/* ── numbers line up ────────────────────────────────────────────────────── */
.n, .num, .fig, .stat, .score, td, th { font-variant-numeric: tabular-nums; }

/* ── the type floor ─────────────────────────────────────────────────────────
   Inline sizes are raised in the markup by the migration, because CSS cannot
   outrank a style attribute without !important on every element. What is left
   here is the floor for anything set by class. */
small, .small, .tiny, .mini, .fine { font-size: 13px; }

/* ── links ──────────────────────────────────────────────────────────────── */
a { color: #B23A1E; }
a:hover { color: #2A2724; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}


/* ══ THE BOTTOM NAV DOES NOT MOVE ═════════════════════════════════════════
   Ronan, 29 Aug: "The directory bounces all over the place... it needs to be
   set at the bottom of the page (LIKE IN HOME). Obviously when not much
   content is on the page it lifts up."

   Two faults, and they were per-page because every page had written its own
   .nav rule:

   1. explore.html used `position: sticky`. Sticky only pins while there is
      content to scroll past it — on a short page it sits in normal flow and
      rides up under the content. That is the lift he saw.
   2. The rest were all `fixed`, but with different padding, so the bar stood
      67px tall on home, 72px on diary/game/courts and 91px on explore. Moving
      between tabs jumped the icons by up to 24px. Pinned, but not still.

   One geometry, defined once, here. `body nav.nav` (0,1,2) outranks every
   page's own `.nav` (0,1,0) whichever order the stylesheets load in, so this
   wins without !important and without editing ten files.

   Height is fixed rather than padding-derived, because padding-derived height
   is exactly what drifted — the icons and labels differ page to page, so only
   an explicit box makes them land in the same place.
   ═══════════════════════════════════════════════════════════════════════ */
body nav.nav{
  position:fixed; left:0; right:0; bottom:0;
  max-width:460px; margin:0 auto; z-index:120;
  box-sizing:border-box;
  height:calc(64px + env(safe-area-inset-bottom,0px));
  padding:0 8px env(safe-area-inset-bottom,0px);
  align-items:center;
  border-top:1px solid var(--line,#E0DCD7);
  background:rgba(255,255,255,.92);
  -webkit-backdrop-filter:blur(22px); backdrop-filter:blur(22px);
}
/* whatever a page calls its items, they centre in the same box */
body nav.nav > *{ align-self:center; }
/* and nothing hides behind it — the last thing on a page stays reachable */
body{ padding-bottom:calc(64px + env(safe-area-inset-bottom,0px)); }


/* ══ FREE FOREVER — NOTHING IS FOR SALE (Ronan, 1 Sep) ══════════════════════
   "app free forever". spoke-tier.js resolves every member to the tier that
   unlocks everything and stamps .freeforever on the body, so the gates below
   it never fire. This is the backstop for what the gates don't reach: the
   badges, locks and upgrade rows that are drawn unconditionally and only make
   sense when there is a plan to move up to.

   Hidden, not deleted — every one of these elements is still in its page and
   still wired to its handler. Turn FREE_FOREVER off in spoke-tier.js and the
   whole paid surface comes back exactly as it was. ══════════════════════════ */
body.freeforever #tierPill,
body.freeforever #planUpgradeRow,
body.freeforever .lock217,
body.freeforever .fr-lock,
body.freeforever .where-upsell { display: none !important; }

/* ══ THE MARK REPLACES THE S — shared copy (1 Sep) ══════════════════════════
   Most pages carry this rule inline beside their own header CSS. cruncher.html
   builds its header differently and only ever loads the skin, so the rule
   lives here too. Same rule, no conflict: it strips whatever disc the badge
   used to draw and lets the SVG fill the box the page already sized. ══ */
.spoke-mark-badge { background: none !important; background-image: none !important;
  border: 0 !important; box-shadow: none !important; color: transparent !important;
  font-size: 0 !important; line-height: 0 !important; padding: 0 !important;
  border-radius: 0 !important; overflow: visible !important;
  display: inline-block !important; vertical-align: middle; }
.spoke-mark-badge::before, .spoke-mark-badge::after { content: none !important; display: none !important; }
.spoke-mark-badge > svg { display: block; width: 100%; height: 100%; }


/* ══ THE KEY COLOUR ════════════════════════════════════════════════════════
   1 Sep 2026. v4 spent TWO hues — green for "good to go", red for "in your
   way" — while the direction settled in August was greyscale plus ONE accent
   on white, and the mark is a third thing, red, matching neither. One accent,
   and it is the mark's.

   Every green literal across the deploy now reads --key / --key-rgb, so the
   whole language moves from here — 314 sites in 22 files. TWELVE were left
   alone on purpose: the court-surface gradients and the tree circles in
   explore.html, and the court lines in engine.html. Those are SCENERY, not a
   verdict, and a red tennis court is not what anyone wants.

     green  #17703C  6.1:1 on white
     red    #D2402B  4.7:1 on white  ← the mark's own red, over the 4.5 floor

   TYPE takes --key2, not --key. At 4.7:1 the mark red clears the floor on
   white but not on its own .055 wash, and that wash is exactly where the wire
   label and the verdict words sit. #B23A1E is 6.0:1 and already in the ramp.

   WHAT HAD TO GIVE. Red meant IN YOUR WAY. Two things cannot both be red, so
   --warn drops to ink-grey — which is what the note at the top of this file
   already argues for: "a members-only Saturday is a closed door, not a
   problem." Nothing is lost, because colour here lands on a WORD and never on
   a number, so no meaning was ever carried by hue alone.

   TO GO BACK TO GREEN, this block only:
     --key:#17703C; --key2:#17703C; --key-rgb:23,112,60;
     --key-pale:#EAF3ED; --warn:#B23A1E;
   and revert the colour literals JS writes into SVG presentation attributes,
   which cannot be tokens because var() is not reliable there:
     sed -i '' 's/#D2402B/#17703C/g' app/cruncher.html   (script block only)
   then put back the four scenery values this pass corrected — deckHard and
   the tree line in cruncher.html, loCt on index.html. Re-run audit.mjs.
   ═════════════════════════════════════════════════════════════════════════ */
:root{
  --key:      #D2402B;
  --key2:     #B23A1E;
  --key-rgb:  210,64,43;
  --key-pale: #FBEDE9;
  --warn:     #5C574F;

  /* v4's "2" tokens are its text variants, so they take the darker red */
  --ember2: var(--key2);
  --jade2:  var(--key2);
}
