/* -------------------------------------------------------------------
   Flappy Buddy — design tokens
   Palette: black stage behind the cabinet, cream glass panels,
   golden-retriever gold, bullish-candle green, warm bark brown text,
   amber ticker accent. (The sky blue lives INSIDE the game, painted on
   the canvas by background.js — it is not a page-chrome color.)
   Type: Fredoka (display) / Nunito (body+UI) / JetBrains Mono (ticker).
   ------------------------------------------------------------------- */
:root {
  /* The "stage" the arcade cabinet sits on — everything OUTSIDE the game.
     Deliberately a flat pure black, never a gradient: see the html/body
     rule below for why a gradient here is what produced a visible strip. */
  --color-stage: #000000;
  --color-cream: #FFF8ED;
  --color-gold: #F4B84A;
  --color-gold-dark: #D99A2B;
  --color-green: #2ECC71;
  --color-green-dark: #1F9C56;
  --color-bark: #5A3E2B;
  --color-bark-deep: #3A2A20;
  --color-amber: #FFD24C;

  --font-display: 'Fredoka', system-ui, sans-serif;
  --font-body: 'Nunito', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, monospace;

  --radius-lg: 28px;
  --radius-md: 18px;
  --radius-sm: 12px;

  --shadow-soft: 0 12px 30px rgba(58, 42, 32, 0.18);
  --shadow-button: 0 6px 0 rgba(0, 0, 0, 0.15);

  /* ---------------------------------------------------------------
     Theme palette — one coherent set of roles every world fills in:
       --t-primary   saturated "brand" color for values that always sit on
                      the cream HUD ticker (score, leaderboard, best)
       --t-secondary heading color, tuned to contrast THIS world's own
                      panel background (dark text on a light panel,
                      bright text on a dark one)
       --t-accent    the bright "pop" color — MPH readout, epic moment
                      text, selected character-card border
       --t-hud       HUD ticker/power-pill border tint
       --t-button    primary-button base color (gradient + shadow are
                      derived from it via color-mix, see .btn--primary)
       --t-popup     card/panel/toast surface tint
       --t-border    card/panel border tint
       --t-text      body text color inside cards/panels
     Declared here at :root as the Classic World's own palette (the
     default, active whenever no .theme-* class is present) so every
     component below can reference var(--t-*) unconditionally — a new
     world theme is then just one small override block redefining these
     eight, not touching component rules at all.
     --------------------------------------------------------------- */
  --t-primary: #1F9C56;
  --t-secondary: #D99A2B;
  --t-accent: #FFD24C;
  --t-hud: rgba(46, 204, 113, 0.55);
  --t-button: #2ECC71;
  --t-popup: #FFF8ED;
  --t-border: rgba(255, 255, 255, 0.6);
  --t-text: #3A2A20;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  /* The stage the cabinet sits on. A single FLAT color — never a gradient —
     declared identically on both boxes.
     html and body are NOT the same size: once body becomes a centered flex
     item it shrinks to the cabinet's width (480px) while html stays the
     full viewport width. A gradient rasterizes across each box's own
     dimensions, so the two layers painted different slices of the same
     ramp and the seam between them read as an extra background layer / a
     colored strip. A flat color cannot disagree with itself at any size,
     which is what actually removes the strip rather than covering it. */
  background: var(--color-stage);
  font-family: var(--font-body);
  color: var(--color-bark-deep);
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}

html {
  height: 100%;
}

body {
  /* min-height, not height: the box can then never resolve SHORTER than
     the viewport and expose whatever is painted behind it as a band along
     the bottom. 100dvh tracks the *dynamic* viewport (mobile toolbars
     showing/hiding) so the stage always covers exactly what is on screen;
     the 100vh line above it is the fallback for browsers without dvh, and
     is overridden wherever dvh is supported. */
  width: 100%;
  min-height: 100vh;
  min-height: 100dvh;
}

button {
  font-family: inherit;
}

#app {
  position: relative;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  max-width: 560px;
  margin: 0 auto;
  overflow: hidden;
  /* Black under the cabinet too, so the corners outside its border-radius
     (and any sub-pixel edge left by a fractional dvh height) resolve to the
     stage color rather than to whatever happens to be painted behind. */
  background: var(--color-stage);
}

