/* /style.css */
/* INSERT COIN — racing-telemetry aesthetic.
   Discipline: ONE accent (signal orange) for brand and live state. Green/red are
   reserved strictly for price direction and never used decoratively. Everything
   else is neutral. Density over decoration; type does the hierarchy, not colour. */

/* ═══════════════════════════════════════════════════════════════════════════
   THE TOKEN LAYER.

   Eight stylesheets are written against these names by people who never read
   each other's files, so every value that more than one of them could plausibly
   need lives here and nowhere else. If you are about to type a literal into
   floor.css / crash.css / arcade.css / meta.css / audio.css / onboarding.css /
   chat.css, the answer is almost always a token from this block.

   Audited before this pass: 88 distinct rgba() literals, 45 distinct font-sizes,
   21 radii, 63 shadows and 37 transition durations across the eight files — for
   a palette of three colours, which is what "assembled" rather than "designed"
   actually looks like from the inside.
   ═══════════════════════════════════════════════════════════════════════════ */
:root{
  /* ── surfaces: four levels, in stacking order ────────────────
     page → recessed well → card → raised (hover / active / nested). A dark UI
     needs the ladder to be explicit; borrowing a lighter grey ad hoc is how a
     card ends up on the same plane as the thing it is supposed to sit above. */
  --bg:#0a0a0c; --bg-1:#0e0e11; --surface:#121216; --surface-2:#16161b;
  --line:#1e1e25; --line-2:#2a2a34; --line-3:#3a3a48;
  --bg-rgb:10,10,12; --bg1-rgb:14,14,17; --surface-rgb:18,18,22;
  /* Glass and scrim. There were four modal backdrops on this site — .72, .78,
     .82 and .86 of the same near-black — which means the page dimmed by a
     different amount depending on which agent wrote the sheet. One value. */
  --glass:rgba(var(--bg-rgb),.72);   /* sticky bars over content */
  --scrim:rgba(6,6,8,.82);           /* anything modal */

  /* ── text: four steps, all of them legible ───────────────────
     Measured against --bg and against --surface-2, the lightest ground a label
     can land on. The bottom two used to be --dim #75747f (4.30:1 on --bg, 3.92
     on --surface-2) and --dim-2 #4e4d57 (2.38:1 / 2.17), and --dim-2 carries the
     10px uppercase keys on the pot strip, the fairness panel, the rig board and
     the crash proof — small text, so the bar is 4.5:1, and it was under half of
     it. Raised until every step clears 4.5:1 on every surface. Still four
     visibly distinct steps; the bottom one is now a colour rather than a
     rumour. */
  --txt:#f2f1ee;    /* 17.51:1 on --bg   15.96:1 on --surface-2 */
  --txt-2:#a8a7b3;  /*  8.33:1           7.59:1 */
  --dim:#94939f;    /*  6.53:1           5.95:1   (was 4.30 / 3.92) */
  --dim-2:#807f8c;  /*  5.02:1           4.58:1   (was 2.38 / 2.17) */
  --txt-rgb:242,241,238;

  /* ── the one accent, and the two directions ──────────────────
     Orange is brand and live state. GREEN AND RED MEAN PRICE DIRECTION AND
     NOTHING ELSE — never "success", never "danger", never decoration. Violet
     for real money lives in onboarding.css and is deliberately none of these.
     The -rgb channel forms exist so a tint can be written as
     rgba(var(--up-rgb),.12) instead of re-typing 47,217,127 for the 91st time. */
  --acc:#ff7a1a; --acc-soft:rgba(255,122,26,.13); --acc-line:rgba(255,122,26,.42);
  --up:#2fd97f; --down:#ff4d5e; --hold:#c9c8d2;
  --acc-rgb:255,122,26; --up-rgb:47,217,127; --down-rgb:255,77,94;
  --shade-rgb:0,0,0; --light-rgb:255,255,255;
  /* hover states, and the dark ink that goes ON a saturated fill */
  --acc-hi:#ff8c3a; --up-hi:#4ae89a; --down-hi:#ff6f83;
  --on-acc:#0a0a0c;  /* 7.58:1 on --acc  */
  --on-up:#06180f;   /* 9.91:1 on --up   */
  --on-down:#1a060c; /* 6.02:1 on --down */
  /* --down at full saturation is 6.10:1 on --bg but only ~2.9:1 on its own 9%
     tint, which is the ground the bust chips and the trap tiles actually sit on.
     This is --down lightened until it clears there: 8.78:1 on --bg, 7.84:1 on
     the tint. Two files had independently arrived at the same hex. */
  --down-txt:#ff8a95;
  /* Two more that are neither brand nor direction, so they cannot borrow one.
     --warn is the near-miss amber; --info the neutral-outcome blue. */
  --warn:#ffc247; --info:#7fb3ff;
  --warn-rgb:255,194,71; --info-rgb:127,179,255;

  --sans:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;
  --mono:ui-monospace,"SF Mono","JetBrains Mono","Cascadia Mono",Menlo,Consolas,monospace;

  /* ── type scale ──────────────────────────────────────────────
     Eleven steps replacing forty-five sizes. The small band was nine distinct
     values inside 5.5px — 9/9.5/10/10.5/11/11.5/12/12.5/13 — which is not a
     scale, it is nine people each nudging by half a pixel. Steps are chosen so
     the most-used values ARE the scale: nothing moves by more than 0.5px in the
     band that carries the labels, and --fs-base stays exactly the body size. */
  --fs-micro:9.5px;   /* uppercase keys, tracking-out, the floor of the system */
  --fs-mini:10.5px;   /* mono captions, tags, sub-rows */
  --fs-xs:11.5px;     /* meta chips, hints, secondary mono */
  --fs-sm:12.5px;     /* notes, small body */
  --fs-md:13.5px;     /* dense UI text, table cells, buttons */
  --fs-base:15px;     /* body */
  --fs-lg:17px;
  --fs-xl:19px;
  --fs-2xl:22px;
  --fs-3xl:26px;
  --fs-4xl:32px;
  --fs-5xl:40px;
  /* Per-component hero numbers (the crash multiplier, the limbo climb) stay on
     their own clamp() — they are tuned against a specific box, not to a scale. */

  /* ── spacing ─────────────────────────────────────────────────
     A 4/6/8/10/12/14/16/20/26/34 ladder: dense at the bottom because this UI is
     dense at the bottom, and it is the ladder the existing values already
     cluster on. */
  --sp-1:4px; --sp-2:6px; --sp-3:8px; --sp-4:10px; --sp-5:12px;
  --sp-6:14px; --sp-7:16px; --sp-8:20px; --sp-9:26px; --sp-10:34px;

  /* ── radius ──────────────────────────────────────────────────
     Six steps for twenty-one values. --r-1 is the 2–3px bar cap, --r-5 the card,
     --r-6 the sheet. Cards move 12px → 14px, which is the only visible change. */
  --r-1:2px; --r-2:4px; --r-3:7px; --r-4:10px; --r-5:14px; --r-6:20px;
  --r-pill:999px;

  /* ── elevation ───────────────────────────────────────────────
     Four levels. Nothing that animates gets one of these: a large blur on a
     moving element is the single most expensive thing on this page, and the
     floor was taken from 25.3ms to 3.9ms a frame by removing exactly that class
     of effect. Glows are a component's own business and stay local. */
  --sh-1:0 1px 3px rgba(var(--shade-rgb),.4);      /* pressed-in / seated */
  --sh-2:0 10px 30px rgba(var(--shade-rgb),.5);    /* popover, toast */
  --sh-3:0 18px 48px rgba(var(--shade-rgb),.6);    /* modal */
  --sh-4:0 30px 90px rgba(var(--shade-rgb),.62);   /* full-screen sheet */

  /* ── motion ──────────────────────────────────────────────────
     Five durations for thirty-seven. Coordinated timing is most of what
     separates an interface that feels built from one that feels assembled —
     when every hover on the page settles at the same moment, the page reads as
     one object. Long ambient loops (the 52s ticker, the 2.6s brand pulse) are
     not on this scale; they are not responses to anything. */
  --dur-1:120ms;  /* press / tactile — must beat the finger */
  --dur-2:180ms;  /* hover, colour, border */
  --dur-3:260ms;  /* small position and size changes */
  --dur-4:400ms;  /* entrances */
  --dur-5:600ms;  /* bars and meters filling */
  --ease:cubic-bezier(.16,1,.3,1);         /* decelerate hard — arrivals */
  --ease-2:cubic-bezier(.4,0,.2,1);        /* standard — state changes */
  --ease-smooth:cubic-bezier(.2,.8,.25,1); /* flips and long travels */
  --ease-spring:cubic-bezier(.34,1.56,.64,1); /* overshoot — things landing */

  /* ── layout constants other files need ───────────────────────
     .rail, .obledger and the chat dock all stick under the header, and all
     three had 56px typed into them separately. */
  --hdr-h:56px;
  --shell-w:1240px;
  --shell-pad:24px;
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--txt);min-height:100vh;
  font:var(--fs-base)/1.5 var(--sans);font-feature-settings:"cv02","cv03","cv04";
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}

/* ── keyboard focus, once, for everything ──────────────────────
   Nine of the ten interactive controls on this site are <button>, and every
   stylesheet that styles one also sets `outline:0` or `outline:none` on its
   focus state — so a keyboard player had no visible focus anywhere except the
   audio slider. :focus-visible only fires for keyboard and assistive input, so
   this costs the mouse player nothing.

   An outline rather than a box-shadow ring: it follows border-radius, it cannot
   be clipped by an ancestor's overflow:hidden (the game rail, the results strip
   and the case reel are all clipping scrollers), and it is not a paint-heavy
   shadow on something that may be mid-transform. --acc is 7.58:1 on --bg and
   6.91:1 on --surface-2, comfortably past the 3:1 a focus indicator owes.

   !important, and this is the case it exists for. Five stylesheets each kill the
   outline on their own inputs — `.slip input:focus`, `.modal input:focus`,
   `.crx-autoin:focus`, `.obshell input:focus`, `.pdchat-input:focus`, plus
   `outline:none` in floor.css's `.stakein` base rule. Those are two-class
   selectors; `:where()` contributes no specificity, so without this the ring
   loses to every one of them and the six most-typed-into fields on the site
   still have no visible focus. A system-level accessibility guarantee cannot be
   something the tenth stylesheet gets to opt out of by accident. Anything that
   wants a different focus treatment can still add a box-shadow on top — which is
   exactly what audio.css's volume slider does, and it still works. */
:where(a,button,input,select,textarea,summary,[tabindex],[role="button"]):focus-visible{
  outline:2px solid var(--acc) !important;
  outline-offset:2px !important;
}
/* Boards are clicked, not tabbed, but they are still reachable, and a ring that
   lands inside a canvas edge is invisible. Push it out. */
:where(.pk,.row,.mft,.ttile,.rl,.crx-chip,.chip):focus-visible{outline-offset:3px !important}
/* faint telemetry grid + grain, so flat panels have some tooth */
body::before{
  content:"";position:fixed;inset:0;pointer-events:none;z-index:0;opacity:.5;
  background-image:
    linear-gradient(rgba(var(--light-rgb),.014) 1px,transparent 1px),
    linear-gradient(90deg,rgba(var(--light-rgb),.014) 1px,transparent 1px);
  background-size:64px 64px;
  mask-image:radial-gradient(120% 90% at 50% 0%,#000 20%,transparent 78%);
}
::selection{background:var(--acc);color:var(--on-acc)}
.wrap{max-width:var(--shell-w);margin:0 auto;padding:0 var(--shell-pad) 120px;position:relative;z-index:1}
h1,h2,h3,h4{margin:0;font-weight:600;letter-spacing:-.022em}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.02em}

/* ── header ───────────────────────────────────────────────── */
header{position:sticky;top:0;z-index:40;background:var(--glass);
  backdrop-filter:blur(20px) saturate(1.5);border-bottom:1px solid var(--line)}
/* min-height, not height. floor.css lets this bar wrap below 700px, but the
   fixed 56px stayed — so at 375px the bar's own content measured 162px tall
   inside a 56px box and painted straight over the ledger, the hero and the
   first card. Everything else on mobile was downstream of this one word. */
.bar{max-width:var(--shell-w);margin:0 auto;padding:0 var(--shell-pad);
  min-height:var(--hdr-h);display:flex;align-items:center;gap:var(--sp-7)}
.brand{display:flex;align-items:center;gap:9px;font-weight:600;font-size:var(--fs-base);letter-spacing:-.03em}
.brand .dot{width:7px;height:7px;border-radius:var(--r-pill);background:var(--acc);
  box-shadow:0 0 0 3px var(--acc-soft);animation:pulse 2.6s var(--ease-2) infinite}
@keyframes pulse{0%,100%{box-shadow:0 0 0 3px var(--acc-soft)}50%{box-shadow:0 0 0 7px transparent}}
.brand b{font-weight:600}.brand span{color:var(--dim);font-weight:400}
.spacer{flex:1}
.meta-chip{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);letter-spacing:-.01em;white-space:nowrap}
.meta-chip b{color:var(--txt-2);font-weight:500}
.acct{display:flex;align-items:center;gap:10px;font-size:var(--fs-md);white-space:nowrap}
.acct .h{color:var(--txt-2)}
.acct .b{font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--txt);font-size:var(--fs-md)}
.streak{font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc);border:1px solid var(--acc-line);
  padding:1px 6px;border-radius:var(--r-2)}
.walletchip{font-family:var(--mono);font-size:var(--fs-xs);padding:4px 9px;border-radius:var(--r-3);
  background:transparent;border:1px solid var(--line-2)}
.walletchip.ok{color:var(--up);border-color:rgba(var(--up-rgb),.3)}
.walletchip.ok:hover{background:rgba(var(--up-rgb),.08);color:var(--up)}
.walletchip.warn{color:var(--acc);border-color:var(--acc-line);background:var(--acc-soft)}
.walletchip.warn::before{content:"⚠ ";}
.walletchip.warn:hover{background:rgba(var(--acc-rgb),.2);color:var(--acc)}

button{font-family:inherit;font-size:var(--fs-md);font-weight:500;letter-spacing:-.01em;cursor:pointer;
  border-radius:var(--r-3);padding:7px 14px;border:1px solid transparent;background:var(--acc);color:var(--on-acc);
  transition:background var(--dur-2) var(--ease-2),border-color var(--dur-2),color var(--dur-2),transform var(--dur-1) var(--ease)}
button:hover:not(:disabled){background:var(--acc-hi)}
button:active:not(:disabled){transform:scale(.975)}
button:disabled{opacity:.3;cursor:not-allowed}
button.ghost{background:transparent;color:var(--txt-2);border-color:var(--line-2)}
button.ghost:hover:not(:disabled){background:var(--surface-2);color:var(--txt);border-color:var(--line-3)}
button.quiet{background:transparent;color:var(--dim);border-color:transparent;padding:6px 10px}
button.quiet:hover:not(:disabled){color:var(--txt);background:var(--surface-2)}

/* ── the way back in ──────────────────────────────────────────
   Injected by app.js next to Play and shown only while signed out. Quieter than the
   primary on purpose: it is not the action for a first visit, it is the action for
   somebody who already had an account and cannot get to it. Recessive, and present.

   It is allowed to shrink to its label and no further — a header that wraps to three
   rows on a phone was a real regression here once, so this never grows the row it is
   on and it drops its border rather than its presence at 700px. */
.recoverbtn{white-space:nowrap;flex:0 0 auto;font-size:var(--fs-sm);padding:6px 11px}
@media(max-width:700px){
  .recoverbtn{border-color:transparent;color:var(--dim);padding:6px 4px}
}

/* The Account tab, signed out. It used to be one flat sentence with nothing to press. */
.acctout-btns{display:flex;gap:10px;flex-wrap:wrap}

/* The sentence directly under the paying board's heading. It is the answer to "which of
   these boards is the real one", so it sits above the table rather than under it, where
   the scoring rule already lives. */
.rs-lead{border-bottom:1px solid var(--line)}

/* ── HAPPY MINUTE, AT REST ────────────────────────────────────
   The event has a banner and the banner exists for 60 seconds at a time, so between
   events the whole feature was invisible and a player could finish a session never
   knowing it was there. happy.js injects this chip into the header permanently.

   Deliberately recessive. The header was cut from nine items to five on the test "does
   this number change while I watch it", and this one does not, so it reads as standing
   context in the same weight as the coin count rather than as another button. It never
   grows the bar and it stands down on a phone, where the header is already two rows and
   the live banner is the introduction anyway.

   Here rather than in happy.css on purpose: that file is measured by a test that bans
   the effects the floor's performance pass removed, and it is the stylesheet for a thing
   that animates. Nothing here animates. */
.hpmc-chip{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;white-space:nowrap;
  background:transparent;border:1px solid var(--line-2);color:var(--dim);
  font-family:var(--mono);font-size:var(--fs-xs);font-weight:400;letter-spacing:-.01em;
  padding:4px 9px;border-radius:var(--r-3)}
.hpmc-chip:hover:not(:disabled){background:var(--surface-2);color:var(--txt-2);border-color:var(--line-3)}
.hpmc-chip .hpmc-x{color:var(--txt-2);font-weight:500}
.hpmc-dot{width:5px;height:5px;border-radius:var(--r-pill);background:var(--line-3);flex:0 0 auto}
/* Warning and live. A colour change and nothing else: the banner is already the event,
   and a second thing pulsing in the header during the loudest sixty seconds on the site
   is noise on top of noise. */
.hpmc-chip[data-phase="warn"]{border-color:var(--acc-line);color:var(--acc)}
.hpmc-chip[data-phase="warn"] .hpmc-x,
.hpmc-chip[data-phase="live"] .hpmc-x{color:var(--acc)}
.hpmc-chip[data-phase="warn"] .hpmc-dot,
.hpmc-chip[data-phase="live"] .hpmc-dot{background:var(--acc)}
.hpmc-chip[data-phase="live"]{border-color:var(--acc-line);color:var(--acc);background:var(--acc-soft)}
@media(max-width:700px){ .hpmc-chip{display:none} }

/* The live bar opens the same panel, which is the only route to it on a phone. */
.hpm-bar{cursor:pointer}

/* The panel. Borrows .modal and .card from above; everything below is just the list. */
.hpmc-sheet .card{max-width:520px;max-height:82vh;overflow:auto}
.hpmc-lead{color:var(--txt-2);font-size:var(--fs-md);line-height:1.6;margin:0 0 10px}
.hpmc-note{color:var(--dim);font-size:var(--fs-sm);line-height:1.6;margin:0 0 16px}
.hpmc-k{font-family:var(--mono);font-size:var(--fs-mini);letter-spacing:.12em;text-transform:uppercase;
  color:var(--dim-2);margin:16px 0 8px}
.hpmc-list{margin:0;padding-left:18px;color:var(--txt-2);font-size:var(--fs-sm);line-height:1.65}
.hpmc-list li{margin-bottom:5px}
.hpmc-row{display:flex;align-items:baseline;gap:10px;font-family:var(--mono);
  font-size:var(--fs-xs);color:var(--dim);padding:6px 0;border-top:1px solid var(--line)}
.hpmc-row b{color:var(--acc);font-weight:500}
.hpmc-row em{font-style:normal;color:var(--txt-2)}
.hpmc-row i{font-style:normal;margin-left:auto;color:var(--dim-2)}
.hpmc-close{width:100%;margin-top:18px}

/* ── hero ─────────────────────────────────────────────────── */
.hero{padding:64px 0 40px;max-width:760px}
.eyebrow{font-family:var(--mono);font-size:var(--fs-xs);letter-spacing:.14em;text-transform:uppercase;
  color:var(--acc);margin-bottom:18px;display:flex;align-items:center;gap:9px}
.eyebrow::before{content:"";width:22px;height:1px;background:var(--acc)}
.hero h1{font-size:clamp(34px,5.4vw,58px);line-height:1.02;letter-spacing:-.04em;font-weight:600}
.hero h1 .q{color:var(--dim-2)}
.hero p{margin:22px 0 0;color:var(--txt-2);font-size:var(--fs-lg);line-height:1.62;max-width:56ch}
.hero p b{color:var(--txt);font-weight:500}

/* ── pot strip ──────────────────────────────────────────────
   `.rail` twice. `paintStrip()` twice. `.warn` twice. And `.strip` twice: this
   file declares it as the four-cell pot grid, floor.css declares it as the game
   floor's flex results row, and floor.css loads second. The pot strip has
   therefore been `display:flex` — so both of the responsive rules below, which
   only ever set grid-template-columns, did nothing at all, and at 375px the four
   cells were a 291px-tall squeeze with the labels overlapping each other.

   Scoped to #strip, which index.html already puts on the element. An id beats a
   class from any file in any order, which is the only thing that reliably
   settles a shared flat namespace — a fourth stylesheet can declare `.strip`
   tomorrow and this stays correct. The floor's own `.strip` is untouched. */
#strip{display:grid;grid-template-columns:auto auto 1fr auto;gap:0;align-items:stretch;
  border:1px solid var(--line);border-radius:var(--r-5);background:var(--surface);overflow:hidden;margin:8px 0 22px}
#strip>div{padding:16px 22px;border-right:1px solid var(--line);display:flex;flex-direction:column;
  justify-content:center;gap:5px;min-width:0}
#strip>div:last-child{border-right:0}
.k{font-family:var(--mono);font-size:var(--fs-mini);letter-spacing:.13em;text-transform:uppercase;color:var(--dim-2)}
.v{font-family:var(--mono);font-size:var(--fs-3xl);font-weight:500;letter-spacing:-.035em;line-height:1}
.v.acc{color:var(--acc)}
#strip .lead{min-width:0}
.lead-row{display:flex;align-items:center;gap:9px;font-size:var(--fs-md);min-width:0}
.lead-row .pos{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2);width:15px;flex:none}
.lead-row .nm{color:var(--txt-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lead-row .amt{font-family:var(--mono);color:var(--up);font-size:var(--fs-sm);margin-left:auto;flex:none}
.splitmeter{display:flex;height:3px;border-radius:var(--r-1);overflow:hidden;background:var(--line);margin-top:3px}
.splitmeter i{display:block;height:100%}
.splitmeter .a{background:var(--acc)}.splitmeter .b{background:var(--line-3)}
.splitnote{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim)}

/* ── ticker ───────────────────────────────────────────────── */
.tick{border-top:1px solid var(--line);border-bottom:1px solid var(--line);height:36px;
  overflow:hidden;position:relative;margin-bottom:34px;
  mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent)}
.tick .rail{display:flex;gap:34px;align-items:center;height:100%;white-space:nowrap;
  animation:roll 52s linear infinite;font-family:var(--mono);font-size:var(--fs-sm);color:var(--dim)}
.tick:hover .rail{animation-play-state:paused}
@keyframes roll{to{transform:translateX(-50%)}}
.tick em{font-style:normal;color:var(--txt-2)}
.tick b{color:var(--acc);font-weight:500}

/* ── nav ──────────────────────────────────────────────────────
   THE TAB STRIP FOLLOWS YOU DOWN THE PAGE, and this is the single change that
   fixes "hard to move around".

   Measured before: the Games tab is 5,038px tall signed in at 1280 and 7,693px
   at 375. The tab strip sat at y=881 and y=1,148 and then scrolled away, so from
   the moment you were actually playing a game there was no route to the Pot, the
   Leaderboard, your Account or How it works at all. Getting to any of them meant
   scrolling a thousand pixels back up, finding the strip, clicking, and landing
   somewhere in the middle of the next tab. On a phone it was worse, because the
   header stops sticking at 700px, so past the first screen the page carried no
   navigation of any kind.

   Sticky, and directly under the two bars that already stick. --nav-top is
   measured at runtime in app.js rather than typed, because what is above it
   changes: the header stops sticking below 700px and the ledger can be absent
   entirely. The fallback below is the desktop sum (56 header + 44 ledger) so a
   stylesheet that arrives before its script is still roughly right.

   A FLAT BACKGROUND, NOT --glass. The header and the ledger are both
   backdrop-filter, and a third blurred layer is a third full-width filter pass on
   every scrolled frame. This one is opaque and costs nothing. */
nav{position:sticky;top:var(--nav-top,100px);z-index:32;
  display:flex;gap:2px;margin-bottom:26px;border-bottom:1px solid var(--line);overflow-x:auto;
  background:var(--bg);scrollbar-width:none}
nav::-webkit-scrollbar{display:none}
nav button{background:none;border:0;border-bottom:1.5px solid transparent;border-radius:0;
  color:var(--dim);padding:11px 15px;font-size:var(--fs-md);white-space:nowrap;margin-bottom:-1px}
nav button:hover{color:var(--txt-2);background:none;transform:none}
nav button.on{color:var(--txt);border-bottom-color:var(--acc)}
/* WHICH TAB AM I ON, at a glance rather than by comparing five greys. The
   underline alone is 1.5px of accent on a strip you are no longer looking at by
   the time you have scrolled to it. */
nav button.on{background:var(--acc-soft);font-weight:600}
/* The five tabs measure 372px at 375, so the strip scrolls sideways and How is
   off the edge on the landing view — a whole destination a phone player never
   sees. Letting them share the row costs nothing: the longest label is
   "Leaderboard" and it still fits. */
@media(max-width:520px){
  nav{gap:0}
  /* `1 1 auto`, not `1 1 0`. Equal fifths of 343px is 69px each and "Leaderboard"
     measures 78, so the one destination with a long name was the one that got cut.
     Sizing from content and sharing out what is left over gives it 98px and still
     fills the row. */
  nav button{flex:1 1 auto;min-width:0;padding:11px 6px;text-align:center;
    font-size:var(--fs-sm);white-space:nowrap}
}

/* ── crumb ────────────────────────────────────────────────────
   For a view that is not a tab. There is one of them (the eligibility table, off How
   it works) and it used to leave the strip pointing at a tab you were no longer on
   with no way back. The name of the thing you are looking at, and the door. */
.crumb{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:0 0 16px}
.crumb-back{background:none;border:1px solid var(--line-2);border-radius:var(--r-pill);
  color:var(--dim);padding:6px 14px;font-size:var(--fs-sm);min-height:34px;
  transition:border-color var(--dur-1),color var(--dur-1)}
.crumb-back::before{content:"←";margin-right:8px}
.crumb-back:hover{border-color:var(--line-3);color:var(--txt);background:none;transform:none}
.crumb-at{font-family:var(--mono);font-size:var(--fs-xs);letter-spacing:.1em;
  text-transform:uppercase;color:var(--dim-2)}

/* ── section head ─────────────────────────────────────────── */
.sec{display:flex;align-items:baseline;gap:12px;margin:34px 0 14px}
.sec h2{font-size:var(--fs-md);font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--dim)}
.sec .rule{flex:1;height:1px;background:var(--line)}
.sec .cnt{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim-2)}

/* ── cards ────────────────────────────────────────────────── */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(370px,1fr));gap:14px}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-5);padding:0;
  transition:border-color var(--dur-3) var(--ease-2),transform var(--dur-3) var(--ease);position:relative;overflow:hidden;
  animation:rise .5s var(--ease) backwards}
@keyframes rise{from{opacity:0;transform:translateY(9px)}}
.card:hover{border-color:var(--line-2)}
.card.stack{grid-column:1/-1}
.chead{display:flex;align-items:center;gap:9px;padding:13px 16px;border-bottom:1px solid var(--line);
  flex-wrap:wrap}
.cbody{padding:14px 16px}
.tag{font-family:var(--mono);font-size:var(--fs-mini);letter-spacing:.1em;text-transform:uppercase;
  color:var(--dim);border:1px solid var(--line-2);padding:2.5px 7px;border-radius:var(--r-2)}
.tag.live{color:var(--acc);border-color:var(--acc-line);background:var(--acc-soft)}
.tag.run{color:var(--txt-2);border-color:var(--line-3)}
.tag.done{color:var(--dim-2)}
.tag.t-even{color:var(--info);border-color:rgba(var(--info-rgb),.3)}
.tag.t-spicy{color:var(--warn);border-color:rgba(var(--warn-rgb),.3)}
.tag.t-degen{color:var(--down);border-color:rgba(var(--down-rgb),.35);background:rgba(var(--down-rgb),.07)}
.ttl{font-size:var(--fs-base);font-weight:550;letter-spacing:-.02em}
.timer{font-family:var(--mono);font-size:var(--fs-sm);color:var(--txt-2);margin-left:auto;
  font-variant-numeric:tabular-nums}
.timer.urgent{color:var(--acc)}

/* ── pulse ────────────────────────────────────────────────── */
.psub{display:flex;align-items:flex-end;gap:13px;margin-bottom:3px}
.psub .tk{font-size:var(--fs-2xl);font-weight:600;letter-spacing:-.035em}
.psub .mv{font-family:var(--mono);font-size:var(--fs-2xl);font-weight:500;letter-spacing:-.04em;line-height:1.1}
.psub .bd{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);margin-left:auto;padding-bottom:3px}
.spark{width:100%;height:34px;display:block;margin:9px 0 2px;overflow:visible}
.spark path{fill:none;stroke-width:1.4;vector-effect:non-scaling-stroke}
.spark .area{stroke:none;opacity:.10}

.gauge{position:relative;height:38px;margin:12px 0 14px;border-radius:var(--r-3);overflow:hidden;
  background:var(--bg-1);border:1px solid var(--line)}
.gauge .z{position:absolute;top:0;bottom:0}
.gauge .zd{left:0;background:linear-gradient(90deg,rgba(var(--down-rgb),.20),rgba(var(--down-rgb),.02))}
.gauge .zu{right:0;background:linear-gradient(270deg,rgba(var(--up-rgb),.20),rgba(var(--up-rgb),.02))}
.gauge .edge{position:absolute;top:0;bottom:0;width:1px;background:var(--line-3)}
.gauge .lab{position:absolute;top:50%;transform:translateY(-50%);font-family:var(--mono);
  font-size:var(--fs-micro);color:var(--dim-2);letter-spacing:.04em}
.gauge .pin{position:absolute;top:-1px;bottom:-1px;width:2px;background:var(--txt);border-radius:var(--r-1);
  box-shadow:0 0 14px rgba(var(--txt-rgb),.6);transition:left var(--dur-5) var(--ease)}
.gauge .val{position:absolute;bottom:4px;transform:translateX(-50%);font-family:var(--mono);
  font-size:var(--fs-mini);font-weight:500;transition:left var(--dur-5) var(--ease);white-space:nowrap;
  text-shadow:0 1px 5px #000}

.picks{display:grid;grid-template-columns:repeat(3,1fr);gap:7px}
.pk{position:relative;border:1px solid var(--line);border-radius:var(--r-4);padding:11px 9px;text-align:center;
  cursor:pointer;background:var(--bg-1);overflow:hidden;
  transition:border-color var(--dur-2),background var(--dur-2),transform var(--dur-2) var(--ease)}
.pk:hover{transform:translateY(-2px);border-color:var(--line-3)}
.pk .lb{font-family:var(--mono);font-size:var(--fs-mini);letter-spacing:.11em;color:var(--dim);text-transform:uppercase}
.pk .mu{font-family:var(--mono);font-size:var(--fs-xl);font-weight:500;letter-spacing:-.04em;margin:5px 0 3px}
.pk .st{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2)}
.pk .load{position:absolute;left:0;bottom:0;height:2px;opacity:.6;transition:width var(--dur-5) var(--ease)}
.pk.up:hover .lb,.pk.up.on .lb{color:var(--up)} .pk.up .load{background:var(--up)}
.pk.down:hover .lb,.pk.down.on .lb{color:var(--down)} .pk.down .load{background:var(--down)}
.pk.flat:hover .lb,.pk.flat.on .lb{color:var(--hold)} .pk.flat .load{background:var(--hold)}
.pk.on{background:var(--surface-2);border-color:var(--line-3);box-shadow:inset 0 0 0 1px var(--line-3)}
.pk.won{border-color:var(--acc);background:var(--acc-soft)}
.pk .kbd{position:absolute;top:6px;right:7px;font-family:var(--mono);font-size:var(--fs-micro);color:var(--dim-2);
  border:1px solid var(--line-2);border-radius:var(--r-1);padding:0 3px;opacity:0;transition:opacity var(--dur-2)}
.pk:hover .kbd{opacity:1}

/* ── race rows ────────────────────────────────────────────── */
.rows{display:flex;flex-direction:column}
.row{position:relative;display:grid;grid-template-columns:18px 1fr 68px 62px 52px;gap:11px;
  align-items:center;padding:9px 4px;cursor:pointer;border-bottom:1px solid var(--line);
  transition:background var(--dur-2)}
.row:last-child{border-bottom:0}
/* Share of pool. This used to be a full-height block behind the row, which read as
   a muddy orange overlay rather than as data — a staircase of brown rectangles that
   looked like a rendering artifact. A thin underline says "bar chart" immediately
   and stops fighting the text for contrast. */
.row .bg{position:absolute;left:0;bottom:0;height:2px;background:var(--acc);opacity:.75;
  transition:width var(--dur-5) var(--ease);z-index:0;border-radius:var(--r-1)}
.row.won .bg{background:var(--up);opacity:1}
.row>*{position:relative;z-index:1}
.row:hover{background:var(--surface-2)}
.row.on{background:var(--surface-2);box-shadow:inset 2px 0 0 var(--acc)}
.row.won .sym{color:var(--acc)}
.row .pos{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim-2);text-align:right}
.row .sym{font-size:var(--fs-md);font-weight:550;letter-spacing:-.015em}
.row .sub{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2);margin-top:1px}
.row .pc{font-family:var(--mono);font-size:var(--fs-md);text-align:right;font-variant-numeric:tabular-nums}
.row .mu{font-family:var(--mono);font-size:var(--fs-sm);color:var(--txt-2);text-align:right}
.row .sk{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2);text-align:right}
.row .mini{width:52px;height:16px}
.up{color:var(--up)}.down{color:var(--down)}.zero{color:var(--dim)}

/* ── slip ─────────────────────────────────────────────────── */
.slip{display:flex;align-items:center;gap:7px;padding:12px 16px;border-top:1px solid var(--line);
  background:var(--bg-1);flex-wrap:wrap}
.slip input{background:var(--surface);border:1px solid var(--line-2);color:var(--txt);border-radius:var(--r-3);
  padding:7px 11px;width:88px;font-family:var(--mono);font-size:var(--fs-md);transition:border-color var(--dur-2)}
.slip input:focus{outline:0;border-color:var(--acc-line)}
.chips{display:flex;gap:3px}
.chips button{padding:6px 9px;font-family:var(--mono);font-size:var(--fs-xs);background:transparent;
  color:var(--dim);border-color:var(--line)}
.chips button:hover{color:var(--txt);border-color:var(--line-3);background:var(--surface)}
.est{flex:1;min-width:150px;font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);text-align:right}
.est b{color:var(--txt);font-weight:500}
.mine{padding:10px 16px;border-top:1px solid var(--line);font-family:var(--mono);font-size:var(--fs-xs);
  color:var(--dim);background:var(--bg-1)}
.mine b{color:var(--txt-2);font-weight:500}
.mine.live{background:linear-gradient(90deg,var(--acc-soft),transparent 60%);
  border-left:2px solid var(--acc)}
.mine-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.mine-val{font-size:var(--fs-xl);font-weight:600;letter-spacing:-.03em;color:var(--txt);
  transition:color var(--dur-3)}
.mine-pnl{font-size:var(--fs-sm);font-weight:600}
.mine-legs{margin-left:auto;color:var(--dim-2);font-size:var(--fs-mini)}
@keyframes flash{0%{color:var(--acc)}100%{}}
.mine-val.bump{animation:flash .6s}
.foot{padding:9px 16px;border-top:1px solid var(--line);font-family:var(--mono);font-size:var(--fs-mini);
  color:var(--dim-2);display:flex;gap:14px;flex-wrap:wrap}
.foot b{color:var(--txt-2);font-weight:500}

/* ── tables ───────────────────────────────────────────────── */
table{width:100%;border-collapse:collapse;font-size:var(--fs-md)}
th{text-align:left;font-family:var(--mono);font-weight:400;font-size:var(--fs-mini);letter-spacing:.11em;
  text-transform:uppercase;color:var(--dim-2);padding:10px 12px;border-bottom:1px solid var(--line)}
td{padding:10px 12px;border-bottom:1px solid var(--line);font-variant-numeric:tabular-nums}
tbody tr{transition:background var(--dur-1)}
tbody tr:hover{background:var(--surface-2)}
tr:last-child td{border-bottom:0}
td.m,th.m{font-family:var(--mono)}
.r{text-align:right}
.note{color:var(--dim);font-size:var(--fs-sm);line-height:1.65}
.note b{color:var(--txt-2);font-weight:500}
.prose{max-width:70ch}
.prose h4{font-size:var(--fs-md);color:var(--txt);margin:22px 0 7px;letter-spacing:-.01em}
.prose p{margin:0 0 4px}
.empty{padding:40px 16px;text-align:center;color:var(--dim-2);font-size:var(--fs-md)}
.stats{display:flex;gap:34px;flex-wrap:wrap;padding:16px}
.stat{display:flex;flex-direction:column;gap:5px}
.stat .v{font-size:var(--fs-2xl)}

/* ── overlays ─────────────────────────────────────────────────
   THE STACK, IN ORDER, AND WHY IT MOVED.

   The modal was written at z-index 50 when the tallest thing on the page was the
   header at 40. Six full-screen surfaces have arrived since and every one of them
   went above it: the tray menu and the onboarding veil and the season ladder at 60,
   the happy banner at 100, the profile overlay at 120. Each was correct on its own
   and together they made the modal the lowest overlay on the site.

   That is not cosmetic, it is a dead button. profile.js's "Share this profile" hands
   the sheet to app.js's share modal and does not close its own overlay first, so the
   modal opened at 50 underneath the profile at 120 and the click did nothing at all,
   every time. Reproduced.

   A modal is the surface that says "deal with this before anything else", so it goes
   above everything that is merely open, and the toast goes above the modal because
   half the toasts on this site are the receipt for a button inside one ("code
   copied"). The two floor overlays stay higher still at 200 and 210, which is right:
   both are pointer-events:none and neither is something you act on. */
