/* ===========================================================================
   RAD GATO — styles
   Retro-80s "radical" neon aesthetic, pulled from the reference painting:
   hot pink, cyan, neon green, sunset orange/yellow, purple. Bold outlines.
   =========================================================================== */

:root {
  --pink: #ff2fb9;
  --magenta: #d6178f;
  --cyan: #21e6ff;
  --green: #b6ff2b;
  --orange: #ff7a1a;
  --yellow: #ffe11a;
  --purple: #a12bff;
  --blue: #2b6bff;
  --ink: #17021a;
  --white: #fff6fb;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;   /* no rubber-band drag on iOS */
  background: #0c0703;
  color: var(--white);
  font-family: "Arial Black", "Arial", system-ui, sans-serif;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;
}

#app {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
}

#game {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

.hidden { display: none !important; }

/* ------------------------------------------------------------------ HUD --- */
#hud {
  position: absolute;
  top: 0; left: 0; right: 0;
  /* Clears BOTH the iOS status bar (safe-area) and the film-frame border. The
     HUD also sits ABOVE #frame (z-index below) so the frame's black top border
     can never clip the pills again — that was why the icons looked cut off. */
  padding: max(34px, calc(env(safe-area-inset-top) + 16px)) 22px 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
  z-index: 16;
}
.hud-row { display: flex; gap: 7px; align-items: center; }
.hud-pill {
  white-space: nowrap;      /* "NIVEL 10" must never wrap to two lines */
  flex: 0 0 auto;
  font-size: clamp(13px, 2.6vw, 22px);
  font-weight: 900;
  letter-spacing: 0.5px;
  padding: 6px 16px;
  border-radius: 6px 14px 6px 14px;
  background: rgba(12, 7, 3, 0.72);
  border: 2px solid rgba(255, 180, 90, 0.4);
  color: #ffe4b0;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(2px);
}
#hud-score {
  margin-left: auto;
  color: var(--yellow);
  border-color: var(--yellow);
}
.hud-pill.combo {
  color: var(--cyan);
  border-color: var(--cyan);
  transition: transform 0.12s ease;
}
.hud-pill.combo.pop { transform: scale(1.35) rotate(-4deg); }

/* The interactive HUD buttons opt back into pointer events (the HUD itself is
   pointer-events:none). */
.hud-pill.mute, .hud-pill.narrate {
  font-family: inherit;
  cursor: pointer;
  pointer-events: auto;
  padding: 6px 12px;
  line-height: 1;
}
.hud-pill.mute { color: var(--green); border-color: var(--green); }
.hud-pill.narrate { color: var(--purple); border-color: var(--purple); }
.hud-pill.mute.off, .hud-pill.narrate.off {
  color: rgba(255, 255, 255, 0.42);
  border-color: rgba(255, 255, 255, 0.28);
}
/* The lives pill: paw + count, kept tight so nine never wraps or overflows. */
.hud-pill.lives { color: #ffd0a0; border-color: rgba(255, 150, 90, 0.6); }

/* ------------------------------------------------------- Now playing --- */
#now-playing {
  position: absolute;
  left: 24px;
  bottom: max(70px, calc(env(safe-area-inset-bottom) + 70px));
  z-index: 6;
  pointer-events: none;
  font-size: clamp(10px, 2vw, 13px);
  font-weight: 700;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--cyan);
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.9), 0 0 14px rgba(33, 230, 255, 0.6);
}
#now-playing.show { animation: np 4.5s ease forwards; }
@keyframes np {
  0%   { opacity: 0; transform: translateX(-12px); }
  10%  { opacity: 1; transform: translateX(0); }
  78%  { opacity: 1; }
  100% { opacity: 0; }
}

#hud-progress {
  height: 12px;
  border-radius: 999px;
  background: rgba(23, 2, 26, 0.55);
  border: 2px solid rgba(255, 255, 255, 0.35);
  overflow: hidden;
}
#hud-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: linear-gradient(90deg, #ff8a1a, #ffd24d);
  box-shadow: 0 0 12px #ff8a1a;
  transition: width 0.25s ease;
}

/* ------------------------------------------------- Sentence-level HUD --- */
/* The Spanish sentence assembling one word per ramp. Locked-in words glow;
   the word you're currently jumping for pulses; the rest stay as dots. */
/* Anchored to the BOTTOM like subtitles: the top band is already busy with the
   HUD, the English prompt and the world-projected word bubbles. */
#sentence-bar {
  position: fixed;
  left: 50%;
  bottom: max(22px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  width: min(96%, 1100px);
  text-align: center;
  pointer-events: none;
  z-index: 6;
  /* Dylan kept missing the sentence entirely. It now sits on its own plate so
     it reads as a thing to look at rather than text lying on the artwork. */
  padding: 8px 14px 10px;
  border-radius: 16px;
  background: rgba(8, 4, 2, 0.72);
  backdrop-filter: blur(3px);
}
#sentence-words {
  font-size: clamp(34px, 7.6vw, 52px);
  font-weight: 900;
  letter-spacing: 0.02em;
  line-height: 1.35;
  text-shadow: 0 2px 0 var(--ink), 0 0 18px rgba(0, 0, 0, 0.8);
}
#sentence-words .sw { margin: 0 0.14em; }
#sentence-words .sw.done {
  color: var(--green);
  text-shadow: 0 2px 0 var(--ink), 0 0 16px var(--green);
}
#sentence-words .sw.now {
  color: var(--yellow);
  text-shadow: 0 2px 0 var(--ink), 0 0 20px var(--yellow);
  animation: swpulse 0.9s ease-in-out infinite;
}
#sentence-words .sw.todo { color: rgba(255, 255, 255, 0.35); }
@keyframes swpulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}
#sentence-en {
  margin-top: 7px;
  font-weight: 800;
  font-size: clamp(24px, 5.6vw, 34px);
  letter-spacing: 0.01em;
  line-height: 1.2;
  color: var(--cyan);
  text-shadow: 0 2px 0 var(--ink), 0 0 16px rgba(33, 230, 255, 0.55),
               0 2px 10px rgba(0, 0, 0, 0.95);
}

/* The sentences you completed, on the level-complete card. A sentence level
   finishes four of them, so this list has to stay compact and scrollable. */
#overlay-body .sentence-win {
  font-size: clamp(15px, 2.9vw, 21px);
  font-weight: 900;
  line-height: 1.15;
  color: var(--green);
  text-shadow: 0 2px 0 var(--ink), 0 0 16px var(--green);
  margin: 7px 0 0;
}
#overlay-body .sentence-win-en {
  font-style: italic;
  font-size: clamp(10px, 1.9vw, 12px);
  line-height: 1.25;
  color: var(--cyan);
  margin-bottom: 0;
}

/* --------------------------------------------------------------- Prompt --- */
/* Pinned directly under the HUD as a compact bar rather than floating at a
   percentage depth. The word bubbles are world-projected, so a free-floating
   prompt could and did drift on top of them on shorter windows. Now it owns a
   fixed strip at the top and game.js keeps the bubbles below it. */
#prompt {
  position: absolute;
  /* BOTTOM of the screen (Dylan: "it's hard to look up and then read the
     middle") — your eyes stay on the cat/bubbles band and the prompt sits in
     thumb territory, above the sentence/rush bar. */
  top: auto;
  /* Lift clears the sentence bar. That bar's height is content-dependent — a
     long English gloss wraps to two lines and it grows by ~50px — so the old
     fixed 12vh let it rise INTO the prompt. Both sit at z-index 6 and the bar
     paints later, so its translucent blurred plate landed on top of the prompt
     word and washed it out. `--sent-lift` is measured live in game.js. */
  bottom: var(--sent-lift, calc(max(22px, env(safe-area-inset-bottom)) + 12vh));
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 8px 24px;
  border-radius: 8px 20px 8px 20px;
  /* This is THE most important thing on screen — it tells you what to pick.
     Near-solid plate + bright border so it can never blend into the backdrop. */
  background: rgba(8, 4, 2, 0.92);
  border: 3px solid var(--yellow);
  box-shadow: 0 0 26px rgba(255, 225, 26, 0.55), 0 6px 20px rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(3px);
  text-align: center;
  white-space: nowrap;
  z-index: 6;
  pointer-events: none;
  animation: prompt-in 0.25s ease;
}
@keyframes prompt-in {
  from { opacity: 0; transform: translateX(-50%) translateY(-14px) scale(0.9); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
.prompt-label {
  font-size: clamp(11px, 2vw, 15px);
  letter-spacing: 2px;
  color: var(--yellow);
  text-shadow: 0 0 10px #ff8a1a;
}
.prompt-word {
  /* Capped against viewport HEIGHT too — a wide, short window used to blow this
     up to 54px and push the bar down into the answer bubbles. */
  font-size: clamp(34px, min(9.5vw, 8vh), 64px);
  /* The curriculum has real prompts now ("TO PRESCRIBE", "THE WITHDRAWAL") —
     anything long must shrink instead of clipping at the card's edge. */
  max-width: 100%;
  overflow-wrap: anywhere;
  line-height: 1.02;
  font-weight: 900;
  color: #fffdf2;
  text-transform: uppercase;
  letter-spacing: 1px;
  /* Dylan's screenshot: the old 3px stroke + brown offset + yellow glow per
     glyph turned the word into unreadable mud on a phone. The PLATE carries
     the drama now; the word itself is just crisp white on dark. */
  -webkit-text-stroke: 2px var(--ink);
  paint-order: stroke fill;
  text-shadow: 0 3px 0 rgba(23, 2, 26, 0.7);
}

/* --------------------------------------------------------- Trick popup --- */
#trick-popup {
  position: absolute;
  /* BELOW the answer row. The popup lingers ~1.15s, which can overlap the next
     question, so at 30% it sat directly on top of the words. */
  top: 64%;
  left: 50%;
  z-index: 7;
  pointer-events: none;
  transform: translate(-50%, -50%);
  text-align: center;
  white-space: nowrap;
}
#trick-popup .tname {
  display: block;
  font-size: clamp(28px, 9vw, 56px);
  font-weight: 900;
  font-style: italic;
  letter-spacing: 1px;
  color: var(--yellow);
  -webkit-text-stroke: 2px var(--ink);
  text-shadow: 0 3px 0 var(--magenta), 0 0 22px rgba(255, 225, 26, 0.85);
}
#trick-popup .tpts {
  display: block;
  font-size: clamp(20px, 6vw, 34px);
  font-weight: 900;
  color: var(--green);
  -webkit-text-stroke: 1.5px var(--ink);
  text-shadow: 0 0 16px rgba(182, 255, 43, 0.9);
}
#trick-popup.bad .tname {
  color: var(--pink);
  text-shadow: 0 3px 0 var(--ink), 0 0 22px rgba(255, 47, 185, 0.9);
}
#trick-popup.pop { animation: trick-pop 1.15s cubic-bezier(0.2, 0.9, 0.3, 1) forwards; }
@keyframes trick-pop {
  0%   { opacity: 0; transform: translate(-50%, -30%) scale(0.5) rotate(-8deg); }
  14%  { opacity: 1; transform: translate(-50%, -50%) scale(1.15) rotate(-4deg); }
  30%  { transform: translate(-50%, -50%) scale(1) rotate(-3deg); }
  75%  { opacity: 1; transform: translate(-50%, -60%) scale(1) rotate(-3deg); }
  100% { opacity: 0; transform: translate(-50%, -90%) scale(0.95) rotate(-3deg); }
}