#canvas-wrap {
  position: absolute;
  inset: 0;
}

#game-canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* Stops a flap tap from panning/zooming the page or firing a double-tap
     zoom. This lives on the CANVAS, not on #app: touch-action is resolved
     by walking up the ancestor chain, so `none` on #app suppressed touch
     panning for everything inside it — including the overlay screens'
     own scroll containers. That left the character grid (395px of scroll),
     How to Play (291px) and the leaderboard (4727px, i.e. 95 of 100 rows)
     completely unscrollable by touch on a phone, while a mouse wheel still
     worked, so it was invisible in desktop testing. Scoping it to the
     canvas keeps gameplay taps inert AND lets the menus scroll normally:
     the HUD, moment and ready-prompt overlays are all pointer-events:none,
     and inactive screens are too, so during play a touch always lands
     here anyway. */
  touch-action: none;
}

/* -------------------------------------------------------------------
   Glass panel base
   ------------------------------------------------------------------- */
.glass {
  background: color-mix(in srgb, var(--t-popup) 82%, transparent);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border: 1px solid var(--t-border);
  box-shadow: var(--shadow-soft);
  transition: background 0.25s ease, border-color 0.25s ease;
}

/* -------------------------------------------------------------------
   Screens (menu, characters, leaderboard, settings, credits, gameover)
   ------------------------------------------------------------------- */
.screen {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* max(...) keeps the usual 24px on unnotched screens, and grows only
     where a device notch/home-indicator/rounded corner actually needs
     more room (landscape notch on wide phones, bottom home-indicator). */
  padding: max(24px, env(safe-area-inset-top, 0px)) max(24px, env(safe-area-inset-right, 0px)) max(24px, env(safe-area-inset-bottom, 0px)) max(24px, env(safe-area-inset-left, 0px));
  z-index: 10;
  opacity: 0;
  transform: scale(0.96);
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}

.screen.screen--active {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  .screen {
    transition: opacity 0.12s linear;
    transform: none;
  }
}

.menu-card,
.panel-card {
  width: 100%;
  max-width: 400px;
  border-radius: var(--radius-lg);
  padding: 32px 28px;
  text-align: center;
}

.panel-card {
  max-height: 82dvh;
  display: flex;
  flex-direction: column;
  text-align: left;
}

.panel-card--compact {
  max-width: 340px;
  text-align: center;
}

.game-title {
  margin: 0 0 6px;
  font-family: var(--font-display);
  line-height: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.game-title__main {
  font-size: 34px;
  font-weight: 600;
  color: var(--t-text);
  transition: color 0.25s ease;
}

.game-title__accent {
  font-size: 48px;
  font-weight: 700;
  color: var(--t-secondary);
  text-shadow: 0 3px 0 rgba(255, 255, 255, 0.6);
  transition: color 0.25s ease;
}

.game-subtitle {
  margin: 0 0 26px;
  font-weight: 700;
  color: var(--t-text);
  opacity: 0.75;
  transition: color 0.25s ease;
}

.menu-buttons {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.btn {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  color: var(--color-bark-deep);
  background: #FFFFFF;
  border: none;
  border-radius: var(--radius-md);
  padding: 14px 20px;
  cursor: pointer;
  box-shadow: var(--shadow-button);
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}

.btn:hover {
  transform: translateY(-1px);
}

.btn:active {
  transform: translateY(4px);
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.15);
}

.btn:focus-visible {
  outline: 3px solid var(--color-green);
  outline-offset: 2px;
}

.btn--primary {
  background: linear-gradient(180deg, var(--t-button) 0%, color-mix(in srgb, var(--t-button) 55%, black) 100%);
  color: #FFFFFF;
  box-shadow: 0 6px 0 color-mix(in srgb, var(--t-button) 55%, black);
  transition: background 0.25s ease, box-shadow 0.25s ease, transform 0.08s ease;
}

.btn--ghost {
  background: transparent;
  box-shadow: none;
  color: var(--t-text);
  opacity: 0.7;
  transition: color 0.25s ease;
}

.btn--icon {
  padding: 10px 14px;
  font-size: 18px;
  box-shadow: none;
  background: rgba(255, 255, 255, 0.7);
}

.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 18px;
}

