/* =========================================================
   THEME — Bridge Run.

   CARBON COPY of the Daily Sort the Food 3D palette, taken value-for-value on
   2026-08-05 from
   `Claude2CodeGames/Production department/the host portal/Daily Food Sort/public/theme.css`
   — which is itself the Overtake City Run palette with the user's teal primary
   ("this is the best theme", 2026-08-03) swapped in for Overtake's sunset orange.

   Copying the LOOK needs nothing but this file: the studio shell already IS this UI
   (same classes, same design card, byte-identical icons and font — all 54 icons and
   FredokaOne verified identical on 2026-08-05). The only thing that ever differed
   was the palette. NEVER fork shell.css to reskin.

   ---- DELIBERATE, INHERITED DEVIATIONS — keep them ----
   1. THE PLAY BUTTON IS TEAL, NOT GREEN. Overtake swaps its play button to
      `.btn.green` at runtime, so in that game green means both "play" and "watch an
      ad". Across our portfolio green means ONLY "this plays a rewarded ad" — a player
      who has met one of our games already reads it that way, and rewarded video is
      where the money comes from (`CLAUDE.md §5`).
   2. --gold and --green keep the studio's semantic values (currency / rewarded).

   ⚠️ THIS BLOCK MUST TARGET :root, NOT #gameShell.
   The bottom nav, every popup (.sh-ov — win, lose, shop, league, out-of-lives), the
   loading splash and the tutorial coachmark are deliberately SIBLINGS of the design
   card, not children of it (they must cover the true viewport, not the card). A theme
   scoped to #gameShell reaches the home screen and NOTHING ELSE — measured on
   triple-match: the nav and every popup stayed on the shell's default dark palette
   while the menu was cream. :root reaches all of it.

   ⚠️ VARIABLE NAMES: the shell reads --panel-2, --gold-dk, --tab and --nav-line.
   The old starter theme.css wrote --panel2, --panelDeep, --goldDk and --redDk, which
   the shell NEVER reads — verified 2026-08-05: 0 references each in shell.css, so
   those lines silently did nothing. The names below are the ones shell.css actually
   consumes. Check against shell.css before adding more.

   ⚠️ BRIDGE RUN'S BOARD IS NOT BLUE — RE-CHECK THIS PALETTE.
   These values were tuned against Daily Food Sort's deep saturated blue board. Bridge
   Run's world is a BRIGHT CYAN sky (#37d0ee) with a near-white track, which is much
   closer in hue AND value to the teal accent. The rule this palette follows is:
   THE UI MUST NEVER COMPETE WITH THE BOARD — much darker or much brighter, never the
   same hue family at a similar value. Right now --accent (#18c6c6) sits close to the
   game's own sky, so expect to push either the accent or the world colour apart once
   the shell is actually rendering over the game. Flagged, not yet fixed — it needs to
   be looked at, not guessed at.
   ========================================================= */
:root{
  /* ---- BLUE primary (user's pick, 2026-08-13 — replaced the 2026-08-03 teal) ----
     Everything that WAS teal is now blue. Near-white cool panels, deep blue-navy ink
     and nav, blue accent. The teal values are kept in the comments below so the old
     theme can be restored exactly if this is ever regretted.  (teal was #18c6c6) */
  --accent:#2f8fe8; --accent-dk:#1c6ab5; --accent-lt:#63b8ff; --accent-base:#1a63aa;

  /* ---- SEMANTIC: gold = currency, green = rewarded ad. Portfolio-wide, do NOT reassign. ----
     ⚠️ TEAL PRIMARY SITS NEARER GREEN THAN ORANGE DID. Green must mean ONLY "this plays a
     rewarded ad" across every game we ship. Teal and green are still clearly different hues
     (cyan vs leaf) and the green here is brightened to widen the gap, but if a player ever
     hesitates between the PLAY button and a rewarded-ad button, push --accent bluer
     (e.g. #12b0d6) rather than making green cooler — the ad colour is the one that stays fixed. */
  --green:#3ddb7d; --green-dk:#22a557;
  --gold:#ffd84d;  --gold-dk:#f0aa22;

  /* ---- furniture: cool near-white panels, deep teal-navy ink and nav ---- */
  --ink:#0b2130;
  /* --tab is the HARD SHADOW under panels, pills and the free-pick cards (see the
     box-shadow rules further down). It was a light TEAL (#7fc0d8) which left a green
     cast under every blue element. (was #7fc0d8) */
  --panel:#f2fbff; --panel-2:#dff2fb; --tab:#93b8de; --text:#0f2b36;
  /* 🔵 THE BOTTOM NAV BAR. Deep blue-navy rather than teal-navy — the same hue family as
     the buttons above it, several steps darker so it still reads as furniture and the
     active tab still pops off it.  (was #123344 / #0a1f2c / #05131c) */
  --nav1:#1b3a63;  --nav2:#0e2340;    --nav-line:#07152a;

  /* ---- button ramps (1 = face top, 2 = face bottom, 3 = the hard shadow, ink = label) ---- */
  /* 🔵 PLAY / TAP TO PLAY / the ACTIVE NAV TAB — the shell paints all three from this one
     ramp on purpose, so pressable things share a colour (CLAUDE.md §3c).
     🚨 DELIBERATELY BRIGHTER AND LIGHTER THAN THE BUY BUTTON (#5b93e6 → #2f62c4, further
     down). They must not become the same blue: the note by `.ubuy` records that TEAL was
     rejected for the buy button precisely because "two teal buttons stacked would make
     'start the game' and 'spend your coins' look like the same action" — and painting the
     whole UI one blue would recreate that, just in blue. PLAY is the brighter, more azure
     hero; BUY is deeper and more indigo. Separation is by BOTH lightness and hue.
     (was teal #4de0e0 / #12b3b3 / #0a7676) */
  --pri-1:#63b8ff; --pri-2:#2f8fe8; --pri-3:#1a63aa; --pri-ink:#ffffff;  /* PLAY */
  --go-1:#5ce68d;  --go-2:#2fc165;  --go-3:#1d8c4c;  --go-ink:#ffffff;   /* rewarded ad */
  --gd-1:#ffe066;  --gd-2:#ffbe2e;  --gd-3:#d99408;  --gd-ink:#402c00;   /* currency */
  --gh-1:#f2fbff;  --gh-2:#dff2fb;  --gh-3:#a9cfdd;                      /* neutral */

  /* ⚠️ --ava-* and --accent-base above are NOT read by shell.css or shell.js — verified
     2026-08-05 (0 references in both). In Daily Food Sort they are consumed by that game's OWN
     game-main.js (4 uses each) for its avatar/board furniture. They are kept here because this
     is a carbon copy and they cost nothing, but they currently do NOTHING in Bridge Run. If you
     add furniture that wants them, they're already defined; if you never do, they can go. */
  --ava-1:#f2fbff; --ava-2:#dff2fb;

  /* Text drawn straight over the 3D board rather than on a panel. */
  --text-scene:#ffffff;
}

/* ══ 🔺 UPGRADE SHELF — compact, with a big buy button ══════════════════════
   Overridden HERE, in the per-game theme, never in `shell.css` — that file stays
   byte-identical to `UI Kit/` across every game (`CLAUDE.md §4`). theme.css loads LAST, so
   these win on source order without touching the shared file.

   🚨 Sized by shrinking the PARTS, not with `transform:scale()`. A transform would shrink
   the cards visually while they still reserved full-size space, and `liftPlayAbove()`
   MEASURES the shelf's height to position the play stack above it — so it would lift by the
   old amount and leave a gap.

   🚨 THE HEIGHT CAME FROM THE ART BOX, not from padding. `.uart` is `aspect-ratio:1.15`,
   i.e. almost square, so it set the card's height off its width — narrowing the shelf alone
   would have made them narrower AND kept them tall. 2.1 is the number that actually makes
   them short.

   ⚠️ No `vw`/`vh` in here: this is inside the 450x800 design card, which is already scaled
   by one transform, so a viewport unit would be scaled twice (`UI-SCALING-STANDARD.md`).
   "A third of the card" is `150px`, not `33vw`. */

/* Narrower shelf: centred and fixed at 292px of the 450px card, rather than spanning it. */
#gameShell .gUpg{ left:50%; right:auto; width:336px; margin-left:-168px; gap:8px; }