/* ------------------------------------------------------------ On fire --- */
#fire-banner {
  position: absolute;
  top: 21%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 8;
  pointer-events: none;
  white-space: nowrap;
  font-size: clamp(24px, 7vw, 52px);
  font-weight: 900;
  font-style: italic;
  letter-spacing: 1px;
  color: #ffe11a;
  -webkit-text-stroke: 2px var(--ink);
  text-shadow: 0 0 18px #ff7a1a, 0 0 36px #ff2f00, 0 3px 0 #b3200a;
}
#fire-banner.pop { animation: fire-pop 1.3s cubic-bezier(0.2, 0.9, 0.3, 1) forwards; }
@keyframes fire-pop {
  0%   { opacity: 0; transform: translateX(-50%) scale(0.4) rotate(-6deg); }
  15%  { opacity: 1; transform: translateX(-50%) scale(1.2) rotate(3deg); }
  30%  { transform: translateX(-50%) scale(1) rotate(-2deg); }
  75%  { opacity: 1; transform: translateX(-50%) scale(1) rotate(-2deg); }
  100% { opacity: 0; transform: translateX(-50%) scale(1.05) translateY(-24px); }
}

/* -------------------------------------------------------------- Overlay --- */
#overlay {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Top padding must clear the notch / Dynamic Island AND the 36px that #hero
     pulls itself above the card edge — on a real iPhone the mascot's head was
     being sliced off by the island. Only visible on device, not in a browser. */
  /* The bottom inset keeps the card's sticky button out of the very bottom of
     the screen. At 24px the ¡VAMOS! button landed ~93% down the display, below
     the comfortable thumb zone and hard against the browser chrome. */
  padding: max(40px, calc(env(safe-area-inset-top) + 44px)) 24px
           max(76px, calc(env(safe-area-inset-bottom) + 64px));
  background: radial-gradient(120% 120% at 50% 25%, rgba(255,150,40,0.28), rgba(12,7,3,0.9));
  backdrop-filter: blur(2px);
}
#overlay.hidden { display: none; }
.overlay-card {
  width: min(440px, 92vw);
  /* 100% of the overlay's PADDING BOX, not 92vh. At 92vh the card was taller
     than the space the overlay's padding reserved, so it overhung the bottom
     inset and dragged the sticky button back down toward the screen edge. */
  max-height: 100%;
  overflow-y: auto;          /* the 4-sentence payoff card can outgrow the screen */
  /* Nothing in this card may ever scroll sideways. A single over-wide child
     (a long button label, a bleeding pseudo-element) used to drag the whole
     card off-centre and clip its text against the screen edge. */
  overflow-x: hidden;
  /* Room for the sticky START bar to sit over, so the last real control can
     always scroll fully clear of it instead of hiding underneath. */
  scroll-padding-bottom: 96px;
  overscroll-behavior: contain;
  /* No visible scrollbar. .overlay-foot spans the card by cancelling its 22px
     padding with negative margins, and that arithmetic is against the PADDING
     box — a classic scrollbar steals width from the content box only, so the
     bar came out ~16px too wide and the card scrolled sideways, which is the
     one thing it must never do. The card already tells you it scrolls with its
     own "more, scroll" hint. */
  scrollbar-width: none;
  text-align: center;
  background: rgba(18, 10, 4, 0.78);
  border: 3px solid #ffb84d;
  border-radius: 22px;
  /* The sticky foot cancels these to span the card, so they must be ONE source
     of truth. The mobile query used to drop the padding to 14px while the foot
     kept subtracting 22px, and the card scrolled 8px sideways at every phone
     size — the exact thing the note above says must never happen. */
  --card-pad-x: 22px;
  --card-pad-b: 34px;
  padding: 26px var(--card-pad-x) var(--card-pad-b);
  box-shadow: 0 0 40px rgba(255, 138, 26, 0.45), inset 0 0 30px rgba(255, 180, 90, 0.1);
}
.overlay-card::-webkit-scrollbar { width: 0; height: 0; }

#hero {
  display: block;
  /* No negative margin: the card scrolls (overflow-y:auto), so anything above
     its edge gets CLIPPED — that was the cat's missing head on the title. */
  margin: 4px auto 0;
  max-height: 16vh;
  max-width: 34%;
  filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.45));
  pointer-events: none;
}
#hero.hidden { display: none; }

/* The wordmark is artwork now (assets/title/wordmark.webp), so the bundled
   display font is gone with it — Dylan's note was that a font could not carry
   the chrome-and-neon look, and he was right. These rules only still apply to
   any TEXT .logo, of which the game currently has none. */
.logo {
  font-size: clamp(28px, 7.5vw, 44px);
  line-height: 0.88;
  font-weight: 900;
  letter-spacing: 1px;
  color: var(--yellow);
  -webkit-text-stroke: 3px var(--ink);
  paint-order: stroke fill;  /* stroke OUTSIDE the glyph, not eating into it */
  text-shadow: 0 5px 0 var(--magenta), 0 0 26px rgba(255, 225, 26, 0.6);
}
/* The wordmark is a transparent WebP, so the type styling above no longer
   applies to it — it only still governs any text .logo left in the DOM. */
.logo:has(.wordmark) {
  -webkit-text-stroke: 0;
  text-shadow: none;
  line-height: 0;
}
.wordmark {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 auto;
  /* The art carries its own neon; this only seats it on the poster. */
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.55));
}
.logo span { color: var(--cyan); display: block; }
.tagline {
  margin-top: 6px;
  font-size: clamp(12px, 2.6vw, 15px);
  font-weight: 700;
  color: var(--white);
  opacity: 0.9;
  letter-spacing: 0.5px;
}
#overlay-body {
  margin: 8px 0 4px;
  /* Clearance for .overlay-foot's own negative bottom margin. That margin
     cancels the card's padding so the footer's background reaches the card's
     rounded corner, but a sticky element's stuck position is computed against
     its margin box — the same negative margin also pulls the footer UP by
     that amount versus where normal flow would put it. With no scrolling
     needed (a short card, like a level-complete with no bonus lines) that
     pull-up landed the footer 12px into the last line of body content, e.g.
     "+135 XP ... días seguidos" behind "Up next: ...". This buffer is exactly
     what the footer steals, so it cancels out regardless of what the last
     line of body content happens to be.
  */
  padding-bottom: var(--card-pad-b);
  font-size: clamp(13px, 3.6vw, 16px);
  font-weight: 700;
  line-height: 1.5;
  color: var(--white);
}
#overlay-body .big {
  font-size: clamp(30px, 9vw, 46px);
  color: var(--green);
  font-weight: 900;
  text-shadow: 0 0 18px rgba(182,255,43,0.7);
}
#overlay-body .stat { color: var(--cyan); }
/* ------------------------------------------------------- Level select --- */
.lvl-select { margin-top: 10px; }
.lvl-select-h {
  font-size: 11px;
  letter-spacing: 3px;
  color: #ffcf7a;
  margin-bottom: 8px;
}
/* One compact row per level so all 7 plus the START button stay above the
   fold — a level picker you have to scroll to reach defeats the point. */
.lvl-grid { display: flex; flex-direction: column; gap: 4px; }
.lvl-btn {
  font-family: inherit;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  padding: 5px 10px;
  border-radius: 4px 10px 4px 10px;
  background: rgba(12, 7, 3, 0.6);
  border: 2px solid rgba(255, 180, 90, 0.4);
  color: var(--white);
  text-align: left;
  transition: transform 0.08s ease, border-color 0.08s ease;
}
.lvl-btn:hover  { border-color: var(--cyan); transform: translateX(3px); }
.lvl-btn:active { transform: translateX(0); }
.lvl-btn .lvl-n {
  font-size: 13px;
  font-weight: 900;
  color: var(--yellow);
  min-width: 12px;
}
.lvl-btn .lvl-name {
  font-size: 11px;
  font-weight: 900;
  flex: 1;
}
.lvl-btn .lvl-sport {
  font-size: 8px;
  letter-spacing: 1.2px;
  color: var(--cyan);
}
/* Locked rows (star-gated bonus levels): visibly a price tag, not a button. */
.lvl-btn.locked {
  cursor: default;
  opacity: 0.55;
  border-style: dashed;
}
.lvl-btn.locked:hover { border-color: rgba(255, 180, 90, 0.4); transform: none; }
.lvl-btn.locked .lvl-sport { color: var(--yellow); font-weight: 900; }