.panel-header h2 {
  font-family: var(--font-display);
  font-size: 24px;
  margin: 0;
  color: var(--t-secondary);
  transition: color 0.25s ease;
}

/* -------------------------------------------------------------------
   HUD (in-game)
   ------------------------------------------------------------------- */
#hud {
  position: absolute;
  /* viewport-fit=cover (see index.html) lets the page extend under a
     device notch/Dynamic Island — without this the HUD's fixed 18px
     offset would sit right under it on notched phones instead of below. */
  top: calc(18px + env(safe-area-inset-top, 0px));
  left: 50%;
  transform: translateX(-50%) translateY(-140%);
  z-index: 5;
  transition: transform 0.25s ease;
  pointer-events: none;
}

#hud.hud--active {
  transform: translateX(-50%) translateY(0);
}
.hud__meters { display:flex; justify-content:center; gap:8px; margin-top:8px; }
.hud__mph,.hud__power { border:1px solid color-mix(in srgb, var(--t-hud) 70%, white); background:rgba(22,35,53,.52); color:#fff; border-radius:999px; padding:5px 10px; font:700 12px var(--font-mono); box-shadow:0 5px 16px rgba(0,0,0,.12); transition: border-color .25s ease; }
.hud__mph span { color:var(--t-accent); font-size:17px; transition: color .25s ease; }.hud__mph small { margin-left:3px; font-size:9px; }
.hud__power { color:#d9eeff; letter-spacing:.05em; }.hud__power--active { background:linear-gradient(90deg,#8c5cd1,#ff8d32); color:#fff; animation:pulse .7s ease-in-out infinite alternate; }
/* Hype popup. top:42% put it dead centre — directly over Buddy and the gap
   he was flying into — at clamp(25px,8vw,42px), which on a phone is most of
   the screen width. Now these fire roughly every 8 passes instead of at
   rare milestones, that position and size would be in the way constantly,
   so it moves up to 27% (clear of the HUD above it and of the mid-field
   where the player actually flies) and drops to roughly half the type size.
   No background plate and no dimming: text plus its existing shadow only,
   so whatever is behind it stays readable. */
.moment { position:absolute; left:50%; top:27%; z-index:8; transform:translate(-50%,-50%) scale(.82); color:white; font:800 clamp(15px,3.6vw,23px) var(--font-display); letter-spacing:.02em; text-align:center; opacity:0; pointer-events:none; text-shadow:0 2px 10px color-mix(in srgb, var(--t-popup) 35%, black); transition:opacity .14s ease,transform .14s ease,text-shadow .25s ease; }
.moment--visible { opacity:1; transform:translate(-50%,-50%) scale(1); }.moment--visible.epic { color:var(--t-accent); transition: color .25s ease; }
@keyframes pulse { to { filter:brightness(1.25); transform:translateY(-2px); } }

.hud__ticker {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 18px;
  border-radius: 999px;
  background: rgba(255, 248, 237, 0.85);
  backdrop-filter: blur(10px);
  border: 1px solid var(--t-hud);
  box-shadow: var(--shadow-soft);
  font-family: var(--font-mono);
  transition: border-color 0.25s ease;
}

.hud__ticker-symbol {
  font-size: 12px;
  font-weight: 700;
  color: var(--color-bark);
  opacity: 0.6;
  letter-spacing: 0.06em;
}

.hud__ticker-arrow {
  color: var(--t-primary);
  font-size: 12px;
  transition: color 0.25s ease;
}

.hud__ticker-score {
  font-size: 22px;
  font-weight: 700;
  color: var(--t-primary);
  display: inline-block;
  transition: color 0.25s ease;
}

.hud__ticker-score--pop {
  animation: scorePop 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes scorePop {
  0% { transform: scale(1); }
  35% { transform: scale(1.45); }
  100% { transform: scale(1); }
}

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

.ready-prompt {
  position: absolute;
  top: calc(72px + env(safe-area-inset-top, 0px));
  left: 50%;
  transform: translateX(-50%) translateY(-10px);
  opacity: 0;
  z-index: 5;
  font-family: var(--font-body);
  font-weight: 800;
  color: var(--t-text);
  background: color-mix(in srgb, var(--t-popup) 85%, white);
  padding: 8px 16px;
  border-radius: 999px;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease, background 0.25s ease, color 0.25s ease;
}

.ready-prompt--visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  animation: bob 1.6s ease-in-out infinite;
}

@keyframes bob {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50% { transform: translateX(-50%) translateY(-6px); }
}

/* -------------------------------------------------------------------
   Character select
   ------------------------------------------------------------------- */
.character-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  overflow-y: auto;
  padding-bottom: 4px;
}

.character-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 16px 10px;
  border-radius: var(--radius-md);
  border: 2px solid transparent;
  background: rgba(255, 255, 255, 0.65);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 700;
  color: var(--color-bark-deep);
  transition: border-color 0.15s ease, transform 0.1s ease;
}