.toast{position:fixed;left:50%;bottom:30px;transform:translateX(-50%);z-index:140;
  background:var(--surface);border:1px solid var(--line-2);border-left:2px solid var(--acc);
  color:var(--txt);padding:12px 20px;border-radius:var(--r-4);font-size:var(--fs-md);
  box-shadow:var(--sh-3);animation:slidein var(--dur-4) var(--ease)}
.toast.err{border-left-color:var(--down)}
.toast.win{border-left-color:var(--up);font-size:var(--fs-base)}
@keyframes slidein{from{opacity:0;transform:translate(-50%,16px)}}
.modal{position:fixed;inset:0;background:var(--scrim);backdrop-filter:blur(10px);
  display:grid;place-items:center;z-index:130;padding:24px;animation:fade var(--dur-3) var(--ease)}
@keyframes fade{from{opacity:0}}
.modal .card{max-width:390px;width:100%;padding:26px;animation:rise .4s var(--ease)}
.modal h3{font-size:var(--fs-xl);letter-spacing:-.03em;margin-bottom:6px}
.modal input{width:100%;margin:16px 0 14px;background:var(--bg-1);border:1px solid var(--line-2);
  color:var(--txt);border-radius:var(--r-3);padding:11px 13px;font-size:var(--fs-base);font-family:var(--mono)}
.modal input:focus{outline:0;border-color:var(--acc-line)}
.steps{display:flex;gap:6px;margin-bottom:16px}
.steps i{height:2px;flex:1;background:var(--line-2);border-radius:var(--r-1);transition:background var(--dur-3)}
.steps i.on{background:var(--acc)}
/* THE SAME BUG AS `.rail`, STILL LIVE. `.warn` is the seed-phrase disclosure
   block in the signup modal — a 10px-padded red box. It was unscoped, and the
   header's wallet button is `class="walletchip warn"` (app.js:387), so it also
   matched. Equal specificity, and `.warn` is declared later, so for as long as
   both have existed the header chip has been rendering with the disclosure
   box's padding, font-size, line-height and radius: 120×41px in a 56px bar
   instead of 120×25px, which is a good part of why the phone header wrapped to
   three rows at all. The chip's own colours survived only because
   `.walletchip.warn` is two classes.

   Scoped to the modal it lives in rather than renamed, because the markup
   belongs to app.js. It is the only place it is used — grep says one <div> in
   index.html and that one button. */
.modal .warn{border:1px solid rgba(var(--down-rgb),.28);background:rgba(var(--down-rgb),.06);
  border-radius:var(--r-3);padding:10px 12px;margin-top:12px;font-size:var(--fs-sm);
  line-height:1.55;color:var(--txt-2)}
.modal .warn b{color:var(--down)}
.share{background:transparent;border:1px solid var(--line-2);color:var(--dim);padding:6px 11px;
  font-size:var(--fs-sm);border-radius:var(--r-3)}
.share:hover{color:var(--acc);border-color:var(--acc-line);background:var(--acc-soft)}
.wins{display:flex;gap:9px;overflow-x:auto;padding-bottom:4px;scrollbar-width:none}
.wins::-webkit-scrollbar{display:none}
.wincard{flex:none;min-width:212px;border:1px solid var(--line);border-radius:var(--r-4);padding:12px 14px;
  background:var(--surface);cursor:pointer;transition:border-color var(--dur-2),transform var(--dur-2) var(--ease)}
.wincard:hover{border-color:var(--acc-line);transform:translateY(-2px)}
.wincard .amt{font-family:var(--mono);font-size:var(--fs-2xl);color:var(--acc);letter-spacing:-.03em}
.wincard .who{font-size:var(--fs-sm);color:var(--txt-2);margin-top:2px}
.wincard .mk{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2);margin-top:3px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.refbox{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
.refbox code{font-family:var(--mono);font-size:var(--fs-sm);background:var(--bg-1);border:1px solid var(--line-2);
  border-radius:var(--r-3);padding:8px 11px;color:var(--txt-2);flex:1;min-width:200px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
#confetti{position:fixed;inset:0;pointer-events:none;z-index:70}
.dots{display:inline-block;width:11px;height:11px;border:1.5px solid var(--line-3);
  border-top-color:var(--acc);border-radius:50%;animation:sp .75s linear infinite;vertical-align:-1px}
@keyframes sp{to{transform:rotate(360deg)}}

@media(max-width:900px){
  #strip{grid-template-columns:1fr 1fr}
  #strip>div:nth-child(2n){border-right:0}
  #strip>div{border-bottom:1px solid var(--line)}
}
@media(max-width:640px){
  .wrap{padding:0 16px 90px}.bar{padding:0 16px}
  .hero{padding:40px 0 28px}
  .grid{grid-template-columns:1fr}
  .row{grid-template-columns:16px 1fr 60px 50px}
  .row .sk{display:none}
  #strip{grid-template-columns:1fr}
  #strip>div{border-right:0}
  .acct .h{display:none}
}
/* This catch-all is what actually covers the ten stylesheets — the per-file
   `prefers-reduced-motion` blocks in floor/crash/arcade/audio/onboarding are
   refinements on top of it, and meta.css, chat.css, profile.css and invite.css
   have none of their own and rely on it entirely. Verified by emulation rather
   than assumed: every animation and transition on the page reports a computed
   duration of 0.01ms with the setting on. */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important}
  /* Duration alone does not cover programmatic scrolling. meta.css's reward
     ladder sets scroll-behavior:smooth and JS scrolls it, which the rule above
     cannot reach — a smooth scroll is exactly the motion this setting is about.
     :root as well as *, because scroll-behavior on the scrolling element is
     read from html rather than inherited to it. */
  html{scroll-behavior:auto!important}
}

/* ══════════ THE HEADER, NARROWING ═════════════════════════════
   Measured, not guessed, and at two widths because it broke differently at each.

   AT 768px THE PAGE SCROLLED SIDEWAYS. The bar is a nowrap flex row and its
   content — brand, coin count, token link, rigs, account block, bonus, sound and
   the profile button another agent added this afternoon — measures 1109px. Flex
   items with text will not shrink below their content, so the document itself
   became 1133px wide inside a 753px viewport: a horizontal scrollbar on every
   tab and every game, sourced entirely from the header. It only ever wrapped
   below 700px, so the whole tablet band had nowhere to put the overflow.

   Two things fix it, and both are needed: let the bar wrap from 900px down, and
   drop the two items that are context rather than control. The coin count and
   the funding-token link are read once and are both still on the How tab; they
   are not things anyone reaches for mid-round. Identity, balance, the rig
   collector and the primary button stay. */
@media(max-width:900px){
  .bar{flex-wrap:wrap;row-gap:var(--sp-2);padding-block:var(--sp-2)}
  #uniChip,#tokenChip{display:none}
}

/* ══════════ AND ON A PHONE ═══════════════════════════════════
   At 375px even the stripped bar is three rows and 150px of sticky chrome — a
   fifth of the viewport, permanently, above the game. */
@media(max-width:700px){
  .bar{gap:var(--sp-3)}
  .brand{font-size:var(--fs-md)}
  /* The spacer is a flex filler for a single row. On a wrapped bar it eats a
     whole line, which is where two of the four rows came from. */
  .bar>.spacer{display:none}
  /* floor.css sets .acct{flex-wrap:wrap} at this width and loads after this
     file, so this needs the extra element to win. Wrapping the account block
     internally is what turned two header rows into three. */
  header .acct{margin-left:auto;flex-wrap:nowrap;min-width:0}
  header .acct .walletchip{min-width:0;flex:0 1 auto}

  /* THE HEADER STOPS STICKING. Even stripped it is two or three rows on a
     phone, and there is a purpose-built 40px bar directly under it —
     onboarding.css's .obledger — that already carries the points balance and
     the SOL prize and was designed to be the thing that follows you down the
     page. Two stacked sticky bars is how you end up with a third of a phone
     screen permanently spent on chrome. One sticky element, and it is the one
     built for the job. The header scrolls away like the hero does.
     (.obledger's own top offset is corrected in onboarding.css.) */
  header{position:static}
}

/* ══════════ TOUCH TARGETS ═════════════════════════════════════
   45 controls measured under 32px in either axis at 375px, the smallest a 25px
   stake chip. WCAG 2.2 AA sets the floor at 24×24, so most were legal and none
   were comfortable — and the stake chips are the most-tapped controls on the
   site. Height only, via min-height and block padding: touching the width would
   reflow the wrapping rows these all sit in.

   Scoped to width alone rather than to (pointer:coarse) on purpose — a 375px
   window on a desktop is still a 375px layout, and a media feature that cannot
   be reproduced in a browser pane is a media feature nobody will ever check. */
@media(max-width:700px){
  .qs,.chips button,.smod,.share,.tag,.obled-help,.mta-btn.mta-sm,
  .crx-quick button,.crx-autotog,.seg button,.rigbuy,.grigchip,.walletchip{
    min-height:34px;padding-block:var(--sp-2)
  }
  /* A 16px-tall range track is a 16px-tall drag target. The thumb is the thing
     being grabbed, so the padding goes on the input rather than the track. */
  .arc-slider,#ouslider{min-height:34px;padding-block:var(--sp-3)}
  /* Icon-only buttons have no text to pad around, so they get a floor in both
     axes — these are the ones a thumb misses. */
  .sndbtn,.obhint-x,.pdchat-icon,.idlechip{min-width:36px;min-height:36px}
  /* The one-tap toggle: the input is 13px and invisible, the label is the target. */
  .qtoggle{min-height:36px}
}

/* ── round missions ─────────────────────────────────────── */
.missions{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:9px;
  margin:0 0 26px;align-items:stretch}
.mz{border:1px solid var(--line);border-radius:var(--r-4);padding:11px 13px;background:var(--surface);
  transition:border-color var(--dur-2),background var(--dur-2)}
.mz.ready{border-color:var(--acc-line);background:var(--acc-soft)}
.mz.done{opacity:.42}
.mz-top{display:flex;align-items:baseline;gap:8px}
.mz-l{font-size:var(--fs-md);font-weight:600;letter-spacing:-.02em}
.mz-r{margin-left:auto;font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc)}
.mz-d{font-size:var(--fs-xs);color:var(--dim);margin:4px 0 8px;line-height:1.45}
.mz-bar{height:3px;background:var(--line);border-radius:var(--r-1);overflow:hidden}
.mz-bar i{display:block;height:100%;background:var(--acc);transition:width var(--dur-5) var(--ease)}
.mz-p{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2);margin-top:5px}
.mz-claim{align-self:center;height:100%;min-height:64px;font-weight:600}

/* ── cash out ───────────────────────────────────────────── */
.sell{margin-left:12px;padding:5px 12px;font-size:var(--fs-sm);font-weight:600;
  background:var(--up);color:var(--on-up);border-radius:var(--r-3)}
.sell:hover:not(:disabled){background:var(--up-hi)}
/* White on --down measures 3.24:1 — under the 4.5:1 this 10px uppercase tag
   needs by a wide margin, and it is a label on the most urgent state the market
   has. --on-down is the same red with dark ink: 6.02:1, and it reads as a
   filled tag rather than a browser default. */
.tag.blitz{color:var(--on-down);background:var(--down);border-color:var(--down);
  animation:blitzpulse 1.1s ease-in-out infinite}
@keyframes blitzpulse{0%,100%{opacity:1}50%{opacity:.62}}
.card.blitzcard{border-color:rgba(var(--down-rgb),.45)}

/* ── quick bet bar ──────────────────────────────────────── */
.quickbar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:11px 16px;
  border:1px solid var(--line);border-radius:var(--r-4);background:var(--surface);margin-bottom:16px;
  transition:border-color var(--dur-3),background var(--dur-3)}
.quickbar.armed{border-color:var(--acc-line);background:linear-gradient(90deg,var(--acc-soft),var(--surface) 55%)}
.qtoggle{display:flex;align-items:center;gap:9px;cursor:pointer;font-size:var(--fs-md);user-select:none}
.qtoggle input{position:absolute;opacity:0;pointer-events:none}
.qtoggle span{width:34px;height:19px;border-radius:var(--r-5);background:var(--line-2);position:relative;
  transition:background var(--dur-3) var(--ease-2);flex:none}
.qtoggle span::after{content:"";position:absolute;top:2px;left:2px;width:15px;height:15px;
  border-radius:50%;background:var(--txt-2);transition:transform var(--dur-3) var(--ease),background var(--dur-3)}
.qtoggle input:checked + span{background:var(--acc)}
.qtoggle input:checked + span::after{transform:translateX(15px);background:var(--on-acc)}
.qtoggle b{font-weight:600;letter-spacing:-.01em}
.qhint{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim)}
.qstakes{display:flex;gap:4px;margin-left:auto}
.qs{font-family:var(--mono);font-size:var(--fs-sm);padding:6px 12px;background:transparent;
  color:var(--dim);border:1px solid var(--line-2)}
.qs:hover{color:var(--txt);border-color:var(--line-3);background:var(--surface-2)}
.qs.on{background:var(--acc);color:var(--on-acc);border-color:var(--acc);font-weight:600}

/* action feedback */
@keyframes hitflash{
  0%{box-shadow:0 0 0 0 rgba(var(--acc-rgb),.55);border-color:var(--acc)}
  100%{box-shadow:0 0 0 14px rgba(var(--acc-rgb),0);border-color:var(--line)}
}
.card.hit{animation:hitflash .7s var(--ease)}
.pk,.row{-webkit-tap-highlight-color:transparent}
.quickbar.armed ~ #view .pk:active,.quickbar.armed ~ #view .row:active{transform:scale(.97)}

@media(max-width:640px){
  .qstakes{margin-left:0;width:100%}
  .qs{flex:1}
  .qhint{display:none}
}

/* ── live race rail ─────────────────────────────────────────
   The round you are competing in used to live on another tab, which meant you could
   not see yourself moving — the single biggest reason a round felt like nothing was
   happening. That is why it is here, above the tabs, on the landing view. It matters
   more now than it did: the round is thirty minutes rather than fifteen, and twice the
   span with no visible movement is twice the problem this element exists to solve.

   IT NO LONGER STICKS, and it had already stopped working before this. It stuck at
   top:56 at z-index 30, and .obledger sticks at top:57 at z-index 39 and is 44px
   tall — so 45 of the rail's 75px were painting underneath the ledger and all that
   ever showed was a 30px sliver of the standings with the clock cut off. Nobody had
   seen the stuck state of this element in its working form.

   Fixing the offset instead was the other option and it is the wrong one: header 57
   plus ledger 44 plus rail 75 plus the tab strip is 217px of permanent chrome on a
   720px screen. The tab strip is navigation and earns its place; a leaderboard
   summary is reference and does not. Below 760px it never stuck anyway. */
.rail{position:relative;z-index:30;display:grid;
  grid-template-columns:170px 1fr;gap:0;align-items:stretch;margin:0 0 12px;
  border:1px solid var(--line);border-radius:var(--r-5);overflow:hidden;
  /* Opaque, and the blur came out with the stickiness. A backdrop-filter is only
     ever worth its cost over content that scrolls beneath it, and nothing does any
     more. It also pulsed on every `.rail.push` frame, which was a full-width filter
     pass on an animating element. */
  background:var(--surface);
  transition:border-color var(--dur-4),box-shadow var(--dur-4)}
.rail-clock{padding:12px 16px;border-right:1px solid var(--line);display:flex;
  flex-direction:column;justify-content:center;gap:3px}
.rail-cd{font-family:var(--mono);font-size:var(--fs-3xl);font-weight:600;letter-spacing:-.04em;line-height:1}
.rail-push{font-size:var(--fs-mini)}
.pushtag{font-family:var(--mono);font-size:var(--fs-mini);letter-spacing:.08em;color:var(--down);font-weight:700}
.rail-board{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:0 2px;padding:8px 10px;align-content:center}
.rr{display:flex;align-items:center;gap:6px;font-size:var(--fs-sm);padding:2px 6px;border-radius:var(--r-2);
  font-variant-numeric:tabular-nums;transition:background var(--dur-3)}
.rr.empty{color:var(--dim-2);grid-column:1/-1;justify-content:center;padding:10px}
.rr-p{font-family:var(--mono);color:var(--dim-2);width:14px;flex:none}
.rr-m{width:9px;flex:none;font-size:8px;color:transparent}
.rr.up .rr-m{color:var(--up)} .rr.down .rr-m{color:var(--down)}
.rr.up{background:rgba(var(--up-rgb),.09)} .rr.down{background:rgba(var(--down-rgb),.07)}
.rr-h{color:var(--txt-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.rr-n{font-family:var(--mono);color:var(--up);margin-left:auto;font-size:var(--fs-xs);flex:none}
.rr-z{font-family:var(--mono);color:var(--acc);font-size:var(--fs-mini);width:38px;text-align:right;flex:none}
.rr.me{background:var(--acc-soft);box-shadow:inset 0 0 0 1px var(--acc-line)}
.rr.me .rr-h{color:var(--txt);font-weight:600}
.rail-you{grid-column:1/-1;border-top:1px solid var(--line);padding:8px 16px;
  display:flex;align-items:center;gap:10px;font-size:var(--fs-sm);background:var(--bg-1)}
.rail-you b{font-family:var(--mono);font-size:var(--fs-base)}
.rr-gap{font-family:var(--mono);color:var(--dim);font-size:var(--fs-xs)}
.rr-def{font-family:var(--mono);color:var(--dim-2);font-size:var(--fs-xs);margin-left:auto}
.rr-prize{font-family:var(--mono);color:var(--acc);font-size:var(--fs-xs)}

.rail.push{border-color:rgba(var(--down-rgb),.55);box-shadow:0 0 26px rgba(var(--down-rgb),.14)}
.rail.push .rail-cd{color:var(--down);animation:railpulse 1s ease-in-out infinite}
@keyframes railpulse{0%,100%{opacity:1}50%{opacity:.55}}

@media(max-width:760px){
  .rail{grid-template-columns:1fr;top:0}
  .rail-clock{border-right:0;border-bottom:1px solid var(--line);flex-direction:row;
    align-items:baseline;gap:10px}
  .rail-board{grid-template-columns:1fr 1fr}
}

/* ── the closing seconds ────────────────────────────────────
   A market resolving is the only genuinely tense moment in the loop, and it used to
   pass without any signal at all. */
.card.ending{border-color:rgba(var(--down-rgb),.6);animation:endingpulse .9s ease-in-out infinite}
@keyframes endingpulse{
  0%,100%{box-shadow:0 0 0 0 rgba(var(--down-rgb),.28)}
  50%{box-shadow:0 0 0 8px rgba(var(--down-rgb),0)}
}
.card.ending .timer{color:var(--down);font-weight:700}
.card.ending .gauge .pin{box-shadow:0 0 20px rgba(var(--light-rgb),.9)}
/* Was `var(--flat, var(--warn))`. `--flat` has never existed in any stylesheet, so
   this had been running on its fallback since it was written — the fallback is
   the only reason it was amber rather than inherited. The near-miss amber is now
   --warn, which is a real token; --hold is the flat-price grey and is a
   different thing, which is probably what --flat was reaching for. */
.miss{color:var(--warn);font-weight:600}
.tk-rank{color:var(--acc)!important}
.tk-rank em{font-style:normal;color:var(--txt)}

/* ── recovery code ──────────────────────────────────────── */
.codebox{font-family:var(--mono);font-size:var(--fs-2xl);letter-spacing:.06em;text-align:center;
  padding:18px;margin:16px 0;border:1px dashed var(--acc-line);border-radius:var(--r-4);
  background:var(--acc-soft);color:var(--acc);user-select:all}

/* ── ROCKET ─────────────────────────────────────────────────
   Almost empty on purpose. One enormous number that is literally your payout, one
   fuse, one button. The density everywhere else is what stopped any single number
   from feeling like it mattered. */
.card.rkt{grid-column:1/-1;background:
  radial-gradient(120% 140% at 50% 120%, rgba(var(--acc-rgb),.10), transparent 60%), var(--surface);
  border-color:var(--acc-line)}
.rkt-tag{color:var(--acc)!important;border-color:var(--acc-line)!important;background:var(--acc-soft)}
.rkt-body{padding:26px 20px 20px;text-align:center}
.rkt-mult{font-family:var(--mono);font-size:clamp(56px,11vw,104px);font-weight:700;
  letter-spacing:-.06em;line-height:.95;color:var(--acc);
  text-shadow:0 0 44px rgba(var(--acc-rgb),.35);font-variant-numeric:tabular-nums}
.rkt-mult i{font-style:normal;font-size:.42em;opacity:.5;margin-left:2px}
.rkt-mult.dead{color:var(--down);text-shadow:0 0 44px rgba(var(--down-rgb),.4)}
.rkt-sub{font-family:var(--mono);font-size:var(--fs-sm);color:var(--dim);margin-top:8px}
.rkt-sub b{color:var(--down)}

/* the fuse: how close the coin is to rugging the round */
.fuse{height:6px;border-radius:var(--r-2);background:var(--line);overflow:hidden;
  margin:18px auto 6px;max-width:520px}
.fuse i{display:block;height:100%;border-radius:var(--r-2);
  background:linear-gradient(90deg,var(--up),var(--acc) 55%,var(--down));
  transition:width var(--dur-5) var(--ease)}
.rkt-fuseline{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim-2)}

.rkt-me{display:flex;align-items:center;justify-content:center;gap:10px;margin-top:20px;
  font-family:var(--mono);font-size:var(--fs-md);color:var(--dim);flex-wrap:wrap}
.rkt-me b{color:var(--txt);font-size:var(--fs-base)}
.rkt-worth{color:var(--acc);font-size:var(--fs-lg);font-weight:600}
.rkt-eject{background:var(--up);color:var(--on-up);font-weight:700;padding:10px 22px;font-size:var(--fs-base)}
.rkt-eject:hover:not(:disabled){background:var(--up-hi)}
.rkt-board{margin-top:20px}
.rkt-go{padding:12px 30px;font-size:var(--fs-base);font-weight:700}

.card.rkt.crashed{border-color:var(--down);
  background:radial-gradient(120% 140% at 50% 120%, rgba(var(--down-rgb),.14), transparent 60%), var(--surface);
  animation:rktboom .5s var(--ease)}
@keyframes rktboom{
  0%{transform:scale(1.015);box-shadow:0 0 0 0 rgba(var(--down-rgb),.5)}
  100%{transform:scale(1);box-shadow:0 0 0 30px rgba(var(--down-rgb),0)}
}
.card.rkt.done{opacity:.72}

.rkt-nodeadline{margin-left:auto;font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim-2)}
.rkt-mult{transition:none}   /* streamed at 60fps; a transition would smear it */

/* ── rocket history ─────────────────────────────────────────
   Every crash game shows the last dozen results. Without it each round is an
   isolated coin flip; with it you can see the distribution and the next launch
   becomes a judgement rather than a guess. */
.rkt-hist{display:flex;gap:5px;justify-content:center;flex-wrap:wrap;margin-bottom:18px}
.rh{font-family:var(--mono);font-size:var(--fs-xs);padding:3px 8px;border-radius:var(--r-6);
  border:1px solid var(--line-2);color:var(--dim)}
.rh.good{color:var(--up);border-color:rgba(var(--up-rgb),.32);background:rgba(var(--up-rgb),.07)}
.rh.bad{color:var(--down);border-color:rgba(var(--down-rgb),.30);background:rgba(var(--down-rgb),.07)}

/* ── rocket curve ───────────────────────────────────────────
   The number alone was a readout. A curve is a thing you watch — it has shape,
   momentum, and a visible history of the ride you are still on. */
.rkt-chart{position:relative;height:200px;margin:0 auto 6px;max-width:640px}
.rkt-chart svg{width:100%;height:100%;display:block;overflow:visible}
.rkt-chart svg.dead .rc-line{stroke:var(--down)}
.rkt-chart svg.dead .rc-fill{fill:rgba(var(--down-rgb),.16)}
.rkt-chart svg.dead .rc-dot{fill:var(--down)}
.rc-line{filter:drop-shadow(0 0 10px rgba(var(--acc-rgb),.55))}
.rc-dot{filter:drop-shadow(0 0 8px rgba(var(--light-rgb),.9))}
.rc-grid{position:absolute;inset:0;pointer-events:none;border-bottom:1px solid var(--line);
  background-image:linear-gradient(var(--line) 1px,transparent 1px);
  background-size:100% 50px;opacity:.5}
.card.rkt .rkt-mult{margin-top:-12px}
@media(max-width:640px){ .rkt-chart{height:150px} }

/* ── instant games ──────────────────────────────────────────
   Two ways to earn points that resolve on click. Everything else on the site waits
   for a window; these are where you sit. */
.gstake{display:flex;align-items:center;gap:12px;padding:11px 16px;margin-bottom:6px;
  border:1px solid var(--line);border-radius:var(--r-4);background:var(--surface)}
.hl-idle{text-align:center;padding:26px 20px}
.hl-idle button{margin-top:14px}
.hl{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:16px;padding:26px 18px}
.hl-side{text-align:center}
.hl-sym{font-size:var(--fs-2xl);font-weight:650;letter-spacing:-.03em}
.hl-val{font-family:var(--mono);font-size:36px;font-weight:600;letter-spacing:-.04em;margin-top:6px}
.hl-val.hidden{color:var(--dim-2)}
.hl-meta{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim-2);margin-top:4px}
.hl-vs{font-family:var(--mono);font-size:var(--fs-sm);color:var(--dim-2);letter-spacing:.2em}
.hl-hi{background:var(--up);color:var(--on-up);font-weight:700;padding:10px 22px}
.hl-lo{background:var(--down);color:var(--on-down);font-weight:700;padding:10px 22px}
.hl-hi:hover:not(:disabled){background:var(--up-hi)}
.hl-lo:hover:not(:disabled){background:var(--down-hi)}

.mf-mines{display:flex;align-items:center;gap:10px;justify-content:center;margin-top:14px}
.mf-mines .qs{padding:6px 12px}
.mfgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(88px,1fr));gap:8px;padding:16px}
.mft{aspect-ratio:1;border:1px solid var(--line-2);border-radius:var(--r-4);background:var(--bg-1);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  cursor:pointer;transition:transform var(--dur-1) var(--ease),border-color var(--dur-2),background var(--dur-2)}
.mft:hover{transform:translateY(-3px);border-color:var(--acc-line);background:var(--surface-2)}
.mft-q{font-family:var(--mono);font-size:var(--fs-2xl);color:var(--dim-2)}
.mft-sym{font-size:var(--fs-md);font-weight:650;letter-spacing:-.02em}
.mft-chg{font-family:var(--mono);font-size:var(--fs-xs)}
.mft.safe{border-color:rgba(var(--up-rgb),.4);background:rgba(var(--up-rgb),.07);cursor:default}
.mft.mine{border-color:var(--down);background:rgba(var(--down-rgb),.13);cursor:default;
  animation:hitflash .6s var(--ease)}
.mft.safe:hover,.mft.mine:hover{transform:none}
@media(max-width:640px){
  .hl{grid-template-columns:1fr;gap:8px}
  .hl-vs{display:none}
  .mfgrid{grid-template-columns:repeat(4,1fr);gap:6px}
  .mft-sym{font-size:var(--fs-xs)}
}

/* ── over / under ───────────────────────────────────────── */
.ou{padding:22px 18px;text-align:center}
.ou-line{display:flex;align-items:center;justify-content:center;gap:18px;flex-wrap:wrap}
.ou-val{font-family:var(--mono);font-size:44px;font-weight:600;letter-spacing:-.04em;
  min-width:150px;color:var(--acc)}
.ou-val i{font-style:normal;font-size:.45em;opacity:.55}
.ou-dir{background:transparent;border:1px solid var(--line-2);color:var(--dim);padding:10px 18px}
.ou-dir.on[data-dir="over"]{background:var(--up);color:var(--on-up);border-color:var(--up);font-weight:700}
.ou-dir.on[data-dir="under"]{background:var(--down);color:var(--on-down);border-color:var(--down);font-weight:700}
#ouslider{width:100%;max-width:460px;margin:18px 0 8px;accent-color:var(--acc)}
.ou-hint{font-family:var(--mono);font-size:var(--fs-sm);color:var(--dim)}
.ou-hint b{color:var(--txt)}
.ou-last{font-family:var(--mono);font-size:var(--fs-sm);margin-top:14px;padding-top:12px;
  border-top:1px solid var(--line)}

/* ── crash chart axes ───────────────────────────────────────
   Labelled ticks are most of why a reference crash chart reads as an instrument
   rather than decoration — without a scale there is nothing to judge the climb
   against, which is why it looked flat however steep it actually was. */
.rc-axis line{stroke:var(--line);stroke-width:1}
.rc-axis text{fill:var(--dim-2);font-family:var(--mono);font-size:var(--fs-xs)}
.rc-axis .xt{fill:var(--dim-2);font-size:var(--fs-mini)}
.rkt-chart{height:250px}
.rkt-chart svg{overflow:visible}
.rc-line{stroke-width:3.5}

/* Three prediction markets, one row. A wall of full-width cards buried them. */
.grid{grid-template-columns:repeat(3,1fr)}
.card.rkt{grid-column:1/-1}
@media(max-width:1000px){ .grid{grid-template-columns:repeat(2,1fr)} }
@media(max-width:680px){ .grid{grid-template-columns:1fr} .rkt-chart{height:190px} }

/* three instant games side by side, same rhythm as the markets row */
.gamegrid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;align-items:start}
.gamegrid .card{margin-bottom:0}
.gamegrid .hl{grid-template-columns:1fr;gap:6px;padding:18px 14px}
.gamegrid .hl-vs{display:none}
.gamegrid .hl-val{font-size:var(--fs-3xl)}
.gamegrid .ou-val{font-size:32px;min-width:110px}
.gamegrid .mfgrid{grid-template-columns:repeat(4,1fr);gap:5px;padding:12px}
.gamegrid .mft{font-size:var(--fs-xs)}
.gamegrid .mft-sym{font-size:var(--fs-mini)}
@media(max-width:1000px){ .gamegrid{grid-template-columns:1fr} }

/* ═══ INSTANT GAMES ═════════════════════════════════════════
   Rebuilt from text-swaps into things with a reveal moment. The research on crash
   UIs is blunt about three things and all three were missing: anticipation before a
   result, instant tactile response to every press, and a cash-out control that is
   impossible to miss under stress. */

.game{display:flex;flex-direction:column;overflow:hidden}
.g-head{display:flex;align-items:center;gap:9px;padding:12px 15px;border-bottom:1px solid var(--line)}
.g-name{font-size:var(--fs-md);font-weight:650;letter-spacing:-.02em}
.g-tag{font-family:var(--mono);font-size:var(--fs-mini);letter-spacing:.09em;text-transform:uppercase;
  color:var(--dim-2);border:1px solid var(--line-2);padding:2px 7px;border-radius:var(--r-2)}
.g-mult{margin-left:auto;font-family:var(--mono);font-size:var(--fs-xl);font-weight:600;
  color:var(--acc);letter-spacing:-.03em}
.g-mult.dead{color:var(--down)}
.g-streak{margin-left:auto;font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc);
  border:1px solid var(--acc-line);background:var(--acc-soft);padding:2px 7px;border-radius:var(--r-2)}
.g-body{padding:16px 15px;flex:1;display:flex;flex-direction:column;justify-content:center}
.g-intro{align-items:center;text-align:center;gap:14px;min-height:190px}
.g-introtxt{font-size:var(--fs-md);color:var(--txt-2);line-height:1.6}
.g-foot{display:flex;align-items:center;gap:10px;padding:12px 15px;
  border-top:1px solid var(--line);background:var(--bg-1)}
.g-res{font-family:var(--mono);font-size:var(--fs-sm);color:var(--dim)}
.g-res b{font-size:var(--fs-lg);font-weight:600}
.g-res.up{color:var(--up)} .g-res.down{color:var(--down)}

/* every control answers the press before the network does */
.g-cta,.g-hi,.g-lo,.g-collect{font-weight:700;letter-spacing:-.01em;
  transition:transform var(--dur-1) var(--ease-2),filter var(--dur-1),background var(--dur-1)}
.g-cta:active:not(:disabled),.g-hi:active:not(:disabled),
.g-lo:active:not(:disabled),.g-collect:active:not(:disabled){transform:scale(.955)}
.g-cta{margin-left:auto;padding:9px 18px}
.busy{opacity:.55;pointer-events:none}
.g-hi,.g-lo{flex:1;padding:13px 0;font-size:var(--fs-md);font-family:var(--mono);letter-spacing:.06em}
.g-hi{background:var(--up);color:var(--on-up)} .g-hi:hover:not(:disabled){background:var(--up-hi)}
.g-lo{background:var(--down);color:var(--on-down)} .g-lo:hover:not(:disabled){background:var(--down-hi)}

/* ── higher / lower: the flip IS the game ── */
.g-ladder{display:flex;gap:4px;padding:0 15px 2px}
.rung{flex:1;text-align:center;font-family:var(--mono);font-size:var(--fs-mini);padding:3px 0;
  border-radius:var(--r-2);color:var(--dim-2);background:var(--bg-1);transition:var(--dur-4) var(--ease)}
.rung.on{color:var(--on-acc);background:var(--acc);font-weight:700}
.hl2{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:10px;min-height:190px}
.hl-mid{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2);letter-spacing:.16em}
.hlcard,.hlface{border:1px solid var(--line-2);border-radius:var(--r-5);background:var(--bg-1);
  padding:16px 8px;text-align:center;min-height:150px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px}
.hl-t{font-family:var(--mono);font-size:var(--fs-micro);letter-spacing:.14em;text-transform:uppercase;color:var(--dim-2)}
.hl-s{font-size:var(--fs-base);font-weight:650;letter-spacing:-.02em}
.hl-s.dim{color:var(--dim-2)}
.hl-n{font-family:var(--mono);font-size:var(--fs-3xl);font-weight:600;letter-spacing:-.04em}
.hl-q{font-family:var(--mono);font-size:40px;color:var(--dim-2)}

.hlflip{position:relative;min-height:150px;perspective:1000px}
.hlflip .hlface{position:absolute;inset:0;backface-visibility:hidden;
  transition:transform var(--dur-5) var(--ease-smooth)}
.hlflip .back{transform:rotateY(0deg)}
.hlflip .front{transform:rotateY(180deg)}
.hlflip.on .back{transform:rotateY(-180deg)}
.hlflip.on .front{transform:rotateY(0deg)}
.hlface.front.win{border-color:var(--up);background:rgba(var(--up-rgb),.10);
  box-shadow:0 0 30px rgba(var(--up-rgb),.18)}
.hlface.front.lose{border-color:var(--down);background:rgba(var(--down-rgb),.10)}

/* ── over / under: draw the distribution, do not assert the odds ── */
.ouchart{position:relative;height:120px;margin-bottom:6px}
.oubars{display:flex;align-items:flex-end;gap:1px;height:100%;
  border-bottom:1px solid var(--line)}
.oubars i{flex:1;background:var(--line-2);border-radius:2px 2px 0 0;
  transition:background var(--dur-2),height var(--dur-3) var(--ease)}
.oubars i.in{background:var(--acc)}
.ouline{position:absolute;top:-4px;bottom:0;width:2px;background:var(--txt);
  transition:left var(--dur-1) linear;pointer-events:none}
.ouline span{position:absolute;top:-18px;left:50%;transform:translateX(-50%);
  font-family:var(--mono);font-size:var(--fs-mini);color:var(--txt);white-space:nowrap}
.oumark{position:absolute;bottom:-2px;transform:translateX(-50%);
  font-family:var(--mono);font-size:var(--fs-micro);padding:2px 5px;border-radius:var(--r-2);white-space:nowrap;
  animation:drop var(--dur-5) var(--ease-spring)}
.oumark.win{background:var(--up);color:var(--on-up)} .oumark.lose{background:var(--down);color:var(--on-down)}
@keyframes drop{from{transform:translate(-50%,-26px);opacity:0}to{transform:translate(-50%,0);opacity:1}}
#ouslider{width:100%;accent-color:var(--acc);margin:6px 0 10px}
.oudirs{display:flex;gap:8px}
.oud{flex:1;padding:10px 0;font-family:var(--mono);font-size:var(--fs-sm);letter-spacing:.06em;
  background:transparent;border:1px solid var(--line-2);color:var(--dim)}
.oud.on[data-dir="over"]{background:var(--up);color:var(--on-up);border-color:var(--up);font-weight:700}
.oud.on[data-dir="under"]{background:var(--down);color:var(--on-down);border-color:var(--down);font-weight:700}

/* ── minefield: tiles turn over, they do not blink ── */
.mfgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.mft{position:relative;aspect-ratio:1;perspective:600px;cursor:pointer}
.mfface{position:absolute;inset:0;border-radius:var(--r-4);border:1px solid var(--line-2);
  background:var(--bg-1);display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:2px;backface-visibility:hidden;
  transition:transform var(--dur-4) var(--ease-smooth),border-color var(--dur-2),background var(--dur-2)}
.mfface.back{font-family:var(--mono);font-size:var(--fs-lg);color:var(--dim-2)}
.mfface.front{transform:rotateY(180deg)}
.mft.flipped .back{transform:rotateY(-180deg)}
.mft.flipped .front{transform:rotateY(0deg)}
.mft:not(.flipped):hover .back{border-color:var(--acc-line);background:var(--surface-2);
  transform:translateY(-2px)}
.mft:not(.flipped):active .back{transform:scale(.94)}
.mft.safe .front{border-color:rgba(var(--up-rgb),.45);background:rgba(var(--up-rgb),.09)}
.mft.mine .front{border-color:var(--down);background:rgba(var(--down-rgb),.16);
  animation:mineshock .5s var(--ease)}
@keyframes mineshock{
  0%{box-shadow:0 0 0 0 rgba(var(--down-rgb),.6)}
  100%{box-shadow:0 0 0 16px rgba(var(--down-rgb),0)}
}
.mft-s{font-size:var(--fs-xs);font-weight:650;letter-spacing:-.02em}
.mft-c{font-family:var(--mono);font-size:var(--fs-micro)}
.mfsel{display:flex;gap:5px;justify-content:center;flex-wrap:wrap}

/* the collect button is the most important control on the board */
.g-collect{margin-left:auto;background:var(--up);color:var(--on-up);padding:12px 20px;
  font-size:var(--fs-md);font-family:var(--mono);letter-spacing:.03em;
  box-shadow:0 0 0 0 rgba(var(--up-rgb),.5);animation:collectpulse 1.8s ease-in-out infinite}