/* ------------------------------------------------- The permanent record --- */
/* Streak / XP / stars chips on the title screen. */
/* ------------------------------------------------ Nine Lives streak box --- */
/* The streak economy strip: hearts, buy-back, revive, the APUESTA, share.
   Compact by design: it sits between the meta chips and the pitch line, so it
   must read as one glanceable band, not a settings page. */
.streak-box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin: 2px 0 10px;
}
.lives-row {
  width: 100%;
  text-align: center;
  font-size: clamp(13px, 3vw, 17px);
  letter-spacing: 2px;
  line-height: 1;
}
.streak-btn {
  font-family: inherit;
  font-weight: 900;
  font-size: clamp(10px, 2.2vw, 12px);
  letter-spacing: 0.5px;
  color: var(--cyan);
  background: rgba(33, 230, 255, 0.08);
  border: 2px solid rgba(33, 230, 255, 0.55);
  border-radius: 999px;
  padding: 5px 12px;
  cursor: pointer;
}
.streak-btn.hot {
  color: var(--yellow);
  border-color: var(--yellow);
  background: rgba(255, 225, 26, 0.1);
}
.streak-btn:disabled {
  opacity: 0.38;
  cursor: default;
}
.wallet-note {
  width: 100%;
  text-align: center;
  font-size: 10px;
  letter-spacing: 2px;
  color: rgba(255, 246, 251, 0.55);
}
.streak-note.lifespent { color: #ff9db0; }
/* The near-miss. Deliberately the loudest thing on the title screen when it
   appears: the streak is dying and the player has to choose. */
.rescue {
  width: 100%;
  padding: 12px 14px;
  margin-bottom: 8px;
  border-radius: 14px;
  border: 2px solid var(--pink);
  background: rgba(255, 47, 185, 0.12);
  box-shadow: 0 0 22px rgba(255, 47, 185, 0.35);
  display: flex; flex-direction: column; align-items: center; gap: 7px;
}
.rescue-t {
  font-family: inherit; font-weight: 900;
  font-size: clamp(12px, 3.2vw, 15px);
  letter-spacing: 0.5px; color: var(--pink); text-wrap: balance;
}
.rescue-b { font-size: 12px; color: var(--white); opacity: 0.9; }
.streak-btn.ghost {
  color: rgba(255,246,251,0.55);
  border-color: rgba(255,246,251,0.28);
  background: none;
}
/* Daily goal: a day only counts toward the streak once this fills. */
.goal-row {
  width: 100%; display: flex; flex-direction: column; align-items: center; gap: 4px;
  font-size: 10px; letter-spacing: 1.5px; font-weight: 700;
  color: rgba(255,246,251,0.7);
}
.goal-row.done { color: var(--green); }
.goal-bar {
  width: min(220px, 70%); height: 7px; border-radius: 999px;
  background: rgba(255,255,255,0.14); overflow: hidden;
}
.goal-bar i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--orange), var(--yellow));
  transition: width 0.4s ease;
}
.goal-row.done .goal-bar i { background: linear-gradient(90deg, var(--green), var(--cyan)); }
/* The one number on this screen that means "I am learning". Bigger and
   brighter than the XP line under it on purpose — XP is today, this is
   everything. */
.goal-known {
  font-size: 15px;
  letter-spacing: 0.5px;
  font-weight: 900;
  color: var(--yellow);
  text-shadow: 0 0 14px rgba(255, 225, 26, 0.45);
}
.goal-known b {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1.4px;
  color: rgba(255, 246, 251, 0.72);
  text-shadow: none;
}
.goal-row.done .goal-known { color: var(--green); text-shadow: 0 0 14px rgba(182, 255, 43, 0.45); }
.guard-pitch { font-size: 10px; letter-spacing: 0.4px; opacity: 0.8; }
/* The buyable next machine in the ride picker. */
.ride-chip.buy {
  border-color: var(--green);
  color: var(--green);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  line-height: 1;
  gap: 2px;
}
.ride-chip.buy .buy-price { font-size: 9px; font-weight: 900; letter-spacing: 0.5px; }
.ride-chip.buy.broke { opacity: 0.45; }
#trick-popup.bad .tname { color: #ff9db0; }

.meta-chips {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-bottom: 8px;
}
.meta-chip {
  font-size: clamp(11px, 2.6vw, 14px);
  font-weight: 900;
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(12, 7, 3, 0.7);
  border: 2px solid rgba(255, 180, 90, 0.45);
  color: var(--white);
  white-space: nowrap;
}
.meta-chip.hot {
  border-color: var(--orange);
  box-shadow: 0 0 14px rgba(255, 122, 26, 0.55);
}
/* XP + streak earned, on the level-complete card. */
.meta-earn {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 14px;
  margin-top: 10px;
  flex-wrap: wrap;
}
.meta-earn .xp-gain {
  font-size: clamp(16px, 4.4vw, 24px);
  font-weight: 900;
  color: var(--yellow);
  text-shadow: 0 0 14px rgba(255, 225, 26, 0.7);
}
.meta-earn .streak-note {
  font-size: clamp(12px, 3vw, 16px);
  font-weight: 800;
  color: #ffd0a0;
}
.meta-earn .streak-note.grew {
  color: var(--orange);
  text-shadow: 0 0 12px rgba(255, 122, 26, 0.7);
}
.bonus-unlocked {
  margin-top: 8px;
  font-size: clamp(12px, 3vw, 16px);
  font-weight: 900;
  color: var(--green);
  text-shadow: 0 0 14px rgba(182, 255, 43, 0.7);
}
/* GIRA DE ESTRELLAS: the star-gated bonus row on the title screen. */
.bonus-select { margin-top: 10px; text-align: left; }
.bonus-select summary {
  cursor: pointer;
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 2px;
  color: var(--yellow);
  text-align: center;
  list-style: none;
}
.bonus-select summary::-webkit-details-marker { display: none; }
.bonus-select[open] summary { margin-bottom: 6px; }
.bonus-card .lvl-name { color: #ffe9c4; }

/* ------------------------------------------------- Cloud account section --- */
.acct {
  margin-top: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
}
.acct input {
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  padding: 7px 12px;
  width: min(220px, 56vw);
  border-radius: 999px;
  border: 2px solid rgba(33, 230, 255, 0.5);
  background: rgba(12, 7, 3, 0.8);
  color: var(--white);
  outline: none;
}
.acct input:focus { border-color: var(--cyan); }
.acct-btn {
  font-family: inherit;
  cursor: pointer;
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 1px;
  padding: 7px 14px;
  border-radius: 999px;
  border: 2px solid var(--cyan);
  background: rgba(33, 230, 255, 0.12);
  color: var(--cyan);
}
.acct-btn:hover { background: rgba(33, 230, 255, 0.25); }
.acct-link {
  font-family: inherit;
  cursor: pointer;
  font-size: 11px;
  font-weight: 700;
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.55);
  text-decoration: underline;
}
.acct-link:hover { color: var(--white); }
.acct-status { font-size: 12px; font-weight: 800; color: var(--cyan); }
.acct-msg {
  width: 100%;
  font-size: 11px;
  font-weight: 700;
  color: #ffd0a0;
}

/* ------------------------------------------------------- Sport select --- */
/* The landing-page ride picker: unlocked sports are playable cards, the next
   two show LOCKED (you can see what's coming), the rest are ??? mysteries. */