.character-card:hover {
  transform: translateY(-2px);
}

.character-card:active {
  transform: translateY(1px) scale(0.97);
}

.character-card--selected {
  border-color: var(--t-accent);
  /* Mixed with white (opaque), not transparent — the card must keep its
     own light, readable surface regardless of what's behind it. On a dark
     world's panel, tinting toward transparent let the dark panel bleed
     through and made the label unreadable; mixing toward white keeps this
     card exactly as legible as every unselected one, just tinted. */
  background: color-mix(in srgb, var(--t-accent) 22%, white);
  transition: border-color 0.25s ease, background 0.25s ease;
}

/* Brief confirming pop the instant a card is picked — same spring curve as
   the HUD score pop, so a selection reads as "chosen," not just recolored. */
.character-card--pop {
  animation: cardPop 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes cardPop {
  0% { transform: scale(1); }
  40% { transform: scale(1.07); }
  100% { transform: scale(1); }
}

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

.character-card__preview { width:118px; height:88px; max-width:100%; filter:drop-shadow(0 6px 7px rgba(58,42,32,.16)); }

.character-card__label {
  font-size: 14px;
}

/* -------------------------------------------------------------------
   Leaderboard
   ------------------------------------------------------------------- */
.leaderboard-pb {
  font-weight: 800;
  color: var(--t-primary);
  margin: 0 0 14px;
  transition: color 0.25s ease;
}

.leaderboard-list {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.leaderboard-row {
  display: grid;
  grid-template-columns: 36px 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.6);
  font-weight: 700;
  opacity: 0;
  transform: translateY(6px);
  animation: rowIn 0.3s ease forwards;
  animation-delay: var(--row-delay, 0s);
}

@keyframes rowIn {
  to { opacity: 1; transform: translateY(0); }
}

.leaderboard-loading {
  animation: loadingPulse 1.1s ease-in-out infinite;
}

@keyframes loadingPulse {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .leaderboard-row {
    opacity: 1;
    transform: none;
    animation: none;
  }
  .leaderboard-loading {
    animation: none;
  }
}

.leaderboard-row__rank {
  color: var(--t-secondary);
  font-family: var(--font-mono);
  font-size: 13px;
  transition: color 0.25s ease;
}

.leaderboard-row__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.leaderboard-row__char {
  font-size: 12px;
  opacity: 0.6;
  font-weight: 600;
}

.leaderboard-row__score {
  font-family: var(--font-mono);
  color: var(--t-primary);
  transition: color 0.25s ease;
}

.leaderboard-empty {
  text-align: center;
  padding: 24px 0;
  opacity: 0.6;
  font-weight: 700;
  list-style: none;
}

/* -------------------------------------------------------------------
   Settings
   ------------------------------------------------------------------- */
.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 18px;
}

.field__label {
  font-weight: 800;
  font-size: 14px;
  color: var(--t-text);
  transition: color 0.25s ease;
}

.field__input {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 700;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  border: 2px solid rgba(0, 0, 0, 0.08);
  background: rgba(255, 255, 255, 0.7);
  color: var(--color-bark-deep);
}

.field__input:focus-visible {
  outline: 3px solid var(--color-green);
  outline-offset: 1px;
}

.field__slider {
  width: 100%;
  accent-color: var(--t-primary);
  transition: accent-color 0.25s ease;
}

/* -------------------------------------------------------------------
   How to Play — a scannable card layout, not a documentation page.
   Every block is designed to be read in isolation at a glance: icon +
   number/label first, sentence (if any) second and short. The single
   .howto-feature card is the only element allowed to read as "important"
   so 88 MPH still stands out as the one big moment worth calling out.
   ------------------------------------------------------------------- */