.g-collect:hover:not(:disabled){background:var(--up-hi)}
.g-collect:disabled{animation:none;background:var(--line-2);color:var(--dim)}
@keyframes collectpulse{
  0%,100%{box-shadow:0 0 0 0 rgba(var(--up-rgb),.42)}
  50%{box-shadow:0 0 0 9px rgba(var(--up-rgb),0)}
}
@media(prefers-reduced-motion:reduce){
  .g-collect{animation:none}
  .hlflip .hlface,.mfface{transition:none}
}

/* ══════════════════════ THE CELEBRATION ══════════════════════
   Owned by public/games.js. The `.bigwin` overlay and its two original tiers live in
   floor.css beside the rest of the game floor; what is here is the third tier above
   them and the room line, both added by the pass that turned a big win from a toast
   into a moment.

   The performance rule the floor established and this obeys: nothing that MOVES may
   carry a shadow, a filter or a backdrop-filter, and every animation is transform or
   opacity only, so it runs on the compositor and never triggers layout or paint. The
   floor went from 25.3ms a frame to a fifth of a millisecond by deleting exactly those
   things, and a celebration is the obvious place to put them back.

   The one exception is the static text-shadow on the overlay's number, which is the
   pattern already set by `.bw-mult` in floor.css: it is set once on an element that is
   only ever transformed, so it is rasterised once and composited from then on. */

/* ── the top tier ──
   500x and up. Deliberately rare: the floor's own paytables can reach it, but on most
   games only through the single slot that pays for all the others, which is precisely
   what makes it worth building a look for. */
.bigwin.max{animation-duration:3.2s}
.bigwin.max .bw-mult{
  color:var(--acc);
  font-size:clamp(76px,19vw,220px);
  text-shadow:0 0 130px rgba(var(--acc-rgb),.7);
}
.bigwin.max .bw-pay{color:var(--acc);font-size:clamp(20px,3.4vw,34px)}

/* ── the room line ──
   One line above the ticker saying what just landed on this floor, to whoever it
   landed on. Height is reserved by the grid rather than animated, so the ticker under
   it does not jump when a notice arrives: it is opacity and transform only, and the
   element keeps its box whether or not it is showing. */
.groom{
  display:flex;align-items:center;gap:8px;
  margin:0 12px 6px;padding:7px 12px;
  border:1px solid rgba(var(--acc-rgb),.34);
  border-radius:var(--r-3);
  background:rgba(var(--acc-rgb),.09);
  font-size:var(--fs-xs);color:var(--txt-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  /* Out of the flow until it has something to say. `visibility` rather than `display`
     so the transition below has two states to move between. */
  opacity:0;transform:translateY(-6px);visibility:hidden;
  transition:opacity var(--dur-3,240ms) var(--ease-2,ease),
             transform var(--dur-3,240ms) var(--ease-2,ease),
             visibility 0s linear var(--dur-3,240ms);
}
.groom.on{
  opacity:1;transform:translateY(0);visibility:visible;
  transition:opacity var(--dur-4) var(--ease-spring),
             transform var(--dur-4) var(--ease-spring),
             visibility 0s;
}
.groom b{color:var(--txt);font-weight:650}
.groom em{font-style:normal;color:var(--acc);font-weight:700;letter-spacing:-.02em}
.groom u{text-decoration:none;color:var(--up);font-weight:650;margin-left:auto}
.groom-game{color:var(--dim)}
.groom-dot{
  flex:0 0 auto;width:6px;height:6px;border-radius:50%;background:var(--acc);
  animation:groom-dot 1.4s ease-in-out infinite;
}
/* Scale and opacity only. A pulsing box-shadow here is a repaint of the row on every
   frame of a live game canvas, which is the class of effect the floor's pass deleted. */
@keyframes groom-dot{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.55);opacity:.55}}

/* A ticker chip that cleared the big-win line. Static border and colour, no animation:
   thirty of these are on screen at once and the strip scrolls. */
.tk.big{border-color:rgba(var(--acc-rgb),.45);background:rgba(var(--acc-rgb),.08)}
.tk.big i{color:var(--acc);font-weight:700}

/* ── reduced motion ──
   Genuinely off, not softened. The overlay and the room line still APPEAR, because
   both carry information somebody asked for, and the number is the whole point of the
   celebration. Nothing moves to deliver it. The screenshake and the sparks are refused
   in games.js rather than here, because they are canvas and script rather than CSS and
   a stylesheet cannot switch them off. */
@media(prefers-reduced-motion:reduce){
  .bigwin.max{animation:none}
  .groom{transition:none}
  .groom-dot{animation:none}
}

/* /floor.css */
/* ═══════════════════════ THE GAME FLOOR ═══════════════════════
   Controls left, board centre, results above, everyone else below.

   This is the layout every established game site converges on, and it converges for
   a reason worth stating: the stake control never moves between games, so switching
   from Plinko to Limbo costs no relearning. The board is the only thing that changes.

   Inherits every token from style.css — one accent, green and red reserved for
   direction and outcome, nothing decorative. */

/* ── game picker ───────────────────────────────────────────────
   Nine tabs became five cards. A tab can only hold a word, and a word does not tell
   you which game you want; a card holds the name, what it pays and what you do, which
   is the whole decision. Equal columns rather than a scroller: five fit on a laptop,
   and a row you can scroll hides the last game from anyone who never scrolls it.

   The hint is the part that carries the weight, so it is present on every card and not
   only the selected one. It costs one line of height and it is the difference between
   a picker and a set of radio buttons. */
.grail{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:8px;padding:2px 0 14px}
.rl{position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:2px;
  min-width:0;padding:11px 14px 12px;border:1px solid var(--line);border-radius:var(--r-4);
  background:var(--surface);text-align:left;
  color:var(--txt-2);cursor:pointer;transition:all var(--dur-2) var(--ease-2)}
.rl b{font-size:var(--fs-md);font-weight:600;letter-spacing:-.01em}
.rl i{font-style:normal;font-size:var(--fs-mini);color:var(--dim);letter-spacing:.02em}
.rl em{font-style:normal;font-size:var(--fs-mini);line-height:1.35;color:var(--dim-2);
  margin-top:3px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* The ordinal is the route through the floor and the keyboard shortcut at the same
   time, so it is dim until you are on it rather than decoration you have to decode. */
.rl .rl-n{position:absolute;top:9px;right:11px;font-size:var(--fs-mini);color:var(--dim-2);
  opacity:.55;transition:opacity var(--dur-1),color var(--dur-1)}
.rl:hover{border-color:var(--line-3);color:var(--txt);transform:translateY(-1px)}
.rl:hover em{color:var(--dim)}
.rl.on{background:var(--acc-soft);border-color:var(--acc-line);color:var(--txt)}
.rl.on i{color:var(--acc)}
.rl.on em{color:var(--txt-2)}
.rl.on .rl-n{color:var(--acc);opacity:1}
.rl:active{transform:scale(.985)}
@media (prefers-reduced-motion:reduce){.rl,.rl:hover,.rl:active{transition:none;transform:none}}

/* ── floor ─────────────────────────────────────────────────── */
.floor{display:grid;grid-template-columns:264px 1fr;gap:14px;align-items:stretch}
.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-5);
  padding:16px;display:flex;flex-direction:column;gap:16px}
.stage{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-5);
  overflow:hidden;display:flex;flex-direction:column;min-height:420px}

.pblock{display:flex;flex-direction:column;gap:7px}
.pblock label{font-size:var(--fs-mini);text-transform:uppercase;letter-spacing:.09em;color:var(--dim)}
.pact{margin-top:auto}

/* ── stake ─────────────────────────────────────────────────── */
.stakerow{display:flex;gap:5px}
.stakein{flex:1;min-width:0;background:var(--bg-1);border:1px solid var(--line-2);border-radius:var(--r-3);
  padding:9px 11px;color:var(--txt);font:600 var(--fs-base) var(--mono);outline:none;transition:border-color var(--dur-1)}
.stakein:focus{border-color:var(--acc-line)}
.smod{background:var(--bg-1);border:1px solid var(--line-2);border-radius:var(--r-3);color:var(--txt-2);
  padding:0 9px;font:600 var(--fs-xs) var(--sans);cursor:pointer;transition:all var(--dur-1)}
.smod:hover{border-color:var(--line-3);color:var(--txt)}
.smod:active{transform:scale(.94)}
.qstakes{display:flex;flex-wrap:wrap;gap:5px}
.qs{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-3);color:var(--dim);
  padding:5px 9px;font:600 var(--fs-xs) var(--mono);cursor:pointer;transition:all var(--dur-1)}
.qs:hover{color:var(--txt);border-color:var(--line-3)}
.qs.on{background:var(--acc-soft);border-color:var(--acc-line);color:var(--acc)}
.qs:active{transform:scale(.94)}

/* ── segmented options ─────────────────────────────────────── */
.seg{display:flex;gap:4px;background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-4);padding:3px}
.seg button{flex:1;background:none;border:0;border-radius:var(--r-3);color:var(--dim);padding:7px 4px;
  font:600 var(--fs-xs) var(--sans);cursor:pointer;transition:all var(--dur-1)}
.seg button:hover{color:var(--txt)}
.seg button.on{background:var(--surface-2);color:var(--txt);box-shadow:var(--sh-1)}

/* ── the bet button ────────────────────────────────────────────
   The one control that must never be hunted for under pressure. */
.bet{width:100%;background:var(--acc);color:var(--on-acc);border:0;border-radius:var(--r-4);
  padding:15px;font:700 var(--fs-base) var(--sans);letter-spacing:-.01em;cursor:pointer;
  transition:transform var(--dur-1) var(--ease-2),filter var(--dur-1);
  box-shadow:0 6px 20px rgba(var(--acc-rgb),.22)}
.bet:hover{filter:brightness(1.08)}
.bet:active{transform:scale(.975)}
.bet.busy{opacity:.55;pointer-events:none}
/* A button the game cannot act on right now. It has to LOOK unpressable as well as be
   unpressable: a full-strength orange CTA reading "Boarding closed" is a promise, and a
   player who presses it and gets nothing has lost the window it was telling them about.
   The hover and press states are cancelled too, or it still answers the cursor. */
.bet:disabled{background:var(--surface-2);color:var(--txt-2);border:1px solid var(--line-2);
  box-shadow:none;cursor:default}
.bet:disabled:hover{filter:none}
.bet:disabled:active{transform:none}
.pnote{font-size:var(--fs-xs);color:var(--dim);text-align:center;min-height:14px}

/* ── fairness ──────────────────────────────────────────────── */
.fairbox{gap:8px}
.fairtog{display:flex;justify-content:space-between;align-items:center;width:100%;
  background:none;border:1px solid var(--line);border-radius:var(--r-4);color:var(--txt-2);
  padding:9px 11px;font:500 var(--fs-sm) var(--sans);cursor:pointer;transition:all var(--dur-1)}
.fairtog:hover{border-color:var(--line-3);color:var(--txt)}
.fairbody{display:flex;flex-direction:column;gap:7px}
.fairwhy{font-size:var(--fs-xs);line-height:1.55;color:var(--dim);margin:0 0 2px}
.fairbody label{font-size:var(--fs-mini);text-transform:uppercase;letter-spacing:.08em;color:var(--dim-2)}
.seed{display:block;background:var(--bg);border:1px solid var(--line);border-radius:var(--r-3);
  padding:7px 9px;font:var(--fs-xs) var(--mono);color:var(--txt-2);word-break:break-all;line-height:1.45}
.seed.sm{font-size:var(--fs-micro);color:var(--dim-2)}
.seedrow{display:flex;gap:5px}
.seedin{flex:1;min-width:0;background:var(--bg);border:1px solid var(--line);border-radius:var(--r-3);
  padding:7px 9px;color:var(--txt);font:var(--fs-xs) var(--mono);outline:none}
.seedsave,.rotate{background:var(--bg-1);border:1px solid var(--line-2);border-radius:var(--r-3);
  color:var(--txt-2);padding:7px 10px;font:600 var(--fs-xs) var(--sans);cursor:pointer;transition:all var(--dur-1)}
.seedsave:hover,.rotate:hover{border-color:var(--acc-line);color:var(--acc)}

/* ── results strip ─────────────────────────────────────────── */
.strip{display:flex;gap:5px;padding:11px 14px;border-bottom:1px solid var(--line);
  overflow-x:auto;scrollbar-width:none;min-height:42px;align-items:center}
.strip::-webkit-scrollbar{display:none}
.stripempty{font-size:var(--fs-xs);color:var(--dim-2)}
.chip{flex:0 0 auto;padding:4px 9px;border-radius:var(--r-6);font-size:var(--fs-xs);font-weight:600;
  border:1px solid transparent}
.chip.bad{background:rgba(var(--down-rgb),.09);color:var(--down-txt);border-color:rgba(var(--down-rgb),.2)}
.chip.ok{background:var(--surface-2);color:var(--txt-2);border-color:var(--line-2)}
.chip.big{background:rgba(var(--up-rgb),.1);color:var(--up);border-color:rgba(var(--up-rgb),.24)}
.chip.huge{background:var(--acc-soft);color:var(--acc);border-color:var(--acc-line)}
.chip.fresh{animation:chipin .34s var(--ease)}
@keyframes chipin{from{transform:scale(.6) translateY(-6px);opacity:0}to{transform:none;opacity:1}}

/* ── board ─────────────────────────────────────────────────── */
.board{flex:1;position:relative;display:flex;align-items:center;justify-content:center;padding:8px}
#pcanvas{display:block;max-width:100%}
.bwin{position:absolute;inset:0;pointer-events:none}
.bw{position:absolute;transform:translate(-50%,0);padding:3px 8px;border-radius:var(--r-3);
  font-size:var(--fs-sm);font-weight:700;animation:bwfly 1.1s var(--ease) forwards;white-space:nowrap}
.bw.w{background:var(--up);color:var(--on-up)}
.bw.l{background:var(--surface-2);color:var(--dim);border:1px solid var(--line-2)}
@keyframes bwfly{
  0%{opacity:0;transform:translate(-50%,6px) scale(.7)}
  18%{opacity:1;transform:translate(-50%,-4px) scale(1.06)}
  30%{transform:translate(-50%,-6px) scale(1)}
  100%{opacity:0;transform:translate(-50%,-42px) scale(.94)}
}

/* ── plinko: the price tag and the verdict ───────────────────
   The canvas used to be the whole board. It now sits in a column with a sentence under
   it, because the two things Plinko never said out loud are what a bucket costs before
   you press and how close the ball came after you did. */
.pstack{display:flex;flex-direction:column;align-items:center;width:100%;gap:6px}
.pboard{position:relative;width:100%}

/* Follows the cursor's height, pinned to the bucket's x. Anchored above the pointer so
   the hand holding the mouse is never covering the number it is asking about. */
.ptip{position:absolute;top:var(--py,0);left:0;transform:translate(-50%,calc(-100% - 14px));
  display:flex;align-items:baseline;gap:7px;white-space:nowrap;pointer-events:none;
  padding:5px 10px;border-radius:var(--r-3);background:var(--bg-1);border:1px solid var(--line-2);
  box-shadow:0 6px 18px rgba(0,0,0,.45);z-index:2}
.ptip b{font-size:var(--fs-md);font-weight:700}
.ptip span{font-size:var(--fs-mini);color:var(--txt-2)}
.ptip i{font-style:normal;font-size:var(--fs-mini);color:var(--dim)}
.ptip.hot{border-color:var(--acc-line)}
.ptip.hot b{color:var(--acc)}
.ptip.ok b{color:var(--up)}
.ptip.cold b{color:var(--dim)}

/* The line that names the counterfactual. Reserves its height when empty so the board
   does not jump up and down between drops. */
.pverdict{min-height:26px;display:flex;align-items:center;justify-content:center;gap:9px;
  font-size:var(--fs-sm);color:var(--dim);opacity:0;transition:opacity var(--dur-2)}
.pverdict.on{opacity:1}
.pverdict b{font-weight:700;font-size:var(--fs-md);color:var(--txt-2)}
.pverdict u{text-decoration:none;font-weight:600;color:var(--dim)}
.pverdict i{font-style:normal;font-size:var(--fs-mini);color:var(--dim-2)}
/* The net follows the net, not the game's verdict class. A 0.84× is a win-shaped
   multiplier and a losing bet, and the two must be allowed to disagree on screen. */
.pverdict u.up{color:var(--up)}
.pverdict u.down{color:var(--down)}
.pverdict.win b{color:var(--up)}
.pverdict.huge b{color:var(--acc)}
.pverdict.miss b{color:var(--txt-2)}
@media(max-width:700px){.pverdict{font-size:var(--fs-mini);gap:6px}.pverdict i{display:none}}

/* ── limbo ─────────────────────────────────────────────────── */
.limbo{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:14px;width:100%;padding:40px 20px}
.lbig{font-size:clamp(56px,11vw,116px);font-weight:700;letter-spacing:-.04em;line-height:1;
  color:var(--txt-2);transition:color var(--dur-2),text-shadow var(--dur-3)}
.lbig.rolling{color:var(--txt)}
.lbig.win{color:var(--up);text-shadow:0 0 48px rgba(var(--up-rgb),.42);animation:lpop .45s var(--ease)}
.lbig.lose{color:var(--down);opacity:.72}
@keyframes lpop{0%{transform:scale(1)}35%{transform:scale(1.09)}100%{transform:scale(1)}}
.lsub{font-size:var(--fs-md);color:var(--dim);min-height:19px;text-align:center}
.lsub b{color:var(--txt-2)}
.lsub b.up{color:var(--up)}
/* The counterfactual. Its own line and quieter than the verdict above it: it is the
   most interesting thing in a loss and it must not read as the headline of a win. */
.lsub .lv-note{display:block;margin-top:3px;font-size:var(--fs-xs);color:var(--dim-2)}
.lscale{position:relative;width:min(420px,80%);height:1px;background:var(--line-2);margin-top:6px}
.lscale i{position:absolute;top:-3px;width:1px;height:7px;background:var(--line-3)}
.lodds{display:flex;justify-content:space-between;align-items:baseline;font-size:var(--fs-xs);
  color:var(--dim);padding-top:2px}
.lodds b{font-size:var(--fs-md);color:var(--txt)}

/* ── live ticker ───────────────────────────────────────────── */
.ticker{margin-top:12px;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-5);
  overflow:hidden;padding:9px 0}
.tk-row{display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;padding:0 12px}
.tk-row::-webkit-scrollbar{display:none}
.tk{flex:0 0 auto;display:flex;align-items:center;gap:6px;padding:5px 10px;border-radius:var(--r-3);
  background:var(--bg-1);border:1px solid var(--line);font-size:var(--fs-xs);color:var(--dim);white-space:nowrap}
.tk b{color:var(--txt-2);font-weight:600}
.tk i{font-style:normal;color:var(--txt-2)}
.tk u{text-decoration:none;color:var(--up);font-weight:600}
.tk.w{border-color:rgba(var(--up-rgb),.2)}

/* ── signed-out intro ──────────────────────────────────────── */
.floor-intro .cbody{padding:22px}
.fi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:14px;margin:16px 0}
.fi-g{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-4);padding:13px}
.fi-g b{display:block;font-size:var(--fs-md);margin-bottom:2px}
.fi-tag{font-size:var(--fs-mini);color:var(--acc);letter-spacing:.02em}
.fi-g p{margin:7px 0 0;font-size:var(--fs-sm);color:var(--dim);line-height:1.5}
.fi-fair{font-size:var(--fs-sm);color:var(--txt-2);margin:0}
.fi-fair b{color:var(--acc)}

/* ── the coin games, hosted in the stage ───────────────────── */
.coingame{width:100%;padding:6px}
.coingame .card{margin:0;border:0;background:none}

@media(max-width:900px){
  .floor{grid-template-columns:1fr}
  .panel{order:2}
  .stage{order:1;min-height:360px}
}
@media(prefers-reduced-motion:reduce){
  .bw,.chip.fresh,.lbig.win{animation:none}
}

/* ── autobet ───────────────────────────────────────────────────
   The warning is not decoration. A doubling run from 100 points is broke by round
   ten on most balances, and that is a number worth reading before starting. */
#autobody{display:flex;flex-direction:column;gap:7px;margin-top:8px}
.autowarn{font-size:var(--fs-mini);line-height:1.45;color:var(--dim);min-height:14px}
.autowarn.hot{color:var(--down)}
.autostat{display:flex;justify-content:space-between;align-items:baseline;gap:8px;
  background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-3);
  padding:8px 10px;font-size:var(--fs-xs);color:var(--dim)}
.autostat b{font-family:var(--mono);font-size:var(--fs-md);color:var(--txt)}
.autostat b.up{color:var(--up)} .autostat b.down{color:var(--down)}
/* was #fff on --down: 3.24:1. --on-down is 6.02:1 on the same red, and this is
   15px bold text on the button a player hits to STOP an autobet run. */
.bet.running{background:var(--down);box-shadow:0 6px 20px rgba(var(--down-rgb),.24);color:var(--on-down)}
.bet.running:hover{filter:brightness(1.06)}

/* ══════════════════════ DICE ══════════════════════
   One track, one number. The shaded band IS the odds. */
.dice{display:flex;flex-direction:column;align-items:center;gap:22px;width:100%;padding:36px 28px}
.dresult{font-size:clamp(46px,9vw,88px);font-weight:700;letter-spacing:-.04em;line-height:1;
  color:var(--txt-2);transition:color var(--dur-2)}
.dresult.win{color:var(--up);text-shadow:0 0 44px rgba(var(--up-rgb),.38)}
.dresult.lose{color:var(--down);opacity:.75}
.dtrack{position:relative;width:min(560px,92%);height:14px;border-radius:var(--r-4);
  background:var(--bg-1);border:1px solid var(--line-2);cursor:ew-resize;touch-action:none}
.dfill{position:absolute;top:0;bottom:0;background:linear-gradient(90deg,rgba(var(--up-rgb),.32),rgba(var(--up-rgb),.52));
  border-radius:var(--r-4);transition:left var(--dur-1) var(--ease-2),width var(--dur-1) var(--ease-2)}
.dmark{position:absolute;top:50%;width:4px;height:30px;margin-left:-2px;border-radius:var(--r-1);
  background:var(--acc);transform:translateY(-50%);box-shadow:0 0 14px rgba(var(--acc-rgb),.6);
  transition:left var(--dur-1) var(--ease-2)}
.dmark span{position:absolute;top:-24px;left:50%;transform:translateX(-50%);
  font-size:var(--fs-sm);font-weight:700;color:var(--acc)}
.dhit{position:absolute;top:50%;width:2px;height:22px;margin-left:-1px;transform:translateY(-50%);
  background:var(--txt);animation:dhitin .4s var(--ease)}
.dhit i{position:absolute;top:20px;left:50%;transform:translateX(-50%);font-style:normal;
  font-size:var(--fs-xs);font-weight:700;white-space:nowrap}
.dhit.win{background:var(--up)} .dhit.win i{color:var(--up)}
.dhit.lose{background:var(--down)} .dhit.lose i{color:var(--down)}
@keyframes dhitin{from{opacity:0;transform:translateY(-50%) scaleY(.2)}to{opacity:1}}
.dscale{display:flex;justify-content:space-between;width:min(560px,92%);
  font:var(--fs-xs) var(--mono);color:var(--dim-2)}
.dhint{font-size:var(--fs-sm);color:var(--dim)}

/* ══════════════════════ WHEEL ══════════════════════ */
.wheelwrap{position:relative;display:flex;align-items:center;justify-content:center;padding:18px}
#wcanvas{display:block}
.wneedle{position:absolute;top:4px;left:50%;transform:translateX(-50%);
  width:0;height:0;border-left:9px solid transparent;border-right:9px solid transparent;
  border-top:16px solid var(--acc);filter:drop-shadow(0 2px 6px rgba(var(--shade-rgb),.6));z-index:2}
.wcentre{position:absolute;font-size:var(--fs-3xl);font-weight:700;color:var(--txt-2);letter-spacing:-.02em}
.wcentre.win{color:var(--up)} .wcentre.lose{color:var(--dim-2)}

/* ══════════════════════ TOWER ══════════════════════
   Drawn top-down: the rung you are climbing toward is at the top, where the eye is. */
.tower{display:flex;flex-direction:column;gap:5px;width:100%;max-width:440px;padding:16px}
.trow{display:flex;align-items:center;gap:10px;padding:3px 6px;border-radius:var(--r-4);
  transition:background var(--dur-2)}
.trow.live{background:var(--acc-soft);box-shadow:inset 0 0 0 1px var(--acc-line)}
.trow.done{opacity:.62}
.tmult{flex:0 0 62px;text-align:right;font-size:var(--fs-xs);font-weight:600;color:var(--dim)}
.trow.live .tmult{color:var(--acc)}
.ttiles{flex:1;display:flex;gap:5px}
.ttile{flex:1;height:38px;border-radius:var(--r-3);border:1px solid var(--line-2);
  background:var(--bg-1);color:var(--dim-2);font-size:var(--fs-base);cursor:not-allowed;
  transition:transform var(--dur-1) var(--ease-2),background var(--dur-2),border-color var(--dur-2)}
.ttile.live{cursor:pointer;background:var(--surface-2);border-color:var(--line-3);color:var(--txt-2)}
.ttile.live:hover{border-color:var(--acc-line);background:var(--acc-soft)}
.ttile.live:active{transform:scale(.94)}
.ttile.safe{background:rgba(var(--up-rgb),.16);border-color:rgba(var(--up-rgb),.4);color:var(--up)}
.ttile.trap{background:rgba(var(--down-rgb),.14);border-color:rgba(var(--down-rgb),.36);color:var(--down)}
.ttile.picked{box-shadow:0 0 0 2px var(--txt) inset}
.ttile.past{opacity:.4}
.seg.vert{flex-direction:column;gap:3px}
.seg.vert button{display:flex;justify-content:space-between;align-items:center;padding:8px 10px}
.seg.vert button i{font-style:normal;font-size:var(--fs-mini);color:var(--dim-2);font-family:var(--mono)}
.seg.vert button.on i{color:var(--acc)}

/* ══════════════════════ BIG WIN ══════════════════════
   Fires at 15x and up only. Anything more frequent becomes wallpaper. */
.bigwin{position:fixed;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:8px;pointer-events:none;z-index:200;
  animation:bigwin 1.9s var(--ease) forwards}
.bigwin.huge{animation-duration:2.6s}
.bw-mult{font-size:clamp(64px,16vw,180px);font-weight:800;letter-spacing:-.05em;line-height:1;
  color:var(--up);text-shadow:0 0 90px rgba(var(--up-rgb),.55)}
.bigwin.huge .bw-mult{color:var(--acc);text-shadow:0 0 110px rgba(var(--acc-rgb),.65)}
.bw-pay{font-size:clamp(18px,3vw,30px);font-weight:700;color:var(--txt)}
@keyframes bigwin{
  0%{opacity:0;transform:scale(.7)}
  12%{opacity:1;transform:scale(1.06)}
  22%{transform:scale(1)}
  72%{opacity:1;transform:scale(1)}
  100%{opacity:0;transform:scale(1.12)}
}

/* ══════════════════════ XP / LEVEL / STREAK ══════════════════════ */
.lvl{display:flex;align-items:center;gap:8px;cursor:pointer}
.lvl-badge{display:grid;place-items:center;min-width:26px;height:26px;padding:0 6px;
  border-radius:var(--r-3);background:var(--acc-soft);border:1px solid var(--acc-line);
  color:var(--acc);font:700 var(--fs-sm) var(--mono)}
.lvl-bar{position:relative;width:76px;height:5px;border-radius:var(--r-2);background:var(--line);overflow:hidden}
.lvl-bar i{position:absolute;inset:0 auto 0 0;background:var(--acc);border-radius:var(--r-2);
  transition:width var(--dur-5) var(--ease)}
.lvl-up{position:fixed;left:50%;top:22%;transform:translateX(-50%);z-index:210;
  background:var(--surface);border:1px solid var(--acc-line);border-radius:var(--r-5);
  padding:16px 26px;text-align:center;pointer-events:none;
  box-shadow:var(--sh-3);animation:lvlup 2.4s var(--ease) forwards}
.lvl-up b{display:block;font-size:var(--fs-2xl);color:var(--acc);letter-spacing:-.02em}
.lvl-up span{font-size:var(--fs-sm);color:var(--dim)}
@keyframes lvlup{
  0%{opacity:0;transform:translateX(-50%) translateY(14px) scale(.9)}
  10%{opacity:1;transform:translateX(-50%) translateY(0) scale(1)}
  80%{opacity:1}
  100%{opacity:0;transform:translateX(-50%) translateY(-12px)}
}

/* daily streak calendar */
.daily{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;margin:14px 0}
.dday{display:flex;flex-direction:column;align-items:center;gap:3px;padding:9px 4px;
  border-radius:var(--r-4);background:var(--bg-1);border:1px solid var(--line);
  font-size:var(--fs-mini);color:var(--dim-2)}
.dday b{font:700 var(--fs-xs) var(--mono);color:var(--txt-2)}
.dday.done{background:rgba(var(--up-rgb),.1);border-color:rgba(var(--up-rgb),.28)}
.dday.done b{color:var(--up)}
.dday.today{background:var(--acc-soft);border-color:var(--acc-line);
  box-shadow:0 0 0 1px var(--acc-line);animation:todaypulse 2s ease-in-out infinite}
.dday.today b{color:var(--acc)}
@keyframes todaypulse{0%,100%{box-shadow:0 0 0 1px var(--acc-line)}50%{box-shadow:0 0 0 3px var(--acc-soft)}}

@media(prefers-reduced-motion:reduce){
  .bigwin,.lvl-up,.dday.today,.dhit{animation:none}
}
@media(max-width:640px){
  .tower{padding:10px}
  .tmult{flex-basis:48px;font-size:var(--fs-mini)}
  .ttile{height:32px}
  .dice{padding:24px 14px;gap:16px}
}

/* ── 24-hour board, as one headline ──────────────────────────
   A ten-row table nobody is on is discouraging. One line saying what the best hit of
   the day was — and that it resets tonight — is an invitation. */
.topday{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:12px;
  padding:10px 14px;border-radius:var(--r-4);background:var(--surface);
  border:1px solid var(--line);font-size:var(--fs-sm);color:var(--dim)}
.topday .td-tag{font-size:var(--fs-mini);text-transform:uppercase;letter-spacing:.09em;
  color:var(--acc);padding:3px 7px;border-radius:var(--r-3);background:var(--acc-soft)}
.topday b{color:var(--txt);font-weight:600}
.topday i{font-style:normal;font-family:var(--mono);font-size:var(--fs-md);font-weight:700;color:var(--up)}
.topday .td-game{color:var(--dim-2)}
.topday .td-me{margin-left:auto;color:var(--acc)}
.topday .td-reset{color:var(--dim-2);font-size:var(--fs-xs)}
.topday .td-me + .td-reset{margin-left:0}
.topday .td-reset:first-of-type{margin-left:auto}

/* sound toggle — off by default, and it looks off */
.sndbtn{padding:6px 9px !important;font-size:var(--fs-md);line-height:1;opacity:.5;transition:opacity var(--dur-1)}
.sndbtn:hover{opacity:.85}
.sndbtn.on{opacity:1;color:var(--acc);border-color:var(--acc-line)}

/* ── the grid min-width trap ──────────────────────────────────
   Grid and flex items default to min-width:auto, which means they refuse to shrink
   below their own content. On a 375px phone the panel and stage both measured 386px
   inside a 343px floor — a 43px overhang, from a column that IS correctly set to 1fr.
   The track was never the problem; the items were. This is the fix, and it has to be
   on every nested scroller too or the overflow just moves down a level. */
.floor > *, .stage > *, .panel > *{min-width:0}
.strip, .tk-row, .grail, .qstakes, .seg{min-width:0;max-width:100%}
.stakerow{min-width:0}
.stakein{min-width:0}

/* Five equal columns need about 150px each before the game name starts wrapping.
   Below that the picker becomes two rows rather than five slivers, because a card whose
   title breaks over two lines stops reading as a title. */