#gameShell .ucard{ padding:6px 6px 6px; gap:4px; border-radius:12px; }
#gameShell .ucard .ulv{ width:21px; height:21px; font-size:11px; top:-7px; right:-6px; }
#gameShell .ucard .un{ font-size:10px; }
/* The height dial — see above. */
/* 🚨 AN EXPLICIT HEIGHT, and `aspect-ratio` turned OFF. This is the fix for the icon
   blowing out to 76px inside a 37px box: a percentage height on the child only resolves
   against a parent with a DEFINITE height, and an `aspect-ratio` box does not provide one —
   so `height:100%` fell back to auto and the square icon simply followed its own width.
   A plain `height` in px makes every percentage below it resolve properly. */
/* 🖼️ A LANDSCAPE ROUNDED RECTANGLE, SIZED OFF THE WIDEST ARTWORK. The well used to be
   near-square, which suited the old 1:1 token icons and wastes the frame now that the art
   is real illustration: `kick-power` is 407x318 (1.28:1) and only fills a square window
   across a third of its height. 52px against the card's 67px of inner width is 1.29:1 —
   the kick art now touches both edges. The tower (243x487, portrait) letterboxes into the
   middle of the same window, which is correct: one window, art centred in it, never
   stretched or cropped. */
#gameShell .ucard .uart{ aspect-ratio:auto; height:66px; border-radius:9px; }
/* 🚨 100%/100% + contain, NOT a percentage of one axis. `.uart` sizes itself with
   `aspect-ratio`, and a percentage HEIGHT does not resolve against an aspect-ratio box —
   it fell back to the icon's intrinsic 256px and rendered a **194px image inside a 95px
   card**, spilling out of its box. Bounding both axes and letting `object-fit:contain` do
   the letterboxing is the only form that is safe here. */
/* 🚨 A PLAIN PIXEL SIZE. Do not "improve" this into a percentage — three separate
   percentage attempts (`height:100%`, then a definite parent height, then `max-height:88%`)
   ALL failed and shipped a 60-75px icon inside a ~34px box. The reason: `.uart` is
   `display:grid` and its single track is content-sized, so the grid area has no definite
   height — against which a percentage height resolves to `auto` and a percentage max-height
   behaves as `none`. The icon then just sized itself from its own 256px source.
   The card lives inside the 450x800 design card, which is scaled by ONE transform, so a px
   size here scales with everything else. Keep it px. */
/* ⚠️ A PERCENTAGE WIDTH IS FINE HERE; A PERCENTAGE HEIGHT IS NOT. Only the height was ever
   the broken axis — `.uart` gets its WIDTH from the card (definite, so `%` resolves) and its
   HEIGHT from a content-sized grid track (indefinite, so `%` falls back to auto and the icon
   blows out to its intrinsic size). Width in `%` also means the art keeps filling the well if
   the shelf is ever re-widthed; a hardcoded px was already 18px short of the real 81px well.
   `contain` then letterboxes whatever shape the art is — the kick (1.28:1) nearly spans it,
   the tower (0.5:1) stands tall in the middle. Neither is stretched or cropped. */
#gameShell .ucard .uart img{ width:calc(100% - 4px); height:62px; object-fit:contain; }
#gameShell .ucard .pips i{ height:3px; }

/* 🪙 THE BUY BUTTON OWNS THE BOTTOM QUARTER. It is the only part of the card that is
   interactive, so it should be the easiest thing to hit — the rest is a label. */
#gameShell .ucard .ubuy{ height:32px; border-radius:10px; font-size:13px; font-weight:900;
                         margin-top:1px; }
#gameShell .ucard .ubuy img{ width:15px; height:15px; }

/* ── 🔘 THE BUY BUTTON IS NO LONGER GOLD ─────────────────────────────────────────
   Three gold buttons in a row under a gold coin counter was too much yellow, and it
   flattened the shelf: the buttons shouted louder than the artwork they are buying.

   🚨 DONE BY RE-POINTING THE RAMP, NOT BY REWRITING THE BUTTON. `.ubuy` is a real
   `.btn`, and the shell paints every button from four variables (face top, face bottom,
   ledge, label). Overriding those keeps the press animation, the disabled state and the
   ink outline exactly as the shell defines them — a hand-rolled background would have
   silently dropped all three.

   A calm steel blue. It went deep navy first, which matched the bottom nav but read as
   heavy and dead next to three bright cards — so it is lifted to a mid blue that still
   looks like a pressable key without shouting.
   🚫 THREE COLOURS WERE OFF THE TABLE, and only one of them for taste:
     • GOLD — what it was, and what the user rejected: three yellow buttons in a row under
       a gold coin counter was too much yellow, and they out-shouted the artwork.
     • GREEN — means "this plays a rewarded ad" across the whole portfolio (CLAUDE.md §5).
       Never reassign it; a player who has met one of our games already reads it that way.
     • TEAL — the PLAY button sits directly above the shelf, and two teal buttons stacked
       would make "start the game" and "spend your coins" look like the same action.
   The gold COIN ICON stays, so currency is still carried by gold — which is the part of
   that convention that actually matters. */
/* 🔵 RICHER BLUE (2026-08-13). The old ramp topped out at #7fa8d9 — a pale, greyed
   steel blue that read as washed out and slightly disabled against the near-white card
   (`--panel:#f2fbff`). Raised the SATURATION rather than the darkness: the note above
   records that deep navy was tried and read "heavy and dead" next to three bright cards,
   so going darker would repeat a rejected experiment. This is the same value range, more
   colour in it — a proper royal blue that still looks like a pressable key.
   The three bans above still hold: not gold, not green (= rewarded ad, portfolio-wide),
   not teal (the PLAY button sits directly above it). */
#gameShell .gUpg .ucard .ubuy{
  --c1:#5b93e6; --c2:#2f62c4; --base:#1d448c; color:#fff; text-shadow:0 2px 0 rgba(0,0,0,.28); }

/* ── 🎨 ON-THEME: why the cards read as foreign objects ──────────────────────────
   Diagnosed by measuring them against the shell furniture that DOES look right in this
   theme (`.profile`, `.chip`, `.btn`). Three separate causes, only one of them cosmetic:

   1. 🔴 **THE PIPS WERE INVISIBLE.** `shell.css` fills an unspent pip with
      `rgba(255,255,255,.16)` — which is correct on the shell's DEFAULT DARK panel and
      completely wrong here, because this game's theme is LIGHT (`--panel:#f2fbff`). White
      at 16% on a near-white card is nothing, so the progress track simply did not render
      and the card had an unexplained empty strip between the art and the button. That is
      not a style preference, it is a light-theme bug — and it is exactly the kind of thing
      a theme override exists to fix, rather than editing shell.css (CLAUDE.md §4).
   2. **No ledge.** The card's `0 4px 0 rgba(0,0,0,.13)` is invisible on a near-white face
      sitting over a bright orange 3D scene, so the cards floated flat while every button,
      badge and pill around them sits on a solid coloured ledge. That contrast — everything
      else physical, the cards flat — is most of what reads as "out of place".
   3. **The art well was the same value as the card.** `--panel-2` (#dff2fb) against
      `--panel` (#f2fbff) is a ~5% step, so the well did not read as a well.

   ⚠️ THE OUTLINE WEIGHTS ARE LEFT ALONE ON PURPOSE. They look thin next to the button, but
   that is `outlines.css` — a 0.55x multiplier baked from the ?dev tuner — doing what it was
   told: panels 1px, interactive things 2px. `.profile` and `.chip` measure 1px too, so the
   card is IN step with the rest of the UI there. Thickening it here would put it out.  */
#gameShell .gUpg .ucard{
  /* Same two-part shape as `.btn`: a solid ledge plus a soft contact shadow. */
  box-shadow:0 4px 0 var(--tab), 0 7px 9px rgba(11,33,48,.16); }
#gameShell .gUpg .ucard .un{ font-size:10px; color:var(--text); }
#gameShell .gUpg .ucard .uart{
  /* Reads as recessed rather than as another flat panel. */
  box-shadow:inset 0 2px 4px rgba(11,33,48,.16); }
/* 🚨 BOTH STATES, or the `.on` fill is lost. This selector is one class more specific than
   the shell's `#gameShell .ucard .pips i.on`, so styling only the base pip would have won
   against the gold gradient too and left every pip looking unspent. */