.sport-select { margin-top: 10px; }
.sport-select-h {
  font-size: 12px;
  letter-spacing: 3px;
  color: #ffcf7a;
  margin-bottom: 8px;
}
.sport-grid { display: flex; flex-direction: column; gap: 6px; }
.sport-btn {
  font-family: inherit;
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 7px 12px;
  border-radius: 6px 14px 6px 14px;
  background: rgba(12, 7, 3, 0.6);
  border: 2px solid rgba(255, 180, 90, 0.4);
  color: var(--white);
  text-align: left;
  min-height: 52px;
}
button.sport-btn { cursor: pointer; transition: transform 0.08s ease, border-color 0.08s ease, box-shadow 0.08s ease; }
button.sport-btn:hover  { border-color: var(--cyan); transform: translateX(3px); box-shadow: 0 0 18px rgba(33,230,255,0.25); }
button.sport-btn:active { transform: translateX(0); }
.sport-art {
  width: 46px; height: 46px;
  object-fit: contain;
  flex: 0 0 auto;
  filter: drop-shadow(0 3px 5px rgba(0,0,0,0.5));
}
.sport-txt { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.sport-name {
  font-size: 15px;
  font-weight: 900;
  letter-spacing: 0.5px;
  color: var(--yellow);
}
.sport-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.3px;
  color: rgba(255, 255, 255, 0.72);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sport-go {
  flex: 0 0 auto;
  font-size: 18px;
  color: var(--green);
  text-shadow: 0 0 10px rgba(182,255,43,0.7);
}
/* Locked-but-visible: dimmed art, a padlock, and no interactivity. */
.sport-btn.locked { opacity: 0.72; border-style: dashed; }
.sport-btn.locked .sport-art { filter: grayscale(0.7) brightness(0.7); opacity: 0.85; }
.sport-btn.locked .sport-name { color: rgba(255, 255, 255, 0.7); }
.sport-lock { flex: 0 0 auto; font-size: 18px; }
/* Mystery: a big "?" stands in for the ride entirely. */
.sport-btn.mystery { border-style: dashed; border-color: rgba(255,255,255,0.2); opacity: 0.6; }
.sport-q {
  flex: 0 0 auto;
  width: 46px;
  text-align: center;
  font-size: 30px;
  font-weight: 900;
  color: var(--cyan);
  text-shadow: 0 0 14px rgba(33,230,255,0.6);
}
.sport-btn.mystery .sport-name { color: rgba(255,255,255,0.5); letter-spacing: 3px; }

.btn {
  margin-top: 14px;
  font-family: inherit;
  font-size: clamp(17px, 4vw, 22px);
  font-weight: 900;
  letter-spacing: 2px;
  color: var(--ink);
  background: linear-gradient(180deg, var(--green), var(--cyan));
  border: 3px solid var(--white);
  border-radius: 14px;
  padding: 11px 34px;
  cursor: pointer;
  /* Long labels ("CONTINUAR · NIVEL 12") must shrink the padding rather than
     push text past the button's own edge. */
  max-width: 100%;
  padding-inline: clamp(12px, 6vw, 34px);
  box-shadow: 0 6px 0 var(--magenta), 0 10px 24px rgba(0,0,0,0.4);
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.btn:active {
  transform: translateY(4px);
  box-shadow: 0 2px 0 var(--magenta), 0 6px 16px rgba(0,0,0,0.4);
}
.hint {
  margin-top: 14px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--cyan);
  opacity: 0.75;
}

/* --------------------------------------------------- Unlock / new gear --- */
#unlock {
  position: fixed;
  inset: 0;
  z-index: 30;                 /* above the level-complete card */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: radial-gradient(120% 120% at 50% 40%, rgba(255,200,40,0.3), rgba(8,4,14,0.94));
}
#unlock.hidden { display: none !important; }
.unlock-card {
  width: min(420px, 92vw);
  max-height: 92vh;
  overflow-y: auto;
  text-align: center;
  padding: 22px 22px 20px;
  border-radius: 22px;
  background: rgba(18, 10, 4, 0.9);
  border: 3px solid var(--yellow);
  box-shadow: 0 0 60px rgba(255, 225, 26, 0.5);
}
#unlock.pop .unlock-card { animation: unlock-in 0.5s cubic-bezier(0.2, 1.1, 0.3, 1); }
@keyframes unlock-in {
  0%   { opacity: 0; transform: scale(0.6) rotate(-6deg); }
  70%  { transform: scale(1.06) rotate(2deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}
.unlock-kicker {
  font-size: 11px;
  letter-spacing: 4px;
  color: var(--cyan);
  margin-bottom: 6px;
}
#unlock-art,
#unlock-clipwrap {
  display: block;
  margin: 0 auto 4px;
  max-height: 26vh;
  max-width: 62%;
  filter: drop-shadow(0 0 22px rgba(255, 225, 26, 0.6));
  animation: unlock-float 2.4s ease-in-out infinite;
}
@keyframes unlock-float {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50%      { transform: translateY(-9px) rotate(2deg); }
}
/* The clip carries its own motion, and the bob/rotate that suits a cut-out
   PNG just makes a rectangular video look loose in the frame. Hold it still,
   give it edges, and let it run a little bigger — this is the payoff beat. */
/* The frame lives on the wrapper, not the video, so the subtitle can sit
   inside it and travel with it. */
#unlock-clipwrap {
  position: relative;
  /* inline-block so the frame shrink-wraps the video: the card is
     text-align:center, so it stays centred without a width of its own.
     max-content fought the height cap here and squashed the clip. */
  display: inline-block;
  width: auto;
  animation: none;
  max-width: 86%;
  max-height: 34vh;
  border-radius: 14px;
  border: 3px solid var(--yellow);
  box-shadow: 0 0 26px rgba(255, 225, 26, 0.55);
  filter: none;
  background: var(--ink);
  overflow: hidden;
}
#unlock-clip {
  display: block;
  max-height: calc(34vh - 6px);
  max-width: 100%;
}
#unlock-clipwrap.hidden,
#unlock-art.hidden { display: none; }
#unlock-name {
  font-size: clamp(26px, 8vw, 44px);
  font-weight: 900;
  color: var(--yellow);
  -webkit-text-stroke: 2px var(--ink);
  text-shadow: 0 4px 0 var(--magenta), 0 0 26px rgba(255, 225, 26, 0.7);
}
#unlock-blurb {
  margin: 8px 0 0;
  font-size: clamp(12px, 2.6vw, 15px);
  font-weight: 700;
  line-height: 1.45;
  color: var(--white);
  opacity: 0.9;
}

/* ----------------------------------------------------- ESTUDIA glossary --- */
/* The pre-level study screen: a GRID of cards, not a cramped scrolling list.
   Dylan's screenshot showed two columns crushed onto one line at ~15px over
   a busy photo — unreadable, his word. Every word now gets a real card, and
   tapping it opens the full-screen flashcard view (below) rather than only
   playing the sound. */
.gloss {
  margin: 10px auto;
  max-width: 480px;
  /* Roomier than 44vh so most phones show the whole lesson without scrolling
     at all; .gloss-more covers the ones that still overflow. */
  max-height: 52vh;
  overflow-y: auto;
  text-align: left;
}
.word-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 9px;
}
/* A sentence card runs long, so it claims the full row rather than being
   squeezed into half a grid cell. */
.word-grid .gloss-row.is-sentence { grid-column: 1 / -1; }
/* Soft fade on the last visible row, so the list reads as continuing rather
   than ending. Paired with the .gloss-more cue below. */
.gloss.has-more {
  -webkit-mask-image: linear-gradient(to bottom, #000 78%, rgba(0, 0, 0, 0.15));
  mask-image: linear-gradient(to bottom, #000 78%, rgba(0, 0, 0, 0.15));
}
.gloss-more {
  margin: 2px auto 8px;
  font-size: clamp(12px, 2.8vw, 15px);
  font-weight: 900;
  letter-spacing: 1.5px;
  color: var(--yellow);
  text-shadow: 0 0 12px rgba(255, 225, 26, 0.6);
  animation: glossbob 1.15s ease-in-out infinite;
}
.gloss-more b { color: var(--white); }
@keyframes glossbob {
  0%, 100% { transform: translateY(0); opacity: 0.75; }
  50%      { transform: translateY(3px); opacity: 1; }
}
.gloss-row {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 12px 34px 10px 12px;
  border: 2px solid rgba(255, 225, 26, 0.4);
  border-radius: 10px 16px 10px 16px;
  background: rgba(0, 0, 0, 0.38);
  cursor: pointer;
  min-height: 64px;
  min-width: 0;
  justify-content: center;
}
.gloss-row:active { background: rgba(255, 225, 26, 0.18); }
.gloss-row b {
  color: var(--yellow);
  /* Bigger than the old row — this is the whole point. A single word fills
     the card; a longer phrase shrinks gracefully via clamp rather than
     wrapping into the speaker button's corner. */
  font-size: clamp(16px, 4.6vw, 21px);
  font-weight: 900;
  line-height: 1.15;
  overflow-wrap: break-word;
}
.gloss-row span {
  color: var(--white);
  opacity: 0.85;
  font-size: clamp(11px, 3vw, 13px);
  overflow-wrap: break-word;
}
.gloss-speaker {
  position: absolute;
  top: 6px; right: 6px;
  width: 26px; height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 225, 26, 0.16);
  color: var(--yellow);
  font-size: 13px;
  line-height: 26px;
  padding: 0;
  cursor: pointer;
}
.gloss-speaker:active { background: rgba(255, 225, 26, 0.4); }
.gloss-hint {
  font-size: clamp(11px, 2.8vw, 13px);
  letter-spacing: 1.5px;
  color: var(--yellow);
  opacity: 0.85;
  margin: 2px 0 4px;
}
.gloss-reto {
  font-size: clamp(12px, 3vw, 14px);
  font-weight: 800;
  color: var(--magenta);
  margin: 6px 0 0;
}

/* ------------------------------------------------- Flashcard viewer --- */
/* One word/phrase, full screen, huge — "people who can't see that well" was
   the exact bar. Opens over the study grid (never replaces it) so the back
   arrow always has somewhere real to return to. */
#flashcards {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: radial-gradient(120% 100% at 50% 30%, #2a0f30, var(--ink) 75%);
  display: grid;
  /* Header and footer size to their own content; the middle row is the only
     1fr, so it is the only thing that centers — no fighting over dead space
     with absolutely-positioned siblings. */
  grid-template-rows: auto 1fr auto;
  padding: max(16px, env(safe-area-inset-top)) 0 max(16px, env(safe-area-inset-bottom));
}
.fc-head {
  position: relative;
  height: 46px;
  margin: 0 16px;
}
.fc-back {
  position: absolute;
  left: 0; top: 0;
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 2px solid var(--cyan);
  background: rgba(0, 0, 0, 0.4);
  color: var(--cyan);
  font-size: 22px;
  font-weight: 900;
  cursor: pointer;
}
.fc-back:active { background: rgba(33, 230, 255, 0.25); }
.fc-dots {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 6px;
  max-width: 60vw;
  flex-wrap: wrap;
  justify-content: center;
}
.fc-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.25);
}
.fc-dot.on { background: var(--yellow); box-shadow: 0 0 8px var(--yellow); }
/* The one row that centers. Arrows sit at its own vertical middle, which
   tracks the word block exactly — not the header/footer that flank it. */
