/* ============================================================
   VoidBox Cyberverse — design system from the Figma file
   Fonts: Monoton (display) + Rajdhani (UI)
   Glass: rgba(0,240,255,.25) + backdrop blur, radius pills
   ============================================================ */

@font-face { font-family: 'Monoton'; src: url('../assets/fonts/monoton-400.woff2') format('woff2'); font-weight: 400; font-display: swap; }
/* Pixelify Sans (variable) — the Boombox mascot's speech-bubble voice */
@font-face { font-family: 'Pixelify Sans'; src: url('../assets/fonts/pixelify-sans.woff2') format('woff2'); font-weight: 400 700; font-display: swap; }
@font-face { font-family: 'Rajdhani'; src: url('../assets/fonts/rajdhani-500.woff2') format('woff2'); font-weight: 500; font-display: swap; }
@font-face { font-family: 'Rajdhani'; src: url('../assets/fonts/rajdhani-600.woff2') format('woff2'); font-weight: 600; font-display: swap; }
@font-face { font-family: 'Rajdhani'; src: url('../assets/fonts/rajdhani-700.woff2') format('woff2'); font-weight: 700; font-display: swap; }

:root {
  --cyan: #00F0FF;
  --red: #FF3030;
  --red-pure: #FF0000;
  --green: #00FF1A;
  --yellow: #FFD700;
  --blue: #2C00FF;
  /* Secondary/body text. Was #6E7BFF, a blue-violet that read as purple against
     the nebula. Now a soft cyan: same family as --cyan so nothing looks purple,
     but a step down from it so headings still lead. Used ONLY as a text colour
     (45 sites) — never a border, fill or gradient — so this is safe to retune.
     --purple is deliberately untouched: it's semantic (a player whose assigned
     colour IS purple, and the struck-through dead-player name). */
  --blue-soft: #7FE4FF;
  --orange: #FF8D28;
  --purple: #9A82DB;
  --dark: #0A0A2A;
  --glass: rgba(20, 150, 210, 0.15);
  --glass-strong: rgba(20, 160, 220, 0.23);
  --glass-red: rgba(255, 30, 40, 0.32);
  --glass-green: rgba(0, 255, 26, 0.20);
  --glass-yellow: rgba(255, 200, 40, 0.30);
  --rainbow: linear-gradient(90deg,#FF0000 0%,#FF8D28 14%,#FFD700 28%,#00FF1A 42%,#00F0FF 56%,#2C00FF 70%,#9A82DB 84%,#0A0A2A 98%);
  /* Separate ramp for TEXT. --rainbow is tuned for bars and ends on #0A0A2A —
     near-black — so the tail of any rainbow word disappeared into the nebula
     ("890 trophies to go" lost its last letters). Every stop here is a light
     tint of the same hue order, so the whole string stays readable while still
     reading as a rainbow. Bars keep the original, punchier gradient. */
  --rainbow-text: linear-gradient(90deg,#FF6B6B 0%,#FFA84D 15%,#FFE55C 30%,#5CFF7A 45%,#5CE8FF 60%,#7FA8FF 75%,#D6A8FF 90%,#FFC4F0 100%);
  --r: clamp(14px, 2.6vmin, 31px);       /* pill radius */
  --pad: clamp(8px, 1.6vmin, 20px);
  /* ---- liquid glass tokens ----
     Tuned to the reference: Light −45° @80%, Refraction 80, Depth 100,
     Dispersion 100, Frost 0 (CLEAR — no backdrop blur), Splay 100.
     Layered as backgrounds/shadows rather than pseudo-elements so they never
     fight the many absolutely-positioned panels. */
  /* ---- surface sheen ----
     This was `none`, on the reasoning that the refraction and the edge rim
     carried the look. They do — on the HIGH tier, with the cursor light on.
     At the default MEDIUM tier there is no backdrop filter and no cursor light,
     so all that was left was a 14%-opacity rectangle: reported, accurately, as
     "liquid glass is just transparent" and "the room screen feels flat".

     A static gradient fixes it for nothing. It is paint, not compositing — no
     backdrop sampling, no per-frame cost, identical on every tier — and it is
     what actually makes a flat fill read as a curved pane catching light:
     bright at the top-left where the light is, falling to nothing across the
     middle, with a faint bounce along the bottom edge. */
  --sheen: linear-gradient(146deg,
    rgba(255,255,255,.11) 0%,
    rgba(255,255,255,.04) 22%,
    rgba(255,255,255,0) 48%,
    rgba(130,205,255,.035) 74%,
    rgba(255,255,255,.065) 100%);
  /* iOS-style edge light: the bright rim + chromatic glow ride the edge FACING
     the cursor, the shadow rides the far edge. app.js sets the direction
     (--edge-dx/dy = crisp rim, --edge-gx/gy = softer glow, same direction).
     Defaults point top-left (the −45° rest position, cursor idle). */
  --edge-dx: 1.5px;  --edge-dy: 1.5px;
  --edge-gx: 5px;    --edge-gy: 5px;
  --glass-edge:
    inset var(--edge-dx) var(--edge-dy) 0 rgba(255,255,255,.7),
    inset calc(var(--edge-dx) * -1) calc(var(--edge-dy) * -1) 1.5px rgba(2,6,32,.45),
    inset var(--edge-gx) var(--edge-gy) 16px rgba(140,225,255,.26),
    inset calc(var(--edge-gx) * -1) calc(var(--edge-gy) * -1) 16px rgba(255,120,200,.16);
  /* ---- elevation ----
     What makes a surface read as sitting ON another one rather than being
     painted onto it.

     Each level is TWO shadows, not one, because that is how a real object
     behaves: a tight, dark CONTACT shadow right under the edge that says where
     it touches, plus a wide soft AMBIENT shadow that says how far above it is.
     A single big blur only ever reads as "floating"; the contact shadow is the
     half that makes something feel put down on top of something else.

     The scale is by proximity, not importance: e1 is a control resting on a
     panel, e4 is a panel resting on the background. */
  --e1: 0 1px 2px rgba(2,6,24,.42), 0 2px 5px rgba(2,6,24,.24);
  --e2: 0 2px 4px rgba(2,6,24,.46), 0 6px 15px rgba(2,6,24,.30);
  --e3: 0 4px 8px rgba(2,6,24,.48), 0 14px 30px rgba(2,6,24,.34);
  --e4: 0 8px 16px rgba(2,6,24,.50), 0 26px 56px rgba(2,6,24,.40);

  /* Thickness. A bright line along the top edge and a dark one along the
     bottom is the whole trick behind an object looking like it has a side you
     could catch a fingernail on. Purely inset, so it costs nothing and never
     affects layout. */
  --bevel:
    inset 0 1.5px 0 rgba(255,255,255,.40),
    inset 0 -1.5px 0 rgba(0,8,34,.55);
  /* The same idea for something small and close, where 1.5px would look chunky. */
  --bevel-sm:
    inset 0 1px 0 rgba(255,255,255,.34),
    inset 0 -1px 0 rgba(0,8,34,.48);

  --spring: cubic-bezier(.22, 1, .36, 1);          /* smooth ease-out */
  --spring-back: cubic-bezier(.34, 1.56, .64, 1);  /* slight overshoot */
}

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body { height: 100%; overflow: hidden; }
html { background: #060618; }

/* The nebula lives on its own fixed layer instead of `background-attachment:
   fixed` on <body> — WebKit sizes fixed-attachment backgrounds unreliably in a
   standalone (Home Screen) web app, which left black bands above and below the
   art on iPhone. A real fixed element always covers the visual viewport. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: #060618 url('../assets/bg-nebula.png') center/cover no-repeat;
}

body {
  font-family: 'Rajdhani', sans-serif;
  /* transparent, not #060618 — an opaque body background paints *over* the
     z-index:-1 ::before layer below. The base colour lives on <html>. */
  background: transparent;
  color: var(--cyan);
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
}
/* …but never on text fields. iOS suppresses the long-press Select/Paste callout
   inside a `user-select: none` subtree, which made it impossible to paste a
   Discord ID (or a room password) on iPhone. */
input, textarea {
  user-select: text;
  -webkit-user-select: text;
  -webkit-touch-callout: default;
}

/* ============ Lobby loading screen ============ */
#loader {
  position: fixed; inset: 0; z-index: 4000; display: grid; place-items: center;
  background:
    radial-gradient(900px 600px at 50% 40%, rgba(0, 240, 255, .10), transparent 60%),
    #04060f url('../assets/bg-nebula.png') center/cover no-repeat;
  transition: opacity .6s ease;
}
#loader.gone { opacity: 0; pointer-events: none; }
#loader::after {            /* faint scanlines for a cyber feel (static) */
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background: repeating-linear-gradient(0deg, rgba(0,240,255,.045) 0 1px, transparent 1px 3px);
}
.ld-inner { position: relative; display: flex; flex-direction: column; align-items: center; gap: clamp(16px, 3vh, 30px);
  width: min(88vw, 460px); text-align: center; }
.ld-hex { width: clamp(96px, 20vh, 150px); height: clamp(96px, 20vh, 150px);
  filter: drop-shadow(0 0 18px rgba(0, 240, 255, .55)); }
.ld-hex-o { fill: none; stroke: var(--cyan); stroke-width: 2.5; animation: ld-pulse 2.2s ease-in-out infinite; }
.ld-hex-i { fill: none; stroke: rgba(255, 215, 0, .6); stroke-width: 1.5; animation: ld-pulse 2.2s ease-in-out infinite .5s; }
.ld-orbit { fill: var(--cyan); filter: drop-shadow(0 0 6px var(--cyan)); }
.ld-vb { font-family: 'Monoton', cursive; font-size: 30px; fill: var(--cyan); }
@keyframes ld-pulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
.ld-title { font-family: 'Monoton', cursive; color: var(--cyan); line-height: 1.1;
  font-size: clamp(22px, 5vw, 40px); text-shadow: 0 0 28px rgba(0, 240, 255, .6); letter-spacing: 1px; }
.ld-bar { width: 100%; height: 12px; border-radius: 999px; overflow: hidden;
  background: rgba(4, 10, 30, .6); border: 1px solid rgba(0, 240, 255, .3); }
.ld-fill { height: 100%; width: 0%; border-radius: 999px;
  background: linear-gradient(90deg, #00F0FF, #6E7BFF); box-shadow: 0 0 16px rgba(0, 240, 255, .6);
  transition: width .35s ease; }
.ld-status { display: flex; justify-content: space-between; width: 100%; color: var(--cyan);
  font-weight: 700; font-size: clamp(12px, 2vw, 16px); letter-spacing: .5px; }
.ld-status #ld-pct { color: #fff; }

body.flicker::after {
  content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 999;
  background: rgba(160, 220, 255, 0.05);
  animation: flick 3.7s infinite steps(1);
}
@keyframes flick {
  0%, 91%, 94%, 100% { opacity: 0; }
  92%, 95% { opacity: 1; }
}

#app { position: fixed; inset: 0; display: flex; }

/* ---------- design-space stage (2109x1406), scaled to fit for pixel-match ---------- */
.stage-host { padding: 0 !important; }
/* Keep the scaled stage clear of hardware cutouts — the notch / Dynamic Island
   on iPhone, a hole-punch camera on Android, the MacBook notch in fullscreen.
   The nebula still fills the whole screen (its own fixed layer); only the interactive
   stage is inset, and fitStage() re-scales into whatever box is left. */
.stage-fit { position: absolute; overflow: hidden;
  top: env(safe-area-inset-top); right: max(env(safe-area-inset-right), var(--safe-x, 0px));
  bottom: env(safe-area-inset-bottom); left: max(env(safe-area-inset-left), var(--safe-x, 0px)); }
/* …except in landscape, where the vertical insets are pure loss. The stage is
   height-limited (2109x1406 into a ~2.2:1 phone viewport), so every px of
   top/bottom inset shrinks the WHOLE UI, while the ~117px of spare margin on
   each side means the left/right insets cost nothing. The cutout is on a side
   in landscape anyway — and iOS reports stale *portrait* insets (59 top +
   34 bottom, ~24% of the height) in standalone mode after a rotation, which is
   what was letterboxing the stage with black bands.
   Scoped to .mobile so a MacBook in fullscreen keeps its notch clearance. */
body.mobile.landscape .stage-fit { top: 0; bottom: 0; }
.stage {
  position: absolute; left: 50%; top: 50%;
  width: 2109px; height: 1406px; transform-origin: center center;
}
.stage .spr { display: block; width: 100%; height: 100%; object-fit: contain; }
.spr { display: inline-block; max-width: 100%; max-height: 100%; object-fit: contain; }

/* design-space typography helpers (px are design px inside .stage) */
.stage .d-mono { font-family: 'Monoton', cursive; color: var(--cyan);
  text-shadow: 0 0 22px rgba(0,240,255,.55), 0 0 6px rgba(0,240,255,.8); }
.stage .d-rj { font-family: 'Rajdhani', sans-serif; font-weight: 600; }
.stage .d-panel {
  background: var(--sheen), rgba(30, 150, 210, 0.20);
  border: 2px solid rgba(170, 240, 255, 0.5);
  border-radius: 44px;
  /* Frost 0 — clear refractive glass, same recipe as .glass */
  backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
  -webkit-backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
  box-shadow: var(--glass-edge), var(--bevel), var(--e4);
}
.stage .d-btn { cursor: pointer;
  transition: filter .18s ease, transform .22s var(--spring-back), box-shadow .28s var(--spring); }
.stage .d-btn:hover { filter: brightness(1.22); transform: translateY(-4px) scale(1.015);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.55), 0 16px 40px rgba(0,0,0,.34), 0 0 34px rgba(0,240,255,.32); }
.stage .d-btn:active {
  transform: translateY(1px) scale(.975);
  box-shadow:
    inset 0 -1px 0 rgba(255,255,255,.26),
    inset 0 1.5px 0 rgba(0,8,34,.5),
    var(--e1);
}

/* ---------- typography ---------- */
.mono { font-family: 'Monoton', cursive; font-weight: 400; letter-spacing: 0.02em; }
.rj { font-family: 'Rajdhani', sans-serif; font-weight: 600; }
.cyan { color: var(--cyan); }
.red { color: var(--red); }
.green { color: var(--green); }
.yellow { color: var(--yellow); }
.blue { color: var(--blue-soft); }
.white { color: #fff; }
.glow { text-shadow: 0 0 18px rgba(0, 240, 255, 0.6); }
.rainbow-text {
  background: var(--rainbow-text);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  /* text-shadow can't be used on background-clip:text (the fill is transparent,
     so the shadow shows straight through the glyphs). drop-shadow filters the
     rendered result instead, giving a real dark outline that lifts the text off
     the busy background. */
  filter: drop-shadow(0 1px 1px rgba(0,0,0,.95)) drop-shadow(0 0 5px rgba(0,0,0,.8));
}

/* ---------- glass panels & buttons ---------- */
.glass {
  background: var(--sheen), var(--glass);
  border-radius: var(--r);
  /* Frost 0 — NO blur. Refraction 80 is suggested by lifting what shows through
     (saturate/contrast/brightness) instead of frosting it out. */
  backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
  -webkit-backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
  border: 1px solid rgba(170, 240, 255, 0.5);
  box-shadow: var(--glass-edge), var(--bevel), var(--e2);
  /* box-shadow kept short so the cursor-tracked edge rim follows responsively
     (it re-targets every frame); the rest keep the softer spring for hovers */
  transition: box-shadow .16s ease-out, border-color .35s var(--spring),
              background-color .35s var(--spring), filter .2s ease, transform .2s var(--spring);
}
.red-glass { background: var(--sheen), var(--glass-red); border-color: rgba(255, 90, 90, 0.5); box-shadow: var(--glass-edge), 0 10px 34px rgba(0,0,0,.3), inset 0 0 18px rgba(255,0,0,.12); }
.green-glass { background: var(--sheen), var(--glass-green); border-color: rgba(80, 255, 110, 0.45); box-shadow: var(--glass-edge), 0 10px 34px rgba(0,0,0,.3), inset 0 0 18px rgba(0,255,26,.12); }
.yellow-glass { background: var(--sheen), var(--glass-yellow); border-color: rgba(255, 225, 90, 0.55); box-shadow: var(--glass-edge), 0 10px 34px rgba(0,0,0,.3), inset 0 0 18px rgba(255,200,0,.12); }
.rainbow-bg { background-image: var(--rainbow); background-size: 400% 400%; }

button {
  font: inherit; color: inherit; cursor: pointer; border: none; background: none;
}
button.glass { transition: transform 0.12s, filter 0.12s; }
button.glass:hover { filter: brightness(1.25); }
/* Pressing something should look like pressing it.
 *
 * The shadow COLLAPSES to the contact level and the surface moves down by a
 * pixel — the object is being pushed toward the thing it was resting on. Scale
 * alone reads as a picture shrinking; losing the ambient shadow is what makes
 * it read as travel. The bevel flips too, so the lit edge is briefly on the
 * bottom, exactly as a real bevel would be once it tips under the light. */
button.glass:active {
  transform: translateY(1px) scale(0.985);
  box-shadow:
    inset 0 -1px 0 rgba(255,255,255,.28),
    inset 0 1.5px 0 rgba(0,8,34,.5),
    var(--e1);
}

/* ---------- screens ---------- */
.screen {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  /* base padding, but never less than the device's safe-area inset on any side
     (stage screens override this to 0 and inset .stage-fit instead) */
  padding:
    max(clamp(10px, 2.5vmin, 30px), env(safe-area-inset-top))
    max(clamp(10px, 2.5vmin, 30px), env(safe-area-inset-right), var(--safe-x, 0px))
    max(clamp(10px, 2.5vmin, 30px), env(safe-area-inset-bottom))
    max(clamp(10px, 2.5vmin, 30px), env(safe-area-inset-left), var(--safe-x, 0px));
  overflow: hidden;
}
/* Same deal as .stage-fit: in landscape the only real cutout is on a side, so
   don't spend vertical space on a (possibly stale portrait) top/bottom inset. */
body.mobile.landscape .screen {
  padding-top: clamp(10px, 2.5vmin, 30px);
  padding-bottom: clamp(10px, 2.5vmin, 30px);
}
.hidden { display: none !important; }

/* ---- the corner Exit ----
 *
 * This is absolutely positioned OVER a scrolling region (.shop-grid is
 * overflow-y:auto), so mid-scroll the shop's own Claim/Buy buttons pass
 * underneath the letters and the two render on top of each other. That was a
 * real, reported visual bug and padding cannot fix it: bottom padding only
 * clears the END of the scroll, and the collision happens in the middle of it.
 *
 * The fix is for the button to stop being transparent. It now sits on its own
 * glass pill, so whatever scrolls behind is blurred rather than smeared
 * through the glyphs. */
.corner-link {
  position: absolute; left: clamp(12px, 3vmin, 40px); bottom: clamp(10px, 2.5vmin, 30px);
  font-family: 'Monoton', cursive; font-size: clamp(22px, 4.2vmin, 44px);
  color: var(--cyan); text-shadow: 0 0 16px rgba(0,240,255,.5);
  z-index: 5;
  padding: .18em .62em; border-radius: 999px; line-height: 1.12;
}
.corner-link:hover { filter: brightness(1.4); }

/* ---------- bars ---------- */
.bar { height: clamp(16px, 3vmin, 34px); border-radius: 999px; overflow: hidden; padding: 3px; }
.bar.big { height: clamp(22px, 4.4vmin, 44px); }
.bar-fill { height: 100%; border-radius: 999px; width: 0%; transition: width 0.6s ease; }
.bar-fill.rainbow { background-image: var(--rainbow); }
.bar-fill.cyan-fill { background: var(--cyan); box-shadow: 0 0 14px var(--cyan); }

/* ---------- toggle (design: red OFF pill / green ON pill + white knob) ---------- */
.toggle {
  width: clamp(52px, 8vmin, 84px); height: clamp(28px, 4.2vmin, 42px);
  border-radius: 999px; position: relative; flex: none;
  background: var(--glass-red); border: 1px solid rgba(255,80,80,.5);
  transition: background .35s cubic-bezier(.22,1,.36,1),
              border-color .35s cubic-bezier(.22,1,.36,1);
}
.toggle::after {
  content: ''; position: absolute; top: 50%; transform: translateY(-50%);
  width: clamp(18px, 3vmin, 30px); height: clamp(18px, 3vmin, 30px);
  border-radius: 50%; background: rgba(255,255,255,0.85);
  box-shadow: 0 2px 6px rgba(0,0,0,.3);
  left: 12%; transition: left .35s cubic-bezier(.34,1.56,.64,1), width .2s ease;
}
.toggle:active::after { width: clamp(21px, 3.5vmin, 35px); }
.toggle.on { background-color: rgba(81, 255, 0, 0.4); border-color: rgba(81,255,0,.5); }
.toggle.on::after { left: 55%; }
/* Settings toggles.
 *
 * These were held at a flat .16s ease on the reasoning that a settings switch
 * should feel snappy. Snappy and satisfying are not the same thing: .16s
 * linear-ish is quick enough to feel abrupt and cheap, and there is nothing to
 * watch. These now travel on a spring with a small overshoot, and the knob
 * stretches into the movement and settles — the physical read of something
 * with a bit of mass being flicked across.
 *
 * All of it is transform/colour on one small element, so it stays on the
 * compositor no matter how many rows are on screen. */
#screen-settings .toggle {
  transition: background-color .3s var(--spring), border-color .3s var(--spring);
}
#screen-settings .toggle::after {
  transition: left .34s var(--spring-back),
              width .22s var(--spring-back),
              box-shadow .3s ease;
}
/* Pressed: the knob squashes toward the track, like a real key taking load. */
#screen-settings .toggle:active::after {
  width: clamp(24px, 4vmin, 40px);
  box-shadow: 0 1px 3px rgba(0,0,0,.45);
}
/* Settled in the ON position it sits a touch prouder — the lit side of the
   switch is the side the light is on. */
#screen-settings .toggle.on::after {
  box-shadow: 0 3px 9px rgba(0,0,0,.34), 0 0 12px rgba(120,255,90,.45);
}
@media (prefers-reduced-motion: reduce) {
  #screen-settings .toggle,
  #screen-settings .toggle::after { transition-duration: .01ms; }
}

/* ---------- checkbox (green check / red cross squares) ---------- */
.checkbox {
  width: clamp(30px, 4.6vmin, 50px); height: clamp(30px, 4.6vmin, 50px);
  border-radius: 30%; flex: none;
  background: rgba(255, 0, 0, 0.25); position: relative;
}
.checkbox::after {
  content: '✕'; position: absolute; inset: 0; display: grid; place-items: center;
  color: #FF383C; font-weight: 700; font-size: 60%;
}
.checkbox.on { background-color: rgba(0, 255, 26, 0.25); }
.checkbox.on::after { content: '✓'; color: #00c414; }

/* ---------- steppers (value + yellow/blue arrows) ---------- */
.stepper { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.stepper .num { font-family: 'Monoton', cursive; font-size: clamp(26px, 5vmin, 56px); line-height: 1; }
.rule-card .num {
  font-family: 'Monoton', cursive; font-size: clamp(48px, 9vmin, 110px); line-height: 1;
  /* every game's room-setting numbers are cyan (#00F0FF) */
  color: var(--cyan); text-shadow: 0 0 18px rgba(0, 240, 255, .55);
}
.lobby-right.nums-cyan .num { color: var(--cyan); text-shadow: 0 0 18px rgba(0, 240, 255, .55); }
/* Custom (host-picked) role amounts read yellow to set them apart from the
   auto-balanced cyan defaults. */
.rule-grid.custom-amt .num { color: var(--yellow); text-shadow: 0 0 18px rgba(255, 215, 0, .55); }
.stepper .lbl { font-family: 'Rajdhani'; font-weight: 600; font-size: clamp(11px, 1.8vmin, 16px); opacity: .9; }
.stepper-box { display: flex; align-items: center; gap: 8px; padding: var(--pad); border-radius: var(--r); }
.arrows { display: flex; flex-direction: column; gap: 4px; }
.arrow-btn {
  width: clamp(34px, 5vmin, 56px); height: clamp(22px, 3.4vmin, 36px);
  border-radius: 12px; display: grid; place-items: center;
  background: rgba(0, 240, 255, 0.18); color: var(--cyan); font-weight: 700;
  font-size: clamp(14px, 2.4vmin, 22px);
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
.arrow-btn.blue-arrow { background-color: rgba(0, 240, 255, 0.2); color: var(--blue-soft); }
/* At the free ceiling the up arrow goes gold — pushing past it is a Pro perk. */
.arrow-btn.pro-lock { background-color: rgba(255, 215, 0, .42); color: #FFD700;
  box-shadow: 0 0 0 2px rgba(255, 215, 0, .65), 0 0 18px rgba(255, 215, 0, .55); }
.arrow-btn:active { transform: scale(0.92); }

/* ============================================================
   MAIN SCREEN (design-space 2109x1406, inside .stage)
   ============================================================ */
@keyframes neonpulse {
  0%, 100% { text-shadow: 0 0 18px rgba(0,240,255,.65); }
  50% { text-shadow: 0 0 34px rgba(0,240,255,.95); }
}
/* The glow pulse animates `text-shadow`, which is a CPU repaint every frame.
   Promote the title to its own compositor layer so that repaint stays on the
   title's own layer instead of invalidating the refraction panels beneath it
   (which would re-run their backdrop-filter 60×/s and tank lobby FPS). */
.stage .ms-title { font-size: 130px; text-align: center; animation: neonpulse 4s ease-in-out infinite;
  will-change: transform; }
/* Icon art is cropped flush to its content (no baked-in transparent margin), so
   the tile gives it nearly the whole square — just enough padding to keep the
   glow off the rounded corners. */
.stage .ms-tile { display: grid; place-items: center; padding: 10px; }
.stage .ms-tile-ico { width: 100%; height: 100%; }
.stage .ms-tile-ico .spr { width: 100%; height: 100%; object-fit: contain;
  filter: drop-shadow(0 0 10px rgba(0,240,255,.45)); }
.stage .ms-trophy-panel {
  display: flex; flex-direction: column; padding: 40px 50px; cursor: pointer;
}
.stage .ms-trophy-top { display: flex; align-items: center; flex: 1; gap: 20px; }
.stage .ms-trophy-num { font-size: 180px; line-height: 1; flex: 1; }
.stage .ms-trophy-ico { width: 380px; height: 300px; flex: none; }
.stage .ms-trophy-ico .spr { width: 100%; height: 100%; object-fit: contain;
  filter: drop-shadow(0 0 14px rgba(0,240,255,.55)); }
.stage .ms-bar-wrap { width: 100%; margin-top: 16px; }
.stage .ms-bar-inner {
  height: 56px; border-radius: 999px; overflow: hidden;
  background: rgba(0,10,30,.35); border: 2px solid rgba(120,230,255,.28); padding: 6px;
}
.stage .ms-bar-fill { height: 100%; border-radius: 999px; width: 0%;
  transition: width .7s cubic-bezier(.22,1,.36,1); }
.stage .ms-bar-fill.rainbow { background-image: var(--rainbow); }

/* Earning trophies: the icon bounces and the number flashes gold. The `.earn`
   class is toggled on the panel in refreshMainScreen() only when the count goes
   up. (The low graphics tier strips all animation, so this no-ops there.) */
@keyframes ms-trophy-pop {
  0%   { transform: scale(1) rotate(0); }
  35%  { transform: scale(1.16) rotate(-4deg); }
  60%  { transform: scale(.98) rotate(2deg); }
  100% { transform: scale(1) rotate(0); }
}
@keyframes ms-num-flash {
  0%, 100% { text-shadow: none; }
  40%      { text-shadow: 0 0 26px rgba(255,225,90,.9), 0 0 60px rgba(255,180,0,.55); }
}
.stage .ms-trophy-panel.earn .ms-trophy-ico { animation: ms-trophy-pop .7s cubic-bezier(.34,1.56,.64,1); }
.stage .ms-trophy-panel.earn .ms-trophy-num { animation: ms-num-flash .7s ease; }
.stage .ms-lvl-panel { display: flex; flex-direction: column; padding: 30px 40px; gap: 6px; cursor: pointer; }
.stage .ms-lvl-text { font-size: 90px; line-height: 1; }
.stage .ms-xp-text { font-size: 38px; color: var(--cyan); }
.stage .ms-play { display: grid; place-items: center; }
.stage .ms-play-text { font-size: 100px; color: var(--green); text-shadow: 0 0 16px rgba(0,255,26,.6); }

/* avatar disc (used in lobby and elsewhere) */
.avatar-disc {
  width: clamp(34px, 6vmin, 64px); height: clamp(34px, 6vmin, 64px);
  border-radius: 50%; background: #fff center/58% no-repeat;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127 96"><path fill="%235865F2" d="M107.7 8.07A105.15 105.15 0 0 0 81.47 0a72.06 72.06 0 0 0-3.36 6.83 97.68 97.68 0 0 0-29.11 0A72.37 72.37 0 0 0 45.64 0a105.89 105.89 0 0 0-26.25 8.09C2.79 32.65-1.71 56.6.54 80.21a105.73 105.73 0 0 0 32.17 16.15 77.7 77.7 0 0 0 6.89-11.11 68.42 68.42 0 0 1-10.85-5.18c.91-.66 1.8-1.34 2.66-2a75.57 75.57 0 0 0 64.32 0c.87.71 1.76 1.39 2.66 2a68.68 68.68 0 0 1-10.87 5.19 77 77 0 0 0 6.89 11.1 105.25 105.25 0 0 0 32.19-16.14c2.64-27.38-4.51-51.11-18.9-72.15ZM42.45 65.69C36.18 65.69 31 60 31 53s5-12.74 11.43-12.74S54 46 53.89 53s-5.05 12.69-11.44 12.69Zm42.24 0C78.41 65.69 73.25 60 73.25 53s5-12.74 11.44-12.74S96.23 46 96.12 53s-5.04 12.69-11.43 12.69Z"/></svg>');
  flex: none;
}
.avatar-disc.small { width: clamp(26px, 4.4vmin, 46px); height: clamp(26px, 4.4vmin, 46px); }
/* real Discord avatar painted on the disc (background set inline) */
.avatar-disc { position: relative; }
.avatar-disc.has-av { background-color: #1a2140; background-size: cover; }
/* avatar decoration — Discord frame; shown ONLY on the high graphics tier */
.avatar-deco { display: none; position: absolute; left: 50%; top: 50%;
  width: 138%; height: 138%; transform: translate(-50%, -50%); pointer-events: none; z-index: 2; }
/* Own setting (Settings ▸ Avatar effects) rather than the graphics tier — many
   Discord frames are animated APNGs and a lobby full of them is costly. */
body.avatar-fx .avatar-deco { display: block; }

/* ============================================================
   SETTINGS
   ============================================================ */
#screen-settings h1 { text-align: center; font-size: clamp(22px, 5vmin, 56px); }
/* Row spacing. 1.6vmin put a label chip, its control and its hint almost
   touching the next row's, which read as one dense block rather than a list of
   separate settings. The gap is now larger than the gap INSIDE a row, so the
   grouping is unambiguous: things closer together belong together. */
.settings-body { flex: 1; overflow-y: auto; display: flex; flex-direction: column;
  gap: 2.9vmin; margin-top: 2.2vmin; padding-bottom: 12vmin; }
/* A scrolling pane meets the screen edge with a hard slice — on a short phone
   (worst in a chrome-free Home Screen launch, where the pane runs to the very
   bottom) a settings row gets cut exactly in half and reads as a glitch. Fade
   the last few px instead. `.has-more` is toggled from JS so the fade is only
   present while there IS more below — otherwise the final row would sit
   permanently half-faded. */
.scroll-fade.has-more {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 42px), transparent 100%);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 42px), transparent 100%);
}
/* clear the floating Exit link so the last row never ends up underneath it */
body.mobile.landscape .settings-body { padding-bottom: 18vmin; }
.section-header { text-align: center; padding: clamp(8px, 1.8vmin, 20px);
  /* A heading belongs to what follows it, so it sits closer to the group below
     than to the one it just ended. */
  margin-top: 1.8vmin; margin-bottom: -0.4vmin; }
.settings-body > .section-header:first-child { margin-top: 0; }
.section-header .mono { font-size: clamp(20px, 4.4vmin, 50px); }
.settings-row { display: flex; align-items: center; gap: 1.9vmin; flex-wrap: wrap;
  /* Room to breathe when a row wraps onto two lines on a narrow screen. */
  row-gap: 1.2vmin; }
.label-chip { padding: clamp(8px, 1.8vmin, 22px) clamp(14px, 3vmin, 36px); font-size: clamp(16px, 3.4vmin, 40px); }
.value-chip { padding: clamp(8px, 1.8vmin, 22px) clamp(12px, 2.4vmin, 28px); font-size: clamp(13px, 2.4vmin, 28px); }
.hint-chip { padding: clamp(6px, 1.2vmin, 14px) clamp(10px, 2vmin, 24px); font-size: clamp(11px, 2vmin, 22px); }
.slider-wrap { flex: 1; min-width: 160px; padding: clamp(10px, 2vmin, 26px); display: flex; align-items: center; }
.divider { height: 6px; border-radius: 999px; margin: 0.6vmin 0; }