#gameShell .gUpg .ucard .pips i{ background:rgba(11,33,48,.16); }
#gameShell .gUpg .ucard .pips i.on{ background:linear-gradient(var(--gold),var(--gold-dk)); }

/* ══ 🪙 THE COIN PAYOUT, MADE BIG ENOUGH TO NOTICE ═══════════════════════════════
   The reward flight is the moment a run's coins become the player's coins, and now that a
   run ENDS at the menu (see `endRunAtMenu` in game.html) it is also the handover into the
   upgrade shelf. At the shell's default 30px it was easy to miss entirely.

   🚨 THE NEGATIVE MARGIN IS NOT PADDING — IT KEEPS THE FLIGHT ON ITS PATH.
   `flyReward` positions each coin with `left`/`top` already offset by HALF the size it
   thinks the coin is, and animates it to a target computed the same way. Growing the coin
   in CSS alone would therefore shift both endpoints down and right by half the extra size,
   so the coins would land low of the counter and the "into your balance" read would break.
   Pulling back half the growth in margin cancels that exactly.

   🚨 EXPRESSED IN `--ui-scale`, NOT IN FLAT PIXELS. The shell sizes the coin as
   `30 * --ui-scale` so it stays the same size RELATIVE TO THE UI on every device. A flat
   `56px !important` would be correct on one screen and wrong on every other — the exact
   trap UI-SCALING-STANDARD.md is written about. 1.85x of whatever the shell chose stays
   proportional at any scale.
   (`!important` is required: `flyReward` writes width/height as INLINE styles.) */
.sh-fly{
  --br-fly: calc(30px * var(--ui-scale, 1));
  width:  calc(var(--br-fly) * 1.85) !important;
  height: calc(var(--br-fly) * 1.85) !important;
  margin: calc(var(--br-fly) * -0.425) 0 0 calc(var(--br-fly) * -0.425);
  filter:drop-shadow(0 3px 5px rgba(0,0,0,.42));
}

/* The counter reacting as they land — see `coinCounterReacts` in game.html.

   🚨 THE PILL ITSELF MUST NOT BE TRANSFORMED. Scaling the whole chip is the obvious way to
   pop a counter and it is wrong here: the chip grows from its centre, so its left edge
   sweeps outwards — MEASURED at 9.2px — and the coin counter visibly lurched left on every
   single landing. It reads as the UI glitching, not as a reward arriving.
   So the two halves are split by what they cost:
     • the PILL gets a `box-shadow` ring, which paints outside the box and moves nothing;
     • the NUMBER gets the scale, as an inline-block inside the pill, where a transform
       cannot push its neighbours around either.
   Same energy, zero layout movement. */
#gameShell .chip.brcoin{ animation:brCoinFlash .45s ease-out; }
@keyframes brCoinFlash{
  0%   { box-shadow:0 3px 0 rgba(0,0,0,.13), 0 0 0 0 rgba(255,216,77,.9); }
  35%  { box-shadow:0 3px 0 rgba(0,0,0,.13), 0 0 0 8px rgba(255,216,77,.42); }
  100% { box-shadow:0 3px 0 rgba(0,0,0,.13), 0 0 0 0 rgba(255,216,77,0); }
}
#gameShell .chip.brcoin #shCoins{ display:inline-block;
  animation:brCoinNum .45s cubic-bezier(.2,1.7,.4,1); }
@keyframes brCoinNum{ 0%,100%{ transform:scale(1); } 40%{ transform:scale(1.45); } }

/* 🚨 HIDE THE SHELL'S COUNTDOWN WHILE A LEVEL IS PARKED BEHIND THE MENU.
   `runCountdown()` fires unconditionally from the shell's `startLevel`, and its timers are
   private, so it cannot be cancelled from the game — only ignored. `Game.go` refuses to
   start the run; this stops the player watching "3 · 2 · 1 · GO!" tick away over a home
   screen where nothing is about to happen. */
body.brmenu .cdown{ display:none !important; }


/* ══ 🔢 "LEVEL n" INSTEAD OF THE PIP TRACK ═══════════════════════════════════════
   The shell draws upgrade progress as a row of little fill bars. On a card this size they
   are five 17x3px slivers — too small to count at a glance, so they read as decoration
   rather than as information, and the one thing the player actually wants to know ("what
   level is my kick?") had to be read off the corner badge instead.
   A word and a number says it outright. The pips are hidden rather than removed, because
   removing them means editing `shell.js` — see `upgradeLevelLabel` in game.html for the
   half that writes the text. */
#gameShell .ucard .pips{ display:none; }
#gameShell .ucard .brlvl{
  font:400 10px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
  color:var(--text); opacity:.72; letter-spacing:.04em; margin:1px 0 1px;
  white-space:nowrap; }

/* ══ 📊 THE WIN PANEL IS A SUM, NOT A STAR RATING ════════════════════════════════
   The stars rated the run against thresholds the player never sees, on an axis they stop
   controlling the moment the kick leaves — so the panel's biggest element was its least
   informative. What replaces them is the actual equation, and every term is something the
   player did:  BLOCKS carried  x  MULTIPLIER kicked into  =  COINS.
   See `showWinBreakdown` in game.html for the half that writes the numbers. */
.sh-ov#shWin .stars{ display:none; }

#brSum{ width:100%; }
#brSum .brsRow{ display:flex; align-items:baseline; justify-content:center; gap:8px; }
#brSum .brsK{ font:400 12px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
              color:var(--text); opacity:.6; letter-spacing:.06em; }
#brSum .brsV{ font:400 30px/1 'Fredoka One','Baloo 2',system-ui,sans-serif; color:var(--text); }
#brSum .brsV2{ font:400 14px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
               color:var(--text); opacity:.75; }
#brSum .brsNew{ font:400 15px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
                color:#0e7a48; background:linear-gradient(var(--go-1),var(--go-2));
                border:2px solid var(--ink); border-radius:9px; padding:3px 10px;
                box-shadow:0 3px 0 var(--go-3); }

/* The equation. Two value cells with an operator between them — a row rather than a list,
   because "a times b" is one idea and stacking it reads as two unrelated stats. */
#brSum .brsSum{ display:flex; align-items:center; justify-content:center; gap:10px;
                margin-top:9px; }
#brSum .brsCell{ display:flex; flex-direction:column; align-items:center; gap:2px;
                 background:var(--panel-2); border:2px solid var(--ink); border-radius:11px;
                 padding:6px 12px; min-width:74px; box-shadow:0 3px 0 var(--tab); }
#brSum .brsCell b{ font:400 24px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
                   color:var(--text); }
#brSum .brsCell i{ font:400 9px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
                   font-style:normal; color:var(--text); opacity:.6; letter-spacing:.06em; }
#brSum .brsOp{ font:400 20px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
               color:var(--text); opacity:.55; }

/* 🚨 STAGGERED, AND EVERY ROW STARTS AT opacity:0. The rows are re-created on each win
   (see showWinBreakdown), which is what re-triggers these — a class toggle would have to be
   removed and reflowed on eight elements first, and any one left half-played shows a row
   frozen mid-slide. Delays are ordered cause -> effect: the kick, then the record it beat,
   then the sum it earned. */
#brSum .brsKick{ opacity:0; animation:brsIn .34s ease-out .10s forwards; }
#brSum .brsRec { opacity:0; animation:brsIn .34s ease-out .26s forwards; }
#brSum .brsSum { opacity:0; animation:brsIn .34s ease-out .42s forwards; }
@keyframes brsIn{ from{ opacity:0; transform:translateY(9px) scale(.94); }
                  to  { opacity:1; transform:translateY(0)   scale(1);   } }
/* The record badge lands harder than the rows around it — it is the only line on this panel
   that is not true of every run. */
#brSum .brsRec.isnew{ animation:brsPop .46s cubic-bezier(.2,1.7,.4,1) .26s forwards; }
@keyframes brsPop{ from{ opacity:0; transform:scale(.5) rotate(-6deg); }
                   to  { opacity:1; transform:scale(1)  rotate(0deg);  } }

/* The coin pill is the ANSWER, so it lands last — after the sum that produces it. */
.sh-ov#shWin .reward-pills{ opacity:0; animation:brsIn .38s ease-out .74s forwards; }

