/* Squirm.io — one stylesheet for the game chrome and the content pages.
   Keeping them together is deliberate: the HUD and the prose share a type
   scale and a palette, and two stylesheets is how they stop matching. */

/*
 * The display face. Self-hosted, latin subset, one variable file for every
 * weight — see assets/font/LICENCE.txt for why it is not a Google link.
 *
 * `swap` on purpose: the fallback is a rounded-ish system face, and a flash of
 * it beats a flash of nothing on the one screen people judge in a second.
 */
@font-face {
  font-family: "Fredoka";
  src: url("/font/fredoka.woff2?v=27d49d7c") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
/*
 * Lilita One — the lettering on every button (Gray picked the "Candy" set,
 * 10 Sep 2026). One weight, Latin subset, 10KB, self-hosted for the same two
 * reasons as Fredoka above: portals reject third-party requests, and a button
 * that falls back to Trebuchet mid-load looks broken.
 */
@font-face {
  font-family: "Lilita One";
  src: url("/font/lilita-one.woff2?v=d2575d4f") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* Orbitron — the Overdrive theme's lettering (29 Sep 2026). One weight, Latin
   subset, 7KB, self-hosted like the others. Loaded only when a rule asks for
   it, which is only under [data-vibe="overdrive"]. */
@font-face {
  font-family: "Orbitron";
  src: url("/font/orbitron-800.woff2?v=e2591ec1") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* The front screen's drop-downs: every panel sits ABOVE every button that opens one. The language
     flag had its own z-index (31), above More's panel (20), so on an upright phone, where the flag
     sits beside the logo, it was drawn over More's top corner (Gray, 18 Oct 2026). One pair of
     layers, not a number per piece: a new nav button or panel takes one of these. */
  --z-navbutton: 10;
  --z-navpanel: 20;
  /*
   * Near-black and lime.
   *
   * The palette was navy and mint, which is calm — and calm is the wrong note
   * for an arcade game about being eaten. Lime on near-black is the genre's
   * own language, it survives being scaled down to a portal thumbnail, and it
   * is the one colour that cannot be confused with slither.io's blue.
   */
  --ink: #07090F;
  --ink-2: #10141F;
  --ink-3: #1A2030;
  --line: #262E42;
  --text: #F2F5FA;
  --text-dim: #9AA6BE;
  --accent: #FF4FAD;
  --accent-2: #D11F7C;
  --accent-ink: #FFFFFF;
  --hot: #FF3B5C;
  --warn: #FFC53D;
  /*
   * Rounder. Much rounder.
   *
   * "no cooperate looking squared sharp business look" — 16px on a 560px panel
   * reads as a business card. Games in this field run 20-28px on panels and
   * fully rounded pills on controls, and the softness is most of what separates
   * "fun" from "enterprise dashboard".
   */
  --radius: 24px;
  --radius-sm: 14px;
  --radius-pill: 999px;

  /*
   * A real colour system, not one accent on grey.
   *
   * Gray, looking at Shell Shockers: "more colour, bright colours, thats what
   * kids want". The lime stays the primary — it is the brand and it survives a
   * portal thumbnail — but a single accent on near-black gives every control
   * the same voice, so nothing reads as more or less important than anything
   * else, and none of it reads as playful.
   *
   * Four more, each with ONE job, so colour means something:
   *   sky    the surface a panel is made of, and the calm default
   *   grape  a second category, for what is yours (skins, progress)
   *   tang   attention: the selected thing, the thing to press next
   *   berry  destructive, and only that
   * Each has a `-deep` for the extruded bottom lip that makes a button look
   * like an object you can push instead of a rectangle with a label in it.
   */
  --sky: #2C8FD6;
  --sky-deep: #1D6BA8;
  --sky-soft: #57ADEC;
  --grape: #8B5CF6;
  --grape-deep: #6D33E0;
  --tang: #FF9F1C;
  --tang-deep: #D97B00;
  --berry: #FF3B5C;
  --berry-deep: #C8102E;
  --lime-deep: #8CBB0F;
  /* White, for the pill inputs and the panel copy that sits on colour. */
  --paper: #FFFFFF;
  --paper-dim: rgba(255, 255, 255, 0.78);
  /*
   * The reading measure, as a fixed length rather than ch.
   *
   * It was 74ch, and ch is relative to the element's OWN font size — so the
   * same variable gave a 46px h1 a max-width of ~1850px and a 17px paragraph
   * ~790px. The heading was effectively unconstrained, which is why it ran the
   * full width of the viewport above a much narrower column.
   */
  --max: 46rem;
  /* The game panel is a picture, not prose — it gets more room than the text. */
  --game-max: 1180px;
  /* The header is sticky and the arena sits directly under it, so its height
     is a layout constant rather than a guess. Keep it in step with the
     header's own padding and wordmark height. */
  --header-h: 68px;
  /* The boost button's size and inset, bottom right. The sound dock lives on the
     phone, so both need to read from one place. */
  /* The dock's corner: its own width plus the inset either side. The menu
     overlay keeps its panel out of this lane. */
  /* The dock's own footprint. No longer reserved as a lane — see .overlay —
     but check-layout uses it to assert the panel clears the dock. */
  --dock-lane: 148px;
  /* The boost button's footprint, bottom RIGHT. Nothing else may claim that
     corner during a run. */
  --boost-size: 96px;
  --boost-inset: 24px;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /*
   * A real display face, self-hosted.
   *
   * It was "Trebuchet MS" — a system font from 1996 that is the single biggest
   * reason this looked like a business tool rather than a game. Gray: "no
   * cooperate looking squared sharp business look". Fredoka is the soft
   * geometric round the WORDMARK is already drawn in, so the lettering and the
   * interface finally speak with one voice.
   *
   * Self-hosted rather than linked: a portal build must carry no third-party
   * resources at all, and a webfont fetched from Google is a request that can
   * fail and leave the game wearing Trebuchet anyway. 29KB, latin subset,
   * variable weight — see assets/font/LICENCE.txt.
   */
  --font-display: "Fredoka", "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
}

* { box-sizing: border-box; }

/* The overlays and boost button all set `display`, which beats the
   user-agent rule for [hidden] regardless of specificity — author styles always
   win over UA styles. Without this, every "hidden" panel renders on load. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.65;
  overflow-x: hidden;
}

img, svg, canvas { max-width: 100%; }

/*
 * Zero specificity, like every container rule below it.
 *
 * The container link rules were wrapped in :where() so a component class
 * always beats them — which is what fixed the invisible call-to-action
 * buttons. But a bare `a` scores (0,0,1), and (0,0,1) beats (0,0,0): the
 * global rule then won against every wrapped container rule and repainted
 * those links in the accent colour. The settings tab you are on was accent
 * text on an accent background, a blank lime pill. Gray spotted it on a
 * phone.
 *
 * With everything at zero, source order decides among these rules — global
 * first, containers after — and any component class still wins by existing.
 */
:where(a) { color: var(--accent); text-underline-offset: 3px; }
:where(a:hover) { color: var(--accent-2); }

.visually-hidden{
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link{
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: #06210F; padding: 10px 16px; border-radius: 0 0 var(--radius-sm) 0;
  font-weight: 700;
}
.skip-link:focus{ left: 0; }

:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* ------------------------------------------------------------ site chrome */
/* ------------------------------------------------------------- the header */
/*
 * The bar the genre actually uses, one notch up.
 *
 * Checked against the field before rebuilding it: the pure-game sites
 * (slither.io, snake.io, wormax.io, agar.io, diep.io) ship no header at all —
 * the game is the page. The ones that carry content pages, as this site does,
 * all run the same bar: chunky logo left, short uppercase nav, and a loud way
 * back into the game. Little Big Snake leads with PLAY NOW; Worms Zone leads
 * with Home. So the shape here is settled convention, and the polish is ours:
 * a sticky glass bar over the arena, display type at nav size, and one lime
 * accent spent on the two things that matter — where you are, and how to play.
 */
/* Same bar as the settings page's, so the two never read as two sites. */
.siteheader{ position: sticky; top: 0; z-index: 30; background: linear-gradient(180deg, #2b2366, #1c1650); border-bottom: 3px solid #fff; box-shadow: 0 2px 0 #7a2a6a, 0 10px 24px -12px rgba(0, 0, 0, 0.6); padding-top: var(--safe-t); }
@supports (backdrop-filter: blur(1px)) {
  .siteheader{ background: linear-gradient(180deg, rgba(43, 35, 102, 0.94), rgba(28, 22, 80, 0.94)); backdrop-filter: blur(14px); }
}
.siteheader__inner{
  display: flex; align-items: center; gap: 10px clamp(14px, 3vw, 30px); flex-wrap: wrap;
  /*
   * Centred on the same column as the game panel below it. The header used to
   * be full-bleed with its contents pinned to the left, so on a wide screen the
   * wordmark sat alone with 1,380px of empty bar beside it while everything
   * underneath was centred — which is what "it is all to the left" was.
   */
  /*
   * The bar spans the window, because the arena under it now does too. It used
   * to be capped at the game box's 1180px so the two lined up; with a
   * full-bleed arena that cap left the wordmark floating in the middle of
   * nothing. Capped well wide so it does not stretch on an ultrawide.
   */
  width: min(1600px, 100%);
  margin-inline: auto;
  padding: 12px clamp(16px, 3vw, 32px);
}
.siteheader__brand{ text-decoration: none; flex: 0 0 auto; display: block; }
.wordmark{
  /* The real wordmark, sized by height so the aspect ratio does the rest. */
  display: block;
  height: 40px;
  width: auto;
  transition: transform 0.16s ease;
}
.siteheader__brand:hover .wordmark{ transform: translateY(-1px) scale(1.02); }

.siteheader__nav{ flex: 1 1 auto; min-width: 0; }
.siteheader__nav ul{
  display: flex; gap: 2px clamp(4px, 1.4vw, 14px); list-style: none; margin: 0; padding: 0;
  /* Five items on a phone beat a hidden menu: a tap you cannot see is a tap
     you do not make, and this is the whole internal link graph. */
  overflow-x: auto; scrollbar-width: none;
}
.siteheader__nav ul::-webkit-scrollbar{ display: none; }
:where(.siteheader__nav) :where(li){ flex: 0 0 auto; }
:where(.siteheader__nav) :where(a){
  position: relative; display: block; padding: 8px 10px 9px;
  font-family: var(--font-display); font-weight: 700;
  font-size: 0.86rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-dim); text-decoration: none; white-space: nowrap;
  transition: color 0.14s ease;
}
/* The underline is a scanline: lit under where you are, sweeping in on hover. */
:where(.siteheader__nav) :where(a::after){
  content: ""; position: absolute; left: 10px; right: 10px; bottom: 2px; height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--accent) 22%, var(--accent) 78%, transparent);
  transform: scaleX(0); transform-origin: left; transition: transform 0.18s ease;
}
:where(.siteheader__nav) :where(a:hover){ color: var(--text); }
:where(.siteheader__nav) :where(a:hover::after){ transform: scaleX(1); }
:where(.siteheader__nav) :where(a[aria-current="page"]){ color: var(--text); }
:where(.siteheader__nav) :where(a[aria-current="page"]::after){ transform: scaleX(1); }

.siteheader__cta{ flex: 0 0 auto; display: inline-block; padding: 8px 18px 10px; border-radius: 999px; text-decoration: none; font-size: 1rem; letter-spacing: 0.02em; --rim: 4px; }
@media (hover: hover){ .siteheader__cta:hover{ filter: brightness(1.07); } }

@media (max-width: 640px) {
  :root { --header-h: 64px; }
  .wordmark{ height: 32px; }
  /*
   * On a page that carries the game there is no Play button in the bar, so the
   * links fit beside the wordmark and the header stays one row — 56px instead
   * of 102px, which is 46px of arena back on a phone.
   */
  .siteheader__inner:not(:has(.siteheader__cta)){
    /* nowrap is the whole trick: with wrapping on, a flex item wider than the
       space left is moved to its own line *before* shrinking is considered, so
       min-width and flex-shrink never got a say and the nav always took a
       second row. */
    flex-wrap: nowrap;
  }
  .siteheader__inner:not(:has(.siteheader__cta)) .siteheader__nav{
    order: 0; flex: 1 1 0; min-width: 0;
  }
  /* The sideways-scrolling row is gone on a phone: the burger holds the
     same links, and a row that hid most of the site behind a swipe was the
     problem being fixed. */
  .siteheader__nav{ display: none; }
  .siteheader__inner{ flex-wrap: nowrap; }
  .siteheader__cog{ display: inline-grid; }
  .siteheader__menu{ display: block; }
  .siteheader__inner:not(:has(.siteheader__cta)) .siteheader__cog{ margin-left: auto; }
  :where(.siteheader__nav) :where(a){ font-size: 0.8rem; padding: 6px 8px 8px; }
  .siteheader__cta{ margin-left: auto; padding: 9px 16px 10px; font-size: 0.82rem; }
}
/*
 * Icon buttons and the phone menu. Hidden on anything wider than a phone,
 * where the full nav row fits and a burger would hide it for no reason.
 */
.iconbtn{ display: inline-grid; place-items: center; flex: 0 0 auto; width: 46px; height: 46px; padding: 0; border-radius: 15px; cursor: pointer; list-style: none; text-decoration: none; --rim: 4px; }
.iconbtn::-webkit-details-marker{ display: none; }
@media (hover: hover){ .iconbtn:hover{ filter: brightness(1.08); } }
.iconbtn:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.icon{ width: 20px; height: 20px; display: block; }
/*
 * The dropdown hangs off its OWN button. It used to be positioned against the
 * nearest positioned ancestor — the header on inner pages, the rail on phones —
 * and on the desktop front screen that was the whole-screen overlay, so the
 * panel opened below the bottom of the window: the button turned into an X and
 * nothing appeared (found on the live site straight after deploy, 10 Sep 2026).
 */
.navmenu{ position: relative; }
.menu__menu{ z-index: 30; }
.navmenu .icon--close{ display: none; }
.navmenu[open] .icon--close{ display: block; }
.navmenu[open] .icon--burger{ display: none; }
.navmenu[open] > .iconbtn{ filter: brightness(0.92) saturate(1.1); transform: scale(0.96); }
/*
 * Hidden above the phone breakpoints — and ONLY there, by media query.
 *
 * These were unconditional `display: none` rules, which put the answer in the
 * hands of source order: the phone rules that show the cog and menu live in
 * earlier @media blocks at the same specificity, so the later "hide" won on
 * phones too. The content pages shipped with no cog and no menu at all; the
 * homepage only escaped because its phone block happens to come after this.
 * Scoping the hide to the widths where it applies means order cannot decide.
 */
@media (min-width: 641px) {
  .siteheader__cog, .siteheader__menu{ display: none; }
}
.menu__menupanel{ max-height: min(72dvh, calc(100dvh - 140px)); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.35) transparent; }
.navmenu__group{
  margin: 14px 4px 2px;
  font-family: var(--font-display); font-weight: 700; font-size: 0.66rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent);
}
.navmenu__group:first-child{ margin-top: 6px; }
.navmenu__sound{
  appearance: none; -webkit-appearance: none; display: block; width: 100%;
  padding: 13px 4px; text-align: left; cursor: pointer;
  background: none; border: 0; border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  font: inherit; font-family: var(--font-display); font-weight: 700; font-size: 1.02rem;
  color: var(--text);
}
@media (hover: hover){ .navmenu__sound:hover{ color: var(--accent); } }
.navmenu__panel{ z-index: var(--z-navpanel); position: absolute; top: calc(100% + 10px); right: 0; left: auto; width: min(360px, calc(100vw - 24px)); padding: 8px 18px 14px; border-radius: 22px; background: linear-gradient(180deg, #2b2366, #1c1650); border: 4px solid #fff; box-shadow: 0 0 0 2px #7a2a6a, 0 20px 44px -12px rgba(0, 0, 0, 0.65); }
.navmenu__panel ul{ list-style: none; margin: 0; padding: 0; display: grid; }
:where(.navmenu__panel) :where(a){ display: block; padding: 12px 4px; font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: #fff; text-decoration: none; border-bottom: 1px solid rgba(255, 255, 255, 0.1); }
:where(.navmenu__panel) :where(a:hover){ color: var(--accent); }
:where(.navmenu__panel) :where(a[aria-current="page"]){ color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .siteheader__nav a::after, .wordmark, .siteheader__cta { transition: none; }
}

/*
 * The footer and breadcrumb share the header's column. Without this they run
 * full-bleed and pin left while everything between them is centred — the
 * "shit is all to the left" that started the layout rework.
 */
.sitefooter > *,
.breadcrumb > *{
  width: min(var(--game-max), 100%);
  margin-inline: auto;
}

.breadcrumb{ padding: 14px clamp(16px, 4vw, 40px) 0; }
:where(.breadcrumb) :where(a, li) { color: var(--paper-dim); }
.breadcrumb ol{
  /*
   * margin-block, never the `margin` shorthand. The shorthand also sets
   * margin-inline, which silently cancelled the `margin-inline: auto` above
   * and pinned the breadcrumb to the far left of a 1512px screen while
   * everything else on the page was centred. Same specificity, written later,
   * so it won — and the centring rule was still sitting right there in the
   * file looking correct.
   */
  display: flex; gap: 8px; list-style: none; margin-block: 0; padding: 0;
  font-size: 0.87rem; color: var(--text-dim);
}
:where(.breadcrumb) :where(li + li::before){ content: "/"; margin-right: 8px; opacity: 0.5; }

main { padding: 22px clamp(16px, 4vw, 40px) 64px; }

/*
 * Centre everything in <main> on the reading measure.
 *
 * `main` had padding and nothing else, so on a wide screen the text column sat
 * hard against the left with 600px of dead space beside it — and worse, only
 * `.prose` and `.lede` were capped, so an h1 ran the full 1345px above a 792px
 * paragraph. A heading four times wider than the text it introduces reads as a
 * broken layout, not a design.
 *
 * Applied to every child rather than a list of block classes, so a block added
 * later cannot quietly escape the measure — which is exactly how the h1 and the
 * CTA came to be outside it.
 */
main > * {
  max-width: var(--max);
  margin-inline: auto;
}

/*
 * The arena is not the exception to the measure — it is not on the page at all.
 *
 * Checked against twenty snake games before rebuilding this. The ones that are
 * a *game* — slither.io, wormate.io, wormax.io, gulper.io, mope.io, splix.io —
 * all do the same thing: the arena fills the window, and a single centred
 * column floats on top of it carrying a wordmark, a nickname and a Play button.
 * Nothing else is above the fold. The ones that wrap the game in a document are
 * the ones whose real product is an app-store listing (worms.zone,
 * littlebigsnake.com, snake.io), and they lead with a download button.
 *
 * Squirm.io was doing neither: a 16:9 letterboxed box centred in a 1180px
 * column with an article under it and grey rails either side, which reads as a
 * blog post with a game widget in it. The arena now breaks out of `main` and
 * fills the viewport under the header, and the prose starts below the fold
 * where it belongs — still in the document, still indexed, just not competing
 * with the thing people came for.
 */
main > .game{
  /* Break out of main's padding and the reading measure both. */
  width: 100vw;
  max-width: none;
  margin-left: 50%;
  transform: translateX(-50vw);
  border-radius: 0;
  border-inline: 0;
  aspect-ratio: auto;
  height: calc(100dvh - var(--header-h));
  max-height: none;
  min-height: 420px;
}
.page--home main{ padding-top: 0; }

/* ------------------------------------------------------------- the menu
 *
 * The front screen is the PAGE, not a card on it.
 *
 * "we dont need that box in the middle man thats one of the main issues nobody
 * does that" — and nobody does: slither.io, wormate.io, wormax.io, gulper.io
 * and littlebigsnake all put the name field and Play straight onto the arena
 * with everything else pinned to the edges. A 430px panel with its own
 * background, border and scrollbar was the one thing making this look like a
 * game embedded in a website rather than a game.
 *
 * Three rows: a top rail, the middle, a bottom rail — plus a footer line. Grid
 * rather than absolute positioning, so the middle is centred in what is
 * actually left over rather than in the whole viewport.
 */
.overlay--menu{
  display: block; padding: 0;
  opacity: 1; transition: opacity 0.26s ease;
  background:
    linear-gradient(to bottom, rgba(12, 22, 58, 0.62) 0%, rgba(12, 22, 58, 0) 18%),
    linear-gradient(to top, rgba(12, 22, 58, 0.66) 0%, rgba(12, 22, 58, 0) 22%),
    radial-gradient(ellipse 120% 90% at 50% 45%, rgba(12, 22, 58, 0) 55%, rgba(12, 22, 58, 0.34) 100%);
}
.overlay--menu.is-out{ opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .overlay--menu{ transition: none; }
}
/*
 * The beta strip, above the menu.
 *
 * Only while it is showing does the overlay become a column, so that the menu
 * gives up exactly the strip's height and not a pixel more. Dismissed, none of
 * these rules match and the menu is precisely what it was before the strip
 * existed — its phone layout took three rounds to get right and is not
 * disturbed here.
 *
 * The strip carries the notch inset; the menu then must not pad for it again,
 * or an iPhone gets the safe area twice.
 */
.overlay--menu:has(> .beta:not([hidden])){ display: flex; flex-direction: column; }
.overlay--menu:has(> .beta:not([hidden])) > .menu{
  flex: 1 1 auto; min-height: 0; height: auto; padding-top: 10px;
}
.beta{
  align-self: center; display: flex; align-items: center; justify-content: center; gap: 10px;
  margin: calc(8px + var(--safe-t)) auto 0; max-width: calc(100% - 24px);
  padding: 5px 6px 5px 8px; border-radius: 999px;
  background: rgba(20, 16, 60, 0.62);
  font-size: 0.8rem; color: #fff;
}
@supports (backdrop-filter: blur(1px)) {
  .beta{ background: rgba(20, 16, 60, 0.46); backdrop-filter: blur(10px); }
}
.beta__text{ margin: 0; min-width: 0; line-height: 1.35; }
.beta__tag{
  display: inline-block; margin-right: 8px; padding: 2px 9px 3px; vertical-align: 1px;
  border-radius: var(--radius-pill); background: #ff4fad; color: #fff;
  font-family: "Lilita One", var(--font-display); font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase;
}
.beta__short{ display: none; }
.beta__close{ flex: 0 0 auto; appearance: none; -webkit-appearance: none; cursor: pointer; width: 30px; height: 30px; padding: 0; border-radius: 10px; line-height: 1; font-size: 0.8rem; --rim: 3px; }
@media (hover: hover){ .beta__close:hover{ filter: brightness(1.06); } }
.beta__close:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
/* Straight after an update the strip is the explanation, so it is louder. */
.beta.is-updated{
  box-shadow: 0 0 0 2px #ff4fad;
}
/* The short line up to 899px: between 621 and about 760 the long one wrapped to two lines
   over the top rail (Gray's screenshot, 15 Sep 2026). */
@media (max-width: 899px) {
  .beta{ justify-content: space-between; font-size: 0.74rem; }
  .beta__long{ display: none; }
  .beta__short{ display: inline; }
}

/*
 * The front screen's own pieces. WHERE each one goes is "THE FRONT SCREEN"
 * near the end of this file: named zones, per orientation (11 Oct 2026).
 */
/* Off is the quiet grey button with the icon crossed out (the slash is in the icon, see .icon__slash); on is the violet one.
   Any icon button that carries a slash, anywhere: the music and sound buttons here, and the two in Settings. */
.iconbtn[aria-pressed="false"]:has(.icon__slash){
  --bodyA: #f6f8fc; --bodyB: #d7dfeb; --bodyC: #aeb9cb; --ink: #5a6a88; --ts: rgba(255, 255, 255, 0);
}
.menu .iconbtn .icon{ width: 24px; height: 24px; stroke-width: 2.6; }
/*
 * The middle: name, Play, Play with friends. It clips rather than spilling onto
 * the rows around it, and may be shorter than its content (fitPanels scales it
 * down as a whole when a window is too short for it).
 */
.menu__core{
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 0; min-width: 0; text-align: center; overflow: hidden;
  /* The clip edge sits outside the column, so Play's rim and jelly are never sliced. */
  padding: 8px 14px; margin: -8px -14px;
}
/*
 * The wordmark. Small, and sized by HEIGHT, a share of the screen's (15% on a
 * phone held sideways, 12% upright, up to 20% on a desktop): on a phone it was
 * the biggest thing on the screen (Gray, 11 Oct 2026). The jelly squash needs a
 * little room round it, carried as margin in its own size.
 */
.menu__mark{
  display: block; width: auto; height: var(--mark-h, 18vh); max-width: 100%;
  min-height: 0; object-fit: contain; flex-shrink: 0;
  margin: calc(var(--mark-h, 18vh) * 0.06) auto 0;
  /*
   * The soft shadow stays INSIDE the room the logo has: a 22px blur was cut off
   * in straight lines, a box behind the logo (Gray, 30 Sep 2026). A 9px blur
   * ends about 14px out.
   */
  filter: drop-shadow(0 4px 0 rgba(40, 16, 90, 0.5)) drop-shadow(0 6px 7px rgba(0, 0, 0, 0.4));
}
.menu__eyebrow{
  /* The homepage's keyword line (it has to stay visible prose): one line, a
     pink live-dot, shrinking with the screen rather than wrapping. No box
     (Gray, 14 Sep 2026): plain letters with the leaderboard's dark ring. */
  margin: 0; display: inline-block; max-width: 100%; flex-shrink: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(0.5rem, 1.6vh, 0.72rem); letter-spacing: 0.12em; text-transform: uppercase;
  color: #fff; padding: 2px 4px;
  text-shadow:
    -1px -1px 0 #1c0d3a, 0 -1px 0 #1c0d3a, 1px -1px 0 #1c0d3a,
    -1px 0 0 #1c0d3a, 1px 0 0 #1c0d3a,
    -1px 1px 0 #1c0d3a, 0 1px 0 #1c0d3a, 1px 1px 0 #1c0d3a,
    0 2px 3px rgba(0, 0, 0, 0.45);
}
.menu__eyebrow::before{
  content: ""; display: inline-block; width: 7px; height: 7px; margin-right: 7px; vertical-align: 1px;
  border-radius: 50%; background: #ff4fad; box-shadow: 0 0 0 3px rgba(255, 79, 173, 0.28);
}
/*
 * The language selector (ui/languagePicker.ts): the current flag on the same
 * round button as the bell, and a short list in the burger menu's own panel
 * and rows. The flag is a drawn picture (shared/languages.ts) in a rounded
 * tile, with the little caret that says the button opens something.
 */
.menu__lang{ z-index: var(--z-navbutton); }
.menu__langbtn{ position: relative; }
.menu__langflag{
  position: relative; display: block; width: 26px; height: 17px; margin-top: -4px; border-radius: 4px; overflow: hidden;
}
.menu__langflag .flag, .langitem__flag .flag{ display: block; width: 100%; height: 100%; filter: none; }
.menu__langflag::after, .langitem__flag::after{
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.3), inset 0 1.5px 0 rgba(255, 255, 255, 0.35);
}
.menu__langcaret{
  position: absolute; left: 50%; bottom: 3px; width: 9px; height: 6px; margin-left: -4.5px;
  fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
}
.menu__lang[open] .menu__langcaret{ transform: rotate(180deg); }
.menu__langpanel{ width: auto; min-width: 190px; max-width: calc(100vw - 24px); }
.menu__langpanel ul{ gap: 2px; }
.langitem{
  display: flex; align-items: center; gap: 12px; border-radius: 12px; padding: 10px 8px;
  border-bottom: 0; text-align: left;
}
@media (hover: hover){ .langitem:hover{ background: rgba(255, 255, 255, 0.08); } }
.langitem__flag{ position: relative; flex: 0 0 auto; display: block; width: 30px; height: 20px; border-radius: 5px; overflow: hidden; }
.langitem__name{ flex: 1 1 auto; }
.langitem[aria-pressed="true"]{ color: var(--accent); }
.langitem[aria-pressed="true"]::after{
  content: ""; flex: 0 0 auto; width: 16px; height: 16px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M2.5 8.5l3.5 3.5 7.5-8' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M2.5 8.5l3.5 3.5 7.5-8' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / contain no-repeat;
}
@media (max-width: 520px), (orientation: landscape) and (max-height: 500px) {
  .menu .menu__langflag{ width: 22px; height: 15px; margin-top: -3px; border-radius: 3px; }
  .menu .menu__langcaret{ bottom: 2px; width: 8px; height: 5px; margin-left: -4px; }
}

.menu__field{
  align-self: stretch; display: flex; align-items: center; gap: 10px; text-align: left;
  padding: 5px 8px 5px 18px; border-radius: 18px; background: #fff;
  box-shadow: 0 0 0 3px rgba(60, 20, 110, 0.4), 0 8px 16px -6px rgba(0, 0, 0, 0.45);
}
.menu__label{
  margin: 0; flex: 0 0 auto;
  font-family: var(--font-display); font-weight: 700; font-size: 0.7rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: #8a6aa8;
}
/*
 * White, not a dark hole.
 *
 * A near-black input on a near-black page is the single most "corporate dark
 * dashboard" element on the screen, and it is also the hardest thing to read
 * over a moving arena. Every game this is measured against uses a bright pill:
 * the one place you TYPE should be the one place that is paper.
 */
.menu__input{
  flex: 1; min-width: 0; width: 100%; padding: 9px 4px; font: inherit;
  font-size: 1.2rem; font-weight: 700; color: #22314d; background: transparent;
  border: 0; border-radius: 12px;
}
.menu__input::placeholder{ color: #8496AE; font-weight: 500; }
.menu__input:focus{ outline: none; }
.menu__field:focus-within{ box-shadow: 0 0 0 3px #ff4fad, 0 8px 16px -6px rgba(0, 0, 0, 0.45); }
/* No name yet and Play pressed (menu.ts askForName): the box says so. */
.menu__field.is-needed{ box-shadow: 0 0 0 4px var(--hot), 0 8px 16px -6px rgba(0, 0, 0, 0.45); animation: name-shake 0.42s ease-in-out; }
@keyframes name-shake{ 20%{ translate: -7px 0; } 40%{ translate: 6px 0; } 60%{ translate: -4px 0; } 80%{ translate: 2px 0; } }
@media (prefers-reduced-motion: reduce){ .menu__field.is-needed{ animation: none; } }
/*
 * Buttons are FLAT.
 *
 * They had an extruded lip and a coloured glow — "get rid of shadows on
 * buttons, the lip, make the buttons everywhere better". The lip is a 2010
 * mobile-game tell and the glow made every control look like it was on fire;
 * together they meant the Play button, the nav chips and the choice cards were
 * all shouting at the same volume.
 *
 * What is left carries the weight instead: a solid fill, a generous radius,
 * real size, and one honest press state — the whole thing dips a pixel and
 * darkens. That is enough to feel like a button and quiet enough that the
 * screen has a hierarchy again.
 */
.menu__play{ flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; gap: 12px; padding: 8px 36px 12px; border-radius: 24px; cursor: pointer; font-size: clamp(1.9rem, 3.4vw, 2.6rem); letter-spacing: 0.01em; min-width: min(280px, 72vw); }
@media (hover: hover){ .menu__play:hover{ filter: brightness(1.07); } }
.menu__play:focus-visible{
  outline: 3px solid #fff; outline-offset: 5px;
}

/*
 * The front screen MOVES.
 *
 * Gray, 10 Sep 2026, twice: Worms Zone's menu is exciting because nothing on it
 * sits still — "main logo bounces around, play button bounces different". Their
 * game is compiled C++ (Emscripten), so the tweens can't be read; these are
 * measured off their menu at 0.25s intervals: the wordmark wobbles like jelly
 * on a slow loop, the To-battle button pulses about 4% on a quicker one, and
 * the two never move together.
 *
 * The button stays FLAT (the no-lip rule above). The excitement is motion and
 * light, not an extruded edge. `scale`/`rotate` are the individual transform
 * properties, so the pulse never fights the button's own press transform.
 */
.menu__mark{
  transform-origin: 50% 88%;
  animation: markDrop 0.7s cubic-bezier(.2,1.6,.4,1) both, markJelly 2.8s ease-in-out 0.7s infinite;
}
@keyframes markDrop{
  0%  { translate: 0 -46px; opacity: 0; scale: 0.9; }
  60% { opacity: 1; }
  100%{ translate: 0 0; opacity: 1; scale: 1; }
}
@keyframes markJelly{
  0%, 100%{ scale: 1 1;       rotate: 0deg; }
  20%     { scale: 1.055 0.95;  rotate: -2deg; }
  40%     { scale: 0.96 1.045;  rotate: 1.4deg; }
  60%     { scale: 1.03 0.98;   rotate: -0.7deg; }
  80%     { scale: 0.99 1.015;  rotate: 0.3deg; }
}



@media (prefers-reduced-motion: reduce){
  .menu__mark{ animation: none; }
}

/* --------------------------------------------------------- the skin strip */
.menu__look{ display: flex; align-items: center; gap: 14px; flex-wrap: wrap; justify-content: center; }
.menu__look .menu__label{ margin: 0; }
.swatches{
  display: flex; align-items: center; gap: 8px;
  max-width: min(520px, 70vw); overflow-x: auto; padding: 3px;
  scrollbar-width: none;
}
.swatches::-webkit-scrollbar{ display: none; }
.swatch{
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%; cursor: pointer;
  border: 2px solid transparent; padding: 0;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.45);
  transition: transform 0.12s ease, border-color 0.12s ease;
}
@media (hover: hover){ .swatch:hover{ transform: scale(1.12); } }
.swatch.is-selected{ border-color: var(--text); }
.swatch:focus-visible{ outline: 2px solid var(--accent); outline-offset: 3px; }
.menu__more{
  background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
  color: var(--text-dim); font-size: 0.94rem; display: inline-flex; align-items: center; gap: 8px;
}
@media (hover: hover){ .menu__more:hover{ color: var(--text); } }
.menu__more:focus-visible{ outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px; }

/*
 * Skin and arena: two matching cards, each a picture, a name and a pair of
 * arrows that choose in place. Gray's front-screen design, 13 Sep 2026:
 * "Skin · Zebra ‹ ›" beside "Arena · Meadow ‹ ›", dark violet with a light
 * rim, over the name and Play. They sit still; the Play button is the one
 * thing on this row that moves.
 */
.pickrow{
  display: flex; justify-content: center; gap: clamp(12px, 2.4vw, 26px);
  width: var(--col);
}
.pickcard{
  flex: 1 1 0; min-width: 0;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto;
  grid-template-areas: "art name prev next";
  align-items: center; column-gap: 8px;
  padding: 7px 8px 7px 8px; border-radius: 20px; color: #fff;
  background: linear-gradient(180deg, #34217c, #231457);
  box-shadow: inset 0 0 0 2px #6d55d9, 0 0 0 2px #190c3d, 0 10px 18px -8px rgba(0, 0, 0, 0.55);
}
.pickcard__art{
  grid-area: art; display: block; width: clamp(80px, 7.2vw, 108px); aspect-ratio: 260 / 150;
  border-radius: 12px;
}
.pickcard__art canvas{ display: block; width: 100%; height: 100%; }
/* The floor is a surface, not a worm: a square swatch with an edge of its own. */
.pickcard__art--floor{
  width: clamp(50px, 5vw, 66px); aspect-ratio: 1; margin: 2px 4px 2px 2px; overflow: hidden;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.22);
}
a.pickcard__art:focus-visible{ outline: 3px solid #fff; outline-offset: 3px; }
.pickcard__label{ grid-area: name; min-width: 0; display: grid; gap: 1px; text-align: left; }
.pickcard__name{
  font-family: "Lilita One", var(--font-display); font-size: clamp(1.02rem, 1.45vw, 1.28rem); color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.35);
}
.pickcard__hint{
  font-size: 0.74rem; font-weight: 700; color: #cbbcff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pickcard__hint:empty{ display: none; }
.pickcard__arrow{
  display: grid; place-items: center; width: 34px; height: 42px; padding: 0; border: 0; border-radius: 10px;
  background: none; color: #fff; cursor: pointer; font: inherit; -webkit-tap-highlight-color: transparent;
}
.pickcard__arrow--prev{ grid-area: prev; }
.pickcard__arrow--next{ grid-area: next; }
.pickcard__arrow svg{
  width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 3.2;
  stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 2px 0 rgba(0, 0, 0, 0.35));
}
@media (hover: hover){ .pickcard__arrow:hover:not(:disabled){ background: rgba(255, 255, 255, 0.1); } }
.pickcard__arrow:active:not(:disabled){ transform: scale(0.88); }
.pickcard__arrow:focus-visible{ outline: 3px solid #fff; outline-offset: 1px; }
/* One arena, or one skin, owned: the arrows have nowhere to go, and say so. */
.pickcard__arrow:disabled{ opacity: 0.3; cursor: default; }
/*
 * The phone layout, and the fault it is fixing.
 *
 * Stacking the two cards was the obvious answer to "two 260px pictures do not
 * fit". Measured on a 393x700 phone it made the pick row 217px tall, which put
 * the middle rail's content at 540px inside a 438px track. `.menu__core` is a
 * CENTRED flex, so the 102px of overflow went out of BOTH ends: the wordmark
 * escaped upward under the top nav and the arena card came down over the
 * bottom rail. Gray's screenshot is that, exactly. "Layout fucked mobile".
 *
 * So the cards stay side by side and change SHAPE instead — picture on top,
 * name under it. Two of those is one row of about 100px rather than two rows
 * of 108, and nothing has to be dropped to make room.
 */
/*
 * And wherever the ROW is narrow, not only the screen. With the front
 * screen's side tiles (27 Sep 2026) the column is narrower on a laptop too,
 * and side by side the names came out "S..." and "Aren..." (Gray's
 * screenshot). A container query asks the row itself.
 */
.pickrow{ container-type: inline-size; container-name: pickrow; }
@container pickrow (max-width: 640px) {
  .pickcard{
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "art art art" "prev name next";
    row-gap: 4px; column-gap: 0; padding: 8px 4px 5px; justify-items: center;
  }
  .pickcard__art{ width: min(46cqw, 150px); }
  .pickcard__art--floor{ width: auto; height: min(20cqw, 74px); margin: 2px 0 0; }
  .pickcard__label{ text-align: center; }
  .pickcard__kind{ display: none; }
  .pickcard__name{ font-size: 1.05rem; }
  .pickcard__arrow{ width: 30px; height: 32px; }
}

.menu__unlock{ margin: 0; font-size: 0.8rem; color: var(--text-dim); min-height: 1.2em; }
.menu__alert{
  margin: 0; max-width: 42ch; padding: 8px 12px; border-radius: var(--radius-sm);
  font-size: 0.86rem; font-weight: 600; line-height: 1.35; color: var(--paper);
  background: rgba(255, 59, 92, 0.16); border: 1px solid rgba(255, 59, 92, 0.45);
}


/* ----------------------------------------------------------- the footer */
.menu__foot{
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px 20px; flex-wrap: wrap;
  font-size: 0.78rem; color: rgba(255, 255, 255, 0.85);
  font-family: var(--font-display); font-weight: 500;
  border-top: 2px solid rgba(255, 255, 255, 0.14); padding-top: 9px;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}
.menu__copy{ flex: 0 0 auto; }
.menu__footlinks{ display: flex; flex-wrap: wrap; gap: 4px 16px; justify-content: flex-end; min-width: 0; }
:where(.menu__footlinks) :where(a){
  color: rgba(255, 255, 255, 0.85); text-decoration: none;
  padding: 3px 2px; border-radius: 6px;
}
:where(.menu__footlinks) :where(a:hover){
  color: #ffc8ec;
}

/*
 * The corner controls belong to a RUN.
 *
 * The menu carries Sound and Settings as words in its top rail, so the round
 * icons in the corner would be a second copy of both on the one screen that
 * has room to spell them out. They come back the moment there is a worm.
 */
.game:has(.overlay[data-game-menu]:not([hidden])) .dock{ display: none; }

/*
 * A chromeless page IS the arena.
 *
 * No site header, no site footer, no document scroll. `--header-h` is set to 0
 * by the measuring script when there is no header to measure, so the arena's
 * `100dvh - var(--header-h)` below resolves to the whole viewport without a
 * second height rule to keep in step with the first.
 *
 * `min-height` has to go with it. A 420px floor inside a 390px landscape phone
 * is 30px of document scroll, and the moment the page can scroll a drag that
 * starts on the arena can pull the page instead of steering the worm.
 */
/*
 * `overflow: hidden` belongs to the ARENA, not to "no site header".
 *
 * It was on .page--chromeless, which the settings page also is — so the
 * settings page had to put `overflow: auto` back, which makes <body> the
 * scroll container. A body scroll container does not deliver scroll events to
 * `window` in Chrome: measured, `scrollY` moved from 0 to 1500 and a listener
 * on window fired ZERO times, so the settings sidebar's scroll spy never
 * updated after the first paint. The two concerns are separate and are
 * separated now.
 */
/*
 * Safe areas, on a phone held sideways.
 *
 * `viewport-fit=cover` lets the arena fill the glass, notch included — which is
 * right, a game should. What it must not do is put anything you need to READ
 * or PRESS under the notch, the home indicator or the browser's own chrome.
 * Gray, in landscape: "cant play sideways it dont line up right and is cut
 * off".
 *
 * So the canvas keeps the whole screen and every layer ON it is inset. One
 * variable per edge, defaulting to zero everywhere that has no cutout.
 */
:root {
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
}
.page--home{ overflow: hidden; }
.page--chromeless main{ padding: 0; }
/*
 * NO VIEWPORT BREAKOUT on the page that IS the arena.
 *
 * `main > .game` widens itself to 100vw and re-centres with
 * `margin-left: 50%; translateX(-50vw)`, which is the right trick for an arena
 * embedded in a padded reading column — and completely wrong here, where main
 * has no padding and no column to escape.
 *
 * It is also actively harmful on iOS. In landscape, Safari's side chrome sits
 * OVER the page: 100vw is wider than the part you can see, so the game hung off
 * BOTH edges — and everything pinned to `inset: 0` inside it went with it.
 * Gray's screenshot shows the readout starting at RANK, with the whole LENGTH
 * panel off the left of the screen behind Safari's sidebar, and the minimap and
 * Boost clipped on the right.
 *
 * There is nothing to break out of. 100% of main is the visible width, by
 * definition, whatever the browser is overlaying it with.
 */
.page--chromeless main > .game{
  width: 100%;
  margin-left: 0;
  transform: none;
  min-height: 0;
}

/* --------------------------------------------------- the content pages
 *
 * The same world as the settings page, not a different one.
 *
 * These were near-black with hairline rules while the settings page had a
 * blue-violet ground, cards and a rounded, coloured toolkit — Gray: "all these
 * old inner pages mate dont match settings page design". Two designs on one
 * site is worse than either of them on its own.
 *
 * So the ground, the surfaces, the type and the accents are shared. What is
 * NOT shared is density: a page of prose wants a reading measure and air, a
 * settings screen wants rows. Matching a design means the same materials, not
 * the same layout.
 */
.page:not(.page--chromeless){ background:
    linear-gradient(180deg, rgba(14, 18, 52, 0.62), rgba(14, 18, 52, 0.78)),
    url("/ground/hex.png?v=cd5c4c25") 0 0 / 512px 512px,
    #1d4f9c;
  background-attachment: fixed; }

:where(h1) { font-family: "Lilita One", var(--font-display); font-weight: 400; font-size: clamp(2.1rem, 4.8vw, 3.2rem); line-height: 1.08; margin-block: 0.2em 0.5em; letter-spacing: 0.005em; color: var(--paper); text-shadow: 0 3px 0 rgba(60, 20, 110, 0.65); }
:where(h2) {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.4rem, 2.6vw, 1.9rem); line-height: 1.2;
  margin-block: 1.6em 0.5em; letter-spacing: 0; color: var(--paper);
}
:where(h3) {
  font-family: var(--font-display); font-weight: 600;
  font-size: 1.15rem; margin: 1.4em 0 0.35em; color: var(--paper);
}

.prose{ max-width: var(--max); color: var(--paper-dim); }
:where(.prose) :where(p){ color: var(--paper-dim); }
.prose strong{ color: var(--paper); }
/*
 * :where(), and it is not a style preference — it is the fix for invisible
 * buttons.
 *
 * `.prose a` scores (0,2,0) and `.button--primary` scores (0,1,0), so an
 * ordinary link rule inside an article BEAT the button component and repainted
 * its label in the accent colour. The button's background IS the accent
 * colour. Every call-to-action inside prose was therefore lime text on a lime
 * button — a blank green slab, on the leaderboard, about, tips and every
 * listicle. Gray: "button text not visible, theres loads like that".
 *
 * :where() makes this rule specificity ZERO, so any component class inside
 * prose wins by simply existing. That is the general rule for anything that
 * styles a bare element inside a container, and check-layout enforces it —
 * otherwise the next container to style its own links breaks buttons again in
 * a way nobody sees until a screenshot arrives.
 */
:where(.prose) :where(a){ color: var(--accent); text-decoration-color: rgba(255, 79, 173, 0.4); }
:where(.prose) :where(a:hover){ text-decoration-color: var(--accent); }
.prose ul, .prose ol { padding-left: 1.25em; }
:where(.prose) :where(li){ margin: 0.4em 0; }

/*
 * Every section is a card, the way the settings sections are.
 *
 * A run of h2s separated by nothing is what made these read as a document from
 * a different site. The card is the shared object: same radius, same lit edge,
 * same translucent surface over the same ground.
 */
.prose > section{ border-radius: var(--radius); padding: 6px clamp(18px, 3vw, 30px) 24px; margin-block: 20px; background: linear-gradient(180deg, #252c62, #1a1f47); border: 4px solid #fff; box-shadow: 0 0 0 2px #7a2a6a, 0 14px 30px -12px rgba(0, 0, 0, 0.55); }
.prose > section:first-of-type h2{ margin-top: 1em; }

/*
 * The opening answer is a panel like the menu's: violet, white rim, the dark
 * purple lip under it. It was a pink left rule on a see-through box, which read
 * as a pull-quote from a different site.
 */
.lede{
  font-size: 1.12rem; color: var(--paper);
  padding: 18px clamp(18px, 3vw, 26px); margin-block: 0 1.6em;
  max-width: var(--max);
  background: linear-gradient(180deg, #4b3aa8, #33257f);
  border: 4px solid #fff; border-radius: var(--radius);
  box-shadow: 0 0 0 2px #2a1465, 0 14px 30px -12px rgba(0, 0, 0, 0.55);
}

/* Headings in the lettering the menu's panels use (Lilita One). */
:where(.prose, .faq, .related, .skingroup) :where(h2){
  font-family: "Lilita One", var(--font-display); font-weight: 400; letter-spacing: 0.01em;
  text-shadow: 0 2px 0 rgba(60, 20, 110, 0.55);
}

.button{ display: inline-flex; align-items: center; justify-content: center; gap: 10px; padding: 9px 24px 12px; border-radius: 18px; text-decoration: none; cursor: pointer; letter-spacing: 0.01em; font-size: 1.1rem; transition: filter 0.12s ease; }




/* A third action on the death screen, weighted below the other two: it is the
   way out, not a way on, and it must not compete with Play Again. */
/*
 * Weighted below the other two — but still visibly a BUTTON.
 *
 * It had a transparent border until you hovered it, which on the death screen
 * made the only way back to the menu look like a caption. Gray: "main menu has
 * no bored unless you hover over it". A control that only announces itself
 * once you have already found it is not discoverable; quiet means lower
 * contrast, not invisible.
 */
.button--quiet{ font-size: 0.95rem; padding: 7px 16px 9px; --rim: 4px; }

.button--big{ font-size: 1.55rem; padding: 10px 30px 14px; width: 100%; }
/*
 * The house button's other sizes and shapes, so a new screen picks one rather
 * than writing its own (Gray, 14 Sep 2026: "stop hand rolling shit, we work
 * from single source and templates"). These set size and shape only; colour is
 * a role class in the Candy section (--primary, --quiet, --glass, --flat).
 */
.button--sm{ font-size: 0.95rem; padding: 6px 16px 9px; border-radius: var(--radius-sm); --rim: 3px; }
.button--block{ display: flex; width: 100%; }
.button--pill{ border-radius: var(--radius-pill); }
@media (hover: hover){ .button:hover{ filter: brightness(1.07); } }
.button:disabled{ opacity: 0.45; cursor: default; filter: grayscale(0.35); animation: none; }
.button__timer{ font-weight: 600; opacity: 0.8; font-size: 0.9em; }
/*
 * The revive offer on the death screen: one button, one line on what it gives,
 * and a bar that drains over the offer's seconds (--revive-s, set from
 * MONETISATION.REVIVE_OFFER_SECONDS), so there is time to read and a clear
 * sign of how much is left.
 */
.revive{ display: grid; gap: 6px; justify-items: stretch; text-align: center; }
.revive[hidden]{ display: none; }
.revive__go{ width: 100%; }
.revive__note{ margin: 0; font-weight: 700; font-size: 0.95rem; color: #fff; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.35); }
.revive__clock{ display: flex; align-items: center; gap: 10px; }
.revive__clock[hidden]{ display: none; }
.revive__bar{
  flex: 1 1 auto; height: 10px; border-radius: 999px; background: #c6f432; transform-origin: left center;
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.2), 0 0 0 3px rgba(12, 8, 36, 0.55);
}
.revive.is-running .revive__bar{ animation: revive-drain var(--revive-s, 9s) linear forwards; }
.revive__secs{ min-width: 2.2em; font-weight: 700; font-size: 0.9rem; font-variant-numeric: tabular-nums; text-align: right; }
.revive__msg{ margin: 0; font-weight: 700; color: #ffe45c; }
@keyframes revive-drain{ from{ transform: scaleX(1); background: #c6f432; } 70%{ background: #c6f432; } to{ transform: scaleX(0); background: #ff4fad; } }
@media (prefers-reduced-motion: reduce){ .revive.is-running .revive__bar{ animation-timing-function: steps(9, end); } }

.cta{ display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-block: 1.6em; }
.cta__note{ color: var(--paper-dim); font-size: 0.92rem; }

.faq{ margin-block: 20px; border-radius: var(--radius); padding: 6px clamp(18px, 3vw, 30px) 20px; background: linear-gradient(180deg, #252c62, #1a1f47); border: 4px solid #fff; box-shadow: 0 0 0 2px #7a2a6a, 0 14px 30px -12px rgba(0, 0, 0, 0.55); }
.faq__item{ border-top: 2px solid rgba(255, 255, 255, 0.07); padding: 16px 0; }
.faq__item:first-of-type{ border-top: 0; }
.faq__q{
  margin: 0 0 0.3em; font-size: 1.1rem;
  font-family: var(--font-display); font-weight: 600; color: var(--paper);
}
.faq__a{ margin: 0; color: var(--paper-dim); }

.related{
  margin-top: 24px; border-radius: var(--radius);
  padding: 6px clamp(18px, 3vw, 30px) 22px;
  background: linear-gradient(180deg, #4b3aa8, #33257f);
  border: 4px solid #fff;
  box-shadow: 0 0 0 2px #2a1465, 0 14px 30px -12px rgba(0, 0, 0, 0.55);
}
.related__list{ list-style: none; margin: 0; padding: 0; }
:where(.related__list) :where(li){ margin: 0.9em 0; }
:where(.related__list) :where(a){ font-family: var(--font-display); font-weight: 600; font-size: 1.05rem; }
.related__desc{ color: var(--paper-dim); font-size: 0.92rem; display: block; }

.gamelist{ list-style: none; margin: 1.2em 0; padding: 0; display: grid; gap: 12px; }
.gamecard{
  border: 3px solid rgba(255, 255, 255, 0.55); border-radius: var(--radius-sm);
  padding: 16px 18px; background: rgba(255, 255, 255, 0.06);
}
.gamecard--ours{
  border-color: #fff; background: linear-gradient(180deg, #4b3aa8, #33257f);
  box-shadow: 0 0 0 2px #2a1465;
}
.gamecard__name{ margin: 0 0 0.15em; font-size: 1.05rem; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.gamecard__badge{
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em;
  background: rgba(110, 231, 183, 0.16); color: var(--accent);
  border: 1px solid rgba(110, 231, 183, 0.4); border-radius: 999px; padding: 2px 9px; font-weight: 700;
}
.gamecard__genre{ margin: 0 0 0.4em; color: var(--accent); font-size: 0.83rem; font-weight: 600; }
.gamecard__blurb{ margin: 0; color: var(--text-dim); font-size: 0.96rem; }

/* --------------------------------------------------------------- skins page */
.skingroup{ max-width: none; }
.skingrid{
  list-style: none; margin: 1.1em 0 0; padding: 0;
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
}
.skincard{
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--ink-2); padding: 12px; text-align: center;
}
.skincard__art{ display: block; width: 100%; height: auto; }
.skincard__name{ margin: 6px 0 2px; font-size: 0.98rem; }
.skincard__unlock{ margin: 0; font-size: 0.8rem; color: var(--text-dim); line-height: 1.4; }

/* ---------------------------------------------------------------- footer */
/*
 * A footer, not a bin.
 *
 * It was one paragraph, a flat run of eleven links, and a "Last updated" date.
 * Eleven links in a row tell a reader nothing about which one they want, and a
 * documentation date on a game is a convention borrowed from the wrong place —
 * no game in the field carries one, and the sitemap's <lastmod> is where a
 * crawler looks anyway.
 */
.sitefooter{ border-top: 3px solid #fff; box-shadow: 0 -2px 0 #7a2a6a; padding: 40px clamp(16px, 4vw, 40px) 0; color: var(--paper-dim); font-size: 0.92rem; background: linear-gradient(180deg, #1c1650, #120e38); }
.sitefooter__inner{
  display: grid; gap: 32px clamp(24px, 5vw, 64px);
  grid-template-columns: minmax(200px, 1fr) 2fr;
  align-items: start;
}
.sitefooter__mark{ display: block; height: 30px; width: auto; margin-bottom: 12px; opacity: 0.9; }
.sitefooter__entity{ margin: 0; max-width: 30ch; line-height: 1.5; }
.sitefooter__nav{
  display: grid; gap: 24px clamp(20px, 4vw, 48px);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.sitefooter__heading{
  margin: 0 0 10px; font-family: var(--font-display);
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text);
}
.sitefooter__links{
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 7px;
}
:where(.sitefooter__links) :where(a){ color: var(--paper-dim); text-decoration: none; }
:where(.sitefooter__links) :where(a:hover){ color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

.sitefooter__base{
  margin-top: 36px; padding: 18px 0 34px;
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: 6px 24px; justify-content: space-between;
  font-size: 0.84rem;
}
.sitefooter__copy, .sitefooter__made { margin: 0; }
@media (max-width: 640px) {
  .sitefooter__inner{ grid-template-columns: 1fr; gap: 28px; }
  .sitefooter__nav{ grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
}

/* ---------------------------------------------------------------- the game */
.game{
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  min-height: 380px;
  max-height: min(78vh, 780px);
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line);
  background: #070a12;
  touch-action: none;
  user-select: none;
}
.game__stage{
  position: absolute; inset: 0;
  /* Mud's average colour: every new player starts on mud, so the arena fades
     in from its own floor. The game repaints this for other floors (GROUND_TONE). */
  background: #9b6134;
}
.squirm-canvas{
  /*
   * !important: PixiJS's autoDensity writes the canvas's size inline, in
   * pixels, from its first measurement. A fixed-width canvas is the one thing
   * that can make iPhone Safari grow a portal's iframe to fit it; the game then
   * re-measures the grown frame and stays too wide, cut off on the right (Gray,
   * CrazyGames on an iPhone held sideways, 28 Sep 2026). The drawing
   * resolution is still the renderer's; the box always follows the stage.
   */
  display: block; width: 100% !important; height: 100% !important;
  /* Hidden until it can draw its real floor and food; see Renderer.reveal. */
  opacity: 0; transition: opacity 450ms ease-out;
}
.squirm-canvas.is-ready{ opacity: 1; }
@media (prefers-reduced-motion: reduce){ .squirm-canvas{ transition: none; } }

/*
 * The loading screen (gameShell.ts .boot, main.ts bootProgress): the game's
 * violet room, the logo, and our worm growing along a track as the game gets
 * ready: main.ts pushes it on at each real step, then grows it the rest of
 * the way over what is left of BOOT_MIN_MS, and it fades into the menu when
 * .game gains .is-ready.
 */
.boot{
  --p: 0.06;
  position: absolute; inset: 0; z-index: 40; overflow: hidden;
  display: grid; place-content: center; justify-items: center; gap: clamp(14px, 4vh, 30px); padding: 20px;
  background:
    radial-gradient(circle at 50% 35%, rgba(255, 255, 255, 0.14), transparent 45%),
    radial-gradient(ellipse 120% 100% at 50% 30%, #6a52e6, #3d2c9e 50%, #1c1250 100%);
  transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}
.game.is-ready .boot{ opacity: 0; visibility: hidden; pointer-events: none; }
.boot__rays{
  position: absolute; inset: 0; pointer-events: none;
  background: repeating-conic-gradient(from var(--ray-turn) at 50% 35%, rgba(255, 255, 255, 0.07) 0 8deg, transparent 8deg 20deg);
  -webkit-mask: radial-gradient(circle at 50% 35%, #000 99px, transparent 544px); mask: radial-gradient(circle at 50% 35%, #000 99px, transparent 544px);
  animation: ray-turn 40s linear infinite;
}
.boot__mark{ position: relative; width: min(78vw, 520px); height: auto; filter: drop-shadow(0 10px 18px rgba(0, 0, 0, 0.45)); animation: boot-bob 2.4s ease-in-out infinite; }
@keyframes boot-bob{ 50%{ translate: 0 -6px; } }
/* The track, and the worm growing along it with its head leading. */
.boot__track{
  position: relative; width: min(72vw, 420px); height: 30px; border-radius: 999px; padding: 4px;
  background: rgba(10, 6, 40, 0.55); box-shadow: inset 0 3px 0 rgba(0, 0, 0, 0.35), 0 0 0 3px rgba(255, 255, 255, 0.85);
}
.boot__worm{
  position: relative; height: 100%; min-width: 22px; width: calc(var(--p) * 100%); border-radius: 999px;
  background:
    repeating-linear-gradient(90deg, transparent 0 14px, rgba(0, 0, 0, 0.12) 14px 16px),
    linear-gradient(180deg, #e3ff8a, #9be22d 55%, #5fae12);
  box-shadow: inset 0 3px 0 rgba(255, 255, 255, 0.45);
  transition: width 0.7s cubic-bezier(0.3, 0.8, 0.3, 1);
}
.boot__head{
  position: absolute; right: -6px; top: 50%; width: 30px; height: 30px; translate: 0 -50%; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #f0ffb0, #9be22d 55%, #4f9a0e); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);
}
.boot__head::before, .boot__head::after{
  content: ""; position: absolute; top: 7px; width: 8px; height: 8px; border-radius: 50%;
  background: radial-gradient(circle at 60% 50%, #1b0431 45%, #fff 50%);
}
.boot__head::before{ right: 5px; }
.boot__head::after{ right: 5px; top: 15px; }
.boot__tip{
  position: relative; margin: 0; max-width: 36ch; min-height: 2.6em; text-align: center;
  font-family: "Lilita One", var(--font-display); font-size: clamp(1rem, 2.6vw, 1.25rem); color: #fff;
  text-shadow: 0 2px 0 #2a0848;
}
@media (prefers-reduced-motion: reduce){ .boot__rays, .boot__mark{ animation: none; } }

.game__status{
  position: absolute; inset-inline: 0; top: 50%; transform: translateY(-50%);
  margin: 0 auto; max-width: 34ch; text-align: center; z-index: 4;
  color: var(--text); background: rgba(10, 14, 26, 0.86);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 14px 18px; font-weight: 600;
}
/* The way out while the client waits for a place in a full box (main.ts). */
.game__status-cancel{
  display: block; margin: 12px auto 0; padding: 8px 20px;
  font: inherit; font-weight: 700; color: var(--text);
  background: transparent; border: 1px solid var(--line);
  border-radius: var(--radius-pill); cursor: pointer;
}
@media (hover: hover){ .game__status-cancel:hover{ border-color: var(--text-dim); } }
.game__status-cancel:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }

/*
 * The HUD belongs to a run, not to the menu.
 *
 * With the arena now behind the menu, LENGTH 10 / RANK — / TOP 10 sat over an
 * attract scene reporting on a worm that does not exist. The corner controls
 * stay: settings and mute are settings, and every game in the field keeps them
 * on the menu screen as corner furniture.
 */
.game:has(.overlay[data-game-lobby]:not([hidden])) .hud__stat,
.game:has(.overlay[data-game-lobby]:not([hidden])) .hud__board,
.game:has(.overlay[data-game-lobby]:not([hidden])) .hud__minimap,
.game:has(.overlay[data-game-menu]:not([hidden])) .hud__stat,
.game:has(.overlay[data-game-menu]:not([hidden])) .hud__board,
.game:has(.overlay[data-game-menu]:not([hidden])) .hud__minimap{ display: none; }

.hud{
  position: absolute; inset: 0; pointer-events: none;
  padding:
    calc(12px + var(--safe-t)) calc(12px + var(--safe-r))
    calc(12px + var(--safe-b)) calc(12px + var(--safe-l));
  /*
   * The HUD is a template: named slots, and every piece takes its slot by
   * name (grid-area), never by row and column numbers. It used to be numbers,
   * with the strip and the board both unable to shrink, so on a phone the strip
   * ran under the Top 10 as soon as it gained a readout (Nuggets, 14 Sep 2026).
   * Now the strip keeps its size and the board's column takes what is left, its
   * names cut short with an ellipsis rather than overlapping. tools/check-layout
   * holds every .hud__ piece to its slot.
   */
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) minmax(0, max-content);
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "stats . board"
    ".     . .    "
    ".     . corner";
  gap: 10px; font-size: 0.9rem;
  /* The one way HUD text stands off the arena: a crisp dark ring round every
     letter, never a box (see .hud__board). The strip and the board share it. */
  --board-ink: #1c0d3a;
  --hud-ring:
    -1px -1px 0 var(--board-ink), 0 -1px 0 var(--board-ink), 1px -1px 0 var(--board-ink),
    -1px 0 0 var(--board-ink), 1px 0 0 var(--board-ink),
    -1px 1px 0 var(--board-ink), 0 1px 0 var(--board-ink), 1px 1px 0 var(--board-ink),
    0 2px 3px rgba(0, 0, 0, 0.45);
}
/*
 * The readout is text on the arena, not a box.
 *
 * It was three fat rounded cards and a floating pill; then one boxed strip
 * with dividers, a lime edge, an "In arena" count and the ping. slither.io
 * shows two lines of small text and nothing else, and every game in the field
 * keeps the play area that clean. Gray, 14 Sep 2026: "every snake game, the
 * actual gameplay, the area is clean as fuck, you trying to keep adding shit
 * in the way". So: Length, Rank "#13 of 40", Nuggets when you have some, in
 * the board's ring, and no box.
 */
.hud__stat{
  display: flex; flex-direction: column; justify-content: center;
  padding: 2px 0;
}
.hud__label{
  display: block; margin: 0 0 1px; white-space: nowrap;
  font-family: var(--font-display); font-weight: 600; font-size: 0.62rem;
  text-transform: uppercase; letter-spacing: 0.13em; color: #fff;
}
.hud__value{
  font-family: var(--font-display); font-weight: 600; font-size: 1.35rem;
  line-height: 1.05; font-variant-numeric: tabular-nums; color: var(--paper);
}

/*
 * The leaderboard is not a panel.
 *
 * It was a bordered card with a near-solid background and full-brightness white
 * text, sitting over a third of a phone screen you are trying to steer through.
 * slither.io — and every other game in the field — draws its board straight
 * onto the arena: no box, muted text, and a shadow instead of a backdrop so it
 * stays readable over a bright patch of floor without hiding it.
 *
 * Muted text and a soft shadow only hold up on slither's black floor. On sand,
 * the phone board's 50% white stood 1.25:1 off the floor and even full white
 * only 1.73:1, where small text wants 4.5 (measured on the floor art, 13 Sep
 * 2026; Gray could not read it on his phone). So the text is full white with a
 * crisp dark ring round every letter, as the "+N" pops have: 18:1 against the
 * ring on any floor, and still no box over the arena.
 */
.hud__board{
  /* One fixed width (Gray, 29 Sep 2026: "needs fixed width for username size"): it grew and
     shrank with whoever was in the Top 10. A long name ends in "..." instead. */
  grid-area: board; justify-self: end; align-self: start; width: min(12.5rem, 100%);
  background: none; border: 0; padding: 4px 2px;
  text-shadow: var(--hud-ring);
}
:where(.hud__board) :where(h2){ margin: 0 0 4px; }
.squirm-board{
  list-style: none; margin: 0; padding: 0; font-variant-numeric: tabular-nums;
  color: #fff;
}
.squirm-board__row{ display: grid; grid-template-columns: 1.4em 1fr auto; gap: 7px; font-size: 0.84rem; line-height: 1.5; }
.squirm-board__row.is-you{ color: var(--accent); font-weight: 700; }
.squirm-board__name{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.squirm-board__row.is-you .squirm-board__len{ color: var(--accent); }
/* The rule between the top ten and your own row further down. */
.squirm-board__gap{ height: 1px; margin: 3px 0; background: rgba(255, 255, 255, 0.85); box-shadow: 0 1px 0 var(--board-ink), 0 -1px 0 var(--board-ink); list-style: none; }

.hud__corner{
  grid-area: corner; justify-self: end; align-self: end;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
}
.hud__minimap{ width: 118px; height: 118px; display: block; }
.hud__stats{
  grid-area: stats; justify-self: start; align-self: start;
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: 18px;
  text-shadow: var(--hud-ring);
}
/* Length is the score. It gets the accent and the size; the others do not,
   or the strip is equal numbers and none of them is the one that matters. */
.hud__stat:first-child .hud__value{ color: var(--accent); font-size: 1.55rem; }
/* The nugget count reads faster with its own icon in front of it, the way a
   currency symbol would — Gray, 15 Sep 2026: "put a nugget before the
   number, that would look better". Same art as the fly-to-counter animation. */
.hud__value--nugget{ display: inline-flex; align-items: center; gap: 4px; }
.hud__nugget-icon{ width: 1em; height: 1em; display: block; filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.6)); }
/*
 * An amount of Nuggets anywhere else: the gold and the number, never the word
 * (ui/nuggetAmount.ts). Gray, 18 Sep 2026: "dont use the word nuggets, use the
 * icon man". Sized off the text it sits in, so one rule serves a card label, a
 * button and the purchase dialog alike.
 */
.nugamt{ display: inline-flex; align-items: center; gap: 0.2em; white-space: nowrap; vertical-align: -0.2em; font-variant-numeric: tabular-nums; }
.nugamt__icon{ width: 1.25em; height: 1.25em; flex: none; filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.55)); }
/*
 * Active arena power-ups (branch: powerups), bottom-centre like Worms Zone: a
 * row of round icons, each with its own draining ring, the newest added on
 * the right (hud.ts updatePowerups). Clear of the boost button (bottom-right,
 * .boostbutton) and the sound dock (bottom-left, .dock) — the safe middle of
 * the bottom edge is otherwise empty. Gray: "a ring around them for a timer
 * that goes down not a number" — so the time left is ONLY the ring, a
 * conic-gradient wedge (--pu-frac, 1 to 0) that sweeps away as the icon's
 * power-up runs out, never a digit; the seconds are still there for a screen
 * reader (aria-label). Each icon carries the power-up's own colour (set
 * inline, --pu-color) behind its painted art (.powerupbar__art), the
 * clean-play-area rule everywhere else in the HUD follows — a ring round an
 * icon, never a box.
 */
.powerupbar{
  position: absolute; left: 50%; bottom: calc(16px + var(--safe-b));
  transform: translateX(-50%);
  display: flex; align-items: flex-end; justify-content: center; gap: 14px;
  /* Never reaches under the boost button or the sound dock: extra icons wrap onto a row above. */
  flex-wrap: wrap-reverse; width: max-content;
  max-width: calc(100vw - 2 * (var(--boost-size) + var(--boost-inset)) - 16px);
  pointer-events: none; z-index: 5;
}
.powerupbar[hidden]{ display: none; }
.powerupbar__item{
  --pu-color: #7ad6ff;
  --pu-size: 76px;
  position: relative; flex: none;
  width: var(--pu-size); height: var(--pu-size); border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.55));
  animation: powerup-in 0.22s ease-out;
}
/* The timer: a thick ring in the power-up's own colour over a dark track, drained clockwise from the top. */
.powerupbar__ring{ position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); overflow: visible; }
.powerupbar__track, .powerupbar__arc{ fill: none; stroke-width: 9; }
.powerupbar__track{ stroke: rgba(11, 16, 32, 0.62); }
.powerupbar__arc{
  stroke: var(--pu-color); stroke-linecap: round;
  stroke-dasharray: 100; stroke-dashoffset: 0;
}
.powerupbar__glyph{
  width: calc(100% - 20px); height: calc(100% - 20px); border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--pu-color); color: #0B1020;
  font-family: var(--font-display); font-weight: 700; font-size: 1.3rem;
  position: relative; overflow: hidden;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9);
}
/*
 * The painted icon (tools/import-powerup-art.py). Sits over the letter glyph
 * so a broken/missing file (onerror, hud.ts buildPowerupItem) simply reveals
 * the drawn fallback already underneath it — never a broken-image box.
 */
.powerupbar__art{ position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
/* The orb art is its own round glass ball: no coloured disc or letter behind it once it is there. */
.powerupbar__glyph:has(.powerupbar__art){ background: none; color: transparent; box-shadow: none; }
.powerupbar__art{ object-fit: contain; border-radius: 0; }
@keyframes powerup-in{ from{ transform: scale(0.5); opacity: 0; } to{ transform: scale(1); opacity: 1; } }
@media (max-width: 640px){
  .powerupbar{ gap: 8px; bottom: calc(12px + var(--safe-b)); }
  .powerupbar__item{ --pu-size: 54px; }
  .powerupbar__glyph{ width: calc(100% - 15px); height: calc(100% - 15px); font-size: 1rem; }
}
@media (prefers-reduced-motion: reduce){ .powerupbar__item{ animation: none; } }

/*
 * The pickup pop (branch: powerups, second round). Gray, after playing the
 * stacking version: "it should pop up in middle of screen which power up you
 * just got so you know". Upper-middle of the stage — above where a worm's
 * head normally sits on screen, so it never covers it (the clean-play-area
 * rule: no box behind it either, just the icon and the layered word). Pointer
 * events off throughout, so it can never eat a click or a touch mid-run.
 */
.powerpop{
  position: absolute; left: 50%; top: 16%; transform: translate(-50%, 0);
  display: grid; justify-items: center; gap: 2px;
  pointer-events: none; z-index: 6; text-align: center;
}
.powerpop[hidden]{ display: none; }
/* The level-up toast reuses .powerpop wholesale, gold instead of the power-up's own colour. */
[data-hud-levelup]{ --pu-color: #ffd166; }
/*
 * It also carries the Halloween news and endings, some long, longer in German
 * ("Wormy_McWormface99 hat den Kopflosen Reiter abgeschnitten!"). They wrap,
 * centred, between the score strip and the Top 10 on a wide screen, below the
 * Top 10 on a phone, and never off the glass (check-overflow's in-run scenarios).
 */
[data-hud-levelup] .powerpop__name{ white-space: normal; width: max-content; max-width: calc(100vw - 2 * (12.5rem + 40px)); line-height: 1.05; text-wrap: balance; }
/*
 * The strip wraps rather than taking the Top 10's width: with Candy, Nuggets
 * and Level in it (the Halloween Party), five readouts in one row squeezed the
 * Top 10 to a 46px sliver and the minimap with it, on a phone held upright
 * (check-overflow's in-run scenarios, 18 Oct 2026).
 */
@media (max-width: 760px) {
  .hud__stats{ max-width: calc(100vw - min(44vw, 12.5rem) - 40px - var(--safe-l, 0px) - var(--safe-r, 0px)); }
}
@media (max-height: 500px) {
  .hud__stats{ max-width: calc(100vw - 10.8rem - 48px - var(--safe-l, 0px) - var(--safe-r, 0px)); }
}
/* On a phone held upright the Top 10 and the minimap fill the top right: the banner goes below your worm, clear of both and of the controls. */
@media (max-width: 760px) {
  [data-hud-levelup]{ top: 62%; }
  [data-hud-levelup] .powerpop__name{ max-width: calc(100vw - 32px - var(--safe-l, 0px) - var(--safe-r, 0px)); font-size: clamp(1.3rem, 6.4vw, 1.9rem); }
}
@media (max-height: 500px) {
  [data-hud-levelup]{ top: 34%; }
  [data-hud-levelup] .powerpop__name{ max-width: calc(100vw - 2 * (10.8rem + 24px) - var(--safe-l, 0px) - var(--safe-r, 0px)); font-size: clamp(1.1rem, 5.2vh, 1.6rem); }
}
.powerpop__glyph{
  position: relative; width: clamp(64px, 11vw, 96px); height: clamp(64px, 11vw, 96px);
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--pu-color, #7ad6ff);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.35), 0 6px 18px rgba(0, 0, 0, 0.55),
    0 0 34px color-mix(in oklab, var(--pu-color, #7ad6ff) 65%, transparent);
}
.powerpop__letter{
  font-family: var(--font-display); font-weight: 700; color: #0b1020; font-size: clamp(1.6rem, 4vw, 2.4rem);
}
.powerpop__art{ position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.powerpop__art[hidden]{ display: none; }
.powerpop__art{ object-fit: contain; border-radius: 0; }
.powerpop__glyph:has(.powerpop__art:not([hidden])){ background: none; box-shadow: none; }
.powerpop__glyph:has(.powerpop__art:not([hidden])) .powerpop__letter{ visibility: hidden; }
.powerpop__name{
  margin: 4px 0 0; font-family: "Lilita One", var(--font-display); font-weight: 400;
  font-size: clamp(1.6rem, 4.4vw, 2.6rem); letter-spacing: 0.01em;
}
.powerpop__name::before{
  color: #1b0431; -webkit-text-stroke: 0.22em #1b0431;
  filter: drop-shadow(0 0.03em 0 #1b0431) drop-shadow(0 0.06em 0.06em rgba(8, 0, 24, 0.5));
}
.powerpop__name::after{ color: var(--pu-color, #7ad6ff); -webkit-text-stroke: 0.1em var(--pu-color, #7ad6ff); }
.powerpop__name > span{ background-image: linear-gradient(180deg, #fff 0%, #fff 55%, #fff0e0 100%); }
.powerpop__extra{
  font-family: var(--font-display); font-weight: 800; font-size: 1.1rem; color: var(--pu-color, #7ad6ff);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.55);
}
.powerpop__extra[hidden]{ display: none; }
/*
 * Scales in with a small overshoot, holds, then floats up and fades — Gray's
 * brief, "pop up... so you know". ~1.4s total (hud.ts POWERUP_POP_MS): a
 * quick overshoot-in (0-12%), a hold through 64% (about 0.9s), then the float
 * and fade to the end.
 */
.powerpop.is-playing{ animation: powerpop-play 1.4s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
@keyframes powerpop-play{
  0%{ transform: translate(-50%, 6px) scale(0.4); opacity: 0; }
  12%{ transform: translate(-50%, 0) scale(1.08); opacity: 1; }
  20%{ transform: translate(-50%, 0) scale(1); opacity: 1; }
  64%{ transform: translate(-50%, 0) scale(1); opacity: 1; }
  100%{ transform: translate(-50%, -26px) scale(1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce){
  .powerpop.is-playing{ animation: powerpop-fade 1.4s ease both; }
}
@keyframes powerpop-fade{ 0%{ opacity: 0; } 15%{ opacity: 1; } 70%{ opacity: 1; } 100%{ opacity: 0; } }
.game:has(.overlay[data-game-menu]:not([hidden])) .powerpop,
.game:has(.overlay[data-game-settings]:not([hidden])) .powerpop,
.game:has(.overlay[data-game-death]:not([hidden])) .powerpop{ display: none; }
/* "of 40" after the rank: context, not a second number to read. */
.hud__of{ margin-left: 0.3em; font-size: 0.62em; }
.chip{
  font: inherit; font-size: 0.76rem; font-weight: 600;
  background: rgba(10, 14, 26, 0.72); color: var(--text-dim);
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 11px; cursor: pointer;
}
@media (hover: hover){ .chip:hover{ color: var(--text); border-color: var(--accent); } }

.boostbutton{ position: absolute; right: calc(var(--boost-inset) + var(--safe-r)); bottom: calc(var(--boost-inset) + var(--safe-b)); width: var(--boost-size); height: var(--boost-size); border-radius: 50%; font-size: 1.05rem; letter-spacing: 0.02em; z-index: 5; --rim: 4px; }
.boostbutton.is-active{ transform: scale(1.07, 0.93); filter: brightness(1.12); }
/* Boost steers nothing on a menu. It also sat in the same corner as the link
   bar's right-hand end, which on a phone put a 96px circle over three links. */
.game:has(.overlay[data-game-menu]:not([hidden])) .boostbutton,
.game:has(.overlay[data-game-settings]:not([hidden])) .boostbutton,
.game:has(.overlay[data-game-death]:not([hidden])) .boostbutton{ display: none; }
/*
 * Boost on the LEFT (Settings > Controls), for a player who steers with the
 * right thumb. The sound dock trades corners with it, so they never share one.
 * `.game.is-boost-left` outranks every per-screen `.dock` rule below.
 */
.game.is-boost-left .boostbutton{ right: auto; left: calc(var(--boost-inset) + var(--safe-l)); }
.game.is-boost-left .dock{ left: auto; right: calc(14px + var(--safe-r)); align-items: flex-end; }
/*
 * Any screen with a boost button keeps the minimap out of the bottom corners.
 *
 * The phone rules below moved it under the leaderboard by screen SIZE, and an
 * iPad in landscape is wider than any of them: the minimap stayed in the
 * bottom-right corner, under the boost button — and with boost switched to
 * the left, under the sound and exit buttons that move there instead (Gray,
 * 27 Sep 2026, on an iPad). Keyed on the boost button being on screen, which
 * is exactly when a bottom corner is taken, whatever the screen's size.
 */
.game:has(.boostbutton:not([hidden])) .hud{
  grid-template-areas:
    "stats . board"
    ".     . corner"
    ".     . .     ";
}
.game:has(.boostbutton:not([hidden])) .hud__corner{ align-self: start; }
/*
 * A SHORT screen with a boost button (a phone on its side): the minimap sits
 * BESIDE the Top 10, in the top row, not under it.
 *
 * Under the board it needs the board's height, its own and a bottom control's
 * (Boost on one side, sound and exit on the other) stacked in one column, and
 * a phone on its side is about 300px tall once Safari's bars and the home
 * strip are out: Boost sat on the minimap, and with Boost on the left the
 * sound and exit buttons did (Gray, 30 Sep 2026, with screenshots: "you
 * should have know this was an issue because you can clearly see it wouldnt
 * fit"). The top row is the one place no bottom control can reach, whichever
 * side Boost is on.
 */
@media (orientation: landscape) and (max-height: 540px) {
  .game:has(.boostbutton:not([hidden])) .hud{
    grid-template-areas:
      "stats corner board"
      ".     .      .    "
      ".     .      .    ";
  }
  .game:has(.boostbutton:not([hidden])) .hud__corner{ align-self: start; justify-self: end; }
}
/*
 * The power-up row is NOT hidden by an overlay-keyed rule here (it once
 * was, the same shape as the boost button above) — hud.ts's `blocked`
 * field, driven directly by main.ts's own `phase === "playing"`, is now
 * the one place that decides it. Every one of these `:has()` rules depends
 * on some OTHER code path keeping an overlay's `hidden` attribute in sync
 * with reality, forever, and two different overlays already broke that
 * promise this same week (the death screen once, the front menu once) in
 * two different ways — the same bug, over and over, because there was no
 * single place it could be fixed for good. See hud.ts.
 */

/*
 * Exactly one scrollbar, ever.
 *
 * The overlay used to scroll and the skin list scrolled inside it, so a tall
 * menu in a short arena produced a scrollbar inside a scrollbar — and the Play
 * button could scroll out of sight. Now the overlay never scrolls; a simple
 * panel scrolls as a whole, and the menu pins its head and scrolls one region
 * beneath it.
 */
/*
 * The menu floats ON the arena, it does not cover it.
 *
 * The backdrop used to be an almost-opaque radial, which meant the first thing
 * a new player saw was a dark rectangle — the same first impression as a page
 * that has not loaded. Behind it now is a live arena (see attract.ts), so the
 * backdrop's job is to keep the panel readable over moving worms, not to hide
 * them: a vignette that darkens the edges and leaves the middle alive.
 */
.overlay{
  position: absolute; inset: 0; z-index: 6;
  display: grid; place-items: center; padding: 16px;
  /*
   * The panel is CENTRED. No lane.
   *
   * This used to reserve 148px on the left for the sound dock, which fixed a
   * real overlap on phones and created a permanent one-sided offset
   * everywhere else — a panel visibly pushed off centre, which is what Gray
   * spotted.
   *
   * The offset was never needed above the phone breakpoint. Measured, not
   * guessed: the panel is min(430px, 100%) and the dock ends at 14px + 148px =
   * 162px, so the panel's left edge is (viewport - 430) / 2, and at the
   * narrowest desktop width the phone rules do not cover — 768px — that is
   * 169px. Clear of the dock at every width from there up, and widening the
   * viewport only pushes them further apart.
   *
   * Below 768px the phone block does not use a lane either: it pins the panel
   * to the top instead, which gathers the slack into one bottom strip the dock
   * and the scroll cue share. tools/check-layout.mjs measures the real gap
   * across the range so this cannot regress into an overlap again.
   */
  background:
    radial-gradient(ellipse 120% 90% at 50% 45%, rgba(4, 6, 12, 0.34), rgba(4, 6, 12, 0.86) 78%);
  overflow: hidden;
}
/* Death, privacy and how-to still want the room dark — they are read, not
   looked past. */
.overlay:has(.panel--death),
.overlay[data-game-lobby]{
  background: radial-gradient(ellipse 120% 90% at 50% 45%, rgba(4, 6, 12, 0.34), rgba(4, 6, 12, 0.78) 80%);
}
.overlay:has(.panel__list):not([data-game-lobby]){
  background: radial-gradient(circle at 50% 40%, rgba(10, 14, 26, 0.86), rgba(4, 6, 12, 0.96));
}
/*
 * Settings is read, not looked past, and it sits ON the menu.
 *
 * At the ordinary backdrop strength the menu panel behind it stayed legible
 * through the blur — the wordmark and the NAME YOUR WORM field showing as a
 * ghost rectangle behind the word "Settings", which reads as a rendering fault
 * rather than as depth.
 */
.overlay--settings{
  background: radial-gradient(circle at 50% 40%, rgba(10, 14, 26, 0.9), rgba(4, 6, 12, 0.97));
}
/*
 * The house panel, in the violet of the game's own screens (the wheel, the
 * reveal, the death screen) rather than the navy it started in, so every
 * pop-up reads as one family (Gray, 27 Sep 2026: "inline with rest of site
 * modals").
 */
.panel{
  width: min(560px, 100%); border-radius: var(--radius); padding: 20px; max-height: 100%; overflow-y: auto;
  background:
    radial-gradient(circle at 50% 0%, rgba(255, 255, 255, 0.12), transparent 45%),
    radial-gradient(ellipse 120% 100% at 50% 10%, #5a48c4, #3a2c9a 50%, #221660 100%);
  border: 4px solid #fff; box-shadow: 0 0 0 2px #7a2a6a, 0 14px 30px -12px rgba(0, 0, 0, 0.55);
}
/*
 * There is no `.panel--menu` any more.
 *
 * It was a 430px card with its own background, border and scrollbar floating
 * in the middle of a live arena — the single thing that made this read as a
 * game embedded in a website. The front screen is laid straight onto the
 * arena now; see `.menu` above.
 */
.panel__head{
  flex: 0 0 auto; padding: 22px 22px 20px;
  border-bottom: 1px solid var(--line);
  border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
  text-align: center;
}
/* The wordmark is the title. A game's front page says its name in its own
   lettering, not in the body face at 1.5rem. */
.panel__mark{ display: block; height: 54px; width: auto; margin: 2px auto 12px; }
.panel__head .panel__title{ font-size: 1.12rem; margin: 0 0 4px; }
.panel__head .panel__sub{ font-size: 0.9rem; margin: 0 0 16px; }
.panel__head .field__label{ text-align: left; }
.panel__head .button--big{ margin-bottom: 0; }
/* min-height:0 is what actually lets a flex child scroll instead of growing. */
.panel__scroll{ flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 4px 20px 20px; }
.panel__section--first{ margin-top: 16px; }
/*
 * The menu's foot: pinned like the head, for the same reason.
 *
 * Settings used to be a section inside the scroller, under forty skin tiles.
 * A control you have to scroll past a wall of cosmetics to reach is a control
 * most people never find.
 */
.panel__foot{
  flex: 0 0 auto; display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;
  padding: 12px 18px 14px; border-top: 1px solid var(--line);
}
.panel__foot .button{ flex: 0 1 auto; }
.panel--death{
  text-align: center; width: min(480px, 100%); padding: 26px 22px 22px;
  /*
   * Arrives, rather than appears. The arena is still playing underneath — the
   * burst, the body coming apart, the food it drops — so the panel comes up
   * from 80% and stays a little transparent at rest, which keeps the thing that
   * just happened visible behind the number.
   */
  /*
   * The reward reveal's room, not a dark glass sheet (Gray, 27 Sep 2026:
   * "inline with rest of site modals"): violet with light rays behind the
   * number, the house white rim.
   */
  position: relative; isolation: isolate;
  background:
    radial-gradient(circle at 50% 18%, rgba(255, 255, 255, 0.16), transparent 40%),
    radial-gradient(ellipse 120% 90% at 50% 24%, #7a64ff, #4332b8 46%, #1f1760 92%);
  animation: deathIn 260ms cubic-bezier(0.16, 0.9, 0.3, 1) both;
}
/* The rays turn inside a clip the size of the panel, so they never make it scroll. */
.death__rays{ position: absolute; z-index: -1; inset: 0; overflow: hidden; pointer-events: none; border-radius: inherit; }
.death__rays::before{
  content: ""; position: absolute; inset: 0;
  background: repeating-conic-gradient(from var(--ray-turn) at 50% 18%, rgba(255, 255, 255, 0.12) 0 9deg, transparent 9deg 24deg);
  -webkit-mask: radial-gradient(circle at 50% 18%, #000 51px, transparent 318px); mask: radial-gradient(circle at 50% 18%, #000 51px, transparent 318px);
  animation: ray-turn 24s linear infinite;
}
/*
 * No sideways scrollbar, ever.
 *
 * Gray, 10 Sep 2026: "the death screen modal flickers with the play again
 * bounce". The panel scrolls (overflow-y:auto, so x is auto too), and at the
 * widest point of the jelly the Play Again button reached a pixel past the
 * padding. An 11px horizontal scrollbar came and went on every bounce, and the
 * content jumped with it. Stepped through the bounce in 2ms steps in Chrome:
 * 19 of 51 steps showed the bar before this, 0 of 51 after. The 6px of room
 * keeps the stretch inside the panel, so nothing is clipped either.
 */
.panel--death{ overflow-x: hidden; }
.panel--death .panel__actions{ padding-inline: 6px; }

/*
 * The death screen on a phone turned sideways: 292px tall in iPhone Safari.
 * The panel scrolled, with the score and Play Again half out of view. Here the
 * score comes down, the buttons sit side by side, and the share row goes (the
 * menu has its own share button).
 */
@media (max-height: 420px) and (orientation: landscape) {
  /* The lobby's room is built from the same pieces (a label, one big number,
     a stack of actions) and had the same fault: Leave sat below the fold at
     812x375 (measured 14 Sep 2026). So it takes the same landscape rules. */
  :is(.panel--death, .panel--lobby){ padding: 12px 18px; width: min(620px, 100%); }
  .death__score, .panel__display{ font-size: 2.4rem; line-height: 1.05; }
  .death__cause{ margin-bottom: 6px; }
  /* Every visible button on one row: Revive comes and goes, Main menu stays. */
  :is(.panel--death, .panel--lobby) .panel__actions{ grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); margin-top: 8px; }
  .panel--death .share{ display: none; }
  /* The member list, one line apart rather than a paragraph apart. */
  .panel--lobby .panel__list{ margin-bottom: 4px; }
  :where(.panel--lobby .panel__list) :where(li){ margin: 0.15em 0; }
  /* Small enough that "Play Again" stays on one line in a third of the row,
     and the label above the score stays in view. */
  :is(.panel--death, .panel--lobby) .panel__actions > .button{ padding: 6px 10px 8px; font-size: 1.1rem; min-height: 0; }
  .death__label, .panel__eyebrow{ font-size: 0.66rem; }
  /* The revive offer is one cell of that row: its button the size of its neighbours', its words allowed to wrap (German). */
  .panel--death .revive{ gap: 3px; }
  .panel--death .revive__go{ padding: 6px 8px 8px; font-size: 0.95rem; min-height: 44px; line-height: 1.05; white-space: normal; }
  .panel--death .revive__note{ font-size: 0.74rem; line-height: 1.1; }
  .panel--death .revive__secs{ font-size: 0.78rem; }
}
@keyframes deathIn {
  from { opacity: 0; transform: scale(0.8); }
  to   { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .panel--death{ animation: none; }
}
/* The backdrop behind it comes up too, so the arena is not slapped with a
   full-strength dim on the frame you died. */
.overlay:has(.panel--death){ animation: deathBackdrop 260ms ease both; }
@keyframes deathBackdrop { from { opacity: 0; } to { opacity: 1; } }

/* One scrollbar left, so make it look deliberate rather than borrowed. */
.panel, .panel__scroll { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.panel::-webkit-scrollbar, .panel__scroll::-webkit-scrollbar { width: 10px; }
.panel::-webkit-scrollbar-track, .panel__scroll::-webkit-scrollbar-track { background: transparent; }
.panel::-webkit-scrollbar-thumb, .panel__scroll::-webkit-scrollbar-thumb {
  background: var(--line); border-radius: 99px;
  border: 3px solid var(--ink-2); background-clip: padding-box;
}
.panel__scroll::-webkit-scrollbar-thumb:hover{ background: #3A4460; }

/*
 * The death screen.
 *
 * One number is the result of the run and everything else is context, so the
 * length is set at five times the size of the label above it. The old panel
 * gave the score, the rank, the best and the killer the same weight, which
 * made the player hunt for the one thing they wanted to know.
 */
.death__label, .panel__eyebrow{
  margin: 0; font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.14em; color: var(--text-dim);
}
.death__label{ color: #ffe45c; letter-spacing: 0.3em; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.35); }
/* The length in the reveal's gold layered type (.fx, as .reward__num). */
.death__score .fx{ font-family: "Lilita One", var(--font-display); font-weight: 400; letter-spacing: 0; }
.death__score .fx::before{ color: #3b1a00; -webkit-text-stroke: 0.14em #3b1a00; filter: drop-shadow(0 0.06em 0 #3b1a00) drop-shadow(0 0.1em 0.1em rgba(0, 0, 0, 0.5)); }
.death__score .fx::after{ color: #fff3b0; -webkit-text-stroke: 0.05em #fff3b0; }
.death__score .fx > span{ background-image: linear-gradient(180deg, #fffbe6 0%, #ffe45c 42%, #ffc21a 60%, #ff9a00 100%); }
.death__score, .panel__display{
  margin: 2px 0 6px;
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(3.2rem, 15vw, 4.6rem); line-height: 1;
  letter-spacing: -0.04em; color: var(--accent);
  font-variant-numeric: tabular-nums;
}
/* A code to pass on: one click or tap selects all of it, where the game's
   no-select rule (.game) made it impossible to select at all (Gray, 15 Sep 2026:
   "the code aint selectable if you double click it"). */
.panel__display--code{
  -webkit-user-select: all; user-select: all; cursor: text;
}
/* The run's numbers as picture tiles (ui/death.ts statTile). */
.death__stats{
  margin: 6px 0 12px; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 6px;
}
.death__stat{
  display: grid; justify-items: center; gap: 0; min-width: 0; padding: 6px 2px 8px; border-radius: 16px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.05));
  border: 2px solid rgba(255, 255, 255, 0.18);
}
.death__stat img{ width: 34px; height: 34px; object-fit: contain; filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.4)); }
.death__stat b{ font-family: "Lilita One", var(--font-display); font-weight: 400; font-size: clamp(0.95rem, 3.6vw, 1.3rem); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.1; color: #fff; text-shadow: 0 2px 0 #1b0431; font-variant-numeric: tabular-nums; }
.death__stat small{ font-size: 0.62rem; white-space: nowrap; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(255, 255, 255, 0.7); }
/* A new personal best: a gold ribbon under the number. */
.death__best{ margin: -4px 0 2px; }
.death__best[hidden]{ display: none; }
.death__ribbon{
  display: inline-block; padding: 4px 18px 5px; border-radius: 8px; rotate: -3deg;
  background: linear-gradient(180deg, #ffe45c, #ff9f1c); border: 3px solid #fff; box-shadow: 0 3px 0 #8a4a00;
  font-family: "Lilita One", var(--font-display); font-size: 1.1rem; color: #fff; text-shadow: 0 2px 0 #7a3d00;
  animation: tile-pulse 1.3s ease-in-out infinite;
}
.death__cause{
  margin: 0 auto 12px; display: table; padding: 6px 14px; border-radius: 999px;
  background: rgba(10, 6, 40, 0.4); color: rgba(255, 255, 255, 0.85); font-size: 0.92rem;
}
.death__cause:empty{ display: none; }
/* Hide this name / Report name: a link's height is its text's; a finger needs 44px. */
.death__report .linkbutton{ display: inline-flex; align-items: center; min-height: 44px; }

/* Share */
.share{ margin-top: 18px; border-top: 1px solid var(--line); padding-top: 14px; }
.share__label{
  margin: 0 0 9px; font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.14em; color: var(--text-dim);
}
.share__row{ display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; }
.share__btn{ display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px 9px; border-radius: 14px; font-size: 0.98rem; text-decoration: none; cursor: pointer; --rim: 3px; }
@media (hover: hover){ .share__btn:hover{ filter: brightness(1.05); } }
/* Confirmation replaces the label rather than appearing beside it, so the row
   does not reflow and shift the buttons under the player's cursor. */
/*
 * The death screen's row: equal round buttons in the brands' own colours, one
 * row that never wraps and never changes size (Gray, 27 Sep 2026: "these
 * buttuns ugly and they keep resizing"). Candy bodies, brand-tinted.
 */
.share__row--round{ display: flex; flex-wrap: nowrap; justify-content: center; gap: clamp(6px, 2.4vw, 12px); }
.share__btn.share__btn--round{
  width: clamp(44px, 12vw, 54px); height: clamp(44px, 12vw, 54px); flex: none; padding: 0; justify-content: center;
  border-radius: 50%; --rim: 3px; --ink: #fff; text-shadow: none;
}
.share__btn.share__btn--round svg{ width: 52%; height: 52%; filter: drop-shadow(0 2px 0 var(--ts)); }
.share__btn--round[hidden]{ display: none; }
.share__btn.share__btn--native{ --bodyA: #c7b2ff; --bodyB: #8a5cff; --bodyC: #5f33d6; --ts: #35187f; }
.share__btn.share__btn--x{ --bodyA: #4a4a4a; --bodyB: #1a1a1a; --bodyC: #000; --ts: #000; }
.share__btn.share__btn--reddit{ --bodyA: #ff8a52; --bodyB: #ff4500; --bodyC: #c73600; --ts: #7a2100; }
.share__btn.share__btn--facebook{ --bodyA: #5b9bff; --bodyB: #1877f2; --bodyC: #0f5bc4; --ts: #083a80; }
.share__btn.share__btn--whatsapp{ --bodyA: #6ff29a; --bodyB: #25d366; --bodyC: #149c48; --ts: #0a5a28; }
.share__btn.share__btn--copy{ --bodyA: #ffd27a; --bodyB: #ff9f1c; --bodyC: #d97b00; --ts: #7a3d00; }
.share__btn.is-done{ --bodyA: #ff9ddf; --bodyB: #ff4fad; --bodyC: #d11f7c; --ts: #7a0f48; --ink: #fff; }

/* On a content page the row is left-aligned with the prose, not centred. */
.share--page{ margin: 2.4em 0 0; border-top: 1px solid var(--line); padding-top: 16px; }
.share--page .share__row{ justify-content: flex-start; }
.panel__title{ font-family: var(--font-display); font-size: 1.5rem; margin: 0 0 6px; }
.panel__sub{ margin: 0 0 16px; color: var(--text-dim); }
.panel__section{ font-size: 1rem; margin: 20px 0 4px; }
.panel__hint{ margin: 4px 0 10px; font-size: 0.85rem; color: var(--text-dim); min-height: 1.2em; }
.panel__note{ margin: 10px 0 0; font-size: 0.79rem; color: var(--text-dim); }
.panel__links{ margin: 14px 0 0; font-size: 0.88rem; }
.panel__list{ margin: 0 0 16px; padding-left: 1.15em; text-align: left; color: var(--text-dim); font-size: 0.92rem; }
:where(.panel__list) :where(li){ margin: 0.55em 0; }
.panel__list strong{ color: var(--text); }

/* A button that reads as a link — used where a real link would be an outbound
   link, which portal builds are rejected for. */
.linkbutton{
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--accent); cursor: pointer; text-decoration: underline;
  text-underline-offset: 3px;
}
@media (hover: hover){ .linkbutton:hover{ color: var(--accent-2); } }
.linkbutton:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.linkbutton:disabled{ text-decoration: none; cursor: default; opacity: 0.8; }
/* Dim: a way out that must not compete, such as Hide or Report on the death
   screen. It had its own copy of this whole rule as .death__reportbtn. */
.linkbutton--dim{ color: var(--text-dim); padding: 2px 4px; text-underline-offset: 2px; }
@media (hover: hover){ .linkbutton--dim:hover{ color: var(--text); } }
.panel__actions{ display: grid; gap: 10px; margin-top: 16px; }

.field{ display: block; margin-bottom: 14px; }
.field__label{ display: block; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--text-dim); margin-bottom: 5px; }
.field__input{
  width: 100%; font: inherit; padding: 12px 14px; border-radius: var(--radius-sm);
  /* Never under 16px: Safari zooms the page in on a smaller text box (6 Oct 2026). */
  font-size: max(16px, 1em);
  border: 1px solid var(--line); background: var(--ink); color: var(--text);
}

.radio{ margin-top: 6px; }
.radio__stations,
.region__choices{ display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
/* The same grid for the floors — four of them, two up on a narrow panel. */
.stations{ display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
.station.is-locked{ opacity: 0.55; cursor: not-allowed; }
.station.is-locked .station__name{ color: var(--text-dim); }
.region{ margin-top: 6px; }
.station{
  display: grid; gap: 2px; padding: 8px 9px; cursor: pointer; text-align: left;
  background: var(--ink-3); border: 2px solid transparent; border-radius: var(--radius-sm);
  color: var(--text); font: inherit;
}
@media (hover: hover){ .station:hover:not(:disabled){ border-color: var(--line); } }
.station.is-selected{ border-color: var(--accent); }
.station.is-selected .station__name{ color: var(--accent); }
.station:disabled{ opacity: 0.5; cursor: not-allowed; }
.station__name{ font-size: 0.88rem; font-weight: 700; }
.station__desc{ font-size: 0.72rem; color: var(--text-dim); line-height: 1.35; }

.radio__transport{ display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.radio__transport .button{ padding: 8px 16px; }
.radio__volume{ flex: 1; display: flex; }
.radio__volume input{ width: 100%; accent-color: var(--accent); }
.radio__now{ margin: 8px 0 0; font-size: 0.82rem; color: var(--text-dim); }
.radio__credit{ margin: 3px 0 0; font-size: 0.72rem; color: var(--text-dim); opacity: 0.85; }

.toast{
  margin: 12px 0 0; padding: 9px 13px; border-radius: var(--radius-sm);
  background: rgba(110, 231, 183, 0.14); border: 1px solid rgba(110, 231, 183, 0.4);
  color: var(--accent); font-weight: 700; font-size: 0.9rem;
}

/* No longer a scroller of its own — .panel__scroll is the only one. */
.squirm-skins{ padding-right: 0; }
.squirm-skins__group + .squirm-skins__group{ margin-top: 12px; }
.squirm-skins__heading{
  margin: 0 0 6px; font-size: 0.72rem; text-transform: uppercase;
  letter-spacing: 0.09em; color: var(--text-dim); font-weight: 700;
}
.squirm-skins__row{ display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 7px; }
.squirm-skin{
  display: grid; gap: 2px; padding: 5px; cursor: pointer;
  background: var(--ink-3); border: 2px solid transparent; border-radius: var(--radius-sm);
  color: var(--text); font: inherit;
}
.squirm-skin__art{ width: 100%; height: 40px; display: block; }
.squirm-skin__name{ font-size: 0.72rem; color: var(--text-dim); }
@media (hover: hover){ .squirm-skin:hover:not(:disabled){ border-color: var(--line); } }
.squirm-skin.is-selected{ border-color: var(--accent); }
.squirm-skin.is-selected .squirm-skin__name{ color: var(--accent); }
.squirm-skin.is-locked{ cursor: not-allowed; opacity: 0.72; }

@media (max-width: 720px) {
  body { font-size: 16px; }
  /* `.game{ aspect-ratio: 3/4 }` used to live here, from when the arena was a
     box on the page. It is the viewport now — see `main > .game`. */
  .hud__board{ min-width: 0; }
  .hud__minimap{ width: 82px; height: 82px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/*
 * The seam between the arena and the article.
 *
 * The arena ends at the window edge and the prose begins immediately under it,
 * so without a marker the first heading reads as the page having scrolled by
 * accident. A small lime eyebrow says "this is the write-up, deliberately".
 */
.eyebrow{
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent); margin: 26px 0 0;
}
.prose section:first-of-type .eyebrow + h2{ margin-top: 0.3em; }

/* ------------------------------------------------------------ sound dock */
/*
 * Above the overlay (z-index 6), because that is the whole point: with sound
 * off, the old chips sat *under* the menu and could be seen but not clicked.
 */
/*
 * Hidden while the death screen is up.
 *
 * The dock sits above the overlay so sound can be changed from the menu — but
 * on the death screen that put a floating pill through the middle of the share
 * buttons. Death is a modal moment with its own actions; the controls are back
 * the instant you are in the arena or the menu again.
 */
.game:has(.overlay[data-game-death]:not([hidden])) .dock{ display: none; }

.dock{
  position: absolute;
  left: calc(14px + var(--safe-l)); bottom: calc(14px + var(--safe-b)); z-index: 9;
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
}
/*
 * The corner does not move any more, because it is not on screen at the same
 * time as anything it could collide with — the menu hides it outright (see the
 * `.menu` block) and puts Sound and Settings in its top rail as words.
 *
 * The rule that used to live here lifted it over the bottom link bar with
 * `bottom: calc(var(--linkbar-h) + 10px)` and a transition. Worth remembering
 * why it went: a `transition` on a value fed by a custom property that
 * JavaScript sets after first paint left the property parked on its FALLBACK
 * permanently — measured 58px with the variable sitting at 70px, unchanged
 * whatever it was set to afterwards. Never transition a property whose value
 * comes from a measured variable.
 */
.dock__bar{
  display: flex; gap: 6px; padding: 5px;
  border-radius: 999px; border: 1px solid var(--line);
  background: rgba(7, 9, 15, 0.55);
}
@supports (backdrop-filter: blur(1px)) {
  .dock__bar{ background: rgba(7, 9, 15, 0.34); backdrop-filter: blur(12px); }
}
.dock__icon{ width: 44px; height: 44px; display: grid; place-items: center; padding: 0; border-radius: 50%; cursor: pointer; font-size: 0.95rem; line-height: 1; --rim: 3px; }
.dock__icon[hidden]{ display: none; }
@media (hover: hover){ .dock__icon:hover{ background: rgba(255, 255, 255, 0.07); color: var(--text); } }
.dock__icon:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.dock__icon[aria-expanded="true"]{ background: var(--accent); color: var(--accent-ink); }
.dock__glyph{
  width: 18px; height: 18px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.dock__speaker-body{ fill: currentColor; stroke: currentColor; stroke-width: 1.2; }
.dock__glyph--leave{ stroke-width: 2; }
/* Waves when there is sound, a cross when there is not. One icon, two states. */
.dock__cross{ opacity: 0; }
/* The game root carries is-silent (ui/sound.ts refresh), not the dock: keyed
   on .dock, the speaker never changed at all (Gray, 28 Sep 2026: "no matter
   what you click it, it stays the same design"). Every mute button, the
   dock's speaker shows the cross (the menu's swaps its own icon on aria-pressed). */
.game.is-silent .dock__wave{ opacity: 0; }
.game.is-silent .dock__cross{ opacity: 1; }
.game.is-silent .dock__icon[data-game-mute]{ color: var(--warn); }

/*
 * Four bars, at rest when nothing is playing and dancing when it is. It is the
 * only ornament in here and it earns its place: it is the one part of the
 * control that tells you the radio is running without opening anything.
 */
.eq{ display: flex; align-items: flex-end; gap: 2px; height: 14px; }
.eq i{ width: 3px; height: 4px; border-radius: 1px; background: var(--text-dim); transition: height 0.2s ease; }
@media (hover: hover){ .dock__icon:hover .eq i{ background: var(--text); } }
.eq.is-playing i{ background: var(--accent); animation: bounce 900ms ease-in-out infinite; }
.eq.is-playing i:nth-child(1){ animation-delay: 0ms; }
.eq.is-playing i:nth-child(2){ animation-delay: 140ms; }
.eq.is-playing i:nth-child(3){ animation-delay: 300ms; }
.eq.is-playing i:nth-child(4){ animation-delay: 80ms; }
@keyframes bounce { 0%, 100% { height: 4px; } 50% { height: 14px; } }
@media (prefers-reduced-motion: reduce) {
  .eq.is-playing i{ animation: none; height: 9px; }
}

/*
 * The dock's popover is gone.
 *
 * It held one copy of the sound switches while the menu held another, and the
 * two disagreed — see settings.ts. There is one settings screen now, and the
 * corner is two buttons that reach it.
 */
.dock__btn{ flex: 1; padding: 5px 12px 8px; border-radius: 12px; cursor: pointer; font-size: 0.92rem; --rim: 3px; }
@media (hover: hover){ .dock__btn:hover{ background: rgba(255, 255, 255, 0.09); } }
.dock__btn:disabled{ opacity: 0.4; cursor: default; }

/* ------------------------------------------------------- the settings screen */
.panel--settings{
  width: min(460px, 100%);
  /* The link bar fades out while this is up, so the whole height is free —
     nothing to reserve, and no constant to go stale. */
  max-height: 100%;
  padding: 0; overflow: hidden; display: flex; flex-direction: column;
  background: rgba(16, 20, 31, 0.86);
  border-color: rgba(255, 79, 173, 0.18);
  box-shadow: 0 30px 80px -40px #000, 0 0 0 1px rgba(0, 0, 0, 0.4);
}
@supports (backdrop-filter: blur(1px)) {
  .panel--settings{ background: rgba(16, 20, 31, 0.7); backdrop-filter: blur(18px) saturate(130%); }
}
/* Title on the left, the way out on the right — the one control on this screen
   that must never need scrolling to, for the same reason Play does not. */
.panel__head--row{
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; text-align: left; padding: 16px 18px;
}
.panel__head--row .panel__title{ margin: 0; font-size: 1.2rem; }

.setting{ display: flex; flex-direction: column; gap: 8px; padding: 16px 0; }
.setting + .setting{ border-top: 1px solid var(--line); }
.setting:first-child{ padding-top: 6px; }
.setting__row{ display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.setting__label{
  margin: 0; font-family: var(--font-display); font-size: 0.76rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-dim);
}
.setting__blurb{ margin: 0; font-size: 0.82rem; color: var(--text-dim); line-height: 1.45; }
.setting__slider{ display: block; }
.setting__slider input{ width: 100%; accent-color: var(--accent); }
.setting__transport{ display: flex; gap: 6px; }
.setting__now{ margin: 0; font-size: 0.76rem; color: var(--text-dim); line-height: 1.4; }
.setting__now.is-problem{ color: var(--warn); }
.setting__credit{ margin: 0; font-size: 0.72rem; color: var(--text-dim); line-height: 1.4; }

/* A switch, because on/off is a state and a button that says "Sound on" is a
   riddle about whether that is what it does or what it will do. */
.switch{ background: none; border: 0; padding: 0; cursor: pointer; }
.switch__track{
  display: block; width: 38px; height: 21px; border-radius: 999px;
  background: var(--ink-3); border: 1px solid var(--line);
  transition: background 0.16s ease, border-color 0.16s ease;
}
.switch__knob{
  display: block; width: 15px; height: 15px; margin: 2px; border-radius: 50%;
  background: var(--text-dim); transition: transform 0.16s ease, background 0.16s ease;
}
.switch[aria-pressed="true"] .switch__track{ background: rgba(255, 79, 173, 0.28); border-color: var(--accent); }
.switch[aria-pressed="true"] .switch__knob{ transform: translateX(17px); background: var(--accent); }
.switch:focus-visible{ outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 999px; }

/*
 * The corner survives on a phone now.
 *
 * It used to hide the settings button below 900px with a coarse pointer,
 * because what it opened was a 268px popover with three station chips and two
 * sliders crammed into the corner over a live arena. What it opens now is a
 * full screen with room to breathe, which is if anything BETTER on a phone
 * than on a desktop. The mute button was always here; the settings button
 * joins it.
 */

/* ------------------------------------------------------- the arena on a phone */
/*
 * Everything here is a fault visible in one portrait screenshot: the HUD boxes
 * were desktop-sized and ate the top of the arena, the leaderboard was nearly
 * opaque and covered a third of the width, the ping chip ran off the right
 * edge, and the corner controls sat underneath the boost button.
 */
@media (max-width: 767px) {
  /*
   * The readout on a phone.
   *
   * "this shit aint gonna work like that on mobile mate" — and it did not:
   * IN ARENA broke across two lines, which made the strip taller than the
   * leaderboard beside it and pushed the two into each other. Labels never
   * wrap now (see .hud__label) and the numbers come down a size.
   */
  .hud{ padding: calc(8px + var(--safe-t)) calc(8px + var(--safe-r))
                 calc(8px + var(--safe-b)) calc(8px + var(--safe-l)); }
  .hud__stats{ gap: 12px; }
  .hud__label{ font-size: 0.5rem; letter-spacing: 0.06em; }
  .hud__value{ font-size: 0.95rem; }
  .hud__stat:first-child .hud__value{ font-size: 1.1rem; }

  /* You are steering through this, on the smallest screen there is. */
  .hud__board{ font-size: 0.7rem; padding: 2px; max-width: 44vw; }
  .hud__board .hud__label{ font-size: 0.55rem; }

  /*
   * Bottom left, and it can sit right in the corner now.
   *
   * It used to be pushed a whole joystick-height up the screen, because the
   * joystick owned that corner during a run. Steering is drag-anywhere now and
   * the only touch control is the boost button, which is bottom RIGHT — so the
   * left corner is free and the dock stops floating oddly above nothing.
   */
  .dock{
    left: calc(12px + var(--safe-l)); right: auto; top: auto;
    bottom: calc(12px + var(--safe-b));
  }


  /* The minimap sat in the bottom-right corner, which on a phone is where the
     boost button is — they overlapped, and the boost button won. On a phone
     the template moves the corner slot up, directly under the leaderboard, in
     the one right-hand space nothing else claims. */
  .hud{
    grid-template-areas:
      "stats . board"
      ".     . corner"
      ".     . .     ";
  }
  .hud__corner{ align-self: start; }
  .hud__minimap{ width: 74px; height: 74px; }
}

/* ---------------------------------------------------------------- the secret
 *
 * The Konami reveal. Sits above everything at z-index 7 — one above the other
 * overlays — because it is shown while the menu is up and must land ON it, not
 * behind it.
 *
 * The suspense is carried by the transition rather than by any content: the
 * panel scales up from 80% while the words arrive on their own timers, so
 * there is a beat where something has clearly happened and you do not yet know
 * what. Same 20%-smaller entrance the death modal uses, deliberately, so the
 * game has one language for "a screen has arrived".
 */
.overlay--secret{
  z-index: 7;
  /*
   * Nearly opaque, and the panel below has its own ground.
   *
   * At 0.86 the menu read straight through it: "JOIN THE ARENA" sat inside the
   * word GALAXY and the skin picker showed through the copy. A reveal has to
   * be the only thing on screen, so this is a curtain rather than a tint.
   */
  background: radial-gradient(circle at 50% 45%, rgba(24, 10, 48, 0.97), rgba(3, 4, 8, 0.985) 70%);
  opacity: 0;
  transition: opacity 340ms ease;
  /* This one IS centred on the arena — it is a moment, not a working panel, so
     the dock lane the menu reserves does not apply. */
  padding: 16px;
}
.overlay--secret.is-in{ opacity: 1; }

.secret{
  text-align: center;
  max-width: 460px;
  width: min(460px, 100%);
  padding: 30px 26px 26px;
  border-radius: var(--radius);
  /* Its own ground, so nothing behind can read through the type even where the
     curtain is thinnest. */
  background: linear-gradient(168deg, #180C36, #08060F 78%);
  border: 1px solid #3A2668;
  box-shadow: 0 0 0 1px rgba(155, 77, 255, 0.16), 0 26px 60px rgba(0, 0, 0, 0.7),
              0 0 70px rgba(123, 59, 224, 0.22);
  transform: scale(0.8);
  opacity: 0;
  transition: transform 620ms cubic-bezier(.16, 1, .3, 1), opacity 500ms ease;
}
.overlay--secret.is-in .secret{ transform: scale(1); opacity: 1; }

/* The worm itself, landing with its name: the surprise is SEEING it (Gray, 26
   Sep 2026, after the first name code: "there was no big surprise it just
   unlocked?"). Empty until the name beat, then it springs in. */
.secret__art{
  display: block; width: 136px; height: 136px; margin: 0 auto 14px;
  transform: scale(0.3) rotate(-12deg); opacity: 0;
  filter: drop-shadow(0 0 26px rgba(229, 139, 234, 0.55));
  transition: transform 760ms cubic-bezier(.2, 1.6, .4, 1), opacity 380ms ease;
}
.secret__art.is-in{ transform: scale(1) rotate(0); opacity: 1; }
.secret__eyebrow{
  font-family: var(--font-display); font-weight: 700; font-size: 0.68rem;
  letter-spacing: 0.24em; text-transform: uppercase;
  color: #7FE8FF; margin: 0 0 10px; min-height: 1em;
}
.secret__name{
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(2.2rem, 9vw, 3.6rem); line-height: 1; margin: 0;
  min-height: 1em;
  /* The skin's own palette, as type. Falls back to plain white where
     background-clip: text is unsupported, which is legible either way. */
  color: #F2F5FA;
  background: linear-gradient(96deg, #7FE8FF, #FFFFFF 38%, #C2338F);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.secret__sub{
  margin: 12px 0 0; color: var(--text-dim); font-size: 0.95rem; min-height: 1.4em;
}
.secret__bar{
  margin: 16px auto 0; width: min(280px, 70vw); height: 6px;
  border-radius: 999px; background: var(--ink-3);
  border: 1px solid var(--line); overflow: hidden;
}
.secret__bar i{
  display: block; height: 100%; width: 0%; border-radius: 999px;
  background: linear-gradient(90deg, #22348F, #C2338F, #7FE8FF);
  transition: width 900ms cubic-bezier(.16, 1, .3, 1);
}
.secret__actions{ margin-top: 22px; }

@media (prefers-reduced-motion: reduce) {
  .overlay--secret, .secret, .secret__bar i, .secret__art { transition-duration: 1ms; }
}

/* ------------------------------------------------------- a phone held sideways
 *
 * This is how people actually play, and nothing was built for it. The phone
 * rules keyed off `max-width: 767px`, and a phone in landscape is 844px wide —
 * so it got the DESKTOP layout in 390px of height: a menu panel taller than
 * the screen, and a HUD sized for a laptop.
 *
 * Keyed on HEIGHT, because that is the thing that is scarce. It applies to a
 * short window on a desktop too, which is correct — the layout has the same
 * problem there.
 */
@media (max-height: 500px) {

  /* The HUD is glanceable, not readable — shrink it rather than let it cover
     a third of a 390px-tall arena. */
  .hud__board{ font-size: 0.72rem; width: 10.8rem; }
  /* Top 5 is plenty. This was a zero-specificity :where() rule and lost to
     .squirm-board__row's own `display: grid`, so a phone on its side showed all ten
     (and the row size below) over 60% of a 390px arena — Gray, 19 Sep 2026. */
  .squirm-board > .squirm-board__row:nth-child(n + 6):not(.is-you){ display: none; }
  .hud__board .squirm-board__row{ font-size: 0.72rem; line-height: 1.35; }
  /* The minimap sat in the bottom-right corner, which is where the boost button is on a
     phone held sideways as much as upright: the boost button covered it. The corner slot
     goes under the leaderboard, as the upright phone's does (the 767px block below). */
  .hud{ grid-template-areas: "stats . board" ". . corner" ". . ."; }
  .hud__corner{ align-self: start; }
  .hud__minimap{ width: 74px; height: 74px; }

  /* Both thumbs are already on the glass; the controls can be smaller and
     tucked further in — but never inside a safe area. In landscape that is
     where the Dynamic Island and the home indicator live, and a mute button
     under either is a mute button you cannot press. */
  :root { --boost-size: 74px; --boost-inset: 14px; }
  .dock{
    bottom: calc(8px + var(--safe-b));
    left: calc(8px + var(--safe-l));
  }
  /* Small on phones, per the brief — the row can otherwise crowd the boost
     button and the sound dock either side of it. */
  .powerupbar{ gap: 8px; bottom: calc(10px + var(--safe-b)); }
  .powerupbar__item{ --pu-size: 56px; }
  .powerupbar__glyph{ width: calc(100% - 16px); height: calc(100% - 16px); font-size: 1rem; }
}

/*
 * Nothing anywhere may rely on the page scrolling to reach the game.
 *
 * A phone that rotates mid-run must not leave the arena half off-screen, and
 * the arena is sized from its own box rather than the viewport, so this is the
 * one rule that keeps them agreeing.
 */
@media (max-height: 500px) and (orientation: landscape) {
  .game{ height: 100dvh; max-height: 100dvh; }
}

/* ==========================================================================
   THE SETTINGS PAGE

   A real page, not a panel over the arena. The shape comes from measuring
   eight of these: Krunker runs eight categories deep, Shell Shockers splits
   tabs from a two-column list, agar.io hangs a gear off the front screen —
   and every one worth copying separates NAVIGATION from CONTENT rather than
   stacking six sections in one column. Two levels, never three: that is the
   depth at which players stop knowing where they are, and it is the one
   number every game-UI guide agrees on.

   It reads as a DOCUMENT, not as an overlay. A shade lighter than the arena,
   with a real column, real dividers and one accent — the arena is a place,
   this is a desk.
   ========================================================================== */

/*
 * Out of the reading measure.
 *
 * `main > *` centres every direct child on --max (46rem / 736px), which is
 * right for prose and wrong for a two-column settings page: measured at
 * 1512px, the whole thing rendered 640px wide and left-shifted, with the skin
 * grid squeezed to two columns. The arena breaks out of the same rule the same
 * way — see `main > .game`.
 */
.page--settings main{ padding: 0; }
.page--settings main > *{ width: 100%; max-width: none; margin-inline: 0; }
/*
 * Colour, not a dark dashboard.
 *
 * "more colour, bright colours, thats what kids want, no cooperate looking
 * squared sharp business look" — this was near-black with hairline dividers,
 * which is a settings screen for an IDE. It is a deep blue-violet now with
 * three coloured glows in it, and every surface on top is a rounded card with
 * a lit edge rather than a 1px box.
 */
.page--settings{
  background:
    radial-gradient(760px 500px at 8% 0%, rgba(139, 92, 246, 0.30), transparent 66%),
    radial-gradient(820px 540px at 96% 6%, rgba(44, 143, 214, 0.28), transparent 68%),
    radial-gradient(900px 500px at 50% 100%, rgba(255, 79, 173, 0.10), transparent 70%),
    linear-gradient(180deg, #161B33 0%, #10152A 40%, #0B0F1E 100%);
  background-attachment: fixed;
  min-height: 100dvh;
}
.settings{ min-height: 100dvh; display: flex; flex-direction: column; }

/* -------------------------------------------------------------- the bar */
.settings__bar{
  position: sticky; top: 0; z-index: 20;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 16px;
  padding: calc(13px + var(--safe-t)) calc(clamp(14px, 4vw, 40px) + var(--safe-r))
           13px calc(clamp(14px, 4vw, 40px) + var(--safe-l));
  border-bottom: 2px solid rgba(255, 255, 255, 0.09);
  background: rgba(16, 21, 42, 0.9);
}
@supports (backdrop-filter: blur(1px)) {
  .settings__bar{ background: rgba(16, 21, 42, 0.7); backdrop-filter: blur(18px) saturate(150%); }
}
.settings__back{ justify-self: start; display: inline-flex; align-items: center; gap: 9px; text-decoration: none; font-size: 1.02rem; padding: 7px 18px 10px; border-radius: 999px; --rim: 4px; }
@media (hover: hover){ .settings__back:hover{ filter: brightness(1.07); } }
.settings__title{
  margin: 0; font-family: var(--font-display); font-weight: 600;
  font-size: 1.2rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--paper);
}
/*
 * The receipt.
 *
 * Writing to localStorage is instant and invisible, which reads as the control
 * not working. Deliberately not a toast: something that flies in over the page
 * on every slider tick would be worse than silence.
 */
.settings__saved{
  justify-self: end; margin: 0; font-size: 0.78rem; color: var(--accent);
  font-family: var(--font-display); font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  opacity: 0; transform: translateY(-2px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.settings__saved.is-on{ opacity: 1; transform: translateY(0); }

/* ------------------------------------------------------------ the split */
.settings__body{
  flex: 1 1 auto; width: 100%; max-width: 1120px; margin-inline: auto;
  display: grid; grid-template-columns: 208px minmax(0, 1fr);
  gap: clamp(24px, 4vw, 56px); padding: clamp(24px, 4vw, 44px) clamp(16px, 4vw, 40px) 80px;
}
/* Under the bar by the bar's MEASURED height (settings.ts sets --settings-bar-h),
   never a guessed number: a guess drifts from the bar and slides the rail under it. */
.settings__nav{ position: sticky; top: calc(var(--settings-bar-h, 70px) + 16px); align-self: start; }
.settings__nav ul{ list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
:where(.settings__nav) :where(a){
  display: block; padding: 11px 18px; border-radius: var(--radius-pill);
  color: var(--paper-dim); text-decoration: none;
  font-family: var(--font-display); font-weight: 600; font-size: 1rem;
  border-left: 0;
  /* Short. A 140ms cross-fade between two items reads, in a screenshot and to
     the eye, as two items being highlighted at once. */
  transition: color 0.08s ease, background 0.08s ease;
}
:where(.settings__nav) :where(a:hover){ color: var(--paper); background: rgba(255, 255, 255, 0.09); }
/* Set by the scroll spy in settings.ts, not by :target — :target only moves
   when a link is clicked, so scrolling past three sections left it lying. */
:where(.settings__nav) :where(a.is-current){ color: var(--accent-ink); background: var(--accent); }

.settings__pane{ min-width: 0; display: grid; gap: 44px; }

/* --------------------------------------------------------- a section */
/*
 * Each section is a CARD, not a run of hairlines.
 *
 * A soft raised surface with a lit top edge — the same object language as the
 * buttons. Hairline dividers on a flat ground is the look Gray called
 * corporate, and it is also harder to scan: nothing tells you where one
 * subject stops.
 */
.set-section{
  /* Clear of everything that sticks at the top, as measured (settings.ts). */
  scroll-margin-top: calc(var(--settings-sticky-h, 80px) + 16px);
  border-radius: var(--radius); padding: 24px clamp(18px, 3vw, 30px) 26px;
  background: rgba(255, 255, 255, 0.055);
  border: 2px solid rgba(255, 255, 255, 0.09);
}
@supports (backdrop-filter: blur(1px)) {
  .set-section{ backdrop-filter: blur(10px) saturate(120%); }
}
.set-section__head{ margin-bottom: 20px; }
.set-section__title{
  margin: 0 0 4px; font-family: var(--font-display); font-weight: 600;
  font-size: 1.7rem; letter-spacing: 0; color: var(--paper);
}
.set-section__blurb{ margin: 0; color: var(--paper-dim); font-size: 0.97rem; }

/* ------------------------------------------------------------- a row */
/*
 * Label and description on the left, the control on the right. This is the
 * anatomy every settings screen that is pleasant to use shares, and the
 * description is not optional: a switch called "Ornaments" with nothing under
 * it is a guess.
 */
.set-row{
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 16px 24px; align-items: center;
  padding: 18px 0; border-top: 2px solid rgba(255, 255, 255, 0.07);
}
.set-row:first-of-type{ border-top: 0; padding-top: 4px; }
.set-row--stack{ grid-template-columns: minmax(0, 1fr); align-items: stretch; }
.set-row--slider{ grid-template-columns: 150px minmax(0, 1fr); padding-top: 0; border-top: 0; }
.set-row__text{ min-width: 0; }
.set-row__label{
  margin: 0 0 3px; font-family: var(--font-display); font-weight: 600;
  font-size: 1.1rem; color: var(--paper);
}
.set-row__label label{ cursor: pointer; }
.set-row__sub{ color: var(--text-dim); font-size: 0.88rem; }
.set-row__desc{ margin: 0; color: var(--paper-dim); font-size: 0.9rem; line-height: 1.5; }
:where(.set-row__desc) :where(a){ color: var(--accent-2); }
.set-row__control{ justify-self: end; }

.set-input{
  width: min(320px, 100%); padding: 14px 18px; font: inherit; font-size: 1.05rem;
  font-weight: 600; color: #16233A; background: var(--paper);
  border: 0; border-radius: var(--radius-sm);
}
.set-input::placeholder{ color: #8496AE; font-weight: 500; }
.set-input:focus{ outline: 3px solid var(--accent); outline-offset: 2px; }
.set-slider{ width: 100%; accent-color: var(--accent); }
.set-note{ margin: 10px 0 0; font-size: 0.85rem; color: var(--paper-dim); line-height: 1.5; }
.set-note.is-problem{ color: var(--warn); }
.set-transport{ display: flex; gap: 8px; margin-top: 10px; }
.set-btn{ padding: 8px 22px 11px; border-radius: 16px; cursor: pointer; font-size: 1.05rem; }
@media (hover: hover){ .set-btn:hover{ filter: brightness(1.07); } }
.set-btn:disabled{ opacity: 0.4; cursor: default; }

/* One row is not like the others, and has to look it. */
.set-row--danger{ border-top: 1px solid rgba(255, 59, 92, 0.28); }
.set-row--danger .set-row__label{ color: var(--hot); }
.set-danger{ padding: 9px 24px 12px; border-radius: 16px; cursor: pointer; font-size: 1.1rem; }
@media (hover: hover){ .set-danger:hover{ filter: brightness(1.07); } }
.set-danger:disabled{ opacity: 0.4; cursor: not-allowed; filter: none; transform: none; }
.set-danger.is-armed{ animation: armed 0.9s ease-in-out infinite; }
@keyframes armed { 50% { filter: brightness(1.3); } }
@media (prefers-reduced-motion: reduce) { .set-danger.is-armed { animation: none; } }

/*
 * Erase's second screen: a warning, then a typed word — see
 * `panel--danger` below and ui/accountPane.ts. Gray, 16 Sep 2026: press-Erase-
 * twice was "too short... kids like to do tricks like that".
 */
.panel--danger, .panel--confirm{ width: min(440px, 100%); text-align: center; }
.panel--danger .panel__actions, .panel--confirm .panel__actions{ grid-auto-flow: column; grid-template-columns: 1fr 1fr; }
/*
 * The purchase confirmation (ui/buyConfirm.ts). The same centred two-button
 * panel as --danger, with the thing being bought as its picture and the price
 * as the page's big number. It sits over whichever shop opened it — Settings
 * and the Burrow are both full-screen overlays — so it stacks above them.
 */
.overlay--confirm{ z-index: 9; background: radial-gradient(circle at 50% 42%, rgba(10, 14, 26, 0.72), rgba(4, 6, 12, 0.92)); }
.panel--buy{ width: min(380px, 100%); text-align: center; animation: buy-in 180ms ease-out both; }
.panel--buy .panel__actions{ grid-auto-flow: column; grid-template-columns: 1fr 1.4fr; }
.buy__art{
  display: block; width: 132px; height: 132px; margin: 0 auto 10px; object-fit: contain;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.5));
}
.buy__art[hidden]{ display: none; }
.buy__name{ margin: 2px 0 6px; font-size: 1.5rem; }
.buy__price{ display: flex; align-items: center; justify-content: center; gap: 8px; margin: 0; }
.buy__price .panel__display{ margin: 0; font-size: clamp(2.2rem, 10vw, 2.8rem); color: #ffd166; }
.buy__nugget{ width: 34px; height: 34px; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5)); }
.buy__balance{ font-size: 0.86rem; }
.buy__balance.is-short{ color: #ff9aa9; font-weight: 700; }
@keyframes buy-in{ from{ transform: scale(0.92); opacity: 0; } to{ transform: scale(1); opacity: 1; } }
@media (prefers-reduced-motion: reduce){ .panel--buy{ animation: none; } }
.panel__input{
  display: block; width: 100%; margin-top: 14px; padding: 11px 14px; font: inherit;
  font-size: 1.15rem; font-weight: 700; letter-spacing: 0.08em; text-align: center;
  color: #22314d; background: #fff; border: 0; border-radius: 12px;
}
.panel__input::placeholder{ color: #22314d; opacity: 0.3; }
/*
 * The settings PAGE (unlike the game shell) has no positioned ancestor, and
 * can scroll taller than one viewport — an absolutely-positioned overlay
 * would centre itself on the whole scrolled page, not on what is on screen.
 * Fixed pins it to the viewport actually showing, wherever the page is
 * scrolled to.
 */
[data-set-wipe-warn], [data-set-wipe-type]{ position: fixed; }

/* ------------------------------------------------------ sign-in buttons */
/*
 * Each provider's own button is the house button in a brand role (.button
 * --flat with --google, --apple or --discord, in the Candy section): the brand
 * sets the fill and the logo, the house button the size, shape, press and
 * focus. Only the row they sit in is laid out here.
 */
.signin{
  display: grid; gap: 10px; margin-top: 14px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}

/* ------------------------------------------------------ choice cards */
/*
 * One of N. Used by quality, arena floors, radio stations and regions — four
 * controls that are the same question, so they are one component rather than
 * four near-identical sets of classes.
 */
.choices{
  display: grid; gap: 10px; margin-top: 14px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.choice{
  display: grid; gap: 4px; text-align: left; cursor: pointer;
  padding: 15px 18px 17px; border-radius: var(--radius-sm);
  border: 2px solid rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.06);
  color: var(--paper); font: inherit;
  transition: border-color 0.14s ease, background 0.14s ease, transform 0.12s ease;
}
@media (hover: hover){ .choice:hover:not(:disabled){ border-color: #38425c; background: rgba(255, 255, 255, 0.05); } }
.choice:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.choice__name{ font-family: var(--font-display); font-weight: 600; font-size: 1.05rem; }
.choice__desc{ color: var(--paper-dim); font-size: 0.82rem; line-height: 1.4; }
.choice.is-selected, .choices .station.is-selected {
  border-color: var(--accent); background: rgba(255, 79, 173, 0.16);
}
.choice.is-selected .choice__name{ color: var(--accent); }
.choice:disabled, .choice.is-locked { opacity: 0.45; cursor: not-allowed; }

/* The pickers reuse the arena's `.station` markup, so it maps onto this. */
.choices .station{
  display: grid; gap: 4px; text-align: left; cursor: pointer;
  padding: 15px 18px 17px; border-radius: var(--radius-sm);
  border: 2px solid rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.06);
  color: var(--paper); font: inherit;
}
.choices .station.is-selected{ border-color: var(--accent); background: rgba(255, 79, 173, 0.07); }
.choices .station.is-selected .station__name{ color: var(--accent); }
.choices .station__name{ font-family: var(--font-display); font-weight: 600; font-size: 1.05rem; }
.choices .station__desc{ color: var(--paper-dim); font-size: 0.82rem; line-height: 1.4; }

/* ------------------------------------------------------- the preview */
/*
 * The point of the skins section.
 *
 * Every cosmetic picker worth copying shows the thing at a size you can see it
 * — Shell Shockers spins a 3D character beside its loadout. A 96px tile is a
 * swatch; this is the worm, and it swims.
 */
.preview{
  margin: 20px 0 8px; border-radius: var(--radius); overflow: hidden;
  border: 2px solid rgba(255, 255, 255, 0.12);
  background:
    radial-gradient(700px 260px at 50% 120%, rgba(255, 79, 173, 0.09), transparent 70%),
    linear-gradient(180deg, #0D111C, #080B13);
}
.preview__canvas{ display: block; width: 100%; height: auto; aspect-ratio: 64 / 26; }
.preview__meta{
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  flex-wrap: wrap; padding: 12px 18px 14px; border-top: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.02);
}
.preview__name{
  margin: 0; font-family: var(--font-display); font-weight: 600; font-size: 1.3rem;
  color: var(--paper);
}
.preview__unlock{ margin: 0; color: var(--paper-dim); font-size: 0.87rem; }

/* ------------------------------------------------------ the skin grid */
.skinset{ display: grid; gap: 30px; margin-top: 10px; }
.skinset__heading{
  margin: 0 0 2px; display: inline-block;
  font-family: var(--font-display); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.15em; text-transform: uppercase; color: var(--accent);
  background: rgba(255, 79, 173, 0.12); padding: 6px 13px; border-radius: var(--radius-pill);
}
.skinset__how{ margin: 8px 0 12px; font-size: 0.86rem; color: var(--paper-dim); }
.skinset__grid{
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
}
.skintile{
  display: grid; gap: 2px; padding: 9px 9px 11px; cursor: pointer;
  border-radius: var(--radius-sm); border: 2px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.06); color: var(--paper); font: inherit;
  transition: border-color 0.14s ease, background 0.14s ease, transform 0.12s ease;
}
@media (hover: hover){
  .skintile:hover:not(:disabled){
    border-color: #38425c; background: rgba(255, 255, 255, 0.055); transform: translateY(-2px);
  }
}
.skintile:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.skintile__art{ display: block; width: 100%; height: auto; aspect-ratio: 176 / 100; }
.skintile__name{
  font-family: var(--font-display); font-size: 0.86rem; font-weight: 500;
  color: var(--paper-dim); text-align: center;
}
.skintile.is-selected{
  border-color: var(--accent); background: rgba(255, 79, 173, 0.16);
}
.skintile.is-selected .skintile__name{ color: var(--accent); }
.skintile.is-locked{ opacity: 0.42; cursor: not-allowed; }
.skintile:disabled{ cursor: not-allowed; }

/* ---------------------------------------------------------- controls */
.keys{ margin: 0; display: grid; gap: 0; }
.keys__row{
  display: grid; grid-template-columns: minmax(0, 240px) minmax(0, 1fr);
  gap: 10px 24px; align-items: baseline;
  padding: 14px 0; border-top: 2px solid rgba(255, 255, 255, 0.07);
}
.keys__row:first-child{ border-top: 0; padding-top: 2px; }
.keys dt{ margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.keys dd{ margin: 0; color: var(--paper-dim); font-size: 0.94rem; line-height: 1.5; }
.keys__or{ color: var(--paper-dim); font-size: 0.82rem; }
/* Flat, like everything else. A key cap does not need a fake bevel to read as
   a key when it is white on a dark page. */
.keys kbd, .menu__keys kbd {
  font-family: var(--font-display); font-weight: 600; font-size: 0.7rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: #16233A;
  background: var(--paper); border: 0;
  border-radius: 8px; padding: 7px 11px; white-space: nowrap;
}

/* -------------------------------------------------------- the stats */
.stats{ margin: 0 0 8px; display: grid; gap: 0; }
.stats__row{
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  padding: 13px 0; border-top: 2px solid rgba(255, 255, 255, 0.07);
}
.stats__row:first-child{ border-top: 0; padding-top: 2px; }
.stats dt{ color: var(--paper-dim); font-size: 0.95rem; }
.stats dd{
  margin: 0; font-family: var(--font-display); font-weight: 600; font-size: 1.35rem;
  color: var(--accent); font-variant-numeric: tabular-nums;
}

.settings__foot{
  margin: 0; padding-top: 26px; border-top: 2px solid rgba(255, 255, 255, 0.08);
  font-size: 0.86rem; color: var(--paper-dim); line-height: 1.6;
}
:where(.settings__foot) :where(a){ color: var(--paper-dim); }
:where(.settings__foot) :where(a:hover){ color: var(--accent); }

/* ------------------------------------------------------- on a phone */
@media (max-width: 820px) {
  /*
   * The settings page on a phone.
   *
   * Gray: "ss2 is mobile, its not nice". It was the desktop layout with the
   * sidebar stacked on top — a rail of tabs above six sections, then a wall of
   * left-aligned rows with their controls pushed to a column that no longer
   * existed. What it needed was not squeezing: the CARD is the unit, and on a
   * phone each row becomes its own little card too, so a control is always
   * next to the words that name it.
   */
  .settings__body{ grid-template-columns: minmax(0, 1fr); gap: 16px; padding: 14px 12px 70px; }
  .settings__nav{
    position: sticky; top: var(--settings-bar-h, 56px); z-index: 15; margin: 0 -12px;
    background: rgba(16, 21, 42, 0.94); padding: 8px 12px;
    border-bottom: 2px solid rgba(255, 255, 255, 0.09);
  }
  @supports (backdrop-filter: blur(1px)) {
    .settings__nav{ background: rgba(16, 21, 42, 0.78); backdrop-filter: blur(16px); }
  }
  /*
   * Every tab on screen, always: the rail WRAPS on a phone.
   *
   * It was a sideways scroller. First with a fade that cut the last tab in half
   * ("button is cut off man"), then padded and snapped with its scrollbar
   * hidden, which left nothing at all to say more sections were past the edge,
   * and clipped the current pill to the rail's own edges (Gray, 14 Sep 2026:
   * "you cannt tell theres more shit to the menu on small screens mate, always
   * got this issue"). Six or seven short tabs fit in two rows; a row of tabs
   * you can see beats a row you have to discover. tools/check-layout holds it.
   */
  .settings__nav ul{
    display: flex; flex-wrap: wrap; justify-content: center; gap: 6px;
  }
  :where(.settings__nav) :where(a){ padding: 8px 14px; white-space: nowrap; font-size: 0.9rem; }

  .settings__pane{ gap: 16px; }
  .set-section{ padding: 18px 15px 20px; border-radius: 20px; }
  .set-section__title{ font-size: 1.4rem; }
  .set-section__blurb{ font-size: 0.92rem; }

  /*
   * Each row is its own card: label, description, then the control underneath
   * at full width. Stacked WITHOUT a surface, a switch ended up floating
   * under a paragraph with nothing tying the two together.
   */
  .set-row{
    grid-template-columns: minmax(0, 1fr); gap: 12px;
    padding: 14px; border-top: 0; margin-top: 10px;
    border-radius: var(--radius-sm); background: rgba(255, 255, 255, 0.05);
  }
  .set-row:first-of-type{ padding-top: 14px; margin-top: 0; }
  .set-row--stack{ background: none; padding: 0; margin-top: 18px; }
  .set-row__control{ justify-self: stretch; }
  .set-row__control .switch{ justify-self: start; }
  .set-input{ width: 100%; }
  .set-row--slider{
    grid-template-columns: minmax(0, 1fr); gap: 8px; background: none; padding: 4px 0 0;
    margin-top: 0;
  }
  .set-row--danger{ background: rgba(255, 59, 92, 0.09); border-top: 0; }
  .set-danger{ width: 100%; }

  /* Two columns of choices at most, or the text inside them wraps to five
     lines and the cards become paragraphs. */
  .choices{ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
  .choice, .choices .station { padding: 13px 14px 15px; }
  .choice__desc, .choices .station__desc { font-size: 0.78rem; }

  .skinset__grid{ grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 8px; }
  .skintile__name{ font-size: 0.78rem; }
  .preview{ margin: 16px 0 6px; }
  .preview__meta{ padding: 11px 14px 12px; }

  /* The key list is two columns of narrow text at this width. One column,
     with the caps above what they do. */
  .keys__row{ grid-template-columns: minmax(0, 1fr); gap: 6px; padding: 12px 0; }

  /* The notch inset stays on a phone, where the notch is. */
  .settings__bar{ grid-template-columns: auto 1fr auto; padding: calc(9px + var(--safe-t)) 12px 9px; }
  .settings__title{ font-size: 0.86rem; letter-spacing: 0.12em; }
  .settings__back{ padding: 8px 13px 9px; font-size: 0.86rem; }
  .settings__saved{ font-size: 0.7rem; }
}

/* ---------------------------------------------------------- the all-time board
 *
 * Real players only, best run first. A table would be the obvious shape and is
 * the wrong one: a rank, a name and a number are three fields, and an <ol> of
 * rows keeps the ordinal in the markup rather than in a column of typed digits.
 */
.board{
  margin: 28px 0 34px; border-radius: var(--radius);
  border: 2px solid var(--line); background: var(--ink-2);
  overflow: hidden;
}
.board__head{
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px 20px; flex-wrap: wrap;
  padding: 18px 22px; border-bottom: 2px solid var(--line);
  background: linear-gradient(180deg, rgba(255, 79, 173, 0.1), transparent);
}
.board__title{
  margin: 0; font-family: var(--font-display); font-weight: 600; font-size: 1.3rem;
}
.board__status{ margin: 0; font-size: 0.86rem; color: var(--text-dim); }
.board__status.is-problem{ color: var(--warn); }
.board__rows{ margin: 0; padding: 0; list-style: none; }
.board__row{
  display: grid; align-items: baseline; gap: 4px 16px;
  grid-template-columns: 3.4rem minmax(0, 1fr) auto 6.5rem;
  padding: 11px 22px; border-top: 1px solid rgba(38, 46, 66, 0.6);
}
.board__row:first-child{ border-top: 0; }
.board__row:nth-child(even){ background: rgba(255, 255, 255, 0.018); }
.board__rank{
  font-family: var(--font-display); font-weight: 600; font-size: 0.9rem;
  color: var(--text-dim); font-variant-numeric: tabular-nums;
}
/* The top three get the colour. Everyone else gets the number — a board where
   every row is decorated has no top three. */
.board__row--1 .board__rank{ color: var(--tang); }
.board__row--2 .board__rank{ color: #C9D4E4; }
.board__row--3 .board__rank{ color: #CE8946; }
.board__row--1 .board__name, .board__row--1 .board__len { color: var(--accent); }
.board__name{
  min-width: 0; overflow-wrap: anywhere;
  font-family: var(--font-display); font-weight: 500; font-size: 1.02rem;
}
.board__len{
  font-family: var(--font-display); font-weight: 600; font-size: 1.05rem;
  font-variant-numeric: tabular-nums; text-align: right;
}
.board__when{
  font-size: 0.78rem; color: var(--text-dim); text-align: right;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 560px) {
  /* The date is the first thing to go: it is context, not the score. */
  .board__row{ grid-template-columns: 2.8rem minmax(0, 1fr) auto; padding: 10px 14px; }
  .board__when{ display: none; }
  .board__head{ padding: 14px 16px; }
}


/* ================================================================ Candy buttons
 * The house button, on every page. Gray picked set C "Candy" out of five
 * chunky-3D mockups on 10 Sep 2026 ("C looks good, lock it in, do all pages").
 * Anatomy measured off Worms Zone's menu: a thick white RIM with a plum outline
 * and a coloured glow, a BODY that is light at the top and deep at the bottom
 * with an inner bevel, and Lilita One lettering
 * outlined in the body's darkest colour.
 *
 * One element, no extra markup: the rim is a transparent border painted by a
 * second background layer. (There was a white gloss pill as ::before; Gray had
 * it removed, 10 Sep: "remove those rectangles in the corner of every button".) Nothing here is
 * overflow:hidden. Chrome drops the radius on a clipped element while it
 * animates and the corners flick square (Gray: "is clipped mate ... looks
 * stupid"). Each button's own rule above keeps only its size and layout.
 */
:is(.button, .menu__play, .siteheader__cta, .iconbtn, .set-btn, .set-danger, .share__btn,
    .dock__icon, .dock__btn, .boostbutton, .beta__close, .settings__back){
  --rimA: #ffffff; --rimB: #e6e2f6; --edge: #7a2a6a; --glow: rgba(209, 31, 124, 0.5);
  --bodyA: #c7b2ff; --bodyB: #8a5cff; --bodyC: #5f33d6; --ts: #35187f; --ink: #ffffff;
  /* No `position` here. It was `relative` for the gloss pill, and as the last
     rule in the sheet it overrode the Boost button's `absolute` — Boost dropped
     into the page flow at the top left and pushed the arena canvas down (Gray,
     on a phone, 10 Sep 2026). The gloss is gone; so is the reason for it. */
  border: var(--rim, 5px) solid transparent;
  background:
    linear-gradient(180deg, var(--bodyA) 0%, var(--bodyB) 52%, var(--bodyC) 100%) padding-box,
    linear-gradient(180deg, var(--rimA), var(--rimB)) border-box;
  box-shadow:
    0 0 0 2px var(--edge), 0 8px 16px -6px var(--glow),
    inset 0 3px 0 rgba(255, 255, 255, 0.38), inset 0 -5px 0 rgba(0, 0, 0, 0.2);
  color: var(--ink); font-family: "Lilita One", var(--font-display); font-weight: 400;
  text-shadow:
    0 2px 0 var(--ts), 2px 0 0 var(--ts), -2px 0 0 var(--ts), 0 -2px 0 var(--ts),
    1.5px 1.5px 0 var(--ts), -1.5px 1.5px 0 var(--ts), 1.5px -1.5px 0 var(--ts), -1.5px -1.5px 0 var(--ts),
    0 4px 0 var(--ts);
  transform-origin: 50% 100%; -webkit-tap-highlight-color: transparent;
}
:is(.button, .menu__play, .siteheader__cta, .iconbtn, .set-btn, .set-danger, .share__btn,
    .dock__icon, .dock__btn, .boostbutton, .beta__close, .settings__back):focus-visible{ outline: 3px solid #fff; outline-offset: 4px; }
:is(.iconbtn, .dock__icon, .share__btn, .dock__btn) svg{ filter: drop-shadow(0 2px 0 var(--ts)); }

/* ================================================================ Press feedback
 * One mechanism for "this answered you", for every control in the game. Gray,
 * 18 Oct 2026, of the top-right row: "theres no feedback on these buttons and
 * no indication when sound is off, best do a sweep of buttons".
 *
 *   - Candy buttons (the list above): the house squash on press, a dim and a
 *     stop sign when disabled, a spinner when busy.
 *   - Everything else that can be pressed (rows, tiles, cards, switches): the
 *     same idea drawn gently, through zero-specificity rules so any component
 *     that wants its own press look still wins by simply writing one.
 *   - Hover only where there is a pointer to hover with. A phone has none, and
 *     a hover rule there sticks to the last thing touched.
 *   - A ring on keyboard focus is the global :focus-visible at the top of the
 *     sheet; the Candy list's own white ring is above.
 *   - A control that is ON or OFF says so: aria-pressed is true when the thing
 *     is ON, every time, and the picture follows it (.icon__slash, .navmenu__switch).
 *
 * The click sound is not here: main.ts plays it for every button and summary
 * in the shell, and it is silent when sound is off.
 */
:where(button, summary, [role="button"]){ touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
:where(button, summary, [role="button"]):active:where(:not(:disabled, [aria-disabled="true"], [aria-busy="true"])){ scale: 0.97; filter: brightness(0.94); }
@media (hover: hover) {
  :where(button, summary, [role="button"]):hover:where(:not(:disabled, [aria-disabled="true"], [aria-busy="true"])){ filter: brightness(1.07); }
}
:where(button, summary, [role="button"]):where(:disabled, [aria-disabled="true"]){ cursor: not-allowed; }
:where(button, summary, [role="button"])[aria-busy="true"]{ cursor: progress; pointer-events: none; }

:is(.button, .menu__play, .siteheader__cta, .iconbtn, .set-btn, .set-danger, .share__btn,
    .dock__icon, .dock__btn, .boostbutton, .beta__close, .settings__back){
  transition: transform 0.09s ease-out, filter 0.12s ease;
}
:is(.button, .menu__play, .siteheader__cta, .iconbtn, .set-btn, .set-danger, .share__btn,
    .dock__icon, .dock__btn, .boostbutton, .beta__close, .settings__back):active:where(:not(:disabled, [aria-disabled="true"], [aria-busy="true"])){
  animation: none; transform: scale(1.05, 0.92); scale: 1; filter: brightness(0.97);
}
:is(.button, .iconbtn, .set-btn, .set-danger, .share__btn, .dock__icon, .dock__btn, .boostbutton, .beta__close, .settings__back, .menu__play, .siteheader__cta):disabled{
  opacity: 0.45; cursor: not-allowed; filter: grayscale(0.35); animation: none;
}
:is(.button, .menu__play, .siteheader__cta, .iconbtn, .set-btn, .set-danger, .share__btn,
    .dock__icon, .dock__btn, .boostbutton, .beta__close, .settings__back)[aria-busy="true"]{
  opacity: 0.8; filter: saturate(0.7); animation: none;
}
/* A ring turning inside a button that is waiting on the network (aria-busy="true"). */
:is(.button:not(.button--tile, .button--tab), .set-btn, .set-danger)[aria-busy="true"]::after{
  content: ""; flex: none; width: 0.85em; height: 0.85em; margin-left: 8px; box-sizing: border-box;
  border: 0.16em solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: control-spin 0.7s linear infinite;
}
@keyframes control-spin{ to{ transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  :is(.button, .set-btn, .set-danger)[aria-busy="true"]::after{ animation: none; border-right-color: currentColor; opacity: 0.5; }
}

/* Rows in a drop-down (More, Language): a wash under the finger, not a squash of the whole row. */
@media (hover: hover) {
  .navmenu__sound:hover:not(:disabled){ background: rgba(255, 255, 255, 0.07); }
}
.navmenu__sound:active:not(:disabled){ scale: 1; filter: none; background: rgba(255, 79, 173, 0.22); }
.navmenu__sound:focus-visible{ outline-offset: -3px; border-radius: 8px; }

/*
 * A row that is a switch: the name stays put and a small switch on its right
 * says on or off (ui/sound.ts, main.ts). Off is dim, on is lit.
 */
.navmenu__switch{ display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.navmenu__switch::after{
  content: ""; flex: none; width: 40px; height: 22px; box-sizing: border-box; border-radius: 999px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  background: radial-gradient(circle at 11px 50%, rgba(255, 255, 255, 0.7) 6px, transparent 7px), rgba(255, 255, 255, 0.06);
}
.navmenu__switch[aria-pressed="true"]{ color: var(--accent); }
.navmenu__switch[aria-pressed="true"]::after{
  border-color: var(--accent);
  background: radial-gradient(circle at 27px 50%, #fff 6px, transparent 7px), var(--accent);
}
.navmenu__switch[aria-pressed="false"]{ color: rgba(255, 255, 255, 0.7); }

/*
 * The crossed-out note and speaker. The slash is drawn inside the icon
 * (icons.ts) and shown while the control is OFF; its halo is the body colour,
 * so it cuts a clean gap through the picture rather than lying over it.
 */
.icon__slash{ opacity: 0; transition: opacity 0.12s ease; }
.icon__slash-halo{ stroke: var(--bodyC, #1c1650); stroke-width: 5; }
[aria-pressed="false"] .icon .icon__slash, .icon__slash.is-off{ opacity: 1; }

/* Roles. Violet is the default above: secondary actions and icon buttons. */
:is(.button--primary, .menu__play, .siteheader__cta, .boostbutton){
  --bodyA: #ff9ddf; --bodyB: #ff4fad; --bodyC: #d11f7c; --ts: #7a0f48;
}
:is(.button--quiet, .share__btn, .beta__close){
  --bodyA: #f6f8fc; --bodyB: #d7dfeb; --bodyC: #aeb9cb; --ink: #22314d; --glow: rgba(0, 0, 0, 0.35);
  text-shadow: 0 2px 0 rgba(255, 255, 255, 0.75);
}
:is(.button--quiet, .share__btn, .beta__close) svg{ filter: none; }
.set-danger{ --bodyA: #ff8a9c; --bodyB: #f0364f; --bodyC: #b81a33; --ts: #6a0a1a; --glow: rgba(184, 26, 51, 0.5); }
/* Glass: the see-through violet of the menu's chips, as a button. For a
   choice that should be found without competing with Play. */
.button--glass{
  --rimA: rgba(203, 188, 255, 0.8); --rimB: rgba(160, 120, 255, 0.6); --edge: transparent; --glow: transparent;
  --bodyA: rgba(43, 30, 110, 0.82); --bodyB: rgba(28, 20, 76, 0.78); --bodyC: rgba(20, 16, 60, 0.74); --ts: #1c0d3a;
}
/*
 * Buy: Worms Zone's chunky green price button, for a price or a "yes, this
 * one" (the Burrow's showcase and card price pills). Blue: their video and
 * upgrade button, the colour --step already wears.
 */
/*
 * Tile: the front screen's side tiles (gameShell.ts menu__side). Worms Zone's
 * left column, in our Candy body, and then some. Gray, 27 Sep 2026: "the box
 * designs are shit, flat boring, no shiny shit anywhere, also they are
 * small". So: big, a colour each (data-tone), the picture bursting out of
 * the top of the tile, a light sweep across the glass every few seconds, and
 * the thing waiting for you moves — the gift wiggles, the wheel turns.
 */
/*
 * Sized from the screen's HEIGHT (--tile), so three always fit between the top
 * and bottom rails. At a fixed 138px they did not on an iPad in landscape
 * (1024x690 usable): "With friends" sat on the Share and Settings buttons
 * (Gray, 27 Sep 2026: "ipad things on screen cutoff in landscape").
 */
.button--tile{
  /* Four tiles stack in the right-hand column since Themes joined it (30 Sep 2026): the
     height term keeps four of them between the top and bottom rails on a laptop. */
  --tile: clamp(60px, min(15.5vh, calc((100vh - 330px) / 4.8)), 138px);
  --tile-shine: linear-gradient(115deg, transparent 38%, rgba(255, 255, 255, 0.55) 50%, transparent 62%);
  position: relative; flex-direction: column; justify-content: flex-end; gap: 2px;
  width: var(--tile); min-height: calc(var(--tile) * 0.86); padding: 0 6px calc(var(--tile) * 0.085); margin-top: calc(var(--tile) * 0.33);
  border-radius: calc(var(--tile) * 0.19); font-size: max(0.8rem, calc(var(--tile) * 0.11)); line-height: 1.05; text-align: center;
  background:
    var(--tile-shine) padding-box 180% 0 / 260% 100% no-repeat,
    radial-gradient(ellipse 90% 60% at 50% 18%, rgba(255, 255, 255, 0.35), transparent 70%) padding-box,
    linear-gradient(180deg, var(--bodyA) 0%, var(--bodyB) 52%, var(--bodyC) 100%) padding-box,
    linear-gradient(180deg, var(--rimA), var(--rimB)) border-box;
  animation: tile-shine 5.5s ease-in-out infinite;
}
.button--tile[data-tone="pink"]{ --bodyA: #ff9ddf; --bodyB: #ff4fad; --bodyC: #c2166f; --ts: #6e0a3f; --glow: rgba(209, 31, 124, 0.55); }
.button--tile[data-tone="orange"]{ --bodyA: #ffd27a; --bodyB: #ff9f1c; --bodyC: #d06a00; --ts: #6e3200; --glow: rgba(217, 123, 0, 0.55); animation-delay: 0.9s; }
.button--tile[data-tone="blue"]{ --bodyA: #8fd0ff; --bodyB: #2c8fd6; --bodyC: #1a5f9e; --ts: #0d3a66; --glow: rgba(29, 107, 168, 0.55); animation-delay: 1.8s; }
.button--tile[data-tone="green"]{ --bodyA: #c8ff7a; --bodyB: #5fd42a; --bodyC: #2f8f0e; --ts: #1d5a06; --glow: rgba(55, 163, 18, 0.55); animation-delay: 0.4s; }
.button--tile[data-tone="violet"]{ --bodyA: #c7b2ff; --bodyB: #8a5cff; --bodyC: #5530c8; --ts: #2e1480; animation-delay: 1.3s; }
.button--tile[data-tone="teal"]{ --bodyA: #8ff5e8; --bodyB: #1fc7b6; --bodyC: #0e8a8a; --ts: #064848; --glow: rgba(14, 138, 138, 0.55); animation-delay: 2.2s; }
.button--tile img{
  width: calc(var(--tile) * 0.75); height: calc(var(--tile) * 0.75); margin: calc(var(--tile) * -0.43) auto 2px; object-fit: contain;
  filter: drop-shadow(0 6px 4px rgba(20, 8, 60, 0.45));
  transition: transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@media (hover: hover){ .button--tile:hover img{ transform: scale(1.08) translateY(-4px); } }
.button--tile .button__label{ display: block; }
@keyframes tile-shine{ 0%, 70%{ background-position: 180% 0, 0 0, 0 0, 0 0; } 100%{ background-position: -80% 0, 0 0, 0 0, 0 0; } }
/* Waiting for you: the gift wiggles, and the wheel turns slowly on its own. */
.button--tile[data-ready] img{ animation: tile-wiggle 2.6s ease-in-out infinite; transform-origin: 50% 90%; }
@keyframes tile-wiggle{ 0%, 70%, 100%{ rotate: 0deg; scale: 1; } 74%{ rotate: -9deg; scale: 1.08; } 80%{ rotate: 8deg; } 86%{ rotate: -5deg; } 92%{ rotate: 3deg; } }
.tile--wheel img{ animation: tile-turn 9s linear infinite; }
@keyframes tile-turn{ to{ rotate: 360deg; } }
/* The countdown or "Ready!" pill hanging off the bottom edge. */
.button--tile .tile__badge{
  position: absolute; bottom: -14px; left: 50%; translate: -50% 0; white-space: nowrap;
  padding: 4px 12px 5px; border-radius: 999px; border: 3px solid #fff; line-height: 1.1;
  background: linear-gradient(180deg, #7a64ff, #4a33c0); font-size: 0.92rem; min-width: 70%;
  box-shadow: 0 3px 0 rgba(20, 8, 60, 0.5);
}
.button--tile[data-ready] .tile__badge{ background: linear-gradient(180deg, #c8ff7a, #37a312); text-shadow: 0 2px 0 #1d5a06; animation: tile-pulse 1.2s ease-in-out infinite; }
@keyframes tile-pulse{ 50%{ scale: 1.1; } }
.button--tile .tile__badge:empty{ display: none; }
/* A tile carrying a pill makes room for it, so the pill never sits on the label. */
.button--tile:has(.tile__badge:not(:empty)){ padding-bottom: 26px; }
.button--tile .iconbtn__dot{ top: -6px; right: -6px; width: 20px; height: 20px; }
@media (prefers-reduced-motion: reduce){ .button--tile, .button--tile img, .button--tile .tile__badge{ animation: none !important; } }
.button--buy{ --bodyA: #b8ff6a; --bodyB: #5fd42a; --bodyC: #37a312; --ts: #1d5a06; --glow: rgba(55, 163, 18, 0.5); }
.button--blue{ --bodyA: #8fd0ff; --bodyB: #2c8fd6; --bodyC: #1d6ba8; --ts: #0d3a66; --glow: rgba(29, 107, 168, 0.5); }
/*
 * Flat: for the sign-in buttons. Google, Apple and Discord each fix their
 * button's fill, logo and lettering, so none of the Candy body, rim or
 * outlined text applies; size, shape, press and focus still come from .button.
 */
.button--flat{
  background: var(--flat-bg, #ffffff); border: 1px solid var(--flat-line, transparent);
  box-shadow: none; text-shadow: none; color: var(--flat-ink, #1f1f1f);
  font-family: inherit; font-weight: 600;
}
.button--flat svg{ filter: none; width: 20px; height: 20px; flex: none; }
.button--google{ --flat-bg: #ffffff; --flat-ink: #1f1f1f; --flat-line: #747775; }
.button--apple{ --flat-bg: #000000; --flat-ink: #ffffff; }
.button--discord{ --flat-bg: #5865f2; --flat-ink: #ffffff; }

/* The one bounce: squash and stretch, the whole button, nothing clipped. Only
   on the buttons that start a run; if everything bounces, nothing stands out. */
:is(.menu__play, [data-death-again]){ animation: cbJelly 2.2s ease-in-out infinite; }
@media (hover: hover){ :is(.menu__play, [data-death-again]):hover{ animation-duration: 1.6s; } }
/* A third of the squash it had (1.12 x 0.88): Gray, 13 Sep 2026, "less jelly
   movement on the play button". It still breathes; it no longer wobbles. */
@keyframes cbJelly{
  0%{ transform: scale(1, 1); } 10%{ transform: scale(1.04, 0.96); } 20%{ transform: scale(0.975, 1.025); }
  30%{ transform: scale(1.012, 0.99); } 40%, 100%{ transform: scale(1, 1); }
}

/*
 * Play, as Gray drew it on 13 Sep 2026: a play mark and PLAY! in capitals on
 * the pink, a pink rim rather than a white one, and a darker lip under it so
 * it stands up off the screen beside the name field.
 */
.menu__play{
  text-transform: uppercase; gap: 14px;
  --rimA: #ffc6ee; --rimB: #ff7fca; --edge: #6b0d40;
  box-shadow:
    0 0 0 2px var(--edge), 0 6px 0 #a3125f, 0 7px 0 2px var(--edge), 0 14px 20px -6px var(--glow),
    inset 0 3px 0 rgba(255, 255, 255, 0.38), inset 0 -5px 0 rgba(0, 0, 0, 0.18);
}
.menu__play-icon{
  width: 0.74em; height: 0.74em; flex: 0 0 auto; fill: currentColor;
  filter: drop-shadow(0 3px 0 var(--ts)) drop-shadow(0 -1.5px 0 var(--ts)) drop-shadow(1.5px 0 0 var(--ts)) drop-shadow(-1.5px 0 0 var(--ts));
}
@media (prefers-reduced-motion: reduce){ :is(.menu__play, [data-death-again]){ animation: none; } }



/* The narrowest phones keep the keyword, not the article. */
@media (max-width: 360px) {
  .menu__eyebrow-the{ display: none; }
}

/*
 * The homepage arena is pinned to the screen, not sized from 100dvh.
 *
 * Gray, on Chrome for iPhone, 10 Sep 2026: "that first time load black half is
 * still there". The arena was `calc(100dvh - var(--header-h))` tall, and on iOS
 * 100dvh can stay at its first-load value when the URL bar collapses, so the
 * game ended short: empty page below it, and the page itself nudged up so the
 * top of the menu was cut off. The canvas re-measuring itself (layout.ts) could
 * not help, because the box it measured was the wrong height. A fixed box with
 * inset: 0 always covers exactly what is visible, and the document has nothing
 * to scroll. The renderer follows through its ResizeObserver on the stage.
 */
html:has(body.page--home){ overflow: hidden; overscroll-behavior: none; height: 100%; }
.page--home main > .game{
  position: fixed; inset: 0; width: auto; height: auto; min-height: 0; max-height: none;
  margin: 0; transform: none;
}


/*
 * Play with friends: the second choice on the screen, so the second-biggest
 * button. Chunky and violet like the corner buttons, with the people icon, and
 * never wider than the column Play sits in.
 */
.menu__friends{
  --rim: 4px; gap: 10px; width: min(340px, var(--col)); padding: 7px 22px 10px;
  border-radius: 20px; font-size: clamp(1.1rem, 1.6vw, 1.3rem);
}
/*
 * The icon sits INSIDE the button's face. It was 40px with -10px margins top and
 * bottom, so it overhung the button's top edge and hung into the bottom lip —
 * Gray, 23 Sep 2026, on both menu buttons: "icons dont sit right, just looks
 * weird". 1.75em tracks the label, and the extra 2px off the bottom margin is
 * half the lip (--rim), so it centres on the face you see, not the box.
 */
.menu__friends img, .menu__wardrobe img{
  width: 1.75em; height: 1.75em; flex: none; margin: -5px 0 -3px -4px; object-fit: contain;
  filter: drop-shadow(0 2px 1px rgba(0, 0, 0, 0.35));
}
/*
 * The Burrow, under Play with friends: the same size and shape, in the orange
 * of the wardrobe's own furniture so the two read as different doors rather
 * than twins. Its icon is the painted worm on its display stand.
 */
.menu__wardrobe{
  --rim: 4px; --bodyA: #ffd27a; --bodyB: #ff9f1c; --bodyC: #d97b00; --ts: #7a3d00;
  gap: 10px; width: min(340px, var(--col)); padding: 7px 22px 10px;
  border-radius: 20px; font-size: clamp(1.1rem, 1.6vw, 1.3rem);
}
/*
 * The two doors under Play wear a rim in their OWN colour, thin, not the house
 * white. Gray, 29 Sep 2026, passing on players' complaints: "too much white
 * around these two buttons". Two stacked buttons with the 4px white rim and the
 * edge ring were more rim than button. Play keeps the white rim: it is the one
 * loud choice on the screen, and now the only one that shines like that.
 */
.menu__friends{ --rim: 3px; --rimA: #d9ccff; --rimB: #9f82ff; --edge: #3a1c8c; }
.menu__wardrobe{ --rim: 3px; --rimA: #ffe2a6; --rimB: #ffb445; --edge: #7a3d00; }
@media (hover: hover){ .menu__wardrobe:hover{ filter: brightness(1.07); } }
.menu__wardrobe:active{ transform: scale(1.03, 0.94); }
@media (hover: hover){ .menu__friends:hover{ filter: brightness(1.07); } }
.menu__friends:active{ transform: scale(1.03, 0.94); }


/*
 * The analytics question: website only (layout.ts), never in a portal build.
 * A small card at the bottom rather than a wall, answered in one tap.
 */
.consent{
  position: fixed; z-index: 60; left: 50%; bottom: calc(12px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%);
  width: min(460px, calc(100% - 24px)); display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px;
  padding: 12px 14px; border-radius: 16px; background: #1a1f47; color: #fff; border: 3px solid #fff;
  box-shadow: 0 0 0 2px #7a2a6a, 0 14px 30px -12px rgba(0, 0, 0, 0.55);
  font-size: 0.9rem; line-height: 1.4;
}
.consent[hidden]{ display: none; }
.consent p{ margin: 0; flex: 1 1 220px; }
.consent__actions{ display: flex; gap: 8px; }
/* Accept and Essential only are house buttons (.button --primary and --quiet,
   --sm); they had a hand-rolled pair of their own here. */

/* Hide or report the name of whoever killed you. See death.ts. */
.death__report{ margin: -2px 0 6px; display: flex; gap: 8px; justify-content: center; align-items: center; font-size: 0.82rem; color: var(--text-dim); }
.death__report[hidden]{ display: none; }
/* Hide this name and Report name are .linkbutton --dim. */

/* ------------------------------------------------------ the lobby */
/*
 * Play with friends, before the match (gameShell, data-game-lobby). Built
 * from the house pieces: the panel, the Candy buttons, the name field, and the
 * death screen's label and big number. Only layout lives here.
 */
.lobby__join{ display: flex; gap: 10px; align-items: center; }
.lobby__join > .menu__field{ flex: 1; }
/* The bots toggle (16 Sep 2026): same .switch as Sound/Music on settings.ts, just this row's layout. */
.lobby__switchrow{ display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 10px 0; }
.lobby__switchlabel{ font-size: 0.92rem; color: var(--text-dim); text-align: left; }
.lobby__count{ display: flex; align-items: center; gap: 8px; }
.lobby__countvalue{
  min-width: 2.6em; text-align: center; font-family: "Lilita One", var(--font-display);
  font-size: 1.35rem; color: #fff; font-variant-numeric: tabular-nums;
}
/* The room reads like the death screen it shares its pieces with: a label, one
   big number (the code), and the rest centred under it. */
.panel--lobby [data-lobby-room]{ text-align: center; }
/* The list sets its own alignment (.panel__list), so it is centred by name too. */
.panel--lobby .panel__list{ list-style: none; padding: 0; text-align: center; }

/* ------------------------------------------------------ in-game screens */
/*
 * Settings inside the game (ui/settingsScreen.ts, gameShell data-game-screen).
 * Gray, 15 Sep 2026: "just make it similar to worms zone", "chunky is good",
 * all in one window. Worms Zone's shape: a title, your currency and a close X
 * along the top, big icon tabs under it, chunky controls, and on the look
 * tabs a live preview. Built from the house pieces (Candy buttons, .station,
 * .iconbtn); what is new here is layout, plus two Candy variants (--tab,
 * --step) in the same family as the rest.
 *
 * The preview is a WINDOW onto the arena running behind: on the Background
 * and Food tabs the screen stops painting its own fill and the window's
 * enormous box-shadow paints it instead, everywhere except inside the window.
 * The bar, tabs and side column sit on a higher layer so the shadow cannot
 * cover them, and the screen clips it to its rounded edge.
 */
.overlay--screen{ background: rgba(8, 6, 24, 0.55); padding: clamp(8px, 2vw, 22px); }
/* The corner speaker and the run's readouts are for a live run; over Settings
   they only cover corners of it. */
.game:has(:is([data-game-screen], [data-game-profile], [data-game-board], [data-game-wheel], [data-game-daily], [data-game-lobby], [data-game-updates], [data-reward]):not([hidden])) :is([data-game-dock], .hud){ visibility: hidden; }
.screen{
  --screen-fill: #2f2573;
  position: relative; overflow: hidden;
  display: grid; grid-template-rows: auto auto minmax(0, 1fr);
  width: min(1120px, 100%); height: min(760px, 100%);
  border-radius: var(--radius); border: 4px solid #fff;
  background: radial-gradient(ellipse 120% 80% at 50% 0%, #3d3196, var(--screen-fill) 70%);
  box-shadow: 0 0 0 2px #7a2a6a, 0 18px 40px -14px rgba(0, 0, 0, 0.7);
}
.screen.is-windowed{ background: transparent; }
.screen__bar{
  position: relative; z-index: 2;
  display: flex; align-items: center; gap: 12px; padding: 12px 14px 8px 20px;
  background: linear-gradient(180deg, #3d3196, #352a86);
}
.screen__title{
  margin: 0 auto 0 0; font-family: "Lilita One", var(--font-display); font-weight: 400;
  font-size: clamp(1.6rem, 3.4vw, 2.3rem); color: #ffe45c; letter-spacing: 0.01em;
  text-shadow: 0 3px 0 #35187f, 2px 0 0 #35187f, -2px 0 0 #35187f, 0 -2px 0 #35187f;
}
/* Your Nuggets, as a pill: the menu's top rail and the Settings bar, one component. */
.nuggetpill{
  display: inline-flex; align-items: center; gap: 8px; padding: 4px 16px 4px 6px;
  border-radius: 999px; background: rgba(12, 8, 36, 0.7);
  font-family: "Lilita One", var(--font-display); font-size: 1.25rem; color: #fff; line-height: 1;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.4);
}
.nuggetpill[hidden]{ display: none; }
.nuggetpill img{ width: 30px; height: 30px; filter: drop-shadow(0 2px 1px rgba(0, 0, 0, 0.55)); }
/* The close X: an icon button in Worms Zone's orange. */
.iconbtn--close{ --bodyA: #ffd27a; --bodyB: #ff9f1c; --bodyC: #d97b00; --ts: #7a3d00; --glow: rgba(217, 123, 0, 0.5); }
.iconbtn--close svg{ width: 24px; height: 24px; }

/* Big icon tabs: the chosen one widens and says its name, as Worms Zone's do. */
.screen__tabs{
  position: relative; z-index: 2;
  display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 14px 0;
  background: #352a86; border-bottom: 4px solid rgba(255, 255, 255, 0.14);
}
.button--tab{
  --rim: 3px; --bodyA: #6f5fd6; --bodyB: #5a48c4; --bodyC: #4636a8; --ts: #231566;
  flex: 1 1 64px; min-width: 64px; max-width: 120px; min-height: 62px; padding: 6px 10px 8px;
  border-radius: 18px 18px 8px 8px; font-size: 1.3rem; gap: 8px;
}
.button--tab svg, .button--tab img{ width: 34px; height: 34px; flex: none; }
.button--tab .button__label{ display: none; }
.button--tab.is-selected{
  --bodyA: #b9a8ff; --bodyB: #8f78ff; --bodyC: #6a52e6;
  flex-grow: 3; max-width: 260px;
}
.button--tab.is-selected .button__label{ display: inline; color: #ffe45c; }

.screen__body{ position: relative; z-index: 1; min-height: 0; overflow-y: auto; padding: 18px clamp(14px, 3vw, 30px) 24px; }
.screen__pane{ display: grid; gap: 18px; align-content: start; max-width: 720px; margin: 0 auto; }
.screen__pane[hidden]{ display: none; }
.screen__pane--windowed{
  max-width: none; height: 100%;
  grid-template-columns: minmax(250px, 340px) minmax(0, 1fr); align-items: stretch;
}
.screen__side{ position: relative; z-index: 1; display: grid; gap: 12px; align-content: start; min-height: 0; overflow-y: auto; padding-right: 4px; }
.screen__window{
  position: relative; z-index: 0; min-height: 240px;
  border-radius: 22px; border: 4px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 0 0 200vmax var(--screen-fill), inset 0 0 0 2px rgba(0, 0, 0, 0.25);
  pointer-events: none;
}
.screen__note{ margin: 0; font-size: 0.9rem; color: var(--text-dim); }
.screen__note[hidden]{ display: none; }
.screen__heading{ margin: 0; font-family: "Lilita One", var(--font-display); font-weight: 400; font-size: 1.2rem; color: #fff; }

/*
 * Every Settings tab is a window onto the arena (27 Sep 2026): the controls
 * down the left in cards, a badge in the window saying what is in use.
 */
.screen__bar::before{
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: 0.07;
  background: url(/squirm-io-worm-icon.png) 0 0 / 54px 54px repeat;
}
.screen__card{
  display: grid; gap: 12px; padding: 14px; border-radius: 20px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.04));
  border: 2px solid rgba(255, 255, 255, 0.12);
}
.screen__card--center{ justify-items: center; text-align: center; }
.screen__card .button svg{ width: 22px; height: 22px; }
/* Two columns that may be narrower than their longest word (a bare `1fr` may not: "Nutzungsbedingungen"
   pushed the whole Settings pane wider than a phone), so a long word breaks (hyphenated in German). */
.screen__links{ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.screen__links .button{ font-size: 0.95rem; padding: 8px 10px 10px; overflow-wrap: anywhere; hyphens: auto; }
.screen__version{
  justify-self: center; background: none; border: 0; padding: 4px 8px; cursor: pointer;
  font: inherit; font-size: 0.78rem; color: var(--text-dim); text-decoration: underline dotted;
}
.screen__side .stepper{ grid-template-columns: auto minmax(0, 1fr) auto auto; row-gap: 6px; }
.screen__side .stepper__label{ grid-column: 1 / -1; }
.screen__tiles--two{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
.station__swatch--art{ background-size: 82% auto; background-repeat: no-repeat; background-color: rgba(10, 6, 40, 0.35); }
.station__swatch--art.is-mirrored{ transform: scaleX(-1); }
.keys--compact .keys__row{ grid-template-columns: auto minmax(0, 1fr); gap: 10px; padding: 6px 0; font-size: 0.86rem; }
.keys--compact kbd{ font-size: 0.66rem; }
/* "✓ Selected", in the window, as Worms Zone's preview says it. */
.screen__badge{
  position: absolute; left: 50%; bottom: 18px; translate: -50% 0; white-space: nowrap; max-width: calc(100% - 24px);
  overflow: hidden; text-overflow: ellipsis;
  padding: 8px 22px 10px; border-radius: 18px; border: 3px solid rgba(255, 255, 255, 0.85);
  background: rgba(22, 12, 64, 0.82); box-shadow: 0 8px 18px -6px rgba(0, 0, 0, 0.6);
  font-family: "Lilita One", var(--font-display); font-size: clamp(1.1rem, 2.2vw, 1.7rem); color: #ffe45c;
  text-shadow: 0 2px 0 #35187f;
}
.screen__badge[hidden], .screen__badge:empty{ display: none; }
/* Where the boost button will sit on a phone, shown in the window. */
.screen__ghostboost{
  position: absolute; bottom: 22%; width: 86px; height: 86px; border-radius: 50%;
  background: radial-gradient(circle at 38% 32%, #ffb3e0, #ff4fad 55%, #b0125f); border: 4px solid #fff;
  box-shadow: 0 0 0 6px rgba(255, 79, 173, 0.35), 0 10px 20px rgba(0, 0, 0, 0.5);
  animation: tile-pulse 1.4s ease-in-out infinite;
}
.screen__ghostboost[data-side="right"]{ right: 26px; }
.screen__ghostboost[data-side="left"]{ left: 26px; }
.screen__ghostboost::after{ content: "BOOST"; position: absolute; inset: 0; display: grid; place-items: center; font-family: "Lilita One", var(--font-display); font-size: 1rem; color: #fff; text-shadow: 0 2px 0 #7a0f48; }

/* Music and Sounds: − [ten cells] + and a speaker, Worms Zone's chunky bars. */
.stepper{ display: grid; grid-template-columns: 5.2em auto minmax(0, 1fr) auto auto; align-items: center; gap: 10px; }
.stepper__label{ font-family: "Lilita One", var(--font-display); font-size: 1.3rem; color: #fff; }
.stepper__bar{
  display: grid; grid-template-columns: repeat(10, 1fr); gap: 3px; height: 30px; padding: 4px;
  border-radius: 10px; background: #1d1650; box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.45);
}
.stepper__cell{ border-radius: 5px; background: rgba(255, 255, 255, 0.1); }
.stepper__cell.is-on{ background: linear-gradient(180deg, #ffd27a, #ff9f1c 55%, #ff4fad); box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.45); }
.stepper.is-muted .stepper__bar{ opacity: 0.6; }
.button--step{ --rim: 3px; --bodyA: #8fd0ff; --bodyB: #2c8fd6; --bodyC: #1d6ba8; --ts: #0d3a66; width: 44px; height: 44px; padding: 0; border-radius: 12px; font-size: 1.6rem; line-height: 1; }
.stepper .iconbtn{ width: 44px; height: 44px; }
.stepper .iconbtn svg{ width: 24px; height: 24px; }

/* Floors and food themes: tiles with the texture itself as the picture. */
.station--tile{ grid-template-rows: auto auto auto; padding: 8px 8px 10px; text-align: center; background: #3b2f8f; border-width: 3px; }
.station--tile.is-selected{ border-color: #c6f432; box-shadow: 0 0 0 2px rgba(198, 244, 50, 0.35); }
.station--tile.is-selected .station__name{ color: #c6f432; }
/* A locked item you can buy with Nuggets is a button, not a wall. */
.station--tile.is-locked.is-buyable{ cursor: pointer; opacity: 0.8; }
.station--tile.is-locked.is-buyable .station__desc{ color: #ffd27a; font-weight: 700; }
.station__swatch{ display: block; aspect-ratio: 1; border-radius: 12px; background-size: 128px 128px; background-position: center; background-repeat: repeat; box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.25); }
.screen__tiles{ display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 10px; }
.screen__pane .choices{ margin-top: 0; }

/* A phone: the window goes under the controls instead of beside them. */
/*
 * A phone (Gray's screenshots, 15 Sep 2026). Seven tabs wrapped onto two rows,
 * and the chosen one widened to show its name, which was cut off ("Optio",
 * "Graphi", "Backgro") and ran into its neighbour. On a phone every tab is an
 * equal square in one row with its icon only, and the chosen one is told by its
 * colour; the pane's own heading says where you are. The floor and food panes
 * give the tiles the height and the window onto the arena a strip under them:
 * the window had taken most of the screen and left a row and a half of floors.
 */
@media (max-width: 760px){
  .screen__pane--windowed{ grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) clamp(120px, 24vh, 190px); }
  .screen__window{ min-height: 0; }
  .screen__tiles{ grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); gap: 8px; }
  .stepper{ grid-template-columns: 4.4em auto minmax(0, 1fr) auto auto; gap: 6px; }
  .stepper__label{ font-size: 1.05rem; }
  .screen__tabs{ flex-wrap: nowrap; gap: 4px; padding: 6px 8px 0; }
  .button--tab, .button--tab.is-selected{ flex: 1 1 0; min-width: 0; max-width: none; min-height: 50px; padding: 5px 2px 7px; }
  .button--tab.is-selected .button__label{ display: none; }
  /* With no name showing, the chosen tab needs more than a lighter body: a lime rim. */
  .button--tab.is-selected{ --rimA: #e8ff7a; --rimB: #c6f432; }
  .button--tab svg, .button--tab img{ width: clamp(24px, 7vw, 32px); height: clamp(24px, 7vw, 32px); }
}
/* A tab's keyboard focus ring sits inside it, rather than a white frame floating round it. */
.button--tab:focus-visible{ outline: 3px solid #fff; outline-offset: -6px; }

/*
 * A phone on its side, in the screens that are laid out for a tall window
 * (Gray, 19 Sep 2026, iPhone: 844x390, and 292 with Safari's bars). Settings and
 * Profile spent 156 of 292px on a title bar and a row of 62px tabs and showed
 * one row of what you opened them for; the purchase confirmation was taller
 * than the screen, so Buy sat below the fold; the private-arena lobby ran its
 * Leave button off the bottom. Each keeps its shape and gives back the room:
 * a slimmer bar and tabs, the picture beside the words instead of above them.
 * Height decides, not width: an 844px-wide phone is short, and a short window on
 * a desktop has the same problem.
 */
@media (orientation: landscape) and (max-height: 500px){
  .overlay--screen{ padding: 6px; }
  .screen:not(.screen--burrow){ height: 100%; border-width: 3px; }
  .screen:not(.screen--burrow) .screen__bar{ padding: 4px 10px 2px 16px; }
  .screen:not(.screen--burrow) .screen__title{ font-size: 1.5rem; }
  .screen:not(.screen--burrow) .iconbtn--close{ width: 44px; height: 44px; }
  .screen__tabs{ padding: 4px 10px 0; }
  .screen .button--tab{ min-height: 44px; padding-block: 3px 5px; }
  .screen .button--tab svg, .screen .button--tab img{ width: 28px; height: 28px; }
  .screen__body{ padding-block: 10px 14px; }
  .screen__pane{ gap: 12px; }
  .screen .button--step, .screen .stepper .iconbtn{ width: 44px; height: 44px; }
  /*
   * Title, tabs, Nuggets and the close X on ONE line (27 Sep 2026): two rows
   * of header left the leaderboard two players tall at 844x360 (Gray:
   * "landscape mobile also many things cut off or hard to choose").
   */
  .screen:not(.screen--burrow){ grid-template-columns: auto minmax(0, 1fr) auto auto; grid-template-rows: auto minmax(0, 1fr) auto; }
  .screen:not(.screen--burrow) .screen__bar{ display: contents; }
  /* With the bar flattened its pattern would lie over the whole screen. */
  .screen:not(.screen--burrow) .screen__bar::before{ display: none; }
  .screen:not(.screen--burrow) .screen__title{ grid-column: 1; grid-row: 1; align-self: center; margin: 0; padding: 4px 6px 2px 14px; font-size: 1.3rem; }
  .screen:not(.screen--burrow) .screen__tabs{ grid-column: 2; grid-row: 1; flex-wrap: nowrap; background: none; border-bottom: 0; padding: 4px 6px 2px; }
  .screen:not(.screen--burrow) .screen__bar > .nuggetpill{ grid-column: 3; grid-row: 1; align-self: center; }
  .screen:not(.screen--burrow) .screen__bar > .iconbtn--close{ grid-column: 4; grid-row: 1; align-self: center; margin: 4px 8px 2px 4px; }
  .screen:not(.screen--burrow) .screen__body{ grid-column: 1 / -1; grid-row: 2; border-top: 3px solid rgba(255, 255, 255, 0.14); }
  .screen:not(.screen--burrow) > .ranks__me{ grid-column: 1 / -1; grid-row: 3; padding: 4px 14px 6px; }
  /* The purchase confirmation: the picture beside the words, both buttons on a row of their own. */
  .panel--buy{ width: min(560px, 100%); display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 16px; align-items: center; text-align: left; padding: 12px 18px; }
  .panel--buy > .buy__art{ grid-column: 1; grid-row: 1 / span 4; width: 92px; height: 92px; margin: 0; }
  .panel--buy > :not(.buy__art){ grid-column: 2; }
  .panel--buy .buy__name{ margin: 0 0 2px; font-size: 1.3rem; }
  .panel--buy .buy__price{ justify-content: flex-start; }
  .panel--buy .buy__price .panel__display{ font-size: 2rem; }
  .panel--buy .panel__note{ margin: 2px 0 0; }
  .panel--buy .panel__actions{ grid-column: 1 / -1; margin-top: 10px; }
  .panel--buy .panel__actions .button{ min-height: 44px; }
}
/*
 * The lobby's first step is six things tall (title, blurb, extra worms, Start,
 * the code row, Back), which is not 292px. Sideways it is two columns instead:
 * what it is on the left, what to press on the right, so Back stays in view.
 */
@media (orientation: landscape) and (max-height: 420px){
  .panel--lobby{ width: min(720px, 100%); }
  .panel--lobby [data-lobby-choose]:not([hidden]){ display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 20px; row-gap: 6px; align-items: center; text-align: left; }
  .panel--lobby [data-lobby-choose] > *{ margin: 0; grid-column: 2; }
  .panel--lobby [data-lobby-choose] > .panel__title{ grid-column: 1; grid-row: 1; font-size: 1.3rem; }
  .panel--lobby [data-lobby-choose] > .panel__sub{ grid-column: 1; grid-row: 2; font-size: 0.8rem; line-height: 1.3; }
  .panel--lobby [data-lobby-choose] > .lobby__switchrow{ grid-column: 1; grid-row: 3; }
  .panel--lobby [data-lobby-choose] > :nth-child(4){ grid-column: 2; grid-row: 1; }
  .panel--lobby [data-lobby-choose] > .share__label{ grid-column: 2; grid-row: 2; align-self: end; }
  .panel--lobby [data-lobby-choose] > .lobby__join{ grid-column: 2; grid-row: 3; }
  .panel--lobby [data-lobby-choose] > .panel__hint{ grid-column: 1; grid-row: 4; }
  .panel--lobby [data-lobby-choose] > :nth-child(8){ grid-column: 2; grid-row: 4; }
  .panel--lobby .panel__actions > .button, .panel--lobby .lobby__join .button{ min-height: 44px; }
  /* The death screen with its XP line, what killed you and the report row is taller than 292px
     (its score scrolled off the top): the result on the left, the cause on the right, buttons across. */
  .panel--death{ display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); column-gap: 20px; row-gap: 2px; align-items: center; width: min(720px, 100%); }
  .panel--death > *{ grid-column: 2; margin-block: 0; }
  /* The run's tiles on the right, where there is room for "3:09" and "146,336" (27 Sep 2026). */
  .panel--death > .death__label{ grid-column: 1; grid-row: 1; }
  .panel--death > .death__score{ grid-column: 1; grid-row: 2; }
  .panel--death > .death__best{ grid-column: 1; grid-row: 3; }
  .panel--death > .death__xp{ grid-column: 1; grid-row: 4; }
  .panel--death > .death__cause{ grid-column: 2; grid-row: 1; }
  .panel--death > .death__stats{ grid-column: 2; grid-row: 2 / span 2; margin: 0; }
  .panel--death > .death__report{ grid-column: 2; grid-row: 4; }
  .panel--death > .toast{ grid-column: 1 / -1; grid-row: 5; }
  .panel--death > .panel__actions{ grid-column: 1 / -1; grid-row: 6; margin-top: 8px; }
  .panel--death .death__stat{ padding: 4px 2px 5px; }
  .panel--death .death__stat img{ width: 26px; height: 26px; }
  .panel--death .death__stats{ grid-auto-columns: auto; }
  .panel--death .death__stat{ padding-inline: 6px; }
  .panel--death .death__stat b{ font-size: 1rem; }
  .panel--death .panel__actions > .button{ min-height: 44px; }
}

/* ------------------------------------------------------ the updates bell */
/*
 * Gray, 15 Sep 2026: "we also need a nice bell notification where we will
 * show updates". An icon button with a red dot until the newest update has
 * been seen; the list is the house panel (ui/updates.ts).
 */
.menu__bell{ position: relative; }
.iconbtn__dot{
  position: absolute; top: -5px; right: -5px; width: 15px; height: 15px; border-radius: 50%;
  background: #ff3b5c; border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
.iconbtn__dot[hidden]{ display: none; }
/*
 * The leaderboard (ui/boardScreen.ts): the Settings screen's shell, a row per
 * player — cup or number, level badge, name, their worm, length — and your
 * own row pinned under the list.
 */
.screen--board{ grid-template-rows: auto auto minmax(0, 1fr) auto; width: min(980px, 100%); }
.ranks{ list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.ranks__row{
  display: grid; grid-template-columns: 56px 46px minmax(0, 1fr) minmax(90px, 220px) auto; align-items: center; gap: 12px;
  min-height: 60px; padding: 6px 16px 6px 10px; border-radius: 16px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.04));
  border: 2px solid rgba(255, 255, 255, 0.08);
}
.ranks__row[data-medal="gold"]{ background: linear-gradient(90deg, rgba(255, 196, 60, 0.32), rgba(255, 255, 255, 0.05) 70%); border-color: rgba(255, 206, 90, 0.55); }
.ranks__row[data-medal="silver"]{ background: linear-gradient(90deg, rgba(215, 225, 245, 0.26), rgba(255, 255, 255, 0.05) 70%); border-color: rgba(220, 230, 250, 0.4); }
.ranks__row[data-medal="bronze"]{ background: linear-gradient(90deg, rgba(230, 140, 70, 0.28), rgba(255, 255, 255, 0.05) 70%); border-color: rgba(235, 150, 80, 0.45); }
.ranks__rank{ display: grid; place-items: center; font-family: "Lilita One", var(--font-display); font-size: 1.5rem; color: #ffd84a; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.35); font-variant-numeric: tabular-nums; }
.ranks__rank img{ width: 52px; height: 52px; object-fit: contain; }
/* The level on a painted shield (tools/import-rewards.py): gold for the podium, violet below it. */
.ranks__level{
  display: grid; place-items: center; width: 46px; height: 46px; padding-top: 2px;
  background: url(/ui/rank-shield-violet.webp?v=84c9d548) center / contain no-repeat;
  font-family: "Lilita One", var(--font-display); font-size: 1.05rem; color: #fff;
  text-shadow: 0 2px 0 #2c1a7a, 1px 0 0 #2c1a7a, -1px 0 0 #2c1a7a, 0 -1px 0 #2c1a7a;
}
.ranks__row[data-medal] .ranks__level{ background-image: url(/ui/rank-shield-gold.webp?v=940111b3); }
.ranks__name{ min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: "Lilita One", var(--font-display); font-size: 1.3rem; color: #fff; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.3); }
.ranks__worm{ width: 100%; height: 48px; }
.ranks__len{ font-family: "Lilita One", var(--font-display); font-size: 1.35rem; color: #fff; font-variant-numeric: tabular-nums; text-align: right; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.3); }
.ranks__me{ position: relative; z-index: 2; padding: 10px clamp(14px, 3vw, 30px) 14px; background: linear-gradient(180deg, #352a86, #2a206a); border-top: 4px solid rgba(255, 255, 255, 0.14); }
.ranks__me[hidden]{ display: none; }
.ranks__row.is-me{ border-color: var(--accent); background: linear-gradient(90deg, rgba(255, 79, 173, 0.28), rgba(255, 255, 255, 0.05) 70%); }
.ranks__none{ margin: 6px 0; text-align: center; color: var(--text-dim); font-weight: 700; }
@media (max-width: 640px){
  .ranks__row{ grid-template-columns: 40px 36px minmax(0, 1fr) 72px auto; gap: 8px; padding: 4px 10px 4px 6px; min-height: 52px; }
  .ranks__rank img{ width: 38px; height: 38px; }
  .ranks__rank{ font-size: 1.15rem; }
  .ranks__level{ width: 36px; height: 36px; font-size: 0.9rem; }
  .ranks__name, .ranks__len{ font-size: 1.02rem; }
  .ranks__worm{ height: 36px; }
}
/* A phone on its side: the leaderboard's rows slimmer, so more than two players fit (27 Sep 2026). */
@media (orientation: landscape) and (max-height: 500px){
  .ranks{ gap: 5px; }
  .ranks__row{ min-height: 42px; padding: 2px 12px 2px 6px; gap: 8px; }
  .ranks__rank img{ width: 34px; height: 34px; }
  .ranks__rank{ font-size: 1.1rem; }
  .ranks__level{ width: 34px; height: 34px; font-size: 0.9rem; }
  .ranks__name, .ranks__len{ font-size: 1rem; }
  .ranks__worm{ height: 30px; }
}

/*
 * The prize wheel (ui/wheelPanel.ts). A stage, not a form: a violet room with
 * a glow and sparkles, the wheel big on a pedestal with the Spin button on
 * it, the level bonus as a meter beside it. Two canvases: the face turns, the
 * rim with its bulbs stays put. Gray, 27 Sep 2026: "prize wheel is absolute
 * dogshit all around".
 */
.overlay--wheel{ background: rgba(8, 4, 28, 0.72); padding: clamp(8px, 2vw, 22px); }
.wheelstage{
  position: relative; overflow: hidden; display: grid; grid-template-rows: auto auto minmax(0, 1fr);
  width: min(1040px, 100%); height: min(800px, 100%); border-radius: var(--radius); border: 4px solid #fff;
  box-shadow: 0 0 0 2px #7a2a6a, 0 18px 40px -14px rgba(0, 0, 0, 0.7);
  background:
    radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.14) 0 2px, transparent 3px) 0 0 / 90px 90px,
    radial-gradient(circle at 70% 80%, rgba(255, 228, 92, 0.18) 0 2px, transparent 3px) 0 0 / 130px 130px,
    radial-gradient(ellipse 80% 70% at 42% 45%, #8a3fd6, #4a1f9a 55%, #25104f);
}
.wheelstage__glow{
  position: absolute; inset: 0; pointer-events: none;
  background: repeating-conic-gradient(from var(--ray-turn) at 42% 46%, rgba(255, 220, 120, 0.12) 0 8deg, transparent 8deg 20deg);
  -webkit-mask: radial-gradient(circle at 42% 46%, #000 93px, transparent 451px); mask: radial-gradient(circle at 42% 46%, #000 93px, transparent 451px);
  animation: ray-turn 30s linear infinite;
}
.wheelstage__bar{ position: relative; z-index: 2; display: flex; align-items: center; justify-content: center; padding: 20px 64px 2px; }
.wheelstage__bar .iconbtn--close{ position: absolute; right: 14px; top: 12px; }
.wheelstage__title{ margin: 0; font-family: "Lilita One", var(--font-display); font-size: clamp(1.8rem, min(5vw, 7vh), 3rem); rotate: -2deg; }
.wheelstage__title::before{ color: #3b0a5a; -webkit-text-stroke: 0.24em #3b0a5a; filter: drop-shadow(0 0.06em 0 #1b0431) drop-shadow(0 0.1em 0.1em rgba(0, 0, 0, 0.5)); }
.wheelstage__title::after{ color: #ff9a00; -webkit-text-stroke: 0.1em #ff9a00; }
.wheelstage__title > span{ background-image: linear-gradient(180deg, #fffbe6 0%, #ffe45c 45%, #ffc21a 55%, #ff9a00 100%); }
.wheelstage__body{
  position: relative; z-index: 1; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) 170px;
  align-items: center; gap: clamp(8px, 2vw, 28px); padding: 0 clamp(12px, 3vw, 36px) 14px;
  /* The wheel sizes itself from the room this row really has (cqh), so a short
     screen shrinks it instead of pushing the header and Spin off the panel —
     an iPad in landscape did (Gray, 27 Sep 2026). */
  container-type: size;
}
.wheelstage__note{
  position: relative; z-index: 1; margin: 0; padding: 2px 16px 4px; text-align: center; min-height: 1.4em;
  font-family: "Lilita One", var(--font-display); font-size: 1.25rem; color: #fff; text-shadow: 0 2px 0 #2a0848;
}
.wheel{
  --size: min(100%, calc((100cqh - 16px) / 1.12), 600px);
  position: relative; justify-self: center; width: var(--size); aspect-ratio: 1 / 1.12;
}
.wheel__rim, .wheel__face{ position: absolute; left: 0; top: 0; width: 100%; aspect-ratio: 1; }
.wheel__rim{ z-index: 2; pointer-events: none; filter: drop-shadow(0 12px 16px rgba(0, 0, 0, 0.45)); }
.wheel__face{ z-index: 1; }
.wheelstage.is-won .wheel__face, .overlay--wheel.is-won .wheel__face{ animation: wh-won 900ms ease-out; }
@keyframes wh-won{ 20%{ scale: 1.03; filter: brightness(1.25); } 60%{ scale: 1; filter: brightness(1.1); } }
/* The pointer: a gold flap at the top that flicks against every peg. */
.wheel__pointer{
  position: absolute; z-index: 3; left: 50%; top: 3.5%; width: 12%; aspect-ratio: 0.8; translate: -50% 0;
  transform-origin: 50% 12%;
  background: linear-gradient(180deg, #fff3a8, #ffc21a 50%, #d98a00);
  clip-path: polygon(10% 0, 90% 0, 90% 30%, 50% 100%, 10% 30%);
  filter: drop-shadow(0 4px 0 #8a4a00) drop-shadow(0 6px 6px rgba(0, 0, 0, 0.4));
}
.wheel__pointer::after{ content: ""; position: absolute; left: 32%; top: 10%; width: 36%; aspect-ratio: 1; border-radius: 50%; background: radial-gradient(circle at 35% 35%, #fff, #e0247f 60%, #8a0f48); }
.wheel__pointer.is-flick{ animation: wh-flick 140ms ease-out; }
@keyframes wh-flick{ 35%{ rotate: -22deg; } }
/* The pedestal under it, and the Spin button standing on it. */
/* The stand: a post from the hub down to a round gold-rimmed base the Spin button sits on. */
.wheel__stand{
  position: absolute; z-index: 0; left: 50%; bottom: 3%; width: 24%; height: 28%; translate: -50% 0;
  background: linear-gradient(90deg, #9c0f52, #ff6aa8 45%, #c2186c);
  clip-path: polygon(30% 0, 70% 0, 100% 100%, 0 100%);
}
.wheel::after{
  content: ""; position: absolute; z-index: 0; left: 50%; bottom: 0; width: 74%; height: 12%; translate: -50% 0;
  border-radius: 50%; background: radial-gradient(ellipse at 50% 35%, #ff7ac4, #c2186c 60%, #6e0a3f);
  box-shadow: 0 0 0 5px #ffd23f, 0 12px 20px rgba(0, 0, 0, 0.45);
}
.wheel__spin{
  position: absolute; z-index: 4; left: 50%; bottom: 2%; width: auto; min-width: 58%; translate: -50% 0;
  font-size: clamp(1.4rem, 3.2vw, 2.1rem); padding: 10px 34px 14px;
}
.wheel__spin[data-kind="video"]{ --bodyA: #8fd0ff; --bodyB: #2c8fd6; --bodyC: #1d6ba8; --ts: #0d3a66; --glow: rgba(29, 107, 168, 0.5); }
.wheel__spin:not(:disabled){ animation: wh-ready 1.3s ease-in-out infinite; }
.wheel__spin:disabled{ filter: saturate(0.5) brightness(0.9); cursor: default; }
.wheel__spin[hidden]{ display: none; }
@keyframes wh-ready{ 50%{ scale: 1.06; } }
/* The level bonus as a meter: your rung lit up. */
.wheel__meter{ display: grid; gap: 8px; justify-items: stretch; }
.wheel__metertitle{ margin: 0; text-align: center; font-family: "Lilita One", var(--font-display); font-size: 1.15rem; color: #ffe45c; text-shadow: 0 2px 0 #2a0848; }
.wheel__ladder{ list-style: none; margin: 0; padding: 8px; display: grid; gap: 6px; border-radius: 22px; background: rgba(20, 8, 60, 0.45); border: 3px solid rgba(255, 255, 255, 0.25); }
.wheel__step{
  display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 12px; border-radius: 14px;
  background: rgba(255, 255, 255, 0.07); color: rgba(255, 255, 255, 0.6); font-weight: 800; font-size: 0.85rem;
}
.wheel__step b{ font-family: "Lilita One", var(--font-display); font-size: 1.5rem; color: #fff; text-shadow: 0 2px 0 #2a0848; }
.wheel__step.is-yours{
  background: linear-gradient(180deg, #ffe45c, #ff9a00); color: #5a2600; box-shadow: 0 0 0 3px #fff, 0 0 18px rgba(255, 200, 60, 0.8);
  scale: 1.06;
}
.wheel__step.is-yours b{ color: #fff; text-shadow: 0 2px 0 #8a4a00; }
@media (max-width: 700px){
  .wheelstage__body{ grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
  .wheel{ --size: min(100%, calc((100cqh - 90px) / 1.12)); }
  .wheel__ladder{ grid-template-columns: repeat(5, 1fr); padding: 6px; }
  .wheel__step{ flex-direction: column; padding: 4px 2px; font-size: 0.66rem; gap: 0; }
  .wheel__step b{ font-size: 1.15rem; }
  .wheel__metertitle{ display: none; }
}
/* A phone on its side (844x360): a small header, the wheel on the left sized to the height, a compact ladder. */
@media (orientation: landscape) and (max-height: 500px) {
  .overlay--wheel{ padding: 6px; }
  .wheelstage{ height: 100%; }
  .wheelstage__bar{ padding: 8px 60px 0; }
  .wheelstage__bar .iconbtn--close{ top: 6px; right: 8px; }
  .wheelstage__title{ font-size: 1.5rem; }
  .wheelstage__note{ font-size: 0.95rem; padding: 0 12px 2px; }
  .wheelstage__body{ grid-template-columns: minmax(0, 1fr) 150px; grid-template-rows: minmax(0, 1fr); padding: 0 16px 8px; }
  .wheel{ --size: min(100%, calc((100cqh - 4px) / 1.12)); }
  .wheel__metertitle{ display: none; }
  .wheel__ladder{ grid-template-columns: none; gap: 4px; padding: 5px; }
  .wheel__step{ flex-direction: row; padding: 3px 10px; font-size: 0.72rem; }
  .wheel__step b{ font-size: 1.05rem; }
  .wheel__spin{ font-size: 1.2rem; padding: 6px 22px 9px; }
}
@media (prefers-reduced-motion: reduce){ .wheel__spin, .wheelstage__glow{ animation: none !important; } }

/* The daily gift (ui/dailyPanel.ts): fourteen days, two rows of seven; on a phone the rows scroll sideways. */
.panel--daily{ width: min(780px, 100%); text-align: center; }
.daily{ list-style: none; margin: 0 0 12px; padding: 4px 2px 8px; display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
.daily__day{ position: relative; display: grid; justify-items: center; align-content: center; gap: 2px; padding: 8px 4px 10px; border-radius: var(--radius-sm); background: rgba(255, 255, 255, 0.07); border: 2px solid rgba(255, 255, 255, 0.1); }
.daily__day.is-big{ background: linear-gradient(180deg, rgba(255, 190, 60, 0.3), rgba(255, 120, 30, 0.18)); border-color: rgba(255, 196, 80, 0.55); }
.daily__day.is-today{ border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent), 0 0 18px rgba(255, 79, 173, 0.45); }
.daily__day.is-done{ opacity: 0.55; }
.daily__label{ font-family: var(--font-display); font-size: 0.9rem; }
.daily__art{ width: 64px; height: 64px; object-fit: contain; filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.35)); }
.daily__day.is-big .daily__art{ width: 78px; height: 78px; }
.daily__gets{ display: flex; flex-wrap: wrap; justify-content: center; gap: 2px 6px; font-weight: 800; font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.daily__gets span{ display: inline-flex; align-items: center; gap: 2px; }
.daily__gets img{ width: 16px; height: 16px; }
.daily__mark{ position: absolute; top: 4px; right: 4px; width: 20px; height: 20px; border-radius: 50%; background: #3ccf5a; color: #fff; font-size: 0.8rem; line-height: 20px; font-weight: 900; }
/*
 * No sideways scrolling row on a phone. It was one: inside a portal's frame a
 * swipe fought the page and the row shook (Gray, CrazyGames on an iPhone, 28
 * Sep 2026: "this dont scroll at all, when you try it glitches and shakes").
 * Every day is on screen at once instead, four across upright, and the panel
 * scales to fit if it has to (client ui/fitPanels.ts).
 */
@media (max-width: 640px){
  .daily{ grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
  .daily__day{ padding: 6px 2px 8px; }
  .daily__art{ width: 48px; height: 48px; }
  .daily__day.is-big .daily__art{ width: 56px; height: 56px; }
  .daily__gets{ font-size: 0.72rem; gap: 1px 4px; }
  .daily__label{ font-size: 0.8rem; }
}
/* A phone on its side: smaller days, the two buttons side by side, so the panel fits without scrolling. */
@media (orientation: landscape) and (max-height: 500px){
  .panel--daily{ padding: 8px 14px 10px; }
  .panel--daily .panel__title{ margin: 0; font-size: 1.2rem; }
  .panel--daily .panel__sub{ margin: 0 0 6px; font-size: 0.8rem; }
  /* The fortnight as two rows of seven, nothing off screen. */
  .daily{ grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 5px; margin-bottom: 6px; padding-bottom: 4px; }
  .daily__day{ padding: 4px 2px 6px; }
  .daily__art{ width: min(38px, 9vh); height: min(38px, 9vh); }
  .daily__day.is-big .daily__art{ width: 44px; height: 44px; }
  .daily__gets{ font-size: 0.7rem; }
  .panel--daily .panel__note{ margin: 0; }
  .panel--daily .panel__actions{ grid-template-columns: 1fr 1fr; margin-top: 6px; }
  .panel--daily .panel__actions .button{ padding: 6px 12px 8px; }
}
/* The smallest phones on their side (667x320): the line under the title goes, or the buttons scroll. */
@media (orientation: landscape) and (max-height: 340px){
  .panel--daily .panel__sub{ display: none; }
}
/*
 * A short window that is not a phone: a laptop, or a game portal's frame.
 * Gray, 28 Sep 2026, on CrazyGames: the fortnight's title went off the top
 * and the panel scrolled. The two rows of days kept their full size (674px
 * of content) in any window from 500 to 760 high. Here the pictures shrink
 * with the height and the two buttons sit side by side, so the whole
 * fortnight fits.
 */
@media (min-height: 501px) and (max-height: 760px) and (min-width: 641px){
  .panel--daily{ padding-top: 12px; padding-bottom: 12px; }
  .panel--daily .panel__title{ margin: 0; }
  .panel--daily .panel__sub{ margin: 0 0 6px; }
  .daily{ gap: 6px; margin-bottom: 6px; padding-bottom: 4px; }
  .daily__day{ padding: 4px 4px 6px; }
  .daily__art{ width: min(64px, 7.5vh); height: min(64px, 7.5vh); }
  .daily__day.is-big .daily__art{ width: min(78px, 9vh); height: min(78px, 9vh); }
  .daily__gets{ font-size: 0.76rem; }
  .panel--daily .panel__actions{ grid-template-columns: 1fr 1fr; margin-top: 6px; }
}
.updates{ list-style: none; margin: 0 0 16px; padding: 0; display: grid; gap: 10px; text-align: left; }
.updates__item{ padding: 12px 14px; border-radius: var(--radius-sm); background: rgba(255, 255, 255, 0.06); border: 2px solid rgba(255, 255, 255, 0.08); }
.updates__item.is-new{ border-color: var(--accent); background: rgba(255, 79, 173, 0.1); }
.updates__date{ margin: 0; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-dim); }
.updates__title{ margin: 2px 0 4px; font-family: var(--font-display); font-size: 1.1rem; }
.updates__body{ margin: 0; font-size: 0.92rem; color: var(--text-dim); line-height: 1.45; }
/* A phone on its side with the browser's bars showing: two news items fit without a sliver of a third row. */
@media (orientation: landscape) and (max-height: 340px) {
  .updates{ gap: 6px; margin-bottom: 10px; }
  .updates__item{ padding: 6px 12px; }
  .updates__title{ font-size: 1rem; margin: 1px 0 2px; }
  .updates__body{ line-height: 1.35; }
}

/* ------------------------------------------------------ level and a cleaner menu */
/*
 * Worms Zone's front screen (15 Sep 2026): a level badge and bar top left, the
 * currency top right, one big Play, round buttons in the corners, and nothing
 * else. Gray: "worms zone has player levels, we need to add that, they show
 * it" and "we got too much going on with footer links too, loads of stuff".
 */
.menu__level{ display: flex; align-items: center; gap: 8px; }
.menu__level[hidden]{ display: none; }
.menu__levelbadge{
  display: grid; place-items: center; min-width: 46px; height: 46px; padding: 0 8px;
  border-radius: 14px; border: 3px solid #fff;
  background: linear-gradient(180deg, #ffd27a, #ff9f1c 55%, #d97b00);
  box-shadow: 0 0 0 2px #7a3d00, 0 4px 0 #a35a00;
  font-family: "Lilita One", var(--font-display); font-size: 1.45rem; color: #fff; line-height: 1;
  text-shadow: 0 2px 0 #7a3d00, 1px 0 0 #7a3d00, -1px 0 0 #7a3d00;
}
.menu__levelbar{
  display: block; width: clamp(90px, 18vw, 150px); height: 15px; overflow: hidden;
  border-radius: 999px; border: 2px solid rgba(255, 255, 255, 0.9); background: rgba(20, 16, 60, 0.75);
}
.menu__levelbar i{ display: block; width: 0; height: 100%; background: linear-gradient(90deg, #c6f432, #e8ff7a); transition: width 0.6s ease; }
/*
 * Your picture, top left: your worm's face in an orange tile with the level in
 * its corner and the bar beside it, Worms Zone's arrangement. It is the way
 * into your Profile, and a red dot on it means an achievement is waiting.
 */
.menu__avatar{
  position: relative; appearance: none; -webkit-appearance: none; cursor: pointer;
  margin: 0; padding: 0 0 8px; border: 0; background: none; color: inherit; font: inherit;
}
/* Violet behind the face, not orange: an orange worm's face vanished into an
   orange tile (Ember on the phone, 15 Sep 2026), and no skin is this violet. */
.menu__avatarart{
  display: block; flex: none; width: 58px; height: 58px; border-radius: 16px; border: 3px solid #fff;
  background: radial-gradient(circle at 50% 36%, #8f7bff, #4a38c4 72%);
  box-shadow: 0 0 0 2px #7a3d00, 0 4px 0 #a35a00;
}
.menu__avatar .menu__levelbadge{
  position: absolute; left: 36px; bottom: 0; min-width: 30px; height: 30px; padding: 0 5px;
  border-radius: 10px; font-size: 1rem;
}
.menu__avatar .menu__levelbar{ margin-left: 10px; }
@media (hover: hover){ .menu__avatar:hover .menu__avatarart{ filter: brightness(1.07); } }
.menu__avatar:focus-visible{ outline: none; }
.menu__avatar:focus-visible .menu__avatarart{ outline: 3px solid var(--accent); outline-offset: 3px; }
.menu__avatardot{
  position: absolute; top: -5px; left: 47px; width: 16px; height: 16px; border-radius: 50%;
  background: #ff3b4f; border: 2px solid #fff; box-shadow: 0 2px 0 rgba(0, 0, 0, 0.35);
}
.menu__avatardot[hidden]{ display: none; }
.button--tab .menu__avatardot{ top: 4px; left: auto; right: 6px; }

/* ---------------------------------------------------------------- profile
 * Profile (ui/profileScreen.ts): the Settings screen shell with two tabs.
 * You: your worm's face, your level, six numbers. Achievements: a podium of
 * bronze, silver and gold counts, then a card per achievement, collectable
 * ones first. Worms Zone's layout, with every card saying what it asks.
 */
.button--tab{ position: relative; }
.screen__pane--wide{ max-width: 1040px; }
.dossier{ display: grid; grid-template-columns: minmax(220px, 300px) minmax(0, 1fr); gap: 22px; align-items: start; }
.dossier__me{
  display: grid; justify-items: center; gap: 10px; padding: 18px 16px 20px;
  border-radius: 22px; background: #3b2f8f; box-shadow: inset 0 -4px 0 rgba(0, 0, 0, 0.25);
}
.dossier__avatar{
  display: block; width: 150px; height: 150px; border-radius: 28px;
  background: radial-gradient(circle at 50% 36%, #8f7bff, #4a38c4 70%);
  border: 4px solid #fff; box-shadow: 0 0 0 2px #7a2a6a, 0 8px 0 -2px rgba(0, 0, 0, 0.3);
}
.dossier__name{
  margin: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: "Lilita One", var(--font-display); font-size: 1.5rem; color: #fff;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.35);
}
.dossier__level{ display: flex; align-items: center; gap: 8px; width: 100%; }
.dossier__level .menu__levelbar{ flex: 1 1 auto; width: auto; }

/* Six numbers, each with its painted icon (generated 15 Sep 2026, our own objects). */
.statgrid{ display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.statcard--length{ --statcard-icon: url(/ui/stat-length.webp?v=67d6fb04); }
.statcard--time{ --statcard-icon: url(/ui/stat-time.webp?v=38d5368e); }
.statcard--kills{ --statcard-icon: url(/ui/stat-kills.webp?v=0e207249); }
.statcard--games{ --statcard-icon: url(/ui/stat-games.webp?v=a7b52a50); }
.statcard--played{ --statcard-icon: url(/ui/stat-played.webp?v=0d1aa30b); }
.statcard--owned{ --statcard-icon: url(/ui/stat-owned.webp?v=74086b12); }
.statcard::before{
  content: ""; grid-row: 2; justify-self: center; align-self: center; width: clamp(56px, 6vw, 76px); aspect-ratio: 1;
  background: var(--statcard-icon, none) center / contain no-repeat; filter: drop-shadow(0 4px 2px rgba(20, 10, 60, 0.35));
}
.statcard__value{ grid-row: 3; }
.statcard{
  display: grid; grid-template-rows: auto 1fr auto; gap: 2px; min-height: 150px;
  padding: 10px 12px 12px; border-radius: 18px; border: 3px solid #fff;
  background: linear-gradient(180deg, #7d8cff, #5a6ae6);
  box-shadow: 0 0 0 2px #3a2a8a, inset 0 -5px 0 rgba(0, 0, 0, 0.18);
}
.statcard__label{ font-family: "Lilita One", var(--font-display); font-size: 1rem; color: #ffe45c; text-shadow: 0 2px 0 #35187f; }
.statcard__value{
  align-self: end; justify-self: end; font-family: "Lilita One", var(--font-display); font-weight: 400;
  font-size: clamp(1.5rem, 2.6vw, 2rem); color: #fff; font-variant-numeric: tabular-nums; text-shadow: 0 2px 0 #35187f;
}

/*
 * The podium: three steps, gold in the middle and highest, silver on its left
 * and bronze on its right — the way a real one stands (it had bronze first).
 *
 * Each step is dressed like a Burrow card (the same ray burst and glow, in its
 * own metal) with a band of sheen across it, rather than a flat fill. Gray,
 * 23 Sep 2026: "cards look boring with no backgrounds".
 */
.podium{ display: flex; justify-content: center; align-items: flex-end; gap: 10px; }
.podium__step{
  --metal: #e7a91c; --metal-light: #ffe27a; --metal-deep: #9a6a00;
  position: relative;
  display: grid; justify-items: center; align-content: end; gap: 2px; width: min(150px, 30%);
  padding: 8px 6px 10px; border-radius: 16px 16px 8px 8px; border: 3px solid #fff;
  background:
    linear-gradient(115deg, transparent 38%, rgba(255, 255, 255, 0.34) 46%, rgba(255, 255, 255, 0.08) 54%, transparent 60%),
    repeating-conic-gradient(from 0deg at 50% 30%, rgba(255, 255, 255, 0.16) 0deg 9deg, transparent 9deg 18deg),
    radial-gradient(circle at 50% 30%, var(--metal-light), var(--metal) 55%, var(--metal-deep) 100%);
  box-shadow: 0 0 0 2px #3a2a8a, inset 0 -5px 0 rgba(0, 0, 0, 0.2), inset 0 3px 0 rgba(255, 255, 255, 0.45);
}
.podium__step--silver{ order: 1; min-height: 112px; --metal: #aebfd4; --metal-light: #f4f8ff; --metal-deep: #6f829c; --medal: #8497b0; }
.podium__step--gold{ order: 2; min-height: 128px; --metal: #f0b52a; --metal-light: #fff0a8; --metal-deep: #b07a06; --medal: #d99a0b; }
.podium__step--bronze{ order: 3; min-height: 100px; --metal: #d9844a; --metal-light: #ffc79a; --metal-deep: #97491c; --medal: #b8622c; }
.podium__cup{
  width: 58px; height: 58px; margin-top: -30px;
  background: var(--medal-img, none) center / contain no-repeat; filter: drop-shadow(0 4px 3px rgba(0, 0, 0, 0.35));
}
.podium__step--bronze{ --medal-img: url(/ui/medal-bronze.webp?v=28c99312); }
.podium__step--silver{ --medal-img: url(/ui/medal-silver.webp?v=33532ef7); }
.podium__step--gold{ --medal-img: url(/ui/medal-gold.webp?v=43404af2); }
.podium__count{ font-family: "Lilita One", var(--font-display); font-weight: 400; font-size: 1.5rem; color: #fff; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.45); font-variant-numeric: tabular-nums; }
.podium__name{ font-family: var(--font-display); font-weight: 700; font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(20, 10, 50, 0.75); }

.trophies{ display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.trophy{
  display: grid; grid-template-rows: auto 1fr auto; gap: 8px;
  padding: 10px 12px 12px; border-radius: 18px; border: 3px solid rgba(255, 255, 255, 0.85);
  background: linear-gradient(180deg, #5a4bd0, #4636a8);
  box-shadow: 0 0 0 2px #2a1d70, inset 0 -5px 0 rgba(0, 0, 0, 0.2);
}
.trophy[data-tier="bronze"]{ border-color: #f2a36b; }
.trophy[data-tier="silver"]{ border-color: #e3ecf7; }
.trophy[data-tier="gold"]{ border-color: #ffd24a; }
.trophy.is-ready{ border-color: #c6f432; box-shadow: 0 0 0 2px #2a1d70, 0 0 18px -2px rgba(198, 244, 50, 0.6), inset 0 -5px 0 rgba(0, 0, 0, 0.2); }
.trophy.is-done{ background: linear-gradient(180deg, #4a3fa8, #3a2f8a); }
.trophy__head{ display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.trophy__heading{ display: flex; align-items: center; gap: 8px; min-width: 0; }
/* The card's own badge (tools/import-badges.py), or the plain medal until it exists. */
.trophy__badge{ width: 40px; height: 40px; flex: none; object-fit: contain; filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.4)); }
.trophy__badge.is-dim{ filter: grayscale(1) brightness(0.6); opacity: 0.55; }
.trophy__badge.is-pulsing{ animation: trophy-badge-pulse 1.6s ease-in-out infinite; }
.trophy__name{ margin: 0; font-family: "Lilita One", var(--font-display); font-weight: 400; font-size: 1.15rem; color: #ffe45c; text-shadow: 0 2px 0 #35187f; }
.trophy__dots{ display: inline-flex; gap: 5px; flex: none; }
.trophy__dot{ width: 12px; height: 12px; border-radius: 50%; background: rgba(0, 0, 0, 0.35); box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.4); }
.trophy__dot:nth-child(1).is-on{ background: #e08a4f; }
.trophy__dot:nth-child(2).is-on{ background: #d9e3ef; }
.trophy__dot:nth-child(3).is-on{ background: #ffd24a; }
.trophy__dot.is-ready{ background: #c6f432; box-shadow: 0 0 0 2px rgba(198, 244, 50, 0.35); }
.trophy__ask{ margin: 0; font-size: 0.95rem; line-height: 1.35; color: #fff; }
.trophy__foot{ display: flex; align-items: center; gap: 10px; min-height: 40px; }
.trophy__bar{
  position: relative; flex: 1 1 auto; height: 26px; border-radius: 9px; overflow: hidden;
  background: #1d1650; box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.45);
}
.trophy__bar > i{ position: absolute; inset: 0 auto 0 0; background: linear-gradient(180deg, #ffd27a, #ff9f1c 55%, #ff4fad); }
.trophy__barlabel{
  position: relative; display: block; text-align: center; line-height: 26px;
  font-family: "Lilita One", var(--font-display); font-size: 0.9rem; color: #fff;
  font-variant-numeric: tabular-nums; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
}
.trophy__prize, .trophy__collect{ display: inline-flex; align-items: center; gap: 4px; }
.trophy__prize{ font-family: "Lilita One", var(--font-display); font-size: 1.05rem; color: #ffd27a; }
.trophy__prize b{ font-weight: 400; }
.trophy__collect{ flex: 1 1 auto; justify-content: center; }
.trophy__done{ font-family: "Lilita One", var(--font-display); font-size: 1.05rem; color: #c6f432; }
/* Event achievements (profileScreen.ts paintEventAchievements): a section per event, and a card the event ended on, greyed. */
.trophies__events{ display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; margin: 4px 0 16px; min-width: 0; }
.trophies__events[hidden]{ display: none; }
.trophies__event{ display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; min-width: 0; }
/* German names run long (Kürbiszertrümmerer): a card's name may break rather than push the grid sideways (check:overflow, 932x430). */
.trophies__event .trophy__name{ overflow-wrap: anywhere; hyphens: auto; }
.trophies__event + .trophies__event{ margin-top: 8px; }
.trophies + .trophies__events{ margin: 16px 0 4px; }
.trophy.is-missed{ filter: grayscale(0.85); opacity: 0.6; }
.trophy__missed{ font-family: "Lilita One", var(--font-display); font-size: 1rem; color: rgba(255, 255, 255, 0.8); }

/*
 * Layered game type (the game-UI research, 15 Sep 2026). A plain outlined
 * word is what made our screens read like a web page. Game titles are a stack:
 * a light-to-dark fill with a hard gloss band, an inner coloured rim, a dark
 * outer outline, a solid extrusion and a ground shadow. The element carries
 * the fill span; ::before (outer outline, extrusion, shadow) and ::after
 * (inner rim) are copies of the same words from data-text, so any script that
 * changes the words changes data-text too (profileScreen.ts `layered`).
 */
.fx{ position: relative; display: inline-block; isolation: isolate; line-height: 1; white-space: nowrap; }
.fx::before, .fx::after{
  content: attr(data-text); content: attr(data-text) / "";
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
}
.fx > span{
  display: inline-block; -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}

/*
 * The reward: one hero word, one number, one button, revealed in beats
 * (profileScreen.ts BEAT: impact 520ms, count 1300ms, bar 2100ms, button
 * 2600ms). Everything else is small tracked capitals or plain text.
 */
.reward{
  --tint: #e39a5a; --t1: #ffe2c2; --t2: #e39a5a; --t3: #9c521f; --ink: #3a1606;
  position: absolute; inset: 0; z-index: 5; overflow: hidden;
  display: grid; place-items: center; padding: 18px;
  background:
    radial-gradient(circle at 50% 34%, rgba(255, 255, 255, 0.16), transparent 42%),
    radial-gradient(circle at 50% 38%, #7a64ff, #4332b8 46%, #1f1760 88%);
  cursor: pointer;
}
.reward[data-tier="silver"]{ --tint: #d6e2ff; --t1: #ffffff; --t2: #cfd8e8; --t3: #7d8aa3; --ink: #1e2436; }
.reward[data-tier="gold"]{ --tint: #ffd84a; --t1: #fff7c4; --t2: #ffd84a; --t3: #d98a00; --ink: #4a2600; }
.reward[hidden]{ display: none; }
/* The one reveal for the whole game (ui/rewardReveal.ts): over every screen, not inside one. */
.reward--page{ position: fixed; z-index: 90; }
.reward__next[hidden], .reward__num[hidden], .reward__extras[hidden]{ display: none; }
/* Confetti falling over the whole screen from the impact beat. */
.reward__rain{ position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.reward__drop{
  position: absolute; top: -24px; left: var(--x); width: 10px; height: 16px; border-radius: 2px; background: var(--c);
  opacity: 0;
}
.reward.is-playing .reward__drop{ animation: rw-rain var(--t, 3s) cubic-bezier(0.3, 0.1, 0.6, 1) var(--d, 0.5s) both; }
@keyframes rw-rain{
  0%{ opacity: 1; translate: 0 0; rotate: 0deg; }
  85%{ opacity: 1; }
  100%{ opacity: 0; translate: var(--sway) 110vh; rotate: var(--r); }
}
/* What else it paid: a row of power-up icons with their counts. */
.reward__extras{ list-style: none; margin: 2px 0 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
/* Each power-up won: its picture, name and count, and what it does (powerupArt.ts chargeExtras). */
.reward__extras{ flex-direction: column; align-items: stretch; width: min(380px, 100%); }
.reward__extra{
  display: flex; align-items: center; gap: 10px; padding: 6px 16px 6px 8px; border-radius: 18px; text-align: left;
  background: rgba(20, 10, 60, 0.5); border: 2px solid rgba(255, 255, 255, 0.35);
}
.reward__extra img{ width: 48px; height: 48px; object-fit: contain; flex: none; }
.reward__extratext{ display: grid; gap: 1px; }
.reward__extratext b{ font-family: "Lilita One", var(--font-display); font-weight: 400; font-size: 1.3rem; color: #fff; text-shadow: 0 2px 0 #1b0431; }
.reward__extratext small{ font-size: 0.86rem; font-weight: 700; color: #ffe45c; }
/* How to use them, under the list. */
.reward__note{ margin: 0; max-width: 380px; font-size: 0.92rem; line-height: 1.4; color: rgba(255, 255, 255, 0.88); }
.reward__note[hidden]{ display: none; }
.reward.is-playing .reward__note{ animation: rw-fade 300ms ease-out 1700ms both; }
.reward.is-playing .reward__extras{ animation: rw-pop 260ms cubic-bezier(0.34, 1.56, 0.64, 1) 1400ms both; }
.reward__rays{
  position: absolute; inset: 0;
  background: repeating-conic-gradient(from var(--ray-turn) at 50% 32%, rgba(255, 255, 255, 0.15) 0 9deg, transparent 9deg 24deg);
  -webkit-mask: radial-gradient(circle at 50% 32%, #000 71px, transparent 424px); mask: radial-gradient(circle at 50% 32%, #000 71px, transparent 424px);
  animation: ray-turn 20s linear infinite; pointer-events: none;
}
.reward__stage{
  position: relative; display: grid; justify-items: center; gap: 8px; width: min(460px, 100%); text-align: center;
}
.reward__eyebrow{
  margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 0.8rem;
  letter-spacing: 0.32em; text-transform: uppercase; color: var(--tint);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.35);
}
.reward__medal{
  width: clamp(120px, 20vh, 180px); height: auto; aspect-ratio: 1;
  filter: drop-shadow(0 10px 8px rgba(0, 0, 0, 0.4)) drop-shadow(0 0 30px color-mix(in oklab, var(--tint) 55%, transparent));
}
.reward__burst{ position: absolute; left: 50%; top: 26%; width: 0; height: 0; pointer-events: none; }
.reward__bit{ position: absolute; left: 0; top: 0; translate: -50% -50%; opacity: 0; }
.reward.is-playing .reward__bit{ animation: rw-fly 2s cubic-bezier(0.12, 0.75, 0.3, 1) var(--d, 0.52s) both; }
.reward__bit--nugget{ width: clamp(34px, 3vw, 52px); height: clamp(34px, 3vw, 52px); background: url(/ui/reward-nuggets.webp?v=ab621f30) center / contain no-repeat; }
.reward__bit--confetti{ width: 10px; height: 18px; border-radius: 3px; background: var(--c, #fff); }

/* Recipe 1, the hero: white with a pink under-band, a pink rim, a deep violet outline and extrusion. */
.reward__title{
  margin: 6px 0 2px; font-family: "Lilita One", var(--font-display); font-weight: 400;
  font-size: clamp(2.3rem, 7vw, 4rem); letter-spacing: 0.01em; rotate: -3deg;
}
.reward__title::before{
  color: #2a0848; -webkit-text-stroke: 0.26em #2a0848;
  filter: drop-shadow(0 0.035em 0 #2a0848) drop-shadow(0 0.035em 0 #1b0431) drop-shadow(0 0.07em 0.08em rgba(8, 0, 24, 0.55));
}
.reward__title::after{ color: #ff4fad; -webkit-text-stroke: 0.12em #ff4fad; }
.reward__title > span{ background-image: linear-gradient(180deg, #ffffff 0%, #ffffff 46%, #ffe3f3 54%, #ffb8df 100%); }

/* Recipe 2, the tier: a swallowtail ribbon in the tier's metal, the word white with a tier-ink outline. */
.reward__ribbonwrap{ filter: drop-shadow(0 0.14em 0 #1b0431) drop-shadow(0 0.3em 0.3em rgba(8, 0, 24, 0.45)); }
.reward__ribbon{
  margin: 0; display: inline-grid; place-items: center; padding: 0.4em 1.7em 0.52em;
  font-family: "Lilita One", var(--font-display); font-size: 1.45rem; letter-spacing: 0.08em; text-transform: uppercase;
  background: linear-gradient(180deg, var(--t1) 0%, var(--t2) 52%, var(--t3) 100%);
  clip-path: polygon(0 0, 100% 0, calc(100% - 0.75em) 50%, 100% 100%, 0 100%, 0.75em 50%);
  box-shadow: inset 0 0.14em 0 rgba(255, 255, 255, 0.6), inset 0 -0.18em 0 rgba(0, 0, 0, 0.28);
}
.reward__ribbon .fx::before{ color: var(--ink); -webkit-text-stroke: 0.2em var(--ink); filter: drop-shadow(0 0.06em 0 var(--ink)); }
.reward__ribbon .fx > span{ background-image: linear-gradient(180deg, #fff 0 55%, #fff0e0 56% 100%); }

/* Recipe 3, the number: gold with a pale rim and a brown outline, a small plus, a tracked unit. */
.reward__num{ margin: 4px 0 0; display: inline-flex; align-items: center; gap: 0.1em; font-family: "Lilita One", var(--font-display); font-size: clamp(3rem, 8vh, 4.2rem); }
.reward__num .fx{ font-variant-numeric: tabular-nums; min-width: 1.6em; text-align: right; }
.reward__num .fx::before{
  color: #3b1a00; -webkit-text-stroke: 0.2em #3b1a00;
  filter: drop-shadow(0 0.04em 0 #3b1a00) drop-shadow(0 0.04em 0 #230f00) drop-shadow(0 0.08em 0.1em rgba(0, 0, 0, 0.5));
}
.reward__num .fx::after{ color: #fff3b0; -webkit-text-stroke: 0.08em #fff3b0; }
.reward__num .fx > span{ background-image: linear-gradient(180deg, #fffbe6 0%, #ffe45c 42%, #ffc21a 60%, #ff9a00 100%); }
.reward__num .fx.is-landed{ animation: rw-land 240ms cubic-bezier(0.3, 1.8, 0.5, 1); }
.reward__plus{ font-size: 0.6em; color: #ffe45c; -webkit-text-stroke: 0.16em #3b1a00; paint-order: stroke fill; }
.reward__nug{ width: 0.95em; height: 0.95em; margin-left: 0.08em; filter: drop-shadow(0 4px 2px rgba(0, 0, 0, 0.4)); }
.reward__unit{
  align-self: flex-end; margin: 0 0 0.9em 0.3em; font-family: var(--font-display); font-weight: 700;
  font-size: 0.8rem; letter-spacing: 0.3em; text-transform: uppercase; color: #fff; opacity: 0.85;
}

/* The next goal: a label, a bar with the next medal ghosted at its end, one plain line. */
.reward__next{ display: grid; gap: 6px; width: min(360px, 100%); margin-top: 6px; }
.reward__nextlabel{
  margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 0.72rem;
  letter-spacing: 0.3em; text-transform: uppercase; color: rgba(255, 255, 255, 0.75);
}
.reward__bar{
  position: relative; height: 26px; margin-right: 22px; border-radius: 13px;
  background: #1d1650; box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.45), 0 0 0 2px rgba(255, 255, 255, 0.18);
}
.reward__bar > i{
  position: absolute; inset: 0 auto 0 0; width: var(--fill, 0%); border-radius: 13px;
  background: linear-gradient(180deg, var(--t1), var(--t2) 55%, var(--t3));
  box-shadow: 0 0 12px color-mix(in oklab, var(--tint) 60%, transparent);
}
.reward.is-playing .reward__bar > i{ animation: rw-fill 500ms ease-out 2100ms both; }
.reward__bartext{
  position: relative; display: block; line-height: 26px; font-family: "Lilita One", var(--font-display); font-size: 0.95rem;
  color: #fff; font-variant-numeric: tabular-nums; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
}
.reward__ghost{ position: absolute; right: -30px; top: 50%; translate: 0 -50%; width: 46px; height: 46px; opacity: 0.45; filter: grayscale(0.3); }
.reward__nexttext{ margin: 0; font-size: 1rem; line-height: 1.4; color: rgba(255, 255, 255, 0.85); }
.reward__actions{ display: grid; justify-items: center; gap: 8px; margin-top: 10px; }
.reward__collect{ min-width: 220px; }
.reward__collect:disabled{ cursor: default; }
.reward__close{ min-width: 140px; }
.reward__close:disabled{ cursor: default; }
/* A skin drawn on a canvas is not square like a medal. */
.reward__medal{ object-fit: contain; }

/* The beats. `both` holds each piece hidden until its moment. */
.reward.is-playing .reward__eyebrow{ animation: rw-fade 150ms ease-out 450ms both; }
.reward.is-playing .reward__medal{ animation: rw-drop 520ms cubic-bezier(0.3, 1.5, 0.5, 1) 0ms both, rw-sway 3s ease-in-out 1200ms infinite; }
.reward.is-playing .reward__title{ animation: rw-title 420ms cubic-bezier(0.34, 1.56, 0.64, 1) 600ms both; }
.reward.is-playing .reward__ribbonwrap{ animation: rw-unfold 260ms cubic-bezier(0.34, 1.56, 0.64, 1) 900ms both; }
.reward.is-playing .reward__num{ animation: rw-fade 200ms ease-out 1250ms both; }
.reward.is-playing .reward__next{ animation: rw-fade 300ms ease-out 2100ms both; }
.reward.is-playing .reward__actions{ animation: rw-pop 200ms cubic-bezier(0.34, 1.56, 0.64, 1) 2600ms both; }
/* A tap skips: every piece jumps to where it ends. */
.reward.is-skipped *{ animation-delay: 0s !important; animation-duration: 1ms !important; }
.reward.is-skipped .reward__medal{ animation: none !important; }

/*
 * A phone on its side: two columns, the prize on the left and what it paid
 * with Collect on the right. One column put Collect below the screen at
 * 844x360, and the reveal could not be closed (27 Sep 2026).
 */
@media (orientation: landscape) and (max-height: 500px) {
  .reward{ padding: 10px 16px; }
  .reward__stage{
    width: min(820px, 100%); grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-auto-flow: dense;
    column-gap: 28px; row-gap: 6px; align-content: center; align-items: center;
  }
  .reward__stage > :is(.reward__eyebrow, .reward__medal, .reward__title, .reward__ribbonwrap){ grid-column: 1; }
  .reward__stage > :is(.reward__num, .reward__extras, .reward__note, .reward__next, .reward__actions){ grid-column: 2; }
  .reward__medal{ width: clamp(70px, 26vh, 130px); }
  .reward__title{ font-size: clamp(1.7rem, 9vh, 2.8rem); margin: 0; }
  .reward__num{ font-size: clamp(2.2rem, 12vh, 3.4rem); margin: 0; }
  .reward__extra{ padding: 4px 12px 4px 6px; }
  .reward__extra img{ width: 38px; height: 38px; }
  .reward__note{ font-size: 0.8rem; }
  /* Wear it and Close side by side: stacked they pushed the stage off a short screen. */
  .reward__actions{ margin-top: 4px; width: 100%; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 8px; }
  .reward__collect, .reward__close{ min-width: 0; width: 100%; }
  .reward__close[hidden]{ display: none; }
  .reward__note{ font-size: 0.72rem; line-height: 1.15; }
  .reward__burst{ left: 25%; }
  /*
   * Nothing to count (a sign-in with no bonus): one column, centred. Two put
   * Play alone at the top right and squeezed the ribbon off the left edge
   * (Gray's iPhone home-screen app, 11 Oct 2026).
   */
  .reward__stage:not(:has(> :is(.reward__num, .reward__extras, .reward__next):not([hidden]))){ width: min(520px, 100%); grid-template-columns: minmax(0, 1fr); }
  .reward__stage:not(:has(> :is(.reward__num, .reward__extras, .reward__next):not([hidden]))) > *{ grid-column: 1; }
  .reward__stage:not(:has(> :is(.reward__num, .reward__extras, .reward__next):not([hidden]))) .reward__actions{ width: min(320px, 100%); }
  .reward__stage:not(:has(> :is(.reward__num, .reward__extras, .reward__next):not([hidden]))) ~ .reward__burst,
  .reward__stage:not(:has(> :is(.reward__num, .reward__extras, .reward__next):not([hidden]))) .reward__burst{ left: 50%; }
}
/* The ribbon never wider than the screen: its words wrap inside it instead. */
.reward__ribbonwrap{ max-width: 100%; }
.reward__ribbon{ max-width: 100%; font-size: clamp(1rem, 5.4vw, 1.45rem); }

@keyframes rw-spin{ to{ rotate: 360deg; } }
/*
 * Light rays that turn INSIDE their own box. They used to be a square 900 to
 * 1400px across, spun in place: clipped on screen, but iPhone Safari sizes a
 * portal's iframe to its content, so the game grew wider than the frame and
 * sat off to the right from the loading screen on (Gray, CrazyGames on an
 * iPhone held sideways, 28 Sep 2026). Now the gradient's start angle turns
 * and the element is never larger than what it covers.
 */
@property --ray-turn{ syntax: "<angle>"; inherits: false; initial-value: 0deg; }
@keyframes ray-turn{ to{ --ray-turn: 360deg; } }
@keyframes rw-fade{ from{ opacity: 0; translate: 0 6px; } }
@keyframes rw-drop{ 0%{ opacity: 0; translate: 0 -40%; scale: 1.6; } 60%{ opacity: 1; scale: 0.92; } 100%{ scale: 1; } }
@keyframes rw-sway{ 0%, 100%{ rotate: -3deg; } 50%{ rotate: 3deg; } }
@keyframes rw-title{ from{ opacity: 0; scale: 0.4; translate: 0 0.5em; } 60%{ opacity: 1; scale: 1.1; } }
@keyframes rw-unfold{ from{ scale: 0 1; opacity: 0; } }
@keyframes rw-pop{ from{ scale: 0.8; opacity: 0; } }
@keyframes rw-fill{ from{ width: 0; } }
@keyframes rw-land{ 40%{ scale: 1.24; } }
@keyframes rw-fly{
  0%{ transform: translate(0, 0) rotate(0) scale(0.3); opacity: 0; }
  6%{ opacity: 1; } 82%{ opacity: 1; }
  100%{ transform: translate(var(--dx), var(--dy)) rotate(var(--r)) scale(1); opacity: 0; }
}
@keyframes trophy-badge-pulse{ 0%, 100%{ transform: scale(1); opacity: 1; } 50%{ transform: scale(1.1); opacity: 0.8; } }

@media (prefers-reduced-motion: reduce){
  .reward__rays, .reward *{ animation: none !important; }
  .trophy__badge.is-pulsing{ animation: none; }
}

@media (max-width: 760px){
  .dossier{ grid-template-columns: minmax(0, 1fr); }
  .statgrid{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .trophies{ grid-template-columns: minmax(0, 1fr); }
}
/*
 * Off the play screen. The footer row stays in the page, so every page is
 * still linked from the homepage for a crawler, and the ☰ menu in the top
 * rail carries the same links for a player; the badges, key hints and rules
 * line go, as in every front screen in the genre.
 */
.menu__foot{ display: none; }

/* The death screen's XP line, and a level-up (SOT section 4). */
.death__xp{
  margin: -4px 0 8px; font-family: "Lilita One", var(--font-display); font-size: 1.3rem; color: #c6f432;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.45);
}
.death__xp[hidden]{ display: none; }
/* The XP pill and the level beside it; a level-up is a gold one that pulses. */
.death__xp{ display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 8px; }
.death__xp[hidden]{ display: none; }
.death__xp [data-death-level]{
  padding: 3px 12px 4px; border-radius: 999px; border: 2px solid #fff; background: linear-gradient(180deg, #b9a8ff, #6a52e6);
  font-family: "Lilita One", var(--font-display); font-size: 0.95rem; color: #fff; text-shadow: 0 2px 0 #2c1a7a;
}
.death__xp .death__levelup{ background: linear-gradient(180deg, #ffe45c, #ff9f1c); text-shadow: 0 2px 0 #7a3d00; animation: tile-pulse 1.1s ease-in-out infinite; }
.death__levelup{ color: #ffd27a; }

/* ------------------------------------------------------ the burrow */
/*
 * The Burrow, the wardrobe inside the game (ui/burrowScreen.ts, gameShell
 * data-game-burrow). Gray, 15 Sep 2026, from Worms Zone's wardrobe, and again
 * on 18 Sep with their Wardrobe and ours side by side: "their layout is way
 * better, bigger cards, better styling, thats what im saying we do similar".
 * So it is their layout: tabs along the top, a rail of icon buttons down the
 * left, the worm standing on a lit stage, and big sunburst cards in two rows
 * that scroll sideways. The Settings screen's shell and the house pieces
 * (Candy buttons, .station, .iconbtn); what is new here is layout, the five
 * rarity colours and the painted stages and cards.
 */
.game:has([data-game-burrow]:not([hidden])) :is([data-game-dock], .hud){ visibility: hidden; }

/* The skin card's picture is the way in: a picture button, like your picture top left. */
button.pickcard__art{
  appearance: none; -webkit-appearance: none; margin: 0; padding: 0; border: 0;
  background: none; color: inherit; font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
@media (hover: hover){ button.pickcard__art:hover canvas{ filter: brightness(1.1); } }
button.pickcard__art:focus-visible{ outline: 3px solid #fff; outline-offset: 3px; }

/*
 * Rarity colours, the SOT's order, as the painted stages and cards are
 * (ui/stage-*.webp, ui/card-*.webp): Common silver-grey, Rare royal blue, Epic
 * violet, Legendary gold-orange, Mythical hot pink. --rarity is the bright
 * face, --rarity-deep the shade under it.
 */
.screen--burrow, .screen--burrow [data-rarity="common"]{ --rarity: #c9d2df; --rarity-deep: #56627a; --stage-art: url(/ui/stage-common.webp?v=db85e601); --card-art: url(/ui/card-common.webp?v=817dba22); }
.screen--burrow [data-rarity="rare"]{ --rarity: #4f8dff; --rarity-deep: #1b3ca3; --stage-art: url(/ui/stage-rare.webp?v=9123adc1); --card-art: url(/ui/card-rare.webp?v=b1324876); }
.screen--burrow [data-rarity="epic"]{ --rarity: #b36bff; --rarity-deep: #531fa6; --stage-art: url(/ui/stage-epic.webp?v=cbfbb8b2); --card-art: url(/ui/card-epic.webp?v=9481a0ec); }
.screen--burrow [data-rarity="legendary"]{ --rarity: #ffb12e; --rarity-deep: #b05200; --stage-art: url(/ui/stage-legendary.webp?v=f8e1ab74); --card-art: url(/ui/card-legendary.webp?v=579393d3); }
.screen--burrow [data-rarity="mythical"]{ --rarity: #ff4fb0; --rarity-deep: #980e5c; --stage-art: url(/ui/stage-mythical.webp?v=2603260d); --card-art: url(/ui/card-mythical.webp?v=18a59e17); }

/*
 * The shell: one row across the top, then rail | showcase | cards. Bigger
 * than the Settings screen's (Gray: "bigger cards"), and it never scrolls
 * up and down: on a phone held sideways the cards scroll sideways instead.
 */
.screen--burrow{ grid-template-rows: auto minmax(0, 1fr); width: min(1280px, 100%); height: min(820px, 100%); }
.burrow__bar{ gap: 10px; padding: 8px 10px 8px 16px; border-bottom: 4px solid rgba(255, 255, 255, 0.14); }
.burrow__bar .screen__title{ margin: 0; flex: none; }
/* "Watch a video: +100 Nuggets" beside the balance, with how many are left (or the wait) under it. */
.burrow__earn{ flex: none; display: inline-grid; justify-items: center; gap: 0; line-height: 1.05; padding-block: 4px; }
.burrow__earn small{ font-size: 0.68rem; font-weight: 700; opacity: 0.9; }
.burrow__earn small:empty{ display: none; }
.burrow__earn:disabled{ cursor: default; }
/* The narrow end: the title, the balance and the offer share one row with the tabs and Close. */
@media (max-width: 700px){ .burrow__bar{ gap: 6px; } .burrow__earn{ font-size: 0.72rem; padding-inline: 8px; } .burrow__earn small{ font-size: 0.6rem; } .burrow__bar .screen__title{ font-size: 1.1rem; } }
/* The front screen's Nuggets pill opens the same offer. */
.nuggetpill.is-offer{ cursor: pointer; }
.nuggetpill.is-offer::after{ content: "+"; margin-left: 2px; font-size: 1.1em; color: #c6f432; }
.burrow__tabs{ display: flex; flex: 1 1 auto; min-width: 0; gap: 6px; margin-right: auto; }
.burrow__tabs .button--tab{ min-height: 54px; border-radius: 16px; }
.burrow__tabs .button--tab.is-selected{ max-width: 280px; }

.burrow{
  position: relative; z-index: 1; min-height: 0;
  display: grid; grid-template-columns: auto clamp(180px, 25vw, 330px) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr); grid-template-areas: "rail show pane"; gap: clamp(8px, 1.3vw, 16px);
  padding: clamp(8px, 1.3vw, 16px) clamp(8px, 1.3vw, 16px) 0;
}
/* Power-ups need the room; eyes and mouths keep the worm on its stage, wearing
   the face being tried on — the reason to have a showcase there at all. */
.burrow[data-tab="upgrades"][data-upgrade="powerups"]{ grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "rail pane"; }
.burrow[data-tab="upgrades"][data-upgrade="powerups"] .showcase{ display: none; }

/* Eyes and mouths reuse the skin card whole; only the art is a picture, not a drawing. */
.facecard img.burrowcard__art{ object-fit: contain; padding: 16% 10% 18%; filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.4)); }
/* The plain card: your own worm's face (facePicker, drawWormAvatar), square, in the middle of the card. */
.facecard .facecard__own{ justify-self: center; align-self: center; width: 62%; height: auto; aspect-ratio: 1 / 1; max-height: 100%; filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.4)); }

/* The rail: square icon buttons down the left, the chosen one ringed in lime. */
.burrow__rails{ grid-area: rail; min-height: 0; }
.burrow__rail{ display: flex; flex-direction: column; gap: 8px; }
.burrow__rail[hidden]{ display: none; }
.burrow__railbtn{ position: relative; width: clamp(42px, 7vh, 56px); height: clamp(42px, 7vh, 56px); border-radius: 14px; --rim: 3px; }
.burrow__railbtn svg{ width: 58%; height: 58%; }
/* A block, so it has no text baseline to sit on, and a size that does not
   depend on the button's shape. As a percentage of the WIDTH it came out 63px
   across a 46px-tall pill (21 Sep); as a percentage of the HEIGHT it had
   nothing to resolve against in a grid cell sized by its content, fell back
   to the tag's own 30px, and sat 2px off the bottom on a phone (Gray, 23 Sep
   2026: "the nugget is not good on smaller viewports"). 28px is the size the
   stroke icons beside it draw at. */
.burrow__railbtn img{ display: block; width: min(58%, 28px); height: auto; aspect-ratio: 1 / 1; object-fit: contain; filter: drop-shadow(0 2px 1px rgba(0, 0, 0, 0.5)); }
.burrow__railbtn[aria-pressed="true"]{ --rimA: #e8ff7a; --rimB: #c6f432; --bodyA: #b9a8ff; --bodyB: #8f78ff; --bodyC: #6a52e6; }

/*
 * The showcase: the whole panel in the rarity colour, the name on a banner,
 * and the worm standing on a lit stage with its button on the stage's front.
 * The stage is the painted pedestal for the rarity (Gray: "generate a real
 * image man, no cheap shit here"); the drawn one under it only shows while
 * the painting has not loaded (burrowScreen.ts adds .has-stage-art).
 */
.showcase{
  grid-area: show; min-height: 0; min-width: 0;
  /* A column, not a grid of numbered rows: a piece that hides (the rarity word
     on a phone, the bar once owned) must not hand the stage's row to another. */
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  margin-bottom: clamp(8px, 1.3vw, 16px); padding: 10px 10px 12px; border-radius: 22px; border: 4px solid #fff;
  background: radial-gradient(ellipse 110% 70% at 50% 28%, color-mix(in srgb, var(--rarity) 88%, #fff), var(--rarity-deep) 88%);
  box-shadow: 0 0 0 2px #1a1150, inset 0 -6px 0 rgba(0, 0, 0, 0.22);
}
.showcase > :not(.showcase__stage){ flex: none; }
.showcase__banner{
  margin: 0; max-width: 100%; padding: 4px 18px 7px; text-align: center; overflow-wrap: anywhere;
  border-radius: 12px; border: 3px solid #fff;
  background: linear-gradient(180deg, color-mix(in srgb, var(--rarity-deep) 70%, #000), var(--rarity-deep));
  box-shadow: 0 0 0 2px #1a1150, 0 4px 0 rgba(0, 0, 0, 0.25);
  font-family: "Lilita One", var(--font-display); font-size: clamp(1.15rem, 2.2vw, 1.8rem); line-height: 1.1; color: #fff;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.45);
}
.showcase__rarity{
  margin: -2px 0 0; font-family: "Lilita One", var(--font-display); font-size: clamp(0.85rem, 1.4vw, 1.05rem);
  letter-spacing: 0.08em; text-transform: uppercase; color: #fff;
  text-shadow: 0 2px 0 var(--rarity-deep), 1px 0 0 var(--rarity-deep), -1px 0 0 var(--rarity-deep);
}
.showcase__stage{
  --s: min(100cqw, 100cqh);
  position: relative; container-type: size; flex: 1 1 0; width: 100%; min-height: 0;
  background: var(--stage-art) center bottom / contain no-repeat;
}
/* The spotlight from above, in the rarity colour. */
.showcase__light{
  position: absolute; left: 50%; top: 0; bottom: calc(var(--s) * 0.16); width: calc(var(--s) * 0.9); margin-left: calc(var(--s) * -0.45);
  pointer-events: none; clip-path: polygon(36% 0, 64% 0, 100% 100%, 0 100%);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0.14) 70%, transparent);
}
/* The drawn stage, until the painted one arrives. */
.showcase__stage::before{
  content: ""; position: absolute; left: 50%; bottom: calc(var(--s) * 0.04);
  width: calc(var(--s) * 0.86); height: calc(var(--s) * 0.3); margin-left: calc(var(--s) * -0.43); border-radius: 50%;
  background: radial-gradient(ellipse at 50% 28%, #fff 0 8%, color-mix(in srgb, var(--rarity) 55%, #fff) 30%, var(--rarity-deep) 62%, #1a1150 100%);
  box-shadow: 0 0 0 4px #fff, 0 0 0 6px var(--rarity), 0 0 30px 8px color-mix(in srgb, var(--rarity) 70%, transparent), 0 10px 0 6px rgba(26, 17, 80, 0.55);
}
.has-stage-art .showcase__stage::before{ display: none; }
/* The worm stands on the pedestal's top: its head is drawn at the canvas's foot. */
.showcase__art{ position: absolute; left: 0; top: 0; z-index: 1; display: block; width: 100%; height: calc(100% - var(--s) * 0.19 - 20px); }
.showcase__action{
  position: absolute; left: 0; right: 0; bottom: calc(var(--s) * 0.02); z-index: 2;
  width: max-content; max-width: 100%; margin-inline: auto; font-size: clamp(1rem, 1.8vw, 1.4rem); padding: 5px 18px 8px;
}
.showcase__action svg{ width: 1.2em; height: 1.2em; flex: none; }
.showcase__action .nugamt__icon{ width: 1.4em; height: 1.4em; }
.showcase__action:disabled{ opacity: 1; filter: none; cursor: default; }
.showcase__route{ margin: 0; max-width: 100%; text-align: center; font-size: clamp(0.72rem, 1.2vw, 0.9rem); line-height: 1.3; color: #fff; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5); }
/*
 * The stage keeps one size whichever worm is focused: the route line and the
 * progress bar keep their room when a worm has none (an owned one), or the
 * stage grew and shrank under it on every pick (Gray, 4 Oct 2026: "the whole
 * preview changes sizes, it looks untidy and glitchy").
 */
.showcase__route{ min-height: 2.6em; }
.showcase__route:empty{ visibility: hidden; }
.showcase__bar{
  position: relative; width: 100%; height: 20px; border-radius: 8px; overflow: hidden;
  background: rgba(20, 14, 60, 0.75); box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.45);
  font-family: "Lilita One", var(--font-display); font-size: 0.8rem; color: #fff; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
}
.showcase__bar[hidden]{ display: block; visibility: hidden; }
.showcase__bar > i{ position: absolute; inset: 0 auto 0 0; background: linear-gradient(180deg, #ffd27a, #ff9f1c 55%, #ff4fad); }
.showcase__bar > span{ position: relative; display: block; text-align: center; line-height: 20px; font-variant-numeric: tabular-nums; }

/*
 * The cards: two rows that scroll sideways, each card sized off the height
 * it is given so a phone held sideways still gets two whole rows, and the
 * next column is cut off at the right edge to say there is more.
 */
.burrow__pane{ grid-area: pane; position: relative; min-width: 0; min-height: 0; container-type: size; }
.burrow__pane[hidden]{ display: none; }
.burrow__grid{
  --gap: clamp(8px, 1.2vw, 14px);
  /* How far each card's price pill hangs below it; the rows leave room for it. */
  --drop: 15px;
  --row: calc((100cqh - var(--gap) - var(--drop) * 2 - 14px) / 2);
  display: grid; grid-auto-flow: column; grid-template-rows: repeat(2, var(--row));
  grid-auto-columns: calc(var(--row) * 0.82); column-gap: var(--gap); row-gap: calc(var(--gap) + var(--drop));
  height: 100%; padding: 2px 4px calc(var(--drop) + 12px); overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity; overscroll-behavior-x: contain;
  scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.4) transparent;
}
.burrow__cell{ position: relative; scroll-snap-align: start; min-height: 0; }
.burrowcard{
  position: relative; display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 0;
  width: 100%; height: 100%; padding: 7px 7px 0; text-align: left; color: #fff;
  border-radius: 18px; border: 3px solid rgba(255, 255, 255, 0.9);
  background:
    var(--card-art) center / cover no-repeat,
    repeating-conic-gradient(from 0deg at 50% 55%, rgba(255, 255, 255, 0.13) 0deg 9deg, transparent 9deg 18deg),
    radial-gradient(circle at 50% 55%, color-mix(in srgb, var(--rarity) 80%, #fff), var(--rarity-deep) 78%);
  box-shadow: 0 0 0 2px #1a1150, inset 0 -5px 0 rgba(0, 0, 0, 0.2);
}
@media (hover: hover){ .burrowcard:hover:not(:disabled){ border-color: #fff; filter: brightness(1.07); } }
.burrowcard:focus-visible{ outline: 3px solid #fff; outline-offset: 3px; }
.burrowcard.is-focused{ border-color: #8dff3c; box-shadow: 0 0 0 3px #8dff3c, 0 0 18px 2px rgba(141, 255, 60, 0.65); }
.burrowcard .station__name{
  position: relative; z-index: 1; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: "Lilita One", var(--font-display); font-weight: 400; font-size: clamp(0.82rem, 1.5vw, 1.15rem); color: #fff;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.5), 1px 0 0 rgba(0, 0, 0, 0.35), -1px 0 0 rgba(0, 0, 0, 0.35);
}
.burrowcard__art{ display: block; width: 100%; height: 100%; min-height: 0; }
/* The chunky pill on the card's foot: the price, a video, how it is earned, or yours. */
.burrowcard__tag{
  position: absolute; left: 0; right: 0; bottom: calc(var(--drop) * -1); z-index: 1;
  width: max-content; max-width: calc(100% + 6px); margin-inline: auto; gap: 4px;
  padding: 3px 12px 6px; font-size: clamp(0.78rem, 1.3vw, 1.05rem); white-space: nowrap; pointer-events: none;
}
.burrowcard__tag svg{ width: 1.1em; height: 1.1em; flex: none; }
/* What you're wearing: a green tick in the corner. */
.burrowcard__worn{
  position: absolute; top: -8px; right: -8px; z-index: 2; display: grid; place-items: center;
  width: 28px; height: 28px; border-radius: 50%; border: 3px solid #fff;
  background: linear-gradient(180deg, #9dff5c, #3fbf1a); box-shadow: 0 0 0 2px #1a5a0c;
  font-family: "Lilita One", var(--font-display); font-size: 0.95rem; color: #fff; text-shadow: 0 1px 0 #1a5a0c;
}
/* Low on the card's right, over the body rather than the name or the face. */
.burrowcard__heart{ position: absolute; right: 6px; bottom: calc(var(--drop) + 8px); z-index: 2; width: 32px; height: 32px; border-radius: 11px; --rim: 3px; }
.burrowcard__heart svg{ width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linejoin: round; }
.burrowcard__heart[aria-pressed="true"]{ --bodyA: #ff9ddf; --bodyB: #ff4fad; --bodyC: #d11f7c; --ts: #7a0f48; }
.burrowcard__heart[aria-pressed="true"] svg{ fill: #fff; }
.burrow__empty{ position: absolute; inset: 30% 10% auto; text-align: center; font-size: 1rem; color: #fff; }
/* A creature's face panes: why there is nothing to wear, and the way to a worm that can. */
.facelock{ display: grid; justify-items: center; gap: 12px; }
/* The Glow Beads food card: the original glowing pellets, drawn here as they are in the arena. */
.station__swatch--glow{
  background:
    radial-gradient(circle at 28% 34%, #6ee7ff 0 7%, rgba(110, 231, 255, 0.45) 11%, transparent 20%),
    radial-gradient(circle at 66% 26%, #ffd166 0 6%, rgba(255, 209, 102, 0.45) 10%, transparent 18%),
    radial-gradient(circle at 52% 60%, #ff6b9d 0 8%, rgba(255, 107, 157, 0.45) 12%, transparent 22%),
    radial-gradient(circle at 24% 74%, #9dff6e 0 6%, rgba(157, 255, 110, 0.45) 10%, transparent 18%),
    radial-gradient(circle at 78% 72%, #c084fc 0 7%, rgba(192, 132, 252, 0.45) 11%, transparent 20%),
    #140f34;
}
.facelock[hidden]{ display: none; }
.facelock .screen__note{ color: #fff; font-size: 1rem; }
.burrow__count{
  position: absolute; right: 8px; bottom: 6px; margin: 0; pointer-events: none;
  font-family: "Lilita One", var(--font-display); font-size: 0.95rem; color: #cbbcff;
}

/*
 * Upgrades: Worms Zone's power-up cards. The potion, its name, a level bar
 * up the card's left, how long it runs now, and a blue + with the next
 * level's price; carried charges under it. Each rail entry on this tab is
 * its own pane ([data-burrow-upgrade-pane]), so eyes and mouths slot in.
 */
.burrow__pane--upgrades{ overflow-x: hidden; overflow-y: auto; padding-bottom: 12px; scrollbar-width: thin; }
.burrow__upgrade{ display: grid; gap: 12px; align-content: start; }
.burrow__upgrade[hidden]{ display: none; }
/*
 * Eyes and mouths are the SKIN cards, so they lay out like the shop: two rows
 * sized to the pane, scrolling sideways. They were measured against nothing —
 * the pane was not a size container, so the grid's 100cqh fell back to the
 * whole window — and the cards came out a window tall, the second row cut off
 * below the pane, with a scrollbar that scrolled a pane of power-ups that was
 * not there. Gray, 23 Sep 2026: "theres no need for cards to be that big as it
 * creates scroll down that dont work".
 */
@media (orientation: landscape), (min-width: 901px){
  .burrow__upgrade:is([data-burrow-upgrade-pane="eyes"], [data-burrow-upgrade-pane="mouth"]){
    container-type: size; height: 100%; grid-template-rows: minmax(0, 1fr) auto; gap: 6px;
  }
  .burrow__upgrade:is([data-burrow-upgrade-pane="eyes"], [data-burrow-upgrade-pane="mouth"]) .burrow__grid{
    --row: calc((100cqh - var(--gap) - var(--drop) * 2 - 14px - var(--note, 0px)) / 2);
  }
  .burrow__upgrade:has(> .screen__note:not([hidden])){ --note: 2.2em; }
  .burrow__pane--upgrades:has(> .burrow__upgrade:is([data-burrow-upgrade-pane="eyes"], [data-burrow-upgrade-pane="mouth"]):not([hidden])){ overflow: hidden; padding-bottom: 0; }
}
.burrow__powerups{ display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr)); gap: 12px; }
.powercard{
  --pu-color: #7ad6ff;
  position: relative; display: grid; grid-template-columns: 14px 56px minmax(0, 1fr); grid-template-rows: auto auto auto;
  grid-template-areas: "lvl icon head" "lvl effect effect" "lvl buy buy";
  column-gap: 10px; row-gap: 6px; padding: 10px 12px 12px 10px; border-radius: 18px; border: 3px solid #fff; color: #fff;
  background:
    repeating-conic-gradient(from 0deg at 30% 20%, rgba(255, 255, 255, 0.07) 0deg 9deg, transparent 9deg 18deg),
    radial-gradient(circle at 30% 20%, color-mix(in srgb, var(--pu-color) 45%, #4a3cb4), #2a2070 80%);
  box-shadow: 0 0 0 2px #1a1150, inset 0 -5px 0 rgba(0, 0, 0, 0.25);
}
.powercard__levels{
  grid-area: lvl; display: flex; flex-direction: column-reverse; gap: 3px; padding: 3px;
  border-radius: 8px; background: rgba(12, 8, 36, 0.6); box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.4);
}
.powercard__levels i{ flex: 1; min-height: 5px; border-radius: 3px; background: rgba(255, 255, 255, 0.12); }
.powercard__levels i.is-on{ background: linear-gradient(180deg, #e8ff7a, #8dd42c); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5); }
.powercard__icon{ grid-area: icon; width: 56px; height: 56px; border-radius: 50%; object-fit: cover; background: var(--pu-color); box-shadow: 0 0 0 3px #fff, 0 4px 0 rgba(0, 0, 0, 0.3); }
.powercard__head{ grid-area: head; display: grid; gap: 4px; align-content: center; justify-items: start; min-width: 0; }
.powercard__name{
  margin: 0; font-family: "Lilita One", var(--font-display); font-weight: 400; font-size: 1.15rem;
  color: #fff; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.45);
}
.powercard__duration{
  padding: 1px 10px 3px; border-radius: 999px; border: 2px solid #fff; background: var(--pu-color);
  font-family: "Lilita One", var(--font-display); font-size: 0.95rem; color: #fff; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
}
.powercard__gain{ font-family: "Lilita One", var(--font-display); font-size: 0.85rem; color: #e8ff7a; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5); }
.powercard__effect{ grid-area: effect; margin: 0; font-size: 0.8rem; line-height: 1.3; color: rgba(255, 255, 255, 0.85); }
.powercard__perlevel{ display: block; margin-top: 3px; font-weight: 700; color: #e8ff7a; }
.powercard__buy{ grid-area: buy; display: grid; gap: 6px; }
.powercard__row{ display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.powercard__price{ font-family: "Lilita One", var(--font-display); font-size: 1.1rem; color: #fff; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.45); }
.powercard__row .button--step{ width: 40px; height: 40px; }
.powercard__charges{ font-size: 0.82rem; font-weight: 700; white-space: nowrap; color: rgba(255, 255, 255, 0.85); }

/* The narrow end of landscape (740px phones): the tabs lose their words, the showcase slims. */
@media (max-width: 900px){
  .burrow__bar .screen__title{ font-size: 1.35rem; }
  .burrow__tabs .button--tab.is-selected .button__label{ display: none; }
  .burrow__tabs .button--tab, .burrow__tabs .button--tab.is-selected{ flex: 0 0 auto; width: 64px; min-width: 0; min-height: 46px; padding: 4px 6px 6px; }
  .burrow__tabs .button--tab.is-selected{ --rimA: #e8ff7a; --rimB: #c6f432; }
  .burrow__tabs .button--tab svg, .burrow__tabs .button--tab img{ width: 30px; height: 30px; }
  .burrow{ grid-template-columns: auto clamp(170px, 27vw, 240px) minmax(0, 1fr); }
}
@media (orientation: landscape) and (max-height: 520px){
  .overlay--screen[data-game-burrow]{ padding: 6px; }
  .burrow__bar{ padding-block: 5px; }
  .showcase__route{ font-size: 0.7rem; display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden; }
  .burrow__bar .iconbtn--close{ width: 44px; height: 44px; }
  .burrow__bar .nuggetpill{ font-size: 1.05rem; }
  .burrow__bar .nuggetpill img{ width: 24px; height: 24px; }
  .burrow__tabs .button--tab{ min-height: 44px; }
  .showcase{ gap: 3px; padding: 6px 8px 8px; border-width: 3px; }
  .showcase__banner{ padding: 2px 12px 4px; }
  .showcase__action{ font-size: 0.95rem; padding: 3px 12px 6px; --rim: 3px; }
  .burrow__grid{ --drop: 11px; }
  .burrowcard{ padding: 5px 5px 0; border-radius: 14px; }
  .burrowcard__tag{ font-size: 0.74rem; padding: 1px 8px 4px; }
  .burrowcard__heart{ width: 26px; height: 26px; border-radius: 9px; right: 4px; }
  .burrowcard__heart svg{ width: 15px; height: 15px; }
  .powercard{ grid-template-columns: 12px 44px minmax(0, 1fr); }
  .powercard__icon{ width: 44px; height: 44px; }
}
/* A phone held sideways: the banner's colour already says the rarity, so its word gives the worm the room. */
@media (orientation: landscape) and (max-height: 420px){
  .showcase__rarity{ display: none; }
}

/*
 * UPRIGHT: the Burrow's own layout, not the landscape one squeezed. Gray, 19 Sep
 * 2026, on his iPhone: "vertical for the burrow ain't gonna work with this
 * design". It did not: the sideways rail | showcase | cards row gave the
 * showcase a full-height column and left one column of cards beside it, and the
 * tabs ran into the wallet. Held upright the screen is a sheet, top to bottom:
 *
 *   The Burrow                    wallet   close
 *   [ shop ][ my worms ][ upgrades ]                 tabs, one full row
 *   [ worm on its stage ] name / rarity / progress   a SHORT band, not a column
 *   [ filter chips, one row ]
 *   [ cards, 2-4 across, scrolling UP AND DOWN ]     the only thing that scrolls
 *
 * The cards go the way a thumb scrolls a phone; only the landscape screen
 * scrolls them sideways (Worms Zone does the same, its wardrobe being
 * landscape only). Keyed on orientation and width so an upright tablet and a
 * narrow desktop window get it too.
 */
@media (orientation: portrait) and (max-width: 900px){
  .overlay--screen[data-game-burrow]{
    padding: calc(6px + var(--safe-t)) calc(6px + var(--safe-r)) calc(6px + var(--safe-b)) calc(6px + var(--safe-l));
  }
  .screen--burrow{ width: 100%; height: 100%; border-width: 3px; border-radius: 22px; }
  .burrow__bar{
    display: grid; grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "title wallet close" "tabs tabs tabs";
    align-items: center; gap: 6px 8px; padding: 8px 8px 0 14px; border-bottom-width: 3px;
  }
  .burrow__bar .screen__title{ grid-area: title; font-size: 1.55rem; }
  .burrow__bar .nuggetpill{ grid-area: wallet; font-size: 1.1rem; }
  .burrow__bar .nuggetpill img{ width: 26px; height: 26px; }
  .burrow__bar .iconbtn--close{ grid-area: close; width: 44px; height: 44px; }
  .burrow__bar .burrow__tabs{ grid-area: tabs; margin: 0; padding-bottom: 8px; gap: 6px; }
  /* Three tabs across the full width; the chosen one is wider and says its name. */
  .burrow__bar .burrow__tabs .button--tab{ flex: 1 1 0; width: auto; min-width: 0; max-width: none; min-height: 48px; padding: 4px 6px 6px; font-size: 1rem; }
  .burrow__bar .burrow__tabs .button--tab.is-selected{ flex-grow: 2.3; max-width: none; }
  .burrow__bar .burrow__tabs .button--tab.is-selected .button__label{ display: inline; white-space: nowrap; }
  .burrow__bar .burrow__tabs .button--tab svg, .burrow__bar .burrow__tabs .button--tab img{ width: 28px; height: 28px; }

  .burrow, .burrow[data-tab="upgrades"][data-upgrade="powerups"]{
    grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr);
    grid-template-areas: "show" "rail" "pane"; gap: 8px; padding: 8px 8px 0;
  }
  /* Power-ups have no worm to show: the chips and the cards. */
  .burrow[data-tab="upgrades"][data-upgrade="powerups"]{ grid-template-rows: auto minmax(0, 1fr); grid-template-areas: "rail" "pane"; }

  /* The filter chips: one row of equal pills, so every one is a big target. */
  .burrow__rail{ display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 96px); justify-content: center; gap: 8px; }
  .burrow__rail[hidden]{ display: none; }
  .burrow__railbtn{ width: 100%; height: 46px; border-radius: 14px; }

  /* The showcase: a short band. The worm and its stage on the left, its name,
     rarity and progress on the right, and the button on the stage's front. */
  .showcase{
    display: grid; grid-template-columns: minmax(0, 0.95fr) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) repeat(5, auto) minmax(0, 1fr); align-items: center; justify-items: stretch;
    column-gap: 8px; row-gap: 4px; height: clamp(168px, 27dvh, 260px); margin-bottom: 0; padding: 8px; border-radius: 18px; border-width: 3px;
  }
  .showcase__stage{ grid-column: 1; grid-row: 1 / -1; align-self: stretch; height: 100%; }
  .showcase__banner{ grid-column: 2; grid-row: 2; padding: 3px 10px 5px; font-size: clamp(1.05rem, 5vw, 1.45rem); border-radius: 12px; }
  .showcase__rarity{ grid-column: 2; grid-row: 3; margin: 0; text-align: center; font-size: 0.9rem; }
  .showcase__bar{ grid-column: 2; grid-row: 4; }
  .showcase__route{ grid-column: 2; grid-row: 5; font-size: 0.78rem; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
  .showcase > [data-burrow-get-nuggets]{ grid-column: 2; grid-row: 6; justify-self: center; min-height: 44px; }
  .showcase__action{ min-height: 44px; font-size: 1.05rem; padding: 4px 14px 7px; }

  /* The cards: as many across as fit, and the pane scrolls up and down. */
  .burrow__grid{
    --gap: 12px; --drop: 15px;
    grid-auto-flow: row; grid-template-rows: none; grid-auto-rows: auto; grid-auto-columns: auto;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 128px), 1fr));
    column-gap: var(--gap); row-gap: calc(var(--gap) + var(--drop)); align-content: start;
    padding: 6px 6px calc(var(--drop) + 28px); overflow-x: hidden; overflow-y: auto;
    scroll-snap-type: none; overscroll-behavior-y: contain;
  }
  /* min-height:auto, not the sideways layout's 0: with 0 an auto row sizes to nothing and every card lands on the last. */
  .burrow__cell{ aspect-ratio: 0.82; min-height: auto; scroll-snap-align: none; }
  .burrowcard__heart{ width: 40px; height: 40px; }
  .burrow__grid{ padding-inline: 10px; }
  /* The count sat on the last card's corner; the grid is the whole story upright. */
  .burrow__count{ display: none; }
  .powercard__buy .button{ min-height: 44px; }
  /* Upgrades scrolls as a pane; the eyes and mouths grids inside it just grow. */
  .burrow__pane--upgrades .burrow__grid{ height: auto; overflow: visible; }
  .burrow__powerups{ grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
  .powercard__row .button--step{ width: 44px; height: 44px; }
}
/* A short upright phone (360x640): the title row goes, so the tabs take its place beside the wallet and close. */
@media (orientation: portrait) and (max-width: 900px) and (max-height: 700px){
  .burrow__bar{ grid-template-areas: "tabs wallet close"; padding: 6px 8px 6px 8px; gap: 6px; }
  .burrow__bar .screen__title{ position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .burrow__bar .burrow__tabs{ padding-bottom: 0; }
  .burrow__bar .burrow__tabs .button--tab.is-selected{ flex-grow: 1; }
  .burrow__bar .burrow__tabs .button--tab.is-selected .button__label{ display: none; }
  .showcase{ height: clamp(140px, 24dvh, 170px); }
}


/*
 * The in-run row (hud.ts updateCharges): small tappable versions of the
 * active-power-up ring above it, each with a number-key hint. Clean-play-area
 * style throughout — a ring in the kind's own colour, never a box.
 */
.chargebar{
  position: absolute; left: 50%; bottom: calc(68px + var(--safe-b)); transform: translateX(-50%);
  display: flex; align-items: flex-end; gap: 8px; z-index: 5;
}
.chargebar[hidden]{ display: none; }
.chargebar__item{
  --pu-color: #7ad6ff;
  appearance: none; -webkit-appearance: none; border: 0; margin: 0; padding: 0; cursor: pointer;
  position: relative; width: 58px; height: 58px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: none;
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.55));
  transition: transform 0.12s ease;
}
/* The active row's ring, full: this one is waiting, not running. */
.chargebar__item::before{
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: rgba(11, 16, 32, 0.55); box-shadow: inset 0 0 0 4px var(--pu-color);
}
@media (hover: hover){ .chargebar__item:hover{ transform: scale(1.08); } }
.chargebar__item:focus-visible{ transform: scale(1.08); outline: 3px solid #fff; outline-offset: 2px; }
.chargebar__item:active{ transform: scale(0.94); }
.chargebar__art{ position: relative; width: calc(100% - 12px); height: calc(100% - 12px); object-fit: contain; }
/* How many: a small gold tag on the orb's foot, in the game's lettering. */
.chargebar__count{
  position: absolute; left: 50%; bottom: -9px; translate: -50% 0; z-index: 1;
  padding: 1px 7px 2px; border-radius: 999px; border: 2px solid #fff;
  background: linear-gradient(180deg, #ffe45c, #ff9f1c); box-shadow: 0 2px 0 #8a4a00;
  font-family: "Lilita One", var(--font-display); font-size: 0.8rem; line-height: 1.1; color: #fff;
  text-shadow: 0 1px 0 #7a3d00, 1px 0 0 #7a3d00, -1px 0 0 #7a3d00;
}
/* The once-only "what are these" bubble (hud.ts showChargeHint). */
.chargebar__hint{
  position: absolute; left: 50%; bottom: calc(100% + 16px); translate: -50% 0; white-space: nowrap;
  padding: 7px 14px 8px; border-radius: 14px; background: #fff; color: #1b1550;
  font-family: "Lilita One", var(--font-display); font-size: 1rem; box-shadow: 0 6px 14px rgba(0, 0, 0, 0.45);
  animation: tile-pulse 1.2s ease-in-out infinite; pointer-events: none;
}
.chargebar__hint::after{ content: ""; position: absolute; left: 50%; top: 100%; translate: -50% 0; border: 8px solid transparent; border-top-color: #fff; }
/*
 * On a touch screen the carried power-ups sit BESIDE Boost, on its inner side,
 * along the same bottom line: the thumb that boosts reaches them without
 * leaving the corner. In the middle of the bottom edge they were in the play
 * area and under neither thumb (Gray, 3 Oct 2026: "powerup charges are almost
 * in the middle of the screen on all mobile devices"). More than fit wrap
 * upwards, never across into the other corner's sound and exit buttons.
 */
.game:has(.boostbutton:not([hidden])) .chargebar{
  left: auto; transform: none;
  right: calc(var(--boost-inset) + var(--safe-r) + var(--boost-size) + 10px);
  bottom: calc(var(--boost-inset) + var(--safe-b));
  flex-wrap: wrap-reverse; justify-content: flex-end;
  max-width: calc(100vw - 2 * (var(--boost-size) + var(--boost-inset)) - 150px);
}
.game.is-boost-left:has(.boostbutton:not([hidden])) .chargebar{
  right: auto; justify-content: flex-start;
  left: calc(var(--boost-inset) + var(--safe-l) + var(--boost-size) + 10px);
}
/* Not overlay-keyed CSS any more — hud.ts's `blocked` field owns this now. See styles.css's .powerupbar note. */

/*
 * Settings' windowed tabs on a phone on its side stay side by side: the
 * 760px rule stacks the window under the controls, which is right for a
 * phone held upright and left the controls a 91px strip at 667x320 (27 Sep
 * 2026, Gray: "landscape mobile also many things cut off or hard to choose").
 */
@media (orientation: landscape) and (max-height: 500px){
  .screen__pane--windowed{ grid-template-columns: minmax(230px, 0.9fr) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); gap: 10px; }
  .screen__window{ min-height: 0; }
  .screen__body{ padding-inline: 10px; }
}


/* ======================================================= THE FRONT SCREEN
 *
 * Gray, 11 Oct 2026: "our layout is never correct when rotating device ... its
 * too cramped, way too much shit on screen, logo on mobile takes way too much
 * space. add login button, do redesign." The design he approved, Worms Zone's
 * arrangement:
 *
 *   top left      your picture, level, and Log in (or your name)
 *   top centre    a small logo: at most 15% of the height sideways, 12% upright, 20% on a desktop
 *   top right     Nuggets, What's new, music
 *   middle        name, one big Play (the biggest thing on the screen), Play with friends
 *   right edge    the time-limited tiles that are on: event, daily gift, prize wheel
 *   bottom        The Burrow left; Settings and More right
 *
 * Upright, the same zones stack: the account bar, the logo, the worm and floor
 * pickers, name and Play in the bottom third, and one row of four big buttons
 * (The Burrow, Events, Top players, More).
 *
 * WHY IT IS BUILT THIS WAY. Every zone is a named grid area, like the HUD's
 * slots, and which arrangement applies is decided by ORIENTATION and HEIGHT in
 * media queries the browser evaluates itself, never by a width breakpoint and
 * never by a height JavaScript measured. A phone held sideways is 844-932px
 * wide, which used to land it in the "<900px is a phone upright" rules: that
 * was the cramped, cut-off screen, and nothing re-ran until the phone was
 * turned. Now the same rotation is a different media query matching, at once.
 */
.menu{
  /* The whole width, stated: .overlay centres its children, and a centred block
     shrinks to its content (Gray, 15 Sep 2026: "why is homepage boxed in"). */
  width: 100%;
  height: 100%;
  /* The drop-downs (More, Events) are placed against the menu itself, inside its padding. */
  position: relative;
  --pad-t: calc(clamp(8px, 2vh, 18px) + var(--safe-t));
  --pad-r: calc(clamp(12px, 2.4vw, 30px) + var(--safe-r));
  --pad-b: calc(clamp(8px, 2vh, 16px) + var(--safe-b));
  --pad-l: calc(clamp(12px, 2.4vw, 30px) + var(--safe-l));
  --top-h: 58px;
  --col: min(560px, 42vw);
  display: grid;
  /*
   * Sideways and on a desktop (Gray, round 3): the round buttons along the top,
   * More the last of them; your worm, your floor and The Burrow down the left;
   * name and Play in the middle; the timed tiles down the right. Both side
   * columns are the whole height between the top row and the bottom inset.
   * `minmax(0, 1fr)`, never a bare 1fr: a bare 1fr cannot shrink below its
   * content, and on a short screen it pushed the bottom row off the glass.
   */
  grid-template-columns: minmax(0, 1fr) minmax(0, var(--col)) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto minmax(0, 1fr) auto;
  grid-template-areas:
    "top   top   top"
    "left  .     side"
    "left  core  side"
    "left  .     side"
    "foot  foot  foot";
  column-gap: clamp(10px, 1.6vw, 18px); row-gap: clamp(6px, 1.4vh, 14px);
  /* Belt and braces: nothing should reach the edge, but a row that grows must
     clip rather than push the bottom row off a screen that cannot scroll. */
  overflow: hidden;
  padding:
    calc(clamp(8px, 2vh, 18px) + var(--safe-t)) calc(clamp(12px, 2.4vw, 30px) + var(--safe-r))
    calc(clamp(8px, 2vh, 16px) + var(--safe-b)) calc(clamp(12px, 2.4vw, 30px) + var(--safe-l));
  --mark-h: min(18vh, 170px);
  --t: clamp(62px, 11vh, 96px);
}
/* Rows may be shorter than their contents, columns narrower than theirs. */
.menu > *{ min-width: 0; min-height: 0; }

/* The top row is a row of its own, so the round buttons on the right take the width they need and the logo stays as near the middle as they allow. */
.menu__top{
  grid-area: top; display: grid; align-items: start; column-gap: clamp(8px, 1.6vw, 18px);
  grid-template-columns: minmax(max-content, 1fr) auto minmax(max-content, 1fr); grid-template-areas: "me brand nav";
}
.menu__me{ grid-area: me; align-self: start; justify-self: start; display: flex; align-items: center; gap: 10px; }
.menu__brand{ grid-area: brand; align-self: start; justify-self: center; display: flex; flex-direction: column; align-items: center; max-width: 100%; }
.menu__nav{ grid-area: nav; align-self: start; justify-self: end; display: flex; align-items: center; gap: 10px; }
.menu__left{ grid-area: left; container-type: size; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; gap: clamp(6px, 2.4cqh, 14px); }
.menu > .menu__core{ grid-area: core; width: 100%; justify-self: center; gap: clamp(8px, 1.6vh, 16px); }
.menu__side{ grid-area: side; container-type: size; display: flex; flex-direction: column; justify-content: center; align-items: flex-end; gap: clamp(10px, 4cqh, 18px); }
/* The bar is not a box: each of its pieces takes a zone of the menu by name. */
.menu__bar{ display: contents; }
.menu__events{ grid-area: events; }
.menu__tile--board{ grid-area: board; }
.menu__tile--burrow{ grid-area: burrow; }
.menu__foot{ grid-area: foot; }

/* Top left: the Log in button and, once signed in, your name (ui/signInPanel.ts). */
.menu__login{ flex: none; white-space: nowrap; }
/*
 * Log in sells itself (Gray, 11 Oct 2026: "needs to be more visible on home and
 * exciting, maybe login button should pulsate and have a red dot"): it breathes,
 * wears the red dot every other "something for you" button has, and a gold pill
 * says what signing in pays (SIGNIN_BONUS).
 */
.menu__login{ position: relative; overflow: visible; animation: login-beat 1.6s ease-in-out infinite; }
.menu__login .iconbtn__dot{ top: -6px; right: -6px; }
.menu__loginbonus{
  position: absolute; left: 50%; bottom: -13px; translate: -50% 0; white-space: nowrap; pointer-events: none;
  padding: 1px 8px 2px; border-radius: 999px; border: 2px solid #fff; line-height: 1.1;
  font-family: "Lilita One", var(--font-display); font-size: 0.78rem; color: #fff; text-shadow: 0 1px 0 #8a4a00;
  background: linear-gradient(180deg, #ffe45c, #ff9a00); box-shadow: 0 2px 0 rgba(20, 8, 60, 0.45);
}
@keyframes login-beat{ 0%, 100%{ scale: 1; filter: none; } 50%{ scale: 1.07; filter: brightness(1.12) drop-shadow(0 0 10px rgba(120, 200, 255, 0.8)); } }
@media (prefers-reduced-motion: reduce){ .menu__login{ animation: none; } }
.menu__login[hidden], .menu__who[hidden]{ display: none; }
/* A narrow phone upright (360px) in German: the login's dot stayed clear of the Nuggets pill beside it. */
@media (max-width: 399px){ .menu__login .iconbtn__dot{ right: -2px; } .menu__login{ padding-inline: 8px; font-size: 0.85rem; } .nuggetpill{ padding: 4px 10px 4px 5px; gap: 6px; font-size: 1.12rem; } .nuggetpill.is-offer::after{ margin-left: 0; } }
.menu__who{
  /* Its own width, up to the cap: a four-letter name was cut to "Da…" while the
     level bar kept its room (Gray, 12 Oct 2026). The bar is what gives. */
  flex: none; min-width: 0; max-width: 22vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: "Lilita One", var(--font-display); font-size: 1.15rem; color: #fff;
  text-shadow: -1px -1px 0 #1c0d3a, 1px -1px 0 #1c0d3a, -1px 1px 0 #1c0d3a, 1px 1px 0 #1c0d3a, 0 2px 3px rgba(0, 0, 0, 0.45);
}
.menu__brand > .menu__eyebrow{ margin-top: -2px; }
/* The level bar is the piece of the top left that gives, so Log in never reaches the logo. */
.menu__avatar .menu__levelbar{ width: clamp(56px, 7vw, 120px); }

/* The middle column: name over Play, one edge, and Play the biggest thing on the screen. */
.menu__start{ display: flex; flex-direction: column; align-items: stretch; gap: clamp(8px, 1.4vh, 14px); width: 100%; }
.menu__start > .menu__play{ min-width: 0; width: 100%; padding: 10px 28px 14px; font-size: clamp(2rem, 5.2vh, 2.9rem); }
.menu .menu__friends{ width: min(340px, 86%); }
.menu__alert{ align-self: center; }

/*
 * The tiles on this screen are the compact Worms Zone tile: picture inside,
 * name under it, any countdown pill hanging off the top edge. Sized by --t,
 * which each place sets from the HEIGHT it has.
 */
.menu .menu__tile{
  width: calc(var(--t) * 1.18); height: calc(var(--t) * 0.94); min-height: 0; margin: 0;
  padding: 4px 3px 6px; justify-content: center; gap: 2px; flex: none;
  border-radius: calc(var(--t) * 0.22); --rim: 3px;
  font-size: max(0.6rem, calc(var(--t) * 0.15)); line-height: 1.02;
}
.menu .menu__tile img{ width: calc(var(--t) * 0.5); height: calc(var(--t) * 0.5); margin: 0 auto; }
@media (hover: hover){ .menu .menu__tile:hover img{ transform: scale(1.08); } }
.menu .menu__tile:has(.tile__badge:not(:empty)){ padding-bottom: 6px; }
.menu .menu__tile .tile__badge{
  top: -10px; bottom: auto; min-width: 0; padding: 1px 7px 2px; border-width: 2px;
  font-size: max(0.56rem, calc(var(--t) * 0.13));
}
.menu .menu__tile .iconbtn__dot{ top: -6px; right: -6px; width: 16px; height: 16px; }
.menu__tileicon{ display: grid; place-items: center; width: calc(var(--t) * 0.5); height: calc(var(--t) * 0.5); }
.menu__tileicon .icon{ width: 74%; height: 74%; stroke-width: 2.8; filter: drop-shadow(0 2px 0 var(--ts)); }
/* A <summary> as a tile: no disclosure triangle. */
.menu :is(.menu__menu, .menu__events) > summary{ list-style: none; cursor: pointer; }
.menu :is(.menu__menu, .menu__events) > summary::-webkit-details-marker{ display: none; }
.menu :is(.menu__menu, .menu__events)[open] > summary{ filter: brightness(0.92) saturate(1.1); }

/*
 * The right edge: three timed tiles always fit, from the column's own height
 * (each tile 0.94 of --t tall, its pill 10px over the top, two gaps).
 */
.menu__side .menu__tile{ --t: clamp(34px, calc((100cqh - 8cqh - 16px) / 2.9), 104px); }
.menu__bar > *{ min-width: 0; min-height: 0; }
/* The left column: your worm, your floor, The Burrow, each a share of the column's height. */
@media (orientation: landscape){
.menu__left .pickrow{ flex-direction: column; justify-content: center; width: min(100%, 250px); gap: clamp(6px, 2.4cqh, 14px); margin: 0; }
.menu__left .pickcard{
  flex: none; padding: 5px 3px 4px; row-gap: 3px; column-gap: 4px;
  grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "prev art next" "name name name"; justify-items: center;
}
.menu__left .pickcard__art{ width: min(100%, calc((36cqh - 34px) / 0.577), 190px); }
.menu__left .pickcard__art--floor{ width: auto; height: clamp(24px, calc(20cqh - 34px), 90px); margin: 0; }
.menu__left .pickcard__label{ text-align: center; padding-inline: 4px; }
.menu__left .pickcard__kind, .menu__left .pickcard__hint{ display: none; }
.menu__left .pickcard__name{ font-size: clamp(0.8rem, 4.2cqh, 1.1rem); line-height: 1.05; white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }
.menu__left .pickcard__arrow{ width: 26px; height: clamp(28px, 10cqh, 42px); }
.menu__left .menu__tile--burrow{ --t: clamp(34px, 22cqh, 96px); }
/* The Burrow as wide as the cards above it, its icon beside a one-line name: as a
   small square its name wrapped onto two lines (Gray, 11 Oct 2026). */
.menu__left .menu__tile--burrow{
  width: min(100%, 250px); height: clamp(40px, 17cqh, 72px); flex-direction: row; gap: 6px;
  padding: 4px 8px 6px; font-size: clamp(0.74rem, 4.4cqh, 1.2rem); white-space: nowrap;
}
.menu__left .menu__tile--burrow img{ width: clamp(28px, 12cqh, 52px); height: clamp(28px, 12cqh, 52px); margin: 0; }
}

/* Sideways and on a desktop: More is the last round button top right, in the bell's style. */
.menu .menu__menu > .menu__tile{
  width: 46px; height: 46px; padding: 0; border-radius: 15px; --rim: 4px; animation: none;
  background:
    linear-gradient(180deg, var(--bodyA) 0%, var(--bodyB) 52%, var(--bodyC) 100%) padding-box,
    linear-gradient(180deg, var(--rimA), var(--rimB)) border-box;
}
.menu .menu__menu > .menu__tile .button__label{ display: none; }
.menu .menu__menu > .menu__tile .menu__tileicon{ width: 24px; height: 24px; }
/* More's rows are a list to scan, tighter than the site menu's on a content page. */
.menu__menupanel .navmenu__sound, :where(.menu__menupanel) :where(a){ padding: 10px 4px; font-size: 1rem; }
.menu__events, .menu__tile--board, .menu__portraitonly, .menu__moredot{ display: none; }

/*
 * The drop-downs hang off the MENU, inside its padding: More opens down from
 * the top right, never past the bottom inset; upright both open up from the
 * bottom row, never past the account bar. Their heights are a share of the
 * menu's own box, so no measured or guessed viewport height is involved (an
 * upright iPhone in Safari ran the sheet off the top: Gray, round 3).
 */
.menu .navmenu:is(.menu__menu, .menu__events){ position: static; }
.menu :is(.menu__menu, .menu__events) > .navmenu__panel{
  top: calc(var(--pad-t) + var(--top-h)); bottom: auto; right: var(--pad-r); left: auto;
  width: min(340px, calc(100% - var(--pad-l) - var(--pad-r)));
  max-height: calc(100% - var(--pad-t) - var(--pad-b) - var(--top-h)); overflow-y: auto; overscroll-behavior: contain;
}
.menu__eventspanel{
  display: flex; flex-wrap: wrap; justify-content: center; gap: 18px 12px; padding: 20px 16px 14px;
}
.menu__eventspanel .menu__tile{ --t: 76px; }
/* Events shows only while one of its tiles is on, and carries their red dot. */
.menu__events:not(:has(.menu__eventspanel > .button--tile:not([hidden]))){ display: none !important; }
.menu__events > summary{ position: relative; }
.menu__events:has(.menu__eventspanel .iconbtn__dot:not([hidden])) > summary::after{
  content: ""; position: absolute; top: -6px; right: -6px; width: 16px; height: 16px; border-radius: 50%;
  background: #ff3b5c; border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
.menu__menu > summary{ position: relative; }
.menu__moredot[hidden]{ display: none !important; }
/* Inside More, the row the red dot on More is for (Gray: "should also show what that alert is for"). */
.navmenu__dot{ position: static; display: inline-block; vertical-align: middle; margin-left: 8px; width: 12px; height: 12px; }
.navmenu__dot[hidden]{ display: none; }
.menu__menupanel [hidden]{ display: none; }

/* --------------------------------------------- a phone held sideways */
/*
 * Keyed on orientation and HEIGHT, at any width: an iPhone sideways is 667 to
 * 932px wide and 320 to 430px tall (717x329 in Chrome, with its tab strip).
 * The same zones as a desktop, smaller.
 */
@media (orientation: landscape) and (max-height: 500px) {
  .beta{ display: none; }
  .menu{
    --col: min(380px, 40vw);
    /* 15% of the height at most, with room for the logo's jelly squash. */
    --mark-h: 13.5vh;
    --top-h: 46px;
    --pad-t: calc(6px + var(--safe-t)); --pad-r: calc(12px + var(--safe-r)); --pad-b: calc(6px + var(--safe-b)); --pad-l: calc(12px + var(--safe-l));
    column-gap: 10px; row-gap: 4px;
    padding: calc(6px + var(--safe-t)) calc(12px + var(--safe-r)) calc(6px + var(--safe-b)) calc(12px + var(--safe-l));
  }
  .menu__eyebrow{ display: none; }
  .menu > .menu__core{ gap: 8px; }
  .menu__start{ gap: 8px; }
  .menu__field{ padding: 2px 8px 2px 14px; border-radius: 14px; }
  .menu__input{ padding: 6px 4px; font-size: 1.05rem; }
  .menu__start > .menu__play{ padding: 4px 20px 8px; font-size: clamp(1.4rem, 8vh, 2.1rem); border-radius: 18px; }
  .menu .menu__friends{ width: 100%; min-height: 40px; padding: 3px 12px 6px; border-radius: 16px; font-size: 1rem; gap: 6px; }
  .menu__avatarart{ width: 40px; height: 40px; border-radius: 12px; border-width: 2px; }
  .menu__avatar{ padding-bottom: 5px; }
  .menu__avatar .menu__levelbadge{ left: 24px; min-width: 22px; height: 22px; padding: 0 4px; border-width: 2px; border-radius: 7px; font-size: 0.8rem; }
  .menu__avatar .menu__levelbar{ width: clamp(36px, 8vw, 72px); height: 10px; margin-left: 6px; border-width: 1.5px; }
  .menu__avatar .menu__avatardot{ left: 30px; top: -4px; width: 13px; height: 13px; }
  .menu__who{ font-size: 1rem; }
  .menu__nav{ gap: 6px; }
  .menu .menu__menu > .menu__tile{ width: 36px; height: 36px; border-radius: 11px; --rim: 3px; }
  .menu .menu__menu > .menu__tile .menu__tileicon{ width: 20px; height: 20px; }
}
/* 320-340px: an iPhone sideways with the browser's bars showing. The frame gives a few pixels. */
@media (orientation: landscape) and (max-height: 340px) {
  .menu{ row-gap: 2px; padding-top: calc(4px + var(--safe-t)); padding-bottom: calc(4px + var(--safe-b)); --pad-t: calc(4px + var(--safe-t)); --pad-b: calc(4px + var(--safe-b)); }
  .menu .menu__friends{ min-height: 36px; font-size: 0.95rem; }
  .menu__start > .menu__play{ font-size: 1.4rem; }
}

/* ---------------------------------------------------- held upright */
@media (orientation: portrait) {
  .menu{
    --col: min(440px, 100%);
    /* Small enough not to dominate (Gray, second pass): about a quarter of the old upright logo's area. */
    --mark-h: 8.5vh;
    --t: clamp(58px, 9vh, 84px);
    --top-h: 54px;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-template-rows: auto auto minmax(0, 1fr) auto auto auto;
    grid-template-areas:
      "me     me     nav   nav"
      "brand  brand  brand brand"
      "pick   pick   pick  pick"
      "core   core   core  core"
      "burrow events board more"
      "foot   foot   foot  foot";
    column-gap: 10px;
  }
  /* No timed tile on: three big buttons, not a gap where Events was. */
  .menu:not(:has(.menu__eventspanel > .button--tile:not([hidden]))){
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-areas:
      "me     nav   nav"
      "brand  brand brand"
      "pick   pick  pick"
      "core   core  core"
      "burrow board more"
      "foot   foot  foot";
  }
  /* The top row and the left column dissolve: each piece takes its own zone. More joins the bottom row. */
  .menu__top, .menu__nav, .menu__left{ display: contents; }
  .menu__nav > .nuggetpill{ grid-area: nav; justify-self: end; align-self: center; }
  /* The language is not tucked into More: it sits at the right end of the logo's row, level with the wordmark, where the top row has no room for it. */
  .menu__lang{ grid-area: brand; justify-self: end; align-self: center; }
  .menu__menu{ grid-area: more; }
  /* The cards size from the room their row has, and step aside when it has none (short phones, the beta strip). */
  .menu .pickrow{ grid-area: pick; align-self: stretch; justify-self: center; width: var(--col); margin: 0; container-type: size; align-items: center; }
  .menu .pickrow .pickcard__art{ width: min(46cqw, 150px, calc((100cqh - 50px) / 0.577)); }
  .menu .pickrow .pickcard__art--floor{ width: auto; height: min(20cqw, 74px, calc(100cqh - 50px)); }
  .menu > .menu__core{ width: var(--col); }
  .menu__side{ display: none; }
  /* No keyword line upright (Gray: it was one more thing on a small screen). It stays in the page. */
  .menu__eyebrow{ display: none; }
  /* What's new, the music and Settings move into More; the corner keeps your Nuggets. */
  .menu__nav > .menu__navbtn{ display: none; }
  .menu .menu__bar > *, .menu .menu__tile--burrow{ min-width: 0; align-self: end; }
  .menu .menu__tile--burrow, .menu .menu__bar > .menu__tile, .menu .menu__bar > details > .menu__tile{ width: 100%; height: var(--t); }
  .menu__events{ display: block; }
  .menu__tile--board{ display: flex; }
  .menu__landonly{ display: none; }
  .menu__portraitonly{ display: list-item; }
  .menu__moredot{ display: block; }
  /* Upright, More is a big button like its row, and both drop-ups open up from the row. */
  .menu .menu__menu > .menu__tile{
    width: 100%; height: var(--t); padding: 4px 3px 6px; border-radius: calc(var(--t) * 0.22); --rim: 3px;
    background:
      var(--tile-shine) padding-box 180% 0 / 260% 100% no-repeat,
      radial-gradient(ellipse 90% 60% at 50% 18%, rgba(255, 255, 255, 0.35), transparent 70%) padding-box,
      linear-gradient(180deg, var(--bodyA) 0%, var(--bodyB) 52%, var(--bodyC) 100%) padding-box,
      linear-gradient(180deg, var(--rimA), var(--rimB)) border-box;
  }
  .menu .menu__menu > .menu__tile .button__label{ display: block; }
  .menu .menu__menu > .menu__tile .menu__tileicon{ width: calc(var(--t) * 0.5); height: calc(var(--t) * 0.5); }
  .menu :is(.menu__menu, .menu__events) > .navmenu__panel{
    top: auto; bottom: calc(var(--pad-b) + var(--t) + 12px);
    max-height: calc(100% - var(--pad-t) - var(--pad-b) - var(--top-h) - var(--t) - 24px);
  }
  .menu .menu__events > .menu__eventspanel{ left: 50%; right: auto; translate: -50% 0; width: max-content; max-width: calc(100% - var(--pad-l) - var(--pad-r)); }
  .menu__avatarart{ width: 44px; height: 44px; border-radius: 13px; }
  .menu__avatar{ padding-bottom: 5px; }
  .menu__avatar .menu__levelbadge{ left: 26px; min-width: 24px; height: 24px; border-radius: 8px; font-size: 0.86rem; }
  .menu__avatar .menu__levelbar{ width: clamp(40px, 16vw, 90px); height: 11px; margin-left: 6px; }
  .menu__avatar .menu__avatardot{ left: 32px; }
  .menu__who{ max-width: 34vw; }
}
/* A short upright phone (Safari's bars showing): the cards shrink before anything else gives. */
@media (orientation: portrait) and (max-height: 720px) {
  .menu{ --mark-h: 8vh; --t: 58px; }
  .menu__start > .menu__play{ font-size: 1.9rem; padding: 6px 24px 10px; }
}
@container pickrow (max-height: 84px) {
  .pickcard{ display: none; }
}
/* Upright and narrow: Nuggets and the Log in button share 360px with your picture. */
@media (orientation: portrait) and (max-width: 400px) {
  .menu__nav > .nuggetpill{ gap: 4px; padding: 2px 10px 2px 3px; font-size: 1rem; }
  .menu__nav > .nuggetpill img{ width: 22px; height: 22px; }
}

/* Small round buttons on a phone: drawn 36px, with a 44px hit area round each. */
@media (orientation: landscape) and (max-height: 500px), (orientation: portrait) and (max-width: 520px) {
  .menu .iconbtn{ position: relative; width: 36px; height: 36px; border-radius: 11px; --rim: 3px; }
  .menu .iconbtn::after{ content: ""; position: absolute; inset: -4px; }
  .menu .iconbtn .icon{ width: 20px; height: 20px; }
  .menu .menu__nav > .nuggetpill{ gap: 4px; padding: 2px 9px 2px 3px; font-size: 0.95rem; }
  .menu .menu__nav > .nuggetpill img{ width: 22px; height: 22px; }
}

/* The Log in panel: one provider button per row. */
.panel--signin .signin{ grid-template-columns: minmax(0, 1fr); margin-top: 8px; }
.signin > [hidden], .signin[hidden], .screen__card[data-account-signin][hidden]{ display: none; }
/* The same buttons in Settings → Account: one per row, in the controls column. */
.screen__card[data-account-signin] .signin{ grid-template-columns: minmax(0, 1fr); margin-top: 8px; }
.screen__card[data-account-signin] [data-signin-note]:empty{ display: none; }
.panel--signin .panel__note:empty{ display: none; }

/*
 * Save your progress (ui/saveNudge.ts). The prompt is the Log in panel with
 * the player's own numbers in it, so it is centred like the other confirm
 * panels; the badge is the passive version (Profile, the death screen, the
 * event) and never covers anything: a line of words with a glass button.
 */
.panel--signin:has([data-signin-nudge]:not([hidden])){ text-align: center; }
.signin__art{ display: block; width: 72px; height: 72px; margin: 0 auto 4px; object-fit: contain; filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.5)); }
.signin__art[hidden]{ display: none; }
.panel--signin [data-signin-nudge-sub]{ margin-bottom: 10px; line-height: 1.45; }
.savebadge{
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 6px 12px;
  margin: 0 0 12px; padding: 8px 12px; border-radius: var(--radius-sm);
  background: rgba(255, 209, 102, 0.1); border: 2px solid rgba(255, 209, 102, 0.38);
  color: var(--paper); font-size: 0.88rem; line-height: 1.35;
}
.savebadge[hidden]{ display: none; }
.savebadge__text{ min-width: 0; text-align: center; }
.savebadge .button{ flex: none; }
.death__save{ margin: 4px 0 8px; font-size: 0.82rem; }
/* The death screen sideways is already a full grid: the line stays off it, and the prompt and Profile carry the ask. */
@media (orientation: landscape) and (max-height: 500px){
  .signin__art{ display: none; }
  .death__save{ display: none; }
}


/* ======================================================= EVERY SCREEN ON A PHONE
 *
 * Gray, 11 Oct 2026: "text overflows everywhere on mobile ... every page inside
 * is shit too". The rules here are measured, not guessed: tools/check-overflow.mjs
 * opens every screen at 390x844, 430x932, 375x667, 844x390, 667x375, 932x430 and
 * 1180x820 with an iPhone's notch and home indicator, and fails on text outside
 * its box, anything clipped or under the notch, controls touching, and a
 * scroller that only just does not fit.
 */

/* Every screen keeps clear of the notch and the home indicator, not only the arena's layers. */
.overlay:not(.overlay--menu){
  padding: max(var(--ovpad, 16px), var(--safe-t)) max(var(--ovpad, 16px), var(--safe-r)) max(var(--ovpad, 16px), var(--safe-b)) max(var(--ovpad, 16px), var(--safe-l));
}
/* The full-size screens (Settings, The Burrow, Profile, Top players) use the edge of a phone. */
.overlay--screen{ --ovpad: clamp(6px, 2vw, 22px); }
@media (orientation: landscape) and (max-height: 500px){
  .overlay:not(.overlay--menu){
    padding: calc(6px + var(--safe-t)) calc(6px + var(--safe-r)) calc(6px + var(--safe-b)) calc(6px + var(--safe-l));
  }
}

/*
 * Tabs on a phone: icons only, all the same size, the chosen one in a lime rim,
 * and its name in the title instead (ui/tabWhere.ts). Sideways there is no room
 * for the word "Settings" as well, so the title is just where you are.
 */
.screen__where{ display: none; }
@media (max-width: 760px), (orientation: landscape) and (max-height: 500px){
  .screen__where:not(:empty){ display: inline; }
  .screen__where::before{ content: " · "; }
  .screen__title{ min-width: 0; }
  /* Settings and Profile: no room for Nuggets beside the title and the X (Account shows them; The Burrow keeps its pill). */
  .screen:not(.screen--burrow) .screen__bar > .nuggetpill{ display: none; }
}
@media (orientation: landscape) and (max-height: 500px){
  .screen:not(.screen--burrow) .screen__name{ display: none; }
  .screen__where::before{ content: none; }
  .screen .button--tab, .screen .button--tab.is-selected{ flex: 1 1 0; min-width: 0; max-width: 64px; padding: 3px 2px 5px; }
  .screen .button--tab.is-selected .button__label{ display: none; }
  .screen .button--tab.is-selected{ --rimA: #e8ff7a; --rimB: #c6f432; }
  /* Your Nuggets are on the front screen and in Account; here the tabs need the room. */
  .screen:not(.screen--burrow) .screen__bar > .nuggetpill{ display: none; }
}

/*
 * Options and Account have nothing to show in the window onto the arena, and
 * on a phone it took half the screen and pushed the cards below the fold
 * (Gray's screenshot: "Games played", then the top of the next card). Their
 * cards flow into as many columns as fit instead.
 */
.screen__pane--windowed:is([data-screen-pane="options"], [data-screen-pane="account"]){ grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
/* With no window, nothing paints the screen's ground: it is the solid screen again, not glass onto the arena (Gray, round 3). */
.screen.is-windowed:has(:is([data-screen-pane="options"], [data-screen-pane="account"]):not([hidden])){
  background: radial-gradient(ellipse 120% 80% at 50% 0%, #3d3196, var(--screen-fill) 70%);
}
.screen__pane--windowed:is([data-screen-pane="options"], [data-screen-pane="account"]) .screen__window{ display: none; }
:is([data-screen-pane="options"], [data-screen-pane="account"]) .screen__side{ align-items: start; align-content: start; padding-right: 0; }
.screen__card[data-account-signin] .button{ font-size: 0.95rem; padding: 8px 12px 9px; }
/*
 * Options: the volume card on the left, the rest stacked beside it. Account:
 * signing in in the widest column (so "Continue with Google" stays on one
 * line), your progress, then Erase. Upright on a phone, one column of each.
 */
@media (orientation: landscape), (min-width: 761px){
  [data-screen-pane="options"] .screen__side{ grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-flow: row dense; }
  [data-screen-pane="options"] .screen__side > .screen__card:first-child{ grid-column: 1; grid-row: span 3; }
  [data-screen-pane="options"] .screen__side > :not(:first-child){ grid-column: 2; }
  [data-screen-pane="account"] .screen__side{ grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 1fr); }
}
@media (orientation: landscape) and (max-height: 500px){
  /* Erase's warning is said again on the two screens it takes to erase; sideways the card is just its button. */
  [data-screen-pane="account"] .screen__card:has([data-account-erase]) .screen__note{ display: none; }
}
@media (orientation: landscape) and (max-height: 500px) and (max-width: 760px){
  [data-screen-pane="account"] .screen__side{ grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr) minmax(0, 0.9fr); }
  .screen__card[data-account-signin] .button{ font-size: 0.82rem; padding: 7px 8px 8px; gap: 6px; }
  .screen__card[data-account-signin] .button svg{ width: 16px; height: 16px; }
}

/* Themes' note under its two tiles: a size smaller where the pane is short, so it fits rather than peeking. */
@media (orientation: landscape) and (max-height: 500px), (orientation: portrait) and (max-height: 720px){
  /* Themes' note goes: the two tiles and the window say it, and a short pane then fits rather than peeking. */
  [data-screen-pane="themes"] .screen__note{ display: none; }
}
/* 320-330px sideways (Chrome on an iPhone, 667x320): the last few pixels. */
@media (orientation: landscape) and (max-height: 330px){
  .screen--burrow .burrow__grid{ --gap: 4px; }
  [data-screen-pane="account"] .keys--compact .keys__row{ padding: 1px 0; }
  .burrow__pane--upgrades{ padding-bottom: 0; }
  [data-screen-pane="account"] .screen__card{ gap: 4px; padding: 6px 10px; }
  [data-screen-pane="account"] .screen__side{ gap: 6px; }
  .screen:not(.screen--burrow) .screen__body{ padding-block: 6px 8px; }
}
@media (orientation: landscape) and (max-height: 360px){
  .burrow__pane--upgrades{ padding-bottom: 0; }
  .burrow__pane--upgrades .burrow__grid{ padding-bottom: calc(var(--drop) + 2px); }
  /* "This worm can't wear a spare head" sits in the middle of the pane, not 30% down and over its foot. */
  .burrow__empty.facelock{ inset: 50% 6% auto; translate: 0 -50%; gap: 8px; }
}

/* Floor, food and theme tiles: a long name ("Neon Motherboard") wraps inside its tile instead of running out of it. */
.station{ min-width: 0; }
.station--tile{ grid-template-columns: minmax(0, 1fr); }
.station--tile > *{ max-width: 100%; }
/* Your Nuggets keep their number inside the pill, everywhere they are shown. */
.nuggetpill{ flex: none; white-space: nowrap; }
.station__name, .station__desc{ min-width: 0; overflow-wrap: anywhere; hyphens: auto; }
@media (max-width: 760px), (orientation: landscape) and (max-height: 500px){
  .screen__tiles{ grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
  .station--tile .station__name{ font-size: 0.8rem; line-height: 1.15; }
  .station--tile .station__desc{ font-size: 0.68rem; }
}

/* The reward reveal is a whole-screen layer of its own: it keeps clear of the notch and the home indicator too. */
.reward{ padding: calc(18px + var(--safe-t)) calc(18px + var(--safe-r)) calc(18px + var(--safe-b)) calc(18px + var(--safe-l)); }
@media (orientation: landscape) and (max-height: 500px){
  .reward{ padding: calc(8px + var(--safe-t)) calc(16px + var(--safe-r)) calc(8px + var(--safe-b)) calc(16px + var(--safe-l)); }
  /* Settings' side column: a little tighter, so a pane that nearly fits does fit. */
  .screen__side{ gap: 8px; }
  .screen__side .screen__note{ font-size: 0.82rem; }
  /* How to play and the privacy notice: their list in two columns, so Back is on screen without scrolling. */
  .panel:has(> .panel__list){ width: min(860px, 100%); }
  .panel > .panel__list{ columns: 2; column-gap: 22px; }
  .panel > .panel__list > li{ break-inside: avoid; }
}
/* A small phone sideways (667x375): cards and lists a size tighter, so a pane that nearly fits does. */
@media (orientation: landscape) and (max-height: 500px) and (max-width: 760px){
  .screen__card{ gap: 8px; padding: 10px 12px; }
  .screen__side .screen__note{ font-size: 0.78rem; line-height: 1.35; }
  .panel:has(> .panel__list){ padding: 14px 16px; }
  .panel > .panel__list{ font-size: 0.84rem; }
}
/* A phone with Safari's bars showing: the Burrow's card pills say their price in smaller letters, inside the card. */
@media (orientation: landscape) and (max-height: 360px){
  .burrowcard__tag{ font-size: 0.64rem; padding: 1px 6px 4px; max-width: 100%; }
  /* Account fits without scrolling: tighter rows, and Erase's warning is said on the next screen anyway. */
  [data-screen-pane="account"] .keys--compact .keys__row{ padding: 3px 0; }
  :is([data-screen-pane="account"], [data-screen-pane="options"]) .screen__card{ gap: 6px; padding: 8px 10px; }
  [data-screen-pane="account"] .screen__side .screen__note{ font-size: 0.76rem; }
  [data-screen-pane="account"] .keys--compact .keys__row{ font-size: 0.8rem; }
  :is([data-screen-pane="account"], [data-screen-pane="options"]) .screen__heading{ font-size: 1.02rem; }
  /* Options: the share card is its button; the line over it goes. */
  [data-screen-pane="options"] .screen__card--center .screen__heading{ display: none; }
  /* The Burrow: tighter gaps and smaller pills, so a short row of cards fits without a sliver to scroll. */
  .burrow__grid{ --gap: 6px; }
  .burrowcard__tag{ font-size: 0.58rem; }
  .burrow__pane--upgrades{ padding-bottom: 0; }
  .panel > .panel__list{ font-size: 0.78rem; line-height: 1.32; }
  [data-screen-pane="account"] .screen__card:has([data-account-erase]) .screen__note{ display: none; }
}

/* The front screen's Nuggets never squeeze below their number. */
.menu__nav > .nuggetpill{ flex: none; white-space: nowrap; }
/* Sideways on a small phone the top row is your picture, Log in, the logo and three buttons: the level bar and How to play (it is in More) give way. */
@media (orientation: landscape) and (max-height: 500px) and (max-width: 760px){
  .menu__avatar .menu__levelbar{ display: none; }
  .menu__nav > [data-game-howto-open]{ display: none; }
}

/* ======================================================= site theme: Overdrive
 *
 * Gray, 29 Sep 2026: "a techno high paced feel ... futuristic like the game
 * Extreme G on n64". One layer keyed on `data-vibe="overdrive"` on the
 * document (client main.ts applySiteTheme; the list is shared/themes.ts), so
 * Classic is untouched and every surface changes together. It works through
 * the house components' own tokens (--bodyA/B/C, --rimA/B, --edge, --glow,
 * --ts, --ink) wherever they have them, and restyles the shells (panel,
 * screen, menu, boot, HUD) that paint their own colours.
 *
 * Gunmetal and near-black, cyan and magenta neon, sharp corners, slanted
 * italic capitals. The heading face falls back through techno faces we may
 * host later (Orbitron, Exo 2) to a narrow system face.
 */
html[data-vibe="overdrive"]{
  --od-cyan: #19E3FF; --od-mag: #FF2BD6; --od-amber: #FFB020; --od-lime: #B6FF3B;
  --od-ink: #0B0E17; --od-steel: #1A2030; --od-steel-2: #242C3F; --od-line: rgba(25, 227, 255, 0.45);
  --od-font: "Orbitron", "Exo 2", "Rajdhani", "Eurostile", "Avenir Next Condensed", "Arial Narrow", system-ui, sans-serif;
  --accent: var(--od-cyan); --accent-2: #0FA9C4; --radius: 10px; --radius-sm: 6px;
}
html[data-vibe="overdrive"] body{ background: var(--od-ink); }

/* Buttons: gunmetal slabs with a neon edge; the primary burns magenta. */
[data-vibe="overdrive"] :is(.button, .menu__play, .iconbtn, .set-btn, .share__btn,
    .dock__icon, .dock__btn, .boostbutton, .beta__close, .settings__back){
  --rimA: #3a4760; --rimB: #1b2231; --edge: var(--od-line); --glow: rgba(25, 227, 255, 0.35);
  --bodyA: #2b3549; --bodyB: #171e2c; --bodyC: #0c1019; --ts: rgba(0, 0, 0, 0.55); --ink: #EAFBFF;
  --rim: 2px;
  border-radius: 7px;
  font-family: var(--od-font); font-weight: 800; font-style: italic; letter-spacing: 0.05em; text-transform: uppercase;
  text-shadow: 0 0 10px rgba(25, 227, 255, 0.55), 0 2px 0 rgba(0, 0, 0, 0.6);
  box-shadow: 0 0 0 1px var(--edge), 0 0 18px -4px var(--glow), inset 0 1px 0 rgba(255, 255, 255, 0.16), inset 0 -3px 0 rgba(0, 0, 0, 0.35);
}
[data-vibe="overdrive"] :is(.button--primary, .menu__play, .boostbutton){
  --bodyA: #ff6be4; --bodyB: #e0149f; --bodyC: #8a0a62; --edge: rgba(255, 43, 214, 0.75); --glow: rgba(255, 43, 214, 0.55);
  --rimA: #ff9bef; --rimB: #b3108a;
  text-shadow: 0 0 12px rgba(255, 180, 245, 0.8), 0 2px 0 rgba(60, 0, 40, 0.7);
}
[data-vibe="overdrive"] :is(.button--quiet, .share__btn, .beta__close){
  --bodyA: #202838; --bodyB: #151b28; --bodyC: #0e131d; --ink: #BFEFFF;
}
[data-vibe="overdrive"] .button--glass{ --bodyA: rgba(20, 28, 44, 0.85); --bodyB: rgba(14, 19, 31, 0.85); --bodyC: rgba(10, 13, 22, 0.85); }
[data-vibe="overdrive"] .set-danger{ --bodyA: #ff5a6e; --bodyB: #c81b36; --bodyC: #6e0716; --edge: rgba(255, 70, 90, 0.7); }

/* The front screen's tiles: dark glass cards lit in their own neon. */
[data-vibe="overdrive"] .button--tile{ --bodyA: #273046; --bodyB: #151b28; --bodyC: #0b0f18; --rimA: var(--tone, var(--od-cyan)); --rimB: #0b0f18; }
[data-vibe="overdrive"] .button--tile[data-tone="pink"]{ --tone: var(--od-mag); --glow: rgba(255, 43, 214, 0.5); --edge: rgba(255, 43, 214, 0.6); }
[data-vibe="overdrive"] .button--tile[data-tone="orange"]{ --tone: var(--od-amber); --glow: rgba(255, 176, 32, 0.45); --edge: rgba(255, 176, 32, 0.6); }
[data-vibe="overdrive"] .button--tile[data-tone="blue"]{ --tone: var(--od-cyan); --glow: rgba(25, 227, 255, 0.45); --edge: rgba(25, 227, 255, 0.6); }
[data-vibe="overdrive"] .button--tile[data-tone="green"]{ --tone: var(--od-lime); --glow: rgba(182, 255, 59, 0.4); --edge: rgba(182, 255, 59, 0.55); }
[data-vibe="overdrive"] .button--tile[data-tone="violet"]{ --tone: #9D7BFF; --glow: rgba(157, 123, 255, 0.45); --edge: rgba(157, 123, 255, 0.6); }
[data-vibe="overdrive"] .button--tile[data-tone="teal"]{ --tone: #25F2C8; --glow: rgba(37, 242, 200, 0.4); --edge: rgba(37, 242, 200, 0.55); }
[data-vibe="overdrive"] .button--tile .button__label{ color: var(--tone, var(--od-cyan)); }

/* Panels and full screens: gunmetal plates with a neon rule round them. */
[data-vibe="overdrive"] .panel{
  background:
    linear-gradient(180deg, rgba(25, 227, 255, 0.08), transparent 30%),
    linear-gradient(160deg, #1f2738 0%, #121826 55%, #0b0f18 100%);
  border: 2px solid var(--od-line); border-radius: 12px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 30px -8px rgba(25, 227, 255, 0.45), 0 18px 40px -14px rgba(0, 0, 0, 0.8);
}
[data-vibe="overdrive"] .screen{
  --screen-fill: #0f1420;
  background: linear-gradient(170deg, #1a2131, var(--screen-fill) 60%);
  border: 2px solid var(--od-line); border-radius: 12px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 34px -8px rgba(25, 227, 255, 0.45), 0 18px 40px -14px rgba(0, 0, 0, 0.8);
}
[data-vibe="overdrive"] .screen__bar{ background: linear-gradient(180deg, #1c2434, #141a27); border-bottom: 1px solid var(--od-line); }
[data-vibe="overdrive"] .screen__bar::before{ opacity: 0; }
[data-vibe="overdrive"] :is(.screen__title, .panel__title, .panel__eyebrow, .screen__heading, .menu__eyebrow, .reward__eyebrow){
  font-family: var(--od-font); font-style: italic; text-transform: uppercase; letter-spacing: 0.06em;
  color: #EAFBFF; text-shadow: 0 0 12px rgba(25, 227, 255, 0.6);
}
[data-vibe="overdrive"] .station{ background: #161c29; border-radius: 6px; }
[data-vibe="overdrive"] .station.is-selected{ border-color: var(--od-cyan); box-shadow: 0 0 14px -4px rgba(25, 227, 255, 0.6); }

/* The front screen: darker edges, the logo lit up in neon. */
[data-vibe="overdrive"] .overlay--menu{
  background:
    linear-gradient(to bottom, rgba(6, 9, 18, 0.75) 0%, rgba(6, 9, 18, 0) 22%),
    linear-gradient(to top, rgba(6, 9, 18, 0.8) 0%, rgba(6, 9, 18, 0) 26%),
    radial-gradient(ellipse 120% 90% at 50% 45%, rgba(6, 9, 18, 0.1) 50%, rgba(6, 9, 18, 0.55) 100%);
}
/* A tight neon rim, not a wide haze: the wide one was clipped into a box (see .menu__mark). */
[data-vibe="overdrive"] .menu__mark{ filter: drop-shadow(0 0 3px rgba(25, 227, 255, 0.9)) drop-shadow(0 0 9px rgba(255, 43, 214, 0.55)) saturate(1.1); }

/* Loading screen and the ray-lit screens: cyan light on near-black. */
[data-vibe="overdrive"] .boot{
  background:
    radial-gradient(circle at 50% 35%, rgba(25, 227, 255, 0.18), transparent 45%),
    radial-gradient(ellipse 120% 100% at 50% 30%, #1a2236, #0d1220 55%, #06080f 100%);
}
[data-vibe="overdrive"] :is(.reward, .death__rays){ filter: hue-rotate(-60deg) saturate(1.2); }

/* The HUD reads like a race dash. */
[data-vibe="overdrive"] :is(.hud__label, .hud__value){ font-family: var(--od-font); font-style: italic; }
[data-vibe="overdrive"] .hud__stat:first-child .hud__value{ color: var(--od-cyan); text-shadow: 0 0 10px rgba(25, 227, 255, 0.7); }

/* Settings' theme cards: a swatch drawn from the theme's own colours. */
/* A theme's card and the Themes window show a capture of the front screen wearing it. */
.station__shot{ display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 10px; box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.25), 0 3px 8px rgba(0, 0, 0, 0.3); }
.screen__window--shot{ position: relative; overflow: hidden; }
.screen__shot{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.screen__window--shot .screen__badge{ z-index: 1; }
/* Overdrive, the rest of the front screen and the Settings shell. */
[data-vibe="overdrive"] .pickcard{
  border-radius: 10px;
  background: linear-gradient(180deg, #1d2536, #111623);
  box-shadow: inset 0 0 0 1px var(--od-line), 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 18px -6px rgba(25, 227, 255, 0.4);
}
[data-vibe="overdrive"] .pickcard__label{ font-family: var(--od-font); font-style: italic; text-transform: uppercase; letter-spacing: 0.04em; }
[data-vibe="overdrive"] .menu__field{
  border-radius: 8px; background: #0d121c;
  box-shadow: inset 0 0 0 1px var(--od-line), 0 0 16px -6px rgba(25, 227, 255, 0.5);
}
[data-vibe="overdrive"] .menu__field:focus-within{ box-shadow: inset 0 0 0 2px var(--od-mag), 0 0 18px -4px rgba(255, 43, 214, 0.6); }
[data-vibe="overdrive"] .menu__label{ color: var(--od-cyan); }
[data-vibe="overdrive"] .menu__input{ color: #EAFBFF; }
[data-vibe="overdrive"] .menu__input::placeholder{ color: #6f86a3; }
[data-vibe="overdrive"] .screen__tabs{ background: #121826; border-bottom: 1px solid var(--od-line); }
[data-vibe="overdrive"] .button--tab{ --bodyA: #222b3d; --bodyB: #182030; --bodyC: #111723; --ts: rgba(0, 0, 0, 0.6); }
[data-vibe="overdrive"] .button--tab[aria-selected="true"]{ --bodyA: #0f5b6c; --bodyB: #0c4655; --bodyC: #08323d; --edge: var(--od-cyan); }
[data-vibe="overdrive"] .screen__window{ border-radius: 10px; border: 2px solid var(--od-line); }
/* A windowed screen stays see-through, or its window shows the panel, not the arena (3 Oct 2026). */
[data-vibe="overdrive"] .screen.is-windowed{ background: transparent; }
[data-vibe="overdrive"] .screen__badge{
  border-radius: 8px; border: 1px solid var(--od-line); background: rgba(8, 12, 20, 0.88);
  font-family: var(--od-font); font-style: italic; text-transform: uppercase; color: #EAFBFF;
}
[data-vibe="overdrive"] .screen__heading{ font-family: var(--od-font); font-style: italic; text-transform: uppercase; letter-spacing: 0.06em; }
/*
 * Profile and Achievements in Overdrive (3 Oct 2026 sweep): the stat cards, the
 * "you" card and the trophies were still Classic violet.
 */
[data-vibe="overdrive"] :is(.statcard, .trophy){
  border: 2px solid var(--od-line); border-radius: 10px;
  background: linear-gradient(180deg, #1f2738, #141a27);
  box-shadow: 0 0 16px -8px rgba(25, 227, 255, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
[data-vibe="overdrive"] .trophy[data-tier="bronze"]{ border-color: #c98a5a; }
[data-vibe="overdrive"] .dossier__me{ border-radius: 10px; background: #161c29; border: 1px solid var(--od-line); box-shadow: none; }
[data-vibe="overdrive"] :is(.statcard__label, .statcard__value){ text-shadow: 0 0 8px rgba(25, 227, 255, 0.45); }
[data-vibe="overdrive"] .statcard__label{ color: var(--od-cyan); font-family: var(--od-font); font-style: italic; text-transform: uppercase; font-size: 0.72rem; letter-spacing: 0.04em; }
/*
 * Overdrive's small labels (4 Oct 2026, Gray: "overdrive boost word is
 * overflowing on mobile, same as achievments"). Orbitron is a wide display
 * face: in a 64px tile "ACHIEVEMENTS" ran 16px past each side, the Burrow's
 * "BEST RUN 28K" tags 20px, a selected tab's name on a phone 22px. Orbitron
 * stays on titles, headings and the big buttons; the tight labels go back to
 * the game's own lettering, which Classic proves fits, in Overdrive's colours.
 */
[data-vibe="overdrive"] :is(.button--tile .button__label, .button--pill, .button--sm, .tile__badge){
  font-family: "Lilita One", var(--font-display); font-weight: 400; font-style: normal;
  letter-spacing: 0.01em; text-transform: none;
}
@media (max-width: 1100px), (max-height: 540px){
  [data-vibe="overdrive"] .button--tab .button__label{
    font-family: "Lilita One", var(--font-display); font-weight: 400; font-style: normal;
    letter-spacing: 0.01em; text-transform: none;
  }
}
/* Boost keeps Orbitron, sized to the button it is in (74px on a phone, 96px elsewhere). */
[data-vibe="overdrive"] .boostbutton{ letter-spacing: 0; font-size: calc(var(--boost-size) * 0.135); }
/*
 * A Burrow card's name shrinks to fit its card rather than run out of it: on a
 * phone on its side the cards are narrow and "Neon Amber" was 15px over, in
 * both themes.
 */
.burrowcard{ container-type: inline-size; }
.burrowcard .station__name{ font-size: min(1em, 13cqi); }

/*
 * "Weak connection" (main.ts watchConnection): its own line under the stats,
 * ringed text in amber like the rest of the HUD, never a box over the arena.
 */
/* No width of its own (contain: inline-size): it fills the stats' width and runs on past it, so it can never widen that column and squeeze the Top 10 and the minimap beside it (6 Oct 2026). */
.hud__net{
  flex-basis: 100%; margin: -10px 0 0; display: flex; align-items: center; gap: 6px;
  contain: inline-size; white-space: nowrap; overflow: visible;
  font-family: "Lilita One", var(--font-display); font-size: 0.95rem; color: #ffc24a;
  text-shadow: var(--hud-ring); animation: net-blink 1.2s ease-in-out infinite;
}
.hud__net[hidden]{ display: none; }
/*
 * The Halloween Party's event line (ui/hud.ts setEventLine): a line of the
 * stats strip, under the readouts, for as long as an event runs. Ringed text
 * like the rest of the strip, never a box over the arena (Gray, 18 Oct 2026:
 * "nobody knows whats going on"). It arrives big, then settles.
 */
.hud__event{
  /*
   * contain: inline-size, as .hud__net: the line takes the strip's width and
   * never sets it. Without it the strip (a max-content column) grew to the
   * line's whole length and the line sat beside Level, off the screen on a
   * phone (check-overflow's in-run scenarios, 18 Oct 2026).
   */
  flex-basis: 100%; max-width: min(26rem, 64vw, 86%); margin: 0; contain: inline-size;
  font-family: var(--font-display); font-weight: 600; font-size: 0.95rem; line-height: 1.25;
  color: #ffb347; white-space: pre-line; overflow-wrap: anywhere;
  transform-origin: left top;
}
.hud__event[hidden]{ display: none; }
.hud__event.is-new{ animation: hud-event-in 900ms ease-out both; }
/* Bigger as it arrives, then settled: 1.15, so even the arriving line stays clear of the Top 10 (check-overflow). */
@keyframes hud-event-in{ 0%{ transform: scale(1.15); filter: brightness(1.5); } 55%{ transform: scale(1.15); filter: brightness(1.5); } 100%{ transform: scale(1); filter: none; } }
.hud__event.is-close{ color: #ff5c5c; }
@media (prefers-reduced-motion: reduce){ .hud__event.is-new{ animation: none; } }
.hud__net svg{ flex: none; filter: drop-shadow(0 1px 0 var(--board-ink)) drop-shadow(0 -1px 0 var(--board-ink)); }
/* The one-time "graphics lowered" note: the same ringed line, steady and allowed to wrap in two short lines so it stays clear of the Top 10. */
.hud__quality{ animation: none; white-space: normal; contain: none; max-width: min(15em, 42vw); line-height: 1.15; color: #fff; }
.hud__quality[hidden]{ display: none; }
@keyframes net-blink{ 0%, 100%{ opacity: 1; } 50%{ opacity: 0.55; } }
@media (prefers-reduced-motion: reduce){ .hud__net{ animation: none; } }

/*
 * Flags and titles (5 Oct 2026). A flag is a small real flag (shared flags.ts,
 * the flag-icons set) beside a name: on the in-run board, the leaderboard and
 * the profile. A title is an achievement's name a player has finished, shown
 * under their name on the profile and beside it on the leaderboard, never over
 * the arena (clean play area).
 */
.flagicon{
  display: inline-block; flex: none; width: 28px; height: 21px; object-fit: cover; border-radius: 3px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35); vertical-align: -4px;
}
.flagicon[hidden]{ display: none; }
.squirm-board__name .flagicon{ width: 16px; height: 12px; margin-right: 5px; vertical-align: -1px; border-radius: 2px; }
.ranks__name .flagicon{ margin-right: 8px; vertical-align: -2px; }
.ranks__title{
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: inherit; font-size: 0.78rem; font-weight: 700; color: #ffd27a; text-shadow: none;
}
.dossier__title{
  margin: -6px 0 0; font-family: "Lilita One", var(--font-display); font-size: 1rem; color: #ffd27a;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.35);
}
.dossier__title[hidden]{ display: none; }
.dossier__picks{ display: grid; gap: 8px; width: 100%; }
.dossier__picks .button{ justify-content: center; gap: 8px; }

/* The list picker (client ui/listPicker.ts): the house panel, a search field, then the choices. */
.panel--picker{ width: min(460px, 100%); display: flex; flex-direction: column; max-height: min(640px, 100%); }
.panel--picker .panel__title{ margin: 0 0 4px; text-align: center; }
.panel--picker .panel__note{ margin: 0 0 10px; text-align: center; }
.pickerlist__search{ margin: 0 0 10px; }
.pickerlist__search[hidden]{ display: none; }
.pickerlist{
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: grid; gap: 6px; padding: 2px 4px 2px 2px;
}
.pickerlist__item{ justify-content: flex-start; gap: 10px; text-align: left; }
.pickerlist__item.is-selected{ box-shadow: 0 0 0 3px #c6f432; }
.pickerlist__text{ display: grid; min-width: 0; }
.pickerlist__text b{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pickerlist__text small{ font-size: 0.75rem; font-weight: 600; opacity: 0.8; }

/*
 * Achievement rarity and Legends (5 Oct 2026, the research pack): how many
 * players hold the tier a card shows, and the Legend a top tier gives.
 */
.trophy__rarity{ margin: 0; font-size: 0.8rem; font-weight: 700; color: rgba(255, 255, 255, 0.75); }
.trophy__rarity.is-rare{ color: #ffd27a; }
.trophy__legend{
  margin: 0; display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 12px;
  background: linear-gradient(90deg, rgba(255, 210, 74, 0.28), rgba(255, 79, 173, 0.18));
  box-shadow: inset 0 0 0 2px rgba(255, 210, 74, 0.55);
  font-family: "Lilita One", var(--font-display); font-size: 0.98rem; color: #fff; text-shadow: 0 2px 0 #35187f;
}
.trophy__legend canvas{ width: 64px; height: 30px; flex: none; }
.trophy__legend small{ font-family: inherit; font-size: 0.74rem; font-weight: 700; color: #ffd27a; text-shadow: none; }
.trophy__share{ flex: none; }
/* A phone held sideways (about 320px tall): the note goes, and everything else gives the list room. */
@media (orientation: landscape) and (max-height: 460px){
  .panel--picker{ padding: 10px 12px; }
  .panel--picker .panel__title{ font-size: 1.05rem; margin: 0 0 6px; }
  .panel--picker .panel__note{ display: none; }
  .pickerlist__search{ margin: 0 0 6px; }
  .pickerlist__search .menu__input{ padding: 4px; font-size: 1rem; }
  .panel--picker .panel__actions{ margin-top: 8px; }
  .panel--picker .panel__actions .button{ padding-block: 6px; }
}

/*
 * A limited event (client ui/eventPanel.ts, 7 Oct 2026): the banner over the
 * house panel, a big countdown in the page's own big-number style, and a
 * line saying when. The menu tile is a .button--tile with its countdown on
 * the .tile__badge, like Daily gift's.
 */
.panel--event{ width: min(480px, 100%); text-align: center; animation: buy-in 180ms ease-out both; }
.event__banner{
  /* Wider than the panel's content, to its edges: the page's img max-width would hold it back. */
  display: block; width: calc(100% + 40px); max-width: none; margin: -20px -20px 10px; height: auto; aspect-ratio: 3 / 2;
  object-fit: cover; border-radius: calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0 0;
}
.event__banner:not([src]){ display: none; }
.panel--event .panel__title{ margin: 2px 0 6px; font-size: 1.6rem; }
.event__count{ margin: 0; font-size: clamp(1.7rem, 7vw, 2.3rem); color: #ffb347; font-variant-numeric: tabular-nums; white-space: nowrap; }
.event__when{ margin: 4px 0 0; font-size: 0.86rem; color: var(--text-dim); }
.panel--event .panel__note{ margin: 10px 0 0; font-size: 0.9rem; }
/* The Halloween Party's board, your candy and the milestones (ui/eventPanel.ts). */
.event__details{ margin: 12px 0 0; text-align: left; }
.event__mine{ margin: 0 0 8px; text-align: center; font-family: var(--font-display); font-weight: 600; color: #ffb347; }
.event__milestones{ list-style: none; margin: 0 0 12px; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.event__milestone{
  display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 6px 4px; border-radius: 10px;
  background: rgba(0, 0, 0, 0.22); text-align: center; font-size: 0.72rem; line-height: 1.2; min-width: 0;
}
.event__milestone b{ font-family: var(--font-display); font-size: 0.85rem; }
.event__milestone span{ font-variant-numeric: tabular-nums; }
.event__milestone small{ color: var(--text-dim); overflow-wrap: anywhere; }
.event__milestone.is-next{ box-shadow: inset 0 0 0 2px #ffb347; }
.event__milestone.is-done{ background: rgba(255, 179, 71, 0.28); }
.event__milestone.is-done b::after{ content: " \2713"; }
.event__boardtitle{ margin: 0 0 4px; text-align: center; }
.event__quests{ list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 4px; }
.event__quest{
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center;
  padding: 6px 10px; border-radius: 10px; background: rgba(0, 0, 0, 0.22); font-size: 0.8rem; line-height: 1.25;
}
.event__questtext{ min-width: 0; overflow-wrap: anywhere; }
.event__questprog{ font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 700; }
.event__quest.is-done{ background: rgba(255, 179, 71, 0.28); }
.event__quest.is-done .event__questprog{ color: #ffb347; }
.event__board{ padding: 0 4px; }
.event__rules{ margin: 10px 0 0; font-size: 0.74rem; color: var(--text-dim); }
@media (prefers-reduced-motion: reduce){ .panel--event{ animation: none; } }
/* Short screens (a phone held sideways): the banner steps aside and the panel is only words. */
@media (orientation: landscape) and (max-height: 460px){
  .event__banner{ display: none; }
  .panel--event{ padding: 10px 16px; }
  .panel--event .panel__title{ font-size: 1.2rem; margin: 0 0 2px; }
  .event__count{ font-size: 1.6rem; }
  .panel--event .panel__note{ margin: 6px 0 0; font-size: 0.8rem; }
  .panel--event .panel__actions{ margin-top: 8px; }
}

/* Contact page (pages/contact.ts). The notes show by :target after the form posts back. */
.contact{display:grid;gap:16px;max-width:560px;margin:8px 0 24px}
.contact__field{display:grid;gap:6px;font-weight:600}
.contact__field em{font-style:normal;font-weight:400;color:var(--text-dim)}
.contact__field input,.contact__field select,.contact__field textarea{
  font:inherit;font-weight:400;color:var(--text);background:var(--ink-2);
  border:1px solid var(--line);border-radius:var(--radius-sm);padding:12px 14px;width:100%;box-sizing:border-box}
.contact__field textarea{resize:vertical;min-height:140px}
.contact__field :focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.contact .button{justify-self:start}
.contact__trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.contact__note{display:none;padding:12px 16px;border-radius:var(--radius-sm);border:1px solid var(--warn);color:var(--text)}
.contact__note--ok{border-color:#3ddc84}
.contact__note:target{display:block}