.panel-card--howto {
  max-width: 460px;
}

.howto-body {
  overflow-y: auto;
  padding-right: 4px;
  padding-bottom: 4px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.howto-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--t-primary) 16%, transparent);
  font-size: 14px;
  flex-shrink: 0;
}

.howto-icon--inline {
  width: 18px;
  height: 18px;
  font-size: 11px;
  margin-right: 4px;
  vertical-align: -4px;
}

.howto-section h3 {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--t-secondary);
  transition: color 0.25s ease;
}

/* -- Controls / Scoring quick chips -------------------------------- */
.howto-quickrow {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.howto-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--t-primary) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--t-primary) 22%, transparent);
  /* Grid items default to min-width:auto, which refuses to shrink below
     the content's natural (unwrapped) size — the classic "grid blowout"
     that was pushing this card past the panel's right edge at narrow
     widths even with min-width:0 already set one level down on
     .howto-chip-text. Needed here too, on the grid item itself. */
  min-width: 0;
}

.howto-chip-icon {
  font-size: 20px;
  line-height: 1;
  flex-shrink: 0;
}

.howto-chip-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.howto-chip-text strong {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--t-text);
  transition: color 0.25s ease;
}

.howto-chip-text small {
  font-size: 11px;
  font-weight: 700;
  opacity: 0.7;
  color: var(--t-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.25s ease;
}

/* -- Collectible tier table ------------------------------------------ */
.howto-tier-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
}

.howto-tier-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 4px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  min-width: 0;
}

.howto-tier-card strong {
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--t-text);
  transition: color 0.25s ease;
}

.howto-tier-card small {
  font-size: 9px;
  font-weight: 700;
  white-space: nowrap;
  letter-spacing: 0.02em;
  opacity: 0.75;
  color: var(--t-text);
  transition: color 0.25s ease;
}

.howto-tier-dot,
.howto-world-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
}

.howto-tier-dot--common { background: #D8CDB4; }
.howto-tier-dot--uncommon { background: #BFE3FF; }
.howto-tier-dot--rare { background: #C6674B; }
.howto-tier-dot--golden { background: #FFD24C; box-shadow: 0 0 6px #FFD24C, 0 0 0 2px rgba(255, 255, 255, 0.5) inset; }

.howto-microcopy {
  margin: 8px 0 0;
  font-size: 11.5px;
  font-weight: 700;
  opacity: 0.72;
  color: var(--t-text);
  display: flex;
  align-items: center;
  transition: color 0.25s ease;
}

/* -- 88 MPH: the one premium highlighted feature card ---------------- */
.howto-feature {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, color-mix(in srgb, var(--t-accent) 22%, transparent), color-mix(in srgb, var(--t-primary) 14%, transparent));
  border: 1px solid color-mix(in srgb, var(--t-accent) 45%, transparent);
  box-shadow: 0 0 22px color-mix(in srgb, var(--t-accent) 25%, transparent);
}

.howto-feature-icon {
  font-size: 30px;
  line-height: 1;
  flex-shrink: 0;
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--t-accent) 70%, transparent));
}

.howto-feature-text h3 {
  margin: 0 0 3px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--t-secondary);
  transition: color 0.25s ease;
}

.howto-feature-text p {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--t-text);
  transition: color 0.25s ease;
}

/* -- Character worlds: dense icon grid -------------------------------- */
.howto-world-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}

.howto-world-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  /* Same grid/flex shrink fix as .howto-chip above — without this the
     row's unwrapped content (name + world) forced the card past the
     panel's edge on narrow screens. */
  min-width: 0;
}

.howto-world-chip strong {
  min-width: 0;
  flex-shrink: 1;
  font-size: 12px;
  color: var(--t-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.25s ease;
}

.howto-world-chip small {
  flex-shrink: 0;
  white-space: nowrap;
  font-size: 10px;
  font-weight: 700;
  opacity: 0.65;
  color: var(--t-text);
  transition: color 0.25s ease;
}

.howto-tip {
  margin: 0;
  padding-top: 2px;
  font-size: 11.5px;
  font-weight: 700;
  opacity: 0.72;
  color: var(--t-text);
  display: flex;
  align-items: center;
  transition: color 0.25s ease;
}

kbd {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--t-text) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--t-text) 25%, transparent);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
}