/* 🚫 THE EMPTY MISSION BAR. `missions:[]` is a deliberate decision (see the note in
   Shell.init — the shell's missions pay GEMS and this game has no gem sink), but the shell
   still renders the bar and it sits at the top of the screen reading a permanent
   "— · 0 / 0" with an empty progress track. A promise the game visibly never keeps.
   Hidden here rather than by touching shell.js. Delete this rule the day missions come
   back; nothing else has to change. */
#gameShell #shMission{ display:none; }

/* ══ 🏆 THE CHASE — the three people in front of you ═════════════════════════════
   Sits below the sum and above COLLECT: the last thing read before the tap should be the
   reason to take it. See `leagueStanding` / `showWinBreakdown` in game.html. */
#brSum .brsChase{ margin-top:10px; border-top:2px dashed rgba(11,33,48,.18); padding-top:8px; }
#brSum .brsChaseHead{ font:400 10px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
                      color:var(--text); opacity:.58; letter-spacing:.08em;
                      text-align:center; margin-bottom:6px; }
#brSum .brsChaseRow{ display:flex; align-items:center; gap:7px; padding:4px 8px;
                     border-radius:8px; background:var(--panel-2); margin-bottom:4px;
                     opacity:0; animation:brsIn .3s ease-out forwards; }
#brSum .bcR{ font:400 10px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
             color:var(--text); opacity:.5; min-width:22px; }
#brSum .bcN{ font:400 12px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
             color:var(--text); flex:1; text-align:left; white-space:nowrap;
             overflow:hidden; text-overflow:ellipsis; }
#brSum .bcD{ font:400 13px/1 'Fredoka One','Baloo 2',system-ui,sans-serif; color:var(--text); }
/* The GAP is the actionable number — "+2.4" is a target you can picture kicking; the rival's
   total is just a fact about somebody else. So the gap is the one that gets the colour. */
#brSum .bcG{ font:400 11px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
             color:#0e7a48; background:rgba(61,219,125,.22); border-radius:6px;
             padding:2px 5px; min-width:38px; }
#brSum .brsChaseRow.brsTop{ justify-content:center;
                            font:400 12px/1.3 'Fredoka One','Baloo 2',system-ui,sans-serif;
                            color:var(--text); opacity:0; padding:7px 8px; }

/* The kick's multiplier now rides next to the distance it measures, rather than sitting in
   the coin equation it no longer feeds — see KICK_COINS in game.html. */
#brSum .brsMult{ font:400 15px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
                 color:var(--text); opacity:.62; }


/* ══ 🪙 THE COIN ICON, ~50% BIGGER EVERYWHERE ════════════════════════════════════
   One rule per place it appears, all lifted together — a coin that is big in the top bar
   and small on a button reads as two different currencies. The shell's sizes are the
   baseline each is scaled from:
     top-bar chip   22 -> 32
     reward pill    24 -> 36
     buy button     15 -> 22   (already themed here, see the .ubuy block above)
   The flying reward coins are NOT in this list — they are sized in JS from `--ui-scale`
   and were already scaled separately (see the .sh-fly block); doubling up on them here
   would make the payout coins comically large. */
#gameShell .chip img{ width:32px; height:32px; }
.sh-ov .rpill img{ width:36px; height:36px; }
#gameShell .gUpg .ucard .ubuy img{ width:22px; height:22px; }
/* The pills have to grow with the icon or it crops — their padding was set for a 22px mark. */
#gameShell .chip{ padding:2px 10px 2px 5px; border-radius:16px; }
.sh-ov .rpill{ padding:7px 15px; }

/* ══ 🎁 THE FREE-PICK PANEL ══════════════════════════════════════════════════════
   Three cards on the home screen after runs 1 and 2 — see `showFreePick` in game.html.
   🚨 Lives on <body>, above the shell's overlays, for the same reason the NEW BEST banner
   does: #gameShell is z-index 10 and the shell's panels are SIBLINGS above it, so a child
   of the design card cannot cover the screen. Scaled by --ui-scale to stay card-sized. */
/* ══ 📦 BOX-SIZING FOR THE GAME'S OWN OUT-OF-CARD PANELS ═══════════════════════
   🚨 THIS IS WHY THE FREE-UPGRADE PANEL LOOKED OFF. shell.css:81 scopes its border-box
   reset to `#gameShell *, .col *, .sh-ov *, #shNav *, #shSplash *, #shTut *` — and every
   panel this game adds itself lives on <body>, OUTSIDE the design card, matching none of
   them. So they inherited the browser default, content-box, and every `width:` in this
   file silently meant "width PLUS padding PLUS border":

       .bpCard  width:392px + 32 padding + 8 border  ->  432px rendered  (+40)
       .bpLv    width:59px  + 14                     ->   73px rendered  (+14)

   432px in a 450px design card is 96% of the full width, against ~289px for every shell
   panel — so it read as bursting out of the layout rather than sitting in it, and the
   oversized LEVEL pills crowded their own cards.

   `#brQuit` was never affected and that is the tell: it is built with `className='sh-ov'`,
   so the shell's own selector already caught it.

   Same family as PITFALLS.md #7 — anything outside the card inherits none of its rules and
   has to restate them. Fixing it here, in the game's own file; shell.css stays byte-identical
   (CLAUDE.md §4). Any NEW body-level panel must be added to this list. */
#brPick, #brPick *, #brNoAd, #brNoAd *, #brSum, #brSum *, #brStart, #brStart *,
#brBest, #brBest *, #brPts, #brPts *, #brTapPlay, #brTapPlay *{ box-sizing:border-box; }

/* 🔴 POSITIONING/SCALING/ENTRANCE ARE THE SHELL'S NOW — see the note in showFreePick().
   These rules used to re-implement `.sh-ov`: fixed inset:0, its own opacity transition, its
   own `transform:scale(var(--ui-scale))` on .bpCard and its own `bpIn` keyframes. All of it
   is deleted rather than tweaked, because a second implementation of the same thing is what
   drifted out of step in the first place. What stays below is ONLY what makes this panel
   look like a free-upgrade picker: the heading, the three option cards, the art and pills.
   ⚠️ Do NOT re-add position/transform/animation here. If the panel needs to move or scale
   differently from every other overlay, that is a shell-level question, not a per-game one. */
#brPick.sh-ov{ z-index:70; }        /* above the other overlays; everything else inherited */
#brPick--dead{ position:fixed; inset:0; z-index:70; opacity:0; transition:opacity .22s;
         display:grid; place-items:center; }
#brPick--dead.on{ opacity:1; }
#brPick--dead .bpBack{ position:absolute; inset:0; background:rgba(11,33,48,.62); }
#brPick--dead .bpCard{ position:relative; width:392px; padding:18px 16px 20px; text-align:center;
                 background:var(--panel); border:4px solid var(--ink); border-radius:22px;
                 box-shadow:0 8px 0 var(--tab), 0 16px 26px rgba(11,33,48,.34);
                 transform:scale(var(--ui-scale,1)); transform-origin:50% 50%; }
#brPick--dead.on .bpCard{ animation:bpIn .44s cubic-bezier(.2,1.5,.35,1); }
@keyframes bpIn{ from{ transform:scale(calc(var(--ui-scale,1) * .7)); opacity:0; }
                 to  { transform:scale(var(--ui-scale,1));            opacity:1; } }
#brPick .bpHead{ font:400 30px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
                 color:var(--gold); -webkit-text-stroke:4px var(--ink); paint-order:stroke fill;
                 text-shadow:0 4px 0 rgba(0,0,0,.3); }
#brPick .bpSub{ margin:6px 0 14px; opacity:.62; color:var(--text);
                font:400 12px/1 'Fredoka One','Baloo 2',system-ui,sans-serif; }
#brPick .bpRow{ display:flex; gap:9px; justify-content:center; }
/* Each card is a real button: the shell's press language (ink outline + hard ledge) so it
   reads as tappable rather than as three pictures. */
#brPick .bpOpt{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; align-items:center;
                gap:4px; padding:10px 7px 9px; cursor:pointer; opacity:0;
                background:var(--panel-2); border:3px solid var(--ink); border-radius:14px;
                box-shadow:0 5px 0 var(--tab); transition:transform .07s, box-shadow .07s;
                animation:bpOptIn .34s ease-out forwards; }