input[type=range] { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; border-radius: 3px; background: rgba(0,0,0,.5); outline: none; }
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; width: clamp(22px, 3.4vmin, 38px); height: clamp(14px, 2.4vmin, 24px);
  border-radius: 8px; background: rgba(45, 1, 252, 0.55); border: 1px solid var(--red-pure);
  box-shadow: 0 4px 10px var(--cyan), 0 -4px 10px var(--cyan); cursor: pointer;
}

/* ============================================================
   ROOM SEARCH
   ============================================================ */
.search-top {
  display: flex; align-items: center; gap: 1.6vmin;
  padding: clamp(10px, 2vmin, 26px) clamp(14px, 2.6vmin, 30px);
}
.search-ico {
  width: clamp(22px, 4.4vmin, 48px); height: clamp(22px, 4.4vmin, 48px);
  flex: none; display: grid; place-items: center; color: var(--cyan);
  filter: drop-shadow(0 0 10px rgba(0, 240, 255, .55));
}
.search-ico svg { width: 100%; height: 100%; }
/* Boombox King stands in for the Pro crown emoji */
.ico-king { height: 1.25em; width: auto; vertical-align: -.3em; margin: 0 .06em;
  filter: drop-shadow(0 0 8px rgba(255, 215, 0, .5)); }
.search-top .mono { font-size: clamp(20px, 4.4vmin, 52px); }
.search-input {
  flex: 1; background: rgba(0, 20, 40, 0.35); border: 1px solid rgba(0,240,255,.3);
  border-radius: 999px; color: var(--cyan); padding: clamp(6px, 1.4vmin, 16px) clamp(12px, 2.4vmin, 28px);
  font-size: clamp(14px, 2.8vmin, 30px); font-weight: 600; outline: none; min-width: 60px;
}
.filter-btn { width: clamp(44px, 7vmin, 88px); height: clamp(44px, 7vmin, 88px); position: relative; flex: none; }
.filter-btn .fl { position: absolute; left: 50%; transform: translateX(-50%); height: 12%; border-radius: 999px; background: var(--cyan); }
.filter-btn .fl1 { width: 60%; top: 24%; }
.filter-btn .fl2 { width: 44%; top: 45%; }
.filter-btn .fl3 { width: 28%; top: 66%; }
.filter-panel {
  position: absolute; right: clamp(10px, 2.5vmin, 30px); top: clamp(70px, 13vmin, 150px);
  z-index: 20; display: flex; gap: 4vmin; padding: clamp(14px, 3vmin, 36px);
}
.filter-col { display: flex; flex-direction: column; gap: 1.2vmin; }
.filter-col .mono { font-size: clamp(18px, 3.6vmin, 42px); }
.filter-item { display: flex; align-items: center; gap: 1.2vmin; font-size: clamp(15px, 2.8vmin, 32px); cursor: pointer; }
.search-actions { display: flex; gap: 1.6vmin; margin-top: 1.6vmin; }
.action-btn { padding: clamp(10px, 2vmin, 24px) clamp(20px, 5vmin, 70px); font-size: clamp(18px, 4vmin, 48px); flex: 1; max-width: 480px; }
.rooms-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(240px, 44vw), 1fr));
  gap: 1.6vmin; margin-top: 1.8vmin; flex: 1; overflow-y: auto; align-content: start;
  padding-bottom: 12vmin;
}
.room-card { padding: clamp(12px, 2.6vmin, 32px); display: flex; flex-direction: column; gap: 1vmin; text-align: center; cursor: pointer; }
.room-card .mono { font-size: clamp(15px, 2.8vmin, 32px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.room-card .rj { font-size: clamp(13px, 2.4vmin, 26px); color: #fff; }
.room-card .lock-line { display: flex; align-items: center; justify-content: center; gap: 1vmin; }
.rooms-empty { grid-column: 1/-1; text-align: center; font-size: clamp(15px, 3vmin, 32px); opacity: .8; padding-top: 6vmin; }

/* ============================================================
   GAMEMODES
   ============================================================ */
.modes-grid {
  flex: 1; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr;
  gap: 2vmin; padding-bottom: 10vmin; min-height: 0;
}
.mode-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  padding: clamp(10px, 2vmin, 26px); gap: clamp(6px, 1.4vmin, 18px); overflow: hidden; position: relative;
}
.mode-tile .mono {
  font-size: clamp(16px, 3.6vmin, 44px); z-index: 3; flex: none;
  text-shadow: 0 0 12px rgba(0,240,255,.7), 0 2px 6px #000;
}
.mode-art {
  flex: 1; width: 100%; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden; position: relative;
}
.mode-art .art-img {
  max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain;
  filter: drop-shadow(0 8px 18px rgba(0,0,0,.5));
}
.mode-art .art-img.rounded { border-radius: 18px; }
.art-hacker { flex: 1; width: 100%; position: relative; min-height: 0; }
.art-hacker img {
  position: absolute; height: 80%; top: 10%; width: auto; object-fit: contain;
  filter: drop-shadow(0 8px 18px rgba(0,0,0,.5));
}
.art-hacker img:nth-child(1) { left: 10%; z-index: 4; }
.art-hacker img:nth-child(2) { left: 30%; z-index: 3; }
.art-hacker img:nth-child(3) { left: 50%; z-index: 2; }
.art-hacker img:nth-child(4) { left: 70%; z-index: 1; }
.floater {
  position: absolute; right: 10%; top: 40%; width: clamp(70px, 14vmin, 160px);
  animation: floaty 3.4s ease-in-out infinite; pointer-events: none;
}
@keyframes floaty { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-14px); } }

/* ============================================================
   BLITZ MODE SELECT
   ============================================================ */
.variant-list { display: flex; flex-direction: column; gap: 2vmin; width: min(92%, 900px); }
.variant-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: clamp(12px, 2.6vmin, 34px) clamp(16px, 3.4vmin, 44px);
}
.variant-row .mono { font-size: clamp(20px, 4.6vmin, 56px); }
.select-btn { padding: clamp(8px, 1.8vmin, 22px) clamp(20px, 4.4vmin, 60px); font-size: clamp(16px, 3.4vmin, 40px); }

/* ============================================================
   LOBBY
   ============================================================ */
.exit-pill {
  position: absolute; top: clamp(10px, 2vmin, 26px); left: clamp(10px, 2vmin, 26px);
  padding: clamp(6px, 1.4vmin, 18px) clamp(18px, 4vmin, 54px);
  font-size: clamp(16px, 3.2vmin, 40px); z-index: 6;
}
/* bottom padding reserves the floating Start button's strip so settings panels
   never render underneath it */
.lobby-layout { display: flex; gap: 2vmin; flex: 1; min-height: 0;
  padding-top: clamp(48px, 9vmin, 110px); padding-bottom: clamp(96px, 16vmin, 200px); }
.lobby-panel {
  width: min(36%, 460px); flex: none; padding: clamp(12px, 2.4vmin, 30px);
  display: flex; flex-direction: column; gap: 1.2vmin; min-height: 0;
}
.lobby-panel h2 { text-align: center; font-size: clamp(20px, 4.4vmin, 52px); }
.lobby-users { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 1vmin; }
.user-row {
  display: flex; align-items: center; gap: 1.2vmin;
  padding: clamp(5px, 1.2vmin, 14px) clamp(8px, 1.6vmin, 18px);
  background: var(--glass); border-radius: 999px; border: 1px solid rgba(0,240,255,.2);
}
.user-row .uname { font-size: clamp(14px, 2.8vmin, 32px); font-weight: 600; color: var(--blue-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.user-row.owner .uname { color: var(--yellow); }
.user-row.me .uname { color: var(--cyan); }
/* Pro members stand out wherever players are listed — gold name, gold edge and
   a small badge. Owner colouring still wins on the name so the host stays clear. */
.user-row.pro {
  border-color: rgba(255,215,0,.55);
  background: linear-gradient(90deg, rgba(255,215,0,.14), var(--glass));
}
.user-row.pro .uname { color: #FFE87A; }
.user-row.owner .uname { color: var(--yellow); }
.pro-tag {
  flex: none; font: 800 clamp(9px, 1.5vmin, 14px) 'Rajdhani'; letter-spacing: .08em;
  color: #241a00; background: linear-gradient(90deg, #FFE87A, #FFB020);
  padding: .15em .5em; border-radius: 999px;
  box-shadow: 0 0 10px rgba(255,215,0,.55);
}
/* profile card */
.pf-name.pro, .pf-card.pro .pf-name { color: #FFE87A; text-shadow: 0 0 14px rgba(255,215,0,.5); }
.pf-card.pro { border-color: rgba(255,215,0,.5); box-shadow: 0 0 30px rgba(255,215,0,.22); }
.user-row .crown { width: clamp(22px, 4vmin, 44px); flex: none; }
/* Privacy row — same padlock art, colours and springy easing as the Blitz
   lobby (.stage .lb-*), re-expressed in this lobby's fluid clamp() sizing. */
/* Hacker "Game size (with bots)" picker — a row of size chips */
.size-row { display: flex; gap: 1.2vmin; flex-wrap: wrap; }
.size-btn {
  flex: 1; min-width: clamp(44px, 8vmin, 96px);
  padding: clamp(6px, 1.4vmin, 16px) 0; border-radius: 999px;
  background: rgba(140,220,255,.14); border: 2px solid rgba(160,235,255,.3);
  color: var(--blue-soft); font-size: clamp(16px, 3.4vmin, 40px); font-weight: 700; cursor: pointer;
  transition: transform .2s cubic-bezier(.34,1.56,.64,1), background .25s ease, color .25s ease, border-color .25s ease;
}
.size-btn:hover { transform: scale(1.05); }
.size-btn.on {
  background: rgba(0,240,255,.22); border-color: var(--cyan); color: var(--cyan);
  box-shadow: 0 0 18px rgba(0,240,255,.35);
}

.privacy-row { display: flex; align-items: center; gap: 1.4vmin; flex-wrap: wrap; }
.lock-ico {
  width: clamp(26px, 4.6vmin, 50px); height: clamp(26px, 4.6vmin, 50px);
  flex: none; display: grid; place-items: center;
  transition: transform .3s cubic-bezier(.34,1.56,.64,1);
}
.lock-ico svg { width: 100%; height: 100%;
  filter: drop-shadow(0 0 8px rgba(0,240,255,.5)); transition: filter .3s ease; }
.lock-ico.locked svg { filter: drop-shadow(0 0 10px rgba(255,80,80,.6)); }
.privacy-btn {
  flex: 1; padding: clamp(6px, 1.4vmin, 16px); font-size: clamp(15px, 3vmin, 34px);
  transition: transform .2s cubic-bezier(.34,1.56,.64,1), filter .2s ease, color .3s ease;
}
.privacy-btn:hover { filter: brightness(1.2); transform: scale(1.02); }
.privacy-btn:active { transform: scale(.97); }
.privacy-btn.private { color: var(--red); }
.pass-input {
  flex: 1; min-width: 0; background: rgba(0,20,40,.5);
  border: 2px solid rgba(0,240,255,.4); border-radius: 999px; color: var(--cyan);
  font-size: clamp(13px, 2.4vmin, 28px); font-weight: 700;
  padding: clamp(5px, 1.1vmin, 12px) clamp(10px, 1.8vmin, 22px); outline: none;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.pass-input:focus { border-color: var(--cyan); box-shadow: 0 0 20px rgba(0,240,255,.3); }
.pass-input::placeholder { color: rgba(0,240,255,.4); }
.pass-input:disabled { opacity: .6; cursor: not-allowed; }
#lobby-roomid { font-size: clamp(13px, 2.4vmin, 26px); color: var(--green); }
#lobby-roomid .rid { color: var(--red); cursor: pointer; }
.lobby-right { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 1.6vmin; min-height: 0; padding-bottom: 2vmin; }
.settings-panel { padding: clamp(12px, 2.4vmin, 30px); }
.settings-panel > h2 { text-align: center; font-size: clamp(18px, 4vmin, 48px); margin-bottom: 1.4vmin; }
.rule-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(170px, 40vw), 1fr)); gap: 1.4vmin; }
/* A card sits ON the panel, so it catches MORE light than the panel does — it
 * is nearer the source. It used to use --glass (.15) on a panel filled at .20,
 * i.e. the thing on top was darker than the thing underneath, which is the one
 * arrangement that reads as painted-on rather than stacked. */
.rule-card {
  padding: clamp(10px, 2vmin, 24px); display: flex; flex-direction: column; align-items: center; gap: 1vmin;
  background: rgba(96, 194, 238, .17); border-radius: var(--r);
  border: 1px solid rgba(150, 235, 255, .30);
}
.rule-card .rj { color: #fff; font-size: clamp(14px, 2.6vmin, 30px); text-align: center; }
/* Matches the Blitz lobby's green Start pill (.lb-start) so every game mode's
   Start button is identical — same clean green glass, border, glow and spring
   animation. Colour is scoped per state so meme-battles' red "can't afford" look
   still works. */
.start-btn {
  position: absolute; bottom: clamp(18px, 3.2vmin, 44px); left: 50%; transform: translateX(-50%);
  padding: clamp(13px, 2.5vmin, 30px) clamp(46px, 10vmin, 132px);
  font-size: clamp(30px, 6.8vmin, 88px); z-index: 6;
  display: inline-flex; align-items: center; justify-content: center; gap: .42em; white-space: nowrap;
  border-radius: 999px; font-weight: 800;
  transition: transform .25s cubic-bezier(.34, 1.56, .64, 1), filter .2s ease, box-shadow .3s ease !important;
}
/* `!important` beats the per-graphics-tier `.green-glass` tint overrides so the
   Start button stays the same bright green pill on every tier, matching .lb-start. */
.start-btn.green {
  background: rgba(0, 255, 40, .32) !important; border: 2px solid rgba(120, 255, 140, .75) !important;
  box-shadow: none !important; color: var(--green) !important; text-shadow: 0 0 30px rgba(0, 255, 80, .8);
}
.start-btn.red {
  background: rgba(255, 40, 40, .30) !important; border: 2px solid rgba(255, 140, 140, .75) !important;
  box-shadow: none !important; color: var(--red) !important; text-shadow: 0 0 30px rgba(255, 60, 60, .8);
}
/* Meme battles: the Start button carries the ticket cost — "9/10 🎟 Start" */
.start-btn .start-tix { width: 1.15em; height: 1.15em; object-fit: contain; flex: none; }
.start-btn .start-tixcount { font-variant-numeric: tabular-nums; }
.start-btn:hover { filter: brightness(1.3); transform: translateX(-50%) scale(1.05); }
.start-btn.green:hover { box-shadow: 0 12px 40px rgba(0, 255, 60, .35); }
.start-btn.red:hover { box-shadow: 0 12px 40px rgba(255, 60, 60, .35); }
.start-btn:active { transform: translateX(-50%) scale(.96); }
.mode-note { text-align: center; color: var(--blue-soft); font-size: clamp(13px, 2.4vmin, 26px); }
.car-select { display: flex; gap: 2vmin; justify-content: center; flex-wrap: wrap; }
.car-opt {
  width: clamp(60px, 11vmin, 130px); height: clamp(100px, 19vmin, 230px);
  display: grid; place-items: center;
  cursor: pointer; border-radius: 14px; padding: 1vmin; transition: transform .15s;
}
.car-opt svg { width: 100%; }
.car-opt img { max-width: 100%; max-height: 100%; width: auto; height: 100%; object-fit: contain; }
.car-opt.taken { opacity: .35; pointer-events: none; }
.car-opt.mine { background-color: rgba(255,255,255,.12); transform: scale(1.06); }

/* ============================================================
   DIALOGS & TOAST
   ============================================================ */
.overlay {
  position: fixed; inset: 0; background: rgba(4, 4, 20, 0.55);
  display: grid; place-items: center; z-index: 100;
  /* keep dialog content clear of the notch / home indicator on every edge */
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
}
/* Whop embedded checkout — its own box so the iframe keeps a stable mount node.
   Full-screen so Apple Pay / Google Pay wallet buttons have room to render (the
   loader already grants `allow="payment; paymentRequest"` on the iframe). */
/* Whop checkout modal — boxed like the intro page's overlay. The frame colour
   switches per SKU: gold for the Pro subscription (an ongoing commitment) and
   cyan for one-time purchases (BoomBucks packs, cosmetic unlocks). Applied via
   `.tier-sub` / `.tier-once` on the box. */
.whop-embed-box {
  width: min(96vw, 640px); height: auto; max-height: 92vh;
  /* Whop sizes its iframe to ITS OWN content (it grows when wallet buttons or
     extra fields appear), which regularly exceeds this box. Without a scroll
     here the surplus was clipped by .whop-embed-mount's overflow:hidden and the
     pay button became unreachable — cut off with no way to get to it. */
  overflow-y: auto; overscroll-behavior: contain;
  border-radius: 20px; border: 1px solid rgba(0, 240, 255, .55);
  padding: clamp(14px, 2vw, 22px);
  padding-bottom: max(clamp(14px, 2vw, 22px), env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 12px;
  background: linear-gradient(180deg, rgba(6, 30, 60, .6), rgba(14, 26, 60, .7));
  box-shadow: 0 0 60px rgba(0, 240, 255, .35);
}
.whop-embed-box.tier-sub {
  border-color: rgba(255, 215, 0, .55);
  background: linear-gradient(180deg, rgba(60, 44, 10, .55), rgba(14, 26, 60, .7));
  box-shadow: 0 0 60px rgba(255, 215, 0, .35);
}
.whop-embed-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex: none; }
.whop-embed-head .mono { font-size: clamp(16px, 2.4vw, 22px); color: var(--cyan);
  text-shadow: 0 0 18px rgba(0, 240, 255, .6); letter-spacing: .5px; }
.whop-embed-box.tier-sub .whop-embed-head .mono { color: #FFE87A; text-shadow: 0 0 18px rgba(255, 215, 0, .6); }
.whop-embed-close {
  width: 36px; height: 36px; flex: none;
  border-radius: 10px; font-size: 18px; font-weight: 800;
  color: #ff9a9a; background: rgba(255, 90, 90, .2); border: 1px solid rgba(255, 120, 120, .5);
  cursor: pointer; transition: filter .18s, transform .1s;
}
.whop-embed-close:hover { filter: brightness(1.2); }
.whop-embed-close:active { transform: scale(.94); }
/* Iframe lives here. Whop's loader resizes the iframe to its own content
   (it grows when wallet buttons appear), so we let it drive the height. */
.whop-embed-mount {
  /* flex:none + min-height:0 so the mount takes the iframe's real height
     instead of being squeezed into the leftover space and clipping it. The box
     above scrolls to reach whatever doesn't fit. */
  flex: none; min-height: 0;
  border-radius: 14px; overflow: hidden;
  background: rgba(4, 10, 30, .4);
  display: flex; align-items: stretch; justify-content: center;
}
.whop-embed-mount > * { width: 100%; flex: none; }
.whop-embed-mount iframe { width: 100%; min-height: 520px; border: 0; display: block; }
/* Short viewports get a bit more of the screen and a smaller pre-mount floor;
   the scrolling itself is now handled by the base rules above. */
@media (max-height: 700px) {
  .whop-embed-box { max-height: 96vh; }
  .whop-embed-mount iframe { min-height: 460px; }
}

.dialog {
  width: min(92vw, 820px); max-height: 88vh; overflow-y: auto;
  padding: clamp(18px, 4vmin, 48px); text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 1.8vmin;
}
.dialog.gold { background: rgba(215, 180, 60, 0.35); border-color: rgba(255, 215, 0, 0.5); }
.dialog img.mascot { width: clamp(70px, 14vmin, 150px); }
.dialog h2 { font-family: 'Monoton', cursive; font-size: clamp(20px, 4.4vmin, 46px); }
.dialog p { font-size: clamp(15px, 2.8vmin, 28px); font-weight: 600; line-height: 1.45; }
.dialog .dialog-btns { display: flex; gap: 2vmin; margin-top: 1vmin; flex-wrap: wrap; justify-content: center; }
.dialog .dialog-btns .glass { padding: clamp(8px, 1.6vmin, 18px) clamp(24px, 5vmin, 64px); font-size: clamp(16px, 3vmin, 34px); font-weight: 600; }
.dialog input.name-input {
  background: rgba(0, 20, 40, .45); border: 1px solid rgba(0,240,255,.35); border-radius: 999px;
  color: var(--cyan); font: 600 clamp(16px, 3vmin, 30px) 'Rajdhani'; padding: 10px 22px; text-align: center; outline: none;
  width: min(80%, 420px);
}

#toast {
  /* clear the notch / Dynamic Island — a bare 3vmin put toasts under it */
  position: fixed; top: calc(env(safe-area-inset-top) + 3vmin); left: 50%; transform: translateX(-50%);
  padding: 10px 26px; z-index: 200; font-weight: 700;
  font-size: clamp(14px, 2.6vmin, 26px); color: #fff; max-width: 86vw; text-align: center;
}

/* ============================================================
   SHOP
   ============================================================ */
.shop-head { display: flex; align-items: flex-start; gap: 2vmin; flex-wrap: wrap; }
.shop-headL { display: flex; flex-direction: column; gap: .8vmin; }
.shop-head h1 { font-size: clamp(20px, 4.6vmin, 54px); }
.shop-wallet { display: flex; flex-direction: column; gap: .6vmin; }
.wallet-row { display: flex; align-items: center; gap: 1.2vmin; font-size: clamp(20px, 4vmin, 52px); }
.wallet-ico { width: clamp(30px, 5.4vmin, 66px); height: clamp(30px, 5.4vmin, 66px); object-fit: contain;
  border-radius: 12px; filter: drop-shadow(0 0 8px rgba(0,240,255,.55)); }
.wallet-row .rainbow-text { line-height: 1; }

/* Boombox mascot + speech bubble (his Pixelify Sans voice) */
.shop-mascot { margin-left: auto; display: flex; align-items: flex-start; gap: 1.2vmin; cursor: pointer; }
.shop-mascot img { width: clamp(70px, 12vmin, 150px); flex: none;
  filter: drop-shadow(0 0 12px rgba(0,240,255,.4)); transition: transform .18s ease; }
.shop-mascot:hover img { transform: scale(1.05) rotate(-2deg); }
.shop-mascot #shop-boombox.boop { animation: boomboop .4s cubic-bezier(.34,1.56,.64,1); }
@keyframes boomboop { 0% { transform: scale(1); } 40% { transform: scale(1.12) rotate(-3deg); } 100% { transform: scale(1); } }
.speech {
  position: relative; align-self: center; max-width: min(46vw, 560px);
  padding: clamp(8px, 1.6vmin, 20px) clamp(12px, 2.2vmin, 28px);
  background: rgba(20, 150, 210, 0.28); border: 3px solid var(--cyan); border-radius: 26px;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 0 26px rgba(0,240,255,.35);
  overflow-wrap: break-word; word-break: break-word; hyphens: auto;
}
.speech.pixel { font-family: 'Pixelify Sans', 'Rajdhani', sans-serif; font-weight: 600;
  font-size: clamp(13px, 2.2vmin, 30px); line-height: 1.25; color: var(--blue-soft);
  text-shadow: 0 0 10px rgba(110,123,255,.4); }
.speech::after {   /* tail pointing right, toward the mascot */
  content: ''; position: absolute; top: 50%; right: -20px; transform: translateY(-50%);
  border: 12px solid transparent; border-left-color: var(--cyan);
}
.speech.pop { animation: speechpop .28s cubic-bezier(.34,1.56,.64,1); }
@keyframes speechpop { 0% { transform: scale(.9); opacity: .4; } 100% { transform: scale(1); opacity: 1; } }

.shop-grid {
  /* Clears the Exit pill at the bottom of the scroll; the pill's own glass
     backdrop handles the mid-scroll overlap. */
  flex: 1; overflow-y: auto; margin-top: 1.6vmin; padding-bottom: 16vmin;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(210px, 44vw), 1fr)); gap: 1.6vmin;
  align-content: start;
}
.shop-card {
  padding: clamp(12px, 2.4vmin, 28px); display: flex; flex-direction: column; align-items: center; gap: 1vmin;
  text-align: center; justify-content: space-between;
}
.shop-card .art { height: clamp(80px, 13vmin, 150px); display: grid; place-items: center; width: 100%; }
.shop-card .art svg { height: 100%; }
/* Absolute max-height (not %) so the logo is always capped — a % max-height
   doesn't resolve against the clamp() art height inside the flex column, which
   left wide cards (the pack stand) uncapped. */
.shop-card .art .shop-ico { max-height: clamp(80px, 13vmin, 150px); max-width: 100%; object-fit: contain;
  filter: drop-shadow(0 3px 10px rgba(0,0,0,.4)); }
.shop-card .cname { color: #fff; font-weight: 700; font-size: clamp(15px, 2.8vmin, 32px); }
.shop-card .shop-note { color: var(--blue-soft); font-weight: 600; font-size: clamp(11px, 1.9vmin, 21px); opacity: .95; }
.shop-card .buy-row { display: flex; gap: 1vmin; flex-wrap: wrap; justify-content: center; width: 100%; }
.pill-btn { border-radius: 999px; padding: clamp(6px, 1.3vmin, 14px) clamp(14px, 2.4vmin, 30px);
  font-weight: 700; font-size: clamp(13px, 2.3vmin, 26px); display: inline-flex; align-items: center; gap: .5vmin;
  cursor: pointer; transition: transform .15s cubic-bezier(.34,1.56,.64,1), filter .15s ease; }
.pill-btn:hover { filter: brightness(1.2); transform: scale(1.04); }
.pill-btn:active { transform: scale(.96); }
.pill-bb { width: clamp(18px, 2.6vmin, 30px); height: clamp(18px, 2.6vmin, 30px);
  object-fit: contain; border-radius: 6px; vertical-align: middle; }
.pill-green { background-color: rgba(0, 255, 26, .3); color: var(--green); }
.pill-cyan { background-color: rgba(0, 240, 255, .25); color: var(--cyan); }
.pill-gold { background-color: rgba(255, 215, 0, .35); color: var(--yellow); }
/* BoomBucks money-pack card: a featured wide stand spanning 3 shop cards,
   with the six price pills laid 3-across (2 rows). */
.shop-packs { grid-column: span 3; }
.shop-packs .pack-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(8px, 1.4vmin, 16px); width: 100%; }
.pack-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  width: 100%; border-radius: 20px; padding: clamp(8px, 1.6vmin, 18px) clamp(6px, 1.2vmin, 12px);
  font-weight: 700; line-height: 1.05; cursor: pointer;
  transition: transform .15s cubic-bezier(.34,1.56,.64,1), filter .15s ease;
}
.pack-btn:hover { filter: brightness(1.2); transform: scale(1.03); }
.pack-btn:active { transform: scale(.97); }
.pack-btn .pk-amt { display: inline-flex; align-items: center; gap: 5px; font-size: clamp(14px, 2.3vmin, 30px); white-space: nowrap; }
.pack-btn .pk-price { font-size: clamp(13px, 2vmin, 26px); opacity: .95; white-space: nowrap; }
.pack-btn .pill-bb { width: clamp(16px, 2.2vmin, 26px); height: clamp(16px, 2.2vmin, 26px); }
/* Whop checkout status + redeem entry under the money packs */
.pack-foot { width: 100%; margin-top: 1vmin; display: flex; flex-direction: column; align-items: center; gap: .8vmin; }
.pack-foot .rj { font-size: clamp(10px, 1.7vmin, 19px); font-weight: 600; text-align: center; }
.pack-redeem { padding: clamp(5px, .9vmin, 11px) clamp(14px, 2.4vmin, 30px); border-radius: 999px;
  font-size: clamp(11px, 1.9vmin, 21px); font-weight: 700; color: var(--cyan);
  background: rgba(0,240,255,.14); border: 2px solid rgba(0,240,255,.45); }
.pack-redeem:hover { background-color: rgba(0,240,255,.26); }

/* ============================================================
   LOBBY (design-space, inside .stage) — Blitz
   ============================================================ */
.stage .lb-exit {
  display: grid; place-items: center; border-radius: 44px;
  background: var(--glass-red); border: 2px solid rgba(255,80,80,.5);
  color: var(--red); font-family: 'Monoton'; font-size: 90px;
  text-shadow: 0 0 12px rgba(255,50,50,.6); cursor: pointer;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  transition: transform .2s cubic-bezier(.34,1.56,.64,1), filter .2s ease, box-shadow .3s ease;
}
.stage .lb-exit:hover { filter: brightness(1.3); transform: scale(1.04);
  box-shadow: 0 6px 30px rgba(255,50,50,.35); }
.stage .lb-exit:active { transform: scale(.96); }
.stage .lb-panel { display: flex; flex-direction: column; padding: 30px 40px; gap: 20px; }
.stage .lb-title { font-family: 'Monoton'; font-size: 130px; color: var(--cyan);
  text-shadow: 0 0 22px rgba(0,240,255,.6); text-align: left; line-height: 1; }