/* -------------------------------------------------------------------
   Game Over
   ------------------------------------------------------------------- */
.gameover-title {
  font-family: var(--font-display);
  font-size: 30px;
  margin: 0 0 4px;
  color: var(--t-text);
  transition: color 0.25s ease;
}

.gameover-newbest {
  font-weight: 800;
  color: var(--t-secondary);
  margin: 0 0 12px;
  opacity: 0;
  height: 0;
  overflow: hidden;
  transition: color 0.25s ease;
}

.gameover-newbest.is-visible {
  opacity: 1;
  height: auto;
  margin-bottom: 12px;
}

.gameover-stats {
  display: flex;
  justify-content: center;
  gap: 28px;
  margin-bottom: 24px;
}

.gameover-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.gameover-stat__label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.6;
}

.gameover-stat__value {
  font-family: var(--font-mono);
  font-size: 28px;
  font-weight: 700;
  color: var(--t-primary);
  transition: color 0.25s ease;
}

/* -------------------------------------------------------------------
   World themes — each is a small override of the eight --t-* roles
   declared at :root (see the comment there for what each role drives).
   Every component above reads var(--t-*) unconditionally, so a theme is
   nothing more than these eight values changing; no component rule is
   ever duplicated per-theme, which is what keeps all seven worlds
   speaking the same design language instead of drifting independently.
   ------------------------------------------------------------------- */
#app.theme-ice {
  --t-primary: #1F6E96;
  --t-secondary: #1F6E96;
  --t-accent: #7FD4FB;
  --t-hud: rgba(95, 195, 238, 0.65);
  --t-button: #5FC3EE;
  --t-popup: #E6F5FD;
  --t-border: rgba(150, 210, 240, 0.7);
  --t-text: #1F3A4A;
}

#app.theme-wizard {
  --t-primary: #6B3FA8;
  --t-secondary: #6B3FA8;
  --t-accent: #C9A6FF;
  --t-hud: rgba(160, 110, 230, 0.65);
  --t-button: #B98DE0;
  --t-popup: #F0E6FD;
  --t-border: rgba(190, 150, 240, 0.7);
  --t-text: #3A2856;
}

#app.theme-movie {
  /* Recolored to match the world's "elegant green, white, subtle gold"
     art direction (see background.js's Movie World palette) — was a
     burgundy/plum brand color left over from an earlier red-carpet-only
     treatment, which no longer matches the emerald cinema/arena backdrop. */
  --t-primary: #1E7A4C;
  --t-secondary: #FFD24C;
  --t-accent: #FFD24C;
  --t-hud: rgba(232, 164, 0, 0.65);
  --t-button: #E8A400;
  --t-popup: #12241A;
  --t-border: rgba(255, 210, 76, 0.55);
  --t-text: #EAF6EC;
}

#app.theme-heaven {
  --t-primary: #C98A2C;
  --t-secondary: #C98A2C;
  --t-accent: #FFE9A8;
  --t-hud: rgba(255, 210, 130, 0.7);
  --t-button: #E0A030;
  --t-popup: #FFFAEE;
  --t-border: rgba(255, 210, 130, 0.7);
  --t-text: #6B4E1E;
}

#app.theme-nightcity {
  /* Was hot pink (#FF2E92) on --t-secondary/--t-accent — read as a cheap
     neon-sign cliche rather than "luxury skyline". Swapped for a chrome/
     silver heading and an electric-violet accent so the palette stays
     cyan + silver + a single cool neon, never pink. */
  --t-primary: #00C2D6;
  --t-secondary: #E4EDF5;
  --t-accent: #8C7CFF;
  --t-hud: rgba(0, 229, 255, 0.6);
  --t-button: #00E5FF;
  --t-popup: #0C0E18;
  --t-border: rgba(0, 229, 255, 0.4);
  --t-text: #CFEFFF;
}

#app.theme-darkforest {
  --t-primary: #2E7D52;
  --t-secondary: #7FE6A8;
  --t-accent: #B8FFC9;
  --t-hud: rgba(120, 200, 160, 0.55);
  --t-button: #4FAE7C;
  --t-popup: #101A20;
  --t-border: rgba(120, 200, 160, 0.4);
  --t-text: #CFE9D6;
}