@keyframes bpOptIn{ from{ opacity:0; transform:translateY(12px); }
                    to  { opacity:1; transform:translateY(0); } }
#brPick .bpOpt:active{ transform:translateY(4px); box-shadow:0 1px 0 var(--tab); }
#brPick .bpArt{ width:100%; height:62px; display:grid; place-items:center;
                background:var(--panel); border:2px solid var(--ink); border-radius:10px; }
#brPick .bpArt img{ width:calc(100% - 8px); height:56px; object-fit:contain; }
#brPick .bpOpt b{ font:400 12px/1 'Fredoka One','Baloo 2',system-ui,sans-serif; color:var(--text); }
#brPick .bpOpt i{ font:400 9px/1.25 'Fredoka One','Baloo 2',system-ui,sans-serif; font-style:normal;
                  color:var(--text); opacity:.6; min-height:22px; }
#brPick .bpLv{ font:400 10px/1 'Fredoka One','Baloo 2',system-ui,sans-serif; color:#0e7a48;
               background:rgba(61,219,125,.24); border-radius:6px; padding:3px 7px; }

/* ══ ▶️ TAP TO PLAY ══════════════════════════════════════════════════════════════
   Sits directly above the upgrade shelf — see `tapToPlayPrompt` in game.html. The shelf is
   147px tall at `bottom:88px`, so 244px clears it with a little air.
   🚨 pointer-events:none: the tap it asks for has to reach the canvas underneath. */
#gameShell #brTapPlay{ position:absolute; left:0; right:0; bottom:244px; text-align:center;
  pointer-events:none; z-index:4; opacity:0; transition:opacity .18s; }
#gameShell #brTapPlay.on{ opacity:1; }
/* −20% again (2026-08-13). Type, padding and radius all scale together — shrinking the
   font alone leaves a fat pill around small text. Border held at 2px: at this size 1px
   reads as a hairline and loses the drawn-object look the rest of the UI has. */
#gameShell #brTapPlay span{ display:inline-block; padding:6px 14px; border-radius:14px;
  background:linear-gradient(180deg,var(--pri-1) 0 44%,var(--pri-2) 56% 100%);
  border:2px solid var(--ink); box-shadow:0 3px 0 var(--pri-3), 0 5px 6px rgba(0,0,0,.16);
  color:var(--pri-ink); letter-spacing:1px;
  font:400 13px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
  animation:brTapPulse .8s ease-in-out infinite alternate; }
@keyframes brTapPulse{ from{ transform:scale(1); } to{ transform:scale(1.07); } }

/* 🚨 THE FREE-PICK PANEL MUST NOT SIT ON TOP OF THE SHELF. It is a modal, so covering the
   screen is its job — but the one thing it must NOT hide is the three cards it is about to
   change, or the player cannot see what they just won appear. Nudged up so the shelf stays
   visible below it, and the scrim lightened so the cards read through. */
/* 🪤 `padding-top:12%` was a moving target: percentage padding ALWAYS resolves against the
   containing block's WIDTH, even for the top edge. On a fixed inset:0 element that means the
   panel's vertical position swung with the window's WIDTH — 52px at 436 wide, 97px in
   landscape — so the same panel sat in a different place on every device.
   Stated in design px and scaled the same way the panel is, so it holds still. */
/* (was a bespoke top-offset; .sh-ov centres it like every other panel) */
#brPick--dead .bpBack{ background:rgba(11,33,48,.45); }

/* ══ 🎁 MORE GAMES RAIL — DOWN 20% ══════════════════════════════════════════════
   shell.css puts both rails at `top:150px`; this drops the LEFT one (the MORE /
   cross-promo gift) to 180. Only `.rail.l` moves — `.rail.r` keeps the shell's
   position, so nothing else in the portfolio shifts.
   🚨 An OVERRIDE in theme.css, not an edit to shell.css — the shell is never forked
   (CLAUDE.md §4), and this game is the only one that wants the gift lower. */
#gameShell .rail.l{ top:180px; }

/* ══ 📐 WIN PANEL vs THE "NEW BEST" BANNER — STOP THE OVERLAP ═══════════════════
   The banner (`#brBest`, positioned in game.html) sits at a fraction of the CARD height;
   the win panel is centred in the overlay. On a record run those two landed on top of
   each other — banner, panel corner and the running score all inside the same 40px.

   Fixed from BOTH ends so neither has to move far: the banner went 19% → 13%, and the
   panel drops 34px here. Adjust them together — moving only one just relocates the
   collision.

   🚨 `margin-top`, NOT `transform`. `.sh-ov.show .panel` animates `transform` (sh-popIn,
   and it carries `--ui-scale`), so adding a translate there would be overwritten the
   moment the pop-in plays. Margin shifts the layout box and the scale still applies on top.

   🚨 Scoped to `#shWin`. The fail and quit panels have no banner over them and stay
   centred where the shell puts them. */
/* 16px, not 34: a full record-run panel already ends within a few px of the viewport
   bottom, so a bigger nudge clips COLLECT on a short screen. The banner does the rest
   of the work by measuring this panel — see `placeBest()` in game.html. */
.sh-ov#shWin .panel{ margin-top:16px; }

/* ══ 🔴 SUBSCRIBE BAR — CLOSE THE GAP UNDER IT ══════════════════════════════════
   shell.css reserves 76 card units for the bar (`#gameShell.subbar .top`), sized for a
   taller bar than ours: MEASURED, the bar is ~40 card units, so 76 leaves ~36 units of
   dead air and the coin chips float in the middle of the sky. 48 = the bar's 40 plus 8
   of breathing room. `.gTitle` moves up by the same 28 so the logo keeps its spacing.
   🚨 OVERRIDES of shell.css, living here — the shell is never forked (CLAUDE.md §4).
   Re-measure if the bar ever gains a second line. */
#gameShell.subbar .top   { padding-top:48px; }
#gameShell.subbar .gTitle{ top:110px; }

/* ══ 📺 HIDE THE "+GEMS" REWARDED BUTTON ═════════════════════════════════════════
   🚨 GEMS HAVE NO SINK IN THIS GAME. The shell's store offers a rewarded ad for gems next
   to the one for coins, but nothing here spends gems — missions are off (they pay gems, see
   the note in Shell.init) and there is no gem exchange configured. Offering a video for a
   currency the player can never use is worse than offering nothing: it teaches them the
   rewards are worthless, and that lesson carries to the COINS button sitting beside it,
   which is the one that actually pays.
   Hidden here rather than in shell.js, which stays byte-identical (CLAUDE.md §4). Delete
   this rule the day gems get a sink. */
#shShopGems{ display:none !important; }

/* ══ 📺 "NO ADS RIGHT NOW" POPUP ═════════════════════════════════════════════════
   Replaces the shell's 1.8s toast for the rewarded-ad failure only — see `noAdPopup` in
   game.html. The player pressed a green button expecting coins and got nothing; that needs
   an answer they cannot miss, or the button reads as broken.
   🚨 On <body> above the overlays, same reason as the free-pick panel and the NEW BEST
   banner: #gameShell is z-index 10 and the shell's panels are SIBLINGS above it, so a child
   of the design card cannot cover the screen. Scaled by --ui-scale to stay card-sized. */
#brNoAd{ position:fixed; inset:0; z-index:75; display:grid; place-items:center;
         opacity:0; transition:opacity .2s; }
#brNoAd.on{ opacity:1; }
#brNoAd .naBack{ position:absolute; inset:0; background:rgba(11,33,48,.55); }
#brNoAd .naCard{ position:relative; width:320px; padding:22px 20px 20px; text-align:center;
  background:var(--panel); border:4px solid var(--ink); border-radius:22px;
  box-shadow:0 8px 0 var(--tab), 0 16px 26px rgba(11,33,48,.34);
  transform:scale(var(--ui-scale,1)); }
#brNoAd.on .naCard{ animation:naIn .4s cubic-bezier(.2,1.5,.35,1); }
@keyframes naIn{ from{ transform:scale(calc(var(--ui-scale,1) * .72)); opacity:0; }
                 to  { transform:scale(var(--ui-scale,1));             opacity:1; } }