@media(max-width:1000px){
  .grail{grid-auto-flow:row;grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
}

/* Below the fold on a phone the board is what matters, so give it the room and let
   the controls sit under it. */
@media(max-width:700px){
  .floor{gap:10px}
  .panel{padding:13px;gap:13px}
  .stage{min-height:300px}
  .board{padding:4px}
  .rl{padding:8px 11px}
  .rl b{font-size:var(--fs-sm)}
  /* The hint is what makes the card worth its height on a laptop and what makes it
     unreadable on a phone: two lines of 11px across a 100px column is a stack of
     single words. Name and tag survive; the sentence goes. */
  .rl em{display:none}
  .rl .rl-n{display:none}
  .topday{font-size:var(--fs-xs);padding:9px 11px;gap:7px}
  .topday .td-me{margin-left:0}
  .wheelwrap{padding:8px}
  .bw-mult{font-size:clamp(52px,18vw,110px)}
  .daily{gap:4px}
  .dday{padding:7px 2px;font-size:var(--fs-micro)}
  .dday b{font-size:var(--fs-mini)}
}

/* ── header at phone width ────────────────────────────────────
   The bar carries handle, level, balance, streak, wallet, bonus and sound in one row.
   At 375px that content measures 777px, which is where the page's horizontal scroll
   was coming from — not from the game floor at all. Let it wrap, and drop the two
   items that are decoration rather than information. */
@media(max-width:700px){
  header .bar{flex-wrap:wrap;row-gap:8px}
  .acct{flex-wrap:wrap;gap:7px;min-width:0}
  .acct .streak{display:none}          /* shown on the account tab anyway */
  .lvl-bar{width:52px}
  .walletchip{max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  #bonusBtn{order:3}
}
@media(max-width:420px){
  .lvl-bar{display:none}               /* the badge alone still reads as a level */
  .brand em{display:none}
}

/* ── trophy wall ──────────────────────────────────────────────
   Locked entries stay fully legible. A greyed-out goal you cannot read is not a goal,
   it is a rumour — so locked ones lose their colour, never their text. */
.xpline{height:6px;border-radius:var(--r-2);background:var(--line);overflow:hidden;margin:2px 0 6px}
.xpline i{display:block;height:100%;background:var(--acc);border-radius:var(--r-2);
  transition:width var(--dur-5) var(--ease)}
.xpnote{font-size:var(--fs-xs);color:var(--dim);margin-bottom:14px}
.trophies{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:7px;
  max-height:300px;overflow-y:auto;padding-right:3px;margin-bottom:14px}
.trophy{display:flex;flex-direction:column;gap:2px;padding:9px 11px;border-radius:var(--r-4);
  background:var(--bg-1);border:1px solid var(--line);opacity:.62;transition:opacity var(--dur-2)}
.trophy b{font-size:var(--fs-sm);color:var(--txt-2)}
.trophy span{font-size:var(--fs-mini);line-height:1.4;color:var(--dim-2)}
.trophy i{font-style:normal;font-size:var(--fs-mini);color:var(--dim);margin-top:2px}
.trophy.got{opacity:1;background:rgba(var(--up-rgb),.08);border-color:rgba(var(--up-rgb),.26)}
.trophy.got b{color:var(--txt)}
.trophy.got i{color:var(--up)}
/* Five ranks, and the accent has to escalate with them.
   The catalogue went from 17 entries to 88 and from three difficulty ranks to five.
   Everything above rank 3 was rendering with the rank 3 accent, so "cash out of Crash
   at 100x" looked exactly like "play all ten games". The rarest entries are the reason
   the common ones feel worth having, so they are the ones that must read differently.
   The escalation is thickness and reach, not new hues: the accent already means
   "notable" everywhere on this site, and a fourth colour would mean nothing. */
.trophy.t3{border-left:2px solid var(--acc-line)}
.trophy.t3.got{border-left-color:var(--acc)}
.trophy.t3.got i{color:var(--acc)}

/* Progress on a locked row. 88 entries is too many to read, so the ones you are close
   to have to be findable by shape rather than by reading every tile. The bar replaces
   the reward figure rather than sitting beside it: on a locked row "how far along am I"
   beats "what does it pay", and both at once is clutter. */
.tbar{height:3px;border-radius:var(--r-2);background:var(--line);overflow:hidden;margin:5px 0 3px}
.tbar i{display:block;height:100%;background:var(--acc-line);border-radius:var(--r-2);
  transition:width var(--dur-5) var(--ease)}
.trophy .tprog{color:var(--dim)}
/* Nearly there. Same accent the earned state uses, so the eye reads it as the same
   family: this one is about to become one of those. */
.trophy.close{opacity:.85;border-color:rgba(var(--acc-rgb),.24)}
.trophy.close .tbar i{background:var(--acc)}
.trophy.close .tprog{color:var(--acc)}

.trophy.t4{border-left:3px solid var(--acc-line)}
.trophy.t4.got{border-left-color:var(--acc);background:rgba(var(--acc-rgb),.07);
  border-color:rgba(var(--acc-rgb),.30)}
.trophy.t4.got b{color:var(--txt)}
.trophy.t4.got i{color:var(--acc)}

/* Rank 5 is "almost nobody". It gets the full border rather than an edge, because at a
   glance across 88 tiles an edge is not enough to find. */
.trophy.t5{border-left:3px solid var(--acc)}
.trophy.t5.got{border:1px solid var(--acc);border-left-width:3px;
  background:rgba(var(--acc-rgb),.12);
  box-shadow:inset 0 0 0 1px rgba(var(--acc-rgb),.10)}
.trophy.t5.got b{color:var(--acc)}
.trophy.t5.got i{color:var(--acc)}

/* the token chip — a link out to pump.fun, never a transaction */
.tokenchip{text-decoration:none;color:var(--dim);transition:all var(--dur-1)}
.tokenchip b{color:var(--acc);font-family:var(--mono);font-weight:600}
.tokenchip:hover{border-color:var(--acc-line);color:var(--txt-2)}

/* ══════════ LIFTING THE THREE OLDER GAMES ══════════
   Minefield, Hi-Lo and Over/Under were built before the floor rebuild and sat in
   small cards. In the full-width stage they were the same size as before with more
   space around them, which read as unfinished rather than spacious.

   Nothing here changes a rule. It is entirely about weight: bigger targets, real
   depth, and a reveal that feels like something happened. */

.coingame{width:100%;padding:10px 14px;display:flex;justify-content:center}
.coingame .card{margin:0;border:0;background:none;width:100%;max-width:560px}
.coingame .g-head{display:flex;align-items:center;gap:10px;padding:2px 2px 14px}
.coingame .g-name{font-size:var(--fs-base);font-weight:600;letter-spacing:-.01em}
.coingame .g-tag{font-size:var(--fs-mini);text-transform:uppercase;letter-spacing:.08em;
  color:var(--dim);padding:3px 8px;border-radius:var(--r-3);background:var(--bg-1);border:1px solid var(--line)}
.coingame .g-mult{margin-left:auto;font:700 var(--fs-2xl) var(--mono);color:var(--acc);letter-spacing:-.02em}
.coingame .g-mult.dead{color:var(--down)}
.coingame .g-foot{display:flex;align-items:center;gap:10px;padding-top:14px}
.coingame .g-res{font-size:var(--fs-sm);color:var(--dim)}
.coingame .g-res.up{color:var(--up)} .coingame .g-res.down{color:var(--down)}
.coingame .g-cta,.coingame .g-collect{margin-left:auto}

/* ── MINEFIELD ───────────────────────────────────────────────
   Four across, square, and big enough to hit on a phone without aiming. */
.coingame .mfgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.coingame .mft{position:relative;aspect-ratio:1;perspective:800px;cursor:pointer;
  background:none;border:0;padding:0;font-size:inherit}
.mfface{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:2px;border-radius:var(--r-5);backface-visibility:hidden;
  transition:transform var(--dur-4) var(--ease),box-shadow var(--dur-2);transform-style:preserve-3d}
.mfface.back{background:linear-gradient(160deg,var(--surface-2),var(--bg-1));
  border:1px solid var(--line-2);box-shadow:0 3px 0 rgba(var(--shade-rgb),.45),inset 0 1px 0 rgba(var(--light-rgb),.05)}
.mf-q{width:9px;height:9px;border-radius:50%;background:var(--line-3);transition:all var(--dur-2)}
.coingame .mft:not(.flipped):hover .mfface.back{transform:translateY(-3px);
  border-color:var(--acc-line);box-shadow:0 6px 0 rgba(var(--shade-rgb),.45),0 0 20px var(--acc-soft)}
.coingame .mft:not(.flipped):hover .mf-q{background:var(--acc);transform:scale(1.5)}
.coingame .mft:not(.flipped):active .mfface.back{transform:translateY(0);box-shadow:0 1px 0 rgba(var(--shade-rgb),.45)}
.mfface.front{transform:rotateY(180deg);border:1px solid var(--line-2);background:var(--surface-2)}
.coingame .mft.flipped .mfface.back{transform:rotateY(-180deg)}
.coingame .mft.flipped .mfface.front{transform:rotateY(0)}
.coingame .mft.safe .mfface.front{background:rgba(var(--up-rgb),.12);border-color:rgba(var(--up-rgb),.4);
  box-shadow:inset 0 0 24px rgba(var(--up-rgb),.14)}
.coingame .mft.mine .mfface.front{background:rgba(var(--down-rgb),.16);border-color:rgba(var(--down-rgb),.5);
  box-shadow:inset 0 0 30px rgba(var(--down-rgb),.22),0 0 26px rgba(var(--down-rgb),.3)}
.mft-icon{font-size:clamp(18px,4.6vw,26px);line-height:1;filter:drop-shadow(0 2px 4px rgba(var(--shade-rgb),.5))}
.coingame .mft.mine .mft-icon{animation:mineboom .5s var(--ease)}
@keyframes mineboom{0%{transform:scale(.3)}45%{transform:scale(1.45)}100%{transform:scale(1)}}
.mft-s{font:600 var(--fs-micro) var(--mono);color:var(--txt-2);max-width:92%;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.mft-c{font:600 var(--fs-micro) var(--mono)}
.mft-c.up{color:var(--up)} .mft-c.down{color:var(--down)}

/* ── HI-LO ───────────────────────────────────────────────────
   The card is the whole game, so give it card proportions and a real flip. */
.coingame .hl{display:flex;flex-direction:column;align-items:center;gap:16px;padding:8px 0}
.hlflip{position:relative;width:min(220px,62vw);aspect-ratio:3/4;perspective:1100px}
.hlface{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:8px;border-radius:var(--r-6);backface-visibility:hidden;
  transition:transform var(--dur-5) var(--ease);border:1px solid var(--line-2)}
.hlface.back{background:linear-gradient(155deg,var(--surface-2),var(--bg-1));
  box-shadow:0 10px 30px rgba(var(--shade-rgb),.5),inset 0 1px 0 rgba(var(--light-rgb),.05)}
.hlface.front{transform:rotateY(180deg);background:var(--surface-2)}
.hlflip.on .hlface.back{transform:rotateY(-180deg)}
.hlflip.on .hlface.front{transform:rotateY(0)}
.hlface.front.win{background:rgba(var(--up-rgb),.14);border-color:rgba(var(--up-rgb),.45);
  box-shadow:0 0 44px rgba(var(--up-rgb),.28)}
.hlface.front.lose{background:rgba(var(--down-rgb),.13);border-color:rgba(var(--down-rgb),.42);
  box-shadow:0 0 40px rgba(var(--down-rgb),.22)}
.hl-q{font:700 54px var(--mono);color:var(--dim-2)}
.hl-n{font:700 30px var(--mono);letter-spacing:-.03em}
.hlface.front.win .hl-n{color:var(--up)} .hlface.front.lose .hl-n{color:var(--down)}
.hl-sym{font:600 var(--fs-sm) var(--mono);color:var(--txt-2)}
.coingame .g-ladder{display:flex;gap:5px;justify-content:center;flex-wrap:wrap}
.coingame .rung{padding:4px 9px;border-radius:var(--r-3);font:600 var(--fs-mini) var(--mono);
  background:var(--bg-1);border:1px solid var(--line);color:var(--dim-2);transition:all var(--dur-3)}
.coingame .rung.on{background:var(--acc-soft);border-color:var(--acc-line);color:var(--acc);
  transform:translateY(-2px)}
.coingame .g-hi,.coingame .g-lo{flex:1;padding:13px;border-radius:var(--r-4);font:700 var(--fs-md) var(--sans);
  border:1px solid var(--line-2);background:var(--surface-2);color:var(--txt);cursor:pointer;
  transition:all var(--dur-1) var(--ease-2)}
.coingame .g-hi:hover{border-color:rgba(var(--up-rgb),.5);background:rgba(var(--up-rgb),.1);color:var(--up)}
.coingame .g-lo:hover{border-color:rgba(var(--down-rgb),.5);background:rgba(var(--down-rgb),.1);color:var(--down)}
.coingame .g-hi:active,.coingame .g-lo:active{transform:scale(.96)}

/* ── OVER / UNDER ────────────────────────────────────────────
   The histogram IS the pitch: real odds you can see rather than a number we assert. */
.coingame .ouchart{position:relative;width:100%;height:150px;margin:6px 0 2px}
.coingame .oubars{display:flex;align-items:flex-end;gap:2px;height:100%}
.coingame .oubars i{flex:1;background:var(--line);border-radius:3px 3px 0 0;
  transition:background var(--dur-2),opacity var(--dur-2);opacity:.45;min-height:2px}
.coingame .oubars i.in{background:linear-gradient(180deg,var(--acc),rgba(var(--acc-rgb),.35));opacity:1}
.coingame .ouline{position:absolute;top:-4px;bottom:0;width:2px;background:var(--txt);
  cursor:ew-resize;box-shadow:0 0 12px rgba(var(--light-rgb),.4)}
.coingame .ouline span{position:absolute;top:-20px;left:50%;transform:translateX(-50%);
  font:700 var(--fs-xs) var(--mono);color:var(--txt);white-space:nowrap}
.coingame .oud{flex:1;padding:11px;border-radius:var(--r-4);font:700 var(--fs-sm) var(--sans);
  border:1px solid var(--line-2);background:var(--surface-2);color:var(--txt-2);cursor:pointer;
  transition:all var(--dur-1)}
.coingame .oud.on{background:var(--acc-soft);border-color:var(--acc-line);color:var(--acc)}
.coingame .oud:active{transform:scale(.96)}

@media(max-width:640px){
  .coingame{padding:6px}
  .coingame .mfgrid{gap:6px}
  .hlflip{width:min(180px,58vw)}
  .coingame .ouchart{height:120px}
}
@media(prefers-reduced-motion:reduce){
  .mfface,.hlface{transition:none}
  .coingame .mft.mine .mft-icon{animation:none}
}

/* ══════════ THE STAGE ══════════
   The backdrop canvas sits behind every board. Boards float above it. */
.stage{position:relative;isolation:isolate}
#stagefx{position:absolute;inset:0;z-index:0;pointer-events:none;display:block}
.stage > .strip,.stage > .board{position:relative;z-index:1}

/* ══════════ LIMBO, REBUILT ══════════
   The number climbs, the rungs light as it passes them, and the target is a line it
   visibly crosses. It used to be one static figure on a flat panel. */
.limbo{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:18px;width:100%;padding:30px 20px;position:relative}
.lrungs{display:flex;gap:5px;flex-wrap:wrap;justify-content:center}
.lrungs i{font-style:normal;padding:4px 10px;border-radius:var(--r-6);
  background:var(--bg-1);border:1px solid var(--line);transition:all var(--dur-3) var(--ease)}
.lrungs i b{font:600 var(--fs-mini) var(--mono);color:var(--dim-2)}
.lrungs i.lit{background:rgba(var(--up-rgb),.12);border-color:rgba(var(--up-rgb),.35);
  transform:translateY(-3px)}
.lrungs i.lit b{color:var(--up)}
.lrungs i.goal{box-shadow:0 0 0 2px var(--acc-line)}
.lrungs i.goal b{color:var(--acc)}
.lstack{display:flex;flex-direction:column;align-items:center;gap:6px;position:relative}
.ltarget{font:600 var(--fs-xs) var(--mono);color:var(--dim);letter-spacing:.06em;
  padding:3px 12px;border-radius:var(--r-6);border:1px dashed var(--line-3)}
.lbig{font-size:clamp(58px,12vw,130px);font-weight:800;letter-spacing:-.045em;line-height:1;
  color:var(--txt-2);transition:color var(--dur-1) linear,text-shadow var(--dur-2);transform-origin:center}
.lbig.lose{opacity:.7}
.lsub{font-size:var(--fs-md);color:var(--dim);min-height:19px;text-align:center}
.lsub b{color:var(--txt-2)} .lsub b.up{color:var(--up)}

/* ══════════ CASE OPENING ══════════
   The reel is the whole game, so it gets the whole stage: full-bleed, tiles lit by
   their tier colour, and a marker you watch items slide under. */
.casewrap{position:relative;width:100%;padding:26px 0}
.cviewport{position:relative;overflow:hidden;width:100%;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}
.cstrip{display:flex;gap:8px;will-change:transform}
.ctile{position:relative;flex:0 0 auto;width:88px;height:110px;border-radius:var(--r-5);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;
  background:linear-gradient(165deg,var(--surface-2),var(--bg-1));
  border:1px solid var(--line-2);border-bottom:3px solid var(--tier);overflow:hidden}
.ct-glow{position:absolute;inset:0;background:radial-gradient(60% 50% at 50% 100%,var(--tier),transparent 70%);
  opacity:.22;pointer-events:none}
.ct-mult{font:700 var(--fs-lg) var(--mono);letter-spacing:-.02em;color:var(--txt);position:relative}
.ct-name{font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.07em;color:var(--tier);
  font-weight:700;position:relative}
.ctile.landed{animation:cland .5s var(--ease)}
.ctile.landed .ct-glow{opacity:.6}
@keyframes cland{0%{transform:scale(1)}40%{transform:scale(1.12)}100%{transform:scale(1)}}
.cmarker{position:absolute;left:50%;top:14px;bottom:14px;width:2px;margin-left:-1px;z-index:3;
  background:linear-gradient(180deg,transparent,var(--acc),transparent);
  box-shadow:0 0 16px var(--acc)}
.cmarker::before,.cmarker::after{content:"";position:absolute;left:50%;transform:translateX(-50%);
  border-left:6px solid transparent;border-right:6px solid transparent}
.cmarker::before{top:-2px;border-top:8px solid var(--acc)}
.cmarker::after{bottom:-2px;border-bottom:8px solid var(--acc)}
.cresult{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-top:20px;min-height:34px}
.cres-item{display:flex;align-items:center;gap:8px;padding:7px 13px;border-radius:var(--r-4);
  background:var(--bg-1);border:1px solid var(--tier);animation:cresin .4s var(--ease)}
.cres-item b{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--tier)}
.cres-item span{font:700 var(--fs-md) var(--mono);color:var(--txt)}
.cres-item u{text-decoration:none;font:600 var(--fs-sm) var(--mono);color:var(--up)}
.cres-item.big{box-shadow:0 0 26px color-mix(in srgb,var(--tier) 40%,transparent)}
@keyframes cresin{from{opacity:0;transform:translateY(8px) scale(.9)}to{opacity:1;transform:none}}
.coddslist{display:flex;flex-direction:column;gap:3px;max-height:210px;overflow-y:auto}
.codds{display:flex;align-items:center;gap:7px;padding:5px 8px;border-radius:var(--r-3);
  background:var(--bg-1);font-size:var(--fs-mini)}
.codds i{width:8px;height:8px;border-radius:var(--r-1);flex:0 0 auto}
.codds b{flex:1;color:var(--txt-2);font-weight:600}
.codds span{font-family:var(--mono);color:var(--txt)}
.codds u{text-decoration:none;font-family:var(--mono);color:var(--dim-2);min-width:44px;text-align:right}
@media(max-width:640px){ .ctile{width:70px;height:92px} }
@media(prefers-reduced-motion:reduce){ .cstrip{transition:none !important} .ctile.landed{animation:none} }

/* ══════════ TOWER, REBUILT ══════════
   It was a list of grey boxes. A climb needs three things a list does not have:
   HEIGHT you can see, a POSITION you occupy, and DANGER that is telegraphed before
   you commit — otherwise every rung is the same decision made nine times. */
.tower{display:flex;flex-direction:column;gap:6px;width:100%;max-width:460px;
  padding:18px 14px;position:relative}
/* The shaft: a lit column behind the ladder, so the tower has a body. */
.tower::before{content:"";position:absolute;left:50%;top:8px;bottom:8px;width:74%;
  transform:translateX(-50%);border-radius:var(--r-5);z-index:0;
  background:linear-gradient(180deg,rgba(var(--acc-rgb),.07),rgba(var(--shade-rgb),.25));
  border:1px solid var(--line);pointer-events:none}
.trow{position:relative;z-index:1;display:flex;align-items:center;gap:10px;
  padding:4px 8px;border-radius:var(--r-4);transition:all var(--dur-3) var(--ease)}
/* Cleared rungs recede — you are above them now. */
.trow.done{opacity:.5;transform:scale(.965)}
.trow.live{background:linear-gradient(90deg,transparent,var(--acc-soft),transparent);
  box-shadow:inset 0 0 0 1px var(--acc-line);animation:trowlive 2.4s ease-in-out infinite}
@keyframes trowlive{0%,100%{box-shadow:inset 0 0 0 1px var(--acc-line)}
  50%{box-shadow:inset 0 0 0 1px var(--acc),0 0 22px var(--acc-soft)}}
.tmult{flex:0 0 66px;text-align:right;font:600 var(--fs-xs) var(--mono);color:var(--dim);
  transition:all var(--dur-3)}
.trow.live .tmult{color:var(--acc);font-size:var(--fs-md);font-weight:700}
.trow.done .tmult{color:var(--up)}
.ttiles{flex:1;display:flex;gap:6px}
/* Tiles have depth: a lip below, and they press into it when hit. */
.ttile{flex:1;height:42px;border-radius:var(--r-4);border:1px solid var(--line-2);
  background:linear-gradient(165deg,var(--surface-2),var(--bg-1));
  color:var(--dim-2);font-size:var(--fs-lg);cursor:not-allowed;position:relative;
  box-shadow:0 3px 0 rgba(var(--shade-rgb),.5);
  transition:transform var(--dur-1) var(--ease-2),background var(--dur-2),border-color var(--dur-2),box-shadow var(--dur-1)}
.ttile.live{cursor:pointer;color:var(--txt-2);border-color:var(--line-3)}
.ttile.live:hover{transform:translateY(-2px);border-color:var(--acc-line);
  background:linear-gradient(165deg,var(--acc-soft),var(--bg-1));
  box-shadow:0 5px 0 rgba(var(--shade-rgb),.5),0 0 20px var(--acc-soft)}
.ttile.live:active{transform:translateY(3px);box-shadow:0 0 0 rgba(var(--shade-rgb),.5)}
.ttile.safe{background:linear-gradient(165deg,rgba(var(--up-rgb),.22),rgba(var(--up-rgb),.08));
  border-color:rgba(var(--up-rgb),.5);color:var(--up);box-shadow:0 3px 0 rgba(var(--shade-rgb),.4),
  inset 0 0 18px rgba(var(--up-rgb),.18)}
.ttile.trap{background:linear-gradient(165deg,rgba(var(--down-rgb),.26),rgba(var(--down-rgb),.1));
  border-color:rgba(var(--down-rgb),.6);color:var(--down);
  box-shadow:0 3px 0 rgba(var(--shade-rgb),.4),inset 0 0 22px rgba(var(--down-rgb),.24),0 0 26px rgba(var(--down-rgb),.3);
  animation:ttrap .45s var(--ease)}
@keyframes ttrap{0%{transform:scale(1)}30%{transform:scale(1.1) rotate(-2deg)}100%{transform:scale(1)}}
.ttile.picked{outline:2px solid var(--txt);outline-offset:1px}
.ttile.past{opacity:.35}
@media(max-width:640px){
  .tower{padding:12px 8px}
  .tmult{flex-basis:52px;font-size:var(--fs-mini)}
  .ttile{height:36px}
}
@media(prefers-reduced-motion:reduce){
  .trow.live{animation:none}
  .ttile.trap{animation:none}
}

/* ══════════ IDLE / RIGS ══════════
   A number in the header that climbs while you do nothing is the whole hook. It has to
   be visible from every screen, and it has to be obviously collectable. */
/* `background:transparent` is the whole fix, and it was a real bug: this is a
   <button>, style.css's base button rule fills it with var(--acc), and this rule
   only ever set border-COLOR — so the idle chip has been rendering as a solid
   orange primary button in the header, competing with the actual primary CTA
   for exactly the attention it is not supposed to take. Worse, `.meta-chip`
   sets color:var(--dim) and beats the base rule's --on-acc, so the label was
   grey-on-orange at 1.16:1 — the single worst contrast measurement on the site,
   and unreadable in a screenshot. It reads as a chip now, which is what the
   `.ready` state below has always assumed by tinting it with --acc-soft. */
.idlechip{cursor:pointer;transition:all var(--dur-2);
  background:transparent;border:1px solid var(--line);color:var(--dim)}
.idlechip b{font-style:normal;font-weight:400;filter:grayscale(1);opacity:.6}
.idlechip.ready{border-color:var(--acc-line);background:var(--acc-soft);color:var(--acc)}
.idlechip.ready b{filter:none;opacity:1}
.idlechip.ready .num{color:var(--acc);font-weight:700}
.idlechip:hover{border-color:var(--line-3)}

.idlehead{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px}
.idle-pending,.idle-rate{background:var(--bg-1);border:1px solid var(--line);
  border-radius:var(--r-4);padding:12px 14px;display:flex;flex-direction:column;gap:2px}
.idle-pending{border-color:var(--acc-line);background:var(--acc-soft)}
.idlehead .k{font-size:var(--fs-mini);text-transform:uppercase;letter-spacing:.09em;color:var(--dim)}
.idlehead b{font:700 var(--fs-2xl) var(--mono);letter-spacing:-.02em;color:var(--txt)}
.idle-pending b{color:var(--acc)}
.idlehead i{font-style:normal;font-size:var(--fs-mini);color:var(--dim-2)}
.idlehead i.capped{color:var(--down)}

.riglist{display:flex;flex-direction:column;gap:6px;max-height:300px;overflow-y:auto;margin-bottom:14px}
.rig{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:var(--r-4);
  background:var(--bg-1);border:1px solid var(--line);transition:all var(--dur-2)}
.rig:hover{border-color:var(--line-3)}
.rig.poor{opacity:.55}
.rig-l{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.rig-l b{font-size:var(--fs-md);color:var(--txt)}
.rig-l span{font-size:var(--fs-mini);color:var(--dim-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rig-r{display:flex;align-items:center;gap:9px;flex:0 0 auto}
.rig-r u{text-decoration:none;font:700 var(--fs-md) var(--mono);color:var(--txt-2);min-width:22px;text-align:right}
.rig-r i{font-style:normal;font-size:var(--fs-mini);color:var(--up);min-width:56px;text-align:right}
.rigbuy{background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r-3);
  color:var(--txt);padding:7px 11px;font:600 var(--fs-xs) var(--mono);cursor:pointer;transition:all var(--dur-1)}
.rigbuy:hover:not(:disabled){border-color:var(--acc-line);color:var(--acc);background:var(--acc-soft)}
.rigbuy:active:not(:disabled){transform:scale(.94)}
.rigbuy:disabled{opacity:.4;cursor:not-allowed}

.prestige{margin-bottom:14px}
.prestige-go{width:100%;padding:12px;border-radius:var(--r-4);border:1px solid var(--acc-line);
  background:linear-gradient(180deg,var(--acc-soft),transparent);color:var(--acc);
  font:700 var(--fs-sm) var(--sans);cursor:pointer;transition:all var(--dur-2)}
.prestige-go:hover{background:var(--acc);color:var(--on-acc)}
.prestige-lock{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-4);
  padding:11px 13px;font-size:var(--fs-xs);color:var(--dim)}
.pbar{height:5px;border-radius:var(--r-2);background:var(--line);overflow:hidden;margin:7px 0 4px}
.pbar i{display:block;height:100%;background:var(--acc);border-radius:var(--r-2);transition:width var(--dur-5) var(--ease)}
.prestige-lock span{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2)}
@media(max-width:640px){ .idlehead{grid-template-columns:1fr} }

/* ══════════════════ RIG BOARD ══════════════════
   Every class here carries the `grig` prefix. app.js and games.js share one global
   scope and one cascade; `.rail` and `paintStrip()` both existed twice and both broke
   silently, so nothing added here gets a name that could plausibly be taken. */

.grigcard .sub{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim);margin-left:auto}

/* Brackets. A division small enough to win beats a ladder you cannot move on. */
.grigchips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px}
.grigchip{display:inline-flex;align-items:center;gap:6px;padding:4px 9px;border-radius:var(--r-pill);
  border:1px solid var(--line-2);font-size:var(--fs-mini);color:var(--dim);white-space:nowrap}
.grigchip u{text-decoration:none;font:600 var(--fs-mini) var(--mono);color:var(--dim-2)}
.grigchip.on{border-color:var(--acc-line);background:var(--acc-soft);color:var(--acc)}
.grigchip.on u{color:var(--acc)}

/* Where you stand. Three panels: your division, your movement, your rival. */
.grigstand{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px}
.grigstand.wide{grid-template-columns:repeat(3,1fr)}
.grigstand-l,.grigrival{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-4);
  padding:10px 12px;display:flex;flex-direction:column;gap:2px;min-width:0}
.grigrival{border-color:var(--acc-line);background:linear-gradient(180deg,var(--acc-soft),transparent)}
.grigstand .k{font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.09em;color:var(--dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.grigstand b{font:700 var(--fs-xl) var(--mono);letter-spacing:-.02em;color:var(--txt)}
.grigstand b small{font:500 var(--fs-xs) var(--mono);color:var(--dim)}
.grigstand b.up{color:var(--up)} .grigstand b.down{color:var(--down)}
.grigstand em{font-style:normal;font-size:var(--fs-mini);color:var(--dim-2)}
.grigrival b{color:var(--acc);font-size:var(--fs-base)}

/* Near-goal pressure, in the rig panel. One row: what, how much it adds, how far off. */
.grignext{display:grid;grid-template-columns:auto 1fr auto;gap:3px 10px;align-items:center;
  background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-4);padding:10px 12px;margin-bottom:12px}
.grignext.ready{border-color:var(--acc-line);background:linear-gradient(180deg,var(--acc-soft),transparent)}
.grignext .k{font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.09em;color:var(--dim);grid-row:1}
.grignext b{font-size:var(--fs-md);color:var(--txt);grid-row:1}
.grignext i{font-style:normal;font:600 var(--fs-xs) var(--mono);color:var(--up);grid-row:1;text-align:right}
.grignext em{grid-column:1/-1;grid-row:2;font-style:normal;font-size:var(--fs-mini);color:var(--dim-2)}

/* "you earned X while you were away" — the single best opening line an idle game has. */
.grigaway{background:linear-gradient(180deg,var(--acc-soft),transparent);
  border:1px solid var(--acc-line);border-radius:var(--r-4);padding:10px 13px;margin-bottom:14px;
  font-size:var(--fs-sm);color:var(--txt-2)}
.grigaway b{color:var(--acc);font-family:var(--mono)}

/* Prestige, sold on what carries over rather than on what burns. */
.grigkeeps{margin-bottom:10px}
.grigkeeps.quiet{opacity:.66;margin:12px 0 0}
.grigkeeps .k{font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.09em;color:var(--dim);margin-bottom:6px}
.grigkeeps ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
.grigkeeps li{display:flex;flex-direction:column;gap:1px;padding:7px 11px;border-radius:var(--r-4);
  background:var(--bg-1);border:1px solid var(--line)}
.grigkeeps li b{font-size:var(--fs-xs);color:var(--txt)}
.grigkeeps li span{font-size:var(--fs-mini);color:var(--dim-2)}

/* Rows. .grigtag now only ever prints "you"; the house-account tag it also carried is
   gone, and the disclosure moved to the note under the table and to rules.html §9. */
.grighead{padding:12px 16px 6px;font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.09em;color:var(--dim)}
.grigtag{text-decoration:none;font:600 var(--fs-micro) var(--mono);text-transform:uppercase;letter-spacing:.1em;
  color:var(--dim);border:1px solid var(--line-2);border-radius:var(--r-1);padding:1px 4px;margin-left:6px;
  vertical-align:middle}
.grigtag.me{color:var(--acc);border-color:var(--acc-line);background:var(--acc-soft)}
.grigpersona{display:block;font-size:var(--fs-mini);color:var(--dim-2);margin-top:1px}
tr.grigme{background:var(--acc-soft)}
tr.grigme td{border-color:var(--acc-line)}

/* Overtakes. The only thing on a leaderboard that happens TO you. */
.grigtick{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}
.grigtick span{font-size:var(--fs-mini);color:var(--dim);background:var(--bg-1);border:1px solid var(--line);
  border-radius:var(--r-pill);padding:4px 10px}
.grigtick b{color:var(--txt-2);font-weight:600}
.grignote{border-top:1px solid var(--line);color:var(--dim-2);font-size:var(--fs-mini)}

@media(max-width:640px){
  .grigstand,.grigstand.wide{grid-template-columns:1fr}
  .grignext{grid-template-columns:1fr auto}
  .grignext .k{grid-column:1/-1}
}

/* ══════════════════ THE LANDING COLUMN ══════════════════
   Layout only. Every class added here carries a `podfold-`, `hdrkit-` or `pod`
   prefix for the same reason the rig board carries `grig-`: app.js and games.js
   share one global scope and one flat cascade, and `.rail` declared in two
   stylesheets broke the prize rail once already.

   MEASURED BEFORE ANY OF IT. Signed in at 1280x800 the first game sat 4,956px
   down the page, behind nine stacked surfaces; at 375x812 it sat 7,562px down,
   behind a header that had wrapped to three rows and 134px. Neither number is a
   density problem. They are an ordering problem: everything above the tabs was
   something a returning player likes, and none of it was the product. */

/* ── the hero, at a tenth of the page rather than half ──────
   It was 362px at 1280 and 332px at 375, which on a phone is a full screen of
   prose before anything happens. The pitch itself is worth keeping and is the
   only thing on the page written for somebody who has never been here, so it
   keeps its headline and one line under it. */
.hero{padding:34px 0 22px;max-width:720px}
.hero .eyebrow{margin-bottom:12px}
.hero h1{font-size:clamp(28px,3.9vw,44px)}
.hero p{margin:12px 0 0;font-size:var(--fs-md);line-height:1.55;max-width:62ch}
/* Signed in you have read it. app.js sets this class from paintAcct. */
body.podknows .hero{padding:20px 0 12px}
body.podknows .hero p{display:none}

/* ── the prize rail, on one line ────────────────────────────
   Two rows of board plus a third row for your own standing measured 129px, and
   it is sticky, so that was 129px permanently spent under a 57px sticky header.
   Clock, board and your place are three cells of one row now. No new background
   or shadow: this thing is sticky over a scrolling page and already carries a
   backdrop-filter from style.css, which is quite enough for it to be paying. */
.rail{margin:0 0 10px}
.rail-clock{padding:9px 15px;gap:2px}
.rail-cd{font-size:var(--fs-xl)}
/* THE BOARD SCROLLS, AT EVERY WIDTH. As an auto-fit grid it wrapped everywhere the
   track was tight: two rows at 768, five at 375, and five again at 1280 once the
   clock took its natural width. A row that wraps sets the height of a sticky bar,
   which is the one place on the page where height is charged on every screen. */
.rail-board{display:flex;align-items:center;padding:5px 8px;gap:2px;
  overflow-x:auto;scrollbar-width:none}
.rail-board::-webkit-scrollbar{display:none}
.rail-board .rr{flex:0 0 auto;gap:6px}
/* .rr.empty carried grid-column:1/-1 to span the old grid. Flex ignores that. */
.rail-board .rr.empty{flex:1 1 auto;justify-content:center;padding:6px}
@media(min-width:761px){
  /* Fixed outer tracks, not `auto`. On auto the clock measured 361px because the
     FINAL-minutes line under it is long, and the board was left with 231px. The
     rail came out 124px tall on a rule meant to make it one line. */
  .rail{grid-template-columns:200px minmax(0,1fr) 280px}
  .rail-clock{grid-column:1;grid-row:1}
  .rail-board{grid-column:2;grid-row:1}
  /* Out of the full-width second row and into a third cell. */
  .rail-you{grid-column:3;grid-row:1;border-top:0;border-left:1px solid var(--line);
    background:transparent;padding:9px 15px;min-width:0;overflow:hidden}
  .rail-you .rr-def{display:none}
  .rail-you .rr-gap{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
}
@media(max-width:760px){
  .rail{grid-template-columns:112px minmax(0,1fr)}
  .rail-clock{padding:8px 11px;flex-direction:column;gap:1px}
  .rail-board{padding:4px 6px}
  .rail-board .rr-z{display:none}
  /* "top 10 share 5,000" wrapped to two lines in a 112px column and was on its own
     33px of the rail's 123. The sticky ledger bar under the header already says the
     prize and the paying places on every screen. The FINAL-minutes tag is the other
     thing this line can hold and that one stays: it is the only urgent state the
     round has. */
  .rail-push > .k{display:none}
  .rail-you{padding:6px 11px;flex-wrap:wrap;gap:5px}
  .rail-you .rr-def{display:none}
}

/* ── the gaps between the bands above the tabs ──────────────
   Each of these was set by whoever added the band, and none of them was set
   against the others. 34px under a 36px ticker is a gap taller than the thing
   it follows. */
.tick{margin-bottom:14px}
nav{margin-bottom:16px}
.quickbar{padding:8px 14px;margin-bottom:14px}
/* On a phone style.css drops the stake chips onto their own full-width line, which
   made a 101px block out of one toggle and five buttons. They scroll instead. */
@media(max-width:640px){
  .quickbar{flex-wrap:nowrap;gap:10px}
  .qstakes{width:auto;margin-left:auto;min-width:0;flex:0 1 auto;flex-wrap:nowrap;
    overflow-x:auto;scrollbar-width:none}
  .qstakes::-webkit-scrollbar{display:none}
  .qs{flex:0 0 auto}
}
#strip{margin:0 0 20px}
/* The Games tab now opens on a section head, and .sec carries 34px of top
   margin for the ones that separate sections further down. */
.sec.secfirst{margin-top:14px}

/* ══════════════════ THE PROGRESSION SHELF ══════════════════
   One folding surface holding round missions, the season pass with its quests
   and rakeback, and the invite bar. The three panels are unchanged and still
   owned by app.js, meta.js and invite.js; this is a lid.

   Collapse is `display:none` on the body rather than a height transition, so a
   closed shelf costs no layout and no paint at all while the floor is running. */
.podfold{margin:0 0 14px}
.podfold-bar{width:100%;display:flex;align-items:center;gap:11px;text-align:left;
  padding:9px 14px;border:1px solid var(--line);border-radius:var(--r-4);
  background:var(--surface);color:var(--txt-2);font-size:var(--fs-md);font-weight:400;
  transition:border-color var(--dur-2) var(--ease-2),background var(--dur-2) var(--ease-2),
    color var(--dur-2) var(--ease-2)}
/* button:hover:not(:disabled) in style.css is (0,0,2,1); a bare .podfold-bar:hover
   loses to it and the shelf turns solid orange on hover. */
.podfold-bar:hover:not(:disabled){background:var(--surface-2);border-color:var(--line-3);color:var(--txt)}
.podfold-bar:active:not(:disabled){transform:none}
.podfold-t{font-weight:600;letter-spacing:-.01em;color:var(--txt);white-space:nowrap;flex:none}
.podfold-lvl{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);white-space:nowrap;flex:none}
.podfold-xp{width:74px;height:4px;border-radius:var(--r-1);background:var(--line);overflow:hidden;flex:none}
.podfold-xp i{display:block;height:100%;background:var(--acc);border-radius:var(--r-1)}
.podfold-sub{font-size:var(--fs-xs);color:var(--dim);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.podfold-tag{font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc);flex:none;
  border:1px solid var(--acc-line);background:var(--acc-soft);border-radius:var(--r-pill);
  padding:2px 9px;white-space:nowrap}
.podfold-chev{margin-left:auto;color:var(--dim-2);font-size:9px;flex:none;padding-left:6px}
.podfold-ready .podfold-bar{border-color:var(--acc-line)}
.podfold-body{display:none}
.podfold-on > .podfold-body{display:block;padding-top:12px}
/* Signed out with a referral ribbon in it: no lid, no summary, no padding above. */
.podfold-bare > .podfold-bar{display:none}
.podfold-bare > .podfold-body{padding-top:0}
/* The three panels each set their own bottom margin for a life above the tabs.
   Inside the shelf the last one only has to clear the shelf. */
.podfold-body > :last-child{margin-bottom:0}
@media(max-width:700px){
  .podfold-bar{gap:9px;padding:9px 12px}
  .podfold-sub{display:none}
  .podfold-xp{width:52px}
}
@media(max-width:420px){
  .podfold-lvl{display:none}
}

/* ══════════════════ THE HEADER TRAY ══════════════════
   Everything in the bar that is a setting or a piece of context rather than a
   live number. It is a descendant of .bar deliberately: profile.js guards its
   own injection with `bar.querySelector('.pf-open-btn')`, so the button has to
   stay somewhere inside .bar or a second one appears on every boot. */
.hdrkit{position:relative;flex:none}
.hdrkit-btn{padding:6px 11px;font-size:15px;line-height:1.1}
.hdrkit-menu{position:absolute;top:calc(100% + 9px);right:0;z-index:60;
  min-width:216px;max-width:min(78vw,300px);display:none;flex-direction:column;
  align-items:stretch;gap:9px;padding:13px;
  border:1px solid var(--line-3);border-radius:var(--r-4);background:var(--surface-2);
  box-shadow:0 6px 18px rgba(0,0,0,.5)}
.hdrkit-on .hdrkit-menu{display:flex}
.hdrkit-k{font-family:var(--mono);font-size:var(--fs-micro);letter-spacing:.13em;
  text-transform:uppercase;color:var(--dim-2)}
.hdrkit-sep{height:1px;background:var(--line)}
/* style.css hides these two outright from 900px down, which was the right call
   while they were sitting in the bar. In here they are the content. */
.hdrkit-menu #uniChip,.hdrkit-menu #tokenChip{display:block;white-space:normal;line-height:1.5}
.hdrkit-menu .tokenchip{text-decoration:none}
.hdrkit-menu .tokenchip:hover{color:var(--txt-2)}
.hdrkit-menu > button{width:100%;text-align:left}
/* floor.css's own .sndbtn rule pads it as a 36px icon square in the bar. In the
   tray it is a full width row like everything else. */
.hdrkit-menu .sndbtn{opacity:1;padding:7px 11px !important;font-size:var(--fs-md)}
/* IT NEEDS A WORD IN HERE. In the bar it was an icon among icons and read fine; in a
   list under "Profile" it is a lone speaker glyph with no label, and the row next to
   it says what it does in a word. audio.js owns the glyph and swaps it on every
   toggle, so the label is added beside it rather than written into it, and the
   button's aria-label already says "Sound" so the accessible name does not double up.
   Placed after, so the icon still leads the row. */
.hdrkit-menu .sndbtn{display:flex;align-items:center;gap:9px}
.hdrkit-menu .sndbtn::after{content:"Sound";font-size:var(--fs-md);color:var(--txt-2)}

/* The two static pages. Styled as rows rather than as links because everything else
   in this tray is a row, and a menu with two things in it that look like body copy
   reads as a footnote rather than as somewhere you can go. 36px tall, which is the
   floor style.css sets for every icon control on a phone. */
.hdrkit-links{display:flex;flex-direction:column;align-items:stretch;gap:9px}
.hdrkit-link{display:flex;align-items:center;min-height:36px;padding:7px 11px;
  border:1px solid var(--line);border-radius:var(--r-3);
  color:var(--txt-2);text-decoration:none;font-size:var(--fs-md);
  transition:border-color var(--dur-1),color var(--dur-1),background var(--dur-1)}
.hdrkit-link:hover{border-color:var(--line-3);color:var(--txt);background:var(--surface)}

/* The bar itself, now that it carries five things instead of nine. */
header .bar{gap:var(--sp-5)}
header .acct{min-width:0}

/* THE MENU ANCHORS TO THE BAR, NOT TO ITS BUTTON, ONCE THE BAR WRAPS.
   style.css lets .bar wrap from 900px down, and a wrapped bar puts the tray button
   wherever the row happens to end. Measured at 375: the button sat at x=102, so
   `right:0` against the button put the menu's left edge at -77px and the first
   third of it was off the side of the screen. Against the bar it opens against the
   same edge at every width. 16px in, which is the bar's own padding at this size,
   so it lines up with everything else on the row above it. */
@media(max-width:900px){
  header .bar{position:relative}
  .hdrkit{position:static}
  .hdrkit-menu{top:calc(100% + 6px);right:16px}
}

/* ══════════════════ THE ROUND SCORE, ON SCREEN ══════════════════
   Ranking moved from raw points netted to a score that stops counting stake past
   CONFIG.roundScoreStakeCap, and nothing rendered it, so every board on the site
   was sorted by a number it did not show. These are the surfaces that say so.

   `rs-` and `rsb-` prefixes, same reason as `grig-` and `podfold-`: app.js and
   games.js share one flat cascade and a generic name has broken a feature twice. */

/* The rule, under the board it governs, never behind a link. */
.rs-rule{border-top:1px solid var(--line);display:flex;align-items:center;
  gap:10px;flex-wrap:wrap}
.rs-rule b{color:var(--txt-2);font-weight:500}
.rs-why{background:transparent;border:1px solid var(--line-2);color:var(--dim);
  padding:3px 10px;border-radius:var(--r-pill);font-size:var(--fs-xs)}
.rs-why:hover:not(:disabled){background:var(--surface-2);color:var(--txt);border-color:var(--line-3)}

/* A row whose round ran past the cap. The colour is the accent rather than a warning
   red: nothing went wrong, the player was simply scored on a smaller round, and the
   stake above the cap still paid out in full. */
.rs-cap{color:var(--acc)}
tr.rs-scaled td{background:rgba(var(--acc-rgb),.045)}

/* ── biggest round today ── */
.rsbcard .sub{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim);margin-left:auto}
.rsb-you{display:flex;align-items:baseline;gap:11px;flex-wrap:wrap;
  padding:12px 16px;border-bottom:1px solid var(--line);background:var(--bg-1)}