/* -------------------------------------------------------------------
   Responsive: landscape phones / short viewports
   ------------------------------------------------------------------- */
@media (max-height: 560px) {
  .menu-card, .panel-card {
    padding: 20px 22px;
  }
  .game-title__accent { font-size: 36px; }
  .game-title__main { font-size: 26px; }
  .game-subtitle { margin-bottom: 16px; }
  .menu-buttons { gap: 8px; }
  .btn { padding: 10px 16px; font-size: 15px; }
  .panel-card { max-height: 90dvh; }

  /* .panel-card bounds itself to the viewport (above), but .menu-card never
     did — so on a short landscape phone the main menu's five buttons ran
     taller than the screen and the last one, "How to Play", sat entirely
     outside it: clipped away by #app's overflow:hidden, with nothing to
     scroll and no way to reach it. Bound it the same way as a safety net at
     any height. */
  .menu-card {
    max-height: 90dvh;
    overflow-y: auto;
    /* Explicit, even though touch-action:none now lives on the canvas
       rather than on #app: this box is the one part of the menu that is
       meant to pan under a finger, so state that outright rather than
       leaving it to inherit. */
    touch-action: pan-y;
    -webkit-overflow-scrolling: touch;
  }
}

/* Landscape phones proper (~320-430px tall). The 560px rule above already
   compresses the menu, but at 320px five buttons plus the title still came
   to ~377px — taller than the whole viewport. This tightens the menu chrome
   just enough that everything FITS rather than relying on the scroll
   fallback, which is the better outcome on a device where scrolling a menu
   is awkward. Scoped to short viewports only: nothing above 430px tall,
   i.e. no phone in portrait and no tablet/desktop, is affected. */
@media (max-height: 430px) {
  .menu-card, .panel-card { padding: 14px 20px; }
  .game-title__accent { font-size: 27px; }
  .game-title__main { font-size: 19px; }
  .game-subtitle { margin-bottom: 8px; font-size: 13px; }
  .menu-buttons { gap: 6px; }
  .btn { padding: 7px 14px; font-size: 14px; }
}

@media (min-width: 720px) {
  #app {
    width: 480px;
    max-width: 480px;
    /* Was 90dvh AND top: 5dvh — flex centering on html/body (below)
       already centers this vertically, so that extra top offset pushed
       the whole cabinet down by an additional 5dvh: a lopsided ~10dvh gap
       above and ~0 below, reading as "detached from the top of the
       browser" instead of framed. Centering now does the actual
       centering, and the height climbed from 90dvh to 96dvh to use far
       more of the available browser height while still leaving a
       deliberate, symmetric frame (not edge-to-edge, not stretched).
       Explicit max-height/min-height pin real min/max pixel bounds so it
       never regresses at extreme viewport heights either. */
    height: 96dvh;
    max-height: 980px;
    /* The floor must never exceed the viewport itself. A flat `min-height:
       560px` won this cascade on a landscape phone that is wider than the
       720px breakpoint but only ~390px tall, forcing a 560px cabinet into a
       390px viewport: 170px hung off the bottom, the page gained a vertical
       scrollbar, and the cabinet stopped being vertically centred — the
       overhang reading as a band under the game. Capping the floor at the
       viewport height keeps the cabinet fully on screen instead of relying
       on overflow to crop it. Inert at every normal size: the viewport is
       taller than 560px there, so this still resolves to exactly 560px.
       The plain values above are the fallback for browsers without min()
       or dvh. */
    min-height: 560px;
    min-height: min(560px, 100vh);
    min-height: min(560px, 100dvh);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: 0 20px 60px rgba(0,0,0,0.25);
  }
  /* Only ONE element does the centering. Previously html AND body were both
     flex containers with the same centering, so the cabinet was centred
     twice inside two differently-sized boxes that each painted their own
     background — the source of the mismatched layer underneath it. body
     alone spans the viewport (see its width/min-height above) and centres
     the cabinet in it; html is left as a plain block behind it. */
  body {
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

/* Respect reduced-motion preferences */
@media (prefers-reduced-motion: reduce) {
  .ready-prompt--visible {
    animation: none;
  }
  .btn {
    transition: none;
  }
}