#brNoAd .naIcon img{ width:56px; height:56px; object-fit:contain; opacity:.85; }
#brNoAd .naHead{ margin:8px 0 6px; color:var(--text);
  font:400 21px/1.1 'Fredoka One','Baloo 2',system-ui,sans-serif; }
#brNoAd .naSub{ color:var(--text); opacity:.7; margin-bottom:16px;
  font:400 12px/1.5 'Fredoka One','Baloo 2',system-ui,sans-serif; }
/* 🚨 THE BUTTON HAS TO BE PAINTED HERE. `shell.css` scopes its button ramps to
   `#gameShell .btn` and `.sh-ov .btn` — and this popup is a direct child of <body> (it has
   to be, to sit above the shell's own overlays), so it matches NEITHER and fell back to the
   bare white default. Same set of variables the shell would have applied, so it still reads
   as the same PLAY-blue key and still follows theme.css. */
#brNoAd .btn{ width:100%; height:46px; cursor:pointer; border-radius:19px; font-weight:800;
  border:4px solid var(--ink); color:var(--pri-ink);
  background:linear-gradient(180deg,var(--pri-1) 0 44%,var(--pri-2) 56% 100%);
  box-shadow:0 5px 0 var(--pri-3), 0 8px 9px rgba(0,0,0,.18);
  font:400 18px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
  transition:transform .07s, box-shadow .07s; }
#brNoAd .btn:active{ transform:translateY(4px);
  box-shadow:0 1px 0 var(--pri-3), 0 3px 5px rgba(0,0,0,.18); }

/* ══ 💎 THE DIAMOND CURRENCY IS GONE FROM THE UI ═════════════════════════════════
   Nothing in this game costs gems, nothing pays them (missions are off precisely because
   they pay gems), and no exchange is configured — so the counter only ever reads 0 and the
   rewarded ad beside the coin one pays into a void. A reward the player can never spend
   teaches them the rewards here are worthless, and that lesson carries to the coin offer
   sitting next to it, which is the one that actually pays.
   🚨 HIDDEN, NOT REMOVED. The shell's gem plumbing (missions, exchange, the +gems ad) is
   left intact so it can come back the day there is something to spend them on — delete
   these two rules and it is all still wired. */
#gameShell #shGems, #gameShell .chip:has(#shGems){ display:none !important; }
#shShopGems{ display:none !important; }

/* ══ 🏷️ THE "AD" BADGE WAS BEING CLIPPED IN THE STORE ═══════════════════════════
   `.sh-ov .rewtag` is `position:absolute; top:-12px` — it is MEANT to overhang the top of
   its button. But `.sh-ov .shop-scroll` is `overflow-y:auto`, and an overflow value other
   than `visible` establishes a clipping box: anything poking out above the scroll area is
   cut off. The shell's own `padding:2px` leaves 2px where the badge needs 12.

   It only shows up in THIS game because `storeAlwaysOffers` (game.html) pins the rewarded
   "+150 COINS" offer as the FIRST row — the first row is the only one whose overhang has
   nothing above it to overlap into. Measured: badge top 346.4 against a scroll-box top of
   353.9, so 7.5px of it was sliced off.

   Fixed with top padding rather than by moving the badge: `top:-12px` is the shell's look
   and every other rewarded button in the game wears it the same way. shell.css stays
   byte-identical (CLAUDE.md §4). 15px = the 12px overhang + the shell's own 2px + 1 spare. */
#gameShell .sh-ov .shop-scroll, .sh-ov .shop-scroll{ padding-top:15px; }

/* ══ 🔖 BUILD STAMP — under the store panel ══════════════════════════════════════
   Hangs below the panel's bottom edge (`.sh-ov .panel` is position:relative in shell.css).
   Small and low-contrast on purpose: it must be READABLE when a build is being tested and
   ignorable the rest of the time — this is a diagnostic, not a UI element.
   No `vw`/`vh` and no percentages: it is inside the 450x800 design card, which is already
   scaled by one transform (UI-SCALING-STANDARD.md). */
/* 🚨 NO `#gameShell` PREFIX. The shell's overlays are SIBLINGS of the design card, not
   children of it (that is the whole z-index 10 / 20 split — see the note on #brPick), so
   `#gameShell #shShop` matches nothing and the first version of this rule silently did
   nothing at all: the stamp inherited the panel's dark text and flowed INSIDE the panel
   instead of hanging under it. Same trap as PITFALLS.md #7 from the other direction. */
#shShop .panel .brBuild{
  position:absolute; left:0; right:0; bottom:-19px;
  font:400 11px/1 'Fredoka One','Baloo 2',system-ui,sans-serif;
  text-align:center; color:#fff; opacity:.42; letter-spacing:.4px;
  text-shadow:0 1px 2px rgba(11,33,48,.55); pointer-events:none;
}

/* ══ 🪙 THE COIN NUMBER IS THE SCOREBOARD — MAKE IT READ ═════════════════════════
   Bigger, because it is the number the whole upgrade loop is about and it was sitting at
   the same weight as ordinary chip text. Tabular figures so it does not jiggle as it counts
   up — a number that shifts width while animating reads as a glitch, not a reward.
   px only: this is inside the 450x800 design card, which is already scaled by one transform
   (UI-SCALING-STANDARD.md), so any viewport unit here would be scaled twice. */
#gameShell .chip.brcoin #shCoins{
  font-size:23px; letter-spacing:.2px;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
}

/* ── and it PULSES while something is affordable ──────────────────────────────
   Driven by `coinPulse()` in game.html, which toggles `.canspend` on affordability — not on
   earning. The animation lives on the whole chip rather than the number so the coin art and
   the count breathe together; scaling just the text inside a fixed chip reads as a wobble.
   `transform` only (no width/box changes) so it cannot reflow the HUD around it. */
#gameShell .chip.brcoin.canspend{ animation:brCoinPulse 1.5s ease-in-out infinite; }
@keyframes brCoinPulse{
  0%,100%{ transform:scale(1);    filter:none; }
  50%    { transform:scale(1.075); filter:drop-shadow(0 0 7px rgba(255,199,0,.75)); }
}
/* Respect the OS setting. A permanent looping animation is exactly the kind a
   vestibular-sensitive player turns this on to stop — the gold glow still marks the state. */
@media (prefers-reduced-motion:reduce){
  #gameShell .chip.brcoin.canspend{ animation:none; filter:drop-shadow(0 0 7px rgba(255,199,0,.75)); }
}

/* ══ ⏳ THE SPLASH SPINNER MUST NOT TOUCH THE LOGO OR THE STUDIO MARK ═══════════════
   shell.css lays the splash out as a centred flex COLUMN (logo, then spinner) but pins
   `.ls-studio` ABSOLUTELY at `top:75%`. Two different positioning systems in one box: the
   flex half re-centres itself as the screen gets shorter, the absolute half does not, so on
   a short viewport the spinner walks down into the studio wordmark. It looks fine in
   portrait on a tall phone, which is exactly why it survived.

   Taking the spinner OUT OF THE FLOW fixes both halves at once: the flex column is then the
   logo alone and stays centred, and the spinner sits at a fixed 90% — below the studio mark
   at 75% and clear of the logo at any aspect ratio. It also stops the spinner's height
   shoving the logo upward while it is visible and letting it drop when it is removed.

   Scaled by --ui-scale like everything else outside the design card (PITFALLS.md #7). */
#shSplash .ls-spin{
  position:absolute; left:50%; top:90%;
  transform:translate(-50%,-50%) scale(var(--ui-scale,1));
  transform-origin:center; margin:0;
}
/* The keyframes rotate via `transform`, which would wipe the centring translate above.
   Re-declare the animation so it spins the ELEMENT without touching its placement. */
#shSplash .ls-spin{ animation:brSplashSpin .85s linear infinite; }
@keyframes brSplashSpin{
  from{ transform:translate(-50%,-50%) scale(var(--ui-scale,1)) rotate(0deg); }
  to  { transform:translate(-50%,-50%) scale(var(--ui-scale,1)) rotate(360deg); }
}