.fc-mid {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  min-width: 0;
}
.fc-stage {
  width: 100%;
  max-width: 640px;
  min-width: 0;
  text-align: center;
  padding: 0 56px;
  display: flex;
  flex-direction: column;
  align-items: center;
  touch-action: pan-y;
}
.fc-es {
  color: var(--yellow);
  font-weight: 900;
  line-height: 1.08;
  text-shadow: 0 4px 0 var(--ink), 0 0 30px rgba(255, 225, 26, 0.5);
  /* Scales down for a long phrase, but stays enormous for a single word —
     this is the size Dylan's "bigger, for people who can't see well" note
     asked for. */
  font-size: clamp(30px, 12vw, 76px);
  overflow-wrap: break-word;
}
.fc-en {
  margin-top: 14px;
  color: var(--cyan);
  font-weight: 800;
  font-size: clamp(16px, 4.6vw, 26px);
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.8);
}
.fc-speaker {
  margin-top: 30px;
  width: 68px; height: 68px;
  border-radius: 50%;
  border: 3px solid var(--white);
  background: linear-gradient(180deg, var(--pink), var(--magenta));
  color: var(--white);
  font-size: 28px;
  cursor: pointer;
  box-shadow: 0 6px 0 rgba(0,0,0,0.35), 0 0 26px rgba(255, 47, 185, 0.5);
}
.fc-speaker:active { transform: translateY(3px); box-shadow: 0 3px 0 rgba(0,0,0,0.35); }
.fc-arrow {
  flex: 0 0 auto;
  width: 44px; height: 64px;
  border: none;
  background: rgba(255, 255, 255, 0.06);
  color: rgba(255, 255, 255, 0.6);
  font-size: 32px;
  font-weight: 900;
  cursor: pointer;
  border-radius: 14px;
}
.fc-arrow:active { background: rgba(255, 255, 255, 0.16); }
/* PLAY NOW on the final card. Sits where the eye already is (under the word,
   below the speaker) so finishing the stack and starting the level is one
   continuous motion. */
.fc-go {
  margin-top: 26px;
  padding: 13px 34px;
  font-size: 17px;
  /* The stage is a narrow flex column, so the button was wrapping to
     "PLAY / NOW". It is two words and must always read as one line. */
  white-space: nowrap;
  width: auto;
}
.fc-go.hidden { display: none; }

/* ------------------------------------------------------ Coach mark --- */
/* "TAP A CARD TO SEE IT BIG" as a branded beat over the word grid. Retires
   itself after a few uses — see game._maybeCoach. */
#coach {
  position: fixed;
  inset: 0;
  z-index: 32;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(8, 3, 14, 0.72);
  animation: coachIn 0.18s ease-out;
}
#coach.hidden { display: none; }
@keyframes coachIn { from { opacity: 0; } to { opacity: 1; } }
.coach-card {
  width: min(320px, 100%);
  text-align: center;
  padding: 24px 22px 22px;
  border-radius: 14px 26px 14px 26px;
  border: 3px solid var(--cyan);
  background: linear-gradient(180deg, #2a0f30, #14061c);
  box-shadow: 0 10px 0 rgba(0, 0, 0, 0.45), 0 0 40px rgba(33, 230, 255, 0.35);
  animation: coachPop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes coachPop {
  from { transform: scale(0.82) translateY(10px); }
  to   { transform: scale(1) translateY(0); }
}
.coach-icon {
  font-size: 40px;
  line-height: 1;
  animation: coachBob 1.4s ease-in-out infinite;
}
@keyframes coachBob {
  0%, 100% { transform: translateY(0) rotate(-8deg); }
  50%      { transform: translateY(-6px) rotate(8deg); }
}
.coach-title {
  margin-top: 10px;
  font-size: clamp(24px, 7vw, 32px);
  font-weight: 900;
  letter-spacing: 1.5px;
  color: var(--yellow);
  text-shadow: 0 3px 0 var(--ink), 0 0 24px rgba(255, 225, 26, 0.5);
}
.coach-body {
  margin: 4px 0 0;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.5px;
  color: var(--cyan);
}
.coach-card .btn { margin-top: 18px; padding: 11px 30px; font-size: 15px; }

.fc-hint {
  /* A normal grid row now, not absolutely positioned — the footer row sizes
     to it automatically, so it can never overlap the speaker button above. */
  text-align: center;
  margin-top: 8px;
  font-size: 12px;
  letter-spacing: 1.5px;
  color: rgba(255, 255, 255, 0.45);
}

/* TITLE SCREEN DENSITY. The whole first screen must fit a 6.9" phone with no
   scrolling — a player should never have to discover that START is further
   down. Language becomes a two-up segmented row, difficulty rows go slim, and
   the mascot gives up some height. */
/* The cat, small, back the way this card looked before the poster existed.
   He got enlarged to 21vh/50% for a stretch when this card WAS the hero shot
   with the synthwave world behind it; now that beat is the video cold-open
   (game._playTitleHero) and this card is reached only after it, on PLAY NOW —
   it goes back to being the compact picker, not another hero moment. */
.title-compact #hero {
  max-height: 11vh;
  max-width: 26%;
}
.title-compact .logo { font-size: clamp(30px, 9vw, 44px); line-height: 0.92; }
.overlay-card .wordmark { max-width: 78%; }
.title-compact .tagline { font-size: 12px; margin-bottom: 2px; }
/* NOTE: there used to be a `#overlay-body > p:first-child { display:none }`
   rule here to hide the old how-to-play line. That line is now a level-one
   card, and the first child is the LANGUAGE PICKER — the rule would have
   hidden it outright. */
.title-compact .lvl-select-h { margin: 8px 0 4px; font-size: 10px; }
.title-compact .diff-select { margin-bottom: 4px; }

/* language: two pills side by side instead of two full-width rows */
.lang-row { display: flex; gap: 8px; }
.lang-row .lang-btn {
  flex: 1 1 0;
  flex-direction: column;
  gap: 2px;
  align-items: center;
  text-align: center;
  padding: 7px 6px;
}
.lang-row .lang-btn .diff-icon { font-size: 20px; }
.lang-row .lang-btn .diff-name { font-size: 14px; letter-spacing: 0.5px; }
.lang-row .lang-btn .diff-tag { display: none; }
.lang-row .lang-btn[disabled] { opacity: 0.45; }

/* Difficulty tiles stay STACKED and centered here — the old rule flattened
   them back into inline one-liners, which undid the two-column tiles and made
   the ladder unreadable again. Compact now means slightly tighter, not a
   different shape. */
.title-compact .diff-btn { padding: 10px 8px; gap: 4px; min-height: 88px; }
.title-compact .diff-btn .diff-icon { font-size: 25px; }
.title-compact .diff-btn .diff-name { font-size: 15px; }
.title-compact .diff-btn .diff-tag { font-size: 10px; }
.title-compact .passport-btn { margin-top: 8px; padding: 6px 18px; font-size: 12px; }
.title-compact .legal { margin-top: 8px; font-size: 11px; }

/* START must NEVER be below the fold. On a 6.9" phone the title card (two
   pickers + passport + legal) is taller than the screen, and the button
   scrolled out of sight — a first-time player, and an App Review tester, would
   simply not find it. Sticking it to the bottom of the scroll box keeps the
   one thing you must press permanently on screen. */
/* The sticky footer BAR owns the backdrop; the button just sits in it. */
.overlay-foot {
  position: sticky;
  bottom: 0;
  z-index: 2;
  /* Cancel the card's 22px padding so the bar spans its full width, with no
     horizontal overflow (that is what caused the sideways scrolling). */
  margin: 0 calc(-1 * var(--card-pad-x)) calc(-1 * var(--card-pad-b));
  padding: 10px var(--card-pad-x) 16px;
  /* Fully opaque: at 0.97 the word rows scrolling underneath still ghosted
     through behind the button. */
  background: #120a04;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  justify-content: center;
  /* Match the card's corners: the bar sits ON the card's bottom edge, and
     square corners poked outside the rounded border. */
  border-radius: 0 0 19px 19px;
}
.overlay-foot .hint { margin: 0; }
.overlay-foot .hint:empty { display: none; }
.overlay-foot #overlay-btn { margin-top: 0; }

.overlay-card > .hint { margin-bottom: 10px; }

/* ------------------------------------------------- Question modes (MEMORIA) --- */
/* The prompt frame is the telegraph: each retrieval mode owns a colour and an
   icon, so the KIND of question reads before a single word is parsed. */
#prompt.mode-audio {
  border-color: var(--cyan);
  box-shadow: 0 0 26px rgba(33, 230, 255, 0.55), 0 6px 20px rgba(0, 0, 0, 0.6);
}
#prompt.mode-audio .prompt-label { color: var(--cyan); text-shadow: 0 0 10px var(--cyan); }
#prompt.mode-audio .prompt-word {
  color: var(--cyan);
  font-size: clamp(30px, 8vw, 46px);
  animation: hear 1.1s ease-in-out infinite;
}
/* The bilingual labels are long; the bar must shrink to fit the phone rather
   than run off both edges. */
#prompt { max-width: min(94vw, 620px); flex-wrap: wrap; justify-content: center; }
.prompt-label { white-space: normal; line-height: 1.15; }
.lbl-en {
  display: block;
  font-size: 0.72em;
  letter-spacing: 1px;
  opacity: 0.75;
  text-transform: none;
}
@keyframes hear {
  0%, 100% { opacity: 0.55; transform: scale(0.97); }
  50%      { opacity: 1;    transform: scale(1.03); }
}
#prompt.mode-rev {
  border-color: var(--purple);
  box-shadow: 0 0 26px rgba(161, 43, 255, 0.5), 0 6px 20px rgba(0, 0, 0, 0.6);
}
#prompt.mode-rev .prompt-label { color: #d9a6ff; text-shadow: 0 0 10px var(--purple); }
#prompt.mode-review { border-style: double; border-width: 5px; }