.stage .lb-users { flex: 1; display: flex; flex-direction: column; gap: 18px; overflow-y: auto; min-height: 0; }
.stage .lb-user {
  display: flex; align-items: center; gap: 20px; padding: 10px 20px;
  border-radius: 999px;
  background: rgba(140,220,255,.16); border: 2px solid rgba(160,235,255,.35);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  transition: background .3s ease;
}
.stage .lb-user:hover { background-color: rgba(140,220,255,.24); }
.stage .lb-user .lb-av {
  /* positioning context so the Discord avatar decoration (absolute, 138%) hugs
     this disc instead of escaping to an ancestor */
  position: relative;
  width: 90px; height: 90px; border-radius: 50%; flex: none;
  background: #fff center/70% no-repeat;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127 96"><path fill="%235865F2" d="M107.7 8.07A105.15 105.15 0 0 0 81.47 0a72.06 72.06 0 0 0-3.36 6.83 97.68 97.68 0 0 0-29.11 0A72.37 72.37 0 0 0 45.64 0a105.89 105.89 0 0 0-26.25 8.09C2.79 32.65-1.71 56.6.54 80.21a105.73 105.73 0 0 0 32.17 16.15 77.7 77.7 0 0 0 6.89-11.11 68.42 68.42 0 0 1-10.85-5.18c.91-.66 1.8-1.34 2.66-2a75.57 75.57 0 0 0 64.32 0c.87.71 1.76 1.39 2.66 2a68.68 68.68 0 0 1-10.87 5.19 77 77 0 0 0 6.89 11.1 105.25 105.25 0 0 0 32.19-16.14c2.64-27.38-4.51-51.11-18.9-72.15ZM42.45 65.69C36.18 65.69 31 60 31 53s5-12.74 11.43-12.74S54 46 53.89 53s-5.05 12.69-11.44 12.69Zm42.24 0C78.41 65.69 73.25 60 73.25 53s5-12.74 11.44-12.74S96.23 46 96.12 53s-5.04 12.69-11.43 12.69Z"/></svg>');
}
.stage .lb-user .lb-name {
  flex: 1; font: 700 66px 'Rajdhani'; color: var(--blue);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stage .lb-user.owner .lb-name { color: var(--yellow); }
.stage .lb-user.me .lb-name { color: var(--cyan); }
.stage .lb-user .lb-king { width: 100px; height: 100px; object-fit: contain; flex: none;
  filter: drop-shadow(0 0 10px rgba(255,215,0,.6)); }

.stage .lb-privrow { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.stage .lb-lock {
  width: 84px; height: 84px; flex: none; display: grid; place-items: center;
  transition: transform .3s cubic-bezier(.34,1.56,.64,1);
}
.stage .lb-lock svg { width: 100%; height: 100%;
  filter: drop-shadow(0 0 8px rgba(0,240,255,.5)); transition: filter .3s ease; }
.stage .lb-lock.locked svg { filter: drop-shadow(0 0 10px rgba(255,80,80,.6)); }
.stage .lb-privacy {
  flex: 1; min-width: 260px; padding: 12px 20px; border-radius: 999px;
  background: rgba(140,220,255,.16); border: 2px solid rgba(160,235,255,.35);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: var(--blue); font: 700 60px 'Rajdhani'; text-align: center; cursor: pointer;
  transition: transform .2s cubic-bezier(.34,1.56,.64,1), filter .2s ease, background .3s ease, color .3s ease;
}
.stage .lb-privacy:hover { filter: brightness(1.2); transform: scale(1.02); }
.stage .lb-privacy:active { transform: scale(.97); }
.stage .lb-privacy.private { color: var(--red); }
.stage .lb-roomid { font: 700 46px 'Rajdhani'; color: var(--green); }
.stage .lb-roomid .rid { color: var(--red); cursor: pointer; transition: filter .2s ease; }
.stage .lb-roomid .rid:hover { filter: brightness(1.3); }

/* House rules panel */
.stage .lb-house { display: flex; flex-direction: column; padding: 30px 40px; gap: 24px; }
.stage .lb-house-title { font-family: 'Monoton'; font-size: 130px; color: var(--cyan);
  text-shadow: 0 0 22px rgba(0,240,255,.6); text-align: center; line-height: 1; }
.stage .lb-rules-grid {
  display: grid; grid-template-columns: 1fr 1fr 1fr;
  gap: 24px; flex: 1;
}
.stage .lb-rule {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 20px; padding: 24px 30px; border-radius: 44px;
  background: rgba(140,220,255,.18); border: 2px solid rgba(160,235,255,.35);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  transition: background .3s ease, transform .3s cubic-bezier(.34,1.56,.64,1), box-shadow .3s ease;
}
.stage .lb-rule:hover { background-color: rgba(140,220,255,.26); transform: translateY(-4px);
  box-shadow: 0 12px 30px rgba(0,240,255,.15); }
.stage .lb-rule-label { color: #fff; font: 700 46px 'Rajdhani'; text-align: center; line-height: 1.05; }
.stage .lb-toggle {
  width: 180px; height: 84px; border-radius: 999px; position: relative;
  background: rgba(140,0,0,.4); border: 2px solid rgba(255,80,80,.55);
  cursor: pointer; flex: none;
  transition: background .35s cubic-bezier(.22,1,.36,1),
              border-color .35s cubic-bezier(.22,1,.36,1);
}
.stage .lb-toggle::after {
  content: ''; position: absolute; top: 50%; transform: translateY(-50%);
  width: 60px; height: 60px; border-radius: 50%;
  background: rgba(255,255,255,.85); left: 8%;
  box-shadow: 0 3px 8px rgba(0,0,0,.3);
  transition: left .35s cubic-bezier(.34,1.56,.64,1);
}
.stage .lb-toggle:active::after { width: 68px; }
.stage .lb-toggle.on { background-color: rgba(0,180,10,.4); border-color: rgba(100,255,120,.55); }
.stage .lb-toggle.on::after { left: 58%; }

/* Timer panel */
.stage .lb-timer { display: flex; align-items: center; gap: 30px; padding: 20px 30px; }
.stage .lb-timer-title { font-family: 'Monoton'; font-size: 100px; color: var(--cyan);
  text-shadow: 0 0 22px rgba(0,240,255,.5); flex: none; }
.stage .lb-timer-body { display: flex; flex-direction: column; align-items: center; flex: none; }
.stage .lb-timer-row { display: flex; align-items: center; gap: 16px; }
.stage .lb-timer-num { font-family: 'Monoton'; font-size: 130px; color: var(--cyan);
  text-shadow: 0 0 20px rgba(0,240,255,.5); line-height: 1;
  transition: transform .35s cubic-bezier(.22,1.4,.36,1), opacity .3s ease; }
.stage .lb-timer-arrows { display: flex; flex-direction: column; gap: 8px; }
.stage .lb-arrow {
  width: 90px; height: 58px; display: grid; place-items: center;
  border-radius: 20px; background: rgba(0,240,255,.18);
  color: var(--cyan); font-family: 'Rajdhani'; font-weight: 700; font-size: 40px;
  cursor: pointer; border: 2px solid rgba(120,140,255,.35);
  transition: transform .18s cubic-bezier(.34,1.56,.64,1), filter .2s ease, background .2s ease;
}
.stage .lb-arrow:hover { filter: brightness(1.3); transform: scale(1.08); }
.stage .lb-arrow:active { transform: scale(.9); }
/* At the free ceiling (5 min) raising the timer is a Pro perk — gold the up
   arrow so it reads as a locked/premium step. */
.stage .lb-arrow.at-cap { color: var(--yellow); background: rgba(255,215,0,.22);
  border-color: rgba(255,215,0,.55); box-shadow: 0 0 18px rgba(255,215,0,.45); }
.stage .lb-timer-unit { font-family: 'Monoton'; font-size: 38px; color: var(--cyan); }
.stage .lb-unlim {
  display: flex; align-items: center; gap: 20px; padding: 14px 30px; border-radius: 999px;
  background: rgba(210,190,60,.35); border: 2px solid rgba(255,220,90,.5);
  cursor: pointer; margin-left: auto; flex: none;
  transition: transform .2s cubic-bezier(.34,1.56,.64,1), filter .2s ease, box-shadow .3s ease;
}
.stage .lb-unlim:hover { filter: brightness(1.15); transform: scale(1.03);
  box-shadow: 0 6px 30px rgba(255,215,90,.3); }
.stage .lb-unlim-label { font-family: 'Rajdhani'; font-weight: 700; font-size: 52px; color: var(--yellow); }
.stage .lb-unlim-t {
  width: 130px; height: 62px; border-radius: 999px; position: relative; flex: none;
  background: rgba(220,80,20,.6); border: 2px solid rgba(255,140,60,.6);
  transition: background .3s ease, border-color .3s ease;
}
.stage .lb-unlim-t::after {
  content: ''; position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 50%; background: rgba(255,255,255,.9);
  left: 6%; box-shadow: 0 2px 6px rgba(0,0,0,.3);
  transition: left .32s cubic-bezier(.34,1.56,.64,1);
}
.stage .lb-unlim-t.on { background-color: rgba(0,180,10,.4); border-color: rgba(100,255,120,.55); }
.stage .lb-unlim-t.on::after { left: 60%; }
.stage .lb-start {
  display: grid; place-items: center; border-radius: 999px;
  background: rgba(0,255,40,.32); border: 2px solid rgba(120,255,140,.75);
  color: var(--green); font-family: 'Rajdhani'; font-weight: 800; font-size: 134px;
  text-shadow: 0 0 30px rgba(0,255,80,.8); cursor: pointer;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  transition: transform .25s cubic-bezier(.34,1.56,.64,1), filter .2s ease, box-shadow .3s ease;
}
.stage .lb-start:hover { filter: brightness(1.3);
  transform: translateX(-50%) scale(1.05);
  box-shadow: 0 12px 40px rgba(0,255,60,.35); }
.stage .lb-start:active { transform: translateX(-50%) scale(.97); }


/* ============================================================
   TROPHY ROAD (design-space, inside .stage)
   ============================================================ */
.stage .tr-title { font-size: 110px; text-align: center; }
.stage .tr-panel {
  display: flex; flex-direction: column; padding: 40px;
}
.stage .tr-top { display: flex; align-items: center; gap: 40px; }
.stage .tr-exit { font-family: 'Monoton'; font-size: 90px; color: var(--cyan);
  text-shadow: 0 0 18px rgba(0,240,255,.6); cursor: pointer; flex: none; }
.stage .tr-exit:hover { filter: brightness(1.4); }
.stage .tr-count { font-size: 150px; line-height: 1; }
.stage .tr-trophy-ico { width: 200px; height: 190px; flex: none; }
.stage .tr-trophy-ico .spr { width: 100%; height: 100%; object-fit: contain;
  filter: drop-shadow(0 0 12px rgba(0,240,255,.5)); }
.stage .tr-featured {
  margin-left: auto; flex: none; display: flex; align-items: center; gap: 30px;
  padding: 30px 50px; border-radius: 44px; min-width: 520px;
  background: linear-gradient(135deg, rgba(255,140,40,.35), rgba(255,0,80,.25), rgba(160,60,220,.3));
  border: 2px solid rgba(255,180,80,.4);
}
.stage .tr-featured-art { width: 200px; height: 200px; display: grid; place-items: center; flex: none; overflow: hidden; }
.stage .tr-featured-art svg { max-width: 100%; max-height: 100%; }
.stage .tr-featured-art .spr { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.stage .tr-featured-info { display: flex; flex-direction: column; gap: 4px; }
.stage .tr-featured-name { color: #fff; font: 700 60px 'Rajdhani'; }
.stage .tr-featured-cond { font: 700 52px 'Rajdhani'; line-height: 1.1; }
.stage .tr-bar-wrap { width: 100%; margin-top: 30px; }
.stage .tr-bar-inner {
  height: 60px; border-radius: 999px; overflow: hidden;
  background: rgba(0,10,30,.35); border: 2px solid rgba(120,230,255,.28); padding: 6px;
}
.stage .tr-bar-fill { height: 100%; border-radius: 999px; width: 0%;
  transition: width .7s cubic-bezier(.22,1,.36,1); background-image: var(--rainbow); }
/* On open, the count flashes gold and the trophy pops — same treatment as the
   main screen's earn animation (keyframes defined there). */
.stage .tr-top.reveal .tr-trophy-ico { animation: ms-trophy-pop .7s cubic-bezier(.34,1.56,.64,1); }
.stage .tr-top.reveal .tr-count { animation: ms-num-flash .7s ease; }
.stage .tr-bar-label {
  margin-top: 10px; text-align: right;
  font: 700 40px 'Rajdhani'; color: var(--blue-soft);
  text-shadow: 0 2px 6px rgba(0,0,0,.7);
}
.stage .tr-rewards { display: flex; gap: 30px; margin-top: 40px; flex: 1; }
/* The short phone canvas (973 tall) leaves ~845px for this panel, but the stock
   header + reward row want ~922. Trim the fixed-size header art rather than
   squashing the reward cards. */
.stage-phone .tr-trophy-ico { width: 150px; height: 140px; }
.stage-phone .tr-count { font-size: 110px; }
.stage-phone .tr-rewards { margin-top: 20px; min-height: 0; }
.stage .tr-card {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 30px; border-radius: 44px;
  background: rgba(140,220,255,.18); border: 2px solid rgba(160,235,255,.35);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  text-align: center;
}
.stage .tr-card-art { height: 220px; width: 100%; display: grid; place-items: center; overflow: hidden; }
.stage .tr-card-art .spr { max-height: 220px; max-width: 260px; height: auto; width: auto; object-fit: contain; }
.stage .tr-card-art svg { max-height: 220px; max-width: 260px; }
/* On the short phone canvas the card can't give the art its full 220px, and a
   fixed height meant the icon was clipped in half. Let it flex and cap the
   sprite to whatever height the row actually has. */
.stage.stage-phone .tr-card-art { height: auto; flex: 1; min-height: 0; }
/* Pin the art inside its box. `max-height:100%` was useless here: the art box
   is an auto-height flex item, so the percentage resolved against its COMPUTED
   height (~210px) rather than the ~85px it actually renders at, and the crate
   spilled out and got cut in half. inset:0 + object-fit can't miss. */
.stage.stage-phone .tr-card-art { position: relative; }
.stage.stage-phone .tr-card-art > * {
  position: absolute; inset: 0;
  width: 100%; height: 100%; max-width: 100%; max-height: 100%;
  object-fit: contain;
}
.stage.stage-phone .tr-card { gap: 10px; padding: 18px 14px; }
.stage .tr-card-name { color: #fff; font: 700 52px 'Rajdhani'; }
.stage .tr-card-cond { font: 700 44px 'Rajdhani'; line-height: 1.15; }
/* Milestone states. A claimable reward has to read as claimable at a glance —
   green edge, lift and a slow pulse — while locked ones recede and claimed ones
   settle into a quiet "done" look. */
.stage .tr-card.tr-ready {
  cursor: pointer;
  background: rgba(0,255,106,.16);
  border-color: rgba(0,255,106,.75);
  box-shadow: 0 0 44px rgba(0,255,26,.45), inset 0 0 30px rgba(0,255,26,.12);
  animation: tr-ready-pulse 2.2s ease-in-out infinite;
}
@keyframes tr-ready-pulse {
  0%, 100% { box-shadow: 0 0 34px rgba(0,255,26,.35), inset 0 0 30px rgba(0,255,26,.10); }
  50%      { box-shadow: 0 0 66px rgba(0,255,26,.75), inset 0 0 30px rgba(0,255,26,.18); }
}
.stage .tr-card.tr-ready:active { transform: scale(.97); }
.stage .tr-card.tr-ready .tr-card-cond { color: var(--green); }
.stage .tr-featured.tr-ready { border-color: rgba(0,255,106,.7); box-shadow: 0 0 44px rgba(0,255,26,.4); }
/* the pulse is a box-shadow blur animation — cheap on desktop, not on a phone */
body.mobile .stage .tr-card.tr-ready {
  animation: none;
  box-shadow: 0 0 50px rgba(0,255,26,.6), inset 0 0 30px rgba(0,255,26,.15);
}

/* ============================================================
   VOID PASS
   ============================================================ */
#screen-pass h1 { text-align: center; font-size: clamp(22px, 5vmin, 58px); }
.road-panel { flex: 1; margin-top: 1.4vmin; padding: clamp(14px, 3vmin, 38px); display: flex; flex-direction: column; gap: 2vmin; position: relative; min-height: 0; overflow-y: auto; }
.corner-inline { font-size: clamp(20px, 4.4vmin, 52px); align-self: flex-start; color: var(--cyan); font-family: 'Monoton'; }
.road-top { display: flex; align-items: center; gap: 2.4vmin; flex-wrap: wrap; }
.road-top .big { font-size: clamp(26px, 5.6vmin, 66px); }
.reward-next { margin-left: auto; padding: clamp(10px, 2vmin, 26px); text-align: center; color: #fff; font-weight: 600; display: flex; flex-direction: column; align-items: center; gap: .6vmin; }
.reward-next svg { height: clamp(50px, 9vmin, 110px); }
.road-rewards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(180px, 40vw), 1fr)); gap: 1.8vmin; }
.reward-card { padding: clamp(12px, 2.4vmin, 30px); display: flex; flex-direction: column; align-items: center; gap: 1vmin; background: var(--glass); border-radius: var(--r); border: 1px solid rgba(0,240,255,.25); text-align: center; }
.reward-card svg { height: clamp(46px, 8.6vmin, 100px); }
.reward-card .cname { color: #fff; font-weight: 600; font-size: clamp(13px, 2.6vmin, 28px); }
.reward-card .cond { font-weight: 600; font-size: clamp(11px, 2.2vmin, 22px); }
.choose-caption-h { text-align: center; font-size: clamp(16px, 3.6vmin, 44px); color: var(--blue-soft); }

/* ============================================================
   VOIDPASS — two lanes (PRO gold on top, FREE cyan below) + quests
   ============================================================ */
#screen-pass {
  --cellH: clamp(76px, 13.5vmin, 168px);
  --lvlH: clamp(26px, 4.4vmin, 52px);
  --colW: clamp(92px, 15vmin, 178px);
  --railW: clamp(74px, 12vmin, 150px);
}
.pass-head { display: flex; flex-direction: column; gap: 1vmin; }
.pass-hud { display: flex; align-items: center; gap: 2vmin; padding: clamp(8px, 1.6vmin, 20px) clamp(12px, 2.4vmin, 30px);
  border-radius: var(--r); flex-wrap: wrap; }
.pass-hud .big { font-size: clamp(20px, 4.2vmin, 50px); white-space: nowrap; }
.pass-xpwrap { flex: 1; min-width: 150px; display: flex; align-items: center; gap: 1.4vmin; }
.pass-xpbar { flex: 1; height: clamp(12px, 2vmin, 24px); }
.pass-xpwrap .rj { font-size: clamp(12px, 2.2vmin, 24px); white-space: nowrap; }
.pass-pro { padding: clamp(6px, 1.2vmin, 15px) clamp(14px, 2.6vmin, 34px); border-radius: 999px;
  font-size: clamp(13px, 2.4vmin, 26px); font-weight: 700; white-space: nowrap;
  background: linear-gradient(180deg, rgba(255,215,0,.42), rgba(200,140,0,.34));
  border: 2px solid rgba(255,215,0,.75); color: #FFE87A;
  box-shadow: 0 0 22px rgba(255,215,0,.4); }
.pass-pro.active { background-color: rgba(255,215,0,.2); cursor: default; }

.pass-tabs { display: flex; gap: 1.2vmin; margin: 1.2vmin 0 .6vmin; }
.pass-tab { position: relative; padding: clamp(6px, 1.2vmin, 14px) clamp(16px, 3vmin, 38px);
  border-radius: 999px; font-size: clamp(13px, 2.4vmin, 26px); font-weight: 700;
  color: var(--blue-soft); background: rgba(0,240,255,.1); border: 2px solid rgba(0,240,255,.25); }
.pass-tab.on { color: #fff; background-color: rgba(0,240,255,.26); border-color: var(--cyan);
  box-shadow: 0 0 20px rgba(0,240,255,.35); }
.tab-dot { display: inline-grid; place-items: center; min-width: 1.5em; height: 1.5em; margin-left: .5em;
  border-radius: 999px; background: var(--green); color: #04240a; font-size: .8em; }

.pass-view { flex: 1; min-height: 0; display: flex; gap: 1vmin; padding-bottom: clamp(48px, 8vmin, 96px); }
/* left rail: the two characters, aligned to their lanes */
/* align-content centres the row group so the characters sit level with the
   lanes — the track's columns are vertically centred too */
.pass-rail { width: var(--railW); flex: none; display: grid; align-items: center; align-content: center;
  grid-template-rows: var(--cellH) 8px var(--lvlH) 8px var(--cellH); }
.rail-row { display: flex; flex-direction: column; align-items: center; gap: .3vmin; }
.rail-row.pro { grid-row: 1; }
.rail-row.free { grid-row: 5; }
.rail-row img { width: 78%; max-height: 74%; object-fit: contain; }
.rail-row .rj { font-size: clamp(11px, 2vmin, 22px); font-weight: 700; letter-spacing: .1em; }
.rail-row.pro .rj { color: #FFD700; text-shadow: 0 0 12px rgba(255,215,0,.6); }
.rail-row.free .rj { color: var(--cyan); text-shadow: 0 0 12px rgba(0,240,255,.6); }

.pass-scroll { flex: 1; min-width: 0; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; scroll-behavior: smooth; }
/* "back to my level" — appears once you've scrolled away from your current
   level, so browsing the track never loses your place. */
.pass-here {
  position: absolute; right: clamp(10px, 2vmin, 24px); bottom: clamp(46px, 8vmin, 96px);
  z-index: 7; display: flex; align-items: center; gap: .4em;
  padding: clamp(6px, 1.2vmin, 12px) clamp(12px, 2.2vmin, 24px); border-radius: 999px;
  font: 700 clamp(13px, 2.2vmin, 22px) 'Rajdhani'; color: #04122a; cursor: pointer;
  background: linear-gradient(90deg, #7FE4FF, #00F0FF); border: none;
  box-shadow: 0 0 22px rgba(0,240,255,.6), 0 4px 12px rgba(0,0,0,.45);
  transition: opacity .25s ease, transform .25s var(--spring);
}
.pass-here:active { transform: scale(.96); }
.pass-here.hidden { display: none !important; }
.pass-track { display: flex; align-items: center; height: 100%; }
.pass-col { width: var(--colW); flex: none; display: grid; align-items: center; justify-items: center;
  grid-template-rows: var(--cellH) 8px var(--lvlH) 8px var(--cellH); }
/* the progression lines: one segment per column, so they read as one long line */
.pass-seg { width: 100%; height: clamp(4px, .7vmin, 8px); background: rgba(255,255,255,.1); }
.pass-seg.pro.on { background: linear-gradient(90deg, #FFD700, #FFB300); box-shadow: 0 0 16px rgba(255,215,0,.65); }
.pass-seg.free.on { background: linear-gradient(90deg, #00F0FF, #00A6E0); box-shadow: 0 0 16px rgba(0,240,255,.6); }
.pass-lvl { display: grid; place-items: center; min-width: 2.2em; height: 100%; padding: 0 .5em;
  border-radius: 999px; font: 700 clamp(13px, 2.4vmin, 26px)/1 'Rajdhani';
  color: var(--blue-soft); background: rgba(10,20,50,.7); border: 2px solid rgba(120,235,255,.3); }
.pass-lvl.reached { color: #fff; border-color: var(--cyan); box-shadow: 0 0 18px rgba(0,240,255,.45); }
.pass-col.current .pass-lvl { color: #04240a; background: var(--green); border-color: #9dffb0;
  box-shadow: 0 0 24px rgba(0,255,26,.6); }

.pass-cell { width: 88%; height: 88%; border-radius: clamp(10px, 1.8vmin, 22px);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .3vmin;
  background: rgba(90,170,220,.14); border: 2px solid rgba(150,235,255,.28); position: relative;
  transition: transform .15s ease, box-shadow .15s ease, filter .15s ease; }
.pass-cell .pc-art { flex: 1; min-height: 0; display: grid; place-items: center; width: 100%; }
.pass-cell .pc-art img { max-width: 76%; max-height: 100%; object-fit: contain; }
.pass-cell .pc-lbl { font-size: clamp(10px, 1.8vmin, 20px); color: #fff; text-align: center;
  padding: 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
/* PRO lane reads richer: gold rim, warm glass, crown sheen */
.pass-cell.pro { border-color: rgba(255,215,0,.5);
  background: linear-gradient(180deg, rgba(255,215,0,.16), rgba(120,80,0,.16)); }
.pass-cell.pro .pc-lbl { color: #FFE87A; }
.pass-cell.exclusive { border-color: #FFD700; box-shadow: 0 0 30px rgba(255,215,0,.55); }
.pass-cell.locked { opacity: .5; filter: saturate(.6); }
.pass-cell.claimed { opacity: .72; border-color: rgba(0,255,26,.5); }
.pass-cell.needpro { opacity: .78; filter: saturate(.85); }
.pass-cell.needpro::after { content: '🔒'; position: absolute; top: 4px; right: 6px; font-size: clamp(11px, 1.9vmin, 20px); }
.pass-cell.ready { border-color: var(--green); box-shadow: 0 0 26px rgba(0,255,26,.45); animation: pass-pulse 1.6s ease-in-out infinite; }
.pass-cell.pro.ready { border-color: #FFD700; box-shadow: 0 0 30px rgba(255,215,0,.6); }
@keyframes pass-pulse { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
.pass-cell.clickable { cursor: pointer; }
.pass-cell.clickable:hover { transform: translateY(-7px) scale(1.04); filter: brightness(1.12); }
.pc-tick { position: absolute; top: 3px; right: 7px; color: var(--green);
  font: 700 clamp(13px, 2.2vmin, 24px) 'Rajdhani'; text-shadow: 0 0 10px rgba(0,255,26,.8); }

/* ---- quests ---- */
.quest-cols { flex: 1; min-height: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 92%), 1fr));
  gap: 2vmin; overflow-y: auto; align-content: start; }
.quest-col h2 { font-size: clamp(16px, 3.2vmin, 38px); display: flex; align-items: baseline; gap: 1vmin; }
.q-reset { font-size: clamp(10px, 1.8vmin, 18px); color: var(--blue-soft); font-family: 'Rajdhani'; }
.quest-list { display: flex; flex-direction: column; gap: 1.2vmin; margin-top: 1vmin; }
/* A quest row is a TEXT surface, not a decorative panel.
 *
 * It used to sit on --glass (a ~20% tint), which over the nebula meant every
 * name, counter and reward was competing with a starfield — reported as
 * "quests are hard to read". Legibility wins over translucency here: the row
 * keeps the sheen and the lit border so it still reads as glass, but the text
 * now has an actual ground under it. */
.quest { padding: clamp(10px, 1.8vmin, 22px); border-radius: var(--r);
  background: var(--sheen), rgba(8, 18, 46, .84);
  border: 2px solid rgba(0,240,255,.32); display: flex; flex-direction: column; gap: .8vmin; }
.quest.ready { border-color: var(--green); box-shadow: 0 0 22px rgba(0,255,26,.32); }
.quest.done { opacity: .6; }
/* Pro-subscription quests — gold, so the richer rewards read as the paid lane
   at a glance. `.ready` still wins on the border so a claimable one stands out. */
.quest.q-pro {
  border-color: rgba(255,215,0,.55);
  background: linear-gradient(180deg, rgba(255,215,0,.10), var(--glass));
}
.quest.q-pro.ready { border-color: var(--green); }
.q-protag {
  font: 800 .78em 'Rajdhani'; letter-spacing: .06em; color: #241a00;
  background: linear-gradient(90deg, #FFE87A, #FFB020);
  padding: .1em .45em; border-radius: 999px; vertical-align: .08em;
}
.q-empty { color: var(--blue-soft); font-size: clamp(12px, 2.1vmin, 20px); opacity: .8; padding: 1vmin 0; }
/* Locked Pro quest seen by a free player: still legible and still shows real
   progress (that's the sell), but visibly not claimable — muted gold, dashed
   edge, and the bar drained of its "ready" green. */
.quest.q-locked { border-style: dashed; border-color: rgba(255,215,0,.45); opacity: .9; }
.quest.q-locked .q-fill { filter: saturate(.5) brightness(.9); }
.quest.q-locked .q-claim {
  color: #241a00; background: linear-gradient(90deg, #FFE87A, #FFB020);
  border-color: rgba(255,215,0,.6); cursor: pointer; opacity: 1;
}
.quest.q-locked .q-rw { color: #FFD700; }
.q-top { display: flex; justify-content: space-between; gap: 1vmin; align-items: baseline; flex-wrap: wrap; }
.q-text { color: #fff; font-weight: 700; font-size: clamp(13px, 2.4vmin, 26px); }
.q-rw { color: var(--yellow); font-weight: 700; font-size: clamp(11px, 2vmin, 22px); white-space: nowrap; }
.q-bar { height: clamp(8px, 1.4vmin, 16px); border-radius: 999px; background: rgba(0,0,0,.5); overflow: hidden; }
.q-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, #00F0FF, #00A6E0); transition: width .3s ease; }
.q-fill.gold { background: linear-gradient(90deg, #FFD700, #FF8D28); }
.q-foot { display: flex; align-items: center; justify-content: space-between; gap: 1vmin; }
.q-foot .rj { color: var(--blue-soft); font-weight: 700; font-size: clamp(11px, 2vmin, 22px); }
.q-claim { padding: clamp(5px, 1vmin, 12px) clamp(14px, 2.6vmin, 32px); border-radius: 999px;
  font-weight: 700; font-size: clamp(12px, 2.2vmin, 24px);
  background: rgba(0,255,26,.28); border: 2px solid rgba(0,255,26,.6); color: #b6ffc4; }
/* "In progress" is a STATUS, and it was the least readable text on the page —
   a dim tint on a dim pill on a starfield. Same shape, real contrast. */
.q-claim:disabled { background-color: rgba(10,22,54,.72); border-color: rgba(150,190,220,.42);
  color: #cfe6ff; cursor: default; }

/* ============================================================
   CRATE OPENING SCREEN
   ============================================================ */
#screen-crate { align-items: center; justify-content: center; gap: 2vmin; overflow-y: auto; }
#screen-crate h1 { text-align: center; font-size: clamp(22px, 5vmin, 58px); flex: none; }
.crate-stage { position: relative; display: flex; flex-direction: column; align-items: center; gap: 2.4vmin; flex: none; }
.crate-art { width: clamp(110px, 20vmin, 240px); height: clamp(110px, 20vmin, 240px); object-fit: contain;
  animation: crate-pop .5s cubic-bezier(.2,.9,.3,1.4) both;
  filter: drop-shadow(0 0 26px rgba(0,240,255,.5)); }
.crate-art.tier-big { filter: drop-shadow(0 0 30px rgba(255,140,40,.55)); }
.crate-art.tier-huge { filter: drop-shadow(0 0 38px rgba(255,215,0,.7)); }
@keyframes crate-pop { 0% { transform: scale(.4) rotate(-12deg); opacity: 0; } 60% { transform: scale(1.12) rotate(4deg); } 100% { transform: none; opacity: 1; } }

/* --- reveal: wind-up shake -> burst flash --- */
.crate-art.winding { animation: crate-shake .82s ease-in-out both; }
@keyframes crate-shake {
  0% { transform: scale(.9) rotate(0); opacity: .85; }
  15%,45%,75% { transform: scale(1.02) rotate(-7deg); }
  30%,60%,90% { transform: scale(1.02) rotate(7deg); }
  100% { transform: scale(.82) rotate(0); opacity: 1; }   /* crouch before the burst */
}
.crate-art.burst { animation: crate-burst .55s cubic-bezier(.2,.9,.3,1.6) both; }
@keyframes crate-burst { 0% { transform: scale(.82); } 45% { transform: scale(1.28); } 100% { transform: scale(1); } }
.crate-flash {
  position: absolute; left: 50%; top: clamp(70px, 13vmin, 150px); z-index: 0; pointer-events: none;
  width: clamp(220px, 62vmin, 720px); height: clamp(220px, 62vmin, 720px);
  border-radius: 50%; opacity: 0; transform: translate(-50%, -50%) scale(0);
  background: radial-gradient(circle, rgba(255,255,255,.95) 0%, rgba(0,240,255,.55) 34%, rgba(0,240,255,0) 70%);
}
.crate-stage.legendary .crate-flash {
  background: radial-gradient(circle, rgba(255,255,255,.98) 0%, rgba(255,215,0,.7) 32%, rgba(255,120,200,0) 72%);
}
.crate-flash.go { animation: crate-flash .6s ease-out; }
@keyframes crate-flash {
  0% { opacity: 0; transform: translate(-50%,-50%) scale(0); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%,-50%) scale(1.7); }
}
.crate-stage.legendary .crate-art { filter: drop-shadow(0 0 44px rgba(255,215,0,.9)) drop-shadow(0 0 80px rgba(255,120,200,.5)); }
.crate-items { display: flex; flex-wrap: wrap; justify-content: center; gap: 1.6vmin;
  max-width: min(94vw, 1180px); }
.crate-item { width: clamp(96px, 15vmin, 170px); padding: clamp(10px, 1.8vmin, 22px);
  border-radius: var(--r); background: var(--glass); border: 2px solid rgba(150,235,255,.35);
  display: flex; flex-direction: column; align-items: center; gap: .5vmin;
  animation: item-flip .42s cubic-bezier(.2,.9,.3,1.35) both; }
@keyframes item-flip { from { opacity: 0; transform: rotateY(70deg) translateY(26px) scale(.85); } to { opacity: 1; transform: none; } }
.crate-item { position: relative; }
.crate-item.rare { border-color: rgba(120,180,255,.8); box-shadow: 0 0 24px rgba(90,150,255,.45); }
.crate-item.epic { border-color: #FFD700; box-shadow: 0 0 30px rgba(255,215,0,.6); }
/* legendary = the 0.01% free-Pro jackpot: gold/pink gradient, pulsing glow, ribbon */
.crate-item.legendary {
  border-color: #FFD700; overflow: visible;
  background: linear-gradient(135deg, rgba(255,180,40,.34), rgba(255,0,120,.26), rgba(160,60,220,.32));
  animation: item-flip .42s cubic-bezier(.2,.9,.3,1.35) both, legend-glow 1.5s ease-in-out .5s infinite;
}
@keyframes legend-glow {
  0%,100% { box-shadow: 0 0 34px rgba(255,215,0,.75), inset 0 0 18px rgba(255,215,0,.25); }
  50%     { box-shadow: 0 0 60px rgba(255,215,0,1), inset 0 0 26px rgba(255,120,200,.45); }
}
.crate-item .ci-ribbon {
  position: absolute; top: -12px; left: 50%; transform: translateX(-50%);
  padding: 2px 12px; border-radius: 999px; white-space: nowrap;
  font-weight: 800; font-size: clamp(9px, 1.5vmin, 16px); letter-spacing: 1px; color: #241a00;
  background: linear-gradient(90deg, #FFE87A, #FFB020); box-shadow: 0 0 16px rgba(255,215,0,.8);
}
.crate-item.legendary .ci-amt { color: #FFE87A; }
.crate-item .ci-ico { height: clamp(40px, 7vmin, 84px); display: grid; place-items: center; }
/* absolute max-height — a % one doesn't resolve against the clamp() in a flex column */
.crate-item .ci-ico img { max-height: clamp(40px, 7vmin, 84px); max-width: 100%; object-fit: contain; }
.crate-item .ci-xp { color: var(--cyan); font-size: clamp(18px, 3.4vmin, 40px); text-shadow: 0 0 14px rgba(0,240,255,.7); }
.crate-item .ci-amt { color: #fff; font-size: clamp(15px, 2.8vmin, 32px); }
.crate-item.epic .ci-amt { color: #FFE87A; }
.crate-item .ci-lbl { color: var(--blue-soft); font-size: clamp(10px, 1.8vmin, 20px); }
.crate-collect { padding: clamp(10px, 2vmin, 26px) clamp(40px, 9vmin, 120px); font-size: clamp(18px, 3.6vmin, 44px); }
/* Brazil loot-box disclosure list shown inside the pre-purchase dialog */
.crate-odds { list-style: none; margin: 10px 0 4px; padding: 0; text-align: left; display: flex; flex-direction: column; gap: 6px; }
.crate-odds li { color: var(--blue-soft); padding: 8px 14px; border-radius: 12px; background: rgba(0,240,255,.08); border: 1px solid rgba(0,240,255,.2); }
.crate-odds li.yellow { color: var(--yellow); background: rgba(255,215,0,.1); border-color: rgba(255,215,0,.35); }
.crate-odds li b { color: #fff; }
.crate-odds li.yellow b { color: var(--yellow); }

/* ============================================================
   FREE VS PRO
   ============================================================ */
#screen-pro h1 { text-align: center; font-size: clamp(18px, 4.4vmin, 52px); }
.pro-cols { flex: 1; display: flex; gap: 3vmin; justify-content: center; margin-top: 2vmin; padding-bottom: 10vmin; min-height: 0; }
.pro-card { flex: 1; max-width: 560px; padding: clamp(16px, 3.4vmin, 44px); display: flex; flex-direction: column; align-items: center; gap: 1.6vmin; overflow-y: auto; }
.pro-card.gold { background: rgba(215, 180, 60, 0.32); border-color: rgba(255, 215, 0, .5); cursor: pointer; }
.pro-card h2 { font-size: clamp(24px, 5.4vmin, 64px); }
.pro-card ul { list-style: none; display: flex; flex-direction: column; gap: 1.2vmin; text-align: center; font-size: clamp(14px, 2.8vmin, 30px); font-weight: 600; }
.pro-card .price { margin-top: auto; font-size: clamp(20px, 4.6vmin, 56px); display: flex; align-items: baseline; gap: 0.5em; justify-content: center; flex-wrap: wrap; }
.pro-card .price .was { font-size: 0.5em; text-decoration: line-through; opacity: 0.55; font-weight: 400; }
.pro-card .discount-tag { font-size: clamp(11px, 1.6vmin, 16px); color: var(--green); font-weight: 700;
  border: 1px solid rgba(0,255,26,.5); border-radius: 999px; padding: 0.2em 0.9em; background: rgba(0,255,26,.08); }

/* ============================================================
   GAME OVER / WIN / LOSE (design-space, inside .stage)
   ============================================================ */
.stage .go-title { font-family:'Monoton'; text-align:center; font-size:120px; color:var(--cyan);
  text-shadow:0 0 34px rgba(0,240,255,.7); }
.stage .go-sub2 { text-align:center; font-weight:600; font-size:48px; color:var(--cyan); }
.stage .go-podium { border-radius:44px; display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:22px; padding:40px 20px; backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px); text-align:center;
  border: 2px solid rgba(255,255,255,.15); }
.stage .go-podium .go-av { width:180px; height:180px; }
.stage .go-podium .go-pname { font-weight:700; font-size:56px; max-width:96%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.stage .go-podium .go-ppts { font-weight:600; font-size:46px; }
.stage .go-podium.p1 { background-color: rgba(215,180,60,.42); color:var(--yellow); box-shadow:0 0 50px rgba(255,215,0,.3); }
.stage .go-podium.p2 { background-color: rgba(160,175,200,.32); color:#e3ecff; }
.stage .go-podium.p3 { background-color: rgba(190,120,60,.34); color:#ffc08a; }
.stage .go-tip { background:rgba(0,170,215,.4); border:2px solid rgba(120,230,255,.4); border-radius:44px;
  padding:26px 44px; font-weight:600; font-size:52px; color:var(--cyan); text-align:center;
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); }
.stage .go-tip .go-tt { font-family:'Monoton'; }
.stage .go-stats { padding:30px 50px; text-align:center; }
.stage .go-stats .go-sh { font-family:'Monoton'; color:var(--cyan); font-size:60px; }
.stage .go-stats .go-sl { color:var(--green); font-family:'Monoton'; font-size:46px; text-align:left; margin-top:8px; }
.stage .go-delta { display:flex; align-items:center; justify-content:center; gap:26px; }
.stage .go-delta .go-plus { font-family:'Monoton'; font-size:110px; color:var(--cyan); }
.stage .go-delta .go-trophy { height:150px; width:auto; }
.stage .go-exit { display:grid; place-items:center; font-family:'Monoton'; font-size:64px; color:var(--red); border-radius:44px; }
.stage .go-again { display:grid; place-items:center; font-family:'Rajdhani'; font-weight:700; font-size:66px; color:var(--green); border-radius:44px; }

/* legacy responsive game-over (unused) */
.go-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; min-height: 0; overflow-y: auto; padding-bottom: 1vmin; }
.go-title { font-family: 'Monoton'; font-size: clamp(24px, 5.4vmin, 62px); color: var(--cyan); text-shadow: 0 0 20px rgba(0,240,255,.6); text-align: center; }
.go-sub { font-weight: 600; font-size: clamp(13px, 2.6vmin, 28px); color: var(--cyan); text-align: center; margin-top: .6vmin; }
.podium { display: flex; align-items: flex-end; gap: 2vmin; margin: 3vmin 0 2vmin; }
.podium-card {
  width: clamp(96px, 18vmin, 220px); border-radius: var(--r);
  display: flex; flex-direction: column; align-items: center; gap: .8vmin;
  padding: clamp(10px, 2vmin, 26px);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  text-align: center;
}
.podium-card .avatar-disc { width: clamp(44px, 8.4vmin, 96px); height: clamp(44px, 8.4vmin, 96px); }
.podium-card .pname { font-weight: 700; font-size: clamp(12px, 2.4vmin, 26px); overflow: hidden; text-overflow: ellipsis; max-width: 100%; white-space: nowrap; }
.podium-card .ppoints { font-weight: 600; font-size: clamp(11px, 2.2vmin, 24px); }
.podium-card.p1 { background-color: rgba(215, 180, 60, .45); height: clamp(150px, 30vmin, 340px); color: var(--yellow); }
.podium-card.p2 { background-color: rgba(160, 170, 190, .35); height: clamp(120px, 24vmin, 280px); color: #dfe6f2; }
.podium-card.p3 { background-color: rgba(190, 120, 60, .35); height: clamp(100px, 20vmin, 230px); color: #ffb37a; }
.tip-panel {
  background: rgba(0, 170, 210, .4); border-radius: var(--r); border: 1px solid rgba(0,240,255,.35);
  padding: clamp(10px, 2.2vmin, 28px) clamp(16px, 3.4vmin, 44px);
  font-size: clamp(15px, 3vmin, 34px); font-weight: 600; color: var(--cyan); text-align: center;
  max-width: min(92%, 1100px);
}
.tip-panel .tt { font-family: 'Monoton'; margin-right: .5em; }
.go-btns { position: absolute; bottom: clamp(10px, 2vmin, 26px); left: 0; right: 0; display: flex; justify-content: space-between; padding: 0 clamp(12px, 2.6vmin, 34px); }
.go-btns .glass { padding: clamp(8px, 1.8vmin, 22px) clamp(22px, 5vmin, 66px); font-size: clamp(18px, 4vmin, 48px); font-weight: 600; }
.stats-panel {
  background: rgba(0, 140, 190, .3); border-radius: var(--r); border: 1px solid rgba(0,240,255,.3);
  padding: clamp(12px, 2.6vmin, 32px); width: min(94%, 1100px); text-align: center;
  display: flex; flex-direction: column; gap: 1vmin;
}
.stats-panel .sh { font-family: 'Monoton'; color: var(--cyan); font-size: clamp(18px, 4vmin, 46px); }
.stats-panel .sl { font-family: 'Monoton'; color: var(--green); font-size: clamp(14px, 3.2vmin, 38px); text-align: left; }
.trophy-delta { display: flex; align-items: center; gap: 2vmin; margin-top: 2.4vmin; }
.trophy-delta .mono { font-size: clamp(28px, 6.4vmin, 80px); color: var(--cyan); text-shadow: 0 0 16px rgba(0,240,255,.7); }
.trophy-delta svg { height: clamp(56px, 11vmin, 140px); }

/* ============================================================
   BLITZ GAME TABLE (design-space, inside .stage)
   ============================================================ */
/* timer pill: big clock, "min left" caption, and a progress bar of time left */
.stage .bz-timer-pill {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 18px 34px 22px; pointer-events: none; overflow: hidden;
}
.stage .bz-timeclock {
  font-family: 'Monoton', cursive; font-size: 104px; line-height: .9; color: var(--cyan);
  text-shadow: 0 0 28px rgba(0,240,255,.65), 0 0 8px rgba(0,240,255,.5);
  transition: color .5s ease, text-shadow .5s ease; font-variant-numeric: tabular-nums;
}
.stage .bz-timelabel {
  font: 700 30px 'Rajdhani'; letter-spacing: .28em; text-transform: uppercase;
  color: rgba(150,235,255,.85); margin-top: -2px;
}
.stage .bz-timebar {
  width: 78%; height: 12px; border-radius: 999px; margin-top: 6px;
  background: rgba(3,12,34,.6); box-shadow: inset 0 1px 3px rgba(0,0,0,.5); overflow: hidden;
}
.stage .bz-timebar-fill {
  display: block; height: 100%; width: 100%; border-radius: 999px;
  background: linear-gradient(90deg, #00F0FF, #4EA8FF);
  box-shadow: 0 0 14px rgba(0,240,255,.7); transition: width .3s linear, background .4s ease;
}
.stage .bz-timebar-fill.warn { background: linear-gradient(90deg, #FF3030, #FF7A3C); box-shadow: 0 0 14px rgba(255,60,60,.7); }
.stage .bz-timeclock.warn { color: #FF4040; text-shadow: 0 0 26px rgba(255,60,60,.75); }
.stage .bz-pile { border-radius: 18px; }
.stage .bz-pile svg, .stage .bz-pile .card-img { width: 100%; height: 100%; filter: drop-shadow(0 8px 16px rgba(0,0,0,.55)); }
@keyframes bz-discard-in {
  /* keep the pile's translateX(-50%) centring — the animation transform
     replaces the inline one, so it must be part of every keyframe */
  0% { transform: translateX(-50%) translateY(-140px) rotate(-10deg) scale(.72); opacity: 0; }
  60% { transform: translateX(-50%) translateY(6px) rotate(2deg) scale(1.03); opacity: 1; }
  100% { transform: translateX(-50%) translateY(0) rotate(0) scale(1); opacity: 1; }
}
.stage .bz-pile.bz-discard { animation: bz-discard-in .9s cubic-bezier(.22,1,.36,1) both; }
@keyframes bz-turn-pulse {
  0%, 100% { box-shadow: 0 0 36px rgba(0,255,26,.45); }
  50% { box-shadow: 0 0 90px rgba(0,255,26,.95); }
}
.stage .bz-disc.turn, .stage .bz-avatar.turn { animation: bz-turn-pulse 2.6s ease-in-out infinite; }

/* direction-of-play loop around the centre piles: 4 arrows chase each other
   along a rounded-square band (CSS motion path; wrapper mirrored for CCW) */
.stage .bz-dir { transform: translateX(-50%); pointer-events: none; z-index: 1; }
.stage .bz-dir svg { width: 100%; height: 100%; overflow: visible; }
.stage .bz-dir-inner { position: absolute; inset: 0; }
/* The play-direction loop: a faint track with arrows chasing round it.
   Behind the piles (z-index 1 above), which sit in the hole. */
.stage .bz-dir-track { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
/* Colour is driven by --dir-tint rather than a fill/stroke attribute.
 *
 * The board rebuilds this markup on every state update, so a CSS transition on
 * the elements themselves can never run — each render starts a brand new node
 * already at the new colour. The variable is interpolated in JS instead (see
 * blitz-ui.js) and written onto whichever .bz-dir is live, which survives the
 * rebuild. */
.stage .bz-dir-flow { stroke: var(--dir-tint, #7fe4ff); }
.stage .bz-chaser path { fill: var(--dir-tint, #7fe4ff); }
/* The mask cut stays black whatever the tint is doing. */
.stage .bz-notch path { fill: #000; }

/* The nesting joint.
 *
 * Each band's TAIL carries a concave V-notch, and the arrow behind it sits its
 * solid head inside that notch with a thin, even gap — the arrows slot into one
 * another. A plain butt-ended dash gap is not that shape and reads as four
 * detached pieces.
 *
 * Cut with an SVG mask: the same triangle as the head, in black, riding the
 * same path shifted forward by exactly the gap. Using the identical shape for
 * the cut and the head is what keeps the two edges parallel the whole way
 * round. (CSS has no destination-out blend mode — that is a canvas operator —
 * so this has to be a real mask.) */

/* The arrow BODIES: four thick dashes travelling the loop, each with a gap
   before the next one's tail — the construction in the reference glyph, where
   every arrow is a side-plus-corner band ending in a solid head.
   Animating stroke-dashoffset moves all four at once for one property change,
   rather than four elements each carrying their own transform. */
.stage .bz-dir-flow {
  animation: bz-flow 9s linear infinite;
}
@keyframes bz-flow { to { stroke-dashoffset: calc(var(--lap) * -1); } }
.stage .bz-dir.ccw .bz-dir-flow { animation-direction: reverse; }

/* One arrow. offset-path puts it on the loop and offset-rotate turns it to
   face the way it is travelling, so it leans into the corners by itself. */
/* Heads and notches are SVG <g> riding the path. offset-path works on SVG
   elements, so everything stays in viewBox units and scales with the stage —
   no HTML box whose pixel size has to be kept in step with the geometry. */
.stage .bz-chaser {
  offset-path: path('M110 22 H510 A88 88 0 0 1 598 110 V320 A88 88 0 0 1 510 408 H110 A88 88 0 0 1 22 320 V110 A88 88 0 0 1 110 22 Z');
  offset-rotate: auto;
  offset-distance: 0%;
  animation-name: bz-chase;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  /* NO drop-shadow. The reference glyph is flat solid colour, and a shadow
     under the head is exactly what made it read as a separate object floating
     near the band instead of the end of it. */
}
/* Counterclockwise runs the same animation backwards — one keyframe pair for
   both directions, so they can never drift apart. */
/* Counterclockwise runs the same keyframes backwards, so the element moves
   backward ALONG the path — but offset-rotate:auto still orients it to the
   path's forward tangent, which left every triangle pointing against its own
   direction of travel. The extra 180deg turns them to face the way they are
   actually going. */
.stage .bz-dir.ccw .bz-chaser { animation-direction: reverse; offset-rotate: auto 180deg; }
@keyframes bz-chase { from { offset-distance: 0%; } to { offset-distance: 100%; } }

/* The reverse: the track flashes as the arrows swap direction. */
.stage .bz-dir.flip .bz-dir-track { animation: bz-dirflip 1s cubic-bezier(.3,1.4,.4,1) both; }
@keyframes bz-dirflip {
    0% { opacity: 1;   transform: scale(1); }
   28% { opacity: .35; transform: scale(1.04); }
  100% { opacity: 1;   transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .stage .bz-chaser { animation: none; }
  .stage .bz-dir.flip .bz-dir-track { animation: none; }
}
/* The low tier drops decorative motion; the arrows still show the direction,
   they just stop travelling. */
html[data-gfx="low"] .stage .bz-chaser { animation: none; }
html[data-gfx="low"] .stage .bz-dir.flip .bz-dir-track { animation: none; }

/* chain hint */
.stage .bz-chainhint {
  text-align: center; color: var(--yellow); font-weight: 700; font-size: 44px;
  text-shadow: 0 2px 8px #000, 0 0 18px rgba(255,215,0,.45); z-index: 25;
}

/* "grab a card" bouncing triangle (points down at the deck) */
.stage .bz-grab {
  width: 0; height: 0;
  border-left: 35px solid transparent;
  border-right: 35px solid transparent;
  border-top: 60px solid var(--yellow);
  filter: drop-shadow(0 0 16px rgba(255,215,0,.75));
  animation: bz-grab-bounce 1.3s ease-in-out infinite;
  pointer-events: none; z-index: 25;
}
@keyframes bz-grab-bounce {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50% { transform: translateX(-50%) translateY(26px); }
}

/* pass button after drawing a playable card */
.stage .bz-pass {
  display: grid; place-items: center;
  background: var(--glass-red); border-color: rgba(255,80,80,.5);
  color: var(--red); font: 700 52px 'Rajdhani';
  text-shadow: 0 0 12px rgba(255,60,60,.5); z-index: 25;
}
.stage .bz-color-picker {
  display: flex; align-items: center; justify-content: center; gap: 40px;
  padding: 30px 40px; transform: translateX(-50%);
  z-index: 50;
}
.stage .bz-color-swatch {
  width: 130px; height: 130px; border-radius: 50%;
  border: 4px solid rgba(255,255,255,.4); cursor: pointer;
  box-shadow: 0 0 30px currentColor; color: inherit;
  transition: transform .15s cubic-bezier(.34,1.56,.64,1);
}
.stage .bz-color-swatch:hover { transform: scale(1.12); }
.stage .bz-color-swatch:active { transform: scale(.94); }

.stage .bz-turn-countdown {
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(0, 240, 255, .18);
  border: 3px solid rgba(0, 240, 255, .55);
  color: var(--cyan);
  font-family: 'Monoton', cursive; font-size: 62px; line-height: 1;
  transform: translateX(-50%);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  text-shadow: 0 0 12px rgba(0,240,255,.6);
  transition: background .2s ease, border-color .2s ease, color .2s ease, text-shadow .2s ease;
  z-index: 30;
}
.stage .bz-turn-countdown.warn {
  background: rgba(255, 40, 40, .28);
  border-color: rgba(255, 80, 80, .8);
  color: #FF3030;
  text-shadow: 0 0 14px rgba(255,50,50,.8);
  animation: bz-tc-pulse .9s ease-in-out infinite;
}
@keyframes bz-tc-pulse {
  0%, 100% { transform: translateX(-50%) scale(1); }
  50% { transform: translateX(-50%) scale(1.1); }
}
.stage .card-img { width: 100%; height: 100%; object-fit: contain; display: block; user-select: none;
  -webkit-user-drag: none; }
.stage .bz-draw { cursor: pointer; transition: transform .15s; }
.stage .bz-draw:hover { transform: scale(1.06); }
.stage .bz-mini { border-radius: 14px; }
.stage .bz-mini svg, .stage .bz-mini .card-img { width: 100%; height: 100%; filter: drop-shadow(0 4px 8px rgba(0,0,0,.5)); }
/* teammate's revealed hand (teams mode) — a soft green ring so it reads as "yours to see" */
.stage .bz-mini.bz-partner { outline: 3px solid rgba(80,255,140,.7); outline-offset: -3px;
  box-shadow: 0 0 14px rgba(80,255,140,.4); }
.stage .bz-pending {
  min-width: 96px; height: 70px; padding: 0 20px; display: grid; place-items: center;
  background: var(--glass-red); border: 2px solid rgba(255,80,80,.6); border-radius: 999px;
  color: #fff; font: 700 46px 'Rajdhani'; z-index: 20;
  transform: translateX(-50%);
}
.stage .bz-colordot { border-radius: 50%; box-shadow: 0 0 40px currentColor, 0 0 12px currentColor; z-index: 5; }
.stage .bz-disc {
  border-radius: 50%; display: grid; place-items: center;
  color: #04122a; font: 700 96px 'Rajdhani';
  box-shadow: 0 0 50px rgba(0,240,255,.3), inset 0 -10px 30px rgba(0,0,0,.25);
  opacity: .78; filter: saturate(.85);
  transition: transform .6s cubic-bezier(.22,1,.36,1), opacity .5s ease, filter .5s ease;
}
.stage .bz-disc.turn {
  outline: 10px solid var(--green); box-shadow: 0 0 60px var(--green);
  opacity: 1; filter: saturate(1.1); transform: scale(1.12);
}
.stage .bz-disc.done { outline: 8px solid var(--yellow); opacity: 1; }
.stage .bz-oppname { text-align: center; color: #fff; font-weight: 700; font-size: 46px; transform: translateX(-50%);
  text-shadow: 0 2px 8px #000; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: .8; transition: color .5s ease, opacity .5s ease, text-shadow .5s ease, font-size .4s ease; }
.stage .bz-oppname.turn { color: var(--green); opacity: 1;
  text-shadow: 0 2px 8px #000, 0 0 24px rgba(0,255,26,.8); font-size: 52px; }
.stage .bz-avatar { transform: translateX(-50%);
  transition: transform .6s cubic-bezier(.22,1,.36,1), box-shadow .5s ease; }
.stage .bz-avatar.turn { outline: 10px solid var(--green); box-shadow: 0 0 50px var(--green);
  border-radius: 50%; transform: translateX(-50%) scale(1.12); }
.stage .bz-call {
  border-radius: 50%; display: grid; place-items: center; text-align: center;
  transform: translateX(-50%); color: var(--cyan); font: 700 34px 'Rajdhani'; padding: 20px;
}
.stage .bz-call.called { color: var(--green); box-shadow: inset 0 0 40px rgba(0,255,26,.2), 0 0 40px rgba(0,255,26,.5); }
.stage .bz-hint-fixed { color: #cfefff; font-size: 34px; font-weight: 600; text-shadow: 0 2px 6px #000; }
.stage .bz-hand { border-radius: 16px; cursor: default; transition: transform .15s; }
.stage .bz-hand svg, .stage .bz-hand .card-img { width: 100%; height: 100%; filter: drop-shadow(0 8px 14px rgba(0,0,0,.6)); }
.stage .bz-hand.playable { cursor: pointer; }
.stage .bz-hand.playable svg, .stage .bz-hand.playable .card-img { filter: drop-shadow(0 0 16px rgba(0,255,26,.9)) drop-shadow(0 8px 14px rgba(0,0,0,.6)); }
.stage .bz-hand.playable { animation: bzlift 1.6s ease-in-out infinite; }
@keyframes bzlift { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-22px); } }
.stage .bz-hand.playable:hover { transform: translateY(-46px) scale(1.08); z-index: 99 !important; animation: none; }
.stage .bz-hand.dim svg, .stage .bz-hand.dim .card-img { filter: grayscale(.55) brightness(.6) drop-shadow(0 8px 14px rgba(0,0,0,.6)); }
/* ---- Blitz mobile performance ----
 * Blitz is the busiest screen in the game, and three of its effects repaint
 * every single frame on a phone. Each is replaced by a static equivalent that
 * carries the SAME information, so nothing about how the game reads changes:
 *   1. .bz-hand.playable animates transform on a card whose <img> carries a
 *      16px green drop-shadow — a filtered layer re-rasterised each frame, once
 *      per playable card (up to ~14 at a time). Now a static lift, and the glow
 *      moves to a box-shadow on the container so the image isn't re-filtered.
 *   2. bz-turn-pulse animates box-shadow BLUR RADIUS, which can never be
 *      composited — it forces a full repaint of the disc every frame. The
 *      whose-turn signal is already carried by the green outline + scale.
 *   3. The direction ring's 4 chasers animate offset-distance along a
 *      motion-path; JS drops that to 2 on a phone (see blitz-ui.js).
 * The timer's own warn animation is deliberately left alone. */
body.mobile .stage .bz-hand.playable {
  animation: none;
  transform: translateY(-18px);
  box-shadow: 0 0 26px rgba(0,255,26,.85);
}
body.mobile .stage .bz-hand.playable svg,
body.mobile .stage .bz-hand.playable .card-img {
  filter: drop-shadow(0 8px 14px rgba(0,0,0,.6));
}
body.mobile .stage .bz-disc.turn,
body.mobile .stage .bz-avatar.turn {
  animation: none;
  box-shadow: 0 0 70px rgba(0,255,26,.85);
}

.stage .bz-timer.warn { animation: bz-timer-warn 1s ease-in-out infinite; }
@keyframes bz-timer-warn {
  0%, 100% { box-shadow: 0 0 30px rgba(255,60,60,.4); }
  50% { box-shadow: 0 0 60px rgba(255,60,60,.8); }
}

/* legacy responsive blitz (unused now, kept for safety) */
.game-screen { padding: 0; }
.blitz-table { position: absolute; inset: 0; }
.blitz-timer {
  position: absolute; top: 1.6vmin; right: 1.6vmin; padding: clamp(6px, 1.4vmin, 18px) clamp(14px, 2.8vmin, 34px);
  font-family: 'Monoton'; font-size: clamp(20px, 4.4vmin, 52px); color: var(--cyan); z-index: 5;
}
.blitz-timer .unit { font-size: 40%; }
.blitz-center { position: absolute; left: 50%; top: 44%; transform: translate(-50%, -50%); display: flex; gap: 2.4vmin; align-items: center; z-index: 3; }
.blitz-pile { width: clamp(70px, 13vmin, 160px); aspect-ratio: 5/7; position: relative; }
.blitz-pile svg { width: 100%; height: 100%; border-radius: 10%; }
.blitz-pile.drawpile { cursor: pointer; transition: transform .15s; }
.blitz-pile.drawpile:hover { transform: scale(1.05); }
.pending-badge {
  position: absolute; top: -12%; right: -12%; background: var(--glass-red); color: #fff;
  border-radius: 999px; padding: 4px 12px; font-weight: 700; font-size: clamp(13px, 2.4vmin, 26px);
}
.opp { position: absolute; display: flex; flex-direction: column; align-items: center; gap: .6vmin; z-index: 2; }
.opp .opp-avatar {
  width: clamp(44px, 9vmin, 110px); height: clamp(44px, 9vmin, 110px); border-radius: 50%;
  display: grid; place-items: center; font-weight: 700; color: #04041a;
  font-size: clamp(16px, 3.4vmin, 40px); box-shadow: 0 0 20px rgba(0,240,255,.35);
}
.opp .opp-name { font-weight: 700; color: #fff; font-size: clamp(11px, 2.2vmin, 24px); max-width: 16vmin; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opp .opp-fan { display: flex; }
.opp .opp-fan .mini-back { width: clamp(22px, 4.4vmin, 54px); aspect-ratio: 5/7; margin-left: calc(clamp(22px, 4.4vmin, 54px) * -0.55); border-radius: 12%; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,.5); }
.opp .opp-fan .mini-back:first-child { margin-left: 0; }
.opp .opp-fan svg { width: 100%; height: 100%; display: block; }
.opp.turn .opp-avatar { outline: 4px solid var(--green); box-shadow: 0 0 26px var(--green); }
.opp.done .opp-avatar { outline: 4px solid var(--yellow); }
.opp-top { top: 1.6vmin; left: 50%; transform: translateX(-50%); flex-direction: row; gap: 1.4vmin; align-items: center; }
.opp-left { left: 1.6vmin; top: 42%; transform: translateY(-50%); flex-direction: row; gap: 1.4vmin; align-items: center; }
.opp-right { right: 1.6vmin; top: 42%; transform: translateY(-50%); flex-direction: row-reverse; gap: 1.4vmin; align-items: center; }
.opp-left .opp-fan, .opp-right .opp-fan { flex-direction: column; }
.opp-left .mini-back, .opp-right .mini-back { margin-left: 0 !important; margin-top: calc(clamp(22px, 4.4vmin, 54px) * -0.8); }
.opp-left .mini-back:first-child, .opp-right .mini-back:first-child { margin-top: 0 !important; }

.my-zone { position: absolute; bottom: 0; left: 0; right: 0; display: flex; align-items: flex-end; padding: 0 1.6vmin 1.2vmin; z-index: 4; }
.my-avatar {
  width: clamp(48px, 10vmin, 120px); height: clamp(48px, 10vmin, 120px); flex: none; margin-right: 1.6vmin;
  border-radius: 50%;
}
.my-zone.turn .my-avatar { outline: 4px solid var(--green); box-shadow: 0 0 26px var(--green); }
.my-hand { flex: 1; display: flex; justify-content: center; align-items: flex-end; overflow-x: auto; padding: 3vmin 1vmin 0; }
.hand-card {
  width: clamp(58px, 11.4vmin, 140px); aspect-ratio: 5/7; flex: none;
  margin-left: calc(clamp(58px, 11.4vmin, 140px) * -0.42);
  transition: transform .16s; cursor: pointer; position: relative; border-radius: 10%;
  filter: drop-shadow(0 6px 12px rgba(0,0,0,.55));
}
.hand-card:first-child { margin-left: 0; }
.hand-card svg { width: 100%; height: 100%; display: block; }
.hand-card.playable:hover, .hand-card.playable.tapped { transform: translateY(-16%) scale(1.06); z-index: 3; }
.hand-card.playable { outline: 3px solid rgba(0, 255, 26, .8); outline-offset: -2px; border-radius: 10%; }
.hand-card.disabled { filter: grayscale(.5) brightness(.6) drop-shadow(0 6px 12px rgba(0,0,0,.55)); }
.blitz-draw-hint { position: absolute; bottom: 26vmin; left: 50%; transform: translateX(-50%); color: var(--yellow); font-weight: 700; font-size: clamp(13px, 2.6vmin, 28px); z-index: 5; text-align: center; }
.color-dot { position: absolute; left: 50%; top: 26%; transform: translate(-50%, -50%); width: clamp(26px, 5vmin, 60px); height: clamp(26px, 5vmin, 60px); border-radius: 50%; box-shadow: 0 0 20px currentColor; z-index: 3; }

.color-picker { display: flex; gap: 2.4vmin; }
.color-picker button { width: clamp(50px, 10vmin, 110px); height: clamp(50px, 10vmin, 110px); border-radius: 50%; box-shadow: 0 0 24px currentColor; transition: transform .12s; }
.color-picker button:hover { transform: scale(1.1); }

/* ============================================================
   HACKER GAME (design-space, inside .stage)
   ============================================================ */
.stage .hk-title { font-family: 'Monoton'; text-align: center; font-size: 96px; color: var(--cyan);
  text-shadow: 0 0 26px rgba(0,240,255,.6); }
.stage .hk-sub { text-align: center; font-weight: 600; font-style: italic; font-size: 46px; color: var(--cyan); }
.stage .hk-role { font-weight: 700; font-size: 44px; color: var(--yellow); text-shadow: 0 2px 6px #000; }
.stage .hk-timer { font-family: 'Monoton'; font-size: 72px; color: var(--cyan); }
.stage .hk-user { display: flex; align-items: center; gap: 22px; padding: 0 30px; border-radius: 999px;
  background: rgba(30,150,210,.16); border: 2px solid rgba(120,230,255,.3);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); cursor: default; transition: filter .12s; }
.stage .hk-user .hk-av { width: 84px; height: 84px; flex: none; }
.stage .hk-user .hk-name { font: 700 56px 'Rajdhani'; color: var(--cyan); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Name colour is whatever the server says this viewer is allowed to know
   (state.users[].color): own name in your role's colour, fellow hackers red,
   fellow recoveries yellow, everyone else cyan. */
.stage .hk-user.col-cyan   .hk-name { color: var(--cyan); }
.stage .hk-user.col-green  .hk-name { color: var(--green); }
.stage .hk-user.col-red    .hk-name { color: var(--red); }
.stage .hk-user.col-yellow .hk-name { color: var(--yellow); }
.stage .hk-user.col-blue   .hk-name { color: var(--blue-soft); }
.stage .hk-user.col-orange .hk-name { color: var(--orange); }
.stage .hk-user.col-purple .hk-name { color: var(--purple); }
.stage .hk-user.me { border-color: rgba(0,255,26,.5); }
.stage .hk-user.dead { opacity: .45; }
.stage .hk-user.dead .hk-name { text-decoration: line-through; }
.stage .hk-user.sel { outline: 5px solid var(--yellow); }
.stage .hk-user.locked { opacity: .5; }
.stage .hk-user[onclick], .stage .hk-user.clickable { cursor: pointer; }
/* Security's private check result — a dot only the Security player is sent */
.stage .hk-user .hk-chk { width: 40px; height: 40px; border-radius: 50%; flex: none;
  margin-left: auto; border: 3px solid rgba(0,0,0,.45); box-shadow: 0 0 14px currentColor; }
.stage .hk-user.chk-white  .hk-chk { background: #fff;         color: #fff; }
.stage .hk-user.chk-red    .hk-chk { background: var(--red);    color: var(--red); }
.stage .hk-user.chk-yellow .hk-chk { background: var(--yellow); color: var(--yellow); }
.stage .hk-user.chk-purple .hk-chk { background: var(--purple); color: var(--purple); }
.stage .hk-user.chk-cyan   .hk-chk { background: var(--cyan);   color: var(--cyan); }
.stage .hk-action { display: grid; place-items: center; transform: translateX(-50%); border-radius: 999px;
  background: var(--glass-green); border: 2px solid rgba(0,255,26,.4); color: var(--green); font: 700 52px 'Rajdhani'; }
.stage .hk-action.on { box-shadow: 0 0 30px rgba(0,255,26,.5); }
.stage .hk-status { text-align: center; font-size: 48px; color: var(--cyan); }
/* the "Tied: …" list lives in the gap between the two fate cards; keep it small
   and wrapping so a long tie can never slip behind a card */
.stage .hk-tied { font-size: 34px; line-height: 1.25; }
.stage .hk-overlay { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
  background: rgba(4,4,26,.72); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); z-index: 40; text-align: center; }
.stage .hk-ovt { font-family: 'Monoton'; font-size: 130px; color: var(--cyan); text-shadow: 0 0 34px rgba(0,240,255,.7); }
.stage .hk-ovs { font-weight: 600; font-size: 56px; color: var(--cyan); }
.stage .hk-voterow { display: flex; align-items: center; gap: 30px; padding: 0 40px; transform: translateX(-50%);
  border-radius: 999px; background: rgba(30,150,210,.18); border: 2px solid rgba(120,230,255,.3); }
.stage .hk-voterow .hk-av { width: 84px; height: 84px; flex: none; }
.stage .hk-voterow .hk-vname { flex: 1; font: 700 60px 'Rajdhani'; color: var(--cyan); }
.stage .hk-voterow .hk-vcount { font: 700 56px 'Rajdhani'; color: var(--cyan); }
/* overflow:hidden + capped image height guarantee the art never spills past the
   rounded rectangle, no matter the sprite's aspect. */
.stage .hk-fate { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; overflow: hidden; box-sizing: border-box; padding: 22px; }
.stage .hk-fate img { width: auto; max-width: 90%; max-height: 350px; object-fit: contain; }
/* Both sprites are transparent-background, so a real drop-shadow silhouette-glows
   them — purple/cyan for the guardian, red for THE HAUNT. */
.stage .hk-fate.spare img {
  max-height: 460px; max-width: 100%;
  filter: drop-shadow(0 0 26px rgba(140,120,255,.85)) drop-shadow(0 0 54px rgba(0,200,255,.55));
}
.stage .hk-fate.eliminate img {
  filter: drop-shadow(0 0 26px rgba(255,60,60,.75)) drop-shadow(0 0 52px rgba(255,20,20,.45));
}
/* The phone canvas gives these cards 320px instead of 550, so the art must come
   down or it pushes the label out of the (overflow:hidden) card. Must sit AFTER
   the .spare/.eliminate rules and use .stage.stage-phone — at equal specificity
   the later rule wins, and .spare's 460px would otherwise stand. */
.stage.stage-phone .hk-fate img { max-height: 175px; }
.stage.stage-phone .hk-fate.spare img { max-height: 185px; }
/* Selection glow matches each side's meaning: spare = green, eliminate = red. */
.stage .hk-fate.spare.on { outline: 5px solid var(--green); box-shadow: 0 0 46px rgba(0,255,26,.45); }
.stage .hk-fate.eliminate.on { outline: 5px solid var(--red); box-shadow: 0 0 46px rgba(255,48,48,.5); }
.stage .hk-fatelabel { font-family: 'Monoton'; font-size: 60px; line-height: 1.05; color: var(--cyan); text-align: center; }

/* legacy responsive hacker (unused) */
.hacker-wrap { position: absolute; inset: 0; display: flex; flex-direction: column; padding: clamp(8px, 1.8vmin, 22px); }
.hacker-head { text-align: center; }
.hacker-head .ht { font-family: 'Monoton'; color: var(--cyan); font-size: clamp(20px, 4.6vmin, 54px); text-shadow: 0 0 18px rgba(0,240,255,.5); }
.hacker-head .hs { font-weight: 600; font-style: italic; color: var(--cyan); font-size: clamp(12px, 2.4vmin, 26px); }
.hacker-timer { position: absolute; top: 1.4vmin; right: 1.6vmin; font-family: 'Monoton'; font-size: clamp(16px, 3.6vmin, 42px); color: var(--cyan); z-index: 5; }
.role-banner { position: absolute; top: 1.4vmin; left: 1.6vmin; font-weight: 700; font-size: clamp(12px, 2.4vmin, 26px); color: var(--yellow); z-index: 5; max-width: 30vw; }
.hacker-grid {
  flex: 1; overflow-y: auto; margin-top: 1.2vmin;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1vmin; align-content: start;
}
.huser {
  display: flex; align-items: center; gap: 1vmin; border-radius: 999px;
  background: var(--glass); border: 1px solid rgba(0,240,255,.22);
  padding: clamp(4px, 1vmin, 12px) clamp(8px, 1.6vmin, 20px);
  cursor: pointer; transition: filter .12s;
}
.huser:hover { filter: brightness(1.3); }
.huser .avatar-disc { width: clamp(24px, 4.6vmin, 52px); height: clamp(24px, 4.6vmin, 52px); }
.huser .uname { font-weight: 700; font-size: clamp(12px, 2.6vmin, 30px); color: var(--cyan); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.huser.me .uname { color: var(--green); }
.huser.hackmate .uname { color: var(--red); }
.huser.dead { opacity: .45; }
.huser.dead .uname { color: var(--purple); text-decoration: line-through; }
.huser.selected { outline: 3px solid var(--yellow); }
.huser.checked-hackers .uname { color: var(--red); }
.huser.checked-guests .uname { color: var(--yellow); }
.hacker-actionbar { display: flex; gap: 1.4vmin; justify-content: center; padding: 1vmin 0; flex-wrap: wrap; }
.hacker-actionbar .glass { padding: clamp(6px, 1.4vmin, 16px) clamp(16px, 3.4vmin, 44px); font-weight: 700; font-size: clamp(14px, 2.8vmin, 32px); }

.phase-overlay {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .6vmin; z-index: 40; background: rgba(4, 4, 26, .72); text-align: center; padding: 4vmin;
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
}
.phase-overlay .pt { font-family: 'Monoton'; color: var(--cyan); font-size: clamp(22px, 5vmin, 58px); text-shadow: 0 0 22px rgba(0,240,255,.7); }
.phase-overlay .ps { color: var(--cyan); font-weight: 600; font-size: clamp(13px, 2.8vmin, 30px); }

.vote-panel { width: min(94%, 900px); margin: 0 auto; display: flex; flex-direction: column; gap: 1.2vmin; }
.vote-row {
  display: flex; align-items: center; gap: 1.4vmin; border-radius: 999px; padding: clamp(5px, 1.2vmin, 14px) clamp(10px, 2vmin, 24px);
  background: var(--glass);
}
.vote-row .vname { flex: 1; font-weight: 700; font-size: clamp(15px, 3vmin, 36px); color: var(--cyan); }
.vote-row .vcount { color: var(--yellow); font-weight: 700; font-size: clamp(14px, 2.8vmin, 32px); }
.fate-cards { display: flex; gap: 2vmin; justify-content: center; margin-top: 1.4vmin; }
.fate-card {
  flex: 1; max-width: 420px; padding: clamp(12px, 2.6vmin, 32px); display: flex; flex-direction: column; align-items: center; gap: 1vmin;
}
.fate-card img { width: clamp(60px, 12vmin, 140px); }
.fate-card .mono { font-size: clamp(16px, 3.6vmin, 42px); }

/* ============================================================
   MEME BATTLES (design-space, inside .stage)
   ============================================================ */
.stage .mm-prompt {
  font-family: 'Monoton', cursive; font-size: 72px; line-height: 1.06; text-align: center;
  color: var(--cyan); text-shadow: 0 0 30px rgba(0,240,255,.65), 0 0 10px rgba(0,240,255,.9);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.stage .mm-timerpill { display: flex; flex-direction: column; align-items: center; justify-content: center; }
.stage .mm-secs { font-family: 'Monoton', cursive; font-size: 92px; color: var(--cyan); line-height: 1;
  text-shadow: 0 0 26px rgba(0,240,255,.6); }
.stage .mm-sec { font-family: 'Rajdhani'; font-weight: 700; font-size: 44px; color: var(--cyan); }
.stage .mm-klipy { font-family: 'Rajdhani'; font-weight: 600; font-style: italic; font-size: 54px;
  color: var(--cyan); text-align: center; opacity: .9; }
.stage .mm-slot {
  background: rgba(90, 170, 220, 0.14);
  border: 2px solid rgba(150, 235, 255, 0.35); border-radius: 44px;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center; text-align: center;
  overflow: hidden; box-shadow: inset 0 0 50px rgba(0,240,255,.06);
}
.stage .mm-slot.clickable { cursor: pointer; transition: filter .14s, transform .14s; }
.stage .mm-slot.clickable:hover { filter: brightness(1.25); transform: scale(1.02); }
.stage .mm-slot img { width: 100%; height: 100%; object-fit: cover; }
.stage .mm-slot .mm-text { color: #fff; font: 700 46px 'Rajdhani'; padding: 24px; text-shadow: 0 2px 8px #000; }
.stage .mm-slot .mm-check { color: var(--green); font-size: 160px; }
.stage .mm-slot.mine { border-color: var(--green); box-shadow: 0 0 30px rgba(0,255,26,.4); }
.stage .mm-winner { border-color: var(--yellow); box-shadow: 0 0 50px rgba(255,215,0,.4); }
.stage .mm-caption { font-family: 'Rajdhani'; font-weight: 700; font-size: 66px; color: var(--cyan);
  text-shadow: 0 0 16px rgba(0,240,255,.5); }
.stage .mm-roundwin { font-family: 'Monoton'; text-align: center; font-size: 64px; color: var(--cyan);
  text-shadow: 0 0 20px rgba(0,240,255,.6); }
.stage .mm-roundwin .mm-wname { color: var(--yellow); }
/* winner reveal: label + a row of avatar + username chips */
.stage .mm-roundwin { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.stage .mm-winlabel { font-family: 'Monoton'; font-size: 48px; color: var(--cyan); }
.stage .mm-winners { display: flex; gap: 40px; justify-content: center; align-items: center; flex-wrap: wrap; }
.stage .mm-winner-chip { display: flex; align-items: center; gap: 16px; }
.stage .mm-winav { width: 76px; height: 76px; }
.stage .mm-winner-chip .mm-winname { font: 700 52px 'Rajdhani'; color: #fff;
  text-shadow: 0 0 14px rgba(0,240,255,.5); }
.stage .mm-winner-chip .mm-winpt { font: 700 40px 'Rajdhani'; color: var(--green);
  text-shadow: 0 0 12px rgba(0,255,26,.6); }
.stage .mm-inputrow { display: flex; gap: 24px; }
.stage .mm-input { flex: 1; background: rgba(0,20,40,.5); border: 2px solid rgba(120,230,255,.4);
  border-radius: 999px; color: #fff; font: 700 52px 'Rajdhani'; padding: 10px 40px; outline: none; }
.stage .mm-send { padding: 0 60px; display: grid; place-items: center; color: var(--green);
  font: 700 56px 'Rajdhani'; border-radius: 44px; background: var(--glass-green); }

/* round meta line under the prompt */
.stage .mm-meta { font: 700 44px 'Rajdhani'; color: var(--blue-soft); }
/* The judge's "players are picking…" panel had no rule at all, so its text fell
   back to the browser default — 16 DESIGN px, which is ~6px once the stage is
   scaled onto a phone (and only ~10px on desktop). */
.stage .mm-loading { display: grid; place-items: center; padding: 40px 60px; }
.stage .mm-loadtext {
  font: 700 56px 'Rajdhani'; color: var(--cyan);
  text-align: center; line-height: 1.25;
}

/* gif cards (2×2 grid + the enlarged winner) */
.stage .mm-gif {
  position: relative;
  background: rgba(90, 170, 220, 0.14);
  border: 3px solid rgba(150, 235, 255, 0.35); border-radius: 40px;
  overflow: hidden; box-shadow: inset 0 0 50px rgba(0,240,255,.06);
}
.stage .mm-gif.mm-square { border-radius: 30px; }
/* cards are sized to each gif's aspect ratio (see meme-ui.js), so the whole
   gif is shown with no cropping and no letterbox bars */
.stage .mm-gif img { width: 100%; height: 100%; object-fit: contain; display: block; }
.stage .mm-gif.clickable { cursor: pointer;
  transition: filter .16s ease, transform .16s cubic-bezier(.2,.9,.3,1.3), box-shadow .16s ease, border-color .16s ease, opacity .2s ease; }
.stage .mm-gif.clickable:hover { filter: brightness(1.14);
  transform: translateY(-12px) scale(1.035); border-color: rgba(0,240,255,.9);
  box-shadow: 0 20px 46px rgba(0,0,0,.5), 0 0 52px rgba(0,240,255,.55); z-index: 3; }
.stage .mm-gif.clickable:active { transform: translateY(-4px) scale(.995); }
/* once you've committed, the cards you didn't take step back so yours pops */
.stage .mm-gif.faded { opacity: .5; filter: saturate(.55) brightness(.78); }
.stage .mm-gif.faded.clickable:hover { opacity: .92; filter: saturate(.9) brightness(1.05); }
.stage .mm-gif.mine { border-color: var(--cyan);
  box-shadow: 0 0 0 6px rgba(0,240,255,.26), 0 0 56px rgba(0,240,255,.6); transform: scale(1.025); z-index: 2; }
/* dealt-in stagger, only when a fresh hand arrives (see meme-ui.js) */
@keyframes mm-deal { from { opacity: 0; transform: translateY(46px) scale(.9); } to { opacity: 1; transform: none; } }
.stage .mm-gif.deal { animation: mm-deal .4s cubic-bezier(.2,.9,.3,1.25) both; }
/* one-shot ring when you lock a pick in */
@keyframes mm-lock { from { box-shadow: 0 0 0 0 rgba(0,240,255,.55); } to { box-shadow: 0 0 0 30px rgba(0,240,255,0); } }
.stage .mm-gif.justpicked::after { content: ''; position: absolute; inset: -3px;
  border-radius: inherit; pointer-events: none; animation: mm-lock .75s ease-out both; }
.stage .mm-gif.voted { border-color: var(--green); box-shadow: 0 0 34px rgba(0,255,26,.45); }
.stage .mm-gif.win { border-color: var(--yellow); box-shadow: 0 0 54px rgba(255,215,0,.5); }
.stage .mm-gif.dim { opacity: .45; }
.stage .mm-badge {
  position: absolute; left: 24px; top: 20px; padding: 6px 26px; border-radius: 999px;
  background: rgba(4,10,30,.75); border: 2px solid rgba(0,240,255,.5);
  color: var(--cyan); font: 700 40px 'Rajdhani';
}
.stage .mm-badge.mm-minebadge { color: var(--cyan); }
/* keyboard hint chip (1–9) on each selectable card */
.stage .mm-badge.mm-num { left: auto; right: 24px; min-width: 62px; text-align: center;
  padding: 4px 18px; color: #fff; background: rgba(4,10,30,.68); border-color: rgba(150,235,255,.45); }
.stage .mm-gif.clickable:hover .mm-badge.mm-num { border-color: var(--cyan); color: var(--cyan); }
.stage .mm-badge.mm-locked { top: auto; bottom: 20px; color: var(--cyan); border-color: rgba(0,240,255,.7);
  background: rgba(0,40,60,.8); }
.stage .mm-badge.mm-picks { left: auto; right: 24px; color: var(--blue-soft); }
.stage .mm-badge.mm-rating { top: auto; bottom: 20px; color: var(--yellow); border-color: rgba(255,215,0,.5); }
.stage .mm-badge.mm-votesb { top: auto; bottom: 20px; left: auto; right: 24px; color: var(--green); border-color: rgba(0,255,26,.5); }

/* loading box */
/* Meme loading screen — mirrors the main-menu #loader (VB hexagon + Monoton
   title + a sweeping indeterminate bar), sized in design px for the stage. */
.stage .mm-load { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 44px; }
.stage .mm-load-hex { width: 240px; height: 240px; filter: drop-shadow(0 0 24px rgba(0,240,255,.55)); }
.stage .mm-load-title { font-size: 96px; color: var(--cyan); text-shadow: 0 0 34px rgba(0,240,255,.6); letter-spacing: 2px; }
.stage .mm-load-sub { font-size: 44px; color: var(--blue-soft); text-align: center; }
/* The phone canvas renders at ~0.40 scale vs desktop's ~0.64, so the loader
   came out noticeably smaller than on a monitor. Size it up to compensate — the
   box has the room (it spans y130..770 of the 973 canvas). */
.stage.stage-phone .mm-load { gap: 34px; }
.stage.stage-phone .mm-load-hex { width: 250px; height: 250px; flex: none; }
.stage.stage-phone .mm-load-title { font-size: 118px; white-space: nowrap; }
.stage.stage-phone .mm-load-sub { font-size: 54px; }
.stage.stage-phone .mm-load-bar { width: 1180px; height: 30px; flex: none; }
.stage.stage-phone .mm-loadtext { font-size: 76px; }

.stage .mm-load-bar { width: 760px; height: 22px; border-radius: 999px; overflow: hidden; position: relative;
  background: rgba(4,10,30,.6); border: 1px solid rgba(0,240,255,.3); }
.stage .mm-load-fill { position: absolute; top: 0; height: 100%; width: 42%; border-radius: 999px;
  background: linear-gradient(90deg, transparent, #00F0FF, #6E7BFF, transparent);
  box-shadow: 0 0 18px rgba(0,240,255,.6); animation: mm-load-sweep 1.3s linear infinite; }
@keyframes mm-load-sweep { from { left: -45%; } to { left: 100%; } }

/* between-round scoreboard strip */
.stage .mm-scorebar { display: flex; gap: 26px; align-items: center; justify-content: center; flex-wrap: wrap; }
.stage .mm-scorechip {
  display: flex; align-items: center; gap: 20px; padding: 12px 34px; border-radius: 999px;
  background: rgba(30,150,210,.18); border: 2px solid rgba(120,230,255,.3);
}
.stage .mm-scorechip .mm-scname { font: 700 44px 'Rajdhani'; color: var(--cyan); }
.stage .mm-scorechip .mm-scpts { font-family: 'Monoton'; font-size: 48px; color: var(--blue-soft); }
.stage .mm-scorechip.top1 { border-color: var(--yellow); box-shadow: 0 0 24px rgba(255,215,0,.35); }
.stage .mm-scorechip.top1 .mm-scpts { color: var(--yellow); }
.stage .mm-scorechip.top2 { border-color: #cfd8ff; }
.stage .mm-scorechip.top3 { border-color: #ff9d5c; }

/* legacy responsive meme (unused) */
.meme-wrap { position: absolute; inset: 0; display: flex; flex-direction: column; padding: clamp(8px, 1.8vmin, 22px); }
.meme-prompt { font-family: 'Monoton'; text-align: center; color: var(--cyan); font-size: clamp(16px, 3.8vmin, 46px); text-shadow: 0 0 18px rgba(0,240,255,.55); }
.meme-meta { display: flex; justify-content: space-between; align-items: center; }
.meme-klipy { font-style: italic; font-weight: 600; color: var(--cyan); opacity: .85; font-size: clamp(11px, 2.2vmin, 24px); }
.meme-timer { font-family: 'Monoton'; color: var(--cyan); font-size: clamp(18px, 4.2vmin, 50px); padding: .4vmin 2vmin; }
.meme-slots { flex: 1; display: grid; gap: 1.6vmin; margin-top: 1.4vmin; min-height: 0; overflow-y: auto; align-content: stretch; }
.meme-slot {
  border-radius: var(--r); background: rgba(120, 200, 235, .28); border: 1px solid rgba(0,240,255,.25);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center; text-align: center;
  padding: clamp(8px, 2vmin, 26px); overflow: hidden; position: relative;
  font-weight: 700; color: #fff; font-size: clamp(14px, 3vmin, 34px);
  cursor: pointer; transition: transform .14s, filter .14s;
}
.meme-slot:hover { filter: brightness(1.2); }
.meme-slot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.meme-slot .who { position: absolute; bottom: 4%; right: 4%; font-size: clamp(11px, 2vmin, 22px); color: var(--yellow); }
.meme-slot.mine { outline: 3px solid var(--green); }
.meme-footer { text-align: center; color: var(--cyan); font-weight: 600; font-size: clamp(14px, 3vmin, 34px); padding: 1vmin 0; }
.meme-input-row { display: flex; gap: 1.2vmin; padding: 1vmin 0; }
.meme-input-row input {
  flex: 1; background: rgba(0, 20, 40, .5); border: 1px solid rgba(0,240,255,.35); border-radius: 999px;
  color: #fff; font: 600 clamp(14px, 2.8vmin, 30px) 'Rajdhani'; padding: clamp(8px, 1.6vmin, 18px) clamp(14px, 2.8vmin, 32px); outline: none; min-width: 40px;
}
.meme-input-row .glass { padding: clamp(8px, 1.6vmin, 18px) clamp(18px, 4vmin, 52px); font-weight: 700; font-size: clamp(14px, 2.8vmin, 32px); }
.meme-winner-name { color: var(--yellow); }

/* ============================================================
   CYBER SPEED
   ============================================================ */
.race-wrap { position: absolute; inset: 0; overflow: hidden; touch-action: none; }
.race-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.race-hud {
  position: absolute; z-index: 5; padding: clamp(6px, 1.4vmin, 16px) clamp(14px, 3vmin, 36px);
  top: max(1.4vmin, env(safe-area-inset-top)); right: max(1.6vmin, env(safe-area-inset-right));
  font-family: 'Monoton'; color: var(--blue-soft); font-size: clamp(20px, 4.6vmin, 54px);
}
.race-hud .unit { font-size: 40%; }
.race-countdown {
  position: absolute; inset: 0; display: grid; place-items: center; z-index: 6; pointer-events: none;
  font-family: 'Monoton'; font-size: clamp(70px, 20vmin, 240px); color: var(--cyan);
  text-shadow: 0 0 40px rgba(0,240,255,.8);
}
/* Mobile-only virtual joystick. The artwork's outer ring is the STATIC base;
   only the centre knob moves, and it's clamped so it can't leave the ring. The
   base's centre is capped with a dark socket so the artwork's baked-in dot
   doesn't double up under the movable knob. Physics is unchanged. */
.joystick {
  position: absolute; z-index: 10; touch-action: none;
  left: max(4vmin, env(safe-area-inset-left)); bottom: max(4vmin, env(safe-area-inset-bottom));
  width: clamp(120px, 26vmin, 230px); height: clamp(120px, 26vmin, 230px);
  border-radius: 50%;
  /* The dark cap only needs to hide the artwork's baked-in centre dot; its fade
     ends INSIDE the knob's radius so no dark ring peeks out around the knob at
     rest. Tinted to the artwork's field, not black. */
  background:
    radial-gradient(circle closest-side, rgba(12,22,52,.82) 0 33%, rgba(12,22,52,0) 40%),
    url('../assets/joystick.png') center/contain no-repeat;
  box-shadow: 0 0 20px rgba(0, 0, 0, .35);
}
.joystick { transition: opacity .18s ease; }
.joystick:not(.rc-live) { opacity: .7; }        /* unobtrusive until a thumb is on it */
.joystick.rc-live { opacity: 1; box-shadow: 0 0 30px rgba(0, 240, 255, .45), 0 0 20px rgba(0, 0, 0, .35); }
.joystick .stick {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 42%; height: 42%; border-radius: 50%;   /* larger than the cap → covers it fully at rest */
  /* just the artwork's centre dot, zoomed out of the full image */
  background: url('../assets/joystick.png') center / 300% 300% no-repeat;
  box-shadow: 0 0 16px rgba(0, 240, 255, .65);
  pointer-events: none;   /* the base owns the pointer events */
}
.joystick.rc-live .stick { box-shadow: 0 0 22px rgba(0, 240, 255, .9); }
/* spring the knob back to centre on release (not while dragging, or it'd lag) */
.joystick .stick.rc-return { transition: left .14s cubic-bezier(.2,.8,.3,1), top .14s cubic-bezier(.2,.8,.3,1); }
body.desktop .joystick { display: none; }
.race-keys-hint { position: absolute; left: 50%; transform: translateX(-50%);
  bottom: max(2vmin, env(safe-area-inset-bottom)); color: var(--cyan); opacity: .75;
  font-weight: 600; font-size: clamp(12px, 2.2vmin, 22px); z-index: 5; }
body.mobile .race-keys-hint { display: none; }

/* ============================================================
   MOBILE ADAPTATIONS
   ============================================================ */
body.mobile button, body.mobile .huser, body.mobile .meme-slot { min-height: 40px; }
/* …but the shape-critical controls must keep their proportions. The 40px
   touch-target minimum above squared off the switch pill (28px tall → 40px) and
   stretched the square checkbox. Scale them UP proportionally instead, so the
   tap target still grows without distorting the control. */
body.mobile .toggle, body.mobile .checkbox { min-height: 0; }
body.mobile .toggle {
  width: clamp(64px, 13vmin, 88px);      /* keeps the ~1.9:1 pill ratio */
  height: clamp(34px, 7vmin, 46px);
}
body.mobile .toggle::after { width: clamp(24px, 5vmin, 34px); height: clamp(24px, 5vmin, 34px); }
body.mobile .checkbox { width: clamp(40px, 7.5vmin, 52px); height: clamp(40px, 7.5vmin, 52px); }

/* ============================================================
   ROTATE GATE + LANDSCAPE SCALE-UP (phones & tablets)
   The game is built in a wide 2109×1406 design space, so portrait squeezes it to
   about half size. Touch devices are asked to rotate; desktop is untouched.
   ============================================================ */
#rotate-gate { display: none; }
/* ---- payments run PORTRAIT ----
   The game is landscape-only, but a checkout is a tall form. While the payment
   modal is open the rule inverts: portrait is allowed (gate suppressed) and
   landscape shows the turn-your-phone prompt instead of the cramped form. */
body.mobile.paying.portrait #rotate-gate { display: none !important; }
.pay-rotate { display: none; }
body.mobile.paying.landscape .pay-rotate {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  position: fixed; inset: 0; z-index: 5;
  background: rgba(4, 6, 24, .94); text-align: center; padding: 20px;
}
body.mobile.paying.landscape .whop-embed-box { visibility: hidden; }
.pay-rotate-ico { font-size: 44px; animation: pay-tilt 1.8s ease-in-out infinite; }
@keyframes pay-tilt { 0%,100% { transform: rotate(-90deg); } 50% { transform: rotate(0deg); } }
.pay-rotate-t { font-size: clamp(18px, 3.4vmin, 28px); color: var(--cyan); text-shadow: 0 0 18px rgba(0,240,255,.6); }
.pay-rotate-s { font-size: clamp(12px, 2.2vmin, 18px); color: var(--blue-soft); }
@media (prefers-reduced-motion: reduce) { .pay-rotate-ico { animation: none; } }
body.mobile.portrait #rotate-gate {
  display: grid; place-items: center;
  position: fixed; inset: 0; z-index: 5000;
  padding: max(16px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right))
           max(16px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  background:
    radial-gradient(900px 600px at 50% 40%, rgba(0,240,255,.10), transparent 60%),
    #04060f url('../assets/bg-nebula.png') center/cover no-repeat;
  text-align: center;
}
/* freeze the app underneath so nothing renders/animates at the wrong size */
body.mobile.portrait #app { visibility: hidden; }
/* …but not while a payment is open — portrait is the SUPPORTED orientation
   there, so the app (and the checkout inside it) has to stay visible. */
body.mobile.paying.portrait #app { visibility: visible; }
.rg-inner { display: flex; flex-direction: column; align-items: center; gap: clamp(14px, 3vh, 26px); }
.rg-phone { width: clamp(110px, 30vw, 190px); height: auto; filter: drop-shadow(0 0 18px rgba(0,240,255,.5)); }
.rg-phone .rg-body { fill: none; stroke: var(--cyan); stroke-width: 4; }
.rg-phone .rg-line { stroke: var(--cyan); stroke-width: 3; stroke-linecap: round; }
.rg-phone .rg-arrow { fill: none; stroke: var(--yellow, #FFD700); stroke-width: 4; stroke-linecap: round; }
.rg-phone .rg-head { fill: var(--yellow, #FFD700); }
.rg-phone { animation: rg-tilt 2.4s ease-in-out infinite; }
@keyframes rg-tilt { 0%, 55% { transform: rotate(0deg); } 80%, 100% { transform: rotate(-90deg); } }
.rg-title { color: var(--cyan); font-size: clamp(20px, 6vw, 34px); text-shadow: 0 0 26px rgba(0,240,255,.6); }
.rg-sub { color: var(--blue-soft); font-size: clamp(13px, 3.6vw, 20px); }
@media (prefers-reduced-motion: reduce) { .rg-phone { animation: none; } }

/* Landscape on a touch device: push type/controls up a size so the UI is
   comfortably readable at arm's length (these screens are flow-layout; the
   design-space stages already scale themselves). */
/* On a landscape phone vmin is the SHORT side (~375px), so vmin-based type comes
   out tiny — these raise the floor rather than the slope. Panes already scroll. */
body.mobile.landscape .value-chip,
body.mobile.landscape .label-chip,
body.mobile.landscape .hint-chip { font-size: clamp(18px, 3.4vmin, 30px); }
body.mobile.landscape .pill-btn,
body.mobile.landscape .fr-act,
body.mobile.landscape .profile-tab { font-size: clamp(18px, 3.2vmin, 26px); }
body.mobile.landscape .wdg-title { font-size: clamp(13px, 2.2vmin, 16px); }
body.mobile.landscape .wdg-val { font-size: clamp(19px, 3.2vmin, 28px); }
body.mobile.landscape .pf-stat-v { font-size: clamp(28px, 4.6vmin, 42px); }
body.mobile.landscape .pf-stat-l { font-size: clamp(13px, 2vmin, 18px); }
body.mobile.landscape .fr-name,
body.mobile.landscape .pf-name { font-size: clamp(20px, 3.4vmin, 30px); }
body.mobile.landscape .fr-id,
body.mobile.landscape .pf-lvl,
body.mobile.landscape .pf-handle { font-size: clamp(14px, 2.2vmin, 20px); }
body.mobile.landscape .mode-note,
body.mobile.landscape .shop-note { font-size: clamp(15px, 2.8vmin, 24px); }
body.mobile.landscape .pf-section-h,
body.mobile.landscape .fr-list-head { font-size: clamp(13px, 2vmin, 18px); }
body.mobile.landscape .cname { font-size: clamp(18px, 3.2vmin, 30px); }
/* room-setting numerals + section titles read at a glance on a small screen */
body.mobile.landscape .rule-card .num { font-size: clamp(38px, 9vmin, 110px); }
body.mobile.landscape .settings-panel h2 { font-size: clamp(20px, 4vmin, 44px); }

/* Profile on a landscape phone. The pane is capped at 620px, which on an 852px
   screen wastes a third of the width, forces a ~717px-tall card and leaves only
   ~266px of it visible — so the widgets sat far below the fold and looked like
   they simply weren't there. Use the full width (the grid then lays out 3
   columns instead of 2) and tighten the chrome so far more fits per screen. */
body.mobile.landscape .profile-pane { width: min(100%, 1200px); }   /* 100%, not vw: stays inside .screen's safe-area padding (iPhone notch) */
body.mobile.landscape .profile-card { padding: 10px 12px; gap: 6px; border-radius: 14px; }
body.mobile.landscape .pf-head { gap: 10px; }
body.mobile.landscape .pf-head .pf-av { width: 52px; height: 52px; }
body.mobile.landscape .pf-name { font-size: 19px; }
body.mobile.landscape .pf-handle, body.mobile.landscape .pf-lvl { font-size: 12px; }
body.mobile.landscape .wdg-board { gap: 6px; }
body.mobile.landscape .wdg { padding: 6px 9px; gap: 7px; }
body.mobile.landscape .wdg-board { row-gap: 5px; }
body.mobile.landscape .pf-section-h { font-size: 12px; margin-top: 2px; }
/* Trim the screen chrome — title + tabs were eating 95px of a 393px screen. */
body.mobile.landscape #screen-friends > .mono { font-size: 17px; line-height: 1.1; }
body.mobile.landscape .profile-tabs { gap: 6px; }
body.mobile.landscape .profile-tabs .profile-tab { padding: 4px 14px; font-size: 14px; }

/* The pane is ~835px wide but the card stacked identity ABOVE the widgets, so
   the widgets started ~300px down and fell off the bottom of a 266px window —
   which is why they looked missing. Two columns instead: identity and stats on
   the left, the whole widget board on the right, visible without scrolling. */
body.mobile.landscape .profile-card {
  display: grid;
  grid-template-columns: minmax(180px, 220px) 1fr;
  column-gap: 10px; row-gap: 4px;
  align-content: start;
}
body.mobile.landscape .profile-card > .pf-head,
body.mobile.landscape .profile-card > .pf-privacy,
body.mobile.landscape .profile-card > .pf-stats { grid-column: 1; }
/* the first section header labels the stats (left), the second the widgets */
body.mobile.landscape .profile-card > .pf-section-h:nth-of-type(1) { grid-column: 1; }
body.mobile.landscape .profile-card > .pf-section-h:nth-of-type(2) { grid-column: 2; grid-row: 1; }
body.mobile.landscape .profile-card > .wdg-board {
  grid-column: 2; grid-row: 2 / -1; align-content: start;
  /* two wider columns beat three narrow ones — at 150px the labels truncated
     to "F…" / "S…" and Best Friend wrapped to six lines */
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* The photo widget spans the full row on desktop; on a phone that forced an
   extra tall row and pushed the last widgets off-screen. Let it sit in a normal
   cell so all six fit in two rows. */
body.mobile.landscape .wdg-board .wdg.wdg-photo { grid-column: auto; }
body.mobile.landscape .wdg-photo .wdg-photo-img,
body.mobile.landscape .wdg-photo img { max-height: 64px; }

/* VoidPass on a landscape phone. The track's row heights come from clamp()s
   whose MINIMUMS (76px cell, 26px level) are bigger than the vmin term on a
   393px-tall screen — so the grid demanded 194px of a pane that has ~215px,
   and the header, tabs, lanes and the Exit link all piled on top of each other.
   Shrink the rows (162px total) and tighten the chrome around them. */
body.mobile.landscape #screen-pass {
  --cellH: 58px;
  --lvlH: 20px;
  --colW: 74px;
  --railW: 56px;
}
/* Same sizing again, but keyed to the VIEWPORT HEIGHT instead of the body
   classes. `body.mobile` comes from device detection and can be overridden in
   Settings > Device, so a phone reporting "computer" kept the tall clamp floors
   and the lanes went on colliding. A short viewport is a short viewport no
   matter what the device claims to be — and with a browser URL bar on screen
   the pass pane is barely 300px, so it needs the smaller rows either way. */
@media (max-height: 500px) {
  /* The track must fit .pass-view's CONTENT box — i.e. after its bottom padding
     (which reserves room for the floating Exit link). Too tall and the flex
     centring spills it upward into the tabs, which is what was overlapping. */
  #screen-pass { --cellH: 50px; --lvlH: 16px; --colW: 68px; --railW: 50px; }
  #screen-pass .road-panel { padding: 6px 8px; gap: 4px; margin-top: 4px; }
  #screen-pass .pass-head { gap: 3px; }
  #screen-pass .pass-head h1 { font-size: 18px; }
  #screen-pass .pass-xpbar { height: 12px; }
  #screen-pass .pass-tabs { margin: 3px 0 1px; gap: 5px; }
  #screen-pass .pass-tabs .pill-btn { padding: 3px 12px; font-size: 14px; }
  #screen-pass .pass-cell .pc-lbl { font-size: 8px; line-height: 1.05; }
  #screen-pass .rail-row .rj { font-size: 9px; }
  #screen-pass .pass-lvl { font-size: 12px; }
  #screen-pass .pass-view { padding-bottom: 28px; }
  #screen-pass .corner-link { font-size: 20px; bottom: 3px; }
}
body.mobile.landscape .road-panel { padding: 8px 10px; gap: 6px; margin-top: 6px; }
body.mobile.landscape .pass-head { gap: 4px; }
body.mobile.landscape .pass-head h1 { font-size: 20px; }
body.mobile.landscape .pass-xpbar { height: 14px; }
body.mobile.landscape .pass-tabs { margin: 4px 0 2px; gap: 6px; }
body.mobile.landscape .pass-tabs .pill-btn { padding: 4px 14px; font-size: 15px; }
body.mobile.landscape .pass-cell .pc-lbl { font-size: 9px; line-height: 1.1; }
body.mobile.landscape .rail-row .rj { font-size: 10px; }
body.mobile.landscape .pass-lvl { font-size: 13px; }
/* keep the bottom lane clear of the floating Exit link */
body.mobile.landscape .pass-view { padding-bottom: 42px; }
body.mobile.landscape #screen-pass .corner-link { font-size: 22px; bottom: 4px; }

/* The non-Blitz lobby (Hacker / Meme / Cyber speed) is a FLOW layout, not a
   design stage, so the phone canvas never reached it. Its clamp() padding is
   floored at 48px top + 96px bottom, which on a 393px landscape phone spends
   37% of the height on empty margin and squeezes the settings into a sliver.
   The Exit pill and Start button are much smaller here, so reserve only what
   they actually occupy. */
body.mobile.landscape .lobby-layout {
  padding-top: 40px; padding-bottom: 72px; gap: 10px;
}
body.mobile.landscape .lobby-panel { width: min(32%, 300px); }
body.mobile.landscape .start-btn {
  bottom: 8px; padding: 8px 40px; font-size: clamp(24px, 5vmin, 34px);
}
body.mobile.landscape .exit-pill { top: 6px; left: 8px; padding: 5px 16px; font-size: 15px; }

@media (max-width: 760px), (orientation: portrait) {
  .modes-grid { grid-template-columns: 1fr; grid-template-rows: repeat(4, 1fr); overflow-y: auto; }
  .lobby-layout { flex-direction: column; padding-top: clamp(44px, 8vmin, 90px); overflow-y: auto; }
  .lobby-panel { width: 100%; max-height: 38vh; }
  .lobby-right { overflow: visible; }
  .pro-cols { flex-direction: column; align-items: center; overflow-y: auto; }
  .pro-card { width: 100%; max-width: none; flex: none; }
  .hacker-grid { grid-template-columns: 1fr; }
  .search-top .mono { display: none; }
  .podium { gap: 1vmin; }
  .opp-left { left: 0.6vmin; } .opp-right { right: 0.6vmin; }
  .filter-panel { left: clamp(10px, 2.5vmin, 30px); flex-wrap: wrap; }
}

/* ============================================================
   DAILY REWARD CLAIM — hCaptcha gate + 10s progress bar (replaces ads)
   ============================================================ */
.claim-card {
  width: min(94vw, 460px); padding: clamp(16px, 2.6vmin, 26px);
  display: flex; flex-direction: column; gap: 16px;
  border: 1px solid rgba(0, 240, 255, .5); box-shadow: 0 0 60px rgba(0, 240, 255, .3);
}
.claim-head { display: flex; align-items: center; justify-content: space-between; }
.claim-head .mono { font-size: clamp(16px, 2.6vmin, 24px); }
.claim-x {
  width: 36px; height: 36px; border-radius: 10px; font-size: 17px; font-weight: 800; cursor: pointer;
  color: #ff9a9a; background: rgba(255, 90, 90, .18); border: 1px solid rgba(255, 120, 120, .5);
}
.claim-x:hover { filter: brightness(1.2); }
.claim-cap { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.claim-cap-label { color: var(--cyan); font-size: clamp(14px, 2.2vmin, 18px); }
#claim-hcap { min-height: 78px; display: flex; justify-content: center; }
.claim-prog { display: flex; flex-direction: column; gap: 12px; }
.claim-msg { color: var(--cyan); font-weight: 700; text-align: center; font-size: clamp(14px, 2.2vmin, 18px); }
.claim-bar {
  width: 100%; height: 22px; border-radius: 999px; overflow: hidden;
  background: rgba(4, 10, 30, .6); border: 1px solid rgba(0, 240, 255, .3);
}
.claim-fill {
  height: 100%; width: 0%; border-radius: 999px;
  background: linear-gradient(90deg, #00F0FF, #6E7BFF);
  box-shadow: 0 0 18px rgba(0, 240, 255, .5);
}
.claim-fill.go { width: 100%; transition: width 10s linear; }
.claim-fill { transition: width .18s ease-out; }   /* smooth the ±10/5% steps */

/* On a landscape phone the card is taller than the screen if it stays a single
   column (dial 260 + head + msg + bar + hint > 393px). The screen is wide and
   short, so put the dial beside the text instead of above it, and size it from
   the SHORT axis (vh) rather than vw. */
body.mobile.landscape .claim-card {
  display: grid; width: min(94vw, 640px);
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto auto auto;
  column-gap: clamp(14px, 3vw, 30px); row-gap: clamp(6px, 1.4vh, 12px);
  align-items: center;
}
body.mobile.landscape .claim-card > .spin-dial {
  grid-column: 1; grid-row: 1 / -1;
  width: clamp(132px, 46vh, 200px); margin: 0;
}
body.mobile.landscape .claim-card > .claim-head { grid-column: 2; grid-row: 1; }
body.mobile.landscape .claim-card > .claim-msg  { grid-column: 2; grid-row: 2; text-align: left; }
body.mobile.landscape .claim-card > .claim-bar  { grid-column: 2; grid-row: 3; }
body.mobile.landscape .claim-card > .spin-hint  { grid-column: 2; grid-row: 4; text-align: left; }

/* ---- easter egg: the door, the key, the archive ----
   Nothing here is shown by default. The door only appears after several rapid
   in-and-out shop visits; the key only drops from hammering Boombox at 3+
   clicks/sec; the archive only opens once the key is claimed. */
.shop-door {
  /* A grid CARD, not a floating element: it sits beside the last crate, scrolls
     with #shop-grid, and stretches to the height of its row. */
  position: relative; cursor: pointer; padding: 0; border: 0; background: none;
  /* buttons shrink-to-fit, so stretch has to be explicit to fill the grid cell */
  width: 100%; align-self: stretch; justify-self: stretch; min-height: clamp(120px, 22vmin, 260px);
  border-radius: 14px; overflow: hidden;
}
.lore-overlay.hidden { display: none; }
.door-slab {
  position: absolute; inset: 0; border-radius: 14px;
  background: linear-gradient(160deg, #2a1c4d, #140b2a 70%);
  border: 2px solid rgba(140,110,255,.55);
  box-shadow: inset 0 0 22px rgba(90,60,200,.55), 0 8px 22px rgba(0,0,0,.6),
              0 0 26px rgba(120,80,255,.28);
}
.door-knob {
  position: absolute; right: 12%; top: 52%; width: clamp(8px,1.4vmin,15px); aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #ffe9a8, #b8860b);
  box-shadow: 0 0 8px rgba(255,215,0,.7);
}
.door-lockwrap { position: absolute; left: 50%; top: 26%; width: clamp(34px,6vmin,66px); transform: translateX(-50%); }
.door-lockwrap svg { width: 100%; height: auto; display: block; filter: drop-shadow(0 2px 5px rgba(0,0,0,.8)); }
.shop-door:hover { filter: brightness(1.15); }
.shop-door:hover .door-slab { box-shadow: inset 0 0 26px rgba(120,80,255,.7), 0 10px 26px rgba(0,0,0,.65), 0 0 34px rgba(140,90,255,.45); }
.shop-door.unlocked .door-slab { border-color: rgba(0,240,255,.6); box-shadow: inset 0 0 26px rgba(0,180,220,.5), 0 0 30px rgba(0,240,255,.35); }

/* the key, sitting where Boombox's speech line normally is */
.speech.has-key { display: grid; place-items: center; padding: clamp(4px,1vmin,10px) clamp(8px,1.6vmin,18px); }
.speech-key {
  width: clamp(70px, 13vmin, 150px); cursor: pointer; padding: 0; border: 0; background: none;
  display: block; animation: key-bob 1.9s ease-in-out infinite, key-pop .5s cubic-bezier(.34,1.56,.64,1);
  filter: drop-shadow(0 0 10px rgba(0,240,255,.85));
}
.speech-key svg { width: 100%; height: auto; display: block; }
.speech-key:hover { filter: drop-shadow(0 0 18px rgba(0,240,255,1)) brightness(1.15); transform: scale(1.07); }
@keyframes key-pop { from { opacity: 0; transform: scale(.3) rotate(-40deg); } to { opacity: 1; transform: none; } }
@keyframes key-bob { 0%,100% { translate: 0 0; } 50% { translate: 0 -5px; } }

/* the archive: near-fullscreen liquid glass */
.lore-overlay {
  position: fixed; inset: 0; z-index: 60; display: grid; place-items: center;
  background: rgba(4,8,24,.62); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  padding: max(2vmin, env(safe-area-inset-top)) max(2vmin, env(safe-area-inset-right), var(--safe-x, 0px))
           max(2vmin, env(safe-area-inset-bottom)) max(2vmin, env(safe-area-inset-left), var(--safe-x, 0px));
}
.lore-glass {
  position: relative; width: min(96%, 1500px); height: min(93%, 940px);   /* 'almost fullscreen' — the caps only bite on very large displays */
  border-radius: clamp(16px, 2.4vmin, 30px);
  background: linear-gradient(150deg, rgba(120,160,255,.16), rgba(10,20,50,.5) 60%);
  border: 1px solid rgba(180,210,255,.34);
  -webkit-backdrop-filter: blur(26px) saturate(1.5); backdrop-filter: blur(26px) saturate(1.5);
  box-shadow: 0 30px 90px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.28),
              inset 0 -20px 60px rgba(80,120,255,.14);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: clamp(10px, 2.2vmin, 28px); padding: clamp(14px, 3vmin, 40px);
  animation: lore-in .4s cubic-bezier(.22,1,.36,1) both;
}
@keyframes lore-in { from { opacity: 0; transform: scale(.96) translateY(10px); } to { opacity: 1; transform: none; } }
.lore-title { font-size: clamp(20px, 4vmin, 46px); letter-spacing: 3px; }
.lore-foot { color: var(--blue-soft); font-size: clamp(12px, 1.9vmin, 18px); text-align: center; }
.lore-close {
  position: absolute; top: clamp(8px,1.4vmin,16px); right: clamp(8px,1.4vmin,16px);
  width: clamp(30px,4vmin,42px); height: clamp(30px,4vmin,42px); border-radius: 50%; cursor: pointer;
  background: rgba(255,60,60,.18); border: 1px solid rgba(255,90,90,.5); color: #fff;
  font-size: clamp(14px,2vmin,20px); line-height: 1;
}
.lore-close:hover { background: rgba(255,60,60,.34); }
.lore-books { display: flex; gap: clamp(14px, 4vmin, 60px); align-items: stretch; }

.lore-book {
  position: relative; width: clamp(120px, 34vmin, 360px); aspect-ratio: 3 / 4;   /* fill the archive panel rather than floating in it */
  border-radius: 4px 10px 10px 4px; cursor: pointer; padding: 0;
  border: 1px solid rgba(255,255,255,.28);
  /* the spine is the darker strip down the left edge */
  box-shadow: inset 10px 0 0 rgba(0,0,0,.42), 0 12px 30px rgba(0,0,0,.6);
  transition: transform .18s var(--spring), filter .18s ease;
  overflow: hidden;
}
.lore-book:hover { transform: translateY(-5px) rotate(-1.5deg); filter: brightness(1.12); }
.lore-book.blue { background: linear-gradient(150deg, #1b3f8f, #0a1c4a); }
.lore-book.red  { background: linear-gradient(150deg, #8f1b1b, #4a0a0a); }
.lore-cover {
  position: absolute; inset: 8% 8% 26% 16%;
  width: auto; height: auto; max-width: 78%; max-height: 62%;
  margin: auto; object-fit: contain; opacity: .94;
  filter: drop-shadow(0 0 6px rgba(0,0,0,.6));
}
.lore-book.blue .lore-cover { filter: drop-shadow(0 0 10px rgba(0,240,255,.55)); }
.lore-book.red  .lore-cover { filter: drop-shadow(0 0 10px rgba(255,60,60,.55)); }
.lore-spine {
  position: absolute; left: 0; right: 0; bottom: 13%; text-align: center;
  color: rgba(255,255,255,.86); font-weight: 800; letter-spacing: 1px;
  font-size: clamp(11px, 2.2vmin, 24px); text-shadow: 0 2px 6px rgba(0,0,0,.9);
}
.lore-lock {
  position: absolute; right: 7%; bottom: 4%; width: clamp(18px, 3.4vmin, 38px);
  filter: drop-shadow(0 1px 3px rgba(0,0,0,.9));
}
.lore-lock svg { width: 100%; height: auto; display: block; }
/* the lock rattles but never opens */
.lore-book.locked-shake, .shop-door.locked-shake { animation: book-shake .32s ease; }
@keyframes book-shake {
  0%,100% { transform: translateX(0) rotate(0); }
  25% { transform: translateX(-3px) rotate(-2deg); }
  75% { transform: translateX(3px) rotate(2deg); }
}
@media (prefers-reduced-motion: reduce) {
  .lore-book.locked-shake, .shop-door.locked-shake, .speech-key, .lore-glass { animation: none; }
}
/* phone landscape: the archive gets tight, so shrink the books */
body.mobile.landscape .lore-glass { height: min(94%, 420px); gap: 8px; padding: 12px; }
body.mobile.landscape .lore-title { font-size: 20px; letter-spacing: 2px; }
body.mobile.landscape .lore-book { width: clamp(80px, 17vmin, 140px); }
body.mobile.landscape .lore-foot { font-size: 12px; }

/* ---- spin-strike dial (daily reward minigame) ---- */
.spin-dial {
  position: relative; display: grid; place-items: center; cursor: pointer;
  width: min(64vw, 260px); aspect-ratio: 1; margin: 0 auto; padding: 0;
  background: none; border: none; border-radius: 50%;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  transition: transform .12s ease, filter .12s ease;
}
.spin-dial:active { transform: scale(.97); }
.spin-dial svg { width: 100%; height: 100%; overflow: visible; display: block; }
.sd-track { fill: rgba(4,10,30,.55); stroke: rgba(0,240,255,.35); stroke-width: 10; }
.sd-zone { fill: none; stroke: #00FF6A; stroke-width: 12; stroke-linecap: round;
  filter: drop-shadow(0 0 10px rgba(0,255,106,.8)); transition: stroke .15s ease; }
.sd-needle-tip { fill: var(--cyan); filter: drop-shadow(0 0 10px rgba(0,240,255,.95)); }
.sd-pct {
  /* Sized so even "100%" stays inside radius ~74 — the pointer sweeps the rim
     from there outward, so the two can never overlap at any angle. */
  position: absolute; font-size: clamp(22px, 6.2vmin, 40px); color: var(--cyan);
  text-shadow: 0 0 22px rgba(0,240,255,.7); pointer-events: none;
  line-height: 1; white-space: nowrap;
}
/* hit / miss feedback */
.spin-dial.hit .sd-zone { stroke: #baffd4; filter: drop-shadow(0 0 20px rgba(0,255,106,1)); }
.spin-dial.hit .sd-pct { color: #7dffa0; }
.spin-dial.miss { animation: sd-shake .26s ease; }
.spin-dial.miss .sd-track { stroke: rgba(255,60,60,.75); }
.spin-dial.miss .sd-pct { color: #ff9a9a; }
@keyframes sd-shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-7px)} 75%{transform:translateX(7px)} }
.spin-hint { text-align: center; color: var(--blue-soft); font-size: clamp(12px, 2vmin, 17px); }
@media (prefers-reduced-motion: reduce) { .spin-dial.miss { animation: none; } }

/* ============================================================
   BLITZ round FX — flying cards, per-player count pills, 1-card burst
   (all sized in design-space px; the stage transform scales them)
   ============================================================ */
.stage .bz-fx { position: absolute; inset: 0; z-index: 60; pointer-events: none; }
.stage .bz-fly { position: absolute; border-radius: 16px; will-change: left, top, transform; }
.stage .bz-fly svg, .stage .bz-fly .card-img {
  width: 100%; height: 100%; filter: drop-shadow(0 10px 18px rgba(0,0,0,.6));
}
/* "1 card left!" burst over a player */
.stage .bz-onecard {
  position: absolute; text-align: center; transform: translateX(-50%);
  font-size: 84px; color: #fff; font-family: 'Monoton', cursive;
  text-shadow: 0 0 24px #ff2b2b, 0 0 10px #ff2b2b, 0 4px 8px #000;
  animation: bz-onecard 1.5s cubic-bezier(.2,1,.3,1) both;
}
@keyframes bz-onecard {
  0%   { opacity: 0; transform: translateX(-50%) scale(.4); }
  18%  { opacity: 1; transform: translateX(-50%) scale(1.12); }
  30%  { transform: translateX(-50%) scale(1); }
  75%  { opacity: 1; }
  100% { opacity: 0; transform: translateX(-50%) translateY(-40px) scale(1); }
}
/* small uniform card-count pill for every player */
.stage .bz-count {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  transform: translateX(-50%); padding: 6px 22px; border-radius: 999px;
  background: rgba(6,16,44,.72); border: 3px solid rgba(150,235,255,.5);
  box-shadow: 0 4px 14px rgba(0,0,0,.4); color: #bfe9ff;
}
.stage .bz-count.team { box-shadow: 0 0 22px currentColor; }
.stage .bz-count-ico { width: 40px; height: 54px; display: grid; place-items: center; }
.stage .bz-count-ico svg { width: 100%; height: 100%; }
.stage .bz-count-n { font: 800 46px 'Rajdhani'; color: #fff; }
.stage .bz-count.team .bz-count-n { color: currentColor; }
/* pro team toggle sheen in the blitz lobby house-rules grid */
.stage .lb-rule-pro { border-color: rgba(255,215,0,.55); background-color: rgba(120,90,0,.16); }
.stage .lb-rule-pro .lb-rule-label { color: #ffe87a; }

/* ============================================================
   LIQUID GLASS — motion & micro-interactions
   ============================================================ */

/* smooth screen-to-screen transition (fires on each UI.show) */
@keyframes lg-screen-in { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.screen:not(.hidden) { animation: lg-screen-in .42s var(--spring) both; }

/* interactive glass buttons: spring press + glow (no positional transform on
   hover, so transform-centred buttons like Start keep their centring) */
button.glass {
  transition: transform .18s var(--spring-back), filter .18s ease,
              box-shadow .3s var(--spring), border-color .3s var(--spring), background-color .3s var(--spring);
}
button.glass:hover {
  filter: brightness(1.2);
  border-color: rgba(185, 248, 255, .7);
  box-shadow: var(--glass-edge), 0 16px 42px rgba(0,0,0,.36), 0 0 30px rgba(0,240,255,.32);
}
button.glass:active { transform: scale(.955); }

/* gold surfaces keep the sheen too */
.pro-card.gold { background: var(--sheen), rgba(215,180,60,.32); }
.dialog.gold { background: var(--sheen), rgba(215,180,60,.35); }

/* tiles & cards that aren't transform-centred get a liquid lift on hover */
.mode-tile, .stage .ms-tile, .reward-card, .pro-card.gold, .shop-card {
  transition: transform .3s var(--spring-back), box-shadow .32s var(--spring),
              filter .2s ease, border-color .3s var(--spring);
}
.mode-tile:hover, .stage .ms-tile:hover, .reward-card:hover, .pro-card.gold:hover, .shop-card:hover {
  transform: translateY(-8px) scale(1.02);
  box-shadow: var(--glass-edge), 0 24px 54px rgba(0,0,0,.42), 0 0 46px rgba(0,240,255,.34);
  filter: brightness(1.05);
}
.mode-tile:active, .stage .ms-tile:active, .pro-card.gold:active, .shop-card:active {
  transform: translateY(-2px) scale(.99);
}

/* faint specular streak sweeping across the icon tiles on hover (only the
   hovered element animates, so this stays cheap even with backdrop blur) */
.mode-tile::after, .stage .ms-tile::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 4;
  background: linear-gradient(115deg, transparent 34%, rgba(255,255,255,.22) 50%, transparent 64%);
  background-size: 260% 100%; background-position: 165% 0; opacity: 0; transition: opacity .3s ease;
}
/* `both` keeps the streak parked at its EXITED end position after the sweep,
   instead of snapping back to the start (165%) — that snap was the sudden glint
   that appeared when you held the cursor on a tile after the sweep finished. */
.mode-tile:hover::after, .stage .ms-tile:hover::after { opacity: 1; animation: lg-sweep 1.15s var(--spring) both; }
@keyframes lg-sweep { from { background-position: 165% 0; opacity: 0; } 30% { opacity: 1; } to { background-position: -70% 0; opacity: 0; } }

/* dialog / overlay / toast entrances */
.overlay:not(.hidden) { animation: lg-fade .26s ease both; }
.overlay:not(.hidden) .dialog { animation: lg-pop .42s var(--spring-back) both; }
@keyframes lg-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes lg-pop { from { opacity: 0; transform: translateY(22px) scale(.93); } to { opacity: 1; transform: none; } }
#toast:not(.hidden) { animation: lg-toast .3s var(--spring-back) both; }
@keyframes lg-toast {
  from { opacity: 0; transform: translateX(-50%) translateY(-18px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* accessibility: honour reduced-motion */
@media (prefers-reduced-motion: reduce) {
  .screen:not(.hidden), .overlay:not(.hidden), .overlay:not(.hidden) .dialog,
  #toast:not(.hidden), .mode-tile:hover::after, .stage .ms-tile:hover::after { animation: none !important; }
  * { transition-duration: .01ms !important; }
}

/* ============================================================
   SEARCH SCREEN — filter panel + room card motion
   ============================================================ */
/* filter panel springs open from the filter button (top-right anchor) and
   folds back out on close (JS holds `.closing` for the out animation) */
.filter-panel { transform-origin: top right; }
.filter-panel:not(.hidden) { animation: lg-filter-in .4s var(--spring-back) both; }
.filter-panel.closing { animation: lg-filter-out .2s ease-in both; pointer-events: none; }
@keyframes lg-filter-in {
  from { opacity: 0; transform: translateY(-16px) scale(.9); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes lg-filter-out {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to   { opacity: 0; transform: translateY(-10px) scale(.94); }
}
/* each row cascades in behind the panel */
.filter-panel:not(.hidden) .filter-col > * { animation: lg-filter-row .34s var(--spring) both; }
@keyframes lg-filter-row { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
.filter-panel:not(.hidden) .filter-col > *:nth-child(1) { animation-delay: .05s; }
.filter-panel:not(.hidden) .filter-col > *:nth-child(2) { animation-delay: .09s; }
.filter-panel:not(.hidden) .filter-col > *:nth-child(3) { animation-delay: .13s; }
.filter-panel:not(.hidden) .filter-col > *:nth-child(4) { animation-delay: .17s; }
.filter-panel:not(.hidden) .filter-col > *:nth-child(5) { animation-delay: .21s; }
/* checkbox flip is springy rather than instant */
.checkbox { transition: background-color .28s var(--spring), border-color .28s var(--spring),
            transform .26s var(--spring-back), box-shadow .28s var(--spring); }
.checkbox:hover { transform: scale(1.12); }
.checkbox:active { transform: scale(.9); }

/* room cards: only NEWLY added cards animate (the grid re-polls every 3s, so
   animating every card each poll made the list strobe) */
.room-card { transition: transform .28s var(--spring-back), box-shadow .3s var(--spring),
             border-color .3s var(--spring), filter .2s ease; }
.room-card:hover { transform: translateY(-6px) scale(1.025);
  box-shadow: var(--glass-edge), 0 22px 48px rgba(0,0,0,.42), 0 0 40px rgba(0,240,255,.32); filter: brightness(1.06); }
.room-card:active { transform: translateY(-1px) scale(.99); }
.room-card.card-in { animation: lg-card-in .46s var(--spring-back) both; }
@keyframes lg-card-in {
  from { opacity: 0; transform: translateY(22px) scale(.92); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.rooms-empty { animation: lg-fade .4s ease both; }

@media (prefers-reduced-motion: reduce) {
  .filter-panel:not(.hidden), .filter-panel.closing, .filter-panel:not(.hidden) .filter-col > *,
  .room-card.card-in, .rooms-empty { animation: none !important; }
}

/* ============================================================
   GRAPHICS TIERS — Low / Medium / High  (html[data-gfx])
   High adds true refraction (SVG displacement + chromatic dispersion);
   Medium keeps the clear glass without the costly filter;
   Low drops backdrop work and motion entirely for weak devices.
   ============================================================ */
.lg-defs { position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none; }

/* ---- HIGH: full liquid glass (Refraction 80, Dispersion 100) ----
   SVG filter refs in backdrop-filter are Chromium-only — iOS/Safari rejects
   `url(#…)`, which killed the ENTIRE declaration and left those surfaces with no
   glass at all ("effects don't load on mobile"). The plain filter is declared
   FIRST so it survives as the fallback when the url() line is thrown out, and
   html.no-svg-backdrop (set in JS for WebKit) forces it even if url() parses but
   never paints. */
html.fx-refract .glass,
html.fx-refract .stage .d-panel {
  backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
  -webkit-backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
  backdrop-filter: url(#lg-refract) saturate(1.9) contrast(1.06) brightness(1.06);
  -webkit-backdrop-filter: url(#lg-refract) saturate(1.9) contrast(1.06) brightness(1.06);
}
html.no-svg-backdrop.fx-refract .glass,
html.no-svg-backdrop.fx-refract .stage .d-panel {
  backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06) !important;
  -webkit-backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06) !important;
}

/* The lobby's large glass surfaces use the cheaper single-pass refraction — the
   chromatic split is invisible at that size but costs 3× per pixel. Same
   specificity as the rule above, placed after it so it wins for these panels.
   (Scoped to the main-screen / room-lobby classes; games keep #lg-refract.) */
html.fx-refract .stage .ms-trophy-panel,
html.fx-refract .stage .ms-lvl-panel,
html.fx-refract .stage .ms-tile,
html.fx-refract .stage .ms-play {
  backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);          /* WebKit fallback */
  -webkit-backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
  backdrop-filter: url(#lg-refract-lite) saturate(1.9) contrast(1.06) brightness(1.06);
  -webkit-backdrop-filter: url(#lg-refract-lite) saturate(1.9) contrast(1.06) brightness(1.06);
}
html.no-svg-backdrop.fx-refract .stage .ms-trophy-panel,
html.no-svg-backdrop.fx-refract .stage .ms-lvl-panel,
html.no-svg-backdrop.fx-refract .stage .ms-tile,
html.no-svg-backdrop.fx-refract .stage .ms-play {
  backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06) !important;
  -webkit-backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06) !important;
}

/* ---- MEDIUM: clear glass, no displacement filter ---- */
html[data-gfx="medium"] .glass,
html[data-gfx="medium"] .stage .d-panel {
  backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
  -webkit-backdrop-filter: saturate(1.9) contrast(1.06) brightness(1.06);
}

/* ---- LOW: no backdrop filtering, no motion, denser tint so text stays legible ---- */
html[data-gfx="low"] .glass,
html[data-gfx="low"] .stage .d-panel {
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
}
html[data-gfx="low"] .glass { background: var(--sheen), rgba(16, 46, 92, .72); }
html[data-gfx="low"] .stage .d-panel { background: var(--sheen), rgba(16, 46, 92, .74); }
html[data-gfx="low"] .red-glass { background: var(--sheen), rgba(120, 20, 30, .72); }
html[data-gfx="low"] .green-glass { background: var(--sheen), rgba(12, 80, 30, .70); }
html[data-gfx="low"] .yellow-glass { background: var(--sheen), rgba(110, 82, 10, .72); }
/* kill the expensive bits: animations, transitions, blur/glow shadows */
html[data-gfx="low"] *,
html[data-gfx="low"] *::before,
html[data-gfx="low"] *::after {
  animation: none !important;
  transition: none !important;
  text-shadow: none !important;
}
html[data-gfx="low"] .glass,
html[data-gfx="low"] .stage .d-panel { box-shadow: var(--glass-edge); }
html[data-gfx="low"] .mode-tile::after,
html[data-gfx="low"] .stage .ms-tile::after { display: none; }

/* ---- MEDIUM lobby perf: the menus don't need live backdrop-filter, and the
   title's animated text-shadow is a per-frame CPU repaint that also invalidates
   every backdrop-filtered panel behind it (the root cause of ~30fps on medium).
   Scoped to `.screen:not(.game-screen)` so the four game screens keep the medium
   look exactly as-is — only the lobby/menu screens are optimized. Panels fall back
   to the Low tier's denser tint so they still read as glass; the title keeps a
   static neon glow. High tier is unchanged. ---- */
html[data-gfx="medium"] .screen:not(.game-screen) .glass,
html[data-gfx="medium"] .screen:not(.game-screen) .stage .d-panel {
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
}
/* Glassier + more see-through than a flat tint: a static diagonal sheen (cheap
   gradient, no backdrop sampling) over a low-alpha fill lets the nebula read
   through, so the panels/tiles look like real glass while staying cheap. */
html[data-gfx="medium"] .screen:not(.game-screen) .glass,
html[data-gfx="medium"] .screen:not(.game-screen) .stage .d-panel {
  background:
    linear-gradient(152deg, rgba(190, 240, 255, .18), rgba(190, 240, 255, .05) 40%, rgba(8, 16, 44, 0) 72%),
    rgba(14, 34, 78, .40);
}
html[data-gfx="medium"] .screen:not(.game-screen) .red-glass { background: var(--sheen), rgba(120, 20, 30, .72); }
html[data-gfx="medium"] .screen:not(.game-screen) .green-glass { background: var(--sheen), rgba(12, 80, 30, .70); }
html[data-gfx="medium"] .screen:not(.game-screen) .yellow-glass { background: var(--sheen), rgba(110, 82, 10, .72); }
html[data-gfx="medium"] .stage .ms-title {
  animation: none; will-change: auto; text-shadow: 0 0 26px rgba(0, 240, 255, .85);
}

/* settings: the Low/Medium/High picker */
.gfx-row { display: flex; gap: 1vmin; flex-wrap: wrap; }
.gfx-opt {
  padding: clamp(6px, 1.2vmin, 14px) clamp(16px, 3vmin, 38px); border-radius: 999px;
  font: 700 clamp(13px, 2.4vmin, 26px) 'Rajdhani'; color: var(--cyan);
  background: rgba(0, 240, 255, .10); border: 2px solid rgba(0, 240, 255, .28);
  transition: all .25s var(--spring);
}
.gfx-opt.on { color: #fff; background-color: rgba(0, 240, 255, .28); border-color: var(--cyan);
  box-shadow: 0 0 22px rgba(0, 240, 255, .4); }

/* car skin picker (Cyber speed lobby) */
.skin-opt { position: relative; display: flex; flex-direction: column; align-items: center; gap: .6vmin; }
.skin-opt .skin-name { font-size: clamp(11px, 2vmin, 22px); color: var(--blue-soft); }
.skin-opt.mine .skin-name { color: #fff; }
.skin-opt.locked { opacity: .5; filter: grayscale(.6); }
.skin-opt.locked:hover { opacity: .72; }

/* ============================================================
   LIQUID GLASS — EVERY SURFACE
   Components below set their own background-color, so the earlier `.glass`
   rules never reached them. We layer the sheen with `background-image` (which
   leaves their colour intact) and add the same lit bevel, so buttons, toggles,
   chips, inputs, cards and in-game panels all read as the same material.
   Deliberately excluded: progress fills and decorative bars (.bar-fill, .q-fill,
   .pass-seg, .rainbow-bg, .filter-btn .fl) — those ARE gradients already.
   ============================================================ */
.pill-btn, .pack-btn, .pack-redeem, .gfx-opt, .q-claim, .pass-tab, .pass-pro,
.toggle, .checkbox, .arrow-btn, .whop-embed-close, .size-btn, .select-btn,
.reward-card, .quest, .crate-item, .pass-cell, .car-opt, .skin-opt,
.podium-card, .variant-row,
.search-input, .pass-input, .dialog input.name-input,
.stage .lb-toggle, .stage .lb-unlim-t, .stage .lb-arrow, .stage .lb-rule,
.stage .lb-user, .stage .lb-privacy, .stage .lb-passinput, .stage .lb-start,
.stage .go-tip, .stage .go-podium, .stage .mm-input,
.stage .bz-count, .stage .bz-call, .stage .bz-pending,
.stage .rule-card, .stage .stepper-box {
  background-image: var(--sheen);
  box-shadow: var(--glass-edge), var(--bevel-sm), var(--e2);
}

/* refraction for the larger surfaces only — running the displacement filter on
   every tiny toggle would be a needless GPU cost */
html.fx-refract .reward-card,
html.fx-refract .quest,
html.fx-refract .crate-item,
html.fx-refract .pass-cell,
html.fx-refract .podium-card,
html.fx-refract .car-opt,
html.fx-refract .variant-row,
html.fx-refract .stage .lb-rule,
html.fx-refract .stage .lb-user,
html.fx-refract .stage .go-podium,
html.fx-refract .stage .rule-card {
  backdrop-filter: saturate(1.6) brightness(1.04);          /* WebKit fallback */
  -webkit-backdrop-filter: saturate(1.6) brightness(1.04);
  backdrop-filter: url(#lg-refract) saturate(1.6) brightness(1.04);
  -webkit-backdrop-filter: url(#lg-refract) saturate(1.6) brightness(1.04);
}
html.no-svg-backdrop[data-gfx="high"] .reward-card,
html.no-svg-backdrop[data-gfx="high"] .quest,
html.no-svg-backdrop[data-gfx="high"] .crate-item,
html.no-svg-backdrop[data-gfx="high"] .pass-cell,
html.no-svg-backdrop[data-gfx="high"] .car-opt,
html.no-svg-backdrop[data-gfx="high"] .variant-row,
html.no-svg-backdrop[data-gfx="high"] .stage .lb-rule,
html.no-svg-backdrop[data-gfx="high"] .stage .lb-user,
html.no-svg-backdrop[data-gfx="high"] .stage .go-podium,
html.no-svg-backdrop[data-gfx="high"] .stage .rule-card {
  backdrop-filter: saturate(1.6) brightness(1.04) !important;
  -webkit-backdrop-filter: saturate(1.6) brightness(1.04) !important;
}
html[data-gfx="medium"] .reward-card, html[data-gfx="medium"] .quest,
html[data-gfx="medium"] .crate-item, html[data-gfx="medium"] .pass-cell,
html[data-gfx="medium"] .podium-card, html[data-gfx="medium"] .car-opt,
html[data-gfx="medium"] .variant-row,
html[data-gfx="medium"] .stage .lb-rule, html[data-gfx="medium"] .stage .lb-user,
html[data-gfx="medium"] .stage .go-podium, html[data-gfx="medium"] .stage .rule-card {
  backdrop-filter: saturate(1.6) brightness(1.04);
  -webkit-backdrop-filter: saturate(1.6) brightness(1.04);
}
html[data-gfx="low"] .reward-card, html[data-gfx="low"] .quest,
html[data-gfx="low"] .crate-item, html[data-gfx="low"] .pass-cell,
html[data-gfx="low"] .podium-card, html[data-gfx="low"] .car-opt,
html[data-gfx="low"] .variant-row,
html[data-gfx="low"] .stage .lb-rule, html[data-gfx="low"] .stage .lb-user,
html[data-gfx="low"] .stage .go-podium, html[data-gfx="low"] .stage .rule-card {
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
}

/* ============================================================
   LEADERBOARD (top 100)
   ============================================================ */
#screen-leaderboard { align-items: center; gap: 1.6vmin; }
#screen-leaderboard h1 { flex: none; }
.lb-tabs { display: flex; gap: 1vmin; flex-wrap: wrap; justify-content: center; flex: none; }
.lb-tab {
  padding: clamp(6px, 1.2vmin, 14px) clamp(14px, 2.6vmin, 34px); border-radius: 999px;
  font: 700 clamp(13px, 2.2vmin, 24px) 'Rajdhani'; color: var(--blue-soft);
  background: rgba(0, 240, 255, .10); border: 2px solid rgba(0, 240, 255, .28);
  cursor: pointer; transition: all .2s var(--spring);
}
.lb-tab.on { color: #fff; background-color: rgba(0, 240, 255, .28); border-color: var(--cyan);
  box-shadow: 0 0 20px rgba(0, 240, 255, .4); }
.lb-head, .lb-row { display: grid; grid-template-columns: clamp(48px,10vw,90px) 1fr clamp(80px,22vw,220px);
  align-items: center; gap: 1.4vmin; width: min(96vw, 760px); }
.lb-head { flex: none; color: var(--blue-soft); font-weight: 700; opacity: .8;
  font-size: clamp(11px, 1.9vmin, 20px); padding: 0 clamp(12px, 2.2vmin, 24px); text-transform: uppercase; letter-spacing: 1px; }
.lb-head .lb-val, .lb-row .lb-val { text-align: right; }
.lb-list { flex: 1; width: min(96vw, 760px); overflow-y: auto; display: flex; flex-direction: column; gap: .8vmin;
  padding: 0 0 max(1.6vmin, env(safe-area-inset-bottom)); min-height: 0; }
.lb-row {
  background: rgba(10, 30, 60, .45); border: 1px solid rgba(120, 220, 255, .22);
  border-radius: 14px; padding: clamp(8px, 1.5vmin, 16px) clamp(12px, 2.2vmin, 24px);
  font-size: clamp(13px, 2.3vmin, 26px); color: #dff;
}
.lb-row .lb-rank { font-weight: 800; color: var(--blue-soft); }
.lb-row.top .lb-rank { font-size: 1.3em; }
.lb-row .lb-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-row .lb-val { color: var(--cyan); font-size: 1.05em; }
.lb-row.me { border-color: var(--yellow); box-shadow: 0 0 18px rgba(255, 215, 0, .3); }
.lb-row.me .lb-name { color: var(--yellow); }
.lb-row.top { border-color: rgba(255, 215, 0, .5); background: rgba(40, 34, 10, .5); }
.lb-row .lb-namewrap { display: flex; align-items: center; gap: 1vmin; min-width: 0; }
.lb-row .lb-av { width: clamp(28px, 4.6vmin, 46px); height: clamp(28px, 4.6vmin, 46px); flex: none; }
.lb-row.clickable { cursor: pointer; transition: border-color .2s, transform .12s, box-shadow .2s; }
.lb-row.clickable:hover { border-color: var(--cyan); box-shadow: 0 0 18px rgba(0, 240, 255, .25); transform: translateY(-1px); }
.lb-empty { text-align: center; color: var(--blue-soft); padding: 6vmin 0; font-size: clamp(14px, 2.6vmin, 28px); }

/* ============================================================
   CREDITS VIDEO PLAYER
   ============================================================ */
/* Credits (text). A scrollable list of contributors — no video player. */
.credits-box { display: flex; flex-direction: column; gap: 1.2vmin; width: min(94vw, 640px); max-height: 90vh;
  padding: clamp(14px, 2.4vmin, 28px); }
.credits-title { text-align: center; margin: 0; font-size: clamp(22px, 4.4vmin, 44px); flex: none; }
.credits-scroll { flex: 1; min-height: 0; overflow-y: auto; padding-right: 6px;
  display: flex; flex-direction: column; gap: clamp(12px, 2vmin, 22px); }
.cr-sect { display: flex; flex-direction: column; gap: 6px; }
.cr-sect h3 { margin: 0 0 4px; font-size: clamp(16px, 3vmin, 30px); letter-spacing: 1.5px; }
.cr-sect ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.cr-sect li { color: #dff; font-size: clamp(13px, 2.2vmin, 22px); padding: 8px 12px; border-radius: 10px;
  background: rgba(10, 20, 45, .5); border: 1px solid rgba(120, 200, 255, .18); }
.cr-sect li b.cyan { color: var(--cyan); }
.cr-where { color: var(--blue-soft); font-weight: 600; font-size: .92em; margin-left: 4px; }
.cr-by { color: #fff; font-weight: 700; }
.cr-foot { text-align: center; margin: 4px 0 0; font-size: clamp(12px, 2vmin, 18px); opacity: .9; }
.cr-close { align-self: center; padding: clamp(8px, 1.6vmin, 16px) clamp(30px, 6vmin, 70px);
  border-radius: 999px; font: 700 clamp(14px, 2.6vmin, 28px) 'Rajdhani'; cursor: pointer; flex: none; }

/* ============================================================
   DEVELOPER MODE console (owner only)
   ============================================================ */
.dev-fab {
  position: fixed; z-index: 4000; right: max(14px, env(safe-area-inset-right), var(--safe-x, 0px)); bottom: max(14px, env(safe-area-inset-bottom));
  padding: 14px 28px; border-radius: 999px; cursor: pointer;
  font: 800 clamp(17px, 2.8vmin, 26px) 'Rajdhani'; color: #241a00;
  background: linear-gradient(90deg, #FFE87A, #FFB020); border: none;
  box-shadow: 0 0 20px rgba(255, 215, 0, .6), 0 4px 12px rgba(0, 0, 0, .4);
}
.dev-fab:hover { filter: brightness(1.08); transform: translateY(-2px); }
#dev-overlay { z-index: 4001; }
.dev-panel {
  width: min(96vw, 640px); max-height: 90vh; display: flex; flex-direction: column;
  padding: clamp(12px, 2vmin, 22px); gap: 1vmin;
  border: 2px solid rgba(255, 215, 0, .5); box-shadow: 0 0 40px rgba(255, 215, 0, .25);
}
.dev-head { display: flex; align-items: center; justify-content: space-between; flex: none; }
.dev-head .mono { font-size: clamp(18px, 3.4vmin, 32px); }
.dev-x { background: rgba(255, 80, 80, .2); border: 1px solid rgba(255, 120, 120, .5); color: #ff9a9a;
  width: 40px; height: 40px; border-radius: 10px; cursor: pointer; font-size: 20px; }
.dev-body { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 1.2vmin; min-height: 0; }
/* Product tabs: Cyberverse (the game) vs the Discord bot. They share one
   profile row but are separate surfaces to test, and a flat list made it
   ambiguous which product a given toggle affected. */
.dev-tabs { display: flex; gap: 1vmin; flex: none; flex-wrap: wrap; }
.dev-tab {
  font: inherit; font-weight: 800; letter-spacing: .5px; cursor: pointer;
  font-size: clamp(11px, 1.9vmin, 16px); padding: 1vmin 2vmin; border-radius: 1.2vmin;
  color: var(--cyan); background: rgba(0, 240, 255, .08);
  border: 1px solid rgba(0, 240, 255, .35); transition: filter .15s, transform .08s;
}
.dev-tab:hover { filter: brightness(1.25); }
.dev-tab:active { transform: scale(.97); }
.dev-tab.on { color: #04121e; background: var(--cyan); border-color: var(--cyan); }
.dev-pane { display: flex; flex-direction: column; gap: 1.2vmin; }
.dev-pane[hidden] { display: none; }
.dev-note { color: var(--muted, #8fa3c8); font-size: clamp(10px, 1.6vmin, 13px); line-height: 1.45; }
.dev-sect { background: rgba(10, 20, 45, .5); border: 1px solid rgba(120, 200, 255, .18); border-radius: 12px; padding: 10px 12px; }
.dev-sect-title { color: var(--yellow); font-weight: 800; text-transform: uppercase; letter-spacing: 1px;
  font-size: clamp(11px, 1.9vmin, 16px); margin-bottom: 8px; }
.dev-row { display: flex; flex-wrap: wrap; gap: 8px; }
/* Pro-pass toggle switch (Account section) */
.dev-switch {
  display: flex; align-items: center; justify-content: space-between; gap: 14px; cursor: pointer;
  padding: 9px 14px; margin-bottom: 10px; border-radius: 12px; user-select: none;
  background: rgba(4, 10, 30, .4); border: 1px solid rgba(0, 240, 255, .22);
}
.dev-switch-label { font-size: clamp(14px, 2.1vmin, 18px); font-weight: 700; color: #fff; }
.dev-switch-track {
  flex: none; width: 58px; height: 30px; border-radius: 999px; position: relative;
  background: rgba(220, 60, 60, .22); border: 1px solid rgba(255, 110, 110, .5);
  transition: background .18s, border-color .18s;
}
.dev-switch-knob {
  position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%;
  background: #ff9a9a; transition: left .18s, background .18s;
}
.dev-switch.on .dev-switch-track { background: rgba(0, 200, 60, .25); border-color: rgba(80, 255, 120, .6); }
.dev-switch.on .dev-switch-knob { left: 33px; background: #9dffb4; }
.dev-btn { padding: 8px 14px; border-radius: 10px; cursor: pointer; font: 700 clamp(12px, 1.9vmin, 17px) 'Rajdhani';
  border: 1px solid transparent; transition: filter .15s, transform .1s; white-space: nowrap; }
.dev-btn:hover { filter: brightness(1.15); }
.dev-btn:active { transform: scale(.95); }
.dev-btn.green { background: rgba(0, 200, 60, .22); border-color: rgba(80, 255, 120, .5); color: #9dffb4; }
.dev-btn.red { background: rgba(220, 40, 40, .2); border-color: rgba(255, 110, 110, .5); color: #ff9a9a; }
.dev-btn.cyan { background: rgba(0, 200, 255, .18); border-color: rgba(0, 240, 255, .5); color: var(--cyan); }
.dev-btn.gold { background: linear-gradient(90deg, rgba(255,215,0,.3), rgba(255,160,0,.25)); border-color: rgba(255, 215, 0, .6); color: #ffe87a; }
.dev-num { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.dev-num-lbl { flex: 0 0 clamp(72px, 18vw, 110px); color: var(--cyan); font-weight: 700; font-size: clamp(12px, 2vmin, 18px); }
.dev-num-in { flex: 1; min-width: 80px; background: rgba(0, 20, 40, .6); border: 1px solid rgba(0, 240, 255, .35);
  border-radius: 8px; color: #fff; font: 600 clamp(12px, 2vmin, 18px) 'Rajdhani'; padding: 7px 12px; outline: none; }
.dev-hk { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.dev-hk-name { flex: 0 0 clamp(80px, 22vw, 150px); color: #dff; font-weight: 700; font-size: clamp(12px, 2vmin, 17px);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dev-hk-sel { flex: 1; min-width: 90px; background: rgba(0, 20, 40, .6); border: 1px solid rgba(0, 240, 255, .35);
  border-radius: 8px; color: #fff; font: 600 clamp(12px, 1.9vmin, 16px) 'Rajdhani'; padding: 6px 8px; }
.dev-note { color: var(--blue-soft); font-size: clamp(11px, 1.8vmin, 15px); margin-top: 6px; opacity: .85; }
.dev-foot { display: flex; gap: 8px; justify-content: flex-end; flex: none; padding-top: 4px; }

/* Dev mode: extra-settings styling (the single .dev-fab is the indicator) */
.section-header.gold-header { background: linear-gradient(90deg, rgba(255,215,0,.28), rgba(255,160,0,.18)); border: 1px solid rgba(255,215,0,.4); }
.value-chip.gold-chip { color: #ffe87a; background-color: rgba(255,215,0,.14); border: 1px solid rgba(255,215,0,.4); }
.dev-fps-overlay {
  position: fixed; z-index: 3998; top: max(8px, env(safe-area-inset-top)); right: max(8px, env(safe-area-inset-right), var(--safe-x, 0px));
  padding: 4px 10px; border-radius: 8px; font: 800 clamp(11px, 1.7vmin, 16px) 'Rajdhani'; font-variant-numeric: tabular-nums;
  color: #9dffb4; background: rgba(0, 20, 10, .6); border: 1px solid rgba(80, 255, 120, .4); pointer-events: none;
}

/* ============================================================
   PROFILE CHIP (main screen) + FRIENDS + Discord
   ============================================================ */
.stage .ms-profile { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 10px; cursor: pointer; }
.stage .ms-profile .avatar-disc { width: 90px; height: 90px; }
.stage .ms-profile .pc-sub { font-size: 30px; color: var(--cyan); font-weight: 700; }

.value-chip.discord-chip { color: #fff; background-color: #5865F2; border: 1px solid rgba(120,130,255,.6); }

#screen-friends { align-items: center; gap: 1.6vmin; }
#screen-friends h1 { flex: none; }

/* ---- Profile screen: tabs + profile card ---- */
.profile-tabs { display: flex; gap: 1vmin; width: min(100%, 620px); flex: none; }
.profile-tab { flex: 1; padding: clamp(9px,1.7vmin,18px); border-radius: 999px; cursor: pointer;
  font: 700 clamp(14px,2.4vmin,24px) 'Rajdhani'; color: var(--blue-soft);
  border: 2px solid rgba(0,240,255,.25); transition: color .2s, border-color .2s, filter .2s; }
.profile-tab:hover { filter: brightness(1.15); }
.profile-tab.on { color: var(--cyan); border-color: var(--cyan); box-shadow: 0 0 16px rgba(0,240,255,.25); }
.profile-pane { width: min(100%, 620px); flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 1.2vmin;
  overflow-y: auto; padding-bottom: max(1.4vmin, env(safe-area-inset-bottom)); }
.profile-pane.hidden { display: none; }

.profile-card { padding: clamp(14px,2.4vmin,26px); border-radius: 20px; display: flex; flex-direction: column; gap: 1.2vmin; }
.pf-head { display: flex; align-items: center; gap: 1.4vmin; }
.pf-head .pf-av { width: clamp(56px,9vmin,96px); height: clamp(56px,9vmin,96px); flex: none; }
.pf-name { color: #fff; font-weight: 700; font-size: clamp(20px,3.4vmin,34px); }
.pf-handle { color: var(--cyan); font-size: clamp(12px,2vmin,19px); opacity: .85; }
.pf-lvl { color: var(--blue-soft); font-size: clamp(13px,2.1vmin,20px); }
.pf-section-h { color: var(--cyan); font-weight: 700; text-transform: uppercase; letter-spacing: 1px;
  font-size: clamp(11px,1.9vmin,16px); margin-top: .4vmin; }
.pf-best { display: flex; align-items: center; gap: 1vmin; min-height: clamp(40px,6vmin,64px); }
.pf-best .pf-bestav { width: clamp(40px,6vmin,64px); height: clamp(40px,6vmin,64px); flex: none; }
.pf-bestname { color: #fff; font-weight: 700; font-size: clamp(15px,2.6vmin,26px); flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pf-bestempty { color: var(--blue-soft); font-size: clamp(13px,2.2vmin,22px); }
.pf-clear { padding: clamp(6px,1.2vmin,12px) clamp(14px,2.2vmin,26px); border-radius: 999px;
  font: 700 clamp(12px,1.9vmin,18px) 'Rajdhani'; cursor: pointer; flex: none; }
.pf-privacy { display: flex; align-items: center; justify-content: space-between; gap: 1.2vmin; }
.pf-privacy-label { color: #fff; font-size: clamp(13px,2.2vmin,22px); }
.pf-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1vmin; }
.pf-stat { background: rgba(10,30,60,.5); border: 1px solid rgba(120,220,255,.22); border-radius: 14px;
  padding: clamp(8px,1.5vmin,16px); text-align: center; }
.pf-stat-v { color: var(--cyan); font-size: clamp(20px,3.6vmin,40px); line-height: 1; }
.pf-stat-l { color: var(--blue-soft); font-size: clamp(11px,1.7vmin,16px); margin-top: .4vmin; }
.fr-star { flex: none; width: clamp(38px,5.6vmin,56px); height: clamp(38px,5.6vmin,56px); border-radius: 50%;
  cursor: pointer; font-size: clamp(18px,3vmin,30px); line-height: 1; color: var(--blue-soft);
  border: 1px solid rgba(255,215,0,.35); transition: color .2s, transform .15s, filter .2s; }
.fr-star:hover { filter: brightness(1.2); transform: scale(1.08); }
.fr-star.on { color: var(--gold); border-color: var(--gold); box-shadow: 0 0 14px rgba(255,215,0,.4); }

/* ---- profile widgets (drag to rearrange, colourful, add/remove) ---- */
/* Two-up wherever there's room — as a single stacked column each widget filled
   the viewport and you could only ever see one at a time. */
.wdg-board { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1vmin; align-items: start; }
/* the photo widget is the tall one — let it span the full row */
.wdg-board .wdg.wdg-photo { grid-column: 1 / -1; }
.wdg { --accent: #00F0FF; display: flex; align-items: center; gap: 1.2vmin; padding: clamp(10px,1.8vmin,18px);
  border-radius: 16px; border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-left: 4px solid var(--accent);
  background: linear-gradient(100deg, color-mix(in srgb, var(--accent) 16%, rgba(10,30,60,.55)), rgba(10,30,60,.55));
  transition: box-shadow .2s, opacity .2s, transform .12s, border-color .2s; }
.wdg:hover { box-shadow: 0 6px 22px color-mix(in srgb, var(--accent) 30%, transparent); }
.wdg-ico { flex: none; width: clamp(38px,6vmin,56px); height: clamp(38px,6vmin,56px); border-radius: 13px;
  display: grid; place-items: center; background: color-mix(in srgb, var(--accent) 22%, rgba(0,0,0,.25));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent); }
.wdg-ico svg { width: 62%; height: 62%; filter: drop-shadow(0 0 6px color-mix(in srgb, var(--accent) 60%, transparent)); }
.wdg-del { flex: none; width: clamp(26px,4vmin,34px); height: clamp(26px,4vmin,34px); border-radius: 50%;
  cursor: pointer; color: var(--blue-soft); font-size: clamp(16px,2.6vmin,24px); line-height: 1;
  background: rgba(255,60,60,.12); border: 1px solid rgba(255,90,90,.35); transition: filter .15s, transform .12s, background .15s; }
.wdg-del:hover { background: rgba(255,60,60,.28); color: #fff; transform: scale(1.1); }
.wdg.dragging { opacity: .96; box-shadow: 0 16px 46px color-mix(in srgb, var(--accent) 50%, transparent); z-index: 20; position: relative; }
.wdg-in { animation: wdg-in-kf .32s cubic-bezier(.34,1.56,.64,1) both; }
@keyframes wdg-in-kf { from { opacity: 0; transform: translateY(10px) scale(.94); } to { opacity: 1; transform: none; } }
.wdg-exit { opacity: 0; transform: scale(.9) translateX(-24px); transition: opacity .18s ease, transform .18s ease; pointer-events: none; }
.wdg-add { display: flex; align-items: center; justify-content: center; gap: 1vmin; cursor: pointer;
  padding: clamp(10px,1.7vmin,16px); border-radius: 16px; border: 1.5px dashed rgba(120,220,255,.5);
  background: rgba(10,30,60,.3); color: var(--cyan); transition: filter .15s, border-color .2s, background .2s, transform .12s; }
.wdg-add:hover { filter: brightness(1.15); border-color: var(--cyan); background: rgba(0,240,255,.1); transform: translateY(-1px); }
.wdg-add:active { transform: scale(.98); }
.wdg-add.full { border-style: solid; border-color: rgba(0,255,26,.35); color: var(--green); cursor: default; }
.wdg-add-plus { font-size: clamp(22px,3.4vmin,32px); font-weight: 800; line-height: 1; }
.wdg-add-txt { font-size: clamp(14px,2.3vmin,22px); font-weight: 700; }
.wdg-grip { flex: none; display: grid; grid-template-columns: repeat(2, 6px); grid-template-rows: repeat(3, 6px);
  gap: 4px; align-content: center; cursor: grab; touch-action: none; padding: 2px; }
.wdg-grip:active { cursor: grabbing; }
.wdg-grip span { width: 6px; height: 6px; border-radius: 50%; background: rgba(120,220,255,.55); }
.wdg-body { flex: 1; min-width: 0; }
.wdg-body.clickable { cursor: pointer; }
.wdg-body.clickable:hover .wdg-val { filter: brightness(1.2); }
.wdg-title { color: var(--blue-soft); text-transform: uppercase; letter-spacing: 1px; font-size: clamp(10px,1.6vmin,14px); font-weight: 700; }
.wdg-val { color: #fff; font-size: clamp(15px,2.6vmin,26px); font-weight: 700; margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wdg-bf { display: flex; align-items: center; gap: 1vmin; margin-top: 4px; }
.wdg-bf .wdg-bfav { width: clamp(34px,5vmin,52px); height: clamp(34px,5vmin,52px); flex: none; }
.wdg-bfname { color: #fff; font-weight: 700; font-size: clamp(15px,2.6vmin,24px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wdg-empty { color: var(--blue-soft); font-size: clamp(13px,2.1vmin,20px); margin-top: 4px; }
/* The photo widget lays out as a column so a large image sits full-width BELOW the
   header (grip + camera icon) instead of squeezing into the text row and spilling
   out. The card grows to the photo's height (overflow clipped as a safety net). */
.wdg.wdg-photo { flex-wrap: wrap; align-items: center; overflow: hidden; }
.wdg.wdg-photo > .wdg-body { flex-basis: 100%; order: 3; min-width: 0; }
.wdg.wdg-photo > .wdg-del { margin-left: auto; }
.wdg-photo-img { display: block; width: 100%; max-height: clamp(160px, 40vmin, 340px); object-fit: cover;
  border-radius: 12px; margin-top: 6px; border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); }
.wdg-photo-img.pending { filter: grayscale(.5) brightness(.7); }
.wdg-photo-hint { font-size: clamp(11px,1.8vmin,16px) !important; color: var(--blue-soft) !important; margin-top: 5px; }
.wdg-declined { color: #ff9a9a !important; }
/* public profile viewer photo + report */
.pp-photo-wrap { position: relative; margin-bottom: 1.2vmin; }
.pp-photo { display: block; width: 100%; max-height: 46vh; object-fit: cover; border-radius: 14px; border: 1px solid var(--line, rgba(0,240,255,.28)); }
.pp-report { position: absolute; top: 10px; right: 10px; border-radius: 999px; cursor: pointer;
  padding: 6px 14px; font: 700 clamp(12px,1.9vmin,17px) 'Rajdhani';
  background: rgba(255,48,48,.28); border: 1px solid rgba(255,90,90,.6); color: #fff;
  backdrop-filter: blur(4px); transition: filter .15s, transform .12s; }
.pp-report:hover { filter: brightness(1.2); transform: scale(1.04); }
.pp-report:disabled { opacity: .7; cursor: default; }

/* prestige best-friend widget */
.wdg-bff { display: flex; align-items: center; gap: 1.4vmin; margin-top: 5px; padding: clamp(6px,1.2vmin,12px);
  border-radius: 12px; background: linear-gradient(100deg, rgba(255,215,0,.18), rgba(255,215,0,.04));
  border: 1px solid rgba(255,215,0,.42); }
.wdg-bff .wdg-bffav { width: clamp(48px,8vmin,74px); height: clamp(48px,8vmin,74px); flex: none;
  box-shadow: 0 0 0 3px rgba(255,215,0,.55), 0 0 18px rgba(255,215,0,.4); }
.wdg-bffmeta { min-width: 0; }
.wdg-bffname { color: #fff; font-weight: 800; font-size: clamp(16px,2.8vmin,28px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wdg-bffhandle { color: var(--gold); font-size: clamp(12px,2vmin,18px); opacity: .9; }
.wdg-bfftag { color: var(--gold); font-weight: 700; font-size: clamp(11px,1.8vmin,15px); letter-spacing: .5px; text-shadow: 0 0 10px rgba(255,215,0,.5); margin-top: 2px; }
/* blitz favourite-card thumbnail */
.wdg-cardrow { display: flex; align-items: center; gap: 1.2vmin; margin-top: 4px; }
.wdg-cardimg { flex: none; width: clamp(44px,7vmin,66px); }
.wdg-cardimg .card-img { width: 100%; height: auto; display: block; border-radius: 6px; }
/* meme best-4 gif grid */
.wdg-gifgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: .6vmin; margin-top: 5px; }
.wdg-gif-thumb { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 8px; border: 1px solid rgba(120,220,255,.25); background: #000; }

/* favourite-card picker */
.cardpick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(clamp(48px,9vmin,74px), 1fr)); gap: 1vmin; max-height: 56vh; overflow-y: auto; padding: 2px; }
.cardpick { padding: 2px; border-radius: 8px; cursor: pointer; background: transparent; border: 2px solid transparent; transition: transform .12s, border-color .15s; }
.cardpick:hover { transform: scale(1.06); }
.cardpick.sel { border-color: var(--cyan); box-shadow: 0 0 14px rgba(0,240,255,.4); }
.cardpick .card-img { width: 100%; height: auto; display: block; border-radius: 6px; }
/* best-gifs picker */
.gifpick-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(clamp(96px,20vmin,150px), 1fr));
  gap: 1vmin; max-height: 52vh; overflow-y: auto; padding: 2px;
  /* The picker sits in .review-body, a COLUMN FLEX container. Without these the
     grid was squashed as a flex item and its auto rows compressed to ~46px,
     clipping every square thumbnail into a flat strip. flex:none stops the
     shrink; min-content + start stop the rows being stretched/compressed. */
  flex: none; grid-auto-rows: min-content; align-content: start;
}
.gifpick { position: relative; cursor: pointer; border-radius: 10px; overflow: hidden; border: 2px solid transparent; }
.gifpick img { width: 100%; aspect-ratio: 1; object-fit: cover; display: block; background: #000; }
.gifpick.sel { border-color: #2D01FC; box-shadow: 0 0 14px rgba(91,107,255,.6); }
.gifpick-check { position: absolute; top: 4px; left: 4px; background: #2D01FC; color: #fff; border-radius: 50%;
  width: 24px; height: 24px; display: grid; place-items: center; font-weight: 800; font-size: 14px; box-shadow: 0 0 10px rgba(91,107,255,.8); }
.gifpick-del { position: absolute; top: 4px; right: 4px; width: 24px; height: 24px; border-radius: 50%; border: none;
  cursor: pointer; background: rgba(0,0,0,.55); font-size: 13px; line-height: 1; }

.gifpick-view { position: absolute; bottom: 4px; right: 4px; width: 28px; height: 28px; border-radius: 8px; border: none;
  cursor: pointer; background: rgba(0,0,0,.6); color: #fff; font-size: 15px; line-height: 1; }
.wdg-gif-thumb { cursor: zoom-in; }

/* Fullscreen gif viewer: the gif as big as the screen allows, one bar of actions. */
.gifview { position: fixed; inset: 0; z-index: 9000; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2.2vmin; padding: 3vmin;
  background: rgba(2,4,14,.92); }
.gifview-stage { flex: 1; min-height: 0; width: 100%; display: grid; place-items: center; }
.gifview-stage img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 14px;
  box-shadow: 0 20px 60px rgba(0,0,0,.6); }
.gifview-bar { display: flex; gap: 1.4vmin; flex-wrap: wrap; justify-content: center;
  padding-bottom: env(safe-area-inset-bottom, 0px); }
.gifview-bar .fr-act { min-width: 120px; }
html[data-gfx="low"] .gifview-stage img { animation: none; }

/* Meme Battles: #2D01FC save-to-favourites star on each gif */
.mm-fav { position: absolute; top: 10px; right: 10px; width: clamp(30px,4vmin,44px); height: clamp(30px,4vmin,44px);
  border-radius: 50%; cursor: pointer; z-index: 6; background: rgba(10,10,30,.55); border: 2px solid #2D01FC;
  color: #8fa0ff; font-size: clamp(15px,2.2vmin,23px); line-height: 1; display: grid; place-items: center;
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); transition: transform .12s, color .15s, background .15s, box-shadow .15s; }
.mm-fav:hover { transform: scale(1.12); }
/* The star sits INSIDE the stage, which is already scaled down on a phone — so
   a vmin-based size shrinks twice over. Size it in design px there instead. */
.stage.stage-phone .mm-fav {
  width: 92px; height: 92px; top: 14px; right: 14px;
  font-size: 58px; border-width: 3px;
}
.mm-fav.on { background: #2D01FC; color: #fff; border-color: #5B6BFF; box-shadow: 0 0 16px rgba(45,1,252,.85); }
.fr-search { display: flex; gap: 1vmin; width: min(100%, 620px); flex: none; }
.fr-input { flex: 1; background: rgba(0,20,40,.6); border: 2px solid rgba(0,240,255,.35); border-radius: 999px;
  color: #fff; font: 600 clamp(15px,2.6vmin,26px) 'Rajdhani'; padding: clamp(10px,2vmin,20px) clamp(18px,3vmin,32px); outline: none; min-width: 60px; }
.fr-input:focus { border-color: var(--cyan); box-shadow: 0 0 18px rgba(0,240,255,.3); }
.fr-go { padding: clamp(10px,2vmin,20px) clamp(22px,4vmin,44px); border-radius: 999px; font: 700 clamp(15px,2.6vmin,26px) 'Rajdhani'; cursor: pointer; }
.fr-result { width: min(100%, 620px); flex: none; }
.fr-list-head { width: min(100%, 620px); color: var(--blue-soft); font-weight: 700; text-transform: uppercase;
  letter-spacing: 1px; font-size: clamp(11px,1.9vmin,18px); flex: none; margin-top: .6vmin; }
.fr-list { width: min(100%, 620px); flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: .8vmin;
  padding-bottom: max(1.4vmin, env(safe-area-inset-bottom)); }
.fr-card { display: flex; align-items: center; gap: 1.2vmin; padding: clamp(8px,1.5vmin,16px) clamp(12px,2vmin,20px);
  border-radius: 16px; background: rgba(10,30,60,.5); border: 1px solid rgba(120,220,255,.22); }
.fr-card .fr-av { width: clamp(42px,7vmin,72px); height: clamp(42px,7vmin,72px); }
.fr-meta { flex: 1; min-width: 0; }
.fr-name { color: #fff; font-weight: 700; font-size: clamp(15px,2.6vmin,26px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fr-handle { color: var(--cyan); font-weight: 600; font-size: .78em; opacity: .85; margin-left: .5em; }
.fr-id { color: var(--blue-soft); font-size: clamp(12px,1.9vmin,18px); }
.fr-act { padding: clamp(7px,1.4vmin,14px) clamp(16px,2.6vmin,30px); border-radius: 999px;
  font: 700 clamp(13px,2vmin,20px) 'Rajdhani'; cursor: pointer; flex: none; }
.fr-act:disabled { opacity: .6; cursor: default; }
.fr-empty { color: var(--blue-soft); text-align: center; padding: 3vmin 1vmin; font-size: clamp(14px,2.4vmin,24px); }
.fr-empty b { color: var(--cyan); }

/* ============ Reviews / feedback survey ============ */
.review-panel {
  width: min(92vw, 640px); max-height: 88vh; display: flex; flex-direction: column;
  border-radius: 20px; padding: clamp(14px, 2.4vmin, 26px); gap: 10px;
  border: 1px solid rgba(0, 240, 255, .4); box-shadow: 0 0 60px rgba(0, 240, 255, .25);
}
.review-head { display: flex; align-items: center; justify-content: space-between; }
.review-head .mono { font-size: clamp(18px, 3vmin, 28px); }
.review-x {
  width: 38px; height: 38px; border-radius: 10px; font-size: 18px; font-weight: 800; cursor: pointer;
  color: #ff9a9a; background: rgba(255, 90, 90, .18); border: 1px solid rgba(255, 120, 120, .5);
}
.review-body { overflow-y: auto; display: flex; flex-direction: column; gap: 16px; padding: 6px 2px; }
.review-q-label { font-size: clamp(15px, 2.2vmin, 19px); color: #fff; margin-bottom: 8px; font-weight: 700; }
.review-opts { display: flex; flex-wrap: wrap; gap: 8px; }
.review-opt {
  padding: 8px 14px; border-radius: 999px; cursor: pointer; font-size: clamp(13px, 1.9vmin, 16px);
  color: var(--ink, #dbe7ff); background: rgba(4, 10, 30, .5); border: 1px solid rgba(0, 240, 255, .3);
  transition: filter .15s, transform .08s;
}
.review-opt:hover { filter: brightness(1.2); }
.review-opt.sel { background: rgba(0, 240, 255, .18); border-color: var(--cyan); color: var(--cyan); }
.review-text {
  width: 100%; border-radius: 12px; padding: 10px 13px; font: inherit; resize: vertical;
  color: #dbe7ff; background: rgba(4, 10, 30, .6); border: 1px solid rgba(0, 240, 255, .3); outline: none;
}
.review-text:focus { border-color: var(--cyan); }
.review-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 4px; }
.review-msg { color: #ff9a9a; font-size: 14px; }
.review-submit {
  padding: 11px 22px; border-radius: 12px; cursor: pointer; font-weight: 700; font-size: clamp(15px, 2.2vmin, 18px);
  color: var(--green); background: rgba(0, 255, 26, .14); border: 1px solid var(--green);
}
.review-submit:hover { filter: brightness(1.2); }

/* ============================================================
   CONSISTENT BUTTON FEEL — one hover + press across every in-game screen
   (shop, room finder, lobby settings, VoidPass, quests, profile, reviews).
   These classes had drifted to slightly different brightness/scale values; this
   overriding block (last in source order) normalises them. Does NOT touch the
   admin panel / dev tools / intro / legal (different files or class sets), nor
   the centre-anchored buttons that carry a layout transform (.start-btn, .lb-*).
   ============================================================ */
.pill-btn, .pack-btn, .pack-redeem, .q-claim, .gfx-opt, .pass-tab, .pass-pro,
.select-btn, .size-btn, .fr-act, .fr-go, .fr-star, .review-opt, .review-submit {
  transition: transform .15s var(--spring-back), filter .16s ease, box-shadow .25s ease, border-color .2s ease, background-color .2s ease;
}
.pill-btn:not(:disabled):hover, .pack-btn:hover, .pack-redeem:not(:disabled):hover,
.q-claim:not(:disabled):hover, .gfx-opt:hover, .pass-tab:hover, .pass-pro:not(.active):hover,
.select-btn:hover, .size-btn:hover, .fr-act:not(:disabled):hover, .fr-go:hover,
.fr-star:hover, .review-opt:hover, .review-submit:hover {
  filter: brightness(1.2); transform: translateY(-1px) scale(1.03);
}
.pill-btn:not(:disabled):active, .pack-btn:active, .pack-redeem:not(:disabled):active,
.q-claim:not(:disabled):active, .gfx-opt:active, .pass-tab:active, .pass-pro:not(.active):active,
.select-btn:active, .size-btn:active, .fr-act:not(:disabled):active, .fr-go:active,
.fr-star:active, .review-opt:active, .review-submit:active {
  transform: scale(.96);
}

/* ============ Ban screen ============ */
.ban-overlay, .maint-overlay { z-index: 300; background: rgba(3, 3, 12, .93); }
.ban-card, .maint-card {
  width: min(92vw, 520px); text-align: center; border-radius: 22px; padding: clamp(22px, 4vmin, 44px);
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  background: linear-gradient(180deg, #0c1330, #080b1e); backdrop-filter: none; -webkit-backdrop-filter: none;
}
.ban-card { border: 1px solid rgba(255, 48, 48, .5); box-shadow: 0 0 70px rgba(255, 48, 48, .3); }
.ban-emoji { font-size: clamp(48px, 9vmin, 84px); }
.ban-title { font-size: clamp(24px, 4.4vmin, 40px); }
.ban-reason { font-size: clamp(16px, 2.6vmin, 22px); color: #fff; font-weight: 700; }
.ban-until { font-size: clamp(14px, 2.2vmin, 18px); color: var(--blue-soft); }
.ban-contact { font-size: clamp(12px, 1.8vmin, 15px); color: var(--blue-soft); margin-top: 6px; }

/* ============ Maintenance gate ============ */
.maint-card { border: 1px solid rgba(0, 240, 255, .45); box-shadow: 0 0 70px rgba(0, 240, 255, .28); }
.maint-emoji { font-size: clamp(46px, 8vmin, 76px); }
.maint-img {
  width: clamp(130px, 24vmin, 200px); height: auto; display: block; margin-bottom: 2px;
  filter: drop-shadow(0 8px 24px rgba(0, 240, 255, .35));
}
.maint-eta { display: flex; align-items: center; gap: 10px; margin-top: 4px;
  font-size: clamp(14px, 2.3vmin, 19px); font-weight: 700; color: var(--cyan); }
.maint-clock { width: clamp(24px, 4vmin, 34px); height: clamp(24px, 4vmin, 34px); flex: none; }
.maint-clock .mc-face { fill: none; stroke: var(--cyan); stroke-width: 2.5; }
.maint-clock .mc-pin { fill: var(--cyan); }
.maint-clock .mc-hand { stroke: var(--cyan); stroke-width: 2; stroke-linecap: round; }
.maint-title { font-size: clamp(22px, 4vmin, 36px); }
.maint-msg { font-size: clamp(15px, 2.4vmin, 20px); color: #cfe0ff; }
.maint-owner { margin-top: 10px; padding: 10px 22px; border-radius: 12px; cursor: pointer; font-weight: 700; }

/* ---- Pro profile background (banner) -------------------------------------
   Lives on .pf-head so no extra child is added to .profile-card — the mobile
   grid rules below target that card's children by :nth-of-type, and an extra
   div would reshuffle them. */
.pf-head { position: relative; border-radius: 16px; }
.pf-head.has-banner {
  background-size: cover; background-position: center;
  padding: clamp(18px,3.4vmin,40px) clamp(12px,2vmin,20px) clamp(12px,2vmin,18px);
  min-height: clamp(96px,17vmin,190px);
  align-items: flex-end;                 /* avatar sits low on the banner, Discord-style */
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.09);
}
.pf-head.has-banner .pf-name,
.pf-head.has-banner .pf-handle,
.pf-head.has-banner .pf-lvl { text-shadow: 0 2px 8px rgba(0,0,0,.85); }
.pf-bg-edit {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  padding: 5px 12px; border-radius: 999px; cursor: pointer;
  font-size: clamp(11px,1.5vmin,15px); line-height: 1.2;
  color: #dff3ff; background: rgba(8,14,40,.72);
  border: 1px solid rgba(255,255,255,.22);
  backdrop-filter: blur(4px);
}
.pf-bg-edit:hover { filter: brightness(1.2); transform: translateY(-1px) scale(1.03); }
.pf-bg-edit:active { transform: scale(.96); }
.pf-bg-edit.pending  { color: #FFB020; border-color: rgba(255,176,32,.5); }
.pf-bg-edit.declined { color: var(--red); border-color: rgba(255,60,60,.5); }

/* ---- host room moderation (kick / ban) ---- */
.user-row .room-mod { margin-left: auto; display: flex; gap: 6px; flex: none; }
.room-mod-btn {
  padding: 4px 12px; border-radius: 999px; cursor: pointer;
  font-size: clamp(11px,1.4vmin,14px); line-height: 1.2;
  background: rgba(8,14,40,.6); border: 1px solid rgba(255,255,255,.2); color: #dff3ff;
}
.room-mod-btn.kick:hover { color: #FFB020; border-color: rgba(255,176,32,.55); filter: brightness(1.2); transform: translateY(-1px) scale(1.03); }
.room-mod-btn.ban:hover  { color: var(--red); border-color: rgba(255,60,60,.55); filter: brightness(1.2); transform: translateY(-1px) scale(1.03); }
.room-mod-btn:active { transform: scale(.96); }
body.mobile.landscape .room-mod-btn { padding: 3px 9px; font-size: 11px; }
/* Blitz draws its lobby on the design-space stage, so its rows need larger
   moderation buttons to match that canvas's scale. */
.stage .lb-user .room-mod { margin-left: auto; gap: 10px; }
.stage .lb-user .room-mod-btn { padding: 8px 20px; font-size: 30px; border-radius: 999px; }

/* ---- item 4: larger profile widgets -------------------------------------
   Appended last so these win over the base rules above without editing them.
   Desktop gets a generous bump; mobile landscape is raised more carefully —
   that window is ~266px tall and an earlier fix had to rescue widgets that
   fell off the bottom of it. */
.wdg-board { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.3vmin; }   /* 260 keeps TWO columns in the 620px pane; 300 collapsed it to one and doubled the board height */
.wdg { gap: 1.6vmin; padding: clamp(14px,2.5vmin,26px); border-radius: 18px; }
.wdg-ico { width: clamp(48px,7.6vmin,72px); height: clamp(48px,7.6vmin,72px); border-radius: 16px; }
.wdg-del { width: clamp(30px,4.6vmin,40px); height: clamp(30px,4.6vmin,40px); font-size: clamp(18px,3vmin,28px); }
.wdg-title { font-size: clamp(12px,1.95vmin,17px); }
.wdg-val { font-size: clamp(18px,3.1vmin,32px); margin-top: 5px; }
.wdg-empty { font-size: clamp(15px,2.4vmin,23px); margin-top: 6px; }
.wdg-grip { grid-template-columns: repeat(2, 7px); grid-template-rows: repeat(3, 7px); gap: 5px; }
.wdg-grip span { width: 7px; height: 7px; }
.wdg-bff { gap: 1.7vmin; padding: clamp(9px,1.6vmin,15px); }
.wdg-bff .wdg-bffav { width: clamp(58px,9.6vmin,90px); height: clamp(58px,9.6vmin,90px); }
.wdg-bffname { font-size: clamp(19px,3.3vmin,33px); }
.wdg-bffhandle { font-size: clamp(14px,2.3vmin,21px); }
.wdg-bfftag { font-size: clamp(13px,2.1vmin,18px); }
.wdg-cardimg { width: clamp(54px,8.4vmin,80px); }
.wdg-gifgrid { gap: .8vmin; margin-top: 7px; }
.wdg-add { padding: clamp(14px,2.4vmin,24px); border-radius: 18px; }
.wdg-add-plus { font-size: clamp(26px,4vmin,38px); }
.wdg-add-txt { font-size: clamp(16px,2.7vmin,26px); }

/* Mobile landscape: still two columns, but a modest bump only — the board
   shares a ~266px-tall window with the stats column. */
body.mobile.landscape .wdg-board { gap: 7px; }
body.mobile.landscape .wdg { padding: 9px 11px; gap: 9px; border-radius: 14px; }
body.mobile.landscape .wdg-ico { width: 40px; height: 40px; border-radius: 11px; }
body.mobile.landscape .wdg-del { width: 26px; height: 26px; font-size: 17px; }
body.mobile.landscape .wdg-title { font-size: 12px; }
body.mobile.landscape .wdg-val { font-size: 18px; margin-top: 3px; }
body.mobile.landscape .wdg-empty { font-size: 14px; margin-top: 3px; }
body.mobile.landscape .wdg-bff .wdg-bffav { width: 44px; height: 44px; }
body.mobile.landscape .wdg-bffname { font-size: 18px; }
body.mobile.landscape .wdg-add { padding: 9px; }
body.mobile.landscape .wdg-add-txt { font-size: 15px; }

/* ---- item 6: match logs + friend requests ---- */
.log-row { border-radius: 14px; padding: clamp(9px,1.6vmin,16px);
  background: rgba(10,30,60,.5); border: 1px solid rgba(0,240,255,.2); display: flex; flex-direction: column; gap: 6px; }
.log-head { display: flex; align-items: center; gap: 1vmin; }
.log-place { font-size: clamp(16px,2.6vmin,24px); flex: none; }
.log-game { color: #fff; font-weight: 700; font-size: clamp(14px,2.2vmin,21px); flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.log-when { color: var(--blue-soft); font-size: clamp(11px,1.7vmin,15px); flex: none; }
.log-tro { color: var(--gold); font-weight: 700; font-size: clamp(12px,1.9vmin,17px); flex: none; }
.log-tro.neg { color: #ff9a9a; }
.log-opps { display: flex; flex-wrap: wrap; gap: 6px; }
.log-opp { display: flex; align-items: center; gap: 6px; padding: 3px 8px 3px 10px; border-radius: 999px;
  background: rgba(0,240,255,.08); border: 1px solid rgba(0,240,255,.22); }
.log-opp-name { color: #cfefff; font-size: clamp(12px,1.9vmin,17px); }
.log-opp-tag { color: var(--green); font-size: clamp(10px,1.6vmin,14px); font-weight: 700; }
.log-opp-add { padding: 2px 10px; border-radius: 999px; font-size: clamp(11px,1.7vmin,15px); cursor: pointer; }
.log-solo { color: var(--blue-soft); font-size: clamp(12px,1.9vmin,16px); }
.fr-reqhead { color: var(--gold) !important; }
.lb-add { flex: none; margin-left: 8px; padding: 2px 12px; border-radius: 999px; cursor: pointer;
  font-size: clamp(13px,2vmin,19px); font-weight: 800; line-height: 1.3; }
body.mobile.landscape .log-row { padding: 7px 9px; gap: 4px; }
body.mobile.landscape .log-opp { padding: 2px 6px 2px 8px; }

/* ---- item 7: referrals tab ---- */
.ref-card { border-radius: 14px; padding: clamp(11px,1.9vmin,20px); background: rgba(10,30,60,.5);
  border: 1px solid rgba(0,240,255,.25); display: flex; flex-direction: column; gap: 8px; }
.ref-title { color: #fff; font-weight: 700; font-size: clamp(15px,2.4vmin,23px); }
.ref-sub { color: var(--blue-soft); font-size: clamp(12px,1.9vmin,17px); }
.ref-linkrow { display: flex; gap: 8px; align-items: center; }
.ref-link { flex: 1; min-width: 0; background: rgba(0,20,40,.6); border: 2px solid rgba(0,240,255,.35);
  border-radius: 999px; padding: 7px 14px; color: var(--cyan); font-size: clamp(12px,1.8vmin,16px); }
.ref-tag { flex: none; padding: 3px 12px; border-radius: 999px; font-weight: 700;
  font-size: clamp(11px,1.7vmin,15px); color: var(--blue-soft);
  background: rgba(0,240,255,.08); border: 1px solid rgba(0,240,255,.25); }
.ref-tag.paid { color: var(--gold); border-color: rgba(255,215,0,.45); background: rgba(255,215,0,.12); }
body.mobile.landscape .ref-card { padding: 9px 11px; gap: 6px; }

/* ---- Gift Pro (item 8) ---- */
.gift-card { gap: 10px; }
.gift-head { display: flex; align-items: center; gap: 1.2vmin; }
.gift-ico { width: clamp(28px,4.4vmin,52px); flex: none; display: block; }
.gift-ico svg { width: 100%; height: auto; display: block; filter: drop-shadow(0 0 8px rgba(255,176,32,.5)); }
.gift-months { display: flex; flex-wrap: wrap; gap: 6px; }
.gift-m {
  min-width: clamp(30px,4.6vmin,46px); padding: 5px 0; border-radius: 10px; cursor: pointer;
  font-weight: 700; font-size: clamp(12px,1.9vmin,18px);
  background: rgba(10,30,60,.5); border: 1px solid rgba(0,240,255,.28); color: #cfefff;
  transition: filter .15s, transform .1s, border-color .15s;
}
.gift-m:hover { filter: brightness(1.2); transform: translateY(-1px); }
.gift-m.on { border-color: var(--gold); color: var(--gold); box-shadow: 0 0 14px rgba(255,215,0,.35); }
.gift-price { font-size: clamp(16px,2.6vmin,26px); }
.gift-was { color: var(--blue-soft); opacity: .8; margin-right: 6px; }
.gift-off { color: var(--green); font-weight: 700; font-size: clamp(11px,1.7vmin,15px); }
.gift-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; cursor: pointer;
  padding: 7px 11px; border-radius: 10px; margin-top: 5px;
  background: rgba(10,30,60,.45); border: 1px solid rgba(0,240,255,.2);
}
.gift-row:hover { filter: brightness(1.15); }
.gift-row-l { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.gift-code { color: var(--cyan); font-size: clamp(11px,1.7vmin,15px); letter-spacing: 1px; }
body.mobile.landscape .gift-m { min-width: 26px; font-size: 12px; padding: 3px 0; }
body.mobile.landscape .gift-price { font-size: 15px; }

/* Gif picker on a phone: bigger cells and controls — the old 70px tiles with
   24px buttons were unusable with a thumb. */
body.mobile.landscape .gifpick-grid { max-height: 46vh; gap: 7px; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
body.mobile.landscape .gifpick-check,
body.mobile.landscape .gifpick-del { width: 30px; height: 30px; font-size: 15px; }
.gifpick { transition: transform .12s ease, border-color .15s ease; }
.gifpick:active { transform: scale(.95); }
.gifpick.sel img { filter: brightness(1.06); }
/* Save stays reachable: it lives inside the scrolling .review-body, so on a
   short landscape phone it fell below the panel edge and looked cut off. */
.gifpick-actions {
  position: sticky; bottom: 0; z-index: 2;
  display: flex; align-items: center; gap: 1vmin; flex-wrap: wrap;
  margin-top: 8px; padding: 8px 2px 2px;
  background: linear-gradient(to top, rgba(8,16,40,.98) 62%, rgba(8,16,40,0));
}
.gifpick-hint { color: var(--blue-soft); font-size: clamp(11px,1.7vmin,15px); }
body.mobile.landscape .gifpick-hint { font-size: 11px; }

/* Widget bodies open a picker on click, but <img> is natively DRAGGABLE: on a
   desktop a mousedown on a thumbnail started an HTML5 drag and swallowed the
   click, so the gifs widget simply would not open once it had gifs in it.
   The art is decorative, so let every pointer event fall through to the body. */
.wdg-body img, .wdg-body svg { -webkit-user-drag: none; user-select: none; }
.wdg-body.clickable img,
.wdg-body.clickable svg,
.wdg-body.clickable .wdg-gifgrid,
.wdg-body.clickable .wdg-cardrow { pointer-events: none; }

/* The Report control now lives on the banner (.pf-head) rather than the old
   .pp-photo-wrap, so it needs the same corner placement the edit button uses.
   Left side, so it never collides with anything on the right. */
.pf-head .pp-report { position: absolute; top: 8px; left: 8px; right: auto; z-index: 2; }

/* ============================================================
   BLITZ — skip / reverse feedback  (QoL)
   ============================================================
   Both are pure transform+opacity animations on their own layer, so neither
   invalidates the backdrop-filtered panels underneath. That matters here more
   than usual: these fire mid-game on the four screens that KEEP backdrop
   filtering on the medium tier, and an animation that dirties the region under
   a filtered panel re-runs that filter every frame. Same reasoning as the
   lobby title's `will-change: transform`. */

/* ---- "1 card left" off-turn ---- */
.stage .bz-call.off {
  opacity: .42;
  filter: saturate(.35);
  cursor: not-allowed;
}
.stage .bz-call.off:hover { filter: saturate(.35); }

/* ---- the skip mark on an avatar ---- */
.stage .bz-skipfx {
  /* z-index above the avatar disc (which sits in the normal stage flow) so the
     mark reads as being ON the player rather than behind them. */
  color: var(--red, #ff4d5e);
  pointer-events: none;
  z-index: 60;
  transform: translateX(-50%);
  filter: drop-shadow(0 0 18px rgba(255, 60, 90, .75)) drop-shadow(0 3px 8px rgba(0,0,0,.7));
  animation: bz-skipfx 3s cubic-bezier(.2, 1, .3, 1) both;
}
.stage .bz-skipfx svg { width: 100%; height: 100%; display: block; }
@keyframes bz-skipfx {
  /* Lands hard, holds while you look at it, then leaves. The long flat middle
     is deliberate — the burst exists to be READ, and a symmetrical in/out
     gives you about 300ms to do it. */
    0% { opacity: 0; transform: translateX(-50%) scale(.35) rotate(-22deg); }
   10% { opacity: 1; transform: translateX(-50%) scale(1.14) rotate(4deg); }
   16% { transform: translateX(-50%) scale(1) rotate(0deg); }
   88% { opacity: 1; transform: translateX(-50%) scale(1) rotate(0deg); }
  100% { opacity: 0; transform: translateX(-50%) scale(.92) rotate(0deg); }
}

@media (prefers-reduced-motion: reduce) {
  .stage .bz-skipfx { animation-duration: .01ms; animation-iteration-count: 1; opacity: 1; }
}

/* ============================================================
   ADVANCED GRAPHICS — independent switches
   ============================================================
   The three tiers are PRESETS over these switches, not a separate mechanism.
   Picking Low/Medium/High sets them all at once; flipping any one on its own
   leaves the preset behind and the tier reads "Custom".

   Refraction is the odd one out: it is opt-IN via html.fx-refract, because it
   is the only effect here that costs a per-pixel SVG filter pass over the
   backdrop and it must never be on by accident. The other three are opt-OUT —
   they are part of the tier's normal look, and these classes remove them.

   Every rule below is an override layered on top of whatever data-gfx already
   said, so a Custom set can start from any base tier without the tier CSS
   needing to know that Custom exists. */

/* ---- backdrop filtering off ---- */
/* ---- "Frosted panels: off" ----
 *
 * This governs BLUR, which is what the switch says it does: "Blurs whatever is
 * behind a panel."
 *
 * .glass and .stage .d-panel are deliberately NOT in this list. They never
 * blur — their backdrop-filter is saturate/contrast/brightness, which LIFTS
 * what shows through rather than obscuring it. Including them meant turning
 * the switch off stripped the saturation from every panel in the game, and the
 * lobby measured 18-27 points less blue than it should. The switch has no
 * business touching a surface that was never frosted. */
/* Only genuinely BLURRED surfaces belong here.
 *
 * Everything that used to be in this list — .reward-card, .quest, .crate-item,
 * .pass-cell, .podium-card, .car-opt, .variant-row, .lb-rule, .lb-user,
 * .go-podium, .rule-card, and .glass/.d-panel before them — runs a saturation
 * LIFT, not a blur: `saturate(1.6) brightness(1.04)` at medium, the same recipe
 * at high with refraction in front. A lift does not obscure what is behind it,
 * so switching it off buys nothing and costs the colour: measured, the lobby
 * came out 18-27 points less blue than it should.
 *
 * That is why only the main menu looked right after the first pass — it is
 * built from .glass and .d-panel, which I had already exempted, while every
 * other screen is built from the names above and was still being stripped.
 *
 * The Low graphics tier still drops backdrop work wholesale further up. That is
 * a deliberate tier choice and is left alone; this switch is not it. */
html.no-fx-backdrop .lg,
html.no-fx-backdrop .lg-strong {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* NO denser tint here, deliberately.
 *
 * That was added on the reasoning that text needs more backing once the blur
 * is gone — but .glass and .d-panel never used BLUR. Their backdrop-filter is
 * saturate/contrast/brightness, which lifts what shows through rather than
 * obscuring it, so dropping it costs no legibility at all.
 *
 * What the dense tint DID cost was the colour: rgba(16,46,92,.80) over the
 * nebula measured 18-27 points less blue than the original's translucent
 * rgba(30,150,210,.20), which is why turning Frosted panels off made the whole
 * lobby read darker and greyer than it should.
 *
 * The Low graphics tier still applies its own denser tint further up — that is
 * a deliberate tier choice and is left alone. */

/* ---- decorative motion off ----
   Scoped to ambient/idle animation, NOT to gameplay feedback. The skip mark and
   the reverse sweep survive: they carry information about the game state, and
   silently dropping them would make the table lie about whose turn it is. */
html.no-fx-motion .stage .ms-title { animation: none !important; }
html.no-fx-motion .rainbow-bg,
html.no-fx-motion .rainbow-text { animation: none !important; }
html.no-fx-motion .stage .bz-disc.turn,
html.no-fx-motion .stage .bz-avatar.turn { animation: none !important; }
html.no-fx-motion .shop-mascot img { transition: none !important; }

/* ---- cursor light off ----
   The rim falls back to its static default; applyGfx() also strips the inline
   --edge-* so no stuck angle is left behind. */
html.no-fx-cursorlight .glass { box-shadow: var(--glass-edge), var(--bevel), var(--e2) !important; }

/* ---- advanced graphics panel ---- */
/* display:contents keeps these as siblings in the settings flow (so they line
   up with every other row) while still being one collapsible unit. */
.gfx-adv { display: contents; }
/* Indented and rule-marked, so an advanced switch is visibly INSIDE the
   Advanced group rather than another top-level setting. */
.gfx-adv > .settings-row { padding-left: clamp(10px, 2.6vmin, 34px); }
.gfx-adv.hidden { display: none !important; }
/* The Custom chip reads as a status rather than a button: no pointer, and it
   only exists while a custom set is actually in force. */
.gfx-opt.gfx-custom { cursor: default; opacity: .95; }
.gfx-opt.gfx-custom.hidden { display: none !important; }

/* ============================================================
   2D MODE — flat, matched to the reference screens
   ============================================================
   An advanced option, not a performance tier: 3D is on by default on every
   graphics preset, because none of this costs anything to composite. It is a
   look, and some people want the flat one.

   ---- WHAT "FLAT" MEANS HERE, AND WHAT IT DOES NOT ----

   It means no BEVEL, no sheen, and no contact shadows — the cues that say one
   surface is sitting on top of another.

   It does NOT mean stripping the edge rim. I tried that, and measured the
   result against the reference: every panel came out 7-31 points brighter in
   blue, because the rim's inset shading is what gives a translucent panel its
   body. Without it the nebula reads straight through and the whole lobby goes
   washed out. The rim is not a stacking cue, it is the glass itself.

   Everything above routes through tokens, so this is mostly a matter of
   redefining them — no rule has to know this mode exists. */
html.flat-2d {
  --sheen: none;
  --glass: rgba(20, 150, 210, 0.14);
  --glass-strong: rgba(20, 160, 220, 0.22);
  /* --glass-edge is deliberately NOT overridden.
   *
   * It is inherited from :root exactly as the original 2D had it. I tried both
   * alternatives and an element-by-element computed-style diff against the
   * original file proved each one wrong: deleting the rim left panels 7-31
   * points brighter in blue, and replacing it with a non-directional version
   * was the ONLY property that still differed from the original across every
   * element tested. The rim is part of the original look. */
  /* Transparent rather than removed, so every `box-shadow: var(--glass-edge),
     var(--bevel), var(--e2)` still parses — a missing variable voids the whole
     declaration, which flattened the entire game once already. */
  --bevel: 0 0 0 rgba(0, 0, 0, 0);
  --bevel-sm: 0 0 0 rgba(0, 0, 0, 0);
  /* Single soft ambient shadows, not the contact+ambient pairs. */
  --e1: 0 4px 12px rgba(0, 0, 0, .24);
  --e2: 0 12px 34px rgba(0, 0, 0, .30);
  --e3: 0 8px 22px rgba(0, 0, 0, .26);
  --e4: 0 14px 38px rgba(0, 0, 0, .30);
}
/* A card is DIMMER than its panel here, which is the opposite of the 3D rule.
   Measured against the reference, the lighter fill 3D uses came out 25 points
   too bright in blue. */
html.flat-2d .rule-card {
  background: var(--glass);
  border-color: rgba(0, 240, 255, .2);
}
html.flat-2d .stage .rule-card,
html.flat-2d .stage .stepper-box {
  box-shadow: var(--glass-edge), 0 8px 22px rgba(0, 0, 0, .26);
}
/* No travel — a flat surface has nowhere to travel to. */
html.flat-2d button.glass:active { transform: scale(.96); box-shadow: var(--glass-edge), var(--e2); }
html.flat-2d .stage .d-btn:active { transform: translateY(0) scale(.96); box-shadow: var(--glass-edge), var(--e4); }

/* ============================================================
   2D — THE MENUS ARE FINISHED LIKE THE MAIN SCREEN
   On Medium the main screen's panels drop the backdrop lift (it is the lobby's
   single biggest per-frame cost) and carry a static diagonal sheen instead.
   Nothing else in the menus ever got that treatment, so the shop, pass, crates,
   leaderboard, mode-select and game-over cards kept `saturate(1.6)` and — with
   `--sheen` off in 2D — no sheen at all. Side by side that reads as the main
   screen being glassy and blue while every screen behind it is duller and
   washed out, which is exactly the complaint.
   The recipe below is the main screen's, applied through `background-image` so
   each surface keeps its OWN background-color: rarity, podium and quest-state
   colours still mean what they meant. The four board screens are excluded —
   the game material is tuned separately, and `:not(.game-screen)` is how the
   original rule already drew that line.
   3D and the High/Low tiers are untouched: Low already has no lift anywhere,
   High lifts everything, and 3D is the mode the owner signed off on.
   ============================================================ */
html.flat-2d[data-gfx="medium"] .screen:not(.game-screen) :is(
  .pill-btn, .pack-btn, .pack-redeem, .gfx-opt, .q-claim, .pass-tab, .pass-pro,
  .toggle, .checkbox, .arrow-btn, .size-btn, .select-btn,
  .reward-card, .quest, .crate-item, .pass-cell, .car-opt, .skin-opt,
  .podium-card, .variant-row, .search-input, .pass-input,
  .lb-toggle, .lb-unlim-t, .lb-arrow, .lb-rule, .lb-user, .lb-privacy,
  .lb-passinput, .lb-start, .go-tip, .go-podium, .rule-card, .stepper-box
) {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  background-image: linear-gradient(152deg,
    rgba(190, 240, 255, .18), rgba(190, 240, 255, .05) 40%, rgba(8, 16, 44, 0) 72%);
}

/* Boombox Evil's diary (April Fools update): the red book opens. */
.lore-diary { position: absolute; inset: clamp(46px,7vmin,70px) clamp(14px,3vmin,40px) clamp(14px,3vmin,40px); display: flex; flex-direction: column; gap: 10px; }
.lore-diary.hidden { display: none; }
.lore-back { align-self: flex-start; background: rgba(255,60,60,.16); border: 1px solid rgba(255,90,90,.45); color: #fff; border-radius: 10px; padding: 6px 12px; cursor: pointer; }
.lore-page { flex: 1; overflow: auto; background: linear-gradient(160deg, rgba(60,8,8,.82), rgba(20,4,4,.9)); border: 1px solid rgba(255,90,90,.35);
  border-radius: 10px; padding: clamp(14px,3vmin,32px); color: #f3dcdc; font-size: clamp(13px,2vmin,18px); line-height: 1.55; }
.lore-page h3 { margin: 0 0 8px; color: #ff6b6b; }
.lore-page h4 { margin: 16px 0 4px; color: #ffb3b3; letter-spacing: 1px; text-transform: uppercase; font-size: .85em; }
.lore-page p { margin: 0 0 8px; }
.lore-link { margin-top: 18px !important; font-weight: 700; }
.lore-link a { color: #ff8a8a; }
.lore-glass.reading .lore-books, .lore-glass.reading .lore-title, .lore-glass.reading #lore-foot { visibility: hidden; }
.lore-tbc { margin-top: 14px !important; opacity: .7; font-style: italic; }
.lore-notebook { flex: 1; width: 100%; border: 0; border-radius: 12px; background: #16110d; }
/* iPhone Safari has no page fullscreen: the diary frame fills the screen instead. */
.lore-diary.pseudo-fs { position: fixed; inset: 0; z-index: 9999; background: #16110d; padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); gap: 0; }
.lore-diary.pseudo-fs .lore-notebook { border-radius: 0; }
.lore-diary.pseudo-fs .lore-back { position: absolute; left: max(10px, env(safe-area-inset-left)); bottom: max(10px, env(safe-area-inset-bottom)); top: auto; z-index: 2; padding: 6px 10px; font-size: 13px; }