/* ══ 🔒 AN UNAFFORDABLE UPGRADE LOOKS UNAFFORDABLE ═══════════════════════════════════════
   Graham: "when we can't afford the booster make the blue button go grey so the player knows."

   🚨 THE SHELL WAS ALREADY TELLING US. It renders the buy button as `btn gold ubuy` when the
   player can afford it and `btn ghost ubuy` when they cannot — the state has been in the DOM
   all along. What was missing was any VISUAL difference: this theme's `--gh-*` ramp is a pale
   blue-white that reads as "a button", so an affordable and an unaffordable upgrade looked
   identical and the player learned nothing from pressing one.
   So this is a theme change, not a shell change (CLAUDE.md §4 — per-game colour lives here and
   only here). Nothing in shell.js or shell.css moves.

   Desaturated grey, dimmed, and the coin icon greyed with it — the price is the reason it is
   disabled, so it should look disabled too. Kept legible rather than nearly invisible: the
   player still needs to READ the cost to know what they are saving toward. */
#gameShell .btn.ghost.ubuy {
  --c1:#e4e8ee; --c2:#cdd4dd; --base:#9aa4b2;
  color:#6b7482;
  opacity:.82;
  filter:saturate(.15);
}
#gameShell .btn.ghost.ubuy img { opacity:.55; filter:grayscale(1); }

/* The card itself steps back a touch, so a shelf of three reads at a glance as "this one is
   in reach, those two are not" without having to compare the buttons. */
#gameShell .ucard:has(.btn.ghost.ubuy) { opacity:.9; }

/* ══ 🟣 PLAYGAMA COLOURS ═════════════════════════════════════════════════════════════════
   CLAUDE.md §3c: every Playgama build wears Playgama's colours, and the look is already
   SETTLED — do not re-litigate it per game. Tokens sampled from their own site and recorded
   in `playgama/PALETTE.md`: brand purple #9747ff, carbon #070708/#1d1d21, electric lime
   #c0ff47 as their CTA.

   🚨 OVERRIDDEN ON TOP OF THIS GAME'S THEME, NOT SWAPPED FOR playgama/ui-shell/theme.css.
   That file carries only colour; THIS file also carries structural fixes that have nothing to
   do with colour (the box-sizing reset for out-of-card panels, the coin pulse, the build
   stamp, the splash spinner placement, the grey unaffordable buttons). Swapping wholesale
   would silently drop all of them — the warning is written into §3c for exactly this reason.

   ⚠️ NOT DONE HERE, and still required before submission (§3c):
     • `subscribe:'PLAYGAMA', subscribeLabel:''` — the @uk4deegames bar must NOT ship to
       Playgama, and without the empty label it renders "SUBSCRIBE TO PLAYGAMA".
     • the purple P coin via the `__SHELL_ICONS` hook.
   Both are game.html config, not CSS, so they belong with the SDK conversion. */
:root{
  /* ---- brand ---- */
  --accent:#c7abff; --accent-dk:#9747ff; --accent-lt:#d8bbff;

  /* ---- furniture — P3 "deep purple": the panel faces, the league table, every popup ---- */
  --ink:#12081f;            /* outline: near-black WITH purple in it, not blue */
  --panel:#421b75;          /* the popup face — this is what makes the panels purple */
  --panel-2:#2e1252;        /* one step down, for inset areas like the league rows */
  --tab:#682eb2;
  --nav-line:#1b0b30;
  --text:#f3eaff;           /* lavender — reads warm against purple rather than grey */
  --text-scene:#f3eaff;

  /* ---- bottom nav — N2 "purple bar" ---- */
  --nav1:#4d2286;  --nav2:#2e1252;

  /* ---- button ramps: 1 = face top, 2 = face bottom, 3 = hard shadow, ink = label.
     🚨 MOVE 1/2/3 TOGETHER or a button stops reading as a key you can press. ---- */
  --pri-1:#aa76ff; --pri-2:#9747ff; --pri-3:#52189a; --pri-ink:#ffffff;  /* PLAY, and the
                                                     ACTIVE NAV TAB — the shell paints both
                                                     from this ramp on purpose, so pressable
                                                     things share one colour. */
  --gh-1:#5a3690;  --gh-2:#421b75;  --gh-3:#2a0f4d;                      /* neutral */

  /* 🟢🟡 SEMANTIC COLOURS ARE LEFT ALONE. Green stays "watch an ad", gold stays "currency"
     (CLAUDE.md §4). Tinting those purple for brand consistency would break the one colour
     rule the player has actually learned. */
  --green:#2fbf6b; --green-dk:#1d8c4c;
  --gold:#ffce3a;  --gold-dk:#e8a417;
}

/* 🟣 THE TOP BAR. shell.css hardcodes #ff0033 for the the host build; theme.css loads after
   it, so this is a plain override with no shell edit. Shape, padding, radius and
   `pointer-events:none` all stay exactly as shell.css defines them.
   ⚠️ The BAR is purple here, but it still says "@uk4deegames" — the wording is
   `Shell.init({subscribe:…})` in game.html and must become 'PLAYGAMA' with
   `subscribeLabel:''` before this ships (§3c). Colour alone is not compliance. */
#shSub{ background:#9747ff; }

/* ❌ THE CLOSE BUTTON — Graham: "change the top red thing to the purple Playgama one."
   It is red because the the host theme paints it from the same #ff0033 family as the subscribe
   bar. Same treatment: override the ramp, leave the geometry alone. */
#gameShell .btn.pclose, .sh-ov .btn.pclose, #gameShell .pclose, .sh-ov .pclose{
  --c1:#aa76ff; --c2:#9747ff; --base:#52189a; color:#fff;
}

/* 🟣 THE SPLASH IS PURPLE, NOT WHITE.
   `CFG.logo` turns the splash plain white by default (§3d), which is right for a the host
   build and wrong here — a white flash into a purple portal is the most jarring frame in the
   whole session, and it is the FIRST one. A vertical carbon-to-purple wash matches what the
   player was looking at a moment earlier on the Playgama grid.
   🚨 The splash is a SIBLING of the design card, not a child (see the note further up this
   file), so it is targeted directly rather than under #gameShell. */
/* 🚨 `.ls-white` IS THE CLASS THAT PAINTS IT WHITE — shell.js adds it whenever `CFG.logo`
   is set, so that is the rule being overridden. Targeting bare `#shSplash` and trusting
   `!important` to win is the kind of thing that works until the shell adds one more rule. */
#shSplash, #shSplash.ls-white, .sh-splash{
  background:linear-gradient(180deg,#1d1d21 0%,#3a1a6b 55%,#52189a 100%) !important;
}

/* ══ 📺 REWARDED AD OUTCOME CARDS ════════════════════════════════════════════════════════
   Deliberately styled off `#brNoAd` — the "no ads right now" card — so all three outcomes
   (granted / closed early / none available) speak in ONE visual language. Inventing a second
   card style for the same moment is how a menu starts looking like three people built it. */
.brAdMsg{ position:fixed; inset:0;
  top:0; left:0; width:100vw; height:100vh; height:100dvh;   /* same reason as #brPause above */
  z-index:130; display:flex; align-items:center;
  justify-content:center; opacity:0; pointer-events:none; transition:opacity .2s ease; }
.brAdMsg.on{ opacity:1; pointer-events:auto; }
.brAdMsg .naBack{ position:absolute; inset:0; background:rgba(10,6,20,.62); }
.brAdMsg .naCard{ position:relative; width:min(300px,78%); padding:22px 20px 18px;
  border-radius:20px; text-align:center; background:var(--panel); color:var(--text);
  border:3px solid var(--ink); box-shadow:0 10px 0 var(--ink), 0 18px 30px rgba(0,0,0,.45);
  transform:translateY(8px) scale(.97); transition:transform .22s cubic-bezier(.2,1.3,.4,1); }
.brAdMsg.on .naCard{ transform:none; }
.brAdMsg .naIcon img{ width:56px; height:56px; }
.brAdMsg .naHead{ font-size:20px; font-weight:900; letter-spacing:.02em; margin:6px 0 4px; }
.brAdMsg .naSub{ font-size:13px; line-height:1.45; opacity:.85; margin-bottom:14px; }
/* 🟢 Granted borrows the REWARDED-AD green, ⚠️ closed stays neutral. The green is the colour
   the player already associates with "watch an ad" (§4), so seeing it again on success closes
   the loop; a closed ad must NOT wear it, or the two outcomes look alike at a glance. */