/* A word you've fumbled before, flagged on the study card. */
.gloss-row.shaky { border-color: rgba(255, 200, 64, 0.75); }
.gloss-row.shaky b { color: #ffd94d; }
.redeemed {
  margin: 6px 0 0;
  font-size: clamp(12px, 3vw, 15px);
  font-weight: 900;
  color: #ffd94d;
  text-shadow: 0 0 12px rgba(255, 196, 0, 0.6);
}

.legal { margin: 14px 0 0; font-size: 12px; opacity: .55; }
.legal a { color: var(--cyan); }

/* ------------------------------------------------------------ Passport --- */
.passport-btn {
  display: block;
  margin: 14px auto 0;
  padding: 8px 22px;
  font: 800 clamp(13px, 3.4vw, 16px) inherit;
  font-family: inherit;
  letter-spacing: 1px;
  color: var(--white);
  background: rgba(0, 0, 0, 0.4);
  border: 2px solid var(--cyan);
  border-radius: 8px 18px 8px 18px;
  cursor: pointer;
}
.passport {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
  max-width: 560px;
  max-height: 52vh;
  overflow-y: auto;
  margin: 10px auto 0;
}
.stamp {
  border: 2px dashed rgba(255, 255, 255, 0.25);
  border-radius: 50% 46% 48% 52% / 52% 48% 50% 46%;   /* inky, imperfect ring */
  padding: 14px 8px;
  transform: rotate(var(--rot, 0deg));
  color: rgba(255, 255, 255, 0.35);
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: center;
  justify-content: center;
  min-height: 84px;
}
.stamp.got {
  border: 3px double var(--magenta);
  color: var(--yellow);
  box-shadow: 0 0 14px rgba(255, 47, 185, 0.35), inset 0 0 18px rgba(255, 47, 185, 0.15);
}
.stamp-city { font-weight: 900; font-size: clamp(10px, 2.6vw, 12px); letter-spacing: 0.5px; text-align: center; }
.stamp-mark { font-size: clamp(11px, 3vw, 14px); letter-spacing: 3px; }
/* ------------------------------------------------- The passport stamp --- */
/* One timeline, four elements, all sharing IMPACT at 31%: the stamper falls
   (accelerating, so it has weight), the mark is born on contact, the stage
   takes the knock, and the ink rings outward. Dylan called the old version
   weak, and it was: a scale-down with a fade reads as a transition, not as
   something being DONE to the page. */
.stamp-stage {
  position: relative;
  margin: 6px auto 12px;
  max-width: 240px;
  animation: stamp-knock 1.15s ease-out 0.2s both;
}
.stamp-slam {
  margin: 0 auto;
  max-width: 240px;
  animation: stamp-slam 1.15s linear 0.2s both;
}
/* The stamper: b is the rubber head, i the handle above it. */
.stamp-press {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 132px;
  margin: -34px 0 0 -66px;
  transform: rotate(var(--rot, -7deg));
  pointer-events: none;
  /* `both`, not `backwards`: on `backwards` the stamper snapped back to its
     resting spot the instant the animation finished and sat there covering the
     stamp it had just made. It has to HOLD the lifted-away final frame. */
  animation: stamp-press 1.15s cubic-bezier(0.5, 0, 0.75, 0) 0.2s both;
}
.stamp-press b {
  display: block;
  height: 30px;
  border-radius: 7px;
  background: linear-gradient(#3a2340, #1d0f22);
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.55), 0 4px 10px rgba(0, 0, 0, 0.5);
}
.stamp-press i {
  display: block;
  width: 52px;
  height: 34px;
  margin: 0 auto;
  border-radius: 10px 10px 4px 4px;
  background: linear-gradient(#c98a4b, #8a5527);
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.3);
}
/* Ink shock, thrown outward on contact. */
.stamp-shock {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 190px;
  height: 88px;
  margin: -44px 0 0 -95px;
  border-radius: 999px;
  border: 3px solid var(--green);
  pointer-events: none;
  opacity: 0;
  animation: stamp-shock 1.15s ease-out 0.2s both;
}
@keyframes stamp-press {
  0%   { transform: translateY(-190px) rotate(var(--rot, -7deg)); opacity: 0; }
  10%  { opacity: 1; }
  31%  { transform: translateY(0) rotate(var(--rot, -7deg)); opacity: 1; }
  40%  { transform: translateY(-10px) rotate(var(--rot, -7deg)); opacity: 1; }
  100% { transform: translateY(-200px) rotate(var(--rot, -7deg)); opacity: 0; }
}
@keyframes stamp-slam {
  0%, 30% { transform: rotate(var(--rot)) scale(1.5); opacity: 0; }
  31%     { transform: rotate(var(--rot)) scale(0.9);  opacity: 1; }
  40%     { transform: rotate(var(--rot)) scale(1.07); opacity: 1; }
  52%     { transform: rotate(var(--rot)) scale(1);    opacity: 1; }
  100%    { transform: rotate(var(--rot)) scale(1);    opacity: 1; }
}
@keyframes stamp-knock {
  0%, 30% { transform: translate(0, 0); }
  32%     { transform: translate(-4px, 2px); }
  35%     { transform: translate(4px, -1px); }
  38%     { transform: translate(-2px, 1px); }
  42%,100%{ transform: translate(0, 0); }
}
@keyframes stamp-shock {
  0%, 30% { opacity: 0; transform: scale(0.45); }
  32%     { opacity: 0.9; transform: scale(0.6); }
  100%    { opacity: 0; transform: scale(2.5); }
}
/* Someone who asked the OS for less motion still gets the stamp, just placed
   rather than thrown at them. */
@media (prefers-reduced-motion: reduce) {
  .stamp-stage, .stamp-slam, .stamp-press, .stamp-shock { animation: none; }
  .stamp-press, .stamp-shock { display: none; }
  .stamp-slam { opacity: 1; transform: rotate(var(--rot)); }
}

/* -------------------------------------------------------------- Mobile --- */
/* Short viewports: the HUD, prompt and subtitle all compete for the same
   vertical band, so everything tightens up. */
@media (max-width: 820px) {
  #hud { padding: max(20px, calc(env(safe-area-inset-top) + 10px)) 9px 0; gap: 5px; }
  .hud-row { gap: 5px; }
  .hud-pill { padding: 4px 8px; border-width: 2px; border-radius: 5px 11px 5px 11px; font-size: clamp(11px, 3vw, 18px); }
  .hud-pill.mute, .hud-pill.narrate { padding: 4px 7px; }
  #hud-progress { height: 7px; border-width: 1px; }
  #prompt {
    /* bottom-anchored like the desktop rule — tighter above the rush bar.
       Same --sent-lift as the desktop rule: this used to hard-code 10vh, which
       ignored the measurement and let a two-line sentence bar cover the prompt
       word on exactly the phones this rule targets. */
    top: auto;
    bottom: var(--sent-lift, calc(max(22px, env(safe-area-inset-bottom)) + 10vh));
    gap: 10px;
    padding: 6px 16px;
  }
  .prompt-label { font-size: 11px; letter-spacing: 1px; }
  #sentence-bar { bottom: max(10px, env(safe-area-inset-bottom)); }
  #sentence-words { font-size: clamp(21px, 6vw, 33px); }
  #sentence-en { font-size: 14px; letter-spacing: 0.1em; }
  #now-playing { left: 10px; bottom: max(56px, calc(env(safe-area-inset-bottom) + 56px)); }
  #frame { border-width: 8px 5px 10px 5px; }
  .overlay-card { --card-pad-x: 14px; --card-pad-b: 16px; padding: 18px var(--card-pad-x) var(--card-pad-b); }
}

/* ----------------------------------------------------------- Touch hint --- */
#touch-hint {
  position: absolute;
  bottom: 12%;
  left: 0; right: 0;
  text-align: center;
  z-index: 4;
  pointer-events: none;
  font-size: clamp(13px, 4vw, 18px);
  font-weight: 900;
  letter-spacing: 2px;
  color: var(--white);
  text-shadow: 0 0 12px var(--cyan);
  animation: pulse 1.1s ease-in-out infinite;
}
@keyframes pulse { 0%,100% { opacity: 0.35; } 50% { opacity: 1; } }

/* ------------------------------------------------------- Film-frame border --- */
#frame {
  position: fixed;
  inset: 0;
  z-index: 15;
  pointer-events: none;
  border-style: solid;
  border-color: #0a0503;
  border-width: 18px 10px 24px 10px;
  box-shadow: inset 0 0 60px 0 rgba(0, 0, 0, 0.4);
}

/* ------------------------------------------------------------ Pause --- */
.hud-pill.pause {
  font-family: inherit;
  cursor: pointer;
  pointer-events: auto;
  padding: 6px 12px;
  line-height: 1;
  color: var(--cyan);
  border-color: var(--cyan);
}
#pause-screen {
  position: fixed;
  inset: 0;
  z-index: 25;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 4, 14, 0.82);
  backdrop-filter: blur(4px);
}
#pause-screen.hidden { display: none !important; }
.pause-card {
  text-align: center;
  padding: 26px 34px;
  border-radius: 22px;
  background: rgba(18, 10, 4, 0.92);
  border: 3px solid var(--cyan);
  box-shadow: 0 0 50px rgba(33, 230, 255, 0.4);
}
.pause-title {
  font-size: clamp(30px, 9vw, 52px);
  font-weight: 900;
  color: var(--cyan);
  letter-spacing: 4px;
  -webkit-text-stroke: 2px var(--ink);
  text-shadow: 0 4px 0 var(--magenta);
}

/* ------------------------------------------- Completed-sentence flash --- */
/* The whole sentence you just finished, thrown up big for a beat before the
   next question so the lesson actually lands. */