.rsb-you .k{font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.09em;color:var(--dim)}
.rsb-you b{font:600 var(--fs-2xl) var(--mono);letter-spacing:-.03em;color:var(--txt)}
.rsb-you em{font-style:normal;font-size:var(--fs-xs);color:var(--dim-2)}
.rsb-you.is-record b{color:var(--acc)}
.rsb-rec{font-family:var(--mono);font-size:var(--fs-mini);color:var(--acc);
  border:1px solid var(--acc-line);background:var(--acc-soft);
  border-radius:var(--r-pill);padding:2px 9px;margin-left:auto}
tr.rsbme{background:var(--acc-soft)}
tr.rsbme td{border-color:var(--acc-line)}

/* The rail's own note, for a player whose live round is being scaled right now. */
.rr-cap{font-family:var(--mono);font-size:var(--fs-mini);color:var(--acc);white-space:nowrap}

@media(max-width:640px){
  .rsb-you{gap:7px}
  .rsb-you b{font-size:var(--fs-xl)}
  .rsb-rec{margin-left:0}
}

/* ══════════════════════════════════════════════════════════════════════════════
   ROCKET, on the game rail.

   Every class starts with `rkx-`, and that is not decoration. `.rkt-*` already exists
   in style.css for the market card this game used to be, and app.js, games.js and this
   file share one CSS namespace: `.rail` lived in two stylesheets and killed the prize
   rail, `paintStrip` lived in two scripts and killed the results strip. A distinctive
   prefix makes that class of bug impossible rather than fixed once.

   The market card was a wide banner in a grid of cards. This is a BOARD, mounted into
   #gboard beside Plinko and Crash, so it claims both axes the way .crx does.
   ══════════════════════════════════════════════════════════════════════════════ */

/* #gboard is a centring flex row, so a child has to claim both axes explicitly or it
   collapses to its content and the chart has nothing to measure itself against. */
.rkx{display:flex;flex-direction:column;flex:1;align-self:stretch;min-width:0;min-height:0}

/* ── recent flights ──────────────────────────────────────────
   Players read patterns into this strip and there are none: each flight is a different
   coin with its own volatility band. It earns its place by being the only thing that
   makes the shape of the game legible before you have paid for a round of it. */
.rkx-histbar{display:flex;align-items:center;gap:10px;padding:2px 2px 9px;min-width:0}
.rkx-histlabel{font-family:var(--mono);font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.11em;color:var(--dim-2);white-space:nowrap}
.rkx-hist{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;flex:1;min-width:0}
.rkx-hist::-webkit-scrollbar{display:none}
.rkx-h{flex:0 0 auto;padding:3px 8px;border-radius:var(--r-6);font-size:var(--fs-xs);font-weight:600;
  font-family:var(--mono);border:1px solid transparent;background:var(--surface-2);color:var(--txt-2)}
.rkx-h.bad{background:rgba(var(--down-rgb),.09);color:var(--down-txt);border-color:rgba(var(--down-rgb),.2)}
.rkx-h.good{background:rgba(var(--up-rgb),.1);color:var(--up);border-color:rgba(var(--up-rgb),.24)}
.rkx-h.empty{background:transparent;color:var(--dim-2);font-weight:400}

/* ── layout ──────────────────────────────────────────────────
   Chart large and central, the flight's own numbers down the right. A narrower side
   column than Crash's, because there is no bettor list here: one coin, one fuse, and
   what it would cost you to stay on it. */
.rkx-body{flex:1;display:grid;grid-template-columns:1fr 216px;gap:12px;min-height:0;min-width:0}
.rkx-stage{position:relative;display:flex;flex-direction:column;align-items:center;
  min-height:340px;min-width:0;padding:14px 14px 68px;border-radius:var(--r-5);
  background:radial-gradient(120% 90% at 50% 110%,rgba(var(--acc-rgb),.05),transparent 62%),
             var(--bg-1);
  border:1px solid var(--line);overflow:hidden}

.rkx-chart{position:relative;width:100%;flex:1;min-height:150px}
.rkx-chart svg{width:100%;height:100%;display:block;overflow:visible}
.rkx-line{filter:drop-shadow(0 0 10px rgba(var(--acc-rgb),.55));transition:stroke var(--dur-2) linear}
.rkx-dot{filter:drop-shadow(0 0 8px rgba(var(--light-rgb),.9))}
.rkx-axis line{stroke:var(--line);stroke-width:1}
.rkx-axis text{fill:var(--dim-2);font-family:var(--mono);font-size:var(--fs-xs)}
.rkx-axis .rkx-xt{fill:var(--dim-2);font-size:var(--fs-mini)}
.rkx-chart svg.dead .rkx-line{stroke:var(--down)}
.rkx-chart svg.dead .rkx-fill{fill:rgba(var(--down-rgb),.16)}
.rkx-chart svg.dead .rkx-dot{fill:var(--down)}

/* ── the number ──────────────────────────────────────────────
   The one thing on the stage competing with the curve, and it IS the payout, which is
   the whole crash-game insight. Streamed at 60fps, so no transition on it: a transition
   would smear a figure that is being written every frame. */
.rkx-mult{font-family:var(--mono);font-weight:700;letter-spacing:-.045em;line-height:1;
  font-size:clamp(40px,7vw,68px);color:var(--acc);transition:none;margin-top:-4px}
.rkx-mult i{font-style:normal;font-size:.42em;opacity:.5;margin-left:2px}
.rkx-mult.dead{color:var(--down);text-shadow:0 0 44px rgba(var(--down-rgb),.4);
  animation:rkx-deadkick .42s var(--ease)}
@keyframes rkx-deadkick{
  0%{transform:scale(1.1)}
  30%{transform:scale(.94) rotate(-1deg)}
  100%{transform:scale(1) rotate(0)}
}

.rkx-state{font-family:var(--mono);font-size:var(--fs-xs);letter-spacing:.09em;
  text-transform:uppercase;color:var(--dim);margin-top:6px}
.rkx-state.live{color:var(--acc)}
.rkx-state.wait{color:var(--dim)}
.rkx-state.bad{color:var(--down);font-weight:700}

/* The sentence that replaced four decimals of telemetry. See the note in rocket.js. */
.rkx-sub{font-family:var(--mono);font-size:var(--fs-sm);color:var(--dim);margin-top:8px;
  text-align:center;padding:0 10px}
.rkx-sub b{color:var(--down)}

/* ── the fuse ────────────────────────────────────────────────
   How close the coin is to taking the round down. It carries the precision so the copy
   above it does not have to, which is the whole division of labour between the two. */
.rkx-fuse{height:6px;width:min(420px,100%);border-radius:var(--r-2);background:var(--line);
  overflow:hidden;margin:12px 0 7px}
.rkx-fuse i{display:block;height:100%;border-radius:var(--r-2);background:var(--up);
  transition:width var(--dur-2) linear,background var(--dur-2) linear}
.rkx-fuse i.warm{background:var(--acc)}
.rkx-fuse i.hot{background:var(--down)}
.rkx-fuseline{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim-2);text-align:center}

/* ── the one button ──────────────────────────────────────────
   Pinned to the bottom of the stage, and it changes job rather than moving. A cash-out
   control that appears somewhere new is a control you have to find while the number is
   still climbing. */
.rkx-cta-wrap{position:absolute;left:0;right:0;bottom:14px;display:flex;justify-content:center}
.rkx-cta{min-width:216px;padding:12px 26px;font-size:var(--fs-base);font-weight:700;
  border-radius:var(--r-4);letter-spacing:-.01em;box-shadow:0 6px 24px rgba(var(--acc-rgb),.22)}
.rkx-cta.out{background:var(--up);color:var(--on-up);box-shadow:0 6px 30px rgba(var(--up-rgb),.35);
  animation:rkx-armed 1s var(--ease-2) infinite}
@keyframes rkx-armed{0%,100%{box-shadow:0 6px 30px rgba(var(--up-rgb),.3)}50%{box-shadow:0 6px 44px rgba(var(--up-rgb),.55)}}
.rkx-cta.out:hover{background:var(--up-hi)}
.rkx-cta:disabled{background:var(--surface-2);color:var(--txt-2);border:1px solid var(--line-2);
  box-shadow:none;animation:none}

/* ── the side column ───────────────────────────────────────── */
.rkx-side{display:flex;flex-direction:column;min-height:0;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-5);overflow:hidden}
.rkx-sidehead{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  padding:10px 12px;border-bottom:1px solid var(--line)}
.rkx-sidehead b{font-size:var(--fs-sm);font-weight:600}
.rkx-sidehead span{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim)}
.rkx-me{display:flex;align-items:baseline;gap:8px;padding:11px 12px;
  border-bottom:1px solid var(--line);font-size:var(--fs-sm)}
.rkx-me:empty{display:none}
.rkx-me .k{font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.09em;color:var(--dim)}
.rkx-me .up{color:var(--up);font-weight:600}
.rkx-me .down{color:var(--down)}
.rkx-worth{color:var(--acc);font-size:var(--fs-lg);font-weight:600;margin-left:auto}
.rkx-note{padding:11px 12px;display:flex;flex-direction:column;gap:9px}
.rkx-note p{margin:0;font-size:var(--fs-mini);color:var(--dim-2);line-height:1.5}

/* left-rail controls */
.rkx-pstat{display:flex;justify-content:space-between;gap:8px;font-family:var(--mono);
  font-size:var(--fs-xs);color:var(--dim);padding:3px 0}
.rkx-pstat b{color:var(--txt-2)}
.rkx-pnote{font-size:var(--fs-mini);color:var(--dim-2);line-height:1.4;padding-top:6px}

/* ── narrow ──────────────────────────────────────────────────
   The chart keeps the room. The side column drops under it and shortens rather than
   disappearing, because "what would I get if I pressed it now" is the only number in
   this game a player is ever actually deciding on. */
@media(max-width:900px){
  .rkx-body{grid-template-columns:1fr;grid-template-rows:minmax(300px,1fr) auto}
  .rkx-side{max-height:190px}
  .rkx-cta{min-width:0;width:calc(100% - 28px)}
}
@media(prefers-reduced-motion:reduce){
  .rkx-cta.out,.rkx-mult.dead{animation:none}
}


/* /arcade.css */
/* ══════════════════════════════════════════════════════════════════════════════
   ARCADE — chrome for the rebuilt Minefield and Over/Under boards.

   Every class here is prefixed `arc-`. app.js, games.js and their two stylesheets
   share one global scope and one cascade, and generic names have silently broken
   this site twice (`paintStrip`, `.rail`). The only unprefixed class used is
   `.g-cta`, deliberately: the floor's panel button fires `#gboard .g-cta`, so that
   one name is the integration contract rather than a collision.
   ══════════════════════════════════════════════════════════════════════════════ */

.arc {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-family: var(--sans);
}
/* The floor caps its coin-game slot at 560px for a card of text. These are boards. */
.coingame > .arc { max-width: 620px; margin: 0 auto; }

/* ── head ───────────────────────────────────────────────────────────────── */

.arc-head { display: flex; align-items: center; gap: 10px; min-height: 30px; }
.arc-title { font-size: var(--fs-base); font-weight: 600; letter-spacing: -.01em; color: var(--txt); }
.arc-chip {
  font: 500 var(--fs-mini) var(--mono);
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--dim); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-3); padding: 3px 7px;
  white-space: nowrap;
}
.arc-chip.warn { color: var(--down); border-color: rgba(var(--down-rgb), .35); }

.arc-flame { display: flex; gap: 5px; }
.arc-st {
  font: 700 var(--fs-mini) var(--mono); font-style: normal;
  padding: 3px 6px; border-radius: var(--r-3); letter-spacing: .04em;
  border: 1px solid transparent;
}
.arc-st.win  { color: var(--up);   background: rgba(var(--up-rgb), .12); border-color: rgba(var(--up-rgb), .3); }
.arc-st.loss { color: var(--down); background: rgba(var(--down-rgb), .1);   border-color: rgba(var(--down-rgb), .26); }
.arc-st.best { color: var(--dim);  background: var(--surface);            border-color: var(--line); }

.arc-mult {
  margin-left: auto;
  font: 700 var(--fs-3xl) var(--mono);
  letter-spacing: -.03em;
  color: var(--dim-2);
  font-variant-numeric: tabular-nums;
  transition: color var(--dur-2) ease;
  /* arcade.js writes `transform` here every frame while a board is live — the
     multiplier climbs and kicks on each safe tile. No CSS transition on it, or the
     transition fights the per-frame value and the kick smears. */
  transform-origin: right center;
  will-change: transform;
}
.arc-mult.live { color: var(--acc); }
.arc-mult.dead { color: var(--down); }

/* ── the risk selector ──────────────────────────────────────────────────── */

.arc-risk { display: flex; gap: 6px; flex-wrap: wrap; }
.arc-chipbtn {
  flex: 1 1 0; min-width: 52px;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 6px 4px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-4);
  cursor: pointer; color: var(--dim);
  transition: border-color var(--dur-1) ease, background var(--dur-1) ease, transform var(--dur-1) ease;
}
.arc-chipbtn b { font: 700 var(--fs-md) var(--mono); color: var(--txt-2); }
.arc-chipbtn i { font: 500 var(--fs-micro) var(--mono); font-style: normal; color: var(--dim-2); }
.arc-chipbtn:hover:not(:disabled) { border-color: var(--acc-line); background: var(--acc-soft); }
.arc-chipbtn:active:not(:disabled) { transform: scale(.96); }
.arc-chipbtn.on { background: var(--acc-soft); border-color: var(--acc-line); }
.arc-chipbtn.on b, .arc-chipbtn.on i { color: var(--acc); }
.arc-chipbtn:disabled { opacity: .42; cursor: default; }

/* ── the stage ──────────────────────────────────────────────────────────── */

.arc-stagewrap {
  position: relative;
  display: flex; justify-content: center;
  width: 100%;
}
.arc-stagewrap canvas { display: block; touch-action: manipulation; }

/* ── the readout and the action ─────────────────────────────────────────── */

.arc-foot { display: flex; align-items: center; gap: 12px; min-height: 46px; }
.arc-read { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.arc-k {
  font: 600 var(--fs-micro) var(--mono); text-transform: uppercase; letter-spacing: .1em;
  color: var(--dim-2);
}
.arc-k.up { color: var(--up); } .arc-k.down { color: var(--down); }
.arc-next { font: 700 var(--fs-lg) var(--mono); color: var(--txt); letter-spacing: -.02em; }
.arc-sub {
  font: 500 var(--fs-xs) var(--mono); color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.arc-actions { margin-left: auto; display: flex; }

/* The collect button. It grows with the pot on purpose — this is the control a player
   is reaching for under stress, and `--arc-grow` is set from the live multiplier. */
.arc-collect {
  --arc-grow: 1;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 11px 20px; border-radius: var(--r-5); cursor: pointer;
  border: 1px solid rgba(var(--up-rgb), .45);
  background: linear-gradient(180deg, rgba(var(--up-rgb), .22), rgba(var(--up-rgb), .08));
  color: var(--up);
  transform: scale(var(--arc-grow));
  transform-origin: right center;
  transition: transform var(--dur-3) cubic-bezier(.2, .9, .3, 1.3), box-shadow var(--dur-3) ease, border-color var(--dur-3) ease;
}
.arc-collect b { font: 700 var(--fs-md) var(--mono); letter-spacing: -.01em; }
.arc-collect i { font: 600 var(--fs-mini) var(--mono); font-style: normal; opacity: .75; }
.arc-collect:hover:not(:disabled) { box-shadow: 0 0 0 3px rgba(var(--up-rgb), .12); }
.arc-collect:active:not(:disabled) { transform: scale(calc(var(--arc-grow) * .96)); }
.arc-collect.hot { border-color: rgba(var(--up-rgb), .7); box-shadow: 0 0 18px rgba(var(--up-rgb), .18); }
.arc-collect.blazing {
  border-color: var(--up);
  box-shadow: 0 0 26px rgba(var(--up-rgb), .32);
  animation: arcPulse 1.15s ease-in-out infinite;
}
.arc-collect:disabled, .arc-collect.idle {
  opacity: .45; cursor: default; animation: none; box-shadow: none;
}
@keyframes arcPulse {
  0%, 100% { box-shadow: 0 0 22px rgba(var(--up-rgb), .26); }
  50%      { box-shadow: 0 0 34px rgba(var(--up-rgb), .48); }
}

.arc .g-cta { margin-left: 0; }
.arc-start, .arc-draw { white-space: nowrap; }

.arc-note {
  font: 500 var(--fs-mini) var(--mono); color: var(--dim-2);
  letter-spacing: .02em; line-height: 1.5;
}

/* ── over/under controls ────────────────────────────────────────────────── */

.arc-ctl { display: flex; flex-direction: column; gap: 10px; }
.arc-dirs { display: flex; gap: 8px; }
.arc-dir {
  flex: 1; padding: 11px; border-radius: var(--r-4);
  font: 700 var(--fs-sm) var(--sans); letter-spacing: .04em;
  background: var(--surface); border: 1px solid var(--line); color: var(--dim);
  cursor: pointer;
  transition: background var(--dur-1) ease, border-color var(--dur-1) ease, color var(--dur-1) ease, transform var(--dur-1) ease;
}
.arc-dir:hover { border-color: var(--acc-line); color: var(--txt-2); }
.arc-dir:active { transform: scale(.97); }
.arc-dir.on { background: var(--acc-soft); border-color: var(--acc-line); color: var(--acc); }

.arc-slider { width: 100%; accent-color: var(--acc); }

/* ── narrow ─────────────────────────────────────────────────────────────── */

@media (max-width: 620px) {
  .arc-mult { font-size: var(--fs-2xl); }
  .arc-chipbtn { min-width: 44px; padding: 5px 3px; }
  .arc-foot { gap: 8px; }
  .arc-collect { padding: 10px 14px; }
  .arc-note { display: none; }
}

/* A player who has asked the operating system to stop moving things gets a board that
   still works: the reveals land instantly instead of animating. The canvas honours
   this too — arcade.js reads the same query before it shakes anything. */
@media (prefers-reduced-motion: reduce) {
  .arc-collect { transition: none; animation: none !important; }
  .arc-chipbtn, .arc-dir { transition: none; }
}

/* /crash.css */
/* ══════════════════════════════════════════════════════════════════════════════
   CRASH.

   Every class here starts with `crx-`. app.js, games.js and this file share one CSS
   namespace, and a collision has silently broken a feature twice (.rail existed in two
   stylesheets and killed the prize rail; paintStrip existed in two scripts and killed
   the results strip). A distinctive prefix makes that class of bug impossible rather
   than fixed-once.

   Design tokens come from style.css. Green/red stay reserved for direction, which here
   means "still climbing" and "dead" — the one place in this app where that reading is
   literal rather than decorative.
   ══════════════════════════════════════════════════════════════════════════════ */

/* #gboard is a centring flex row, so a child has to claim both axes explicitly or it
   collapses to its content and the canvas has nothing to measure itself against. */
.crx{display:flex;flex-direction:column;flex:1;align-self:stretch;min-width:0;min-height:0;gap:0}

/* ── recent busts ───────────────────────────────────────────
   Players read patterns into this strip. There are none — every round is independent
   — but the strip is also the only place the fairness claim is ever actually tested,
   so every chip on it opens its own receipt. */
.crx-histbar{display:flex;align-items:center;gap:10px;padding:2px 2px 9px;min-width:0}
.crx-histlabel{font-family:var(--mono);font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.11em;color:var(--dim-2);white-space:nowrap}
.crx-hist{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;flex:1;min-width:0}
.crx-hist::-webkit-scrollbar{display:none}
.crx-chip{flex:0 0 auto;padding:3px 8px;border-radius:var(--r-6);font-size:var(--fs-xs);font-weight:600;
  border:1px solid transparent;background:var(--surface-2);color:var(--txt-2);cursor:pointer;
  transition:transform var(--dur-1) var(--ease),border-color var(--dur-1)}
.crx-chip:hover{transform:translateY(-1px)}
.crx-chip.bad{background:rgba(var(--down-rgb),.09);color:var(--down-txt);border-color:rgba(var(--down-rgb),.2)}
.crx-chip.good{background:rgba(var(--up-rgb),.1);color:var(--up);border-color:rgba(var(--up-rgb),.24)}
.crx-chip.gold{background:rgba(var(--acc-rgb),.14);color:var(--acc);border-color:var(--acc-line);
  box-shadow:0 0 0 1px rgba(var(--acc-rgb),.1)}

/* ── layout ─────────────────────────────────────────────────
   Chart large and central, the room down the right. The live bet list is not a side
   panel in the decorative sense: it is the single biggest reason a shared-round crash
   game holds people, so it gets a fixed column rather than a drawer. */
.crx-body{flex:1;display:grid;grid-template-columns:1fr 232px;gap:12px;min-height:0;min-width:0}
.crx-stage{position:relative;min-height:320px;min-width:0;border-radius:var(--r-5);
  background:radial-gradient(120% 90% at 50% 110%,rgba(var(--acc-rgb),.05),transparent 62%),
             var(--bg-1);
  border:1px solid var(--line);overflow:hidden}
/* Absolute, so the canvas's own pixel size can never feed back into the box it is
   measuring itself against. */
.crx-canvas{position:absolute;left:0;top:0;display:block}

/* ── the number ─────────────────────────────────────────────
   Dead centre, and it is the only thing on the stage competing with the curve. Size
   and colour are driven from JS by the multiplier itself — a 40x is physically bigger
   than a 1.4x, which is a signal that needs no reading. */
.crx-hud{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;pointer-events:none;gap:6px}
.crx-mult{font-family:var(--mono);font-weight:700;letter-spacing:-.045em;line-height:1;
  font-size:46px;color:var(--up);transition:color var(--dur-2) linear}
.crx-mult.crx-dead{animation:crx-deadkick .42s var(--ease)}
@keyframes crx-deadkick{
  0%{transform:scale(1.12)}
  30%{transform:scale(.94) rotate(-1.2deg)}
  100%{transform:scale(1) rotate(0)}
}
.crx-state{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);
  display:flex;align-items:center;gap:7px}
.crx-state b{color:var(--txt-2);font-weight:600}
.crx-pip{width:6px;height:6px;border-radius:50%;background:var(--dim-2);display:inline-block}
.crx-pip.live{background:var(--up);box-shadow:0 0 0 3px rgba(var(--up-rgb),.16);
  animation:crx-pulse 1.1s var(--ease-2) infinite}
@keyframes crx-pulse{0%,100%{box-shadow:0 0 0 3px rgba(var(--up-rgb),.16)}50%{box-shadow:0 0 0 7px transparent}}
.crx-bustword{color:var(--down);font-weight:700;letter-spacing:.1em}

/* ── the verdict ────────────────────────────────────────────
   The near-miss line. It always names the counterfactual — what the round did after
   you left, or how far short you fell — because the effect that keeps a crash player
   in their seat is "I almost had it", and that is only felt when the almost is stated.
   Toned four ways so the same layout can read as triumph or as a knife. */
.crx-verdict{position:absolute;left:0;right:0;bottom:64px;display:flex;flex-direction:column;
  align-items:center;gap:2px;opacity:0;transform:translateY(8px);pointer-events:none;
  transition:opacity var(--dur-3) var(--ease),transform var(--dur-3) var(--ease)}
.crx-verdict.show{opacity:1;transform:translateY(0)}
.crx-verdict b{font-size:var(--fs-xl);font-weight:700;letter-spacing:-.03em}
.crx-verdict span{font-size:var(--fs-sm);color:var(--txt-2);text-align:center;padding:0 14px}
.crx-verdict.win b{color:var(--up)}
.crx-verdict.clutch b{color:var(--up)}
.crx-verdict.clutch span{color:var(--up)}
.crx-verdict.ouch b{color:var(--up)}
.crx-verdict.ouch span{color:var(--acc)}
.crx-verdict.bust b{color:var(--down)}
.crx-verdict.sonear b{color:var(--down)}
.crx-verdict.sonear span{color:var(--acc);font-weight:600}
.crx-verdict.sonear{animation:crx-nudge .5s var(--ease)}
@keyframes crx-nudge{0%,100%{transform:translateX(0)}25%{transform:translateX(-5px)}75%{transform:translateX(5px)}}

/* ── the one button ─────────────────────────────────────────
   Big, centred under the chart, and it changes job rather than moving. A cash-out
   button that appears somewhere new is a button you have to find while the number is
   still climbing. */
.crx-cta-wrap{position:absolute;left:0;right:0;bottom:14px;display:flex;justify-content:center}
.crx-cta{min-width:216px;padding:12px 26px;font-size:var(--fs-base);font-weight:700;border-radius:var(--r-4);
  letter-spacing:-.01em;box-shadow:0 6px 24px rgba(var(--acc-rgb),.22)}
.crx-cta.cash{background:var(--up);color:var(--on-up);box-shadow:0 6px 30px rgba(var(--up-rgb),.35);
  animation:crx-armed 1s var(--ease-2) infinite}
@keyframes crx-armed{0%,100%{box-shadow:0 6px 30px rgba(var(--up-rgb),.3)}50%{box-shadow:0 6px 44px rgba(var(--up-rgb),.55)}}
.crx-cta.cash:hover{background:var(--up-hi)}
.crx-cta.waiting{background:var(--surface-2);color:var(--txt-2);border:1px solid var(--line-2);
  box-shadow:none}

/* ── the room ───────────────────────────────────────────────
   Cashed-out rows float to the top. A column of green multipliers climbing away from
   the people still holding is the social proof this whole game runs on. House accounts
   sit in the same list untagged now, and their bets are real plays against the real
   bust point. rules.html §9 says what they are. */
.crx-side{display:flex;flex-direction:column;min-height:0;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-5);overflow:hidden}
.crx-sidehead{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  padding:10px 12px;border-bottom:1px solid var(--line)}
.crx-sidehead b{font-size:var(--fs-sm);font-weight:600}
.crx-sidehead span{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim)}
.crx-list{flex:1;overflow-y:auto;scrollbar-width:thin;padding:4px}
.crx-list::-webkit-scrollbar{width:5px}
.crx-list::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:var(--r-2)}
.crx-row{display:grid;grid-template-columns:1fr auto 44px 56px;gap:6px;align-items:center;
  padding:5px 7px;border-radius:var(--r-3);font-size:var(--fs-xs);color:var(--txt-2);
  transition:background var(--dur-2),color var(--dur-2)}