.brAdMsg.good .naCard{ border-color:var(--go-3); box-shadow:0 10px 0 var(--go-3), 0 18px 30px rgba(0,0,0,.45); }
.brAdMsg.good .naHead{ color:var(--go-1); }

/* ══ ⏸️ PAUSE OVERLAY + RESUME COUNTDOWN ═════════════════════════════════════════════════
   Covers the whole design card so the frozen world is genuinely hidden, not dimmed — a
   half-visible paused game invites the player to keep watching it and makes the freeze look
   like a stutter. Sits INSIDE #gameShell so it scales with the card (UI-SCALING-STANDARD:
   never vw/vh in here). */
/* 🚨 `fixed`, and a z-index ABOVE THE BOTTOM NAV. It was `absolute` inside the card, which
   clipped it to 450x800 and left the 3D scene visible either side on a wide window. As a
   sibling of the card it covers the whole viewport — nav included, so nothing looks
   interactive while the game is stopped. */
/* 🚨 EXPLICIT `100vw` / `100dvh`, NOT JUST `inset:0`.
   `inset:0` resolves against the nearest POSITIONED ancestor, and `position:fixed` silently
   falls back to that ancestor rather than the viewport if anything above it has a `transform`,
   `filter` or `backdrop-filter` — all of which this shell uses on the design card. It measured
   full-size in my window and still left the game visible round the edges elsewhere.
   `dvh` also handles the mobile case `inset:0` gets wrong: a phone's URL bar shrinks the visual
   viewport, and `100vh` keeps reporting the LARGE height, so the overlay ends short at the
   bottom exactly where the nav sits.
   Legal here because this element lives OUTSIDE the 450x800 design card — viewport units are
   only banned INSIDE it (UI-SCALING-STANDARD), where they would be scaled twice. */
#brPause{ position:fixed; inset:0;
  top:0; left:0; width:100vw; height:100vh; height:100dvh;
  z-index:120; display:grid; place-items:center;
  opacity:0; pointer-events:none; transition:opacity .18s ease; }
#brPause.on{ opacity:1; pointer-events:auto; }
#brPause .pBack{ position:absolute; inset:0; background:rgba(12,8,24,.82);
  backdrop-filter:blur(3px); }
#brPause .pMid{ position:relative; text-align:center; }
#brPause .pWord{ font-size:40px; font-weight:900; letter-spacing:.14em; color:var(--text);
  text-shadow:0 4px 0 var(--ink), 0 10px 22px rgba(0,0,0,.5); }
/* The count is deliberately LOUDER than the word it replaces — the player is waiting on it,
   and a countdown you have to squint at is worse than none. */
#brPause .pCount{ font-size:104px; font-weight:900; line-height:1; color:#fff;
  text-shadow:0 6px 0 var(--ink), 0 16px 30px rgba(0,0,0,.55);
  animation:brCount .68s cubic-bezier(.2,1.4,.4,1); }
@keyframes brCount{ 0%{ transform:scale(1.6); opacity:0; } 30%{ transform:scale(1); opacity:1; }
                    100%{ transform:scale(.92); opacity:.85; } }

/* ══ 🔤 THE OUT-OF-CARD PANELS MUST WEAR THE GAME'S FONT ═════════════════════════════════
   🚨 `shell.css` sets `font-family:'Fredoka One'` on **`#gameShell`**, and both the pause
   overlay and the ad-outcome cards are deliberately SIBLINGS of that card (so they can cover
   the whole viewport). Siblings inherit nothing from it — so they silently fell back to
   system-ui and looked like browser chrome dropped on top of the game. Graham: "they use a
   different font and look crap."

   This is the cost of living outside the card, and anything else placed out there — a future
   confirm dialog, an error panel — needs the same three lines. It is not enough to match the
   colours; the typeface is what makes a panel read as part of the same product.

   `!important` because these rules land before some shell defaults in the cascade and the
   panels are built at runtime; without it the fallback wins on first paint. */
#brPause, #brPause *,
.brAdMsg, .brAdMsg *{
  font-family:'Fredoka One','Baloo 2',system-ui,sans-serif !important;
}

/* Now the font is right, the weights can come back to the shell's own scale — Fredoka is a
   single display weight, so 900 on top of it just smears the outline. */
#brPause .pWord{ font-weight:400; letter-spacing:.10em; }
#brPause .pCount{ font-weight:400; }
.brAdMsg .naHead{ font-weight:400; letter-spacing:.02em; font-size:21px; }
.brAdMsg .naSub{ font-weight:400; opacity:.9; }
/* Match the shell's button geometry rather than inventing one. */
.brAdMsg .naCard button{ font-family:inherit; width:100%; margin-top:2px; }

/* ══ 🏠 NO HOME BUTTON DURING A RUN ══════════════════════════════════════════════════════
   Graham: remove the home button from the main game while running.

   `#shQuit` is the house icon the shell puts in the in-run HUD. Hidden rather than removed —
   it lives in `shell.js`, which stays byte-identical across every game (§4), so the only place
   a per-game decision like this belongs is here.

   🚨 `display:none`, NOT `opacity:0` or `visibility:hidden`. An invisible button is still a
   TAP TARGET sitting in the top-left corner — exactly where a right-handed player's thumb
   travels when steering left — and quitting a run by accident is far worse than the button
   being visible. It also stops it taking keyboard focus.

   Nothing is stranded by this: the run ends at the finish line and returns to the menu on its
   own, so there is no state a player can get stuck in without it. */
#gameShell #shQuit{ display:none !important; }

/* ══ ⏸️ NOTHING ANIMATES WHILE THE GAME IS PAUSED ════════════════════════════════
   A paused game that still has moving parts does not read as paused — it reads as
   broken. `Game.pause()` stops the simulation, but CSS `@keyframes` are driven by the
   BROWSER, not by our loop, so they carry on regardless. The one that gave it away was
   the NEW BEST starburst (`#brBestRay`, a 7s infinite spin) turning behind the host's
   dimmed pause overlay.

   Blanket freeze, then one explicit exception — far safer than trying to list every
   animated element, because the next one added would be missed and nobody would notice
   until a screenshot like that one turned up.

   `game-paused` is set by shell.js on a HOST pause and by Game.pause() for our own. */
body.game-paused *,
body.game-paused *::before,
body.game-paused *::after { animation-play-state: paused !important; }

/* 🚨 THE EXCEPTION: the shell's own pause overlay. It is the one thing that SHOULD still
   move while paused — freezing it would leave a half-faded panel mid-transition, which is
   the exact "looks broken" this rule exists to prevent. */
body.game-paused #shPaused,
body.game-paused #shPaused *,
body.game-paused #shPaused *::before,
body.game-paused #shPaused *::after { animation-play-state: running !important; }

/* 🔴 AND EVERY ONE-SHOT ENTRANCE. Graham, 2026-08-20: "it's the free giveaway window that
   messes up after the first run." MEASURED: `#brPick .bpCard` sat at `scale(0.583)` while
   `--ui-scale` was `0.833` — 0.833 x 0.7, which is the FIRST FRAME of its `bpIn` entrance.
   The blanket freeze above had caught it and held it there.

   The distinction the freeze was missing: pausing should stop AMBIENT motion — a looping
   starburst, a pulse — because motion behind a pause overlay reads as broken. A one-shot
   ENTRANCE is the opposite: freezing it doesn't stop motion, it strands a panel at 70% size
   and 0 opacity, permanently, because nothing re-runs it when play resumes.

   These are all short, finite, and safe to let finish: the panel is being PRESENTED, and it
   presents whether the sim is running or not. Anything ambient stays frozen by the rule above.
   ⚠️ Add new one-shot panel animations here, or they will freeze the same way. */
body.game-paused #brPick, body.game-paused #brPick *,
body.game-paused #brSum,  body.game-paused #brSum *,
body.game-paused #brNoAd, body.game-paused #brNoAd *,
body.game-paused #brBest, body.game-paused #brBest *,
body.game-paused .sh-ov,  body.game-paused .sh-ov *{ animation-play-state: running !important; }

/* The shell's `.sh-ov .panel` is 340px, sized for a message box. This one holds three option
   cards side by side, so it needs to be wider — but it is still the SHELL'S panel, scaled and
   positioned by the shell. Only the width is ours. */
#brPick .panel{ width:392px; padding:18px 16px 20px; text-align:center; }