#sentence-flash {
  position: fixed;
  left: 50%;
  top: 44%;
  transform: translate(-50%, -50%);
  z-index: 18;
  pointer-events: none;
  text-align: center;
  width: min(94%, 900px);
}
#sentence-flash.hidden { display: none !important; }
#sentence-flash-es {
  font-size: clamp(26px, 7vw, 54px);
  font-weight: 900;
  line-height: 1.1;
  color: var(--green);
  -webkit-text-stroke: 2px var(--ink);
  text-shadow: 0 4px 0 var(--ink), 0 0 30px var(--green);
}
#sentence-flash-en {
  margin-top: 10px;
  font-size: clamp(19px, 4.6vw, 30px);
  font-weight: 800;
  font-style: italic;
  color: var(--cyan);
  text-shadow: 0 2px 0 var(--ink), 0 0 18px rgba(33, 230, 255, 0.8);
}
#sentence-flash.pop { animation: sflash 2.1s cubic-bezier(0.2, 0.9, 0.3, 1) forwards; }
@keyframes sflash {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.6) rotate(-3deg); }
  12%  { opacity: 1; transform: translate(-50%, -50%) scale(1.12) rotate(2deg); }
  24%  { transform: translate(-50%, -50%) scale(1) rotate(0deg); }
  80%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -62%) scale(1.04); }
}

/* --------------------------------------------- Unlock celebration --- */
#celebrate {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;               /* behind the card, over the dimmed backdrop */
}
.unlock-card { position: relative; z-index: 1; }
#unlock-es {
  margin-top: 6px;
  font-size: clamp(17px, 4.4vw, 26px);
  font-weight: 900;
  color: var(--green);
  -webkit-text-stroke: 1px var(--ink);
  text-shadow: 0 2px 0 var(--ink), 0 0 18px var(--green);
}
#unlock-en {
  margin-top: 2px;
  font-size: clamp(12px, 2.8vw, 16px);
  font-weight: 800;
  font-style: italic;
  color: var(--cyan);
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.9);
}

/* ------------------------------------------------------ Exit / difficulty --- */
.hud-pill.exit {
  font-family: inherit;
  cursor: pointer;
  pointer-events: auto;
  padding: 6px 10px;
  line-height: 1;
  font-size: clamp(10px, 2.2vw, 14px);
  letter-spacing: 1px;
  color: var(--pink);
  border-color: var(--pink);
}
.btn-secondary {
  margin-top: 10px;
  background: linear-gradient(180deg, var(--pink), var(--purple));
  color: var(--white);
  font-size: clamp(14px, 3.2vw, 18px);
  padding: 9px 26px;
}
.diff-select { margin-top: 12px; }
/* TWO COLUMNS OF THREE. A single tall column pushed START off the bottom of
   the phone and made every rung feel like a list item; paired tiles fit the
   ladder on one screen and leave room for each one to be bigger and easier to
   hit. The sixth cell is always FLUENT. */
.diff-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 9px;
}
.diff-btn {
  font-family: inherit;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: 100%;
  min-width: 0;
  min-height: 96px;
  padding: 13px 10px;
  border-radius: 8px 18px 8px 18px;
  background: rgba(12, 7, 3, 0.6);
  border: 2px solid rgba(255, 180, 90, 0.4);
  color: var(--white);
  text-align: center;
  transition: transform 0.08s ease, border-color 0.08s ease, box-shadow 0.08s ease;
}
.diff-btn:hover { transform: translateY(-2px); }
.diff-btn .diff-icon { font-size: 30px; flex: 0 0 auto; line-height: 1; }
.diff-btn .diff-text { min-width: 0; }
.diff-btn .diff-name {
  display: block;
  font-size: 17px;
  font-weight: 900;
  letter-spacing: 1px;
  color: var(--yellow);
  overflow-wrap: break-word;
}
.diff-btn .diff-tag {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.25;
  color: rgba(255, 255, 255, 0.75);
  overflow-wrap: break-word;
}
/* FLUENT: the earned rung. Locked it reads as a goal, not as a dead button —
   dimmed and un-tappable, but never hidden, because the point is that you can
   see what you are working toward. */
.diff-btn.fluent {
  border-color: rgba(182, 255, 43, 0.55);
  background: linear-gradient(180deg, rgba(60, 20, 70, 0.7), rgba(12, 7, 3, 0.7));
}
.diff-btn.fluent .diff-name { color: var(--green); }
.diff-btn.fluent.locked {
  cursor: default;
  opacity: 0.62;
  border-color: rgba(255, 255, 255, 0.22);
}
.diff-btn.fluent.locked:hover { transform: none; }
.diff-btn.fluent.locked .diff-name { color: rgba(255, 255, 255, 0.75); }
.diff-btn.sel {
  border-color: var(--green);
  box-shadow: 0 0 22px rgba(182, 255, 43, 0.4);
}
.diff-btn.sel .diff-name { color: var(--green); }
.dev-levels { margin-top: 12px; text-align: left; }
.dev-levels summary {
  cursor: pointer;
  font-size: 10px;
  letter-spacing: 2px;
  color: rgba(255, 255, 255, 0.4);
}


/* ------------------------------------------------------- Ride picker --- */
/* Shown once a second machine is earned. Chips, not cards: it sits between
   difficulty and START on the title screen and must not push START off. */
.ride-select { margin-top: 10px; }
.ride-row {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
}
.ride-chip {
  font-family: inherit;
  cursor: pointer;
  font-size: 20px;
  line-height: 1;
  padding: 7px 11px;
  border-radius: 999px;
  background: rgba(12, 7, 3, 0.7);
  border: 2px solid rgba(255, 180, 90, 0.4);
  color: var(--white);
  transition: transform 0.08s ease, border-color 0.08s ease;
}
.ride-chip.auto {
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 1px;
  padding: 10px 13px;
  color: #ffd0a0;
}
.ride-chip:hover { transform: translateY(-2px); }
.ride-chip.sel {
  border-color: var(--green);
  box-shadow: 0 0 14px rgba(182, 255, 43, 0.55);
  background: rgba(182, 255, 43, 0.12);
}
.ride-chip.locked {
  cursor: default;
  opacity: 0.4;
  border-style: dashed;
  font-size: 15px;
  padding: 10px 12px;
}
.ride-chip.locked:hover { transform: none; }

/* ------------------------------- Half-pipe: prompt above the word row --- */
/* On normal levels the prompt sits low, near the thumb, because the bubbles
   are world-projected around the cat. The half-pipe is the opposite: the
   words hang at the top of the arena and the player stares up the whole
   round, so the English belongs up there with them. */
#prompt.hp {
  bottom: auto;
  top: calc(max(34px, env(safe-area-inset-top) + 16px) + 52px);
}


/* Narrow phones: eight HUD pills do not fit one 375px row, so the mute and
   narrate buttons were being clipped off the right edge. Shrink first, and
   allow a second row as the backstop. hpGeom measures the HUD's real height,
   so the half-pipe word row follows it down automatically. */
@media (max-width: 430px) {
  .hud-row { flex-wrap: wrap; row-gap: 4px; }
  .hud-pill { font-size: clamp(10px, 2.9vw, 15px); padding: 4px 7px; }
  .hud-pill.exit { font-size: clamp(9px, 2.4vw, 12px); padding: 4px 6px; }
  .hud-pill.mute, .hud-pill.narrate, .hud-pill.pause { padding: 4px 6px; }
}

/* Secondary action on the level-complete card: the way home, and the way to
   the passport, without starting the next level first. */
.card-exit {
  display: block;
  width: 100%;
  margin-top: 10px;
  font-size: clamp(13px, 3.2vw, 17px);
  padding: 9px 14px;
}
/* SALIDA in the corner, where phones put back. Deliberately quiet: the
   level-complete card's job is to celebrate and send you to the next level,
   and a full-width exit button competing with NEXT LEVEL made the whole card
   read as busy. */
.card-exit-corner {
  position: absolute;
  left: max(14px, env(safe-area-inset-left));
  top: max(14px, env(safe-area-inset-top));
  z-index: 6;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 2px solid var(--cyan);
  background: rgba(0, 0, 0, 0.55);
  color: var(--cyan);
  font-family: inherit;
  font-size: 22px;
  font-weight: 900;
  line-height: 1;
  cursor: pointer;
}
.card-exit-corner:active { background: rgba(33, 230, 255, 0.25); }
.card-exit-corner.hidden { display: none; }

/* ------------------------------------------------ Title backdrop --- */
/* The real illustrated poster (assets/title/title_hero.jpg) — Rad Gato
   launching off twin ramps over the tour's five monuments, sunset behind
   him. This replaced a CSS+SVG line-art stand-in that only a sliver of ever
   showed past the card; Dylan's ask was blunt: "why not just show the whole
   thing?" So now it IS the whole thing — full-bleed, and the card below is
   sized to the bottom of the screen on purpose, so everything above it
   (cat, ramps, sunset, monuments) is fully in view with nothing on top of
   it. Size doesn't need to be web-frugal here; this ships inside the app. */
#title-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  /* Clickable so a tap can SKIP the cold-open (see main.js). It is only ever
     visible during the hero stage — the menu stage hides it outright — so
     this can never swallow a tap meant for the picker. */
  pointer-events: auto;
  background: #150a35;
}
#title-bg.hidden { display: none; }
#title-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 18%;
}
/* The wordmark floats directly on the art, in the open sky above the cat —
   it used to live inside the scrolling card, so it scrolled away with
   everything else and sat on the card's own background instead of the
   poster. Static text: the title screen's copy never varies by state. */
.title-float {
  display: none;
}
#overlay:has(#title-bg:not(.hidden)) .title-float {
  display: block;
  position: absolute;
  top: max(18px, env(safe-area-inset-top));
  left: 0; right: 0;
  z-index: 2;
  text-align: center;
  pointer-events: none;
}
/* On the hero the wordmark runs edge to edge across the top on ONE line, the
   way the reference does — stacked RAD / GATO read small and timid over a
   full-bleed poster. `span` is display:block elsewhere, so undo that here. */