.crx-row + .crx-row{margin-top:1px}
.crx-row.out{background:rgba(var(--up-rgb),.09);color:var(--up)}
.crx-row.out.fresh{animation:crx-landed .5s var(--ease)}
@keyframes crx-landed{0%{background:rgba(var(--up-rgb),.4);transform:translateX(-6px)}100%{background:rgba(var(--up-rgb),.09);transform:none}}
.crx-row.dead{color:var(--dim-2)}
.crx-row.me{outline:1px solid var(--acc-line);background:var(--acc-soft)}
.crx-row.me.out{outline-color:rgba(var(--up-rgb),.4)}
.crx-who{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.crx-stake{color:var(--dim);font-size:var(--fs-mini)}
.crx-at{text-align:right;font-size:var(--fs-mini);color:var(--dim)}
.crx-row.out .crx-at{color:var(--up);font-weight:600}
.crx-won{text-align:right;font-size:var(--fs-mini);font-weight:600}
.crx-row.dead .crx-won{color:var(--down);opacity:.7}
.crx-empty{padding:22px 12px;text-align:center;font-size:var(--fs-xs);color:var(--dim-2)}

/* the commitment, always visible */
.crx-proof{border-top:1px solid var(--line);padding:9px 12px;display:flex;flex-direction:column;gap:3px}
.crx-proof b{font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.09em;color:var(--dim-2);font-weight:500}
.crx-proof code{font-family:var(--mono);font-size:var(--fs-micro);color:var(--txt-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.crx-proof span{font-size:var(--fs-mini);color:var(--dim-2);line-height:1.4}

/* ── left-rail controls ───────────────────────────────────── */
.crx-autorow{display:flex;gap:6px}
.crx-autoin{flex:1;min-width:0;background:var(--bg-1);border:1px solid var(--line-2);
  border-radius:var(--r-3);padding:9px 11px;color:var(--txt);font-size:var(--fs-md);font-family:var(--mono)}
.crx-autoin:focus{outline:none;border-color:var(--acc-line)}
.crx-autotog{flex:0 0 62px;background:transparent;color:var(--dim);border:1px solid var(--line-2);
  font-family:var(--mono);font-size:var(--fs-xs);letter-spacing:.06em}
.crx-autotog:hover{background:var(--surface-2);color:var(--txt)}
.crx-autotog.on{background:var(--acc-soft);color:var(--acc);border-color:var(--acc-line)}
.crx-quick{display:flex;gap:4px;flex-wrap:wrap}
.crx-quick button{flex:1 1 44px;padding:6px 0;background:transparent;color:var(--dim);
  border:1px solid var(--line);font-family:var(--mono);font-size:var(--fs-xs)}
.crx-quick button:hover{color:var(--txt);border-color:var(--line-3);background:var(--surface-2)}
.crx-quick button.on{background:var(--acc-soft);color:var(--acc);border-color:var(--acc-line)}
.crx-odds{display:flex;justify-content:space-between;gap:8px;font-size:var(--fs-mini);color:var(--dim);
  font-family:var(--mono);padding-top:2px}
.crx-odds b{color:var(--txt-2)}
.crx-panelnote{font-size:var(--fs-mini);color:var(--dim-2);line-height:1.4}

/* ── the receipt modal ──────────────────────────────────────
   Reachable from any chip in the history strip. The seeds are the whole product here,
   so they are shown whole and selectable rather than truncated. */
.crx-proofbody p{margin:0 0 14px;font-size:var(--fs-sm);color:var(--txt-2);line-height:1.55}
.crx-proofbody label{display:block;font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.09em;
  color:var(--dim);margin:10px 0 4px}
.crx-proofbody code{display:block;font-family:var(--mono);font-size:var(--fs-mini);color:var(--txt-2);
  background:var(--bg-1);border:1px solid var(--line);border-radius:var(--r-3);padding:8px 10px;
  word-break:break-all;user-select:all}
.crx-proofok{margin-top:14px;padding:9px 11px;border-radius:var(--r-3);font-family:var(--mono);font-size:var(--fs-xs)}
.crx-proofok.yes{background:rgba(var(--up-rgb),.1);color:var(--up);border:1px solid rgba(var(--up-rgb),.24)}
.crx-proofok.no{background:var(--surface-2);color:var(--dim);border:1px solid var(--line-2)}

/* ── narrow ─────────────────────────────────────────────────
   The chart keeps the room; the bet list drops under it and shortens rather than
   disappearing. A crash game with nobody visible in it is the one thing worse than a
   small chart. */
@media (max-width:900px){
  .crx-body{grid-template-columns:1fr;grid-template-rows:minmax(280px,1fr) auto}
  .crx-side{max-height:210px}
  .crx-mult{font-size:38px}
  .crx-cta{min-width:0;width:calc(100% - 28px)}
}
@media (prefers-reduced-motion:reduce){
  .crx-cta.cash,.crx-pip.live,.crx-row.out.fresh,.crx-mult.crx-dead,.crx-verdict.sonear{animation:none}
}

/* /jackpot.css */
/* ══════════════════════════════════════════════════════════════════════════════
   JACKPOT.

   Every class here starts with `jpt-`. app.js, games.js, crash.js and this file share
   one CSS namespace, and a collision has silently broken a feature twice (.rail existed
   in two stylesheets and killed the prize rail; paintStrip existed in two scripts and
   killed the results strip). A distinctive prefix makes that class of bug impossible
   rather than fixed-once.

   Design tokens come from style.css. GREEN AND RED STILL MEAN DIRECTION AND NOTHING
   ELSE, which is why the wedge palette in jackpot.js is ten curated hues rather than
   hash-to-HSL: an unconstrained hash lands on the exact --up green and calls a bot's
   slice "good". Red appears in exactly one place here, on the clock, where it means the
   thing it means everywhere else: this is about to be over.
   ══════════════════════════════════════════════════════════════════════════════ */

/* #gboard is a centring flex row, so a child has to claim both axes explicitly or it
   collapses to its content and the canvas has nothing to measure itself against. */
.jpt{display:flex;flex-direction:column;flex:1;align-self:stretch;min-width:0;min-height:0;gap:0}

/* ── recent pots ────────────────────────────────────────────
   The chip carries the pot SIZE and is gold when the pot went to a small slice. Which
   number to print was a real choice: pot size is what people brag about, but "taken
   from 6%" is the shot everybody is actually here for, and a strip of totals alone
   hides it completely. Every chip opens its own full receipt. */
.jpt-histbar{display:flex;align-items:center;gap:10px;padding:2px 2px 9px;min-width:0}
.jpt-histlabel{font-family:var(--mono);font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.11em;color:var(--dim-2);white-space:nowrap}
.jpt-hist{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;flex:1;min-width:0}
.jpt-hist::-webkit-scrollbar{display:none}
.jpt-chip{flex:0 0 auto;padding:3px 8px;border-radius:var(--r-6);font-size:var(--fs-xs);font-weight:600;
  border:1px solid transparent;background:var(--surface-2);color:var(--txt-2);cursor:pointer;
  transition:transform var(--dur-1) var(--ease),border-color var(--dur-1)}
.jpt-chip:hover{transform:translateY(-1px)}
.jpt-chip.good{background:rgba(var(--info-rgb),.1);color:var(--info);border-color:rgba(var(--info-rgb),.24)}
.jpt-chip.gold{background:rgba(var(--acc-rgb),.14);color:var(--acc);border-color:var(--acc-line);
  box-shadow:0 0 0 1px rgba(var(--acc-rgb),.1)}

/* ── layout ─────────────────────────────────────────────────
   Ring large and central, the room down the right. The entrant list is not decoration
   here the way a live feed is elsewhere: in this game the other names ARE the odds, so
   it gets a fixed column and a share percentage on every row. */
.jpt-body{flex:1;display:grid;grid-template-columns:1fr 232px;gap:12px;min-height:0;min-width:0}
.jpt-stage{position:relative;min-height:340px;min-width:0;border-radius:var(--r-5);
  background:radial-gradient(120% 90% at 50% 50%,rgba(var(--acc-rgb),.05),transparent 62%),
             var(--bg-1);
  border:1px solid var(--line);overflow:hidden}
/* Absolute, so the canvas's own pixel size can never feed back into the box it is
   measuring itself against. */
.jpt-canvas{position:absolute;left:0;top:0;display:block}

/* ── the pot ────────────────────────────────────────────────
   Dead centre, inside the ring. Size is driven from JS by the pot itself, so a big one
   is physically bigger. That is the cheapest possible "this one is different" and it
   needs no reading. */
/* The bottom 96px of the stage belong to the verdict and the button, so the HUD is
   centred in what is LEFT rather than in the whole box — the same reserved band the
   canvas sizes the ring against (BAND in jackpot.js). Centring both in the full height
   put the result line inside the ring's lower arc on a short stage. */
.jpt-hud{position:absolute;inset:0 0 96px 0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;pointer-events:none;gap:3px}
.jpt-potlabel{font-family:var(--mono);font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.16em;color:var(--dim-2)}
.jpt-pot{font-family:var(--mono);font-weight:700;letter-spacing:-.045em;line-height:1;
  font-size:38px;color:var(--txt);transition:color var(--dur-2) linear}
.jpt-pot.jpt-hot{color:var(--acc);text-shadow:0 0 34px rgba(var(--acc-rgb),.45);
  animation:jpt-throb .9s var(--ease-2) infinite}
@keyframes jpt-throb{0%,100%{transform:scale(1)}50%{transform:scale(1.035)}}
.jpt-state{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);
  display:flex;align-items:center;gap:7px;margin-top:4px;text-align:center;padding:0 12px}
.jpt-state b{color:var(--txt-2);font-weight:600}
.jpt-pip{width:6px;height:6px;border-radius:50%;background:var(--dim-2);display:inline-block;flex:0 0 auto}
.jpt-pip.live{background:var(--acc);box-shadow:0 0 0 3px rgba(var(--acc-rgb),.16);
  animation:jpt-pulse 1.1s var(--ease-2) infinite}
@keyframes jpt-pulse{0%,100%{box-shadow:0 0 0 3px rgba(var(--acc-rgb),.16)}50%{box-shadow:0 0 0 7px transparent}}
.jpt-wonword{color:var(--acc);font-weight:700;letter-spacing:.02em}

/* ── the verdict ────────────────────────────────────────────
   It always names the counterfactual, because the share you bought is the only part of
   a pot game you controlled. "You lost" says nothing; "you were the biggest share at
   31% and it went to the 24%" says exactly what the next decision is. */
.jpt-verdict{position:absolute;left:50%;bottom:58px;transform:translate(-50%,8px);
  display:flex;flex-direction:column;align-items:center;gap:2px;opacity:0;pointer-events:none;
  max-width:min(94%,460px);
  /* A scrim, because this lands over the bottom of the ring and the ring is nine
     saturated colours. Text on top of a wheel with no ground under it is unreadable on
     exactly the round that had the most people in it. */
  background:rgba(var(--bg-rgb),.9);border:1px solid var(--line);border-radius:var(--r-5);
  padding:7px 16px 9px;
  transition:opacity var(--dur-3) var(--ease),transform var(--dur-3) var(--ease)}
.jpt-verdict:empty{display:none}
.jpt-verdict.show{opacity:1;transform:translate(-50%,0)}
.jpt-verdict b{font-size:var(--fs-xl);font-weight:700;letter-spacing:-.03em;color:var(--txt)}
.jpt-verdict span{font-size:var(--fs-sm);color:var(--txt-2);text-align:center;padding:0 18px}
.jpt-verdict.win b{color:var(--up)}
.jpt-verdict.lost b{color:var(--down)}
.jpt-verdict.sonear b{color:var(--down)}
.jpt-verdict.sonear span{color:var(--warn);font-weight:600}
.jpt-verdict.flat b{color:var(--txt-2)}
.jpt-verdict.sonear{animation:jpt-nudge .5s var(--ease)}
/* Keeps the -50% centring: an animation that only set translateX would drop the
   translate(-50%) the positioning depends on and throw the box half a width right. */
@keyframes jpt-nudge{
  0%,100%{transform:translate(-50%,0)}
  25%{transform:translate(calc(-50% - 5px),0)}
  75%{transform:translate(calc(-50% + 5px),0)}
}

/* ── the one button ─────────────────────────────────────────
   Same job as Crash's: big, centred, and it changes what it says rather than moving.
   A control that appears somewhere new is a control you have to find while a clock is
   running. */
.jpt-cta-wrap{position:absolute;left:0;right:0;bottom:14px;display:flex;justify-content:center}
.jpt-cta{min-width:216px;padding:12px 26px;font-size:var(--fs-base);font-weight:700;border-radius:var(--r-4);
  letter-spacing:-.01em;box-shadow:0 6px 24px rgba(var(--acc-rgb),.22)}
/* Already in, and topping up is a different act from joining: it is an answer to
   something somebody else did. Outlined rather than filled, so the filled state stays
   reserved for the decision that costs you nothing to skip. */
.jpt-cta.add{background:transparent;color:var(--acc);border:1px solid var(--acc-line);box-shadow:none}
.jpt-cta.add:hover{background:var(--acc-soft)}
.jpt-cta.shut{background:var(--surface-2);color:var(--txt-2);border:1px solid var(--line-2);
  box-shadow:none}
/* Armed for the next pot. Deliberately quiet: nothing has been spent yet, and a filled
   button here would read as money already down. */
.jpt-cta.waiting{background:var(--surface-2);color:var(--acc);border:1px solid var(--acc-line);
  box-shadow:none}

/* ── the room ───────────────────────────────────────────────
   Ordered by share, not by arrival: the list answers "who am I playing against", and
   that is a ranking. The ring already carries arrival order. Every row has a colour dot
   tying it to its wedge, which is what lets a player find themselves without reading. */
.jpt-side{display:flex;flex-direction:column;min-height:0;gap:8px}
.jpt-sidehead{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  font-size:var(--fs-xs);color:var(--dim);padding:0 2px}
.jpt-sidehead b{color:var(--txt-2);font-size:var(--fs-sm)}
.jpt-sidehead .num{font-family:var(--mono);color:var(--acc);font-weight:600}
.jpt-list{flex:1;min-height:0;overflow-y:auto;scrollbar-width:thin;display:flex;
  flex-direction:column;gap:3px;padding-right:2px}
.jpt-row{display:grid;grid-template-columns:9px 1fr auto auto;align-items:center;gap:7px;
  padding:6px 8px;border-radius:var(--r-6);background:var(--surface);
  font-size:var(--fs-xs);border:1px solid transparent;
  transition:background var(--dur-1),border-color var(--dur-1)}
.jpt-dot{width:9px;height:9px;border-radius:50%;display:block}
.jpt-who{color:var(--txt-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* A .jpt-bot tag used to sit here. Names in this product are never tagged; house
   accounts are disclosed in rules.html §9 instead. */
.jpt-stake{font-family:var(--mono);color:var(--dim);font-size:var(--fs-micro)}
.jpt-share{font-family:var(--mono);color:var(--txt);font-weight:600;min-width:44px;text-align:right}
.jpt-row.me{border-color:var(--acc-line);background:rgba(var(--acc-rgb),.06)}
.jpt-row.me .jpt-who{color:var(--acc)}
.jpt-row.won{background:rgba(var(--acc-rgb),.13);border-color:var(--acc-line)}
.jpt-row.won .jpt-share{color:var(--acc)}
.jpt-empty{padding:16px 10px;text-align:center;color:var(--dim-2);font-size:var(--fs-xs)}

/* ── the receipt ────────────────────────────────────────────
   Always on screen. The commitment before, the seed and the winning ticket after. */
.jpt-proof{border-top:1px solid var(--line);padding-top:8px;display:flex;flex-direction:column;gap:3px;
  font-size:var(--fs-micro);color:var(--dim-2)}
.jpt-proof b{color:var(--dim);font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.1em}
.jpt-proof code{font-family:var(--mono);color:var(--txt-2);font-size:var(--fs-micro);
  word-break:break-all;line-height:1.4}

/* ── the panel, in games.js's left rail ─────────────────────
   Two numbers other pot sites hide, both one division: what your slice is, and what it
   would cost to be the biggest one in the room. The second is the whole reason to press
   the button twice and nobody works it out in their head while a clock is running. */
.jpt-meter{height:8px;border-radius:var(--r-6);background:var(--surface-2);overflow:hidden;
  border:1px solid var(--line)}
.jpt-meter i{display:block;height:100%;background:var(--acc);border-radius:var(--r-6);
  transition:width var(--dur-3) var(--ease)}
.jpt-odds{display:flex;flex-direction:column;gap:2px;margin-top:7px;
  font-size:var(--fs-xs);color:var(--dim)}
.jpt-odds b{color:var(--txt-2);font-weight:600}
.jpt-odds .num{font-family:var(--mono);color:var(--acc)}
.jpt-lead{width:100%;padding:9px 10px;font-size:var(--fs-md);font-weight:600;border-radius:var(--r-4);
  background:transparent;color:var(--acc);border:1px solid var(--acc-line)}
.jpt-lead:hover:not(:disabled){background:var(--acc-soft)}
.jpt-lead:disabled{color:var(--dim-2);border-color:var(--line-2);cursor:default}
.jpt-panelnote{margin-top:7px;font-size:var(--fs-micro);color:var(--dim-2);font-family:var(--mono)}

/* ── the proof modal ────────────────────────────────────────
   The ticket table is the part worth having. A hash people cannot check is a claim; a
   list of ranges with one number falling inside one of them is a proof anyone can read. */
.jpt-proofbody p{font-size:var(--fs-sm);color:var(--txt-2);line-height:1.55;margin:0 0 10px}
.jpt-proofbody label{display:block;font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.1em;color:var(--dim-2);margin:10px 0 3px}
.jpt-proofbody code{display:block;font-family:var(--mono);font-size:var(--fs-mini);color:var(--txt-2);
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-6);
  padding:6px 8px;word-break:break-all}
.jpt-proofbody p code{display:inline;padding:1px 4px;border:0;background:var(--surface-2)}
.jpt-tickets{width:100%;border-collapse:collapse;font-size:var(--fs-xs);margin:4px 0 2px}
.jpt-tickets th{text-align:left;font-weight:500;color:var(--dim-2);font-size:var(--fs-micro);
  text-transform:uppercase;letter-spacing:.09em;padding:4px 6px;border-bottom:1px solid var(--line)}
.jpt-tickets td{padding:5px 6px;color:var(--txt-2);border-bottom:1px solid var(--line)}
.jpt-tickets td.num{font-family:var(--mono);text-align:right}
.jpt-tickets tr.win td{color:var(--acc);font-weight:600}
.jpt-proofok{margin-top:12px;padding:8px 10px;border-radius:var(--r-6);font-size:var(--fs-sm)}
.jpt-proofok.yes{background:rgba(var(--up-rgb),.1);color:var(--up);border:1px solid rgba(var(--up-rgb),.24)}
.jpt-proofok.no{background:var(--surface-2);color:var(--dim);border:1px solid var(--line-2)}

/* ── narrow ─────────────────────────────────────────────────
   The room drops under the ring rather than off the screen. It is the odds; hiding it
   on a phone would leave a wheel with no explanation of what it is dividing. */
@media (max-width:860px){
  .jpt-body{grid-template-columns:1fr;grid-template-rows:1fr auto}
  .jpt-stage{min-height:300px}
  .jpt-list{max-height:180px}
  .jpt-pot{font-size:32px}
}

@media (prefers-reduced-motion:reduce){
  .jpt-pot.jpt-hot,.jpt-verdict.sonear{animation:none}
  .jpt-pip.live{animation:none}
}

/* /bullbear.css */
/* ══════════════════════════════════════════════════════════════════════════════
   BULLS OR BEARS.

   Every class here starts with `bvb-`. app.js, games.js, crash.js, jackpot.js and this
   file share one CSS namespace, and a collision has silently broken a feature twice
   (.rail existed in two stylesheets and killed the prize rail; paintStrip existed in
   two scripts and killed the results strip). A distinctive prefix makes that class of
   bug impossible rather than fixed once.

   Design tokens come from style.css. GREEN AND RED MEAN DIRECTION, and this is the one
   board on the site where that is not a borrowed convention but the game itself: bulls
   are up and bears are down. Nothing else here is allowed to use either colour, which
   is why the winner is shown by DIMMING the loser rather than by tinting anything.
   ══════════════════════════════════════════════════════════════════════════════ */

/* #gboard is a centring flex row, so a child has to claim both axes explicitly or it
   collapses to its content and the canvas has nothing to measure itself against. */
.bvb{display:flex;flex-direction:column;flex:1;align-self:stretch;min-width:0;min-height:0;gap:0}

/* ── recent rounds ──────────────────────────────────────────
   The chip carries the MOVE, not the pot. Pot size says how busy the room was; the
   move says what actually happened, and it is the only column a player can check
   against a chart. Every chip opens its own full receipt. */
.bvb-histbar{display:flex;align-items:center;gap:10px;padding:2px 2px 9px;min-width:0}
.bvb-histlabel{font-family:var(--mono);font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.11em;color:var(--dim-2);white-space:nowrap}
.bvb-hist{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;flex:1;min-width:0}
.bvb-hist::-webkit-scrollbar{display:none}
.bvb-chip{flex:0 0 auto;padding:3px 8px;border-radius:var(--r-6);font-size:var(--fs-xs);font-weight:600;
  border:1px solid transparent;background:var(--surface-2);color:var(--txt-2);cursor:pointer;
  transition:transform var(--dur-1) var(--ease),border-color var(--dur-1)}
.bvb-chip:hover{transform:translateY(-1px)}
.bvb-chip.up{background:rgba(var(--up-rgb),.12);color:var(--up);border-color:rgba(var(--up-rgb),.26)}
.bvb-chip.down{background:rgba(var(--down-rgb),.12);color:var(--down);border-color:rgba(var(--down-rgb),.26)}
.bvb-chip.void{color:var(--dim-2)}

/* ── layout ─────────────────────────────────────────────────
   Chart large and central, the two teams down the right. The roster is not decoration
   here the way a live feed is elsewhere: every handle in a column is money that moved
   the price on the button next to it. So it gets a fixed column and both sides get
   equal room, because a layout that gives the popular side more space would be the
   interface taking a view. */
.bvb-body{flex:1;display:grid;grid-template-columns:1fr 236px;gap:12px;min-height:0;min-width:0}
.bvb-stage{position:relative;min-height:360px;min-width:0;border-radius:var(--r-5);
  background:var(--bg-1);border:1px solid var(--line);overflow:hidden}
/* Absolute, so the canvas's own pixel size can never feed back into the box it is
   measuring itself against. */
.bvb-canvas{position:absolute;left:0;top:0;display:block}

/* ── the coin and the move ──────────────────────────────────
   Top left rather than dead centre: the chart is the hero here and a number floating
   over its middle would sit on top of the line at the moment the line matters most. */
.bvb-hud{position:absolute;left:14px;top:10px;right:14px;display:flex;flex-direction:column;
  align-items:flex-start;gap:1px;pointer-events:none}
.bvb-coin{font-family:var(--mono);font-size:var(--fs-xs);text-transform:uppercase;
  letter-spacing:.14em;color:var(--dim)}
.bvb-move{font-family:var(--mono);font-weight:700;letter-spacing:-.04em;line-height:1.05;
  font-size:34px;color:var(--txt-2);transition:color var(--dur-2) linear}
.bvb-move.up{color:var(--up)}
.bvb-move.down{color:var(--down)}
.bvb-state{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);
  display:flex;align-items:center;gap:7px;margin-top:2px}
.bvb-state b{color:var(--txt-2);font-weight:600}
/* The live pip pulses with a scaling pseudo element rather than a growing box-shadow.
   A shadow radius is a repaint on every frame of something that never stops, and the
   floor went from 25.3ms to 3.9ms a frame by deleting exactly that class of effect.
   Transform and opacity only, which the compositor does for free. */
.bvb-pip{position:relative;width:6px;height:6px;border-radius:50%;background:var(--dim-2);
  display:inline-block;flex:0 0 auto}
.bvb-pip.live{background:var(--acc)}
.bvb-pip.live::after{content:'';position:absolute;inset:-3px;border-radius:50%;
  background:rgba(var(--acc-rgb),.3);animation:bvb-pulse 1.15s var(--ease-2) infinite}
@keyframes bvb-pulse{0%{transform:scale(.7);opacity:.85}100%{transform:scale(2.1);opacity:0}}

/* ── the two sides ──────────────────────────────────────────
   The multiple lives ON the button, because it is what the button costs. Every other
   version of this format hides the price one panel away and it is the single number
   that decides which one you press. */
.bvb-picks{position:absolute;left:14px;right:14px;bottom:14px;display:grid;
  grid-template-columns:1fr 1fr;gap:10px}
.bvb-side{display:flex;flex-direction:column;align-items:center;gap:1px;padding:10px 8px 9px;
  border-radius:var(--r-5);border:1px solid var(--line-2);background:var(--surface);
  color:var(--txt);cursor:pointer;font-family:var(--sans);
  transition:transform var(--dur-1) var(--ease),border-color var(--dur-1),background var(--dur-1),opacity var(--dur-2)}
.bvb-side b{font-size:var(--fs-md);font-weight:700;letter-spacing:.01em}
.bvb-side span{font-family:var(--mono);font-size:var(--fs-sm);color:var(--dim);font-weight:600}
.bvb-side:hover{transform:translateY(-1px)}
.bvb-side:active{transform:translateY(0)}
.bvb-bulls{border-color:rgba(var(--up-rgb),.3);background:rgba(var(--up-rgb),.08)}
.bvb-bulls b{color:var(--up)}
.bvb-bears{border-color:rgba(var(--down-rgb),.3);background:rgba(var(--down-rgb),.08)}
.bvb-bears b{color:var(--down)}
.bvb-bulls.mine{background:rgba(var(--up-rgb),.2);border-color:var(--up)}
.bvb-bears.mine{background:rgba(var(--down-rgb),.2);border-color:var(--down)}
.bvb-side.queued{border-style:dashed}
.bvb-side.shut{opacity:.55}
.bvb-side.won{box-shadow:0 0 0 1px currentColor,0 0 26px rgba(255,255,255,.1)}
.bvb-side.lost{opacity:.32}
/* The floor's own button never guesses a side, so when it is pressed with no side
   chosen it points at the two that do. */
.bvb-nudge{animation:bvb-nudge .5s var(--ease-2) 2}
@keyframes bvb-nudge{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}

/* ── the verdict ────────────────────────────────────────────
   It always names the other side, because in a team game that is what happened to you.
   Sits above the buttons with a scrim, because it lands over the bottom of the chart. */
.bvb-verdict{position:absolute;left:50%;bottom:80px;transform:translate(-50%,8px);
  display:flex;flex-direction:column;align-items:center;gap:2px;opacity:0;pointer-events:none;
  max-width:min(94%,470px);text-align:center;
  background:rgba(var(--bg-rgb),.9);border:1px solid var(--line);border-radius:var(--r-5);
  padding:7px 16px 9px;
  transition:opacity var(--dur-3) var(--ease),transform var(--dur-3) var(--ease)}
.bvb-verdict.show{opacity:1;transform:translate(-50%,0)}
.bvb-verdict b{font-size:var(--fs-lg);font-weight:700;letter-spacing:-.02em;line-height:1}
.bvb-verdict span{font-size:var(--fs-xs);color:var(--dim);line-height:1.35}
.bvb-verdict.win b{color:var(--up)}
.bvb-verdict.lost b{color:var(--down)}
.bvb-verdict.flat b,.bvb-verdict.void b{color:var(--txt-2)}

/* ── the two rosters ────────────────────────────────────────
   Side by side and equally wide. This column answers "who am I actually playing
   against", which in this format is a list rather than a number. */
.bvb-sidebar{display:flex;flex-direction:column;gap:8px;min-height:0;min-width:0}
.bvb-sidehead{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  font-size:var(--fs-xs);color:var(--dim);padding:0 2px}
.bvb-sidehead b{color:var(--txt-2);font-weight:600}
.bvb-sidehead .num{color:var(--acc);font-family:var(--mono)}
.bvb-teams{display:grid;grid-template-columns:1fr 1fr;gap:8px;flex:1;min-height:0}
.bvb-team{display:flex;flex-direction:column;min-height:0;min-width:0;
  border:1px solid var(--line);border-radius:var(--r-5);background:var(--bg-1);padding:8px 6px 6px}
.bvb-teamhead{display:flex;align-items:baseline;justify-content:space-between;gap:6px;padding:0 4px}
.bvb-teamhead b{font-size:var(--fs-sm);font-weight:700}
.bvb-teamhead .num{font-family:var(--mono);font-size:var(--fs-xs);font-weight:600}
.bvb-teamhead.bulls b,.bvb-teamhead.bulls .num{color:var(--up)}
.bvb-teamhead.bears b,.bvb-teamhead.bears .num{color:var(--down)}
.bvb-teamsum{font-family:var(--mono);font-size:var(--fs-micro);color:var(--dim-2);padding:2px 4px 6px;
  border-bottom:1px solid var(--line)}
.bvb-rows{flex:1;overflow-y:auto;scrollbar-width:thin;min-height:0;padding-top:4px}
.bvb-rows::-webkit-scrollbar{width:5px}
.bvb-rows::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:3px}
.bvb-row{display:flex;align-items:center;justify-content:space-between;gap:6px;
  padding:3px 4px;border-radius:var(--r-3);font-size:var(--fs-xs)}
.bvb-row.me{background:rgba(var(--acc-rgb),.13)}
.bvb-who{color:var(--txt-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.bvb-stake{font-family:var(--mono);color:var(--dim);flex:0 0 auto}
.bvb-empty{font-size:var(--fs-micro);color:var(--dim-2);padding:6px 4px;line-height:1.4}
/* The losing side dims rather than the winning side lighting up, so the two team
   colours never have to compete with a third one for "this is the good news". */
.bvb-dim{opacity:.4}

/* ── the receipt ────────────────────────────────────────────
   Always on screen. Before a round ends it shows the commitment for the NEXT one,
   which is the strongest thing this game has to say: the coin after this one is
   already fixed, and its field does not exist yet. */
.bvb-proof{border-top:1px solid var(--line);padding-top:8px;display:flex;flex-direction:column;gap:3px}
.bvb-proof b{font-family:var(--mono);font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.1em;color:var(--dim-2);font-weight:600}
.bvb-proof code{font-family:var(--mono);font-size:var(--fs-micro);color:var(--txt-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bvb-proof span{font-size:var(--fs-micro);color:var(--dim-2);line-height:1.4}

/* ── the proof modal ────────────────────────────────────────*/
.bvb-proofbody{display:flex;flex-direction:column;gap:9px;font-size:var(--fs-xs);color:var(--dim);line-height:1.5}
.bvb-proofbody b{color:var(--txt-2)}
.bvb-proofbody label{font-family:var(--mono);font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.1em;color:var(--dim-2);margin-top:4px}
.bvb-proofbody code{font-family:var(--mono);font-size:var(--fs-micro);color:var(--txt-2);
  background:var(--surface-2);border-radius:var(--r-3);padding:5px 7px;word-break:break-all}
.bvb-fieldtable{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:var(--fs-micro)}
.bvb-fieldtable th{text-align:left;color:var(--dim-2);font-weight:500;padding:3px 6px;
  border-bottom:1px solid var(--line)}
.bvb-fieldtable td{padding:3px 6px;color:var(--dim)}
.bvb-fieldtable tr.bvb-hit td{color:var(--acc)}
.bvb-proofok{margin-top:6px;padding:7px 9px;border-radius:var(--r-3);font-family:var(--mono);
  font-size:var(--fs-micro)}
.bvb-proofok.yes{background:rgba(var(--up-rgb),.1);color:var(--up)}
.bvb-proofok.no{background:var(--surface-2);color:var(--dim)}

/* ── the rail panel ─────────────────────────────────────────*/
.bvb-meter{display:flex;height:8px;border-radius:4px;overflow:hidden;background:var(--surface-2);gap:1px}
.bvb-meter i{display:block;transition:width var(--dur-3) var(--ease)}
.bvb-meter i:first-child{background:var(--up)}
.bvb-meter i:last-child{background:var(--down)}
.bvb-split{display:flex;justify-content:space-between;gap:8px;margin-top:5px;
  font-family:var(--mono);font-size:var(--fs-micro);color:var(--dim-2)}
.bvb-split b{color:var(--txt-2);font-weight:600}
.bvb-split .bvb-b b{color:var(--up)}
.bvb-split .bvb-r b{color:var(--down)}
.bvb-underdog{width:100%;padding:8px;border-radius:var(--r-4);border:1px solid var(--line-2);
  background:var(--surface);color:var(--txt-2);font-size:var(--fs-xs);font-weight:600;cursor:pointer;
  transition:border-color var(--dur-1),background var(--dur-1)}
.bvb-underdog.bulls{border-color:rgba(var(--up-rgb),.35);color:var(--up)}
.bvb-underdog.bears{border-color:rgba(var(--down-rgb),.35);color:var(--down)}
.bvb-underdog:disabled{opacity:.45;cursor:default}
.bvb-panelnote{font-size:var(--fs-micro);color:var(--dim-2);line-height:1.45;margin-top:6px}

/* ── narrow ─────────────────────────────────────────────────
   Under 860 the roster wraps below the chart, which is also where games.js drops its
   own second column. The two teams stay side by side even there: they are a comparison,
   and stacking them turns the comparison into scrolling. */
@media (max-width:860px){
  .bvb-body{grid-template-columns:1fr}
  .bvb-stage{min-height:320px}
  .bvb-teams{max-height:190px}
}
@media (max-width:480px){
  .bvb-move{font-size:27px}
  .bvb-picks{left:10px;right:10px;bottom:10px;gap:8px}
  .bvb-verdict{bottom:74px}
}

/* /duel.css */
/* ══════════════════════════════════════════════════════════════════════════════
   DUEL.

   Every class here starts with `dul-`. app.js, games.js, crash.js, jackpot.js and this
   file share one CSS namespace, and a collision has silently broken a feature twice
   (.rail existed in two stylesheets and killed the prize rail; paintStrip existed in two
   scripts and killed the results strip). A distinctive prefix makes that class of bug
   impossible rather than fixed-once.

   Design tokens come from style.css. GREEN AND RED STILL MEAN DIRECTION AND NOTHING
   ELSE. The two players are never coloured against each other: a duel where one side is
   green and the other red has announced a winner before the first case opened, which is
   the exact thing the server goes to some trouble to withhold. The only differentiation
   between the two columns is which one is YOURS, in brand orange, and which one is
   currently ahead, in weight.
   ══════════════════════════════════════════════════════════════════════════════ */

/* #gboard is a centring flex row, so a child has to claim both axes explicitly or it
   collapses to its content and the canvas has nothing to measure itself against. */
.dul{display:flex;flex-direction:column;flex:1;align-self:stretch;min-width:0;min-height:0;gap:0}

/* ── what is turning right now ──────────────────────────────
   The spectator entry point, and it sits above everything else on purpose. Somebody
   arriving at this tile with no duel of their own should be one click from watching one,
   because watching is most of what this format is worth. */
.dul-topbar{display:flex;align-items:center;gap:10px;padding:2px 2px 9px;min-width:0}
.dul-toplabel{font-family:var(--mono);font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.11em;color:var(--dim-2);white-space:nowrap}
.dul-livestrip{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;flex:1;min-width:0}
.dul-livestrip::-webkit-scrollbar{display:none}
.dul-livenone{font-size:var(--fs-xs);color:var(--dim-2)}
.dul-livechip{flex:0 0 auto;padding:3px 9px;border-radius:var(--r-6);font-size:var(--fs-xs);
  font-weight:600;border:1px solid transparent;background:var(--surface-2);color:var(--txt-2);
  cursor:pointer;white-space:nowrap;
  transition:transform var(--dur-1) var(--ease),border-color var(--dur-1)}
.dul-livechip:hover{transform:translateY(-1px)}
.dul-livechip b{margin-left:6px;color:var(--acc)}
.dul-livechip.on{border-color:var(--acc-line);background:rgba(var(--acc-rgb),.12)}
.dul-livechip.mine{color:var(--txt)}

/* ── layout ─────────────────────────────────────────────────
   Stage large and central, the board down the right. The lobby is not decoration the way
   a live feed is elsewhere: in this format the offers ARE the game, so it gets a fixed
   column and every row is a thing you can act on. */
.dul-body{flex:1;display:grid;grid-template-columns:1fr 246px;gap:12px;min-height:0;min-width:0}
.dul-stage{position:relative;min-height:360px;min-width:0;border-radius:var(--r-5);
  background:radial-gradient(120% 90% at 50% 30%,rgba(var(--acc-rgb),.05),transparent 62%),
             var(--bg-1);
  border:1px solid var(--line);overflow:hidden}
/* Absolute, so the canvas's own pixel size can never feed back into the box it is
   measuring itself against. */
.dul-canvas{position:absolute;left:0;top:0;display:block}

/* ── the two names and the two scores ───────────────────────
   DOM rather than canvas, and that is deliberate: this is the one thing a spectator with
   a frozen frame loop still has to be able to read. The scores are the whole game, so
   they sit level with each other at the top of their own columns and are compared by
   eye, not by arithmetic. */
.dul-head{position:absolute;left:0;right:0;top:0;height:62px;display:grid;
  grid-template-columns:1fr 96px 1fr;align-items:center;padding:8px 14px 0;pointer-events:none}
.dul-side-head{display:flex;flex-direction:column;gap:1px;min-width:0}
.dul-side-head:last-child{align-items:flex-end;text-align:right}
.dul-name{font-size:var(--fs-sm);font-weight:600;color:var(--txt-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.dul-side-head.me .dul-name{color:var(--acc)}
.dul-score{font-family:var(--mono);font-size:22px;font-weight:700;letter-spacing:-.04em;
  line-height:1;color:var(--dim);transition:color var(--dur-2) linear}
/* Ahead is weight and brightness, never a colour that means good or bad. Whoever is
   behind is still one case away. */
.dul-side-head.ahead .dul-score{color:var(--txt)}
.dul-vs{display:flex;align-items:center;justify-content:center;text-align:center}
.dul-vs span{font-family:var(--mono);font-size:var(--fs-micro);color:var(--dim-2);
  letter-spacing:.06em;white-space:nowrap}

/* ── the status line ────────────────────────────────────────
   Under the columns, above the button. Counted off the synced clock rather than off the
   last poll, so the tenths are honest in the second that matters. */
.dul-state{position:absolute;left:0;right:0;bottom:60px;display:flex;flex-wrap:wrap;
  align-items:center;justify-content:center;gap:7px;padding:0 14px;pointer-events:none;
  font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);text-align:center}
.dul-state b{color:var(--txt-2);font-weight:600}
.dul-pip{width:6px;height:6px;border-radius:50%;background:var(--dim-2);display:inline-block;flex:0 0 auto}
.dul-pip.live{background:var(--acc);box-shadow:0 0 0 3px rgba(var(--acc-rgb),.16);
  animation:dul-pulse 1.1s var(--ease-2) infinite}
@keyframes dul-pulse{0%,100%{box-shadow:0 0 0 3px rgba(var(--acc-rgb),.16)}50%{box-shadow:0 0 0 7px transparent}}
/* The pair cap. It is a fact, not a telling off, so it is a note rather than a warning. */
.dul-capnote{flex:1 0 100%;font-family:var(--sans);font-size:var(--fs-micro);color:var(--dim-2)}

/* ── the verdict ────────────────────────────────────────────
   It always names the margin, because in a duel the only thing that matters afterwards
   is how close it was. That is what makes the rematch button worth pressing.

   ONE ROW, IN THE STATUS LINE'S OWN SLOT, and that is a correction rather than a style
   choice. The first version was a two-line card floating over the bottom of the stage,
   the way the Jackpot's sits over the lower arc of its ring. On a wheel that costs
   nothing. Here it landed across the last two case rows — which are the rounds that
   decided the duel and the whole reason anybody is still looking. So it takes the status
   line's place instead (`hud()` hides that while this is up: the two were saying the same
   thing anyway) and the columns are never covered. */
.dul-verdict{position:absolute;left:50%;bottom:56px;transform:translate(-50%,6px);
  display:flex;align-items:baseline;justify-content:center;gap:9px;opacity:0;pointer-events:none;
  max-width:min(96%,540px);
  background:rgba(var(--bg-rgb),.88);border:1px solid var(--line);border-radius:var(--r-5);
  padding:5px 14px 6px;
  transition:opacity var(--dur-3) var(--ease),transform var(--dur-3) var(--ease)}
.dul-verdict:empty{display:none}
.dul-verdict.show{opacity:1;transform:translate(-50%,0)}
.dul-verdict b{font-size:var(--fs-lg);font-weight:700;letter-spacing:-.03em;color:var(--txt);
  white-space:nowrap}
.dul-verdict span{font-size:var(--fs-sm);color:var(--txt-2);text-align:left;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dul-verdict.win b{color:var(--up)}
.dul-verdict.lost b{color:var(--down)}
.dul-verdict.sonear b{color:var(--down)}
.dul-verdict.sonear span{color:var(--warn);font-weight:600}
.dul-verdict.flat b{color:var(--txt-2)}
.dul-verdict.sonear{animation:dul-nudge .5s var(--ease)}
/* Keeps the -50% centring: an animation that only set translateX would drop the
   translate(-50%) the positioning depends on and throw the box half a width right. */
@keyframes dul-nudge{
  0%,100%{transform:translate(-50%,0)}
  25%{transform:translate(calc(-50% - 5px),0)}
  75%{transform:translate(calc(-50% + 5px),0)}
}

/* ── the one button ─────────────────────────────────────────
   Same job as Crash's and the Jackpot's: big, centred, and it changes what it says rather
   than moving. A control that appears somewhere new is a control you have to find while a
   clock is running. */
.dul-cta-wrap{position:absolute;left:0;right:0;bottom:14px;display:flex;justify-content:center;
  gap:10px;flex-wrap:wrap}
.dul-cta{min-width:224px;padding:12px 26px;font-size:var(--fs-base);font-weight:700;
  border-radius:var(--r-4);letter-spacing:-.01em;box-shadow:0 6px 24px rgba(var(--acc-rgb),.22)}
/* Taking your own offer back down is not the same act as posting or accepting one, so it
   gets the outline. The filled state stays reserved for the thing worth doing. */
.dul-cta.add{background:transparent;color:var(--acc);border:1px solid var(--acc-line);box-shadow:none}
.dul-cta.add:hover{background:var(--acc-soft)}
.dul-cta.shut{opacity:.45;pointer-events:none;box-shadow:none}
/* Posting your own sits beside taking one, outlined for the same reason `.add` is: the
   filled button stays reserved for the faster game, which is answering an offer already
   on the board. Narrower, because it is the second choice and should not read as a
   competing headline. */
.dul-cta.dul-alt{min-width:0;padding:12px 20px;background:transparent;color:var(--acc);
  border:1px solid var(--acc-line);box-shadow:none;font-weight:600}
.dul-cta.dul-alt:hover{background:var(--acc-soft)}

/* ── the board ──────────────────────────────────────────────
   Lobby on top, round log under it, receipt at the bottom. The lobby comes first because
   an empty one is this format's failure mode and a full one is its entire pitch. */
.dul-side{display:flex;flex-direction:column;gap:8px;min-height:0;min-width:0}
.dul-sidehead{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  font-size:var(--fs-xs);color:var(--dim);padding:0 2px}
.dul-sidehead b{font-size:var(--fs-sm);color:var(--txt-2);font-weight:600}
.dul-sidehead span{font-family:var(--mono);font-size:var(--fs-micro);color:var(--dim-2)}

.dul-lobby{flex:1 1 auto;min-height:96px;overflow-y:auto;scrollbar-width:thin;
  display:flex;flex-direction:column;gap:6px}
.dul-empty{font-size:var(--fs-xs);color:var(--dim-2);padding:10px 4px;text-align:center}

.dul-offer{border:1px solid var(--line);border-radius:var(--r-5);background:var(--surface);
  padding:7px 9px 8px;display:flex;flex-direction:column;gap:4px;cursor:pointer;
  transition:border-color var(--dur-1),background var(--dur-1)}
.dul-offer:hover{border-color:var(--acc-line);background:var(--surface-2)}
.dul-offer.mine{border-color:var(--acc-line);background:rgba(var(--acc-rgb),.06)}
.dul-offer-top{display:flex;align-items:baseline;justify-content:space-between;gap:8px;min-width:0}
.dul-who{font-size:var(--fs-sm);font-weight:600;color:var(--txt-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dul-stake{font-family:var(--mono);font-size:var(--fs-sm);font-weight:700;color:var(--acc)}
.dul-offer-bot{display:flex;align-items:center;justify-content:space-between;gap:8px;min-width:0}
.dul-terms{font-family:var(--mono);font-size:var(--fs-micro);color:var(--dim-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dul-take{flex:0 0 auto;padding:3px 11px;font-size:var(--fs-xs);font-weight:700;
  border-radius:var(--r-6);background:var(--acc);color:#140b03;border:0;cursor:pointer}
.dul-drop{flex:0 0 auto;padding:3px 9px;font-size:var(--fs-micro);border-radius:var(--r-6);
  background:transparent;color:var(--dim);border:1px solid var(--line);cursor:pointer}
/* The record against the person looking. This is the line that turns a price into an
   opponent, and it is the whole reason the pair record is stored at all. */
.dul-riv{font-family:var(--mono);font-size:var(--fs-micro);color:var(--warn)}

/* ── the round log ──────────────────────────────────────────
   DOM, and load-bearing for it: this is the readable record of a duel that survives a
   frozen frame loop, a hidden tab, and arriving halfway through. */
.dul-logwrap{flex:0 0 auto;display:flex;flex-direction:column;gap:5px;min-height:0}
.dul-log{max-height:150px;overflow-y:auto;scrollbar-width:thin;display:flex;flex-direction:column;gap:2px}
.dul-logrow{display:grid;grid-template-columns:18px 1fr 42px 42px 1fr;align-items:center;gap:5px;
  font-size:var(--fs-micro);padding:2px 3px;border-radius:var(--r-6)}
.dul-logrow.sudden{background:rgba(var(--acc-rgb),.08)}
.dul-logn{font-family:var(--mono);color:var(--dim-2);text-align:center}
.dul-logitem{color:var(--c,var(--dim));opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dul-logitem.right{text-align:right}
.dul-logitem.up{opacity:1;font-weight:600}
.dul-logscore{font-family:var(--mono);color:var(--txt-2);text-align:center}

/* ── the receipt ────────────────────────────────────────────
   Always on screen, never in the way. Before the last case lands it says what is sealed;
   after it, it hands over the seed and offers to recompute the whole duel. */
.dul-proof{flex:0 0 auto;border-top:1px solid var(--line);padding-top:7px;
  display:flex;flex-direction:column;gap:3px}
.dul-proof b{font-size:var(--fs-micro);text-transform:uppercase;letter-spacing:.1em;color:var(--dim-2)}
.dul-proof code{font-family:var(--mono);font-size:var(--fs-micro);color:var(--txt-2);
  word-break:break-all;line-height:1.35}
.dul-proof span{font-size:var(--fs-micro);color:var(--dim-2);line-height:1.4}
.dul-verify{align-self:flex-start;margin-top:3px;padding:2px 9px;font-size:var(--fs-micro);
  border-radius:var(--r-6);background:transparent;color:var(--acc);
  border:1px solid var(--acc-line);cursor:pointer}

/* ── the panel, in games.js's left rail ─────────────────────
   The decision this format gives you, and it is made before the duel rather than during
   it. Three segmented controls and one note that says the case does not change the price,
   which is the fact that makes choosing one a matter of taste rather than of value. */
.dul-seg{display:flex;gap:4px}
.dul-seg button{flex:1;padding:6px 4px;font-size:var(--fs-xs);font-weight:600;border-radius:var(--r-6);
  background:var(--surface-2);color:var(--dim);border:1px solid transparent;cursor:pointer;
  transition:color var(--dur-1),border-color var(--dur-1),background var(--dur-1)}
.dul-seg button:hover{color:var(--txt-2)}
.dul-seg button.on{background:rgba(var(--acc-rgb),.13);color:var(--acc);border-color:var(--acc-line)}
.dul-hint{font-size:var(--fs-micro);color:var(--dim-2);line-height:1.4;margin-top:4px}
.dul-panelnote{display:flex;flex-direction:column;gap:2px;margin-top:9px;
  font-size:var(--fs-micro);color:var(--dim-2);line-height:1.45}
.dul-panelnote b{color:var(--txt-2)}

/* ── the proof modal ────────────────────────────────────────
   The table is the duel, round by round, and it is the thing somebody checks by hand. */
.dul-proofbody p{font-size:var(--fs-sm);color:var(--txt-2);line-height:1.55;margin:0 0 10px}
.dul-proofbody label{display:block;font-size:var(--fs-micro);text-transform:uppercase;
  letter-spacing:.1em;color:var(--dim-2);margin:10px 0 3px}
.dul-proofbody code{display:block;font-family:var(--mono);font-size:var(--fs-micro);
  color:var(--txt-2);word-break:break-all;background:var(--surface-2);
  border-radius:var(--r-6);padding:6px 8px}
.dul-scoreline{font-size:var(--fs-sm);color:var(--txt-2)}
.dul-scoreline b{font-family:var(--mono);color:var(--txt)}
.dul-tickets{width:100%;border-collapse:collapse;margin:6px 0 4px;font-size:var(--fs-xs)}
.dul-tickets th{text-align:left;font-weight:600;color:var(--dim-2);font-size:var(--fs-micro);
  text-transform:uppercase;letter-spacing:.08em;padding:4px 6px;border-bottom:1px solid var(--line)}
.dul-tickets td{padding:4px 6px;color:var(--txt-2);border-bottom:1px solid var(--line-2,var(--line))}
.dul-tickets tr.sd td{color:var(--acc)}
.dul-proofok{margin-top:10px;padding:7px 10px;border-radius:var(--r-6);font-size:var(--fs-xs);
  font-family:var(--mono)}
.dul-proofok.yes{background:rgba(var(--up-rgb),.1);color:var(--up)}
.dul-proofok.no{background:var(--surface-2);color:var(--dim)}

/* ── narrow ─────────────────────────────────────────────────
   Under 860 the board goes below the stage rather than beside it, which is the same
   breakpoint crash.css and jackpot.css use. The lobby keeps its full height there: on a
   phone the list of offers is the game and the stage is what you scroll to. */
@media (max-width:860px){
  .dul-body{grid-template-columns:1fr;grid-template-rows:minmax(320px,1fr) auto}
  .dul-side{max-height:none}
  .dul-lobby{max-height:210px}
  .dul-log{max-height:120px}
  .dul-head{height:56px}
  .dul-score{font-size:19px}
}

@media (prefers-reduced-motion:reduce){
  .dul-pip.live{animation:none}
  .dul-verdict.sonear{animation:none}
  .dul-verdict{transition:none}
}

/* /meta.css */
/* Meta layer — season pass, quests, rakeback.
   Every selector is prefixed `mta-`. app.js and games.js share one global scope and a
   generic class name has silently broken a feature twice (`.rail`, `paintStrip`), so
   nothing in here is a word another stylesheet would plausibly reach for.
   Tokens only — no new colours. The accent stays reserved for live state. */

.mta-grid{
  display:grid;gap:10px;margin:0 0 26px;
  grid-template-columns:minmax(260px,1fr) minmax(300px,1.6fr) minmax(190px,.8fr);
}
@media(max-width:1000px){.mta-grid{grid-template-columns:1fr}}

.mta-pass,.mta-questbox,.mta-rake{
  border:1px solid var(--line);border-radius:var(--r-5);background:var(--surface);
  padding:13px 15px;display:flex;flex-direction:column;gap:9px;min-width:0;
}
.mta-pass{background:linear-gradient(160deg,var(--acc-soft),var(--surface) 62%)}

.mta-phead{display:flex;align-items:center;gap:9px}
.mta-eyebrow{font-family:var(--mono);font-size:var(--fs-mini);letter-spacing:.08em;
  text-transform:uppercase;color:var(--dim)}
.mta-timer{margin-left:auto;font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2)}

.mta-trow{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.mta-tier{font-size:var(--fs-2xl);font-weight:600;letter-spacing:-.03em}
.mta-tier em{font-style:normal;font-size:var(--fs-md);color:var(--dim);font-weight:400}
.mta-nextrw{font-family:var(--mono);font-size:var(--fs-xs);color:var(--txt-2)}
.mta-nextrw.is-mile{color:var(--acc)}
.mta-badge{margin-left:auto;font-family:var(--mono);font-size:var(--fs-mini);color:var(--on-acc);
  background:var(--acc);border-radius:var(--r-2);padding:2px 7px;font-weight:600}

.mta-bar{height:3px;background:var(--line);border-radius:var(--r-1);overflow:hidden}
.mta-bar i{display:block;height:100%;background:var(--acc);transition:width var(--dur-5) var(--ease)}
.mta-bigbar{height:6px;border-radius:var(--r-1)}

.mta-away{font-size:var(--fs-xs);color:var(--dim);line-height:1.5}
.mta-away b{color:var(--txt);font-family:var(--mono)}

.mta-pactions{display:flex;gap:7px;margin-top:auto;flex-wrap:wrap}
.mta-btn{font-size:var(--fs-sm);font-weight:600;padding:7px 13px;border-radius:var(--r-3);
  background:var(--acc);color:var(--on-acc);border:1px solid var(--acc);cursor:pointer}
.mta-btn:hover:not(:disabled){filter:brightness(1.08)}
.mta-btn:disabled{opacity:.4;cursor:default}
.mta-btn.mta-ghost{background:transparent;color:var(--txt-2);border-color:var(--line-2)}
.mta-btn.mta-ghost:hover{color:var(--txt);border-color:var(--line-3);background:var(--surface-2)}
.mta-btn.mta-sm{margin-left:auto;padding:4px 10px;font-size:var(--fs-xs)}

/* ── quests ───────────────────────────────────────────────── */
.mta-quests{display:grid;gap:7px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}
.mta-quest{border:1px solid var(--line);border-radius:var(--r-4);padding:9px 11px;
  background:var(--bg-1);transition:border-color var(--dur-2),background var(--dur-2)}
.mta-quest.is-ready{border-color:var(--acc-line);background:var(--acc-soft)}
.mta-quest.is-done{opacity:.4}
.mta-qtop{display:flex;align-items:baseline;gap:7px}
.mta-qtag{font-family:var(--mono);font-size:var(--fs-micro);letter-spacing:.06em;text-transform:uppercase;
  color:var(--dim-2);border:1px solid var(--line-2);border-radius:var(--r-2);padding:1px 5px}
.mta-qtag.mta-weekly{color:var(--acc);border-color:var(--acc-line)}
.mta-qlabel{font-size:var(--fs-sm);font-weight:600;letter-spacing:-.02em}
.mta-qreward{margin-left:auto;font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc)}
.mta-qdetail{font-size:var(--fs-mini);color:var(--dim);margin:4px 0 7px;line-height:1.45}
.mta-qfoot{display:flex;gap:8px;font-family:var(--mono);font-size:var(--fs-micro);
  color:var(--dim-2);margin-top:5px}
.mta-stars{margin-left:auto}

/* ── rakeback ─────────────────────────────────────────────── */
.mta-rake.is-cold{border-color:var(--line-3);background:linear-gradient(160deg,rgba(var(--up-rgb),.07),var(--surface) 60%)}
.mta-rakebig{font-family:var(--mono);font-size:var(--fs-3xl);font-variant-numeric:tabular-nums;
  letter-spacing:-.03em;line-height:1}
.mta-rakesub{font-size:var(--fs-xs);color:var(--dim);line-height:1.45}
.mta-coldtag{color:var(--up);font-weight:600}
.mta-rakefoot{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2);margin-top:auto}

/* ── the ladder sheet ─────────────────────────────────────── */
.mta-overlay{position:fixed;inset:0;z-index:60;background:var(--scrim);
  backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;padding:20px}
.mta-sheet{width:min(1100px,100%);max-height:86vh;overflow:auto;background:var(--surface);
  border:1px solid var(--line-2);border-radius:var(--r-5);padding:20px}
.mta-shead{display:flex;align-items:flex-start;gap:14px;margin-bottom:12px}
.mta-shead h3{font-size:var(--fs-lg)}
.mta-ssub{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);margin-top:4px}
.mta-shead .mta-btn{margin-left:auto}
.mta-break{display:flex;gap:14px;flex-wrap:wrap;font-family:var(--mono);font-size:var(--fs-mini);
  color:var(--dim);border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:9px 0;margin-bottom:14px}

.mta-ladder{display:flex;gap:8px;overflow-x:auto;padding:4px 2px 12px;scroll-behavior:smooth}
.mta-tile{flex:0 0 92px;border:1px solid var(--line);border-radius:var(--r-4);padding:10px 8px;
  background:var(--bg-1);text-align:center;position:relative}
.mta-tile.is-mile{border-color:var(--line-3);background:var(--surface-2)}
.mta-tile.is-open{border-color:var(--acc-line);background:var(--acc-soft)}
.mta-tile.is-claimed{opacity:.42}
.mta-tile.is-locked{opacity:.72}
.mta-tnum{font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2)}
.mta-treward{font-size:var(--fs-md);font-weight:600;letter-spacing:-.02em;margin:3px 0}
.mta-tile.is-mile .mta-treward{color:var(--acc)}
.mta-tstars{font-family:var(--mono);font-size:var(--fs-micro);color:var(--dim)}
.mta-tick{position:absolute;top:5px;right:7px;font-size:var(--fs-mini);color:var(--up)}

.mta-sfoot{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  border-top:1px solid var(--line);padding-top:13px}
.mta-note{font-size:var(--fs-xs);color:var(--dim);max-width:60ch;line-height:1.5}
.mta-sfoot .mta-btn{margin-left:auto}

/* /audio.css */
/* ══════════════════════════════════════════════════════════════════════════════
   AUDIO CONTROL  —  the popover behind the header's sound button, and the
   optional companion screen flash.

   Every class in here is prefixed `sfx`. app.js and games.js share one global
   scope and one flat stylesheet namespace, and a generic name has silently
   broken a feature twice already (`.rail` and `paintStrip()`). Nothing here is a
   word another file could plausibly want.

   The button itself (`.sndbtn`) is styled in floor.css and is left alone.
   ══════════════════════════════════════════════════════════════════════════════ */

.sfxpop{
  position:fixed;z-index:80;min-width:246px;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-4);
  padding:12px 13px 11px;
  box-shadow:var(--sh-3), 0 0 0 1px rgba(var(--shade-rgb),.3);
  opacity:0;transform:translateY(-6px) scale(.97);pointer-events:none;
  transition:opacity var(--dur-1) ease, transform var(--dur-1) ease;
}
.sfxpop.open{opacity:1;transform:none;pointer-events:auto}

.sfxpop-row{display:flex;align-items:center;gap:10px;margin-bottom:9px}
.sfxpop-lab{font-family:var(--mono);font-size:var(--fs-xs);letter-spacing:.06em;
  text-transform:uppercase;color:var(--dim);white-space:nowrap}
.sfxpop-val{font-size:var(--fs-xs);color:var(--txt-2);min-width:34px;text-align:right}

/* A range input styled to match, in both engines. The default control is the one
   thing on the page that would have looked like a browser. */
.sfxpop-range{
  -webkit-appearance:none;appearance:none;flex:1;height:3px;border-radius:var(--r-1);
  background:var(--line-3);outline:none;margin:0;cursor:pointer;
}
.sfxpop-range::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;width:13px;height:13px;border-radius:50%;
  background:var(--acc);border:0;cursor:pointer;
  box-shadow:0 0 0 3px var(--acc-soft);
}
.sfxpop-range::-moz-range-thumb{
  width:13px;height:13px;border-radius:50%;background:var(--acc);border:0;cursor:pointer;
  box-shadow:0 0 0 3px var(--acc-soft);
}
.sfxpop-range:focus-visible{box-shadow:0 0 0 2px var(--acc-line)}

.sfxpop-chk{cursor:pointer;font-size:var(--fs-sm);color:var(--txt-2);margin-bottom:8px}
.sfxpop-chk input{accent-color:var(--acc);cursor:pointer;margin:0}

.sfxpop-note{font-size:var(--fs-xs);line-height:1.5;color:var(--dim-2);
  border-top:1px solid var(--line);padding-top:8px}

/* ── companion flash ─────────────────────────────────────────────
   Off by default (`__sfx.visuals(true)` opts in) and never drawn at all when the
   player has asked for reduced motion — a full-viewport tint pulse is precisely
   the class of effect that setting exists to suppress. The media query below is
   a second belt on top of the JS guard, so a stale flag cannot leak one through. */
.sfxflash{
  position:fixed;inset:0;z-index:70;pointer-events:none;
  animation:sfxflash-in .42s ease-out forwards;
}
.sfxflash-win{background:radial-gradient(ellipse at 50% 45%, var(--acc-soft), transparent 62%)}
.sfxflash-lose{background:radial-gradient(ellipse at 50% 45%, rgba(var(--down-rgb),.14), transparent 62%)}

@keyframes sfxflash-in{
  0%{opacity:0}
  18%{opacity:1}
  100%{opacity:0}
}

@media (prefers-reduced-motion: reduce){
  .sfxflash{display:none}
  .sfxpop{transition:none}
}

@media (max-width:560px){
  .sfxpop{min-width:0;left:8px;right:8px}
}

/* /onboarding.css */
/* ══════════════════════════════════════════════════════════════════════════════
   ONBOARDING — the first sixty seconds.

   Every class in this file is prefixed `ob`. app.js, games.js and the two existing
   stylesheets share one global namespace and a generic name has silently broken a
   feature twice (`paintStrip`, `.rail`), so nothing here is allowed a generic name
   even when it would read better.

   COLOUR IS THE CURRENCY. The single job of this file is that points and SOL can
   never be mistaken for each other:

     POINTS  orange (the existing accent), monospace, ◈, square corners.
     SOL     violet, pill-shaped, ◎, and the literal word SOL every single time.

   Violet rather than green on purpose. style.css reserves green and red strictly for
   price direction, and a green prize chip next to a green "+2.4%" is exactly the
   confusion this file exists to prevent. Violet is Solana's own colour and is used
   nowhere else in the app, so a violet pill means real money and nothing else does.
   ══════════════════════════════════════════════════════════════════════════════ */

:root{
  --ob-pts:var(--acc);
  --ob-pts-soft:var(--acc-soft);
  --ob-pts-line:var(--acc-line);
  --ob-sol:#a67bff;
  --ob-sol-soft:rgba(166,123,255,.13);
  --ob-sol-line:rgba(166,123,255,.44);
}

/* ── the two tokens ────────────────────────────────────────────────────────── */
.obtok{display:inline-flex;align-items:center;gap:5px;white-space:nowrap;
  font-family:var(--mono);font-size:var(--fs-sm);font-variant-numeric:tabular-nums;
  padding:2px 8px;line-height:1.55;letter-spacing:-.01em}
.obtok.pts{color:var(--ob-pts);background:var(--ob-pts-soft);
  border:1px solid var(--ob-pts-line);border-radius:var(--r-2)}
.obtok.sol{color:var(--ob-sol);background:var(--ob-sol-soft);
  border:1px solid var(--ob-sol-line);border-radius:var(--r-pill)}
.obtok b{font-weight:600}
.obtok i{font-style:normal;opacity:.75}
/* Sized in em so it tracks the text, `block` so the flex row centres the SVG box itself
   rather than a text baseline that a substituted font would have moved. See the note by
   SYM_PTS in onboarding.js for why these are drawn instead of typed. */
.obtok .obsym{width:1em;height:1em;flex:0 0 auto;display:block}

/* The balance in the header is points, everywhere, always. Marking it at the source
   costs one rule and means the number a player looks at most carries its unit. */
.acct .b::before{content:"◈ ";color:var(--acc);font-weight:600}

/* ── the always-on ledger bar ──────────────────────────────────────────────── */
/* --ob-top is measured from the real header at runtime. Hardcoding 56px meant one
   extra chip in the bar — and other people are adding chips to it — put a sliver of
   the ledger underneath the header instead of below it. */
.obledger{position:sticky;top:var(--ob-top,56px);z-index:39;
  background:rgba(var(--bg-rgb),.88);backdrop-filter:blur(18px) saturate(1.4);
  border-bottom:1px solid var(--line)}
.obled-in{max-width:1240px;margin:0 auto;padding:7px 24px;
  display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.obled-c{display:flex;align-items:center;gap:8px;min-width:0;cursor:pointer;
  border-radius:var(--r-3);padding:2px 4px;transition:background var(--dur-1)}
.obled-c:hover{background:var(--surface-2)}
.obled-t{font-size:var(--fs-xs);color:var(--dim);letter-spacing:-.005em}
.obled-t b{color:var(--txt-2);font-weight:500}
.obled-t em{font-style:normal;color:var(--ob-sol)}
.obled-sep{flex:1;min-width:8px}
.obled-live{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);
  display:flex;align-items:center;gap:6px}
.obled-live i{width:6px;height:6px;border-radius:50%;background:var(--up);font-style:normal;
  box-shadow:0 0 0 3px rgba(var(--up-rgb),.14)}
.obled-help{background:transparent;border:1px solid var(--line-2);color:var(--dim);
  border-radius:var(--r-pill);padding:3px 11px;font-size:var(--fs-xs)}
.obled-help:hover:not(:disabled){background:var(--surface-2);color:var(--txt);border-color:var(--line-3)}
@media(max-width:860px){
  .obled-in{padding:7px 16px;gap:9px}
  .obled-t.obopt{display:none}
  .obled-live{display:none}
}
/* Below 700px style.css stops the header sticking — stripped of the two context
   chips it is still two rows and 110px, and two stacked sticky bars was a third
   of a phone screen spent on chrome before any game was drawn. This bar becomes
   the only sticky element, so its offset is the top of the viewport rather than
   the height of a header that is no longer there. --ob-top is still measured and
   still correct; it is just no longer the right question at this width. */
@media(max-width:700px){
  .obledger{top:0}
  /* Measured at 375px: the two currency chips are 150px and 158px, the help
     button 84px, and 392px of content in a 343px row wrapped the help onto a
     second line — an 86px sticky bar for 40px of information. The two chips are
     this file's entire stated job and they stay; "What's this?" is the same
     explanation the flow itself gives and the How tab repeats, so on a phone it
     is the thing that goes. */
  .obled-in{flex-wrap:nowrap;padding-block:5px}
  .obled-help{display:none}
}

/* ── the landing pitch (signed out) / goal ladder (signed in) ──────────────── */
.obpitch{border:1px solid var(--line);border-radius:var(--r-5);background:var(--surface);
  padding:18px 20px;margin:0 0 22px;animation:obrise .5s var(--ease)}
.obpitch-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:4px}
.obpitch-h h3{font-size:var(--fs-lg);letter-spacing:-.03em}
.obpitch-h span{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);
  letter-spacing:.1em;text-transform:uppercase}
.obthree{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:16px 0 14px}
.obthree > div{border:1px solid var(--line);border-radius:var(--r-4);padding:13px 14px;background:var(--bg-1)}
.obthree u{display:block;text-decoration:none;font:600 var(--fs-mini) var(--mono);color:var(--dim-2);
  letter-spacing:.1em;margin-bottom:7px}
.obthree b{display:block;font-size:var(--fs-md);letter-spacing:-.02em;margin-bottom:5px}
.obthree p{margin:0;font-size:var(--fs-sm);line-height:1.6;color:var(--dim)}
.obpitch-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  border-top:1px solid var(--line);padding-top:13px}
.obpitch-foot .obnote{flex:1;min-width:220px}
/* Deliberately not the dimmest grey available. Everything in an `.obnote` is either
   an honesty disclosure or a safety warning, and a disclosure nobody can read is not
   a disclosure. */
.obnote{font-size:var(--fs-xs);line-height:1.62;color:var(--dim)}
.obnote b{color:var(--txt-2);font-weight:500}
@media(max-width:760px){ .obthree{grid-template-columns:1fr} }

/* ── first-session goal ladder ─────────────────────────────────────────────── */
.obgoals{display:flex;flex-direction:column;gap:7px;margin:14px 0 12px}
.obgoal{display:flex;align-items:center;gap:11px;padding:9px 12px;border-radius:var(--r-4);
  border:1px solid var(--line);background:var(--bg-1);transition:border-color var(--dur-2),opacity var(--dur-2)}
.obgoal .obtick{flex:none;width:19px;height:19px;border-radius:50%;border:1px solid var(--line-3);
  display:grid;place-items:center;font-size:var(--fs-mini);color:var(--dim-2);font-family:var(--mono)}
.obgoal-t{flex:1;min-width:0;font-size:var(--fs-md);letter-spacing:-.01em}
.obgoal-t span{display:block;font-size:var(--fs-xs);color:var(--dim);margin-top:2px;line-height:1.5}
.obgoal-r{font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc);white-space:nowrap}
.obgoal.obdone{opacity:.55;border-color:rgba(var(--up-rgb),.26)}
.obgoal.obdone .obtick{border-color:rgba(var(--up-rgb),.5);color:var(--up)}
.obgoal.obdone .obgoal-r{color:var(--up)}
.obgoal button{padding:5px 11px;font-size:var(--fs-xs)}

/* ── the flow ──────────────────────────────────────────────────────────────── */
.obveil{position:fixed;inset:0;z-index:60;background:var(--scrim);
  backdrop-filter:blur(12px);display:grid;place-items:center;padding:20px;
  animation:obfade .3s var(--ease);overflow-y:auto}
@keyframes obfade{from{opacity:0}}
@keyframes obrise{from{opacity:0;transform:translateY(10px)}}
.obshell{width:100%;max-width:540px;background:var(--surface);border:1px solid var(--line-2);
  border-radius:var(--r-5);padding:24px;animation:obrise .42s var(--ease);
  box-shadow:var(--sh-4);margin:auto}
.obtop{display:flex;align-items:center;gap:10px;margin-bottom:16px}
.obdots{display:flex;gap:5px;flex:1}
.obdots i{height:2px;flex:1;background:var(--line-2);border-radius:var(--r-1);transition:background var(--dur-4)}
.obdots i.obon{background:var(--acc)}
.obskip{background:transparent;border:0;color:var(--dim-2);font-size:var(--fs-xs);padding:4px 6px}
.obskip:hover:not(:disabled){background:transparent;color:var(--txt-2)}
.obshell h3{font-size:var(--fs-2xl);letter-spacing:-.035em;line-height:1.15;margin-bottom:8px}
.obshell h3 em{font-style:normal;color:var(--ob-sol)}
.obsub{font-size:var(--fs-md);line-height:1.62;color:var(--txt-2);margin:0 0 4px}
.obsub b{color:var(--txt)}
.obshell input{width:100%;margin:16px 0 12px;background:var(--bg-1);border:1px solid var(--line-2);
  color:var(--txt);border-radius:var(--r-4);padding:12px 13px;font-size:var(--fs-base);font-family:var(--mono)}
.obshell input:focus{outline:0;border-color:var(--acc-line)}
.obfoot{display:flex;gap:8px;margin-top:18px}
.obfoot button{flex:1}
.obfoot button.obquiet{flex:0 0 auto}

/* the two-currency cards — the single most important thing in the flow */
.obcards{display:grid;grid-template-columns:1fr 1fr;gap:11px;margin:16px 0 14px}
.obcard{border-radius:var(--r-5);padding:14px;border:1px solid var(--line);background:var(--bg-1)}
.obcard.obc-pts{border-color:var(--ob-pts-line);background:linear-gradient(180deg,var(--ob-pts-soft),transparent 70%)}
.obcard.obc-sol{border-color:var(--ob-sol-line);background:linear-gradient(180deg,var(--ob-sol-soft),transparent 70%)}
.obcard-h{display:flex;align-items:center;gap:7px;margin-bottom:9px}
.obcard-g{font-size:var(--fs-lg);line-height:1}
.obc-pts .obcard-g{color:var(--ob-pts)}
.obc-sol .obcard-g{color:var(--ob-sol)}
.obcard-h b{font:600 var(--fs-sm) var(--mono);letter-spacing:.09em}
.obc-pts .obcard-h b{color:var(--ob-pts)}
.obc-sol .obcard-h b{color:var(--ob-sol)}
.obcard p{margin:0;font-size:var(--fs-sm);line-height:1.62;color:var(--txt-2)}
.obcard p + p{margin-top:7px;color:var(--dim)}
.obcard .obbig{display:block;font:600 var(--fs-xl) var(--mono);letter-spacing:-.03em;color:var(--txt);margin-bottom:6px}
@media(max-width:520px){ .obcards{grid-template-columns:1fr} }

/* the live-market pick */
.obpicks{display:grid;gap:8px;margin:14px 0 6px}
.obpick{display:flex;align-items:center;gap:11px;width:100%;text-align:left;
  background:var(--bg-1);border:1px solid var(--line-2);color:var(--txt);
  border-radius:var(--r-4);padding:12px 14px;font-size:var(--fs-md);font-weight:500}
.obpick:hover:not(:disabled){background:var(--surface-2);border-color:var(--acc-line);color:var(--txt)}
/* The risk buttons are disabled until the paytable lands, so the state has to read as
   "not yet" rather than as three identical live buttons. */
.obpick:disabled{opacity:.55}
.obpick .obpk-l{flex:1;min-width:0}
.obpick .obpk-l span{display:block;font-size:var(--fs-xs);color:var(--dim);font-weight:400;margin-top:2px}
.obpick .obpk-m{font-family:var(--mono);font-size:var(--fs-md);color:var(--acc)}

/* the reveal — welcome pack and spin results */
.obreveal{text-align:center;padding:20px 6px 12px}
.obreveal .obamt{font:700 46px/1 var(--mono);letter-spacing:-.04em;color:var(--acc);
  font-variant-numeric:tabular-nums}
.obreveal .obamt.obzero{color:var(--dim)}
.obreveal .obwhat{font-size:var(--fs-sm);color:var(--dim);margin-top:9px}

/* ── the scoring slide ─────────────────────────────────────────────────────────
   Two rows instead of a paragraph. The rule has a before and an after, and a player
   remembers a boundary they can see far better than one described in a sentence.

   The second row is DIMMED, never red, and there is no warning colour anywhere on this
   slide. Staking past the counted stake is allowed and pays in full; colouring it as a
   mistake would be the site lying about its own rules in the direction that flatters
   them. */
.obscore{display:flex;flex-direction:column;gap:8px;margin:16px 0 14px}
.obsc{display:flex;align-items:center;justify-content:space-between;gap:10px;
  flex-wrap:wrap;padding:11px 13px;border:1px solid var(--line);
  border-radius:var(--r-4);background:var(--bg-1);font-size:var(--fs-sm)}
.obsc > span{color:var(--dim);display:inline-flex;align-items:center;gap:7px}
.obsc b{color:var(--txt);font-weight:500;letter-spacing:-.01em}
.obsc.obsc-cut{opacity:.66}
.obsc.obsc-cut b{color:var(--dim)}

/* ── the cap line on the game floor ────────────────────────────────────────────
   Sits directly under games.js's stake box, which is the one place a player is looking
   at the number the rule is about. Sized as a caption on purpose: it appears only while
   it applies, so it never has to compete with the game for attention, and the MAX button
   puts it on screen the instant a whole balance is aimed at one round. */
.obcap{margin:8px 0 0;padding:8px 10px;border-radius:var(--r-3);
  border:1px solid var(--line);background:var(--bg-1);
  font-size:var(--fs-xs);line-height:1.7;color:var(--dim)}
/* Deliberately NOT the accent. An orange panel with an orange phrase inside it beside
   an orange stake box reads as an alert, and this is a statement of a rule under which
   nothing bad is happening. The token keeps its own colour because that is the currency
   marking and it is the same everywhere; the sentence around it stays quiet. */
/* `>` and not a descendant: the emphasised clause wraps a token, and .obtok's own inner
   <b> is the figure itself. A descendant rule here has the same specificity as .obtok b
   and sits later in the file, so it would win and grey out the number inside the chip. */
.obcap > b{color:var(--txt-2);font-weight:500}
.obcap .obtok{font-size:var(--fs-xs);padding:1px 6px}

/* recovery code */
.obcode{font-family:var(--mono);font-size:var(--fs-lg);letter-spacing:.05em;text-align:center;
  padding:13px;margin:12px 0 6px;border:1px dashed var(--acc-line);border-radius:var(--r-4);
  background:var(--acc-soft);color:var(--acc);user-select:all}

/* ── contextual first-time hints ───────────────────────────────────────────── */
.obhint{position:relative;margin:0;padding:12px 40px 12px 15px;
  border-bottom:1px solid var(--line);background:linear-gradient(90deg,var(--acc-soft),transparent 70%);
  animation:obrise .4s var(--ease);z-index:2}
.obhint b{display:block;font-size:var(--fs-sm);letter-spacing:-.01em;margin-bottom:3px}
.obhint p{margin:0;font-size:var(--fs-sm);line-height:1.6;color:var(--txt-2)}
.obhint p em{font-style:normal;color:var(--dim)}
.obhint-x{position:absolute;top:8px;right:8px;background:transparent;border:0;
  color:var(--dim-2);font-size:var(--fs-base);line-height:1;padding:4px 8px;border-radius:var(--r-3)}
.obhint-x:hover:not(:disabled){background:var(--surface-2);color:var(--txt)}

/* the standalone hint used outside the game stage */
.obhint.obfree{border:1px solid var(--line);border-radius:var(--r-5);margin:0 0 16px}

@media(prefers-reduced-motion:reduce){
  .obveil,.obshell,.obpitch,.obhint{animation:none}
}

/* /chat.css */
/* Live chat dock.
   Every class here is prefixed `pdchat-`. app.js and games.js share one global scope and
   `.rail` being declared in two stylesheets silently broke the prize rail once already —
   so nothing in this file is a name anything else could plausibly want. */

.pdchat-dock{
  /* --hdr-h, not 56: the header height was typed separately into this file,
     style.css's .rail and onboarding.css's ledger, so a sixth chip in the bar
     used to slide one of the three under it. */
  position:fixed;top:var(--hdr-h);right:0;bottom:0;width:318px;z-index:35;
  display:flex;flex-direction:column;
  background:rgba(var(--bg1-rgb),.94);backdrop-filter:blur(18px) saturate(1.4);
  border-left:1px solid var(--line);
  transform:translateX(0);transition:transform var(--dur-3) var(--ease);
}
.pdchat-dock.pdchat-shut{transform:translateX(101%);pointer-events:none}

/* Give the page its space back rather than sitting on top of it.
   `.wrap` is app.js's container and this is the only rule in this file that touches
   anything outside the `pdchat-` namespace — scoped to a body class only this file ever
   sets, so with the dock hidden the page is untouched. Below 1200px there is not enough
   room to reserve 318px AND leave a usable board, so the dock defaults to closed there
   and overlays as a sheet when opened (see chat.js). */
@media (min-width:1200px){
  body.pdchat-docked .wrap{margin-right:318px}
}

.pdchat-head{
  display:flex;align-items:center;gap:8px;padding:0 12px;height:40px;flex:0 0 auto;
  border-bottom:1px solid var(--line);font-size:var(--fs-md);letter-spacing:-.02em;
}
.pdchat-head b{font-weight:600}
.pdchat-grow{flex:1}
.pdchat-live{
  width:6px;height:6px;border-radius:50%;background:var(--acc);
  box-shadow:0 0 0 3px var(--acc-soft);animation:pdchat-pulse 2.6s var(--ease-2) infinite;
}
@keyframes pdchat-pulse{0%,100%{box-shadow:0 0 0 3px var(--acc-soft)}50%{box-shadow:0 0 0 6px transparent}}
.pdchat-count{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim)}
.pdchat-icon{
  background:transparent;border:0;color:var(--dim);font-size:var(--fs-lg);line-height:1;
  padding:2px 4px;cursor:pointer;border-radius:var(--r-2);
}
.pdchat-icon:hover{color:var(--txt);background:var(--surface-2)}

/* ── the log ─────────────────────────────────────────────── */
.pdchat-log{
  flex:1 1 auto;overflow-y:auto;overflow-x:hidden;padding:10px 12px 14px;
  display:flex;flex-direction:column;gap:9px;overscroll-behavior:contain;
}
.pdchat-log::-webkit-scrollbar{width:8px}
.pdchat-log::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:var(--r-2)}

.pdchat-msg{display:flex;flex-direction:column;gap:2px;animation:pdchat-in .22s var(--ease)}
@keyframes pdchat-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.pdchat-who{display:flex;align-items:center;gap:6px;font-size:var(--fs-xs);font-family:var(--mono)}
.pdchat-handle{color:var(--txt-2);font-weight:500;max-width:150px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.pdchat-clickable{cursor:pointer}
.pdchat-clickable:hover{color:var(--acc);text-decoration:underline}
.pdchat-mine .pdchat-handle{color:var(--acc)}
/* There was a .pdchat-bot badge here. House accounts are no longer tagged row by row;
   they are disclosed in rules.html §9 instead. The `bot` field is still on every
   message and still gates tipping and rain, it just does not print. */
.pdchat-time{margin-left:auto;color:var(--dim-2);font-size:var(--fs-mini)}
.pdchat-body{
  font-size:var(--fs-md);line-height:1.45;color:var(--txt);
  overflow-wrap:anywhere;word-break:break-word;
}
.pdchat-sys{
  font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);text-align:center;
  padding:3px 6px;border-top:1px dashed var(--line);border-bottom:1px dashed var(--line);
}

/* ── rain ────────────────────────────────────────────────── */
.pdchat-rain{
  flex:0 0 auto;padding:10px 12px;border-bottom:1px solid var(--acc-line);
  background:linear-gradient(180deg,var(--acc-soft),transparent);
}
.pdchat-rain-top{display:flex;align-items:baseline;gap:8px;margin-bottom:7px}
.pdchat-rain-top b{font-family:var(--mono);font-size:var(--fs-base);color:var(--acc)}
.pdchat-rain-top::before{content:"RAIN";font-family:var(--mono);font-size:var(--fs-micro);
  letter-spacing:.1em;color:var(--acc);align-self:center}
.pdchat-rain-n{margin-left:auto;font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim)}
.pdchat-rain-btn{
  width:100%;padding:8px;border-radius:var(--r-3);border:1px solid var(--acc-line);
  background:var(--acc);color:var(--on-acc);font-weight:600;font-size:var(--fs-md);cursor:pointer;
  font-family:var(--mono);
}
.pdchat-rain-btn:hover:not(:disabled){filter:brightness(1.08)}
.pdchat-rain-btn:disabled{background:transparent;color:var(--dim);cursor:default;
  border-color:var(--line-2)}
