/* $DDD 6ix Arena — GTA-era SA vibe · Toronto · .io */
@import url("/games/shared/economy/shop.css?v=20260806-mobile1");

:root {
  --bg: #0b0a08;
  --panel: #14110c;
  --ink: #f5ecd8;
  --muted: #a89878;
  --gold: #ffd24a;
  --gold-dim: #c9a227;
  --mint: #9dccff;
  --pink: #ff7eb6;
  --violet: #a87eff;
  --sa-green: #a8d08d;
  --line: rgba(255, 210, 74, 0.18);
  --sans: "Space Grotesk", system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, monospace;
  --serif: "Instrument Serif", Georgia, serif;

  /* ── hvskin1: the drooly.ai design system, verbatim from home.css ───────
     The arena used to be a different-looking product from the homepage that
     links to it. These are the SAME token names and the SAME values as the
     `body.home-v2` block in /home.css — copied, not re-derived, so a diff
     between the two files is a real drift signal (tests/ddd-css-contract
     asserts value-for-value equality).

     Scope note: these skin the SHELL — header, panels, lobby overlay, footer.
     Gameplay signal keeps its own palette: --gold (★ tickets / Golden
     Ticket), --mint, --pink, --sa-green and the faction colours on
     .mode-btn--horde / --bureau / --wokenet all still mean something, and
     repainting them would delete information. arena.js is untouched. */
  --hv-espresso: #17120e;
  --hv-espresso-2: #1f1811;
  --hv-espresso-3: #2a2118;
  --hv-paper: #f4ead8;
  --hv-paper-2: #e9dbc2;
  --hv-ink: #f8f0e2;
  --hv-ink-dim: #d9cbb4;
  --hv-ink-mute: #a99b85;
  --hv-ink-dark: #1a1410;
  --hv-ink-dark-dim: #4a3d30;
  --hv-s1: #e8552f;
  --hv-s2: #f2892c;
  --hv-s3: #f5c242;
  --hv-s4: #4fa96b;
  --hv-s5: #2e8fb5;
  --hv-s6: #7b4fa3;
  --hv-mint: #7fe8d0;
  --hv-band: linear-gradient(90deg,
    var(--hv-s1) 0 16.666%, var(--hv-s2) 16.666% 33.333%,
    var(--hv-s3) 33.333% 50%, var(--hv-s4) 50% 66.666%,
    var(--hv-s5) 66.666% 83.333%, var(--hv-s6) 83.333% 100%);
  --hv-round: 18px;
  --hv-round-sm: 12px;
  --hv-round-pill: 999px;
}

*, *::before, *::after { box-sizing: border-box; }
html { color-scheme: dark; scroll-behavior: smooth; }
body.ddd-io {
  margin: 0;
  min-height: 100dvh;
  font-family: var(--sans);
  color: var(--ink);
  /* SA-era Maple dusk page wash: orange haze + teal floor */
  background:
    radial-gradient(ellipse 55% 42% at 78% 8%, rgba(255, 150, 60, 0.22), transparent 55%),
    radial-gradient(ellipse 50% 38% at 12% 95%, rgba(40, 140, 140, 0.16), transparent 50%),
    radial-gradient(ellipse 40% 30% at 50% 40%, rgba(90, 40, 90, 0.1), transparent 60%),
    linear-gradient(180deg, #1a0e14 0%, #0b0a08 45%, #081018 100%);
  line-height: 1.5;
}
a { color: var(--gold); }
a:hover { color: var(--mint); }
.skip {
  position: absolute; left: -999px; top: 0;
}
.skip:focus { left: 8px; z-index: 99; background: var(--gold); color: #111; padding: 8px 12px; }

.top {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px;
  padding: 12px max(16px, env(safe-area-inset-right, 0px), 3vw) 12px max(16px, env(safe-area-inset-left, 0px), 3vw);
  padding-top: max(12px, env(safe-area-inset-top, 0px));
  border-bottom: 1px solid var(--line);
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(12px) saturate(1.2);
  -webkit-backdrop-filter: blur(12px) saturate(1.2);
  position: sticky; top: 0; z-index: 20;
}
.brand { display: grid; gap: 2px; }
.brand .mark {
  font-weight: 800; letter-spacing: 0.04em; color: var(--gold);
  font-size: 1.25rem; text-shadow: 0 0 20px rgba(255,210,74,0.35);
}
.brand .sub {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted);
}
.top nav {
  display: flex; flex-wrap: wrap; gap: 14px; margin-left: auto;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
}
.top nav a {
  /* bothaxes1 — min-height was set and min-width was not, so short labels came
     out under the floor on the other axis: measured on prod, "Hub" 21x44 and
     "Lore" 28x44. Third time this exact mirror has appeared in this project
     (thumbs1 had width without height; touchfloor1 had height without width),
     which is why the CSS contract test now asserts both axes for nav links.
     hub.css and platformer.css already carried the full pair. */
  color: var(--muted); text-decoration: none;
  min-height: 44px; min-width: 44px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
}
.top nav a:hover { color: var(--ink); }
.radio-dock {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
}
.radio-eq {
  display: inline-flex; align-items: flex-end; gap: 3px;
  height: 18px; width: 18px; margin-right: 2px;
  opacity: 0.35;
  transition: opacity 0.2s ease;
}
.radio-eq[data-on="true"] { opacity: 1; }
.radio-eq i {
  display: block; width: 3px; border-radius: 1px;
  background: var(--sa-green);
  height: 6px;
  transform-origin: bottom center;
}
.radio-eq[data-on="true"] i:nth-child(1) { animation: radio-eq 0.55s ease-in-out infinite alternate; }
.radio-eq[data-on="true"] i:nth-child(2) { animation: radio-eq 0.42s ease-in-out 0.08s infinite alternate; height: 12px; }
.radio-eq[data-on="true"] i:nth-child(3) { animation: radio-eq 0.5s ease-in-out 0.04s infinite alternate; height: 9px; }
.radio-eq[data-on="true"] i:nth-child(4) { animation: radio-eq 0.38s ease-in-out 0.12s infinite alternate; height: 14px; }
@keyframes radio-eq {
  from { transform: scaleY(0.35); opacity: 0.55; }
  to { transform: scaleY(1); opacity: 1; }
}
.music-btn {
  min-height: 44px; min-width: 44px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--line); background: transparent; color: var(--sa-green);
  font: 600 12px var(--mono); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.12s ease;
}
.music-btn:hover {
  border-color: rgba(168, 208, 141, 0.45);
  color: var(--ink);
}
.music-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--sa-green);
}
.music-btn:active { transform: scale(0.97); }
.music-btn.ghost { color: var(--muted); min-width: 44px; padding: 0 12px; }
.music-btn.radio-open {
  color: var(--mint);
  border-color: rgba(157, 204, 255, 0.28);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.music-btn.radio-open:hover { color: var(--ink); border-color: rgba(157, 204, 255, 0.55); }
.music-btn[aria-pressed="true"] {
  background: rgba(168, 208, 141, 0.18);
  border-color: var(--sa-green);
  color: var(--sa-green);
  box-shadow: 0 0 18px rgba(168, 208, 141, 0.12);
}
.radio-now {
  margin: 0;
  padding: 8px max(16px, env(safe-area-inset-right, 0px), 3vw) 8px max(16px, env(safe-area-inset-left, 0px), 3vw);
  font: 500 12px/1.4 var(--mono);
  letter-spacing: 0.04em;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  background: rgba(9, 20, 16, 0.72);
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  min-height: 44px; /* apple3 — studio 44px rail */
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.radio-now[data-on="true"] {
  color: var(--ink);
  border-bottom-color: rgba(168, 208, 141, 0.28);
  background:
    linear-gradient(90deg, rgba(168, 208, 141, 0.1), transparent 42%),
    rgba(9, 20, 16, 0.85);
}
.radio-now:empty { display: none; }
.radio-now__badge {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 4px;
  border: 1px solid rgba(168, 152, 120, 0.35);
  color: var(--muted);
  background: rgba(0, 0, 0, 0.25);
  /* appleradio1 — keep badge ≥ touch-adjacent read height */
  min-height: 28px;
  display: inline-flex;
  align-items: center;
}
.radio-now[data-on="true"] .radio-now__badge {
  color: #0b120e;
  background: var(--sa-green);
  border-color: transparent;
  box-shadow: 0 0 12px rgba(168, 208, 141, 0.35);
  animation: radio-now-live 2.4s ease-in-out infinite;
}
@keyframes radio-now-live {
  0%, 100% { box-shadow: 0 0 10px rgba(168, 208, 141, 0.28); }
  50% { box-shadow: 0 0 16px rgba(168, 208, 141, 0.55); }
}
.radio-now__line {
  flex: 1 1 12rem;
  min-width: 0;
  overflow: hidden;
  /* appleradio1 — soft edge fade like Music Now Playing */
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 calc(100% - 18px), transparent);
  mask-image: linear-gradient(90deg, #000 0%, #000 calc(100% - 18px), transparent);
}
.radio-now__scroll {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
  will-change: transform;
}
/* Long track titles scroll when on-air (set data-scroll via JS) */
.radio-now__line[data-scroll="true"] .radio-now__scroll {
  max-width: none;
  overflow: visible;
  text-overflow: clip;
  padding-right: 2em;
  animation: radio-now-marquee 16s linear infinite;
}
@keyframes radio-now-marquee {
  0%, 12% { transform: translateX(0); }
  88%, 100% { transform: translateX(calc(-1 * var(--scroll-dist, 0px))); }
}
.radio-promo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  margin: 14px 0 0;
  padding: 12px 14px;
  border: 1px solid rgba(207, 240, 236, 0.28);
  border-radius: 8px;
  background: linear-gradient(135deg, rgba(12, 28, 26, 0.85), rgba(207, 240, 236, 0.06));
  font-size: 0.92rem;
  color: var(--muted);
  line-height: 1.35;
}
.radio-promo strong { color: var(--sa-green); }
.radio-promo a { color: var(--mint); }
.radio-promo__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 0 14px;
  min-height: 44px;
  min-width: 44px;
  border-radius: 4px;
  border: 1px solid rgba(207, 240, 236, 0.45);
  background: rgba(207, 240, 236, 0.12);
  color: #cff0ec !important;
  font-family: var(--mono, "IBM Plex Mono", monospace);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-decoration: none !important;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: background 140ms ease, border-color 140ms ease;
}
.radio-promo__btn:hover {
  background: rgba(207, 240, 236, 0.22);
  border-color: rgba(207, 240, 236, 0.7);
}
.radio-promo__btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px #0b0a08, 0 0 0 4px #9dccff;
}
.radio-promo__btn.ghost {
  background: transparent;
  border-color: rgba(157, 204, 255, 0.4);
  color: #9dccff !important;
}
.radio-promo__meta {
  flex: 1 1 100%;
  color: var(--muted);
  font-size: 0.82rem;
}

.lore-card {
  margin: 0 0 18px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: linear-gradient(145deg, rgba(255, 210, 74, 0.08), rgba(0, 0, 0, 0.35));
}
.lore-card h2 {
  margin: 0 0 8px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.35rem;
  color: var(--gold);
}
.lore-card p { margin: 0 0 8px; color: var(--ink); text-wrap: pretty; }
.lore-card .lore-fine { margin: 0; color: var(--muted); }

.layout {
  max-width: 1180px; margin: 0 auto;
  padding: 28px max(16px, 3vw) 48px;
  display: grid; gap: 28px;
}
.hero-copy { max-width: 40rem; }
.pill {
  display: inline-flex; align-items: center; gap: 8px;
  margin: 0 0 12px; padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--line); font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.12em; color: var(--gold); background: rgba(0,0,0,0.35);
}
.pill .dot {
  width: 7px; height: 7px; border-radius: 50%; background: #f44;
  box-shadow: 0 0 0 0 rgba(255,68,68,0.5); animation: pulse 1.4s ease infinite;
}
@keyframes pulse {
  70% { box-shadow: 0 0 0 8px rgba(255,68,68,0); }
}
.hero-copy h1 {
  margin: 0; font-size: clamp(2.2rem, 6vw, 3.4rem); line-height: 0.95;
  letter-spacing: -0.03em; font-weight: 700;
}
.hero-copy h1 em {
  font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--gold);
}
/* wonka1 — front-door hook, straight from the launch tweets: Willy Wonka x
   Hunger Games golden-ticket energy + the three trials. Reuses the hero rhythm;
   .hook is the gold headline beat, .trials-line the tight follow. Responsive by
   clamp, so mobile stays intact. */
.hook {
  margin: 14px 0 0;
  font-size: clamp(1.05rem, 3.4vw, 1.35rem);
  line-height: 1.25;
  color: var(--ink);
}
.hook strong { color: var(--gold); }
.trials-line {
  margin: 8px 0 0;
  font-size: clamp(0.95rem, 2.6vw, 1.05rem);
  line-height: 1.35;
  color: var(--muted);
}
.trials-line strong { color: var(--ink); font-weight: 700; }
.trials-line em { font-family: var(--serif); font-style: italic; color: var(--sa-green); }
.lede { color: var(--muted); margin: 14px 0 0; font-size: 1.05rem; }
.tagline {
  font-family: var(--serif); font-style: italic; color: var(--gold);
  font-size: 1.2rem; margin: 10px 0 0;
}
.tagline em { color: var(--mint); }
.bullets {
  margin: 18px 0 0; padding-left: 1.1rem; color: var(--muted); font-size: 0.92rem;
}
.bullets li { margin: 6px 0; }
.bullets strong { color: var(--ink); }