.title-float .logo {
  margin: 0 auto;
  padding: 0 4vw;
  max-width: 680px;
}
/* LENS FLARE. A bright bar sweeps across, masked by the wordmark itself so it
   lights only the letters instead of washing a rectangle over the poster.
   Fired from game.js the moment PLAY NOW lands. */
.wm-wrap { position: relative; display: block; }
.wm-wrap::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(100deg,
    transparent 38%, rgba(255, 255, 255, 0.15) 46%,
    rgba(255, 255, 255, 0.92) 50%,
    rgba(255, 255, 255, 0.15) 54%, transparent 62%);
  background-size: 300% 100%;
  background-repeat: no-repeat;
  -webkit-mask-image: url('../assets/title/wordmark.webp');
  mask-image: url('../assets/title/wordmark.webp');
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
.wm-wrap.flare::after { animation: wmFlare 1.1s cubic-bezier(0.3, 0, 0.2, 1) both; }
@keyframes wmFlare {
  0%   { background-position: 175% 0; opacity: 0; }
  12%  { opacity: 1; }
  80%  { opacity: 1; }
  100% { background-position: -75% 0; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .wm-wrap.flare::after { animation: none; }
}
/* Movie-subtitle treatment: white fill, hard black outline, soft shadow under
   it so it survives whatever colour the poster puts behind it. */
.title-float .tagline {
  margin-top: 6px;
  font-size: clamp(15px, 4.6vw, 26px);
  font-weight: 900;
  letter-spacing: 0.02em;
  color: #fff;
  opacity: 1;
  -webkit-text-stroke: clamp(2.5px, 0.8vw, 4px) #000;
  paint-order: stroke fill;
  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.9), 0 0 16px rgba(0, 0, 0, 0.75);
}
/* The wordmark's entrance once the trick freezes: overshoot in, settle. Big
   and a little cocky, the way the rest of this screen already is. */
.title-float.title-pop { animation: titlePop 0.62s cubic-bezier(0.2, 1.6, 0.4, 1) both; }
@keyframes titlePop {
  0%   { opacity: 0; transform: scale(0.35) translateY(-14px); }
  60%  { opacity: 1; transform: scale(1.12) translateY(2px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
/* PLAY NOW: the one CTA on the hero screen, sitting in the neon grid floor
   below the monuments rather than boxed in a card — see game._playTitleHero.
   Same pop-in as the wordmark, on a short delay behind it. */
.title-playnow {
  display: none;
  position: absolute;
  left: 50%;
  bottom: max(9vh, calc(env(safe-area-inset-bottom) + 64px));
  z-index: 2;
  transform: translateX(-50%);
  /* A block-level abs-positioned box with only `left` set should shrink to
     its content, but buttons don't always play along — width came out as
     exactly half the viewport, wrapping "PLAY NOW" onto two lines. Force it. */
  width: max-content;
  white-space: nowrap;
  padding: 15px 46px;
  font-size: 19px;
  letter-spacing: 0.5px;
}
#overlay:has(#title-bg:not(.hidden)) .title-playnow:not(.hidden) { display: block; }
/* titlePop's own keyframes set `transform` at every step, which REPLACES
   (not composes with) the static translateX(-50%) above the instant this
   class lands — the button visibly jumped half its own width to the right.
   A dedicated keyframe set that folds the centering into every step. */
/* The stagger behind the wordmark lives here, not in a JS timer — see
   game._playTitleHero. `both` holds the 0% frame during the delay, so the
   button is present and clickable while it waits to animate in. */
.title-playnow.title-pop {
  animation: titlePopCenter 0.5s cubic-bezier(0.2, 1.6, 0.4, 1) 0.22s both;
}
@keyframes titlePopCenter {
  0%   { opacity: 0; transform: translateX(-50%) scale(0.35) translateY(-14px); }
  60%  { opacity: 1; transform: translateX(-50%) scale(1.12) translateY(2px); }
  100% { opacity: 1; transform: translateX(-50%) scale(1) translateY(0); }
}
/* The hero-screen glass-card treatment (translucent card, neon meta-chips,
   faded footer) that used to live here is gone along with the layout it was
   built for: the video and the card used to share one screen and fight for
   space. They're now two sequential, mutually exclusive stages — the video
   cold-open (game._playTitleHero), then PLAY NOW swaps to this card, at
   which point #title-bg is hidden. So whenever .overlay-card is actually
   visible, #title-bg:not(.hidden) is never true, and this card just uses
   its normal opaque styling — the "older, cat-small" look Dylan asked to
   have back, not a new treatment. */
/* A stamp earned this session, called out when the passport opens. */
.stamp.fresh {
  animation: stampfresh 1.4s ease-in-out 3;
  border-color: var(--green) !important;
  box-shadow: 0 0 26px rgba(182, 255, 43, 0.75);
}
@keyframes stampfresh {
  0%, 100% { transform: rotate(var(--rot)) scale(1); }
  50%      { transform: rotate(var(--rot)) scale(1.09); }
}

/* A word you have actually listened to on the study card. Stays lit for the
   rest of the card, so a glance tells you what you skimmed past. */
.gloss-row.studied {
  background: rgba(255, 225, 26, 0.16);
  border-color: var(--yellow);
  box-shadow: 0 0 14px rgba(255, 225, 26, 0.4), inset 0 0 18px rgba(255, 225, 26, 0.12);
}
.gloss-row.studied b { color: var(--yellow); }
.gloss-row.studied span { color: #fff3c4; }
/* A checkmark earns its place here: it reads as done, not just highlighted. */
.gloss-row.studied b::after {
  content: ' ✓';
  color: var(--green);
  font-size: 0.85em;
}


/* The card's primary button is the last element, so its own drop edge defines
   the bottom of the scroll area. Without this the card scrolled into empty
   space below VAMOS / NEXT LEVEL, and the magenta edge was clipped off. */
.overlay-card #overlay-btn:last-child { margin-bottom: 4px; }
/* Whatever sits directly above the sticky bar gets breathing room, so no
   control is ever half-hidden behind START at rest. */
.overlay-card > *:nth-last-child(2) { margin-bottom: 18px; }

/* Listening mode: the English gloss under the speaker icon. Deliberately
   secondary, so hearing the word is still the fast path, but a muted phone
   or a loud room can still answer. */
.audio-gloss {
  display: block;
  margin-top: 2px;
  font-size: clamp(13px, 3.2vw, 18px);
  font-weight: 800;
  letter-spacing: 1px;
  color: rgba(255, 255, 255, 0.72);
  -webkit-text-stroke: 0;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.9);
  text-transform: uppercase;
}

/* Combo pill result colour. It used to change silently, so a x2 gave no signal
   that the last answer had actually landed. */
.hud-pill.combo.good {
  color: var(--green);
  border-color: var(--green);
  background: rgba(182, 255, 43, 0.18);
  box-shadow: 0 0 16px rgba(182, 255, 43, 0.7);
}
.hud-pill.combo.bad {
  color: var(--pink);
  border-color: var(--pink);
  background: rgba(255, 47, 185, 0.2);
  box-shadow: 0 0 16px rgba(255, 47, 185, 0.75);
}

/* ------------------------------------------------------------- paywall --- */
.unlock-btn {
  width: 100%;
  margin: 0 0 12px;
  background: linear-gradient(90deg, var(--yellow), var(--orange));
  color: var(--ink);
}
.soft-unlock { margin: 10px 0 0; font-size: 14px; color: var(--yellow); }
.link-btn {
  font-family: inherit; font-size: inherit; font-weight: 800;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--cyan); text-decoration: underline;
}
.overlay-card.paywall #overlay-tag { color: var(--yellow); letter-spacing: .12em; }
.pw-lead { margin: 2px 0 12px; font-size: clamp(15px, 4.2vw, 18px); font-weight: 900; color: var(--white); }
.pw-tiers { display: flex; flex-direction: column; gap: 10px; }
.pw-tier {
  position: relative;
  font-family: inherit; cursor: pointer; width: 100%; text-align: left;
  display: grid; grid-template-columns: 1fr auto; column-gap: 10px; align-items: center;
  padding: 12px 14px; border-radius: 8px 18px 8px 18px;
  background: rgba(255, 255, 255, .06); border: 2px solid rgba(255, 255, 255, .22);
  color: var(--white);
}
.pw-tier.sel {
  border-color: var(--green);
  background: rgba(182, 255, 43, .12);
  box-shadow: 0 0 0 2px rgba(182, 255, 43, .35), 0 0 22px rgba(182, 255, 43, .35);
}
.pw-name { font-weight: 900; letter-spacing: .08em; font-size: 15px; }
.pw-price { font-weight: 900; font-size: 17px; color: var(--yellow); text-align: right; white-space: nowrap; }
.pw-sub { grid-column: 1 / -1; font-size: 12.5px; font-weight: 700; opacity: .8; }
.pw-tier.sel .pw-sub { opacity: 1; color: var(--green); }
.pw-fine { margin: 8px 0 0; font-size: 11px; line-height: 1.4; opacity: .6; }
.pw-links { margin: 12px 0 0; font-size: 13px; opacity: .9; }
.pw-links a { color: var(--cyan); }
.pw-error { margin: 10px 0 0; font-size: 13px; font-weight: 800; color: var(--pink); }

.pw-badge {
  position: absolute; top: -10px; right: 12px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 900; letter-spacing: .1em;
  background: var(--pink); color: var(--white);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .35);
}
.pw-tier.sel .pw-badge { background: var(--green); color: var(--ink); }
.pw-tiers { padding-top: 6px; }