.pdchat-rain-btn.pdchat-in{color:var(--up);border-color:rgba(var(--up-rgb),.35)}
.pdchat-rain-why{margin-top:6px;font-size:var(--fs-xs);color:var(--dim);line-height:1.4}

/* ── composer ────────────────────────────────────────────── */
/* The bottom padding carries the safe area inset. On an iPhone the home indicator sits
   over the bottom ~34px of the viewport, so a composer flush to bottom:0 puts the send
   button underneath it. env() resolves to 0px everywhere it does not apply, so the
   fallback is the plain value. */
.pdchat-foot{
  flex:0 0 auto;border-top:1px solid var(--line);
  padding:9px 12px 11px;
  padding-bottom:calc(11px + env(safe-area-inset-bottom, 0px));
  padding-left:calc(12px + env(safe-area-inset-left, 0px));
  padding-right:calc(12px + env(safe-area-inset-right, 0px));
}
.pdchat-form{display:flex;gap:7px}
.pdchat-input{
  flex:1;min-width:0;background:var(--surface);border:1px solid var(--line-2);
  color:var(--txt);border-radius:var(--r-3);padding:8px 10px;font:var(--fs-md)/1.3 var(--sans);
}
.pdchat-input:focus{outline:none;border-color:var(--acc-line)}
.pdchat-input:disabled{opacity:.5}
.pdchat-send{
  background:var(--surface-2);border:1px solid var(--line-2);color:var(--txt);
  border-radius:var(--r-3);padding:0 12px;font-size:var(--fs-sm);font-weight:500;cursor:pointer;
}
.pdchat-send:hover:not(:disabled){border-color:var(--acc-line);color:var(--acc)}
.pdchat-send:disabled{opacity:.45;cursor:default}
.pdchat-gate{margin-top:7px;font-size:var(--fs-xs);color:var(--dim);line-height:1.4;min-height:14px}
.pdchat-locked{color:var(--txt-2)}
.pdchat-bar{margin-top:5px;height:3px;background:var(--line);border-radius:var(--r-1);overflow:hidden}
.pdchat-bar i{display:block;height:100%;background:var(--acc);border-radius:var(--r-1);
  transition:width var(--dur-4) var(--ease)}

/* ── the reopen tab ──────────────────────────────────────── */
.pdchat-tab{
  /* Lifted clear of the home indicator. This is the one fixed bottom element this
     feature adds, so it is the one that would land underneath it. */
  position:fixed;right:0;bottom:calc(26px + env(safe-area-inset-bottom, 0px));z-index:36;
  display:flex;align-items:center;gap:7px;
  padding:9px 13px 9px 11px;border-radius:9px 0 0 9px;cursor:pointer;
  background:var(--surface);border:1px solid var(--line-2);border-right:0;
  color:var(--txt-2);font-size:var(--fs-sm);font-weight:500;
  transition:transform var(--dur-3) var(--ease),color var(--dur-2);
}
.pdchat-tab:hover{color:var(--txt);border-color:var(--acc-line)}
.pdchat-tab.pdchat-tab-hidden{transform:translateX(110%);pointer-events:none}
.pdchat-tab-dot{width:6px;height:6px;border-radius:50%;background:var(--acc)}
.pdchat-tab.pdchat-tab-rain{border-color:var(--acc-line);color:var(--acc)}
.pdchat-tab.pdchat-tab-rain .pdchat-tab-dot{animation:pdchat-pulse 1.2s infinite}
.pdchat-tab-badge{
  font-family:var(--mono);font-size:var(--fs-mini);background:var(--acc);color:var(--on-acc);
  border-radius:var(--r-4);padding:1px 5px;font-weight:600;
}