.mode-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.55rem 0 0.35rem;
}
.mode-btn {
  cursor: pointer;
  border-radius: 8px;
  border: 1px solid rgba(255, 60, 60, 0.4);
  background: rgba(20, 6, 8, 0.85);
  color: #ffb0b0;
  padding: 0 0.9rem;
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: 700 0.78rem var(--mono);
  letter-spacing: 0.06em;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: filter 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.mode-btn:hover { filter: brightness(1.1); border-color: #ff4040; }
.mode-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px #0b0a08, 0 0 0 4px #9dccff;
}
.mode-btn:active { filter: brightness(0.96); }
.mode-btn.is-on {
  background: linear-gradient(90deg, #ff2020, #a01010);
  color: #fff;
  border-color: #ff4040;
  box-shadow: 0 0 16px rgba(255, 40, 40, 0.35);
}
.mode-btn--horde.is-on {
  background: linear-gradient(90deg, #ff4040, #801010);
  border-color: #ff4040;
  box-shadow: 0 0 16px rgba(255, 40, 40, 0.4);
}
.mode-btn--bureau {
  border-color: rgba(232, 165, 75, 0.45);
  color: #ffd8a0;
}
.mode-btn--bureau.is-on {
  background: linear-gradient(90deg, #e8a54b, #8a5a18);
  color: #1a1004;
  border-color: #e8a54b;
  box-shadow: 0 0 16px rgba(232, 165, 75, 0.4);
}
.mode-btn--wokenet {
  border-color: rgba(168, 126, 255, 0.45);
  color: #d8c0ff;
}
.mode-btn--wokenet.is-on {
  background: linear-gradient(90deg, #a87eff, #4a2080);
  color: #fff;
  border-color: #a87eff;
  box-shadow: 0 0 16px rgba(168, 126, 255, 0.4);
}
.mode-btn--random.is-on {
  background: linear-gradient(90deg, #5eead4, #1a6a60);
  color: #041210;
  border-color: #5eead4;
  box-shadow: 0 0 16px rgba(94, 234, 212, 0.35);
}
.team-pick .mode-btn {
  min-width: 5.5rem;
  font-weight: 800;
  letter-spacing: 0.08em;
}
.drop-bar .join { margin-top: 0.5rem; }
/* wonka1 — the golden-ticket hook on the drop-bar, which LEADS on mobile
   (.layout > .drop-bar { order: -3 }) so phone players get the tweet energy
   first thing, not buried below the fold with the hero. Gold, tight, one beat. */
.drop-bar__hype {
  margin: 0 0 6px;
  font-size: clamp(0.9rem, 3vw, 1.05rem);
  line-height: 1.3;
  color: var(--ink);
}
.drop-bar__hype strong { color: var(--gold); }
.drop-bar__hype em {
  font-family: var(--serif); font-style: italic; color: var(--mint); font-weight: 400;
}
.join {
  margin-top: 22px; display: flex; flex-wrap: wrap; gap: 10px; align-items: end;
}
.join label { display: grid; gap: 6px; font-size: 12px; color: var(--muted); }
.join input {
  min-height: 48px; min-width: 180px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: 4px;
  background: #000; color: var(--ink); font: 600 15px var(--sans);
}
.cta {
  min-height: 48px; padding: 0 22px; border: 0; border-radius: 4px;
  background: linear-gradient(90deg, #ffd24a, #ff914d); color: #111;
  font: 800 14px var(--sans); cursor: pointer; letter-spacing: 0.02em;
  box-shadow: 0 0 0 0 rgba(255, 210, 74, 0.4);
  animation: cta-pulse 2.2s ease-in-out infinite;
}
.cta:hover { filter: brightness(1.06); animation: none; }
.cta:focus-visible {
  outline: 2px solid #5eead4;
  outline-offset: 2px;
}
@keyframes cta-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 210, 74, 0.35); }
  50% { box-shadow: 0 0 24px 4px rgba(255, 210, 74, 0.25); }
}
.fine { margin: 14px 0 0; font-size: 0.8rem; color: var(--muted); max-width: 42rem; }
.status { margin: 10px 0 0; font-family: var(--mono); font-size: 12px; color: var(--sa-green); }
.status[data-kind="live"] { color: var(--mint); }
.status[data-kind="hot"] { color: var(--pink); }
.status[data-kind="win"] { color: var(--gold); font-weight: 700; }

.contest-banner {
  margin-top: 20px; padding: 16px 18px;
  border: 1px solid rgba(255, 210, 74, 0.35);
  border-radius: 6px;
  background: linear-gradient(135deg, rgba(255, 210, 74, 0.1), rgba(168, 126, 255, 0.08));
}
.contest-banner h2 {
  margin: 0 0 8px; font-size: 1.15rem; color: var(--gold);
}
.contest-banner p { margin: 0; color: var(--muted); font-size: 0.92rem; }
.contest-banner strong { color: var(--ink); }

.stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 260px;
  gap: 16px; align-items: start;
}
.stage--with-club {
  align-items: stretch;
}
.play-stack {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
}
.play-stack .canvas-wrap {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  border-bottom-width: 1px;
}
.play-stack .chat-mount--docked {
  margin: 0;
}
.play-stack .chat-mount--docked .gchat {
  margin: 0;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  border-top: 0;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
}
.legend .hint-club {
  margin-top: 8px;
  color: #5eead4;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.canvas-wrap {
  position: relative;
  border: 3px solid var(--gold-dim);
  border-radius: 2px;
  overflow: hidden;
  background: #0a0806;
  box-shadow:
    0 20px 60px rgba(0,0,0,0.55),
    0 0 0 1px rgba(255,210,74,0.2),
    inset 0 0 0 1px rgba(255, 160, 60, 0.12);
}
/* apple3 — first-paint boot glass until io-main creates the arena */
.arena-boot {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 8px;
  pointer-events: none;
  padding: max(16px, env(safe-area-inset-top, 0px)) 20px max(16px, env(safe-area-inset-bottom, 0px));
  background:
    radial-gradient(ellipse 70% 50% at 50% 42%, rgba(255, 210, 74, 0.12), transparent 60%),
    radial-gradient(ellipse 40% 30% at 80% 80%, rgba(157, 204, 255, 0.06), transparent 55%),
    rgba(8, 7, 6, 0.78);
  backdrop-filter: blur(6px) saturate(1.05);
  -webkit-backdrop-filter: blur(6px) saturate(1.05);
  transition: opacity 240ms ease, visibility 240ms ease;
}
.arena-boot[hidden] {
  display: none;
}
.arena-boot.is-done {
  opacity: 0;
  visibility: hidden;
}
/* applehud1 — settle the bar to full before fade (Apple-style ready snap) */
.arena-boot.is-done .arena-boot__fill {
  animation: none;
  width: 100%;
  transform: none;
  opacity: 1;
}
.arena-boot.is-done .arena-boot__pulse {
  animation: none;
  opacity: 0.9;
  transform: scaleX(1);
}
.arena-boot.is-done .arena-boot__line {
  color: rgba(255, 210, 74, 0.95);
}
.arena-boot__mark {
  font: 800 1.4rem/1 var(--sans, "Space Grotesk", system-ui, sans-serif);
  letter-spacing: 0.14em;
  color: var(--gold, #ffd24a);
  text-shadow: 0 0 28px rgba(255, 210, 74, 0.4);
}
.arena-boot__line {
  font: 600 11px/1.3 var(--mono, "IBM Plex Mono", monospace);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(246, 240, 228, 0.82);
}
.arena-boot__sub {
  font: 500 10px/1.3 var(--mono, "IBM Plex Mono", monospace);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(168, 152, 136, 0.9);
  margin-top: -2px;
}
.arena-boot__track {
  display: block;
  width: min(148px, 42vw);
  height: 3px;
  margin-top: 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 210, 74, 0.12);
}
.arena-boot__fill {
  display: block;
  height: 100%;
  width: 42%;
  border-radius: inherit;
  background: linear-gradient(90deg, rgba(255, 210, 74, 0.35), var(--gold, #ffd24a), rgba(157, 204, 255, 0.75));
  transform-origin: left center;
  animation: arena-boot-fill 1.15s cubic-bezier(0.45, 0.05, 0.25, 1) infinite;
}
@keyframes arena-boot-fill {
  0% { transform: translateX(-110%) scaleX(0.55); opacity: 0.55; }
  50% { transform: translateX(80%) scaleX(1); opacity: 1; }
  100% { transform: translateX(220%) scaleX(0.55); opacity: 0.45; }
}
.arena-boot__pulse {
  width: 28px;
  height: 3px;
  border-radius: 999px;
  background: linear-gradient(90deg, transparent, var(--gold, #ffd24a), transparent);
  opacity: 0.45;
  animation: arena-boot-pulse 1.1s ease-in-out infinite;
}
@keyframes arena-boot-pulse {
  0%, 100% { opacity: 0.2; transform: scaleX(0.65); }
  50% { opacity: 0.9; transform: scaleX(1); }
}
.canvas-wrap::before {
  content: "";
  pointer-events: none;
  position: absolute;
  inset: 0;
  z-index: 2;
  box-shadow: inset 0 0 56px rgba(40, 20, 10, 0.4);
  /* gfxdense1-corner */
  background:
    linear-gradient(135deg, rgba(255, 200, 80, 0.4) 0 10px, transparent 10px) top left / 28px 28px no-repeat,
    linear-gradient(225deg, rgba(255, 200, 80, 0.4) 0 10px, transparent 10px) top right / 28px 28px no-repeat,
    linear-gradient(45deg, rgba(255, 200, 80, 0.28) 0 10px, transparent 10px) bottom left / 28px 28px no-repeat,
    linear-gradient(315deg, rgba(255, 200, 80, 0.28) 0 10px, transparent 10px) bottom right / 28px 28px no-repeat;
}
#arena-canvas {
  display: block; width: 100%; height: auto;
  touch-action: none; cursor: crosshair;
  image-rendering: auto;
}
.sa-chrome {
  position: absolute; left: 0; right: 0; top: 0;
  z-index: 4;
  display: flex; justify-content: space-between; align-items: center;
  gap: 8px;
  /* applehud1 — clear notch + fullscreen FAB (44px) so rail never collides */
  padding:
    max(7px, env(safe-area-inset-top, 0px))
    max(56px, calc(12px + env(safe-area-inset-right, 0px) + 44px))
    8px
    max(12px, env(safe-area-inset-left, 0px));
  pointer-events: none;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.18em;
  font-weight: 700;
  color: rgba(255, 210, 74, 0.78); text-transform: uppercase;
  text-shadow: 0 1px 0 #000, 0 0 12px rgba(255, 180, 60, 0.35);
  background: linear-gradient(180deg, rgba(12, 8, 4, 0.62), transparent 92%);
  border-bottom: 1px solid rgba(255, 180, 60, 0.18);
}
.sa-chrome > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* mchud2 — the canvas HUD now paints a 14px paper title on an espresso rail
   at y19, and this DOM rail paints THE SAME TITLE on top of it. Measured on
   prod at 1440x900: two copies of "DDD · AI ARMAGEDDON · RANDOM TEAM" about
   10px apart in the top-left corner, which reads as a rendering fault.

   The mobile block already deleted this rail on 2026-08-07 for exactly this
   reason ("covered 7% AND double-drew the same title the canvas HUD already
   paints"); desktop was simply never revisited. Same argument, same fix, all
   widths. The markup and its aria-hidden ident copy stay in the document. */
.sa-chrome { display: none; }

.sa-chrome::after {
  content: "★★★★★";
  flex: 0 0 auto;
  letter-spacing: 0.2em;
  color: rgba(255, 60, 50, 0.45);
  font-size: 10px;
}

.side {
  border: 1px solid var(--line); border-radius: 4px;
  background: var(--panel); padding: 14px;
}
.side h2 {
  margin: 0 0 10px; font-size: 0.85rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--gold); font-family: var(--mono);
}
.player-row,
.board li[data-player] {
  cursor: pointer;
}
.player-row:hover .nm,
.board li[data-player]:hover .nm {
  color: var(--gold);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.board {
  list-style: none; margin: 0; padding: 0;
  max-height: 280px; overflow: auto;
}
.board li {
  display: grid; grid-template-columns: 28px minmax(0, 1fr) auto auto; gap: 6px;
  padding: 6px 0; border-bottom: 1px solid rgba(255,255,255,0.06);
  font-size: 12px;
}
.board li.me { color: var(--gold); background: rgba(255, 210, 74, 0.06); }
.board li.down { opacity: 0.4; }
.board .rk { color: var(--muted); font-family: var(--mono); }
.board .st { color: var(--pink); font-style: normal; font-weight: 700; font-size: 10px; }
.canvas-wrap:focus-within {
  box-shadow: 0 20px 60px rgba(0,0,0,0.45), 0 0 0 2px rgba(255,210,74,0.45);
}
.legend { margin-top: 12px; font-size: 11px; color: var(--muted); }
.legend .g { color: var(--gold); }
.legend .c { color: #4aa3ff; }
.legend .a { color: #e8c45a; }
.legend .h { color: #ff7eb6; }
.legend .s { color: var(--violet); }
.legend .mp { color: #5eead4; }
.mp-peers-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  margin-top: 14px;
  margin-bottom: 6px;
}
.mp-peers-h {
  margin: 0 !important;
  font-size: 0.75rem !important;
  letter-spacing: 0.12em;
  color: #5eead4 !important;
  text-transform: uppercase;
  flex: 1 1 auto;
}
.mp-transport {
  font: 600 9px/1 "IBM Plex Mono", monospace;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 4px 7px;
  border-radius: 999px;
  border: 1px solid rgba(94, 234, 212, 0.35);
  color: var(--muted);
  background: rgba(0, 0, 0, 0.45);
}
.mp-transport[data-kind="live"] {
  color: #5eead4;
  border-color: rgba(94, 234, 212, 0.65);
  box-shadow: 0 0 12px rgba(94, 234, 212, 0.25);
}
.mp-transport[data-kind="ok"] { color: #9dccff; border-color: rgba(157, 204, 255, 0.4); }
.mp-transport[data-kind="err"] { color: #ff8a8a; border-color: rgba(255, 120, 120, 0.45); }
.mp-share {
  font: 600 11px/1 "Space Grotesk", system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0 14px;
  border-radius: 999px;
  border: 1px solid rgba(255, 210, 74, 0.45);
  background: rgba(255, 210, 74, 0.12);
  color: #ffd24a;
  cursor: pointer;
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: filter 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.mp-share:hover { filter: brightness(1.1); background: rgba(255, 210, 74, 0.18); }
.mp-share:active { filter: brightness(0.95); }
.mp-share:focus-visible {
  outline: 2px solid #5eead4;
  outline-offset: 2px;
}
.mp-peers .empty { display: block; color: var(--muted); font-size: 11px; padding: 6px 0; }
.mp-peers li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 4px 8px;
  align-items: center;
  padding: 6px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  font-size: 12px;
}
.mp-peers li.down { opacity: 0.55; }
.mp-peers .nm { font-weight: 600; color: #e8f4ff; }
.mp-peers .meta {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  align-items: center;
  font: 500 10px/1 "IBM Plex Mono", monospace;
  color: var(--muted);
}
.mp-peers .hp-bar {
  flex: 1 1 72px;
  max-width: 100px;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
}
.mp-peers .hp-bar > i {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, #5eead4, #9dccff);
  border-radius: inherit;
}
.mp-peers li.down .hp-bar > i { background: #ff6b6b; }
.mp-peers .via {
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #5eead4;
  opacity: 0.85;
}
.mp-peers .via.local { color: #ffd24a; }
.link-out {
  display: inline-block; margin-top: 14px; font-size: 12px;
  font-family: var(--mono); color: var(--mint);
}

/* Multiplayer room seed UI */
.mp-room {
  margin: 16px 0 8px;
  padding: 14px 16px;
  border: 1px solid rgba(94, 234, 212, 0.28);
  border-radius: 8px;
  background: linear-gradient(135deg, rgba(94, 234, 212, 0.08), rgba(0, 0, 0, 0.35));
}
.mp-room__title {
  margin: 0 0 6px;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #5eead4;
  font-family: var(--mono);
}
.mp-room__lede {
  margin: 0 0 12px;
  font-size: 0.88rem;
  color: var(--muted);
  line-height: 1.45;
}
.mp-room__row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: flex-end;
}
.mp-room__row label {
  display: grid;
  gap: 4px;
  flex: 1 1 120px;
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.mp-room__row input {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 10px 12px;
  border-radius: 6px;
  border: 1px solid rgba(94, 234, 212, 0.35);
  background: rgba(0, 0, 0, 0.45);
  color: #5eead4;
  min-height: 44px;
}
.mp-room__btn {
  min-height: 44px;
  padding: 10px 14px;
  border-radius: 6px;
  border: 1px solid rgba(94, 234, 212, 0.45);
  background: color-mix(in oklab, #5eead4 22%, transparent);
  color: #e8fffa;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
}
.mp-room__btn:hover { filter: brightness(1.08); }
.mp-room__btn.ghost {
  background: transparent;
  color: #5eead4;
}
.mp-room__status {
  margin: 10px 0 0;
  font-size: 12px;
  font-family: var(--mono);
  color: #9cf;
}
.mp-room__status[data-kind="live"] { color: #5eead4; }
.mp-room__status[data-kind="err"] { color: #ff8a8a; }
.mp-room__status[data-kind="ok"] { color: var(--muted); }

.contest-panel {
  margin-top: 28px; padding: 20px;
  border: 1px solid var(--line); border-radius: 6px; background: rgba(0,0,0,0.35);
}
.contest-pill {
  display: inline-block; margin: 0 0 10px; padding: 4px 10px;
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em;
  color: #111; background: var(--gold); border-radius: 999px; font-weight: 700;
}
.contest-panel h2 { margin: 0 0 8px; color: var(--gold); }
.contest-lede { color: var(--muted); margin: 0 0 14px; font-size: 0.95rem; }
.contest-board {
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--line); border-radius: 4px; overflow: hidden;
}
.contest-board li {
  display: grid; grid-template-columns: 36px minmax(0, 1fr) auto 28px; gap: 8px;
  padding: 10px 12px; border-bottom: 1px solid rgba(255,255,255,0.06);
  background: rgba(0,0,0,0.25); font-size: 13px;
}
.contest-board li:nth-child(-n+3) { background: rgba(255, 210, 74, 0.08); }
.contest-board li.empty { display: block; color: var(--muted); }
.contest-board .rk { font-family: var(--mono); color: var(--gold); }
.contest-board .sc { font-family: var(--mono); font-weight: 700; }
.contest-board .prize { color: var(--gold); }
.contest-claim { margin: 14px 0 8px; font-size: 0.9rem; color: var(--ink); }
.contest-links { font-size: 0.85rem; }
.contest-fine { margin: 10px 0 0; font-size: 0.78rem; color: var(--muted); }

.foot {
  border-top: 1px solid var(--line); padding: 28px max(16px, 3vw) 40px;
  text-align: center; color: var(--muted); font-size: 0.85rem;
}
.foot p { margin: 0 0 8px; }

/* Mobile combat pads + virtual stick */
.io-touch {
  display: none;
  position: absolute;
  right: max(10px, env(safe-area-inset-right, 0px));
  bottom: max(12px, calc(10px + env(safe-area-inset-bottom, 0px)));
  z-index: 6;
  gap: 8px;
  pointer-events: none;
}
.io-pad {
  pointer-events: auto;
  min-width: 64px;
  min-height: 52px;
  padding: 10px 12px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: rgba(0, 0, 0, 0.72);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  /* applehud1 — snappy press without laggy hover wash */
  transition:
    transform 0.12s cubic-bezier(0.2, 0.8, 0.2, 1),
    background 0.12s ease,
    border-color 0.12s ease,
    color 0.12s ease,
    box-shadow 0.12s ease;
}
.io-pad:focus-visible {
  outline: 2px solid var(--gold, #ffd24a);
  outline-offset: 2px;
}
.io-pad:active,
.io-pad.held {
  background: rgba(255, 210, 74, 0.28);
  border-color: var(--gold-dim);
  color: var(--gold);
  transform: scale(0.96);
  box-shadow:
    0 2px 10px rgba(0, 0, 0, 0.4),
    0 0 0 1px rgba(255, 210, 74, 0.25),
    0 0 18px rgba(255, 210, 74, 0.18);
}
/* applehud1 — action-tinted plates (readable at a glance on phone) */
.io-pad.fire {
  color: #ffe566;
  border-color: rgba(255, 229, 102, 0.38);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(255, 229, 102, 0.08);
}
.io-pad.melt {
  color: var(--pink);
  border-color: rgba(255, 126, 182, 0.4);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(255, 126, 182, 0.1);
}
.io-pad.dash {
  color: var(--mint);
  border-color: rgba(157, 204, 255, 0.4);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(157, 204, 255, 0.1);
}
.io-pad.ride {
  color: #9dccff;
  font-size: 10px;
  border-color: rgba(157, 204, 255, 0.32);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(157, 204, 255, 0.08);
}
.io-pad.fire:active,
.io-pad.fire.held {
  background: rgba(255, 229, 102, 0.22);
  border-color: #ffe566;
  color: #fff3a8;
  box-shadow: 0 2px 12px rgba(255, 210, 74, 0.28), 0 0 0 1px rgba(255, 229, 102, 0.35);
}
.io-pad.melt:active,
.io-pad.melt.held {
  background: rgba(255, 126, 182, 0.24);
  border-color: var(--pink);
  color: #ffc2dc;
  box-shadow: 0 2px 12px rgba(255, 126, 182, 0.3), 0 0 0 1px rgba(255, 126, 182, 0.4);
}
.io-pad.dash:active,
.io-pad.dash.held {
  background: rgba(157, 204, 255, 0.22);
  border-color: var(--mint);
  color: #d6ecff;
  box-shadow: 0 2px 12px rgba(157, 204, 255, 0.28), 0 0 0 1px rgba(157, 204, 255, 0.4);
}
.io-pad.ride:active,
.io-pad.ride.held {
  background: rgba(157, 204, 255, 0.18);
  border-color: #9dccff;
  color: #e8f4ff;
  box-shadow: 0 2px 12px rgba(157, 204, 255, 0.22), 0 0 0 1px rgba(157, 204, 255, 0.35);
}


/* gamesloop04 — util row: USE / DM / SVC / gun cycle on mobile */
.io-touch__util,
.io-touch__combat {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  pointer-events: none;
}
.io-touch__util .io-pad,
.io-touch__combat .io-pad {
  pointer-events: auto;
}
.io-touch__util {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: 200px;
  gap: 6px;
  margin-bottom: 2px;
}
.io-touch__util .io-pad {
  /* bothaxes-util1 — this rule is 2-class specificity, so it beats the
     `.io-pad { min-height: 48px }` inside @media (max-width: 430px) (1 class)
     and wins at EVERY breakpoint. min-height was 40px here, so the util pads
     rendered 48x40 on phones — 4px under the 44px floor. Same min-*-one-axis
     mirror as thumbs1/touchfloor1/bothaxes1. Both axes >= 44 now. */
  min-width: 48px;
  min-height: 44px;
  padding: 6px 8px;
  font-size: 9px;
  letter-spacing: 0.06em;
  border-radius: 11px;
}
.io-pad.use {
  color: #ffd24a;
  border-color: rgba(255, 210, 74, 0.4);
}
.io-pad.bond {
  color: #c4b5fd;
  border-color: rgba(196, 181, 253, 0.48);
}
.io-pad.dm {
  color: #ff9ad4;
  border-color: rgba(255, 154, 212, 0.42);
}
.io-pad.high {
  color: #7dff9a;
  border-color: rgba(125, 255, 154, 0.45);
}
.io-pad.svc {
  color: #c9a0ff;
  border-color: rgba(201, 160, 255, 0.42);
}
.io-pad.gunprev,
.io-pad.gunnext {
  color: #9dccff;
  border-color: rgba(157, 204, 255, 0.32);
  font-size: 8px;
}
.io-pad.use:active,
.io-pad.use.held {
  background: rgba(255, 210, 74, 0.22);
  border-color: #ffd24a;
  color: #ffe9a0;
}
.io-pad.bond:active,
.io-pad.bond.held {
  background: rgba(168, 126, 255, 0.24);
  border-color: #c4b5fd;
  color: #ede9fe;
}
.io-pad.dm:active,
.io-pad.dm.held {
  background: rgba(255, 126, 182, 0.24);
  border-color: #ff9ad4;
  color: #ffd0e8;
}
.io-pad.high:active,
.io-pad.high.held {
  background: rgba(125, 255, 154, 0.22);
  border-color: #7dff9a;
  color: #c8ffd4;
}
.io-pad.svc:active,
.io-pad.svc.held {
  background: rgba(201, 160, 255, 0.22);
  border-color: #c9a0ff;
  color: #efe0ff;
}
/* The util row used to be re-tuned again at 430px (max-width 176px, 36px pads,
   8px type). Every one of those declarations is now shadowed by the thumbs1
   grid below — which applies at (max-width: 900px), (pointer: coarse), a
   strict superset — so the block was two contradictory sources of truth for
   the same pixels. Removed rather than kept in sync. */

/* Left virtual stick */
.io-stick {
  display: none;
  position: absolute;
  left: max(10px, env(safe-area-inset-left, 0px));
  bottom: max(12px, calc(10px + env(safe-area-inset-bottom, 0px)));
  z-index: 6;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;
}
.io-stick__pad {
  pointer-events: auto;
  width: 112px;
  height: 112px;
  border-radius: 50%;
  border: 2px solid rgba(255, 210, 74, 0.35);
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 210, 74, 0.08), transparent 60%),
    rgba(0, 0, 0, 0.55);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 6px 20px rgba(0, 0, 0, 0.4);
  touch-action: none;
  position: relative;
  -webkit-tap-highlight-color: transparent;
}
.io-stick__knob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 44px;
  height: 44px;
  margin: -22px 0 0 -22px;
  border-radius: 50%;
  background: linear-gradient(145deg, rgba(255, 210, 74, 0.85), rgba(201, 162, 39, 0.75));
  border: 2px solid rgba(255, 255, 255, 0.25);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  pointer-events: none;
  will-change: transform;
}
.io-stick.is-active .io-stick__pad {
  border-color: rgba(94, 234, 212, 0.55);
}
.io-stick__hint {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  color: var(--muted);
  pointer-events: none;
  text-transform: uppercase;
  font-weight: 700;
}

@media (max-width: 900px), (pointer: coarse) {
  .io-touch {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
  }
  .io-stick {
    display: flex;
  }
  /* leavematch1 — only reveal on the mobile gate AND when arena is live */
  .io-leave.is-live {
    display: inline-flex;
  }
  .play-stack .canvas-wrap {
    min-height: min(62dvh, 520px);
  }
  .canvas-wrap canvas {
    touch-action: none;
  }
  /* stack side board under play — full-width mobile */
  .stage {
    gap: 0.75rem;
  }
  .side {
    order: 2;
    max-height: none;
  }
  .side .board {
    max-height: 12rem;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
  }
  .hero h1,
  .hero .lede {
    max-width: 100%;
  }
  /* economy / wallet denser */
  .economy-mount,
  #ddd-wallet-bar,
  #wetdrool-wallet-bar {
    font-size: 0.9rem;
  }
  body.is-mobile-play .top {
    gap: 8px 12px;
    padding-bottom: 8px;
  }
  body.is-mobile-play .top nav a {
    /* appleradio1 — never drop below studio 44px target */
    min-height: 44px;
    font-size: 10px;
  }
}

@media (max-width: 900px) {
  .stage { grid-template-columns: minmax(0, 1fr); }
  .top nav { width: 100%; margin-left: 0; }
  .radio-dock {
    width: 100%;
    margin-left: 0;
    justify-content: flex-start;
  }
  .music-btn#music-toggle { flex: 1 1 auto; }
  /* club dock under canvas — keep composer thumb-reachable */
  .play-stack .chat-mount--docked {
    max-height: 42dvh;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
  }
  .drop-bar,
  .join-card {
    margin-left: max(12px, env(safe-area-inset-left));
    margin-right: max(12px, env(safe-area-inset-right));
  }
}

@media (max-width: 430px) {
  .io-stick__pad {
    width: 100px;
    height: 100px;
  }
  .io-pad {
    min-width: 58px;
    min-height: 48px;
    font-size: 10px;
    padding: 8px 10px;
  }
  .io-touch {
    gap: 6px;
  }
  .brand .mark {
    font-size: 1.05rem;
  }
  .pill {
    font-size: 0.72rem;
  }
  /* applehud1 — phone: drop red stars so dual labels fit next to FAB */
  .sa-chrome {
    font-size: 8px;
    letter-spacing: 0.12em;
    padding-right: max(52px, calc(8px + env(safe-area-inset-right, 0px) + 44px));
  }
  .sa-chrome::after {
    display: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pill .dot { animation: none; }
  .cta { animation: none; }
  .mode-btn { transition: none; }
  .mode-btn:active { filter: none; }
  .music-btn, .radio-now, .radio-eq, .mp-share, .fs-fab, .io-pad, .radio-promo__btn { transition: none; }
  .music-btn:active { transform: none; }
  .io-pad:active,
  .io-pad.held { transform: none; }
  .fs-fab:active { transform: none; }
  .mp-share:active { filter: none; }
  /* appleradio1 — quiet ON AIR breath + no marquee when reduce-motion */
  .radio-now[data-on="true"] .radio-now__badge {
    animation: none !important;
  }
  .radio-now__line[data-scroll="true"] .radio-now__scroll {
    animation: none !important;
    transform: none !important;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    padding-right: 0;
  }
  .radio-eq[data-on="true"] i,
  .radio-eq[data-on="true"] i:nth-child(1),
  .radio-eq[data-on="true"] i:nth-child(2),
  .radio-eq[data-on="true"] i:nth-child(3),
  .radio-eq[data-on="true"] i:nth-child(4) {
    animation: none !important;
    transform: none;
    height: 10px;
    opacity: 0.85;
  }
  /* apple2 — kill CRT scan / grain shimmer for vestibular comfort */
  .canvas-wrap::after {
    opacity: 0 !important;
    background: none !important;
    box-shadow: none !important;
  }
  .arena-boot__pulse { animation: none !important; opacity: 0.85; }
  .arena-boot__fill {
    animation: none !important;
    width: 58%;
    transform: none;
    opacity: 0.9;
  }
  .arena-boot { transition: none; }
}

.mp-room--moshpit .mp-room__row { display: none; }
.mp-room--moshpit .mp-room__title { color: #5eead4; }


/* gfxdense4-scan — soft CRT/SA glass (very light, web-first) */
.canvas-wrap::after {
  content: "";
  pointer-events: none;
  position: absolute;
  inset: 0;
  z-index: 3;
  /* gfxdense19 — slightly stronger scan read */
  opacity: 0.065;
  background: repeating-linear-gradient(
    0deg,
    transparent,
    transparent 2px,
    rgba(0, 0, 0, 0.55) 2px,
    rgba(0, 0, 0, 0.55) 3px
  );
}


/* gfxdense5-focus */
.canvas-wrap:focus-within {
  box-shadow:
    0 20px 60px rgba(0, 0, 0, 0.5),
    0 0 0 2px rgba(255, 210, 74, 0.55),
    0 0 28px rgba(255, 210, 74, 0.18);
}
body.wetdrool-arena .canvas-wrap:focus-within {
  box-shadow:
    0 20px 60px rgba(0, 0, 0, 0.5),
    0 0 0 2px rgba(255, 126, 182, 0.55),
    0 0 28px rgba(255, 126, 182, 0.2);
}


/* gfxdense7-war — subtle red rim when arena has focus (machine hunt feel) */
.canvas-wrap:focus-within {
  box-shadow:
    0 20px 60px rgba(0, 0, 0, 0.55),
    0 0 0 2px rgba(255, 60, 60, 0.35),
    0 0 32px rgba(255, 40, 60, 0.12),
    0 0 0 1px rgba(255, 210, 74, 0.25);
}


/* gfxdense8-faction legend chips (if page injects) */
.faction-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.faction-legend span {
  padding: 4px 8px;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 2px;
  background: rgba(0,0,0,0.35);
}
.faction-legend .hz { color: #ff6a6a; border-color: rgba(255,40,40,0.4); }
.faction-legend .cb { color: #9dccff; border-color: rgba(74,163,255,0.45); }
.faction-legend .wn { color: #e0c0ff; border-color: rgba(168,126,255,0.45); }


/* gfxdense9-foilbar */
.canvas-wrap {
  box-shadow:
    0 20px 60px rgba(0, 0, 0, 0.55),
    0 0 0 1px rgba(255, 210, 74, 0.2),
    inset 0 -3px 0 rgba(255, 210, 74, 0.35);
}

/* Fullscreen arena shell */
body.is-fs-play { overflow: hidden; }
.canvas-wrap.is-fullscreen,
body.is-fs-play .canvas-wrap.is-fullscreen {
  position: fixed !important;
  inset: 0 !important;
  z-index: 9000;
  width: 100vw !important;
  /* dvh, not vh: on mobile Safari/Chrome 100vh is the *largest* viewport and
     leaves the last ~60-90px of the arena under the browser chrome. This rule
     is also reused by the classes-only immersive mode (setMobileImmersive), so
     it runs outside a real fullscreen session too. */
  height: 100dvh !important;
  max-width: none !important;
  max-height: none !important;
  border-radius: 0 !important;
  background: #050608;
}
body.is-fs-play .canvas-wrap.is-fullscreen canvas {
  width: 100% !important;
  height: 100% !important;
}
.fs-fab {
  position: absolute;
  /* applehud1 — stay above sa-chrome; safe-area + 44px target */
  top: max(0.45rem, env(safe-area-inset-top, 0px));
  right: max(0.45rem, env(safe-area-inset-right, 0px));
  z-index: 7;
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.28);
  background: rgba(0,0,0,0.62);
  color: #f5f0e6;
  font-size: 1.05rem;
  cursor: pointer;
  line-height: 1;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease, transform 0.12s ease;
}
.fs-fab:active { transform: scale(0.96); }
.fs-fab:hover { border-color: #ffd24a; color: #ffd24a; background: rgba(0,0,0,0.72); }
.fs-fab:focus-visible {
  outline: 2px solid #5eead4;
  outline-offset: 2px;
}

/* leavematch1 — mobile-only explicit exit from a LIVE match back to lobby.
   Two-gate visibility: base display:none keeps it off desktop AND in
   lobby/end; io-main adds `.is-live` only while phase==="live", and the
   coarse/narrow @media below is the ONLY place it flips to inline-flex.
   Both-axis 44px floor — set min-width AND min-height (mirror-bug guard). */
.io-leave {
  display: none;
  position: absolute;
  top: max(0.5rem, calc(env(safe-area-inset-top, 0px) + 34px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 7;
  min-width: 88px;
  min-height: 44px;
  padding: 0 16px;
  border-radius: 999px;
  border: 1px solid rgba(255, 120, 120, 0.5);
  background: rgba(20, 6, 6, 0.72);
  color: #ffd7d7;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.12em;
  cursor: pointer;
  line-height: 1;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}
.io-leave:active { transform: translateX(-50%) scale(0.96); }
.io-leave:hover { border-color: rgba(255, 150, 150, 0.85); color: #fff; }
.io-leave:focus-visible {
  outline: 2px solid #5eead4;
  outline-offset: 2px;
}
.fs-fab[aria-pressed="true"] {
  border-color: rgba(255, 210, 74, 0.65);
  color: #ffd24a;
  box-shadow: 0 0 14px rgba(255, 210, 74, 0.2);
}
body.wetdrool-arena .fs-fab:hover { border-color: #ff7eb6; color: #ff7eb6; }
body.wetdrool-arena .fs-fab[aria-pressed="true"] {
  border-color: rgba(255, 126, 182, 0.7);
  color: #ff7eb6;
  box-shadow: 0 0 14px rgba(255, 126, 182, 0.22);
}
body.is-fs-play .top,
body.is-fs-play .radio-now,
body.is-fs-play .foot,
body.is-fs-play .hero-copy { /* keep chrome out of the way when native FS may not hide all */
  display: none;
}


/* gfxdense10-scan-soft — lighter than prior scan; SA glass */
.canvas-wrap {
  background-image: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.03) 0%,
    transparent 18%,
    transparent 82%,
    rgba(0, 0, 0, 0.15) 100%
  );
}

/* gfxdense11 — foil rim + radar-adjacent chrome plate */
.canvas-wrap {
  box-shadow:
    inset 0 0 0 1px rgba(232, 196, 90, 0.22),
    inset 0 0 40px rgba(0, 0, 0, 0.35),
    0 8px 28px rgba(0, 0, 0, 0.45);
}
body.wetdrool-arena .canvas-wrap {
  box-shadow:
    inset 0 0 0 1px rgba(255, 126, 182, 0.28),
    inset 0 0 48px rgba(40, 0, 30, 0.4),
    0 8px 28px rgba(20, 0, 16, 0.55);
}
.hud-chip,
.stat-chip,
[data-hud] {
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
}

/* gfxdense12 — SA glass corner ticks + subtle film grain (CSS only, no FPS hit) */
.canvas-wrap::after {
  content: "";
  pointer-events: none;
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow:
    inset 10px 10px 0 -8px rgba(255, 210, 74, 0.35),
    inset -10px 10px 0 -8px rgba(255, 210, 74, 0.25),
    inset 10px -10px 0 -8px rgba(255, 210, 74, 0.2),
    inset -10px -10px 0 -8px rgba(255, 210, 74, 0.2);
  opacity: 0.85;
  z-index: 2;
}
body.wetdrool-arena .canvas-wrap::after {
  box-shadow:
    inset 10px 10px 0 -8px rgba(255, 126, 182, 0.45),
    inset -10px 10px 0 -8px rgba(255, 126, 182, 0.3),
    inset 10px -10px 0 -8px rgba(255, 100, 160, 0.25),
    inset -10px -10px 0 -8px rgba(255, 100, 160, 0.25);
}


/* gfxdense13 — top HUD glass bevel */
.canvas-wrap {
  background-image:
    linear-gradient(180deg, rgba(255,255,255,0.04) 0%, transparent 14%),
    linear-gradient(180deg, transparent 70%, rgba(0,0,0,0.18) 100%);
}


/* gfxdense14 — radar-side chrome pulse bar */
.canvas-wrap {
  outline: 1px solid rgba(255, 210, 74, 0.12);
}
body.wetdrool-arena .canvas-wrap,
.wetdrool-arena .canvas-wrap {
  outline-color: rgba(255, 126, 182, 0.18);
}


/* gfxdense15 — aim-chrome canvas cursor hint (CSS only) */
.canvas-wrap canvas {
  cursor: crosshair;
}


/* gfxdense16 — deeper canvas inset shadow */
.canvas-wrap {
  box-shadow:
    inset 0 0 60px rgba(0, 0, 0, 0.35),
    0 12px 40px rgba(0, 0, 0, 0.45);
}


/* gfxdense17 — foil focus ring when playing */
.canvas-wrap:focus-within {
  outline: 1px solid rgba(255, 210, 74, 0.35);
  outline-offset: 2px;
}
body.wetdrool-arena .canvas-wrap:focus-within {
  outline-color: rgba(255, 126, 182, 0.4);
}

/* gfxdense18 — soft foil outer glow on play surface */
.canvas-wrap {
  box-shadow:
    inset 0 0 60px rgba(0, 0, 0, 0.35),
    0 12px 40px rgba(0, 0, 0, 0.45),
    0 0 0 1px rgba(255, 210, 74, 0.08),
    0 0 28px rgba(255, 210, 74, 0.06);
}
body.wetdrool-arena .canvas-wrap {
  box-shadow:
    inset 0 0 60px rgba(0, 0, 0, 0.4),
    0 12px 40px rgba(0, 0, 0, 0.5),
    0 0 0 1px rgba(255, 126, 182, 0.1),
    0 0 28px rgba(255, 100, 180, 0.08);
}


/* gfxdense20 — play surface foil micro-bevel */
.canvas-wrap canvas {
  border-radius: 2px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
body.wetdrool-arena .canvas-wrap canvas {
  box-shadow: inset 0 1px 0 rgba(255, 180, 220, 0.08);
}


/* gfxdense21 — soft vignette wash on wrap edge */
.canvas-wrap {
  background:
    radial-gradient(ellipse 90% 80% at 50% 45%, transparent 55%, rgba(0, 0, 0, 0.18) 100%);
}
body.wetdrool-arena .canvas-wrap {
  background:
    radial-gradient(ellipse 90% 80% at 50% 45%, transparent 50%, rgba(40, 0, 30, 0.22) 100%);
}


/* gfxdense22 — gold/pink hairline frame */
.canvas-wrap {
  border: 1px solid rgba(255, 210, 74, 0.14);
}
body.wetdrool-arena .canvas-wrap {
  border-color: rgba(255, 126, 182, 0.16);
}


/* gfxdense23 — foil corner ticks on play wrap */
.canvas-wrap::before {
  box-shadow:
    inset 0 0 0 1px rgba(255, 210, 74, 0.06),
    inset 12px 12px 0 -11px rgba(255, 210, 74, 0.25),
    inset -12px -12px 0 -11px rgba(255, 210, 74, 0.18);
}
body.wetdrool-arena .canvas-wrap::before {
  box-shadow:
    inset 0 0 0 1px rgba(255, 126, 182, 0.08),
    inset 12px 12px 0 -11px rgba(255, 126, 182, 0.3),
    inset -12px -12px 0 -11px rgba(255, 100, 160, 0.2);
}


/* gfxdense24 — deeper play well */
.canvas-wrap {
  box-shadow:
    inset 0 0 80px rgba(0, 0, 0, 0.28),
    0 14px 44px rgba(0, 0, 0, 0.5);
}


/* gfxdense25 — subtle top foil sheen on canvas wrap */
.canvas-wrap {
  background-image: linear-gradient(180deg, rgba(255, 210, 74, 0.04), transparent 28%);
}
body.wetdrool-arena .canvas-wrap {
  background-image: linear-gradient(180deg, rgba(255, 126, 182, 0.05), transparent 28%);
}


/* gfxdense26 — soft outer glow pulse-ready (static) */
.canvas-wrap {
  outline: 1px solid rgba(255, 210, 74, 0.1);
  outline-offset: 1px;
}
body.wetdrool-arena .canvas-wrap {
  outline-color: rgba(255, 126, 182, 0.12);
}


/* gfxdense27 — foil corner pin via border-image free box */
.stage .canvas-wrap {
  box-shadow:
    inset 0 0 0 1px rgba(255, 210, 74, 0.08),
    6px 6px 0 -5px rgba(255, 210, 74, 0.2),
    -6px 6px 0 -5px rgba(255, 210, 74, 0.15),
    6px -6px 0 -5px rgba(255, 210, 74, 0.12),
    -6px -6px 0 -5px rgba(255, 210, 74, 0.12);
}
body.wetdrool-arena .stage .canvas-wrap {
  box-shadow:
    inset 0 0 0 1px rgba(255, 126, 182, 0.1),
    6px 6px 0 -5px rgba(255, 126, 182, 0.22),
    -6px 6px 0 -5px rgba(255, 126, 182, 0.16),
    6px -6px 0 -5px rgba(255, 100, 160, 0.14),
    -6px -6px 0 -5px rgba(255, 100, 160, 0.14);
}


/* gfxdense28 — bottom chrome hairline + foil grit */
.stage .canvas-wrap {
  background-image:
    linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, transparent 12%),
    linear-gradient(0deg, rgba(255, 210, 74, 0.06) 0%, transparent 8%);
}
body.wetdrool-arena .stage .canvas-wrap {
  background-image:
    linear-gradient(180deg, rgba(255, 200, 220, 0.04) 0%, transparent 12%),
    linear-gradient(0deg, rgba(255, 126, 182, 0.08) 0%, transparent 8%);
}


/* gfxdense29 — corner grit ticks */
.stage .canvas-wrap {
  background-size: 100% 100%, 100% 100%, 6px 6px;
  background-image:
    linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, transparent 12%),
    linear-gradient(0deg, rgba(255, 210, 74, 0.06) 0%, transparent 8%),
    radial-gradient(circle at 4px 4px, rgba(255, 210, 74, 0.12) 0 1px, transparent 1.5px);
}
body.wetdrool-arena .stage .canvas-wrap {
  background-image:
    linear-gradient(180deg, rgba(255, 200, 220, 0.04) 0%, transparent 12%),
    linear-gradient(0deg, rgba(255, 126, 182, 0.08) 0%, transparent 8%),
    radial-gradient(circle at 4px 4px, rgba(255, 126, 182, 0.14) 0 1px, transparent 1.5px);
}


/* gfxdense30 — soft outer vignette rim */
.stage .canvas-wrap {
  box-shadow:
    inset 0 0 48px rgba(0, 0, 0, 0.32),
    inset 0 0 0 1px rgba(255, 210, 74, 0.1),
    0 0 0 1px rgba(255, 210, 74, 0.05);
}
body.wetdrool-arena .stage .canvas-wrap {
  box-shadow:
    inset 0 0 48px rgba(0, 0, 0, 0.38),
    inset 0 0 0 1px rgba(255, 126, 182, 0.12),
    0 0 0 1px rgba(255, 126, 182, 0.06);
}

/* cross-play device badge */
.mp-peers .plat{font-size:.62em;opacity:.75;letter-spacing:.04em;margin-left:.25rem;color:#9dccff}
.mp-peers .plat:before{content:"";}


/* gfxdense31 — top foil sheen hairline */
.stage .canvas-wrap::after {
  content: "";
  pointer-events: none;
  position: absolute;
  inset: 0 0 auto 0;
  height: 10px;
  background: linear-gradient(180deg, rgba(255, 210, 74, 0.1), transparent);
  border-radius: inherit;
}
body.wetdrool-arena .stage .canvas-wrap::after {
  background: linear-gradient(180deg, rgba(255, 126, 182, 0.12), transparent);
}


/* gfxdense32 — bottom foil status bar */
.stage .canvas-wrap {
  border-bottom: 1px solid rgba(255, 210, 74, 0.16);
}
body.wetdrool-arena .stage .canvas-wrap {
  border-bottom-color: rgba(255, 126, 182, 0.18);
}


/* gfxdense33 — inner rim hairline */
.stage .canvas-wrap {
  outline: 1px solid rgba(255, 210, 74, 0.1);
  outline-offset: -2px;
}
body.wetdrool-arena .stage .canvas-wrap {
  outline-color: rgba(255, 126, 182, 0.12);
}


/* gfxdense34 — corner bracket ticks */
.stage .canvas-wrap {
  box-shadow:
    inset 0 0 0 1px rgba(255, 210, 74, 0.08),
    inset 8px 8px 0 -7px rgba(255, 210, 74, 0.18),
    inset -8px -8px 0 -7px rgba(255, 210, 74, 0.12);
}
body.wetdrool-arena .stage .canvas-wrap {
  box-shadow:
    inset 0 0 0 1px rgba(255, 126, 182, 0.08),
    inset 8px 8px 0 -7px rgba(255, 126, 182, 0.2),
    inset -8px -8px 0 -7px rgba(255, 126, 182, 0.15);
}


/* gfxdense35 — soft top film grain band */
.stage .canvas-wrap::after {
  opacity: 0.9;
  background-image: linear-gradient(180deg, rgba(255, 210, 74, 0.08), transparent),
    repeating-linear-gradient(
      0deg,
      transparent,
      transparent 2px,
      rgba(255, 210, 74, 0.025) 2px,
      rgba(255, 210, 74, 0.025) 3px
    );
}
body.wetdrool-arena .stage .canvas-wrap::after {
  background-image: linear-gradient(180deg, rgba(255, 126, 182, 0.1), transparent),
    repeating-linear-gradient(
      0deg,
      transparent,
      transparent 2px,
      rgba(255, 126, 182, 0.03) 2px,
      rgba(255, 126, 182, 0.03) 3px
    );
}


/* gfxdense36 — play well depth */
.stage .canvas-wrap {
  box-shadow:
    inset 0 0 72px rgba(0, 0, 0, 0.36),
    inset 0 0 0 1px rgba(255, 210, 74, 0.09);
}
body.wetdrool-arena .stage .canvas-wrap {
  box-shadow:
    inset 0 0 80px rgba(0, 0, 0, 0.42),
    inset 0 0 0 1px rgba(255, 126, 182, 0.1);
}


/* gfxdense37 — side chrome rails */
.stage .canvas-wrap {
  border-left: 1px solid rgba(255, 210, 74, 0.12);
  border-right: 1px solid rgba(255, 210, 74, 0.08);
}
body.wetdrool-arena .stage .canvas-wrap {
  border-left-color: rgba(255, 126, 182, 0.14);
  border-right-color: rgba(255, 126, 182, 0.1);
}

/* gamesloop09 — keep stick/pads from scrolling the page away mid-match */
body.is-mobile-play.is-arena-live {
  overflow: hidden;
  overscroll-behavior: none;
  touch-action: manipulation;
  height: 100dvh;
}
body.is-mobile-play.is-arena-live .play-stack .canvas-wrap {
  touch-action: none;
}


/* gfxdense38 — corner LED pins */
.stage .canvas-wrap::after {
  /* keep prior ::after; add via box-shadow pins on wrap */
}
.stage .canvas-wrap {
  box-shadow:
    inset 0 0 72px rgba(0, 0, 0, 0.36),
    inset 0 0 0 1px rgba(255, 210, 74, 0.09),
    inset 6px 6px 0 -4px rgba(255, 210, 74, 0.35),
    inset -6px 6px 0 -4px rgba(255, 210, 74, 0.22),
    inset 6px -6px 0 -4px rgba(255, 210, 74, 0.18),
    inset -6px -6px 0 -4px rgba(255, 210, 74, 0.12);
}
body.wetdrool-arena .stage .canvas-wrap {
  box-shadow:
    inset 0 0 80px rgba(0, 0, 0, 0.42),
    inset 0 0 0 1px rgba(255, 126, 182, 0.1),
    inset 6px 6px 0 -4px rgba(255, 126, 182, 0.4),
    inset -6px 6px 0 -4px rgba(255, 126, 182, 0.28),
    inset 6px -6px 0 -4px rgba(255, 126, 182, 0.22),
    inset -6px -6px 0 -4px rgba(255, 126, 182, 0.15);
}


/* gfxdense39 — top foil scan bar */
.stage .canvas-wrap {
  border-top: 1px solid rgba(255, 210, 74, 0.16);
}
body.wetdrool-arena .stage .canvas-wrap {
  border-top-color: rgba(255, 126, 182, 0.18);
}
.stage .canvas-wrap::before {
  /* foil shimmer line using existing ::before when present; fallback outline */
  outline: none;
}


/* gfxdense40 — bottom chrome lip */
.stage .canvas-wrap {
  border-bottom: 1px solid rgba(255, 210, 74, 0.12);
}
body.wetdrool-arena .stage .canvas-wrap {
  border-bottom-color: rgba(255, 126, 182, 0.14);
}


/* gfxdense41 — inset hairline frame */
.stage .canvas-wrap {
  outline: 1px solid rgba(255, 210, 74, 0.06);
  outline-offset: -3px;
}
body.wetdrool-arena .stage .canvas-wrap {
  outline-color: rgba(255, 126, 182, 0.08);
}


/* gfxdense42 — soft outer glow rim */
.stage .canvas-wrap {
  box-shadow:
    inset 0 0 72px rgba(0, 0, 0, 0.36),
    inset 0 0 0 1px rgba(255, 210, 74, 0.09),
    0 0 0 1px rgba(255, 210, 74, 0.06),
    0 0 24px rgba(255, 210, 74, 0.04);
}
body.wetdrool-arena .stage .canvas-wrap {
  box-shadow:
    inset 0 0 80px rgba(0, 0, 0, 0.42),
    inset 0 0 0 1px rgba(255, 126, 182, 0.1),
    0 0 0 1px rgba(255, 126, 182, 0.07),
    0 0 28px rgba(255, 126, 182, 0.05);
}


/* gfxdense43 — corner micro-brackets */
.stage .canvas-wrap {
  background-image:
    linear-gradient(rgba(255, 210, 74, 0.35), rgba(255, 210, 74, 0.35)),
    linear-gradient(rgba(255, 210, 74, 0.35), rgba(255, 210, 74, 0.35)),
    linear-gradient(rgba(255, 210, 74, 0.28), rgba(255, 210, 74, 0.28)),
    linear-gradient(rgba(255, 210, 74, 0.28), rgba(255, 210, 74, 0.28));
  background-size: 10px 1px, 1px 10px, 10px 1px, 1px 10px;
  background-position: 4px 4px, 4px 4px, calc(100% - 4px) calc(100% - 4px), calc(100% - 4px) calc(100% - 4px);
  background-repeat: no-repeat;
}
body.wetdrool-arena .stage .canvas-wrap {
  background-image:
    linear-gradient(rgba(255, 126, 182, 0.4), rgba(255, 126, 182, 0.4)),
    linear-gradient(rgba(255, 126, 182, 0.4), rgba(255, 126, 182, 0.4)),
    linear-gradient(rgba(255, 126, 182, 0.32), rgba(255, 126, 182, 0.32)),
    linear-gradient(rgba(255, 126, 182, 0.32), rgba(255, 126, 182, 0.32));
}

/* ── Mobile: put the game first ───────────────────────────────────────────
   Measured 2026-08-07 on a 375x812 phone: the document ran 9,143px tall and
   .canvas-wrap started at 7,126px — roughly nine screens of marketing before
   a player could reach the arena, with .hero-copy alone at 6,692px. The header
   added another 333px of chrome (160px of wrapped nav + a 94px radio dock)
   above the fold.

   .layout is a grid, and grid items honour `order`, so the game moves up
   without changing display or touching the desktop layout. The drop bar was
   moved out of .hero-copy in index.html so it travels with the arena — `order`
   cannot lift a child out of its parent. */
@media (max-width: 900px), (pointer: coarse) {
  .layout {
    padding-top: 14px;
    gap: 18px;
  }
  .layout > .drop-bar { order: -3; }
  .layout > .stage { order: -2; }
  .layout > #contest-mount { order: -1; }
  .layout > .hero-copy { order: 1; }

  /* One scrollable row of nav instead of three wrapped ones. Keeps the 44px
     touch target; only the wrapping goes. */
  .top {
    gap: 8px 12px;
    padding-top: max(8px, env(safe-area-inset-top, 0px));
    padding-bottom: 8px;
  }
  .top nav {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: 16px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 86%, transparent);
    mask-image: linear-gradient(90deg, #000 86%, transparent);
  }
  .top nav::-webkit-scrollbar { display: none; }
  .top nav a { white-space: nowrap; }
  .radio-dock { gap: 6px; }
  .radio-dock .music-btn {
    padding: 8px 10px;
    font-size: 10px;
    min-height: 44px;
  }
}

/* ── Mobile pass 2: get the UI off the play field ─────────────────────────
   Measured 2026-08-07 on a 375x812 phone, canvas 341x380:
     .io-touch (the pad cluster)   covered 26% of the canvas
     .io-touch__util alone          covered 11%, sitting mid-field
     .sa-chrome                     covered 7% AND double-drew the same
                                    title the canvas HUD already paints
     #fs-toggle                     clipped the canvas top-right corner
   The util pads are secondary actions (USE/BOND/DM/HIGH/GUN±) and had no
   business in the middle of the arena. They become a compact row hugging the
   bottom edge; combat stays thumb-right. */
@media (max-width: 900px), (pointer: coarse) {
  /* Stack the cluster along the bottom edge instead of up the right side.
     NOT full-width: the movement stick owns the bottom-left, and stretching
     this to the left edge put the util row on top of it (measured 52x23px on
     USE, 42x23px on BOND, 3x48px on FIRE, in BOTH orientations). Stay right,
     and cap the width so the row can never reach the stick. */
  .io-touch {
    left: auto;
    flex-direction: column-reverse;
    align-items: flex-end;
    gap: 6px;
    max-width: min(62vw, 340px);
  }
  /* Secondary actions: one flat row, small, hard against the bottom. */
  /* Secondary row lives ABOVE the combat cluster, right-hand side, so it can
     never share space with the bottom-left movement stick.

     `justify-content: flex-end` was the trap: when the row is wider than its
     box, flex-end overflow spills LEFT past the container — which is how USE
     and BOND ended up sitting on the stick even after the box was capped and
     right-aligned. flex-start keeps the content inside the box and lets it
     scroll instead. */
  /* thumbs1 — the top-left corner was free, but "free" is not "reachable".
     Measured mid-match on a 375x812 phone: stick x12-108/y684-772, combat
     y756-796, and this row sitting at y60-92 — the top of an 812px screen.
     USE is enter/exit vehicle, the most-pressed button in a driving game, and
     reaching it meant letting go of the stick and stretching to the far corner.
     That is the same defect as covering the play field, just quieter.

     Every previous attempt assumed this had to stay ONE ROW OF SIX, and on a
     375px bottom band a 6-wide row genuinely cannot clear the stick. A 3x2
     grid can: 3*48 + 2*8 = 160px, which fits the right-hand column with room
     to spare (stick ends at x108, the grid starts at x205).

     It also needs no fixed positioning or hand-computed offsets. The parent
     .io-touch is already `column-reverse` + `align-items: flex-end`, so a
     normal-flow child stacks directly ABOVE the combat cluster, hard right,
     and follows it automatically if either row ever changes height. */
  .io-touch__util {
    position: static;
    display: grid;
    grid-template-columns: repeat(3, 48px);
    grid-auto-rows: 48px;
    gap: 8px;
    justify-content: end;
    width: max-content;
    max-width: none;
    margin-bottom: 0;
    overflow: visible;
    /* .io-touch is column-reverse, so the FIRST flow child lands at the bottom.
       Rejoining the flow put the secondary pads under the resting thumb and
       pushed FIRE/MELT/DASH/RIDE above them — backwards. Measured: util
       y700-796, combat y644-684. Primary actions belong in the corner. */
    order: 1;
  }
  /* Square 48px pads clear the 44px touch-target floor these were 4px under
     (they were min-height:40px while min-width:44px — the width floor was set
     and the height floor was not). */
  .io-touch__util .io-pad {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    width: 48px;
    height: 48px;
    min-width: 48px;
    min-height: 48px;
    padding: 4px 2px;
    font-size: 9px;
    line-height: 1.05;
    letter-spacing: 0.02em;
    opacity: 0.9;
  }
  /* Primary combat stays a right-hand row under the thumb.
     `margin-left: 8px` was supposed to keep FIRE off the stick and did not:
     the row's natural width is 248px inside a 232px box, so it overflowed LEFT
     — the same flex-overflow trap that put the util row on the stick before.
     Measured after the grid landed: FIRE x108-164 vs stick x11-111, a 3x9px
     overlap of two live controls.
     Fixed-width columns instead of intrinsic sizing: 4*52 + 3*8 = 232px, which
     is exactly the box, so there is nothing left to spill. */
  .io-touch__combat {
    display: grid;
    grid-template-columns: repeat(4, 52px);
    grid-auto-rows: 48px;
    justify-content: end;
    gap: 8px;
    margin-left: 0;
    width: max-content;
  }
  .io-touch__combat .io-pad {
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    width: 52px;
    height: 48px;
    min-width: 52px;
    min-height: 48px;
    padding: 4px 2px;
    font-size: 10px;
    line-height: 1.05;
    letter-spacing: 0.02em;
  }

  /* The canvas already paints its own title rail — don't draw it twice. */
  .sa-chrome { display: none; }

  /* Keep the sticky header from parking on the canvas when you scroll to it.
     stickyfix1 — this rule lived here until thumbs1 closed this block early to
     open a 360px one, which swept it into that narrower block. It has been
     inert on every phone wider than 360px since. Measured at 375x812 with it
     inert: scrolling the game into view hid 162px of a 380px canvas (42.6%)
     behind a 163px sticky header.

     The offset is driven by --ddd-top-h, which io-main.js sets from the
     header's measured height, because the literal 96px it used to carry was
     already 67px short of the real header and would drift again the next time
     a nav row is added. Falls back to a conservative 176px if the JS has not
     run yet. */
  .canvas-wrap {
    scroll-margin-top: calc(var(--ddd-top-h, 176px) + 8px);
  }
}

/* A 4-wide combat row (232px) plus a 100px stick needs ~353px of bottom band.
   Measured on 320x568: FIRE overlapped the stick by 33px. Fold combat to 2x2
   (2*52 + 8 = 112px, clearing the stick by 87px) rather than shrinking the
   pads back under the 44px floor this pass exists to establish. */
@media (max-width: 360px) {
  .io-touch__combat {
    grid-template-columns: repeat(2, 52px);
  }
}

@media (max-width: 430px) {
  /* Team pick was wrapping 3 + 1, stranding RANDOM on its own row. */
  .mode-row.team-pick {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }
  .mode-row.team-pick .mode-btn {
    width: 100%;
    min-height: 48px;
  }
}

/* ── Landscape phone: keep the util row off the movement stick ────────────
   Measured 2026-08-07 at 812x375 mid-match: the stick pad sat at x11-123 /
   y231-343 and the util pad row at x11-63 / y320-360 — a real 52x23px overlap
   of two live controls, because both hug the left edge and a landscape phone
   is only 375px tall. Touching the bottom of the stick could fire a util pad.

   Portrait is unaffected: this only applies to short viewports.

   Gate note: the controls themselves are shown by `(max-width: 900px), (pointer:
   coarse)`. Gating this on `pointer: coarse` ALONE silently did nothing on a
   landscape phone viewport that reports a fine pointer — verified: the overlap
   was still 52x23px after that version shipped. Match the same or-shape. */
@media (max-height: 480px) and (max-width: 1024px), (pointer: coarse) and (max-height: 480px) {
  /* Tighter cap on a short screen — the stick sits higher relative to the row. */
  .io-touch { max-width: min(58vw, 300px); }
  /* Shorter screen — trim the grid to 44px squares so the two tiers still
     clear the bottom edge, but never below the 44px touch floor. */
  .io-touch__util {
    grid-template-columns: repeat(3, 44px);
    grid-auto-rows: 44px;
    gap: 6px;
  }
  .io-touch__util .io-pad {
    width: 44px;
    height: 44px;
    min-width: 44px;
    min-height: 44px;
    padding: 4px 2px;
  }
  .io-touch__combat .io-pad {
    /* Was `min-height: 44px` alone, and inert: thumbs1 gave the portrait rule
       an explicit `width: 52px; height: 48px`, so a lower min-height overrides
       nothing — measured 52x48 in landscape either way. Declared on both axes
       at the size that actually renders, so the floor is stated rather than
       inherited from a block three breakpoints away. */
    min-width: 52px;
    min-height: 48px;
  }
  /* A 112px stick eats a third of a 375px-tall screen. */
  .io-stick__pad {
    width: 92px;
    height: 92px;
  }
  .io-stick__knob {
    width: 38px;
    height: 38px;
    margin: -19px 0 0 -19px;
  }
  .io-stick__hint { display: none; }
}

/* ── Mobile pre-drop: show the game you are about to play ────────────────
   Measured 2026-08-07 on a 375x812 phone, lobby state: 553px of chrome sat
   above the canvas, so only 204 of its 380px were visible before dropping in.
     .top          163px
     .radio-now     45px  ("STANDBY" — pure noise when the radio is off)
     .drop-bar     345px  (hint 53 + team grid 104 + join 130)

   Live play is already fullscreen; this is purely the lobby. Desktop is
   untouched — everything here is inside the coarse/small-viewport query. */
@media (max-width: 900px), (pointer: coarse) {
  /* Now-playing strip earns its space only while something is playing. */
  .radio-now[data-on="false"] { display: none; }

  .drop-bar {
    padding-top: 10px;
    padding-bottom: 10px;
    gap: 8px;
  }
  .drop-bar__hint {
    margin: 0;
    font-size: 0.82rem;
    line-height: 1.35;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* Alias and the drop button share one row instead of stacking two. */
  .join {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: 8px;
  }
  .join label { margin: 0; min-width: 0; }
  .join .cta { min-height: 52px; white-space: nowrap; }

  .top { gap: 6px 12px; }
}

@media (max-width: 430px) {
  /* 2x2 team grid stays (it stopped RANDOM stranding on its own row) but the
     buttons do not need to be 48px tall to be a comfortable target. */
  .mode-row.team-pick { gap: 6px; }
  .mode-row.team-pick .mode-btn { min-height: 44px; }
}

/* ── mobilefold1: arena above the fold in the lobby ───────────────────────
   Computed from the DOM structure at 375px (estimates, not a live-phone
   measure): even after the pre-drop pass, the lobby still stacked ~163px of
   header (brand row + wrapped scrolling nav row + radio-dock row) plus a
   ~335px drop-bar (hype 2 lines ~46 + hint 2 lines ~46 + 2x2 team grid ~102
   + join row ~88 + padding) above the canvas — ~500px of chrome on an 812px
   screen before the arena.

   Fix, all inside the or-gate and appended LAST so it wins the cascade over
   the earlier 430px 2x2 team grid:

   1. Header becomes ONE horizontally-scrollable band (~57px): brand mark,
      then nav links, then the radio dock, all in a single nowrap row. Nothing
      is removed — prev/next/Player/Full are a swipe away, and fullscreen
      still has the on-canvas #fs-fab. The nav keeps its own gated
      overflow-x rule (navrow1 contract) but scrolls via the band here.
      --ddd-top-h re-measures automatically (ResizeObserver in io-main.js),
      so the canvas scroll-margin follows the slimmer header for free.
   2. Drop-bar: hype and hint each clamp to ONE line (full copy lives on in
      .hero-copy below the arena, untouched for desktop); team pick is a
      single row of four minmax(0,1fr) columns — every button still floors
      44px on BOTH axes; the DROP IN CTA keeps its 52px height.
   3. Contest banner condenses to a compact pill (it sits below the arena on
      mobile via order:1, so this is polish, not fold math).

   Desktop: zero change — everything is inside the or-gate. */
@media (max-width: 900px), (pointer: coarse) {
  .top,
  body.is-mobile-play .top {
    flex-wrap: nowrap;
    align-items: center;
    gap: 12px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-top: max(6px, env(safe-area-inset-top, 0px));
    padding-bottom: 6px;
    -webkit-mask-image: linear-gradient(90deg, #000 92%, transparent);
    mask-image: linear-gradient(90deg, #000 92%, transparent);
  }
  .top::-webkit-scrollbar { display: none; }
  .brand {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
  }
  .brand .sub { display: none; }
  .top nav {
    flex: 0 0 auto;
    width: max-content;
    margin-left: 0;
    overflow-x: visible;
    -webkit-mask-image: none;
    mask-image: none;
  }
  .radio-dock {
    width: max-content;
    flex: 0 0 auto;
    flex-wrap: nowrap;
    margin-left: 0;
  }

  /* Hook stays, as one line — the full Wonka/trials copy is in .hero-copy. */
  .drop-bar__hype {
    margin: 0 0 4px;
    font-size: 0.85rem;
    line-height: 1.35;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .drop-bar__hint {
    -webkit-line-clamp: 1;
  }
  /* One row of four. minmax(0,1fr), never bare 1fr (mobile blowout rule).
     At 375px each column is ~83px wide — both axes still floor at 44. */
  .mode-row.team-pick {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px;
  }
  .mode-row.team-pick .mode-btn {
    width: 100%;
    min-width: 44px;
    min-height: 44px;
    padding: 0 2px;
    font-size: 10px;
    letter-spacing: 0.04em;
  }

  /* Contest banner → compact pill (below the arena on mobile). */
  .contest-banner {
    margin-top: 12px;
    padding: 10px 12px;
    border-radius: 999px 12px 12px 999px;
  }
  .contest-banner h2 { margin: 0 0 4px; font-size: 0.95rem; }
  .contest-banner p { font-size: 0.8rem; }
}

/* ══════════════════════════════════════════════════════════════════════════
   immersive1 + hvskin1 — "full screen by default" and the drooly.ai shell
   ══════════════════════════════════════════════════════════════════════════

   WHAT "FULL SCREEN BY DEFAULT" HONESTLY MEANS HERE
   -------------------------------------------------
   A page cannot put itself in real fullscreen on load: the Fullscreen API
   requires a user activation, and every browser refuses without one. So this
   is two halves, and only one of them is CSS:

     (a) THIS FILE — the arena shell is sized to the viewport from first paint
         and the lobby controls OVERLAY it instead of stacking above it, so the
         player lands *in* the game with scrollY at 0. No API, nothing to
         refuse, works everywhere including iOS.
     (b) io-main.js — asks for real fullscreen once, on the DROP-IN gesture,
         which is a legal activation. If the browser says no (iOS Safari has no
         requestFullscreen for a <div>), (a) is already the layout and nothing
         moves.

   Before: at 375x812 the lobby stacked a header band (~57px) plus a ~335px
   drop-bar above a canvas that then got min(62dvh, 520px). The play surface
   started ~390px down an 812px screen. Now the canvas box IS
   calc(100dvh - header) and the drop-bar sits on top of its bottom edge.

   dvh, never vh: 100vh on a phone is the *large* viewport, so the bottom
   80-ish px of the arena would live under the URL bar.

   Appended last on purpose — these override the earlier mobile passes
   (mobilefold1 etc.) whose fold math assumed a stacked lobby. */

/* ── The page shell wears the homepage's skin ─────────────────────────── */
body.ddd-io {
  /* Maple dusk haze kept (it is the game's mood, and it reads over espresso),
     but the base is now the homepage's warm CRT foundation instead of an
     unrelated near-black. */
  background:
    radial-gradient(ellipse 55% 42% at 78% 8%, rgba(255, 150, 60, 0.20), transparent 55%),
    radial-gradient(ellipse 50% 38% at 12% 95%, rgba(46, 143, 181, 0.16), transparent 50%),
    radial-gradient(ellipse 40% 30% at 50% 40%, rgba(123, 79, 163, 0.10), transparent 60%),
    linear-gradient(180deg, var(--hv-espresso-3) 0%, var(--hv-espresso) 48%, var(--hv-espresso-2) 100%);
  background-attachment: fixed;
  color: var(--hv-ink);
}

.top {
  background: color-mix(in srgb, var(--hv-espresso) 86%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--hv-ink-mute) 24%, transparent);
}
/* The six-stripe spectrum band is the homepage's signature rule. One 3px line
   under the header ties the arena to drooly.ai without touching gameplay. */
.top::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 3px;
  background: var(--hv-band);
  opacity: 0.9;
  pointer-events: none;
}
.top nav a { color: var(--hv-ink-dim); }
.top nav a:hover { color: var(--hv-ink); }
.brand .sub { color: var(--hv-ink-mute); }
.music-btn {
  border-radius: var(--hv-round-pill);
  border-color: color-mix(in srgb, var(--hv-ink-mute) 32%, transparent);
  /* both axes, always — the mirror bug has shipped here three times */
  min-width: 44px;
  min-height: 44px;
}
.music-btn.ghost {
  color: var(--hv-ink-mute);
  min-width: 44px;
  min-height: 44px;
}

/* Panels below the arena: espresso surfaces, homepage radii, ink type. */
.side,
.mp-room,
.contest-banner,
.lore-card,
.cur-card,
.contest-panel {
  border-radius: var(--hv-round);
  background: color-mix(in srgb, var(--hv-espresso-2) 92%, transparent);
  border: 1px solid color-mix(in srgb, var(--hv-ink-mute) 20%, transparent);
}
.side { padding: 14px; }
.lede,
.fine,
.mp-room__lede,
.contest-banner p { color: var(--hv-ink-dim); }
.foot {
  border-top: 1px solid color-mix(in srgb, var(--hv-ink-mute) 20%, transparent);
  color: var(--hv-ink-mute);
}
.join input {
  border-radius: var(--hv-round-sm);
  background: color-mix(in srgb, var(--hv-espresso) 88%, transparent);
  border-color: color-mix(in srgb, var(--hv-ink-mute) 34%, transparent);
  color: var(--hv-ink);
  min-height: 48px;
  min-width: 44px;
}
.mode-btn { border-radius: var(--hv-round-sm); }
.cta {
  /* Gold stays: it is the Golden Drool Ticket, not decoration. Only the
     geometry and the ink colour move onto the design system. */
  border-radius: var(--hv-round-pill);
  color: var(--hv-ink-dark);
  min-height: 48px;
  min-width: 44px;
}

/* ── The arena owns the viewport ──────────────────────────────────────── */
/* Marketing copy is not deleted — it moves BELOW the play surface for every
   viewport (grid `order`, so the DOM keeps its reading/SEO sequence). */
.layout > .stage { order: -2; }
.layout > #contest-mount { order: -1; }
.layout > .hero-copy { order: 1; }

.canvas-wrap { border-radius: var(--hv-round); }
.play-stack .canvas-wrap {
  /* Desktop: fill what is left of the viewport under the header, minus the
     docked Maple Club, and never taller than the clamp. */
  height: clamp(420px, calc(100dvh - var(--ddd-top-h, 120px) - 240px), 820px);
  border-top-left-radius: var(--hv-round);
  border-top-right-radius: var(--hv-round);
}

/* ── The lobby panel is an overlay, not a stack ───────────────────────── */
.canvas-wrap > .drop-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 8;
  margin: 0;
  display: grid;
  gap: 8px;
  max-height: 68%;
  overflow-y: auto;
  padding:
    14px
    max(14px, env(safe-area-inset-right, 0px))
    max(14px, env(safe-area-inset-bottom, 0px))
    max(14px, env(safe-area-inset-left, 0px));
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--hv-espresso) 0%, transparent) 0%,
    color-mix(in srgb, var(--hv-espresso) 80%, transparent) 26%,
    color-mix(in srgb, var(--hv-espresso) 94%, transparent) 100%
  );
  backdrop-filter: blur(10px) saturate(1.1);
  -webkit-backdrop-filter: blur(10px) saturate(1.1);
  border-top: 1px solid color-mix(in srgb, var(--hv-ink-mute) 22%, transparent);
}
.canvas-wrap > .drop-bar .join { margin-top: 0; }
/* Measured in headless Chromium at 1440x900: the un-clamped panel was 269px on
   a 532px canvas — half the play surface. One line each; the full Wonka/trials
   copy is untouched in .hero-copy directly below the arena. */
.canvas-wrap > .drop-bar .drop-bar__hype,
.canvas-wrap > .drop-bar .drop-bar__hint {
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* The side rail's jump links were 36x18 and 101x18 in the same measurement —
   real, hit-tested, in-viewport tappables under the 44px floor. Both axes. */
.link-out {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  min-width: 44px;
}
/* Live match: the panel and the boot glass step off the play field. */
.canvas-wrap.is-playing > .drop-bar { display: none; }
/* Lobby: the thumb pads have nothing to drive yet and were sitting under the
   panel. They appear with the match. (3-class selector, so it beats the
   `.io-touch { display: flex }` inside the phone gate regardless of order.) */
.canvas-wrap:not(.is-playing) .io-touch,
.canvas-wrap:not(.is-playing) .io-stick { display: none; }

/* ── Phone / coarse pointer: full-bleed, header-height-aware ──────────── */
/* or-shaped gate, never (pointer: coarse) alone — a coarse-only gate shipped
   inert on a phone-sized viewport once already. */
@media (max-width: 900px), (pointer: coarse) {
  .layout {
    padding-top: 0;
    gap: 14px;
  }
  /* Cancel the mobilefold1 ordering: with the drop-bar now inside the shell
     there is no `.layout > .drop-bar` left to order, and the stage leads. */
  .layout > .stage {
    order: -2;
    /* full-bleed: undo .layout's horizontal padding for the play surface only */
    margin-inline: calc(-1 * max(16px, 3vw));
  }
  .play-stack .canvas-wrap {
    height: calc(100dvh - var(--ddd-top-h, 56px));
    min-height: calc(100dvh - var(--ddd-top-h, 56px));
    max-height: none;
    border-left-width: 0;
    border-right-width: 0;
    border-radius: 0;
  }
  /* The overlay's own gutters replace the old page-level ones. */
  .canvas-wrap > .drop-bar {
    margin-left: 0;
    margin-right: 0;
    gap: 6px;
    padding-top: 10px;
  }
  /* Four columns, minmax(0,1fr) — a bare 1fr is minmax(auto,1fr) and lets
     WOKENET force the track past 375px. Both axes floor at 44. */
  .canvas-wrap > .drop-bar .mode-row.team-pick {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px;
    margin: 0;
  }
  .canvas-wrap > .drop-bar .mode-row.team-pick .mode-btn {
    width: 100%;
    min-width: 44px;
    min-height: 44px;
    padding: 0 2px;
    font-size: 10px;
  }
  .canvas-wrap > .drop-bar .join {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: 8px;
    margin: 0;
  }
  .canvas-wrap > .drop-bar .join .cta {
    min-height: 52px;
    min-width: 44px;
    white-space: nowrap;
  }
  .canvas-wrap > .drop-bar .join input {
    min-height: 48px;
    min-width: 44px;
  }
}

/* Short landscape phones: the overlay must not swallow the arena. */
@media (max-height: 520px) and (max-width: 1024px),
       (pointer: coarse) and (max-height: 520px) {
  .canvas-wrap > .drop-bar { max-height: 78%; }
  .canvas-wrap > .drop-bar .drop-bar__hype,
  .canvas-wrap > .drop-bar .drop-bar__hint { display: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   fullbleed1 — CANVAS EDGE-TO-EDGE ON LOAD, ZERO CHROME
   ══════════════════════════════════════════════════════════════════════════

   MEASURED ON PROD (drooly.ai/games/ddd) AT 375x812, CDP + a real screenshot,
   not read off the source. What a phone actually got:

     header.top      375x57 — but its flex row ran to x=1394. "TOP 10 TICKETS"
                     ended mid-word at the viewport edge and the entire radio
                     dock (music, prev, next, Player, Full) lived off-screen
                     behind a mask fade. It read as broken, not swipeable.
     canvas          375x753 @ y58 — a header-shaped notch out of the top.
     .drop-bar__hype "🎟 10 golden tickets. One arena. Willy Wonka meet…"
     .drop-bar__hint "PICK YOUR TEAM. HORDE vs BUREAU vs WOKENET —…"
                     Both truncated by -webkit-line-clamp / text-overflow,
                     which is the mobilefold1 pass solving a height problem by
                     cutting sentences in half. Truncation is not a layout.
     #fs-fab         44x44 box containing the string "⛶ Full" (fullscreen.js
                     rewrites it via data-fs-short) — a two-line squashed box.
     the drop-bar    ~230px of hype + hint + team grid + alias row over the
                     bottom third of the play surface.

   THE CALL: full-bleed game, zero chrome. On load a player sees the arena at
   100dvw x 100dvh and one DROP IN control. Everything else is either below
   the fold or one tap behind the compact TEAM toggle. NOTHING is deleted —
   the header, nav, radio dock, hero copy, contest, lore, currency card, side
   rail and club all still ship on this page, they just no longer stand
   between a player and the game.

   Appended last so it wins the cascade over immersive1/mobilefold1, whose
   fold math assumed a header above the arena. */

/* ── The Options disclosure (the "behind a tap" half) ─────────────────── */
/* Collapsed is the SHIPPED state (data-open="false" is in the markup, so
   there is no first-paint flash of a panel that then folds away). */
.drop-bar[data-open="false"] > .drop-bar__more { display: none; }
.drop-bar[data-open="false"] .join__alias { display: none; }
.drop-bar__more {
  display: grid;
  gap: 8px;
  min-width: 0;
}
.drop-opts {
  /* both axes, always — the one-axis floor has shipped broken here 3 times */
  min-width: 64px;
  min-height: 48px;
  padding: 0 14px;
  border-radius: var(--hv-round-pill);
  border: 1px solid color-mix(in srgb, var(--hv-ink-mute) 44%, transparent);
  background: color-mix(in srgb, var(--hv-espresso) 78%, transparent);
  color: var(--hv-ink-dim);
  font: 800 11px/1 var(--mono);
  letter-spacing: 0.12em;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.drop-opts:hover { color: var(--hv-ink); border-color: var(--gold); }
.drop-opts:focus-visible { outline: 2px solid #5eead4; outline-offset: 2px; }
.drop-opts[aria-expanded="true"] {
  color: var(--hv-ink-dark);
  background: var(--hv-ink-dim);
  border-color: var(--hv-ink-dim);
}

/* ── Phone / coarse pointer: the arena IS the first screen ────────────── */
/* or-shaped gate, never (pointer: coarse) alone. */
@media (max-width: 900px), (pointer: coarse) {
  /* 1. The site header leaves the initial view. It is not deleted — body
        becomes a flex column and the header, now-playing strip and footer
        order AFTER <main>, so they are reachable by scrolling past the
        arena. `position: static` because a sticky header would otherwise
        ride back up over the play surface. */
  body.ddd-io {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
  }
  body.ddd-io > .skip { order: -1; }
  body.ddd-io > .layout { order: 0; }
  body.ddd-io > header.top { order: 1; position: static; top: auto; }
  body.ddd-io > .radio-now { order: 2; }
  body.ddd-io > .foot { order: 3; }

  /* 2. Nothing may sit between the top of the viewport and the canvas. */
  .layout {
    padding: 0 0 32px;
    gap: 0;
  }
  .layout > .stage { margin-inline: 0; }
  .layout > .hero-copy,
  .layout > #contest-mount {
    padding-inline: max(16px, 3vw);
    margin-top: 22px;
  }

  /* 3. 100dvw x 100dvh. --ddd-top-h is published as 0px by io-main.js on this
        gate (the header is no longer above the arena) and stays the measured
        header height on desktop, so the offset is still measured, never a
        literal. dvh, never vh. */
  .play-stack .canvas-wrap {
    height: calc(100dvh - var(--ddd-top-h, 0px));
    min-height: calc(100dvh - var(--ddd-top-h, 0px));
    max-height: none;
    width: 100dvw;
    max-width: 100dvw;
    margin: 0;
    border-width: 0;
    border-radius: 0;
    outline: 0;
  }
  .play-stack .canvas-wrap canvas,
  .canvas-wrap.is-fill canvas {
    display: block;
    width: 100%;
    height: 100%;
  }

  /* 4. NO TRUNCATION ANYWHERE. The clamps that produced "Willy Wonka meet…"
        are cancelled; the strings themselves were shortened in index.html so
        they fit at 375px without one. */
  .canvas-wrap > .drop-bar .drop-bar__hype,
  .canvas-wrap > .drop-bar .drop-bar__hint {
    display: block;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    -webkit-line-clamp: none;
    margin: 0;
  }

  /* 5. Collapsed = one DROP IN pill and one TEAM toggle, floating clean over
        the arena with no panel scrim behind them. */
  .canvas-wrap > .drop-bar {
    max-height: 82%;
    gap: 8px;
  }
  .canvas-wrap > .drop-bar[data-open="false"] {
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-top: 0;
    padding-top: 0;
    /* Clears the in-canvas ICEFAM ticker that paints along the bottom edge —
       measured at ~17px tall at 375x812. The pill used to sit on top of it and
       shear it in half. */
    padding-bottom: max(30px, calc(env(safe-area-inset-bottom, 0px) + 22px));
  }
  .canvas-wrap > .drop-bar .join {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: end;
    gap: 8px;
    margin: 0;
  }
  .canvas-wrap > .drop-bar[data-open="false"] .join {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .canvas-wrap > .drop-bar .join .cta {
    min-height: 52px;
    min-width: 44px;
    white-space: nowrap;
  }
  .canvas-wrap > .drop-bar .drop-opts {
    min-width: 64px;
    min-height: 52px;
  }

  /* 6. Pre-game: the ONLY things over the canvas are the two controls above.
        The fullscreen FAB and the SA chrome rail earn their place once a
        match is live; before that they are chrome on an empty lobby. */
  .canvas-wrap:not(.is-playing) .fs-fab,
  .canvas-wrap:not(.is-playing) .sa-chrome { display: none; }
}

/* Short landscape phones: keep the opened panel off the whole play field. */
@media (max-height: 520px) and (max-width: 1024px),
       (pointer: coarse) and (max-height: 520px) {
  .canvas-wrap > .drop-bar { max-height: 86%; }
  .canvas-wrap > .drop-bar[data-open="true"] .drop-bar__hype { display: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   maplecity1 — THE CHROME BELOW THE ARENA IS MAPLE CITY
   ══════════════════════════════════════════════════════════════════════════

   fullbleed1 took the chrome OFF the play surface. It did not make the chrome
   that survived look like the same product as the portal that links here.
   Measured on prod at 375x812 and 1440x900 (CDP, real screenshots) before this
   pass:

     .hero-copy    375x7043 — one 7,000px column of Space Grotesk body copy
                   with no section structure at all. At 1440 the same column
                   was 640px wide, hard left, with 670px of empty page beside
                   it. Two different sites, one hostname.
     header.top    11 mono links + a 5-button radio dock. At 375 the row ran
                   to x=1394 inside a masked scroller; at 1440 it wrapped to
                   two bands, 128px tall.
     the cards     four private design systems in one document — .contest-banner
                   6px radius, .lore-card 10px, .cur-card 16px (declared in an
                   inline <style> in the body), .mp-room 8px, .side 4px. Four
                   different border colours, four heading treatments, no serif
                   anywhere except one italic line.

   WHAT THIS ADOPTS FROM games/portal.css (read, not copied — that file is
   another lane's and is untouched):
     · EB Garamond as the display serif, Space Grotesk body, IBM Plex Mono for
       eyebrows/kickers/labels. Same three-role hierarchy.
     · the six-stripe spectrum as a RULE, not a decoration: under the header,
       across the arrival band, on the roof of the two kiosk-weight cards, and
       above the footer.
     · addressed buildings: every card carries a mono address kicker
       ("Reading room · Lore Library") the way each storefront on the street
       does. Same espresso-2 surface, same 18px radius, same hairline.
     · block labels with a trailing hairline, so the page reads as blocks on a
       street instead of one column of text.
     · the door: one paper-coloured pill, one ghost pill, both ≥56px.

   WHAT IT DELIBERATELY DOES NOT TOUCH: gameplay signal. --gold is the Golden
   Drool Ticket, the faction colours on .mode-btn--horde/--bureau/--wokenet are
   information, the mint peer colour means "someone else is here". Repainting
   those would delete meaning, so they stay.

   Appended last so it wins the cascade over hvskin1 and fullbleed1 without
   editing either — their measured rules are still the ones doing the work
   above the fold. */

:root {
  /* Same names, same values as home.css — see the hvskin1 note above. The
     contract test compares these declaration-for-declaration against home.css,
     so adding them here widens the drift net rather than dodging it. */
  --hv-shell: min(calc(100% - 40px), 1180px);
  --hv-serif: "EB Garamond", "Instrument Serif", Iowan Old Style, Georgia, "Times New Roman", serif;
  --hv-band-soft: linear-gradient(90deg,
    rgba(232, 85, 47, .9), rgba(242, 137, 44, .9), rgba(245, 194, 66, .9),
    rgba(79, 169, 107, .9), rgba(46, 143, 181, .9), rgba(123, 79, 163, .9));
  /* One accent for the whole below-fold surface. Cards differ by ADDRESS, not
     by colour — that is what stopped the old page reading as four products. */
  --dc-accent: var(--hv-s3);
  --dc-hair: color-mix(in srgb, var(--hv-ink-mute) 22%, transparent);
  --dc-hair-strong: color-mix(in srgb, var(--hv-ink-mute) 38%, transparent);
}

/* ── shared type ───────────────────────────────────────────────────────── */
.block-label {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: clamp(30px, 5vw, 54px) 0 14px;
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--hv-ink-mute);
}
.block-label::after {
  content: "";
  flex: 1 1 auto;
  height: 2px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--hv-ink-mute) 18%, transparent);
}
.hero-copy > .block-label:first-child { margin-top: 0; }

.band-rule {
  width: clamp(120px, 22vw, 240px);
  height: 10px;
  margin: 26px 0 0;
  border: 0;
  border-radius: var(--hv-round-pill);
  background: var(--hv-band);
}
.band-rule.is-full { width: 100%; height: 6px; margin: 0; }

/* ── the header wears the portal's header ──────────────────────────────── */
.top {
  gap: 10px 18px;
  background: color-mix(in srgb, var(--hv-espresso) 84%, transparent);
  border-bottom: 0;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  /* it is a link now (back to the city), so it takes the floor — both axes */
  min-height: 44px;
  min-width: 44px;
  text-decoration: none;
  color: var(--hv-ink);
  border-radius: var(--hv-round-pill);
}
.brand:hover { color: var(--hv-ink); }
.brand .brand-mark {
  width: 34px;
  height: 34px;
  flex: none;
  display: block;
}
.brand .brand-word { display: block; min-width: 0; }
.brand .mark {
  display: block;
  font-family: var(--sans);
  font-size: 1.12rem;
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--hv-ink);
  text-shadow: none;
}
.brand .mark i { font-style: normal; color: var(--hv-mint); }
.brand .sub {
  display: block;
  margin-top: 3px;
  font-family: var(--mono);
  font-size: 0.58rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--hv-ink-mute);
}
.top nav {
  gap: 4px 6px;
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.12em;
}
.top nav a {
  /* both axes, always — "Lore" measured 28x44 on prod once already */
  min-height: 44px;
  min-width: 44px;
  padding: 0 10px;
  border-radius: var(--hv-round-pill);
  color: var(--hv-ink-mute);
  transition: color 160ms ease, background-color 160ms ease;
}
.top nav a:hover { color: var(--hv-ink); background: rgba(248, 240, 226, 0.07); }
.top nav a[aria-current="page"] {
  color: var(--hv-ink);
  box-shadow: inset 0 0 0 2px rgba(248, 240, 226, 0.18);
}
.top nav a.is-adult { color: #d59ac0; }

/* The broadcast rail: dock + now-playing read as one strip under the band. */
.radio-dock { gap: 6px; margin-left: auto; }
.music-btn {
  min-height: 44px;
  min-width: 44px;
  border-radius: var(--hv-round-pill);
  border-color: var(--dc-hair);
  color: var(--hv-ink-mute);
  font: 600 11px var(--mono);
  letter-spacing: 0.1em;
}
.music-btn:hover { color: var(--hv-ink); border-color: var(--dc-hair-strong); }
.music-btn.ghost { min-height: 44px; min-width: 44px; color: var(--hv-ink-mute); }
.music-btn.radio-open { color: var(--hv-mint); border-color: color-mix(in srgb, var(--hv-mint) 30%, transparent); }
.radio-now {
  background: color-mix(in srgb, var(--hv-espresso-2) 88%, transparent);
  border-bottom: 1px solid var(--dc-hair);
  color: var(--hv-ink-mute);
  font-family: var(--mono);
}
.radio-now__badge {
  border-color: var(--dc-hair);
  color: var(--hv-ink-mute);
  background: color-mix(in srgb, var(--hv-espresso) 60%, transparent);
  border-radius: var(--hv-round-pill);
}

/* ── the arrival band ──────────────────────────────────────────────────── */
.layout > .city-gate { order: -1; }
.city-gate {
  display: grid;
  gap: 0;
  padding: clamp(26px, 4vw, 40px) 0 clamp(6px, 2vw, 14px);
}
.city-gate__eyebrow {
  margin: 22px 0 0;
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--hv-ink-mute);
}
.city-gate__title {
  margin: 12px 0 0;
  font-family: var(--hv-serif);
  font-weight: 600;
  font-size: clamp(1.9rem, 5vw, 3rem);
  line-height: 1.04;
  letter-spacing: -0.02em;
  color: var(--hv-ink);
  text-wrap: balance;
}
.city-gate__title em { font-style: italic; font-weight: 500; color: var(--dc-accent); }
.city-gate__lede {
  margin: 14px 0 0;
  max-width: 46ch;
  color: var(--hv-ink-dim);
  font-size: clamp(1rem, 1.6vw, 1.12rem);
  line-height: 1.5;
  text-wrap: pretty;
}
.city-gate__doors { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }

/* the door — the portal's primary/ghost pair, both axes over the floor */
.door {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 56px;
  min-width: 56px;
  padding: 0 26px;
  border: 2px solid transparent;
  border-radius: var(--hv-round-pill);
  background: var(--hv-paper);
  color: var(--hv-ink-dark);
  font: 700 1rem var(--sans);
  letter-spacing: -0.01em;
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: transform 180ms ease, background-color 180ms ease, box-shadow 180ms ease;
}
.door:hover {
  color: var(--hv-ink-dark);
  transform: translateY(-2px);
  background: var(--hv-mint);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.38);
}
.door:active { transform: translateY(0); }
.door.is-ghost {
  background: transparent;
  border-color: var(--dc-hair-strong);
  color: var(--hv-ink);
}
.door.is-ghost:hover {
  background: rgba(248, 240, 226, 0.1);
  border-color: rgba(248, 240, 226, 0.6);
  color: var(--hv-ink);
}
.door:focus-visible { outline: 3px solid var(--hv-mint); outline-offset: 4px; }

/* ── one card treatment for every panel below the arena ────────────────── */
/* .contest-panel is injected by leaderboard.js and .side lives in the stage —
   both are named explicitly so the treatment cannot miss them. */
.card,
.contest-banner,
.lore-card,
.cur-card,
.mp-room,
.contest-panel,
.side {
  position: relative;
  margin: 0;
  padding: clamp(18px, 2.6vw, 26px);
  border: 1px solid var(--dc-hair);
  border-radius: var(--hv-round);
  background: var(--hv-espresso-2);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.34);
  overflow: hidden;
}
/* the cornice: one hairline of accent along the roof of every card */
.card::before,
.contest-banner::before,
.lore-card::before,
.cur-card::before,
.mp-room::before,
.contest-panel::before,
.side::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: var(--dc-accent);
  opacity: 0.85;
  pointer-events: none;
}
/* the two kiosk-weight cards get the full spectrum instead of one stripe */
.card--band::before,
.contest-panel::before { background: var(--hv-band); opacity: 0.95; }

.card-kicker {
  display: block;
  margin: 0 0 8px;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--hv-ink-mute);
}
.contest-banner h2,
.lore-card h2,
.cur-card .cur-card__title,
.mp-room .mp-room__title,
.contest-panel h2 {
  margin: 0 0 10px;
  font-family: var(--hv-serif);
  font-weight: 600;
  font-size: clamp(1.35rem, 2.6vw, 1.9rem);
  line-height: 1.08;
  letter-spacing: -0.018em;
  text-transform: none;
  color: var(--hv-ink);
}
.contest-banner p,
.lore-card p,
.mp-room .mp-room__lede,
.contest-panel .contest-lede {
  color: var(--hv-ink-dim);
  font-size: 0.95rem;
  line-height: 1.6;
  max-width: 68ch;
}
.contest-banner strong,
.lore-card strong,
.mp-room strong { color: var(--hv-ink); }
.lore-card .lore-fine,
.contest-banner .contest-next,
.contest-panel .contest-fine {
  color: var(--hv-ink-mute);
  font-size: 0.82rem;
  line-height: 1.7;
}
.contest-banner .contest-next {
  margin-top: 12px;
  padding: 10px 0 10px 16px;
  border-left: 3px solid var(--hv-mint);
  font-family: var(--mono);
  font-size: 0.78rem;
}
.lore-card .lore-fine { margin-top: 14px; }
.contest-panel .contest-pill {
  border-radius: var(--hv-round-pill);
  background: var(--dc-accent);
  color: var(--hv-ink-dark);
  font-family: var(--mono);
}

/* ── the hero column ───────────────────────────────────────────────────── */
/* No 40rem cage: the copy shares the arena's own column, and each element
   carries its own measure instead. At 1440 the old cage left 670px of dead
   page beside a 640px text column. */
.hero-copy { max-width: none; }
.hero-copy .pill {
  /* the portal's eyebrow: a mono line, not a bordered chip */
  display: inline-flex;
  margin: 0 0 18px;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--hv-ink-mute);
}
.hero-copy .pill .dot {
  width: 7px;
  height: 7px;
  background: var(--hv-s1);
}
.hero-copy h1 {
  font-family: var(--hv-serif);
  font-weight: 600;
  font-size: clamp(2.6rem, 7.4vw, 5.2rem);
  line-height: 0.98;
  letter-spacing: -0.022em;
  color: var(--hv-ink);
  text-wrap: balance;
}
.hero-copy h1 em {
  font-family: var(--hv-serif);
  font-style: italic;
  font-weight: 500;
  color: var(--dc-accent);
}
.hook {
  margin: 26px 0 0;
  max-width: 46ch;
  font-size: clamp(1.06rem, 1.9vw, 1.32rem);
  line-height: 1.5;
  color: var(--hv-ink-dim);
  text-wrap: pretty;
}
.hook strong { color: var(--hv-ink); }
.trials-line {
  margin: 14px 0 0;
  max-width: 52ch;
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.8;
  color: var(--hv-ink-mute);
}
.trials-line strong { color: var(--hv-ink-dim); font-weight: 600; }
.trials-line em { font-family: var(--hv-serif); font-style: italic; color: var(--hv-mint); }
.lede {
  margin: 20px 0 0;
  max-width: 66ch;
  font-size: 1.02rem;
  line-height: 1.65;
  color: var(--hv-ink-dim);
  text-wrap: pretty;
}
.lede strong { color: var(--hv-ink); }
.tagline {
  margin: 18px 0 0;
  font-family: var(--hv-serif);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(1.2rem, 2.4vw, 1.6rem);
  color: var(--dc-accent);
}
.tagline em { color: var(--hv-mint); }
.fine {
  margin: 20px 0 0;
  max-width: 74ch;
  padding: 10px 0 10px 16px;
  border-left: 3px solid var(--hv-mint);
  font-family: var(--mono);
  font-size: 0.76rem;
  line-height: 1.7;
  color: var(--hv-ink-mute);
}
.fine strong { color: var(--hv-ink-dim); }
.status {
  margin: 16px 0 0;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.06em;
  color: var(--hv-ink-mute);
}

/* ── the systems board (was a 20-item bullet wall) ─────────────────────── */
/* Portal's .mc-board, applied without touching one word of the copy: the list
   items become cells on a hairline grid, the leading <strong> becomes the cell
   title. No bare Nfr track — minmax(0,1fr) only. */
.bullets {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
  gap: 1px;
  border-radius: var(--hv-round);
  overflow: hidden;
  background: var(--dc-hair);
  font-size: 0.88rem;
}
.bullets li {
  margin: 0;
  padding: 16px 18px;
  background: var(--hv-espresso-2);
  color: var(--hv-ink-mute);
  line-height: 1.6;
}
.bullets strong { color: var(--hv-ink); font-weight: 700; }
.bullets li > strong:first-child {
  display: block;
  margin-bottom: 4px;
  font-size: 0.95rem;
  letter-spacing: -0.01em;
}

/* ── the currency kiosk (the inline <style> that used to own this) ─────── */
.cur-card__lede {
  margin: 0 0 18px;
  max-width: 60ch;
  color: var(--hv-ink-dim);
  font-size: 0.95rem;
  line-height: 1.6;
}
.cur-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
  margin: 0;
}
.cur {
  margin: 0;
  padding: 16px 18px;
  border: 1px solid var(--dc-hair);
  border-radius: var(--hv-round-sm);
  background: color-mix(in srgb, var(--hv-espresso) 70%, transparent);
}
/* TIX green / DDD gold stay: which bag is which is information, not styling. */
.cur--tix { border-color: color-mix(in srgb, var(--hv-s4) 46%, transparent); }
.cur--ddd { border-color: color-mix(in srgb, var(--hv-s3) 46%, transparent); }
.cur__name {
  display: block;
  font-family: var(--sans);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.cur--tix .cur__name { color: var(--hv-s4); }
.cur--ddd .cur__name { color: var(--hv-s3); }
.cur__what {
  display: block;
  margin: 4px 0 10px;
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--hv-ink-mute);
}
.cur__list {
  margin: 0;
  padding-left: 1.1rem;
  font-size: 0.86rem;
  line-height: 1.65;
  color: var(--hv-ink-dim);
}
.cur__list strong { color: var(--hv-ink); }
.cur-card__both {
  margin: 18px 0 0;
  max-width: 68ch;
  color: var(--hv-ink-dim);
  font-size: 0.9rem;
  line-height: 1.6;
}
.cur-card__both strong { color: var(--hv-ink); }
.cur-card__fine {
  margin: 12px 0 0;
  max-width: 74ch;
  padding: 10px 0 10px 16px;
  border-left: 3px solid var(--hv-mint);
  font-family: var(--mono);
  font-size: 0.74rem;
  line-height: 1.7;
  color: var(--hv-ink-mute);
}

/* ── the radio promo reads as a notice, not a third button style ───────── */
.radio-promo {
  margin: 18px 0 0;
  padding: 16px 18px;
  border: 1px solid var(--dc-hair);
  border-radius: var(--hv-round);
  background: var(--hv-espresso-2);
  color: var(--hv-ink-dim);
  font-size: 0.92rem;
}
.radio-promo strong { color: var(--hv-ink); }
.radio-promo__btn {
  min-height: 48px;
  min-width: 48px;
  border-radius: var(--hv-round-pill);
  border-color: var(--dc-hair-strong);
  background: transparent;
  color: var(--hv-ink) !important;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
}
.radio-promo__btn:hover { background: rgba(248, 240, 226, 0.1); border-color: rgba(248, 240, 226, 0.6); }
.radio-promo__btn.ghost { color: var(--hv-mint) !important; border-color: color-mix(in srgb, var(--hv-mint) 34%, transparent); }
.radio-promo__meta {
  flex: 1 1 100%;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.7;
  color: var(--hv-ink-mute);
}

/* ── the side rail ─────────────────────────────────────────────────────── */
.side h2 {
  font-family: var(--mono);
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--hv-ink-mute);
}
.side .legend { color: var(--hv-ink-mute); }
.link-out {
  /* the portal's door, at rail scale — both axes, always */
  min-height: 48px;
  min-width: 48px;
  padding: 0 16px;
  margin-top: 8px;
  justify-content: space-between;
  gap: 10px;
  border-radius: var(--hv-round-sm);
  background: color-mix(in srgb, var(--hv-espresso) 70%, transparent);
  border: 1px solid var(--dc-hair);
  color: var(--hv-ink-dim);
  font: 600 0.86rem var(--sans);
  text-decoration: none;
  transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease;
}
.link-out:hover {
  background: var(--hv-paper);
  border-color: var(--hv-paper);
  color: var(--hv-ink-dark);
}

/* ── economy mounts: framed by the page, never restyled from here ──────── */
/* games/shared/economy/shop.css is another lane's file. All this does is give
   its panels the same vertical rhythm as the cards around them. */
.economy-mount { margin-top: 14px; }

/* ── the footer ────────────────────────────────────────────────────────── */
.foot {
  position: relative;
  margin-top: clamp(40px, 7vw, 80px);
  padding: clamp(34px, 5vw, 54px) 0 max(40px, calc(28px + env(safe-area-inset-bottom, 0px)));
  border-top: 0;
  background: var(--hv-espresso-2);
  text-align: left;
  color: var(--hv-ink-mute);
}
.foot::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 6px;
  background: var(--hv-band);
}
.foot-shell {
  width: var(--hv-shell);
  margin-inline: auto;
}
.foot-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin: 0 0 16px;
}
.foot-nav a {
  display: inline-flex;
  align-items: center;
  /* both axes — a footer link is a real tappable */
  min-height: 44px;
  min-width: 44px;
  padding: 0 12px;
  border-radius: var(--hv-round-pill);
  color: var(--hv-ink-dim);
  text-decoration: none;
  font-size: 0.88rem;
}
.foot-nav a:hover { color: var(--hv-ink); background: rgba(248, 240, 226, 0.07); }
.foot p { margin: 0 0 10px; font-size: 0.9rem; }
.foot .foot-fine { font-size: 0.78rem; line-height: 1.7; max-width: 78ch; color: var(--hv-ink-mute); }

/* ── the two controls that sit over live gameplay ──────────────────────── */
/* Portal button language — pill geometry, portal type, portal press feel —
   but still a HUD: the fill is the spectrum's own two warm stripes (which is
   what --gold already was), and there is no page-style hover lift over a
   moving canvas. */
.cta,
.canvas-wrap > .drop-bar .join .cta {
  min-height: 52px;
  min-width: 52px;
  padding: 0 26px;
  border: 0;
  border-radius: var(--hv-round-pill);
  background: linear-gradient(90deg, var(--hv-s3), var(--hv-s2));
  color: var(--hv-ink-dark);
  font: 700 0.96rem var(--sans);
  letter-spacing: -0.01em;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
  /* restrained motion: the old rule ran an infinite glow pulse over the arena */
  animation: none;
  transition: transform 140ms ease, filter 140ms ease;
}
.cta:hover { filter: brightness(1.05); animation: none; }
.cta:active { transform: translateY(1px); }
.cta:focus-visible { outline: 3px solid var(--hv-mint); outline-offset: 3px; }
.drop-opts,
.canvas-wrap > .drop-bar .drop-opts {
  min-height: 52px;
  min-width: 64px;
  border-radius: var(--hv-round-pill);
  border: 2px solid rgba(248, 240, 226, 0.34);
  background: color-mix(in srgb, var(--hv-espresso) 72%, transparent);
  color: var(--hv-ink);
  font: 700 11px/1 var(--mono);
  letter-spacing: 0.14em;
}
.drop-opts:hover {
  color: var(--hv-ink-dark);
  background: var(--hv-paper);
  border-color: var(--hv-paper);
}
.drop-opts[aria-expanded="true"] {
  color: var(--hv-ink-dark);
  background: var(--hv-paper);
  border-color: var(--hv-paper);
}

/* In-page anchors have to clear the sticky desktop header, or #lore lands
   underneath it. Measured header height, never a literal. */
:where(.lore-card, .cur-card, .contest-banner, .contest-panel, .city-gate)[id] {
  scroll-margin-top: calc(var(--ddd-top-h, 88px) + 16px);
}

/* ── responsive: width-first, or-shaped, never coarse alone ────────────── */
@media (max-width: 900px), (pointer: coarse) {
  :root { --hv-shell: min(calc(100% - 28px), 1180px); }
  .layout > .city-gate { padding-inline: max(16px, 3vw); margin-top: 18px; }
  .hero-copy h1 { font-size: clamp(2.3rem, 10.4vw, 3.2rem); }
  .block-label { margin-top: clamp(26px, 7vw, 40px); }
  .door { width: 100%; }
  .bullets { grid-template-columns: minmax(0, 1fr); }
  .foot { text-align: left; }
  .foot-shell { width: auto; padding-inline: max(16px, 3vw); }
  .cur-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 380px) {
  .hero-copy h1 { font-size: clamp(2rem, 9.6vw, 2.6rem); }
  .city-gate__title { font-size: clamp(1.7rem, 8vw, 2.2rem); }
  .top nav a { padding: 0 8px; }
}
@media (hover: none) {
  .door:hover { transform: none; background: var(--hv-paper); box-shadow: none; }
  .door.is-ghost:hover { background: transparent; }
  .link-out:hover {
    background: color-mix(in srgb, var(--hv-espresso) 70%, transparent);
    border-color: var(--dc-hair);
    color: var(--hv-ink-dim);
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-copy .pill .dot { animation: none; }
  .door, .link-out, .cta, .top nav a, .foot-nav a { transition: none !important; }
  .door:hover, .cta:active { transform: none; }
}
@media (prefers-contrast: more) {
  .lede, .hook, .bullets li, .contest-banner p, .lore-card p, .cur__list { color: var(--hv-ink); }
  .contest-banner, .lore-card, .cur-card, .mp-room, .contest-panel, .side {
    border-color: rgba(248, 240, 226, 0.4);
  }
}

/* ── maplecity1, second pass: what the screenshots showed ──────────────── */
/* Rendered at 375x812 and 1440x900 in headless Chromium against the local
   build before deploying, and read with eyes. Four things the assertions
   could not see: */

/* 1. The eyebrow. As an inline-flex row the 42-character address string wrapped
      at 375 and left "· DDD" hanging on its own line with the live dot floating
      beside the gap. As a block with an inline dot it wraps like prose. */
.hero-copy .pill {
  display: block;
  line-height: 1.75;
}
.hero-copy .pill .dot {
  display: inline-block;
  vertical-align: 0.06em;
  margin-right: 8px;
}

/* 2. My `.brand .sub` rule was unconditional and re-showed the sub-line that
      the phone band rule deliberately hides — the brand grew to 275px of a
      375px viewport inside a sideways scroller. The phone gate wins it back.
      Or-shaped, never coarse alone. */
@media (max-width: 900px), (pointer: coarse) {
  .brand .sub { display: none; }
  .brand .mark { font-size: 1.02rem; }
  .radio-dock { margin-left: 0; }
  .foot-nav { gap: 2px 6px; }
  .foot-nav a { padding: 0 10px; font-size: 0.84rem; }
}

/* 3. The sticky desktop header is 122px of translucent glass, so a whole card
      used to read through it while scrolling. More espresso, more blur — the
      band underneath still ties it to the portal. */
.top {
  background: color-mix(in srgb, var(--hv-espresso) 94%, transparent);
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
}
/* …and every in-page target clears it. --ddd-top-h is the measured header, so
   this tracks a two-row header on a 1280 screen and a one-row header on 1600
   without a literal anywhere. */
.hero-copy,
#hero-title,
.block-label { scroll-margin-top: calc(var(--ddd-top-h, 88px) + 16px); }

/* 4. The side rail's doors flowed inline, so "Hub →" and "Story Run →" shared
      a line while "Trials →" sat alone — a ragged three-line stack. One door
      per line, full rail width. */
.side .link-out { display: flex; width: 100%; }

/* ── maplecity1, third pass ────────────────────────────────────────────── */

/* 5. The systems board is a 19-cell grid, so the last row has empty cells. With
      the hairline colour as the grid's background those holes rendered as pale
      blocks. Tint the gutter toward espresso instead: the rules still read,
      the holes disappear. */
.bullets { background: color-mix(in srgb, var(--hv-ink-mute) 14%, var(--hv-espresso)); }

/* 6. The eyebrow is 42 characters. At .2em tracking it needs 410px and wrapped
      to "PINK · DDD" on a line of its own at 375. Tighter tracking fits the
      whole address on one line — shrinking the type beats orphaning a word,
      and beats cutting the string. */
@media (max-width: 900px), (pointer: coarse) {
  .hero-copy .pill { font-size: 0.62rem; letter-spacing: 0.12em; }
}

/* 7. The play surface was framed in 3px of saturated gold with a glow — the
      loudest edge on a page whose whole language is a 1px hairline. It is now
      the same card frame as every panel below it, so the arena reads as the
      building you are standing in. Gold stays where it means something (the
      ticket, the CTA, the focus ring below). The phone gate's
      `.play-stack .canvas-wrap { border-width: 0 }` is 2-class and still wins,
      so full-bleed is untouched. */
.canvas-wrap {
  border: 1px solid var(--dc-hair);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

/* ── maplecity1, fourth pass: two things only the prod screenshots showed ── */
@media (max-width: 900px), (pointer: coarse) {
  /* 8. On the phone gate the header is ordered to the BOTTOM of the page, so
        it lands 40px above the footer — and both were painting the six-stripe
        band. Measured at 375x812 on prod: two spectrum rules 40px apart with
        an empty strip between them (.radio-now is display:none down here),
        which reads as a rendering fault, not a design. Down here the footer
        owns the band and the two blocks become one bottom rail. */
  .top::after { display: none; }
  .foot { margin-top: 0; }

  /* 9. The header is a sideways scroller by contract (mobilefold1 — three
        wrapped rows were 163px of the fold). At a 30px fade "CITY HALL" still
        ended flush against the viewport edge and read as clipped text rather
        than as more-to-the-right. A wider fade is the difference between a
        scroller and a bug. */
  .top,
  body.is-mobile-play .top {
    -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 76%, transparent 100%);
    mask-image: linear-gradient(90deg, #000 0%, #000 76%, transparent 100%);
    padding-right: 28px;
  }
}

/* ── land1: short-landscape live controls reclaim play height ──────────────
   Portrait phone stack is util-over-combat (~148px tall at 3×2 + 4×1).
   On a 812×375 landscape that eats ~40% of the canvas. Put util + combat
   side-by-side (row, flex-end) so the cluster is only ~max(94,48) ≈ 94px —
   ~54px back to the play field. Same or-gate shape as landscape1. Both-axis
   floors stay ≥44. Leave pill stays centre-top (mchud2 war-chip handshake).
   Appended LAST so it wins the cascade over thumbs1 column stacking. */
@media (max-height: 480px) and (max-width: 1024px),
       (pointer: coarse) and (max-height: 480px) {
  .io-touch {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: flex-end;
    justify-content: flex-end;
    gap: 8px;
    max-width: min(72vw, 420px);
  }
  .io-touch__util {
    order: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(44px, 44px));
    grid-auto-rows: 44px;
    gap: 6px;
    max-width: none;
  }
  .io-touch__util .io-pad {
    min-width: 44px;
    min-height: 44px;
    width: 44px;
    height: 44px;
    padding: 0;
  }
  .io-touch__combat {
    order: 0;
    display: grid;
    grid-template-columns: repeat(4, minmax(52px, 52px));
    grid-auto-rows: 48px;
    gap: 8px;
    max-width: none;
  }
  .io-touch__combat .io-pad {
    min-width: 52px;
    min-height: 48px;
    width: 52px;
    height: 48px;
    padding: 0;
  }
  /* Stick trims 8px so the free middle gap (radar home) stays open. */
  .io-stick__pad {
    width: 84px;
    height: 84px;
    min-width: 84px;
    min-height: 84px;
  }
  .io-stick__knob {
    width: 40px;
    height: 40px;
    margin: -20px 0 0 -20px;
  }
}

/* ── art1: Maple City campaign plate ───────────────────────────────────
   The lobby gets one authored composition instead of asking a moving gameplay
   frame to double as campaign art. This layer is visual-only: the shared-layer
   canvas keeps booting underneath it, every control remains real HTML above it,
   and `.is-playing` reveals the canvas without changing game state. */
.arena-campaign {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
  isolation: isolate;
  background: #0a090b;
  opacity: 1;
  visibility: visible;
  transition: opacity 260ms ease, visibility 260ms ease;
}

.arena-campaign > img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: 50% 53%;
  filter: saturate(0.86) contrast(1.06) brightness(0.78);
}

.arena-campaign::before,
.arena-campaign::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.arena-campaign::before {
  background:
    linear-gradient(180deg, rgba(7, 7, 10, 0.48) 0%, transparent 31%, transparent 58%, rgba(8, 7, 7, 0.86) 100%),
    linear-gradient(90deg, rgba(8, 7, 7, 0.38), transparent 24%, transparent 76%, rgba(8, 7, 7, 0.38));
}

.arena-campaign::after {
  inset: 10px;
  border: 1px solid rgba(248, 240, 226, 0.2);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.5),
    inset 0 -120px 120px -100px rgba(255, 176, 77, 0.24);
}

.arena-campaign__ident {
  position: absolute;
  z-index: 2;
  top: clamp(20px, 4vw, 44px);
  left: clamp(20px, 4vw, 48px);
  display: grid;
  gap: 7px;
  max-width: min(62%, 520px);
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.88);
}

.arena-campaign__ident span {
  font: 600 clamp(2.25rem, 6.3vw, 5.25rem) / 0.86 var(--hv-serif);
  letter-spacing: -0.045em;
  color: var(--hv-paper);
}

.arena-campaign__ident small {
  font: 600 clamp(0.56rem, 1vw, 0.72rem) / 1.3 var(--mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--hv-mint);
}

.canvas-wrap.is-playing > .arena-campaign {
  opacity: 0;
  visibility: hidden;
}

@media (max-width: 900px), (pointer: coarse) {
  .arena-campaign > img {
    object-position: 50% 50%;
  }
  .arena-campaign::after {
    inset: 0;
    border-left: 0;
    border-right: 0;
  }
  .arena-campaign__ident {
    top: max(18px, env(safe-area-inset-top, 0px));
    left: max(16px, env(safe-area-inset-left, 0px));
    max-width: 72%;
  }
  .arena-campaign__ident span {
    font-size: clamp(2rem, 12vw, 3.3rem);
  }
}

@media (max-height: 480px) and (max-width: 1024px),
       (pointer: coarse) and (max-height: 480px) {
  .arena-campaign__ident {
    top: max(12px, env(safe-area-inset-top, 0px));
    left: max(14px, env(safe-area-inset-left, 0px));
  }
  .arena-campaign__ident span { font-size: clamp(1.7rem, 8vw, 2.7rem); }
  .arena-campaign__ident small { font-size: 0.54rem; }
}

@media (prefers-reduced-motion: reduce) {
  .arena-campaign { transition: none; }
}

@media (prefers-contrast: more) {
  .arena-campaign::before {
    background: linear-gradient(180deg, rgba(0, 0, 0, 0.64), transparent 42%, rgba(0, 0, 0, 0.9));
  }
  .arena-campaign__ident small { color: #bffef2; }
}

/* ── teamswitch1 — the always-reachable team control ────────────────────────
 *
 * The team strip used to live inside the collapsed #drop-more disclosure and
 * vanished entirely once a match went live. It now has two docks and moves
 * between them (games/ddd/team-switch.js); these rules style both.
 */

.team-dock {
  pointer-events: auto;
}

/* Lobby: always visible, directly above the DROP IN row. No disclosure, no
   "PICK YOUR TEAM." sentence — the four coloured chips ARE the instruction. */
.team-dock--lobby {
  display: block;
  margin: 0.25rem 0 0.15rem;
}
.team-dock--lobby .team-pick {
  display: flex;
  gap: 6px;
  flex-wrap: nowrap;
}
.team-dock--lobby .team-pick .mode-btn {
  /* Equal share of the bar, so four chips fit a 320px phone without wrapping
     and without min-width fighting flex-basis. */
  flex: 1 1 0;
  min-width: 0;
  padding: 6px 2px;
}

/* In-match: a single faction button, bottom-left, clear of the move stick
   (which sits bottom-left in .io-stick) — nudged up so the two never overlap. */
/* Placement: the RIGHT edge, vertically centred.
 *
 * Measured against the live phone HUD rather than guessed. Top-left is the
 * title band + faction score chip; below that are the health/sprint/weapon
 * bars. Top-centre is Leave, top-right is fullscreen, bottom-left is the move
 * stick and bottom-right is the combat pad cluster. The mid-right strip
 * between the DDD BAG panel and the radar is the only region with nothing in
 * it, so that is where the switcher lives — and the menu opens downward into
 * the same empty column. */
/* Same [hidden] trap as the menu below, one level up: this rule's `display`
 * outranks the UA `[hidden] { display:none }`, so the empty in-match dock
 * painted as a dark plate over the LOBBY. Caught on production, not in the
 * diff — which is the argument for looking at the page. */
.team-dock--match[hidden] {
  display: none;
}
.team-dock--match {
  position: absolute;
  right: max(10px, env(safe-area-inset-right));
  top: 42%;
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}

.team-fab {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-width: 52px;
  min-height: 52px;
  padding: 6px 8px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: rgba(0, 0, 0, 0.72);
  color: var(--ink);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  transition: transform 0.12s cubic-bezier(0.2, 0.8, 0.2, 1), border-color 0.12s ease, color 0.12s ease;
}
.team-fab:active { transform: scale(0.96); }
.team-fab:focus-visible {
  outline: 2px solid var(--gold, #ffd24a);
  outline-offset: 2px;
}
.team-fab__tag {
  font-family: var(--mono);
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.08em;
  opacity: 0.8;
}

/* The fab wears the team's colour, so "which side am I on" is answered by
   glancing at it — the thing the faction legend in the sidebar could never do
   because the sidebar is not on screen during a phone match. */
.team-fab[data-mode="horde"]   { color: #ff6a6a; border-color: rgba(255, 64, 64, 0.5); }
.team-fab[data-mode="bureau"]  { color: #ffd8a0; border-color: rgba(232, 165, 75, 0.5); }
.team-fab[data-mode="wokenet"] { color: #d8c0ff; border-color: rgba(168, 126, 255, 0.5); }
.team-fab[data-mode="random"]  { color: #5eead4; border-color: rgba(94, 234, 212, 0.5); }

/* The expanded menu stacks under the fab. Vertical, not horizontal: a
   horizontal strip here would run under the leave button at phone widths. */
/* `[hidden]` MUST win here. The UA sheet sets `display:none` on [hidden] at
 * specificity 0, so this rule's `display:flex` silently overrode it and the
 * menu rendered permanently open over the arena. Any styled element that is
 * also toggled with .hidden needs this guard. */
.team-dock--match .team-pick[hidden] {
  display: none;
}
.team-dock--match .team-pick {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 6px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: rgba(0, 0, 0, 0.82);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
  animation: team-menu-in 0.14s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.team-dock--match .team-pick .mode-btn {
  min-width: 82px;
  min-height: 44px;
  padding: 4px 8px;
}

@keyframes team-menu-in {
  from { opacity: 0; transform: translateY(-4px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .team-dock--match .team-pick { animation: none; }
  .team-fab { transition: none; }
}


/* ─────────────────────────────────────────────────────────────────────────────
   livestation1 — ICEFAM FM live-station chrome.

   The games stopped shipping their own jukebox and now play the real icefam.fm
   rotation, so the readout has two new jobs:

   1. Disclose. A track can be explicit or AI-assisted; both get a chip, always,
      never a tooltip you have to hunt for.
   2. Tell the truth about the station. The ON AIR badge is a claim that sound is
      coming out of the speakers. `data-state` splits the four cases the old
      two-state (on/off) markup could not say out loud — live, muted,
      tap-to-tune-in (browser autoplay block), and offline (station unreachable,
      game entirely unaffected) — and only `live` is allowed to pulse.

   No media gate here on purpose: these are unconditional invariants, and a gate
   is exactly how `stickyfix1` lost a rule for two cycles.
   ──────────────────────────────────────────────────────────────────────────── */
.radio-now__tags {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 6px;
}
.radio-now__tags[hidden] { display: none; }
.radio-now__tag {
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  padding: 4px 7px;
  border-radius: 999px;
  font: 700 9px/1 var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  background: rgba(0, 0, 0, 0.32);
  border: 1px solid color-mix(in srgb, var(--hv-ink-mute) 38%, transparent);
  color: var(--hv-ink-dim);
}
.radio-now__tag[data-kind="explicit"] {
  color: var(--hv-s1);
  border-color: color-mix(in srgb, var(--hv-s1) 55%, transparent);
}
.radio-now__tag[data-kind="ai"] {
  color: var(--hv-s5);
  border-color: color-mix(in srgb, var(--hv-s5) 55%, transparent);
}
/* Muted is not on air. Stop the pulse; the stream still runs in sync. */
.radio-now[data-state="muted"] .radio-now__badge,
.radio-now[data-state="gesture"] .radio-now__badge,
.radio-now[data-state="offline"] .radio-now__badge {
  animation: none;
  box-shadow: none;
}
.radio-now[data-state="gesture"] .radio-now__badge {
  color: var(--hv-ink-dark);
  background: var(--hv-s3);
  border-color: transparent;
}
.radio-now[data-state="offline"] { color: var(--hv-ink-mute); }
.radio-now[data-state="offline"] .radio-now__badge {
  color: var(--hv-ink-mute);
  background: rgba(0, 0, 0, 0.35);
  border-color: color-mix(in srgb, var(--hv-ink-mute) 40%, transparent);
}
/* 44px on BOTH axes — a one-axis floor has shipped broken here three times. */
.music-btn#radio-mute {
  min-width: 44px;
  min-height: 44px;
  font-size: 15px;
  padding: 0 10px;
}
.music-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
}