/* ── narrow screens ──────────────────────────────────────── */
/* Full-height sheet rather than a column: a 318px dock over a 375px viewport is the
   whole screen anyway, and half-covering the game is worse than covering it. */
@media (max-width:900px){
  .pdchat-dock{width:100%;top:var(--hdr-h);border-left:0;border-top:1px solid var(--line)}
  /* Keeps the inset — a plain `bottom:18px` here silently undid the safe area on the
     one class of device that needs it. */
  .pdchat-tab{bottom:calc(18px + env(safe-area-inset-bottom, 0px))}
}
/* Below 700px style.css stops the header sticking, so there is no fixed 56px
   band to sit under any more — the dock starts at the top of the viewport. Left
   at 56 it hung a header's worth of empty page above a full-screen sheet. */
@media (max-width:700px){
  .pdchat-dock{top:0}
}

/* /profile.css */
/* Player profiles and the hall of fame.
 *
 * Every class here is prefixed `pf-`. That is not tidiness, it is a bug that has
 * already happened twice: app.js and games.js share one global scope and one cascade,
 * and `.rail` defined in two stylesheets silently broke the prize rail once already.
 * A generic name in this file would break something in a panel nobody was looking at.
 *
 * Design follows style.css's discipline rather than inventing its own: one accent
 * (signal orange) for brand and live state, green/red reserved for direction, type
 * carries the hierarchy. A profile that looked like a different product would read as
 * bolted on, which is exactly what it is trying not to be. */

.pf-scrim{
  position:fixed;inset:0;z-index:120;display:none;
  background:var(--scrim);backdrop-filter:blur(9px) saturate(1.2);
  overflow-y:auto;overscroll-behavior:contain;
  padding:min(6vh,var(--hdr-h)) 16px 80px;
}
.pf-scrim.pf-on{display:block;animation:pf-fade var(--dur-2) var(--ease-2)}
@keyframes pf-fade{from{opacity:0}to{opacity:1}}

.pf-sheet{
  max-width:840px;margin:0 auto;background:var(--bg-1);border:1px solid var(--line);
  border-radius:var(--r-5);overflow:hidden;
  box-shadow:var(--sh-4);
  animation:pf-rise var(--dur-3) var(--ease);
}
@keyframes pf-rise{from{transform:translateY(14px);opacity:.4}to{transform:none;opacity:1}}

/* ── chrome ── */
.pf-top{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1px solid var(--line);
  position:sticky;top:0;background:rgba(var(--bg1-rgb),.92);backdrop-filter:blur(14px);z-index:2}
.pf-tabs{display:flex;gap:4px}
.pf-tab{font-family:var(--mono);font-size:var(--fs-xs);letter-spacing:.06em;text-transform:uppercase;
  color:var(--dim);background:transparent;border:1px solid transparent;border-radius:var(--r-3);
  padding:6px 11px;cursor:pointer}
.pf-tab:hover{color:var(--txt-2)}
.pf-tab.pf-sel{color:var(--txt);background:var(--surface);border-color:var(--line-2)}
.pf-sp{flex:1}
.pf-x{background:transparent;border:1px solid var(--line-2);color:var(--dim);border-radius:var(--r-3);
  width:28px;height:28px;line-height:1;cursor:pointer;font-size:var(--fs-base)}
.pf-x:hover{color:var(--txt);border-color:var(--line-3)}

.pf-body{padding:20px 18px 26px}
.pf-body[hidden]{display:none}

/* ── identity ── */
.pf-head{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap}
.pf-name{font-size:30px;font-weight:600;letter-spacing:-.03em;line-height:1.1;
  word-break:break-word;min-width:0}
.pf-sub{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);margin-top:5px;letter-spacing:-.01em}

/* A red .pf-bot pill and a .pf-botnote panel used to sit here. Individual house
   accounts are no longer marked in the interface; the disclosure lives in rules.html
   §9 instead. Nothing renders these classes any more. */

/* level bar */
.pf-lvl{margin:16px 0 4px}
.pf-lvlrow{display:flex;justify-content:space-between;font-family:var(--mono);font-size:var(--fs-xs);
  color:var(--dim);margin-bottom:6px}
.pf-bar{height:7px;border-radius:var(--r-2);background:var(--surface);border:1px solid var(--line);overflow:hidden}
.pf-fill{height:100%;background:var(--acc);border-radius:var(--r-1);transition:width var(--dur-5) var(--ease)}

/* record ribbon */
.pf-titles{display:flex;flex-wrap:wrap;gap:6px;margin-top:14px}
.pf-title{font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc);background:var(--acc-soft);
  border:1px solid var(--acc-line);border-radius:var(--r-3);padding:4px 9px;white-space:nowrap}
.pf-title i{font-style:normal;color:var(--txt-2)}

/* ── stat grid ── */
.pf-grid{display:grid;gap:10px;margin-top:18px;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.pf-cell{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-4);padding:12px 13px}
.pf-k{font-family:var(--mono);font-size:var(--fs-mini);letter-spacing:.11em;text-transform:uppercase;color:var(--dim-2)}
.pf-v{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:var(--fs-2xl);font-weight:600;
  letter-spacing:-.03em;margin-top:6px;line-height:1.1;word-break:break-word}
.pf-v.pf-hot{color:var(--acc)}
.pf-n{font-size:var(--fs-xs);color:var(--dim);margin-top:4px;line-height:1.35}

/* ── trophies ── */
.pf-sec{margin-top:24px}
.pf-sech{display:flex;align-items:baseline;gap:9px;margin-bottom:10px}
.pf-sech h4{font-size:var(--fs-md);font-weight:600;letter-spacing:-.01em}
.pf-sech span{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim)}
.pf-trophies{display:flex;flex-wrap:wrap;gap:6px}
.pf-tro{font-family:var(--mono);font-size:var(--fs-xs);color:var(--txt-2);background:var(--surface);
  border:1px solid var(--line-2);border-radius:var(--r-3);padding:4px 9px}
.pf-tro.pf-t3{color:var(--acc);border-color:var(--acc-line);background:var(--acc-soft)}
.pf-tro.pf-t2{color:var(--txt)}

/* ── actions ── */
.pf-acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:22px}
.pf-btn{font:inherit;font-size:var(--fs-md);padding:9px 15px;border-radius:var(--r-4);cursor:pointer;
  background:var(--acc);color:var(--on-acc);border:0;font-weight:560}
.pf-btn:hover{filter:brightness(1.08)}
.pf-btn.pf-ghost{background:transparent;color:var(--txt-2);border:1px solid var(--line-2)}
.pf-btn.pf-ghost:hover{color:var(--txt);border-color:var(--line-3)}

/* ── hall of fame ── */
.pf-hofnote{font-size:var(--fs-sm);color:var(--txt-2);line-height:1.55;margin-bottom:4px}
.pf-hofnote b{color:var(--txt)}
.pf-reset{font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc)}
.pf-rec{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-4);
  padding:12px 13px;margin-top:10px}
.pf-rech{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.pf-rech b{font-size:var(--fs-md);font-weight:600}
.pf-rech em{font-style:normal;font-family:var(--mono);font-size:var(--fs-mini);color:var(--dim-2);
  letter-spacing:.08em;text-transform:uppercase}
.pf-row{display:flex;align-items:center;gap:10px;padding:7px 0;border-top:1px solid var(--line);
  font-size:var(--fs-md);margin-top:7px}
.pf-row:first-of-type{border-top:0}
.pf-pl{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim-2);width:18px;flex:none}
.pf-row.pf-1 .pf-pl{color:var(--acc)}
.pf-who{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  color:var(--txt-2);background:none;border:0;font:inherit;padding:0;text-align:left;cursor:pointer}
.pf-who:hover{color:var(--acc)}
.pf-val{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:var(--fs-md);font-weight:600;
  color:var(--txt);flex:none}
.pf-ctx{font-family:var(--mono);font-size:var(--fs-xs);color:var(--dim);flex:none}
.pf-open{font-family:var(--mono);font-size:var(--fs-xs);color:var(--acc);padding:9px 0 2px}

.pf-empty{font-size:var(--fs-sm);color:var(--dim);padding:10px 0}
.pf-err{font-size:var(--fs-md);color:var(--down);padding:18px 0}

/* The header entry point. Injected by profile.js rather than written into index.html,
   so this file and that one are the whole feature. */
.pf-open-btn{white-space:nowrap}

@media (max-width:560px){
  .pf-sheet{border-radius:var(--r-5)}
  .pf-name{font-size:var(--fs-2xl)}
  .pf-body{padding:16px 14px 22px}
  .pf-ctx{display:none}
}

/* /invite.css */
/* Invite loop.
 *
 * Every class here is prefixed `inv-`, without exception. app.js, games.js and their
 * stylesheets share one global scope and one cascade; `.rail` was defined in two files
 * once and the prize rail silently broke. A generic name in here would do the same.
 *
 * Everything comes from style.css's tokens — surfaces, the type scale, the one accent —
 * so this inherits the site's language instead of inventing a second one. Note in
 * particular that --up and --down appear NOWHERE below: style.css states that green and
 * red mean price direction and nothing else, so a paid invite is accent and one that paid
 * nothing is dim, rather than the success/danger pair every other product would reach for.
 */

.inv-wrap {
  border: 1px solid var(--line);
  border-radius: var(--r-5);
  background: linear-gradient(180deg, var(--acc-soft), transparent 70%), var(--surface);
  margin: 14px 0;
  overflow: hidden;
}
.inv-wrap.is-open { border-color: var(--acc-line); }

/* ── the always-visible bar ── */
.inv-head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 11px 14px;
  cursor: pointer;
  user-select: none;
  font-size: var(--fs-md);
}
.inv-head:hover { background: rgba(var(--light-rgb), .022); }

.inv-eyebrow {
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--dim-2);
}
.inv-badge {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  letter-spacing: .08em;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  color: var(--acc);
  border: 1px solid var(--acc-line);
  background: var(--acc-soft);
}
.inv-count { color: var(--dim); }
.inv-count b { color: var(--txt); }
.inv-earned { font-family: var(--mono); color: var(--acc); }
/* The goal-gradient line: a distance you can measure in actions is a distance you close.
   It is the last thing on the bar because it is the reason to open it. */
.inv-next {
  margin-left: auto;
  color: var(--dim);
  font-size: var(--fs-sm);
}
.inv-next b { color: var(--txt); }
.inv-chev { color: var(--dim-2); font-size: var(--fs-xs); }

/* ── the expanded panel ── */
.inv-panel { border-top: 1px solid var(--line); padding: 14px; }

.inv-linkrow {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.inv-link {
  flex: 1 1 260px;
  min-width: 0;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: var(--txt);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  padding: 9px 11px;
  /* The link is the product here. It must never be the thing that makes the page scroll
     sideways on a phone, and it must never be truncated to the point of being unusable —
     so it scrolls inside its own box. */
  overflow-x: auto;
  white-space: nowrap;
}

.inv-btn {
  font: inherit;
  font-size: var(--fs-sm);
  padding: 8px 13px;
  border-radius: var(--r-3);
  border: 1px solid transparent;
  background: var(--acc);
  color: var(--on-acc);
  font-weight: 600;
  cursor: pointer;
}
.inv-btn:hover { background: var(--acc-hi); }
.inv-ghost {
  background: transparent;
  color: var(--txt);
  border-color: var(--line-2);
  font-weight: 500;
}
.inv-ghost:hover { background: var(--surface-2); }

.inv-split {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}
@media (max-width: 700px) { .inv-split { grid-template-columns: minmax(0, 1fr); } }

.inv-preview {
  width: 100%;
  display: block;
  border-radius: var(--r-4);
  border: 1px solid var(--line);
}
.inv-cardnote {
  margin-top: 6px;
  font-size: var(--fs-xs);
  color: var(--dim-2);
}

.inv-term {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-md);
  color: var(--dim);
}
.inv-term b { font-family: var(--mono); color: var(--acc); }
.inv-fine {
  margin-top: 10px;
  font-size: var(--fs-xs);
  line-height: 1.55;
  color: var(--dim-2);
}
/* `.inv-warn` lived here to colour the daily cap notice amber. There is no cap on paid
   invites any more and no notice to colour, so the rule is gone rather than left behind
   as a class nothing sets. */

/* ── who joined ── */
.inv-list { margin-top: 16px; }
.inv-listhead {
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--dim-2);
  margin-bottom: 6px;
}
.inv-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 88px minmax(0, 1.4fr) auto;
  gap: 10px;
  align-items: center;
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-sm);
}
@media (max-width: 620px) {
  .inv-row { grid-template-columns: minmax(0, 1fr) auto; }
  .inv-row .inv-prog, .inv-row .inv-need { display: none; }
}
.inv-who { color: var(--txt); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inv-need { color: var(--dim-2); font-size: var(--fs-xs); }
.inv-paid { font-family: var(--mono); color: var(--dim); text-align: right; }
.inv-row.is-paid .inv-paid { color: var(--acc); }
/* Only ever a row written under the old daily ceiling. It counts towards the ladder and
   paid nothing, so it reads as dim rather than as a warning about a limit that is gone. */
.inv-row.is-capped .inv-paid { color: var(--dim-2); }

.inv-bar {
  height: 5px;
  border-radius: var(--r-1);
  background: var(--bg);
  border: 1px solid var(--line);
  overflow: hidden;
}
.inv-bar i {
  display: block;
  height: 100%;
  background: var(--acc);
}

.inv-empty {
  margin-top: 14px;
  font-size: var(--fs-sm);
  color: var(--dim);
  line-height: 1.6;
}

/* ── the ladder ── */
.inv-ladder {
  display: flex;
  gap: 8px;
  margin-top: 16px;
  /* Scrolls inside itself. A five-rung ladder must not be what makes the page wide. */
  overflow-x: auto;
  padding-bottom: 4px;
}
.inv-rung {
  position: relative;
  /* 118, not 108. The top rung reads +100,000 now that the ladder carries the scaling,
     and at --fs-md mono that is eight glyphs plus the sign against a 10px gutter. */
  flex: 0 0 118px;
  border: 1px solid var(--line);
  border-radius: var(--r-4);
  padding: 9px 10px;
  background: var(--bg-1);
}
.inv-rung.is-done {
  border-color: var(--acc-line);
  background: var(--acc-soft);
}
.inv-rank {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--txt);
}
.inv-at { font-size: var(--fs-xs); color: var(--dim-2); margin-top: 1px; }
.inv-rw { font-family: var(--mono); font-size: var(--fs-md); color: var(--acc); margin-top: 5px; }
.inv-tick {
  position: absolute;
  top: 7px;
  right: 8px;
  font-size: var(--fs-xs);
  color: var(--acc);
}

/* ── the arrival ribbon ──
   Shown to a visitor who followed somebody's link and has no account yet. Deliberately
   not a modal: onboarding.js already owns the signup flow, and two stacked modals was
   called out there as worse than either of them alone. */
.inv-arrive {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin: 14px 0;
  padding: 13px 16px;
  border: 1px solid var(--acc-line);
  border-radius: var(--r-5);
  background: linear-gradient(90deg, rgba(var(--acc-rgb), .13), rgba(var(--acc-rgb), .03));
}
.inv-arrivewho { font-size: var(--fs-md); color: var(--dim); display: grid; gap: 2px; }
.inv-arrivewho b { color: var(--txt); font-size: var(--fs-lg); }
.inv-arrivegift { display: flex; align-items: baseline; gap: 9px; flex: 1 1 240px; min-width: 0; }
.inv-big {
  font-family: var(--mono);
  font-size: var(--fs-4xl);
  font-weight: 600;
  color: var(--acc);
  line-height: 1;
}
.inv-sub { font-size: var(--fs-xs); color: var(--dim); line-height: 1.45; }
.inv-arrive .inv-btn { padding: 10px 18px; }

/* /happy.css */
/* HAPPY MINUTE — the site-wide 3x event.
   Owned by public/happy.js. Every class here is prefixed `hpm-` and nothing else on
   this site uses that prefix. That is not fussiness: `.rail` was declared in two
   stylesheets once and silently broke the prize rail, and `paintStrip()` existed in
   two files and silently rendered nothing. One namespace, no collisions.

   ── THE PERFORMANCE RULE THIS FILE OBEYS ────────────────────────────────────────
   The game floor was taken from 25.3ms a frame at dpr 2 down to 3.9ms, and the two
   things that bought nearly all of it were removing full-canvas gradients from the
   render loop and removing large blurs from moving elements. This event runs ON TOP
   of that floor, at exactly the moment ten Plinko balls are most likely to be in the
   air, so it may not spend the budget the floor just saved.

   So, without exception, in this file:
     * NO backdrop-filter. Anywhere. It is the single most expensive property here
       and a full-width bar over a live canvas is its worst case.
     * NO box-shadow, filter or blur on anything that animates. Static elements may
       carry a seated shadow; moving ones may not.
     * Every animation is transform or opacity ONLY, so it runs on the compositor and
       never triggers layout or paint. There is not one animated width, top, left,
       background-position or colour transition below.
     * The colour wash that marks the live state is a static gradient on a fixed
       element the compositor rasterises once, exactly as the game floor's stage
       backdrop was moved out of its render loop into CSS.

   ── LAYERING ────────────────────────────────────────────────────────────────────
   z-index 100. Above the header (40), the rail (30), modals (50), toasts (60) and
   the confetti canvas (70), because for seventy seconds this IS the page. Below the
   profile overlay (120) and the floor's level-up banner (210), both of which are
   deliberate interruptions of their own. */

/* ══════════════════════ THE BAR ══════════════════════ */

.hpm{
  position:fixed;left:0;right:0;top:0;z-index:100;
  pointer-events:none;                 /* it is an announcement, not a control */
  display:none;
  /* The whole bar arrives and leaves on one transform. Held here rather than on a
     child so the canvas band and the bar travel together as one layer. */
  will-change:transform;
}
.hpm[data-phase="warn"],
.hpm[data-phase="live"],
.hpm[data-phase="over"]{display:block}

/* THE SKY. This was a 200px band, and the reason it was a band is still true: a
   full-viewport canvas at dpr 1.5 on a 1440x900 screen is 2.9M pixels to CLEAR every
   frame, which is the whole 60fps budget before a single particle is drawn.

   What changed is that happy.js no longer clears the canvas. It clears the bounding
   box of what the previous frame actually drew, and nothing at all when the sky is
   empty, so the cost is now proportional to the fireworks rather than to the surface
   they are on. That is what pays for the height, and the height is what makes them
   fireworks rather than sparks: a shell has to have somewhere to rise to.

   Capped in px as well as in vh so a tall monitor does not buy a bigger clear for a
   burst that is the same size on every screen.

   The wash is a STATIC gradient on the canvas ELEMENT, not something drawn into it,
   for exactly the reason the game floor moved its stage wash into CSS: the compositor
   rasterises it once and never touches it again, where a full-canvas gradient built in
   a render loop measured 17.8ms a frame on its own. This is the top of the site
   visibly changing colour for a minute, and it costs one rasterisation. */
.hpm-fx{
  position:absolute;left:0;top:0;width:100%;
  height:min(52vh,440px);
  display:block;pointer-events:none;
  background:linear-gradient(180deg,
    rgba(var(--acc-rgb),.13) 0%,
    rgba(var(--acc-rgb),.05) 42%,
    rgba(var(--acc-rgb),0) 100%);
}
/* The count in has no fireworks in it on purpose, so its sky is shorter and amber. */
.hpm[data-phase="warn"] .hpm-fx{
  height:min(30vh,240px);
  background:linear-gradient(180deg,
    rgba(var(--warn-rgb),.13) 0%,
    rgba(var(--warn-rgb),0) 100%);
}
/* And the comedown goes red with the rest of it. */
.hpm.hpm-last .hpm-fx{
  background:linear-gradient(180deg,
    rgba(var(--down-rgb),.14) 0%,
    rgba(var(--down-rgb),.05) 42%,
    rgba(var(--down-rgb),0) 100%);
}
/* When it is over the sky is gone. The stillness is the whole point of the six
   seconds, and a glowing header over the words "happy minute over" is not still. */
.hpm[data-phase="over"] .hpm-fx{background:none;height:0}

.hpm-bar{
  position:relative;
  display:flex;align-items:center;gap:var(--sp-6);
  padding:var(--sp-4) var(--sp-7);
  min-height:60px;
  border-bottom:1px solid var(--acc-line);
  /* Static. Rasterised once by the compositor and never touched again. */
  background:
    linear-gradient(180deg, rgba(var(--acc-rgb),.20) 0%, rgba(var(--acc-rgb),.06) 100%),
    rgba(10,10,12,.94);
  animation:hpm-drop var(--dur-4) var(--ease-spring) both;
}
@keyframes hpm-drop{from{transform:translateY(-100%)}to{transform:translateY(0)}}

.hpm.hpm-going .hpm-bar{animation:hpm-lift var(--dur-4) var(--ease-2) both}
@keyframes hpm-lift{from{transform:translateY(0)}to{transform:translateY(-100%)}}

/* The sweep. One pseudo-element travelling across the bar on transform alone, which
   is the cheap way to do a shine — a moving background-position repaints the whole
   bar every frame and this does not repaint anything. */
.hpm-bar::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(100deg,
    rgba(var(--light-rgb),0) 38%,
    rgba(var(--light-rgb),.11) 50%,
    rgba(var(--light-rgb),0) 62%);
  transform:translateX(-100%);
  animation:hpm-sweep 2.6s linear infinite;
}
@keyframes hpm-sweep{to{transform:translateX(100%)}}

/* ── the multiplier chip ── */
.hpm-x{
  flex:0 0 auto;
  display:grid;place-items:center;
  min-width:58px;height:38px;padding:0 var(--sp-4);
  border-radius:var(--r-4);
  background:var(--acc);color:var(--on-acc);
  font-family:var(--mono);font-size:var(--fs-2xl);font-weight:700;
  letter-spacing:-.03em;
  animation:hpm-throb 1.1s var(--ease-2) infinite;
}
/* Scale only. A pulsing box-shadow here would be a repaint of a 58px box sixty times
   a second on top of a live game canvas, which is exactly the class of effect the
   floor's performance pass deleted. */
@keyframes hpm-throb{0%,100%{transform:scale(1)}50%{transform:scale(1.075)}}

/* ── the words ── */
.hpm-mid{flex:1 1 auto;min-width:0}
.hpm-title{
  font-size:var(--fs-lg);font-weight:700;letter-spacing:.10em;
  text-transform:uppercase;color:var(--txt);line-height:1.15;
}
/* It WRAPS. It used to be one nowrap line with an ellipsis on it, and the sentence
   it carries is the only place the event says what it pays and what it does not
   count toward. Cutting that mid-word is worse than a bar one line taller: the
   honesty clause is the half that gets cut, because it is at the end. The bar's
   height is measured after layout by setHeight(), so a second line is paid for by
   the page offset automatically and nothing ends up underneath it. */
.hpm-sub{
  font-size:var(--fs-sm);color:var(--txt-2);line-height:1.35;margin-top:2px;
}
/* The honesty clause. Dimmer than the pitch, never smaller than the site's floor for
   small text, and never hidden behind a tap. */
.hpm-fine{color:var(--dim);font-family:var(--mono);font-size:var(--fs-mini)}

/* ── the countdown ── */
.hpm-cd{
  flex:0 0 auto;text-align:right;font-family:var(--mono);
  display:flex;flex-direction:column;align-items:flex-end;gap:1px;
}
.hpm-cd b{
  font-size:var(--fs-3xl);font-weight:700;color:var(--txt);
  letter-spacing:-.04em;line-height:1;
  /* Tabular figures. Without this the countdown jitters sideways every second as the
     glyph widths change, which reads as a bug in the exact moment people are staring
     at it. */
  font-variant-numeric:tabular-nums;
}
.hpm-cd i{
  font-style:normal;font-size:var(--fs-micro);letter-spacing:.14em;
  text-transform:uppercase;color:var(--dim);
}

/* ── the room's running total ── */
.hpm-room{
  flex:0 0 auto;display:none;text-align:right;font-family:var(--mono);
  padding-left:var(--sp-6);border-left:1px solid var(--line-2);
}
.hpm-room.hpm-has{display:block}
.hpm-room b{display:block;font-size:var(--fs-lg);color:var(--acc);font-variant-numeric:tabular-nums}
.hpm-room i{
  font-style:normal;font-size:var(--fs-micro);letter-spacing:.14em;
  text-transform:uppercase;color:var(--dim);
}

/* ── the drain ──
   A single bar that empties over the minute. Driven by a transform on the X axis,
   set once per second from script, so it is one compositor property and never a
   layout. Animating `width` here would relayout the bar sixty times a second. */
.hpm-rail{
  position:relative;height:3px;overflow:hidden;
  background:rgba(var(--acc-rgb),.14);
}
.hpm-rail>i{
  display:block;height:100%;width:100%;
  transform-origin:left center;transform:scaleX(1);
  background:var(--acc);
  transition:transform 1s linear;
}

/* ══════════════════════ THE THREE PHASES ══════════════════════ */

/* WARNING. Amber rather than orange, so the pre-roll and the event itself are not
   the same picture — the bar visibly CHANGES at zero, which is what makes the start
   an event rather than a continuation. */
.hpm[data-phase="warn"] .hpm-bar{
  background:
    linear-gradient(180deg, rgba(var(--warn-rgb),.20) 0%, rgba(var(--warn-rgb),.05) 100%),
    rgba(10,10,12,.94);
  border-bottom-color:rgba(var(--warn-rgb),.45);
}
.hpm[data-phase="warn"] .hpm-x{background:var(--warn);animation-duration:.55s}
.hpm[data-phase="warn"] .hpm-cd b{color:var(--warn)}
.hpm[data-phase="warn"] .hpm-rail>i{background:var(--warn)}

/* LIVE. Nothing extra: the default styling above IS the live look. */

/* THE COMEDOWN. The last ten seconds have to feel like the last ten seconds, so the
   countdown takes over the bar: it grows, it goes red, and it beats once a second.
   The beat is scale only, and the size change is a font-size on a static element
   rather than an animated one, so neither costs a frame. */
.hpm.hpm-last .hpm-cd b{
  font-size:var(--fs-5xl);color:var(--down-txt);
  animation:hpm-beat 1s var(--ease-spring) infinite;
}
@keyframes hpm-beat{0%{transform:scale(1.22)}40%,100%{transform:scale(1)}}
.hpm.hpm-last .hpm-cd i{color:var(--down-txt)}
.hpm.hpm-last .hpm-bar{border-bottom-color:rgba(var(--down-rgb),.5)}
.hpm.hpm-last .hpm-rail>i{background:var(--down)}
.hpm.hpm-last .hpm-x{animation-duration:.5s}

/* OVER. Everything stops moving. That stillness is the point of the six seconds:
   the site was loud, and now it plainly is not. */
.hpm[data-phase="over"] .hpm-bar{
  background:linear-gradient(180deg, rgba(var(--surface-rgb),.98) 0%, rgba(10,10,12,.96) 100%);
  border-bottom-color:var(--line-2);
}
.hpm[data-phase="over"] .hpm-bar::after{animation:none;opacity:0}
.hpm[data-phase="over"] .hpm-x{background:var(--line-3);color:var(--txt-2);animation:none}
.hpm[data-phase="over"] .hpm-cd{display:none}
.hpm[data-phase="over"] .hpm-rail>i{transform:scaleX(0)}
.hpm[data-phase="over"] .hpm-title{color:var(--txt-2)}

/* ══════════════════════ THE REST OF THE SITE ══════════════════════
   A distinct look while it is live, done with ONE fixed, static, non-animating
   element rather than by touching anybody else's rules. Six agents share this
   repo; a happy minute that restyles `.card` is a happy minute that breaks a game.
   This is an inset frame in the accent colour: unmistakable, and it cannot reflow
   or repaint anything because it participates in no layout and never changes. */
body.hpm-live::after{
  content:"";position:fixed;inset:0;pointer-events:none;z-index:99;
  box-shadow:inset 0 0 0 2px rgba(var(--acc-rgb),.30);
}
body.hpm-last::after{box-shadow:inset 0 0 0 2px rgba(var(--down-rgb),.34)}

/* The corners, so the frame reads as the room being lit rather than as a border. Same
   element, one extra static gradient layer on it: a `box-shadow` here is seated on a
   fixed element that never moves and never animates, which is the one case the floor's
   performance rule allows, and the gradients are rasterised once with it. */
body.hpm-live::after{
  background:
    radial-gradient(80% 42% at 50% 0%, rgba(var(--acc-rgb),.11) 0%, rgba(var(--acc-rgb),0) 100%),
    radial-gradient(70% 34% at 50% 100%, rgba(var(--acc-rgb),.07) 0%, rgba(var(--acc-rgb),0) 100%);
}
body.hpm-last::after{
  background:
    radial-gradient(80% 42% at 50% 0%, rgba(var(--down-rgb),.12) 0%, rgba(var(--down-rgb),0) 100%),
    radial-gradient(70% 34% at 50% 100%, rgba(var(--down-rgb),.08) 0%, rgba(var(--down-rgb),0) 100%);
}

/* The page keeps its own space. One reflow when the bar arrives and one when it
   leaves, rather than a bar that sits on top of the header for seventy seconds and
   hides the balance somebody is trying to bet with. */
body.hpm-on{padding-top:var(--hpm-h,64px)}

/* AND THE STICKY BARS MOVE WITH IT, which the padding alone does not do.
   The padding was correct and it only held until the player scrolled. The header is
   `position:sticky; top:0`, the balance ledger sticks under it and the tab strip
   under that, so at any scroll offset at all the header re-pinned to y=0 — which is
   underneath a fixed 65px bar. Measured at 1400px with the event live and the page
   scrolled 900px: bar 0 to 65, header 0 to 57, ledger 57 to 100. The brand, the
   balance, the wallet chip, the claim button and the menu were all behind the banner
   for the whole minute, and `pointer-events:none` meant they still took the click,
   so the header was invisible rather than merely blocked. That is worse.

   So each sticky offset gains the bar's own height for as long as the bar is up.
   `top` on a sticky element changes once on arrival and once on departure, which is
   the same single reflow the padding already costs, and nothing here animates. The
   height comes from the same `--hpm-h` the padding uses, measured off the real bar by
   setHeight(), so the three can never disagree. */
body.hpm-on header{top:var(--hpm-h,64px)}
body.hpm-on .obledger{top:calc(var(--ob-top,56px) + var(--hpm-h,64px))}
body.hpm-on nav{top:calc(var(--nav-top,100px) + var(--hpm-h,64px))}
/* Below 700px the header stops sticking and the ledger takes over at top:0, so its
   offset there is the bar and nothing else. Same breakpoint as style.css's, not this
   file's 720, because it is that rule this one is answering. */
@media (max-width:700px){
  body.hpm-on .obledger{top:var(--hpm-h,64px)}
}

/* ══════════════════════ NARROW ══════════════════════
   Below 720px the sub-line and the room total are the first things to go: the
   countdown and the multiplier are the two things that must survive at any width,
   and the honesty clause moves onto its own line rather than being truncated away. */
@media (max-width:720px){
  .hpm-bar{gap:var(--sp-4);padding:var(--sp-3) var(--sp-5);min-height:54px}
  .hpm-title{font-size:var(--fs-md)}
  /* The clamp is gone. It said two lines and the sentence needs three at 375px, so
     what the phone actually got was "Bonus COINS do not count toward the SO", cut
     mid-word — the exact outcome the comment above this block says it is avoiding.
     The clause moves onto its own line instead, which is what was intended. */
  .hpm-sub{font-size:var(--fs-xs)}
  .hpm-fine{display:block}
  .hpm-cd b{font-size:var(--fs-2xl)}
  .hpm.hpm-last .hpm-cd b{font-size:var(--fs-4xl)}
  .hpm-room{display:none}
  .hpm-room.hpm-has{display:none}
  /* A phone gets a shorter sky, which is the cheaper clear AND the better picture:
     bursts sized for a 1440px screen fill a 375px one edge to edge and stop reading
     as separate fireworks. */
  .hpm-fx{height:min(42vh,300px)}
  .hpm[data-phase="warn"] .hpm-fx{height:min(24vh,170px)}
}

/* ══════════════════════ REDUCED MOTION ══════════════════════
   Everything decorative stops. The information does not: the bar, the words, the
   countdown and the drain all still work, because somebody who asked the operating
   system for less movement still needs to know the minute is running out. */
@media (prefers-reduced-motion:reduce){
  .hpm-bar,.hpm.hpm-going .hpm-bar{animation:none}
  .hpm-bar::after{display:none}
  .hpm-x,.hpm.hpm-last .hpm-cd b{animation:none}
  .hpm-rail>i{transition:none}
  /* The sky goes entirely. happy.js already refuses to start the frame loop under this
     setting, so nothing would ever be drawn into it, but a 440px gradient glowing over
     the top half of the page is itself the thing somebody asked not to have. Turned
     genuinely off, not softened: no canvas, no wash, no lit corners. */
  .hpm-fx{display:none}
  body.hpm-live::after,body.hpm-last::after{background:none}
}

/* /shop.css */
/* THE SHOP.
   Every class is `shp-` prefixed. app.js and games.js share one global scope and
   style.css and floor.css share one cascade; `.rail` was defined in two stylesheets once
   and broke the prize rail, and that is the whole reason for the prefix.

   Every value here is a token from style.css. If you are about to type a literal, the
   answer is almost always a variable that already exists. */

.shp-host { display: block; }

.shp-wrap {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 760px;
}

.shp-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.shp-head h2 {
  margin: 0;
  font-size: 18px;
  letter-spacing: .16em;
  text-transform: uppercase;
}
.shp-head p { margin: 6px 0 0; color: var(--dim); font-size: 12.5px; max-width: 52ch; }
.shp-bal {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--acc);
  border: 1px solid var(--acc-line);
  background: var(--acc-soft);
  border-radius: 999px;
  padding: 5px 12px;
  white-space: nowrap;
}

.shp-tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.shp-tabs button {
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--txt-2);
  border-radius: 999px;
  padding: 6px 13px;
  font-size: 12px;
  cursor: pointer;
}
.shp-tabs button.shp-on { border-color: var(--acc-line); background: var(--acc-soft); color: var(--acc); }

.shp-shelf {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.shp-shelf h4 {
  margin: 0;
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim-2);
}
.shp-blurb { margin: 0; font-size: 12.5px; color: var(--txt-2); line-height: 1.5; }
.shp-note, .shp-empty, .shp-signin { margin: 0; font-size: 12px; color: var(--dim); line-height: 1.5; }
.shp-fine { margin: 0; font-size: 11.5px; color: var(--dim-2); }
.shp-msg {
  margin: 0;
  font-size: 12.5px;
  color: var(--acc);
  border-left: 2px solid var(--acc-line);
  padding-left: 10px;
}

/* What is already bought, at the top of the shelf, because it is the number the whole
   shelf is about and a player should not have to add up ten rows to find it. */
.shp-now {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}
.shp-now-k { font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--dim-2); }
.shp-now b { font-family: var(--mono); font-size: 22px; color: var(--acc); }
.shp-now-of { font-size: 11.5px; color: var(--dim); }

/* The ladder. Owned rungs read as done, the next one carries the only live button, and
   the ones after it print their price and nothing else: the price IS the message, since
   the whole design is that each tier costs far more than the last. */
.shp-ladder { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 5px; }
.shp-rung {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 8px 11px;
}
.shp-rung-t { font-size: 11.5px; color: var(--dim); white-space: nowrap; }
.shp-rung-at { font-family: var(--mono); font-size: 13px; color: var(--txt-2); }
.shp-rung-act { justify-self: end; }
.shp-rung.shp-owned { border-color: var(--acc-line); background: var(--acc-soft); }
.shp-rung.shp-owned .shp-rung-at { color: var(--acc); }
.shp-rung.shp-next { border-color: var(--line-3); }
.shp-have { font-style: normal; font-size: 11px; color: var(--acc); }
.shp-later { font-style: normal; font-size: 11.5px; color: var(--dim-2); }

.shp-buy {
  background: var(--acc);
  color: var(--on-acc);
  border: none;
  border-radius: 9px;
  padding: 8px 13px;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.shp-buy:hover { background: var(--acc-hi); }
.shp-buy:disabled { opacity: .5; cursor: default; }
.shp-thin {
  background: var(--surface-2);
  color: var(--txt-2);
  border: 1px solid var(--line-2);
  font-weight: 500;
  white-space: normal;
  text-align: left;
}
.shp-thin:hover { background: var(--surface-2); border-color: var(--acc-line); color: var(--acc); }

.shp-counts {
  margin: 0;
  padding: 0 0 0 18px;
  list-style: disc;
  color: var(--dim);
  font-size: 12px;
  line-height: 1.55;
}

/* The quest desk. Both sides of every swap on one line. */
.shp-quests { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
.shp-quest {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 4px 10px;
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 9px 11px;
}
.shp-q-main { display: flex; flex-direction: column; min-width: 0; }
.shp-q-main b { font-size: 12.5px; color: var(--txt); }
.shp-q-main i { font-style: normal; font-size: 11.5px; color: var(--dim); overflow-wrap: anywhere; }
.shp-q-pay { font-family: var(--mono); font-size: 11.5px; color: var(--dim-2); white-space: nowrap; }
.shp-q-act { grid-column: 1 / -1; display: flex; }
.shp-q-act .shp-buy { width: 100%; }
.shp-swapnote { font-style: normal; font-size: 11px; color: var(--dim-2); }

/* The exclusions. Same treatment the level ladder gives its own: stated as plainly as
   the goods, in the same panel, never in a footnote somebody has to go looking for. */
.shp-does {
  margin: 0;
  padding: 12px 14px 12px 28px;
  list-style: disc;
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: 12px;
  color: var(--dim);
  font-size: 12px;
  line-height: 1.55;
}
.shp-does li + li { margin-top: 6px; }

/* The header chip app.js paints next to the balance. Lives here rather than in style.css
   because it is the shop's number and the shop is what makes it worth showing: below
   1.00x it is not drawn at all. */
.shp-multchip {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--acc);
  background: var(--acc-soft);
  border: 1px solid var(--acc-line);
  border-radius: 999px;
  padding: 3px 9px;
  cursor: pointer;
  white-space: nowrap;
}
.shp-multchip:hover { border-color: var(--acc); }
