/* ============================================================================
   deFITnitive — Design system
   Light, white/beige, energetic Blob-first look. Two brand colors only:
   FIT yellow + dark purple ink, on a warm white/beige neutral. Characters
   carry the energy — backgrounds and cards stay calm and uncluttered.
   ========================================================================== */

@import url("https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700;800&family=Inter:wght@400;500;600;700;800&display=swap");

:root {
  /* -- Palette -------------------------------------------------------- */
  --bg: #fff8ec;
  --bg-alt: #fbefd8;
  --surface: #ffffff;
  --surface-2: #f6eedb;
  --border: #ecdfc2;

  --text: #241539;
  --text-dim: #6e6083;
  --text-faint: #a89cbd;

  /* -- The 2-color brand: FIT yellow + dark purple -------------------- */
  --purple: #35186b;
  --purple-2: #6a3fd6;
  --yellow: #ffc629;
  --yellow-deep: #ffa300;
  /* Semantic "progress/good" green — missions-done ring and the weekly goal
     bar always fill green regardless of brand accent, so "further along" =
     unambiguously good at a glance. */
  --green: #2fa563;
  --green-deep: #1f8a51;

  /* Compatibility aliases: older markup/i18n strings reference these
     token names directly (inline styles, shared HTML strings). Repointing
     the values here keeps every call site on-brand without touching each
     one individually. */
  --cyan: var(--purple);
  --magenta: var(--error);
  --volt: var(--yellow);
  --orange: var(--yellow-deep);
  --brand-blue: var(--purple);

  --error: #e1483d;

  --gradient-hero: linear-gradient(135deg, var(--purple) 0%, var(--purple-2) 100%);
  --gradient-power: linear-gradient(90deg, var(--yellow-deep), var(--yellow));
  --gradient-cta: linear-gradient(135deg, var(--yellow) 0%, var(--yellow-deep) 100%);
  --gradient-gold: linear-gradient(90deg, var(--yellow), var(--yellow-deep));

  --glow-cyan: 0 10px 26px -8px rgba(53, 24, 107, 0.35);
  --glow-magenta: 0 0 0 3px rgba(225, 72, 61, 0.16);
  --glow-volt: 0 10px 26px -8px rgba(255, 163, 0, 0.45);
  --glow-yellow: 0 10px 26px -8px rgba(255, 163, 0, 0.45);

  --font-display: "Baloo 2", "Inter", sans-serif;
  --font-body: "Inter", system-ui, sans-serif;

  --radius: 14px;
  --radius-sm: 8px;
  --radius-lg: 24px;

  --container: 1180px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg { max-width: 100%; display: block; }
a { color: inherit; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.08;
  margin: 0 0 0.4em;
  color: var(--text);
}

p { margin: 0 0 1em; color: var(--text-dim); }

.container {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 20px;
  position: relative;
  z-index: 1;
}

section { position: relative; z-index: 1; padding: 48px 0; }
.section-tight { padding: 34px 0; }
@media (min-width: 640px) {
  section { padding: 72px 0; }
  .section-tight { padding: 48px 0; }
}

.product-behind-trailer-note { margin-top: 18px; }
.trailer-reward-pill.trailer-reward-claimed { opacity: 0.55; }
.welcome-back-modal-box .trailer-reward-desc { font-size: 0.95rem; color: var(--text-dim); }
.video-embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-top: 16px;
  box-shadow: 0 24px 60px -24px rgba(53, 24, 107, 0.4);
}
.video-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: 0.78rem;
  color: var(--purple-2);
  margin-bottom: 14px;
}
.eyebrow::before {
  content: "";
  width: 22px; height: 4px;
  border-radius: 4px;
  background: var(--yellow);
  display: inline-block;
}

.section-head { max-width: 640px; margin: 0 auto 40px; text-align: center; }
.section-head h2 { font-size: clamp(1.8rem, 4.5vw, 2.6rem); }
.section-head p { font-size: 1.05rem; }

/* -- Buttons ------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: 0.02em;
  font-size: 1rem;
  line-height: 1.25;
  padding: 15px 28px;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
}
.btn:active { transform: scale(0.97); }
.btn-primary {
  background: var(--gradient-cta);
  color: var(--purple);
  box-shadow: var(--glow-yellow);
  position: relative; overflow: hidden; isolation: isolate;
}
@media (prefers-reduced-motion: no-preference) {
  .btn-primary { animation: ctaPulse 2.2s ease-in-out infinite; }
  .btn-primary:hover { animation-play-state: paused; }
}
@keyframes ctaPulse {
  0%, 100% { box-shadow: var(--glow-yellow); transform: scale(1); }
  50% { box-shadow: 0 10px 34px -6px rgba(255, 163, 0, 0.75); transform: scale(1.03); }
}
.btn-primary:hover { filter: brightness(1.04); transform: translateY(-1px); box-shadow: 0 14px 30px -8px rgba(255, 163, 0, 0.55); }
/* Holofoil shimmer sweep, reusing the same trading-card foil effect as the
   Customization showcase (see .holofoil below) on the primary CTA. */
.btn-primary::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg,
    transparent 0%, transparent 40%,
    rgba(255, 255, 255, 0.75) 47%,
    rgba(255, 255, 255, 0.95) 50%,
    rgba(106, 63, 214, 0.35) 53%,
    transparent 60%, transparent 100%);
  background-size: 250% 250%;
  background-position: 0% 0%;
  mix-blend-mode: overlay;
}
@media (prefers-reduced-motion: no-preference) {
  .btn-primary::after { animation: holofoilSweep 4.5s ease-in-out infinite; }
}
/* -- Inactive CTA state: flat, muted, non-pulsing orange — used on
   sharePositionBtn/useGoldenTicketBtn/openTrainModalBtn once there's
   nothing left to do with that button today. Still visibly orange (not
   grayed out), just "not flowing" like the active gradient/glow/shimmer. */
.btn-primary.btn-cta-inactive {
  background: var(--yellow-deep); opacity: 0.6;
  box-shadow: none; animation: none; cursor: default;
}
.btn-primary.btn-cta-inactive:hover {
  filter: none; transform: none; box-shadow: none;
}
.btn-primary.btn-cta-inactive::after { animation: none; opacity: 0; }
.btn-secondary {
  background: var(--surface);
  color: var(--purple);
  border: 2px solid var(--border);
}
.btn-secondary:hover { border-color: var(--purple); color: var(--purple); background: var(--bg-alt); }
.btn-block { width: 100%; }
.btn-sm { padding: 10px 18px; font-size: 0.85rem; }

/* -- Nav ----------------------------------------------------------------- */
.nav {
  position: sticky; top: 0; z-index: 50;
  backdrop-filter: blur(14px);
  background: rgba(255, 248, 236, 0.86);
  border-bottom: 1px solid var(--border);
}
.nav .container { display: flex; align-items: center; justify-content: space-between; padding-top: 14px; padding-bottom: 14px; gap: 12px; }
.nav-right { display: flex; align-items: center; gap: 16px; }
.brand { display: flex; align-items: center; gap: 10px; font-family: var(--font-display); font-weight: 700; font-size: 1.3rem; text-decoration: none; }
.brand-mark {
  width: 34px; height: 34px; border-radius: 10px;
  background: var(--yellow);
  box-shadow: 0 4px 0 var(--yellow-deep);
  display: grid; place-items: center;
  font-size: 1.1rem;
}
/* Absolutely positioned so this line sits under the wordmark without adding
   to it — otherwise the two-line text block is taller than the 34px brand
   mark and stretches the whole nav row to match it. */
.brand small {
  display: block; position: absolute; left: 0; top: 100%;
  font-size: 0.55rem; color: var(--text-faint); letter-spacing: 0.05em; font-weight: 600;
  text-transform: uppercase; white-space: nowrap; line-height: 1.2;
  max-width: 132px; overflow: hidden; text-overflow: ellipsis;
}

/* -- Brand wordmark: "FIT" always yellow, the rest always dark purple -- */
.wordmark { position: relative; font-weight: 800; color: var(--purple); }
/* Nav-logo instance only (scoped via .brand ancestor — doesn't touch the
   plain, inline .wordmark the JS brand-highlighter injects into prose
   elsewhere). A tight line-height pulls the slogan right up against the
   wordmark instead of leaving descender space between them, and the
   translateY nudges the whole two-line block (wordmark + slogan move
   together, since the slogan is positioned relative to this element) back
   up so it's centered against the brand-mark icon as a whole, not just the
   wordmark's own line. */
.brand .wordmark { line-height: 1; transform: translateY(-6px); }
.wordmark .wm-a, .wordmark .wm-b { color: var(--purple); }
.wordmark .wm-fit { color: var(--yellow-deep); }
.nav-links { display: none; gap: 26px; font-family: var(--font-display); font-weight: 600; letter-spacing: 0.02em; font-size: 0.95rem; }
.nav-links a { text-decoration: none; color: var(--text-dim); }
.nav-links a:hover { color: var(--purple); }

/* Below 860px, .nav-links is reached through the hamburger toggle instead
   of being shown inline — it drops down as a full-width panel anchored to
   .nav (the nearest positioned ancestor, via its own position:sticky). */
.nav-links.open {
  display: flex;
  position: absolute;
  top: 100%; left: 0; right: 0;
  flex-direction: column;
  gap: 2px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  padding: 8px 20px 18px;
  box-shadow: 0 20px 34px -22px rgba(53, 24, 107, 0.4);
}
.nav-links.open a { padding: 11px 6px; border-radius: 8px; }
.nav-links.open a:hover, .nav-links.open a:active { background: var(--bg-alt); color: var(--purple); }

@media (min-width: 860px) {
  .nav-links { display: flex; position: static; flex-direction: row; gap: 26px; padding: 0; background: none; border: none; box-shadow: none; }
}

/* -- Mobile hamburger nav toggle ------------------------------------------ */
.nav-toggle {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  width: 38px; height: 38px; flex-shrink: 0;
  border-radius: 10px; border: 1px solid var(--border); background: var(--surface);
  cursor: pointer; padding: 0;
}
.nav-toggle span { display: block; width: 17px; height: 2px; border-radius: 2px; background: var(--purple); transition: transform 0.2s ease, opacity 0.2s ease; }
.nav-toggle.open span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle.open span:nth-child(2) { opacity: 0; }
.nav-toggle.open span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
@media (min-width: 860px) { .nav-toggle { display: none; } }

/* .btn-text-muted is block/full-width for its usual card context; the nav
   needs the same look inline and no wider than its own text. Selector
   includes the id so it outweighs .btn-text-muted's own rule regardless of
   source order (equal class-only specificity would otherwise let whichever
   rule comes later in the file win). */
#signOutBtn.nav-signout-btn { display: inline; width: auto; padding: 6px 2px; white-space: nowrap; }
.lang-switch { display: flex; gap: 2px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; padding: 3px; flex-shrink: 0; }
.lang-btn {
  font-family: var(--font-display); font-weight: 700; font-size: 0.72rem;
  letter-spacing: 0.03em; text-transform: uppercase;
  background: none; border: none; color: var(--text-dim);
  padding: 5px 10px; border-radius: 999px; cursor: pointer; line-height: 1.2;
}
.lang-btn:hover:not(.active) { color: var(--purple); }
.lang-btn.active { background: var(--purple); color: #fff; }

@media (max-width: 480px) {
  .nav .container { padding-left: 12px; padding-right: 12px; gap: 6px; }
  .nav .brand { font-size: 1rem; gap: 6px; }
  .nav .brand-mark { width: 26px; height: 26px; font-size: 0.9rem; }
  /* Narrower and smaller so the (longer, ES) slogan can't reach the
     hamburger/lang-switch/CTA cluster to its right — see .brand small above. */
  .nav .brand small { font-size: 0.44rem; max-width: 96px; }
  .nav .brand .wordmark { transform: translateY(-4px); }
  .nav-right { gap: 6px; }
  .lang-switch { padding: 2px; }
  .lang-btn { padding: 3px 6px; font-size: 0.62rem; }
  /* product.html and get-to-the-top.html still render a .btn-sm nav CTA;
     index.html no longer does. */
  .nav-right .btn-sm { padding: 7px 10px; font-size: 0.72rem; }
  #signOutBtn.nav-signout-btn { font-size: 0.72rem; padding: 6px 0; }
  .nav-toggle { width: 32px; height: 32px; }
}

/* Generic two-column responsive grid, reused by the camera-tracking section */
.hero-grid { display: grid; gap: 40px; align-items: center; }
@media (min-width: 940px) { .hero-grid { grid-template-columns: 1.05fr 0.95fr; } }

/* Safety net against the classic flex/grid item min-width:auto trap: without
   this, a single long/unbreakable child (e.g. a nowrap button or a
   fixed-intrinsic-size image) can force its grid/flex ancestor chain wider
   than the viewport, which then gets silently clipped by any overflow:hidden
   ancestor (e.g. .hero) instead of wrapping — the report's "doesn't fit on
   mobile" symptom traced back to exactly this. */
.hero > .container,
.hero-grid, .hero-grid > *,
.hero-mini-grid, .hero-mini-grid > *,
.grid-2, .grid-2 > *,
.grid-3, .grid-3 > *,
.grid-4, .grid-4 > * {
  min-width: 0;
}

/* -- Hero ----------------------------------------------------------------- */
.hero { padding-top: 44px; padding-bottom: 8px; position: relative; overflow: hidden; }
.hero::before {
  content: "";
  position: absolute; z-index: 0; pointer-events: none;
  top: -180px; right: -160px; width: 520px; height: 520px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 198, 41, 0.35), rgba(255, 198, 41, 0) 70%);
}
.hero::after {
  content: "";
  position: absolute; z-index: 0; pointer-events: none;
  bottom: -220px; left: -180px; width: 480px; height: 480px; border-radius: 50%;
  background: radial-gradient(circle, rgba(106, 63, 214, 0.16), rgba(106, 63, 214, 0) 70%);
}
.hero-solo { max-width: 820px; }

.hero h1 {
  font-size: clamp(2.6rem, 8vw, 4.8rem);
  color: var(--purple);
}
.hero h1 .wm-fit, .hero h1 .hl { color: var(--yellow-deep); }
.hero-sub { font-size: clamp(1.2rem, 3vw, 1.5rem); font-weight: 600; font-family: var(--font-display); color: var(--text); max-width: 46ch; }
.hero-ctas { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 28px; }
.hero-meta { display: flex; gap: 10px; margin-top: 30px; flex-wrap: wrap; }
.hero-meta div {
  font-family: var(--font-display); font-weight: 700; font-size: 0.78rem;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim);
  padding: 9px 16px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface);
}
.hero-meta .hero-meta-highlight {
  color: var(--purple); background: var(--yellow); border-color: transparent;
}

/* Hero blob showcase — the character is the first thing visitors see */
.hero-showcase { position: relative; display: flex; justify-content: center; align-items: flex-end; min-height: 320px; margin-top: 8px; }
.hero-showcase .blob-img { width: min(360px, 78vw); filter: drop-shadow(0 30px 40px rgba(53, 24, 107, 0.18)); }
.hero-showcase-badge {
  position: absolute; top: 6%; left: 4%;
  background: var(--surface); border: 1px solid var(--border); border-radius: 999px;
  padding: 8px 14px; font-family: var(--font-display); font-weight: 700; font-size: 0.85rem;
  box-shadow: 0 10px 24px -12px rgba(53, 24, 107, 0.25);
}
.hero-showcase-badge.badge-2 { top: auto; bottom: 10%; left: auto; right: 2%; }

/* -- Hero banner 1: deFITnitive explainer (index.html) ----------------------- */
/* min() caps the peek-inducing min-height on very tall viewports; on shorter
   ones the row of (now much bigger) cards will exceed it anyway, which is
   fine — the peek is a bonus on typical laptop/desktop heights, not a rule. */
.hero { display: flex; flex-direction: column; justify-content: center; min-height: min(88vh, 800px); }

.hero-wordmark { font-size: clamp(2.6rem, 7vw, 4.2rem); color: var(--purple); margin-bottom: 2px; line-height: 1; }
.hero-tagline { font-size: clamp(1.05rem, 2.2vw, 1.25rem); font-weight: 600; font-family: var(--font-display); color: var(--text-dim); margin-bottom: 0; }

.hero-mini-grid { display: grid; gap: 20px; grid-template-columns: 1fr; margin-top: 30px; }
@media (min-width: 640px) { .hero-mini-grid { grid-template-columns: repeat(3, 1fr); } }
.hero-mini-item { display: flex; flex-direction: column; align-items: center; }
.hero-mini-card { padding: 22px 20px; text-align: center; width: 100%; }
.hero-mini-card .card-media { height: 190px; position: relative; margin-bottom: 0; }
.hero-mini-title { font-size: 1.15rem; margin: 12px 0 0; text-align: center; }
.hero-mini-title em { color: var(--purple-2); font-style: italic; }

/* Reserved-height slot below the image, present (even empty) on every card
   in the row so the card boxes (and the titles below them) stay the same
   height no matter which card has pills/name content to show. Fixed (not
   min-) height + overflow:hidden guarantees it never grows past this, even
   if a translation makes the pill/name text longer than expected. */
.hero-mini-caption { height: 58px; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; }

.lvl-up-badge {
  position: absolute; top: -2px; right: calc(50% - 74px);
  background: var(--yellow); color: var(--purple);
  font-family: var(--font-display); font-weight: 800; font-size: 0.72rem;
  letter-spacing: 0.04em; text-transform: uppercase;
  padding: 4px 10px; border-radius: 999px; box-shadow: 0 4px 0 var(--yellow-deep);
  transform: rotate(8deg); z-index: 2;
}
.hero-mini-pills { display: flex; gap: 5px; justify-content: center; flex-wrap: nowrap; }
.hero-mini-pill {
  display: inline-flex; align-items: center; gap: 3px;
  font-family: var(--font-display); font-weight: 700; font-size: 0.66rem;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px;
  padding: 2px 8px; color: var(--text-dim); white-space: nowrap;
}
.hero-mini-name {
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--font-display); font-weight: 800; font-size: 0.8rem;
  color: var(--purple); background: #f1ecfb; border-radius: 999px;
  padding: 2px 11px; white-space: nowrap;
}

/* Mini camera-tracking mockup reused inside card 1 — the SVG skeleton scales
   automatically with its viewBox, but the HTML badge/rep/tag overlays use
   fixed CSS units so they need their own smaller sizing at this scale. */
.hero-cam-mini { padding: 0; }
.hero-cam-mini .cam-frame { height: 100%; width: auto; max-width: 100%; margin: 0 auto; }
.hero-cam-mini .cam-badge { font-size: 0.48rem; padding: 3px 7px; top: 8px; left: 8px; }
.hero-cam-mini .cam-rep { top: 8px; right: 8px; padding: 4px 8px; border-radius: 10px; }
.hero-cam-mini .cam-rep .n { font-size: 0.95rem; }
.hero-cam-mini .cam-rep .l { font-size: 0.4rem; }
.hero-cam-mini .cam-player-tag { font-size: 0.5rem; padding: 3px 8px; bottom: 8px; left: 8px; }
.hero-cam-mini .cam-blob { width: 26%; bottom: 6px; right: 6px; }

/* -- Fight card: two evenly-matched fighters + a bolt crossing the card ------ */
.hero-vs-media { display: flex; align-items: flex-end; justify-content: space-between; gap: 6px; height: 100%; padding: 0 4px; position: relative; }
.hero-vs-fighter { flex: 1; height: 100%; display: flex; align-items: flex-end; justify-content: center; min-width: 0; }
/* object-fit:contain inside two identically-sized boxes is what actually
   guarantees both fighters render at the same size — max-height/max-width
   alone don't, since each Blob PNG has a different aspect ratio and one of
   the two constraints always binds first for one character but not the other. */
.hero-vs-fighter img { width: 100%; height: 90%; object-fit: contain; object-position: bottom center; }
.hero-vs-bolt {
  position: absolute; top: -6%; left: 50%; height: 112%; width: 44px;
  transform: translateX(-50%) rotate(10deg); z-index: 2;
  filter: drop-shadow(0 3px 5px rgba(53, 24, 107, 0.45));
}
.hero-vs-bolt polygon { fill: var(--yellow); stroke: var(--purple); stroke-width: 3; stroke-linejoin: round; }
.hero-vs-caption { flex-direction: row !important; justify-content: space-around; gap: 4px; }
.hero-vs-fighter-info { display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; }
.hero-vs-fighter-info .hero-mini-pills { flex-wrap: wrap; }

.hero-cta-row { text-align: center; margin-top: 28px; }
.btn-xl { padding: 20px 26px; font-size: 1.3rem; max-width: 100%; }
@media (min-width: 560px) { .btn-xl { padding: 24px 46px; font-size: 1.3rem; } }
.cta-2line { white-space: pre-line; line-height: 1.3; }

/* -- Signup banner 2: full-width, photo on the right ------------------------- */
.signup-heading { font-size: clamp(1.7rem, 3.6vw, 2.6rem); }
/* A bold purple outline keeps this readable against the cream background —
   the plain yellow-deep fill alone was too close in value to --bg. The
   heartbeat pulse (two quick beats, then a rest, every 1s) draws the eye
   back to it periodically instead of relying on color contrast alone. */
.signup-heading .hl {
  display: inline-block;
  color: var(--yellow-deep);
  -webkit-text-stroke: 2px var(--purple);
  paint-order: stroke fill;
}
@media (prefers-reduced-motion: no-preference) {
  .signup-heading .hl { animation: heartbeatPulse 1s ease-in-out infinite; }
}
@keyframes heartbeatPulse {
  0%, 40%, 100% { transform: scale(1); }
  10% { transform: scale(1.12); }
  20% { transform: scale(1); }
  30% { transform: scale(1.08); }
}
.signup-ctas { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.signup-photo img { width: 100%; display: block; border-radius: var(--radius-lg); box-shadow: 0 24px 60px -24px rgba(53, 24, 107, 0.4); }

/* -- Signup card: Google/email auth + check-inbox + success states ---------- */
.signup-card { text-align: center; }
.signup-eyebrow {
  font-family: var(--font-display); font-weight: 800; font-size: 0.95rem;
  color: var(--purple); margin-bottom: 16px;
}
.btn-google {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 13px 20px; border-radius: 999px;
  background: var(--surface); border: 2px solid var(--border);
  font-family: var(--font-display); font-weight: 700; font-size: 1.35rem; color: var(--text);
  cursor: pointer; transition: border-color 0.15s ease, transform 0.15s ease;
}
.btn-google:hover { border-color: var(--purple-2); transform: translateY(-1px); }
.btn-google-icon { width: 20px; height: 20px; flex-shrink: 0; }
.signup-divider {
  display: flex; align-items: center; gap: 12px; margin: 18px 0;
  font-size: 0.8rem; color: var(--text-faint);
}
.signup-divider::before, .signup-divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.signup-trust-line {
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  margin-top: 16px; font-size: 0.75rem; color: var(--text-faint);
}
.recaptcha-note {
  margin-top: 10px; text-align: center; font-size: 0.7rem; line-height: 1.4;
}
.recaptcha-note a { color: var(--text-faint); text-decoration: underline; }
.recaptcha-note a:hover { color: var(--purple); }
.signup-sent-to { margin: 10px 0 4px; }
.signup-sent-to strong { color: var(--purple); }
.signup-success-sub { color: var(--text-dim); margin-bottom: 16px; }
.btn-text-muted {
  display: block; width: 100%; background: none; border: none; padding: 8px;
  font-family: var(--font-body); font-size: 0.85rem; color: var(--text-faint);
  text-decoration: underline; cursor: pointer; text-align: center;
}
.btn-text-muted:hover { color: var(--text-dim); }
/* Single reminder checkbox row shown only on the post-signup success card */
.checklist-single {
  display: flex; align-items: flex-start; gap: 10px; text-align: left;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px 14px; margin: 16px 0; font-size: 0.85rem; color: var(--text-dim); cursor: pointer;
}
.checklist-single input { accent-color: var(--purple); width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }

/* -- Blob picker (first-run onboarding on get-to-the-top.html) --------------
   This page's "hero" for a brand-new visitor: a full-scale character-select
   grid using individual splash art per Blob (not a small icon row), so the
   picker itself carries the visual weight the plain hero banner used to. */
.blob-picker-hero { padding-top: 34px; }
.blob-picker-hero .hero-solo { max-width: 640px; margin-bottom: 8px; }

/* 6-column trick so mobile reads as a 3-then-2 funnel (row 2 centered under
   row 1) instead of a left-aligned orphan row; each option spans 2 of the
   6 columns. Desktop resets every option back to auto so the 5 Blobs sit in
   a single even row. */
.blob-pick-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; margin: 28px 0; }
.blob-pick-option:nth-child(1) { grid-column: 1 / 3; }
.blob-pick-option:nth-child(2) { grid-column: 3 / 5; }
.blob-pick-option:nth-child(3) { grid-column: 5 / 7; }
.blob-pick-option:nth-child(4) { grid-column: 2 / 4; }
.blob-pick-option:nth-child(5) { grid-column: 4 / 6; }
@media (min-width: 720px) {
  .blob-pick-grid { grid-template-columns: repeat(5, 1fr); max-width: 940px; margin-left: auto; margin-right: auto; }
  .blob-pick-option { grid-column: auto !important; }
}
.blob-pick-option {
  background: var(--surface); border: 2px solid var(--border); border-radius: var(--radius-lg);
  padding: 10px 8px 14px; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 700; font-size: 1rem; color: var(--text);
  box-shadow: 0 16px 34px -24px rgba(53, 24, 107, 0.35);
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
/* Overrides the generic .card-media rule further down (fixed height:96px,
   flex/align-items) explicitly — that rule doesn't touch height/display, so
   without resetting them here they'd otherwise leak through and squash this
   into a short wide box, cropping the hero-pose art. */
.blob-pick-option .card-media {
  width: 100%; height: auto; aspect-ratio: 1 / 1; display: block; margin-bottom: 0;
  border-radius: 20px; overflow: hidden; background: #fbf7ef;
}
.blob-pick-option .card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Desktop: taller portrait card so the hero-pose splash art (which already
   has generous background/rays padding around the character) reads as a
   proper photo card filling its rounded corners edge-to-edge, instead of
   letterboxing inside a plain-colored mat. */
@media (min-width: 720px) {
  .blob-pick-option .card-media { aspect-ratio: 3 / 4; }
}
.blob-pick-option:hover { border-color: var(--purple-2); transform: translateY(-2px); }
.blob-pick-option.selected {
  border-color: var(--yellow-deep); background: #fff8e6; color: var(--text);
  box-shadow: 0 0 0 2px var(--yellow-deep) inset, 0 16px 34px -24px rgba(53, 24, 107, 0.35);
}

/* -- Cards / Quest cards --------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 20px;
  box-shadow: 0 16px 40px -28px rgba(53, 24, 107, 0.35);
}
@media (min-width: 640px) { .card { padding: 28px; } }
.card-glow-cyan { box-shadow: 0 20px 50px -28px rgba(53, 24, 107, 0.4); border-color: #ded0c9; }
.card-glow-magenta { box-shadow: 0 20px 50px -28px rgba(255, 163, 0, 0.4); border-color: #f3dd9e; }

.grid-3 { display: grid; gap: 20px; grid-template-columns: 1fr; }
.grid-2 { display: grid; gap: 20px; grid-template-columns: 1fr; }
.grid-4 { display: grid; gap: 20px; grid-template-columns: 1fr; }
@media (min-width: 720px) { .grid-3 { grid-template-columns: repeat(3, 1fr); } .grid-2 { grid-template-columns: repeat(2, 1fr); } .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .grid-4 { grid-template-columns: repeat(4, 1fr); } }

.quest-card { display: flex; flex-direction: column; gap: 12px; text-align: center; align-items: center; container-type: inline-size; }
.quest-card .quest-icon {
  width: 56px; height: 56px; border-radius: 16px;
  display: grid; place-items: center; font-size: 1.5rem;
  background: var(--bg-alt); border: 1px solid var(--border);
}
.quest-card h3 { font-size: 1.25rem; margin-bottom: 4px; }

/* Fixed-height media slot so images of very different intrinsic aspect
   ratios (our Blob PNGs) still line up titles/subtitles across a card row —
   the image fits the slot, the slot never resizes to fit the image. */
.card-media { width: 100%; height: 96px; display: flex; align-items: flex-end; justify-content: center; margin-bottom: 6px; }
.card-media img { max-height: 100%; max-width: 100%; width: auto; display: block; }
.quest-card h3 { min-height: 2.2em; display: flex; align-items: center; justify-content: center; }
.quest-reward {
  margin-top: auto;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  width: 100%;
}
/* The headline half of the reward ("+10 POWER POINTS"): a purple brand
   pill, full card width so all 3 pills in a row match exactly. Fluid,
   container-relative size (not viewport-relative) so it stays readable and
   never wraps, even for the longer Golden Ticket / Spanish copy. */
.quest-reward-pill {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  width: 100%; box-sizing: border-box;
  background: var(--purple); color: #fff;
  font-family: var(--font-display); font-weight: 800;
  padding: 11px 14px; border-radius: var(--radius-lg);
  letter-spacing: 0.01em; text-align: center;
  box-shadow: 0 10px 24px -10px rgba(53, 24, 107, 0.55);
  font-size: clamp(0.6rem, 3.4cqw, 1.05rem);
  /* The reward detail now lives inside the pill (after " / "), which rarely
     fits the old single-line nowrap pill at readable size — let it wrap to
     a second line instead of shrinking illegibly or spilling out. */
  white-space: normal; flex-wrap: wrap; row-gap: 2px;
}
.quest-reward-detail {
  font-family: var(--font-display); font-weight: 700; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--purple);
}
/* Now nested inside .quest-reward-pill (after the main reward text + " / ")
   instead of sitting below it as its own line — inherit the pill's white
   color and fluid clamp()/cqw font-size instead of the standalone styling
   above, which was sized/colored for sitting on the page background. */
.quest-reward-pill .quest-reward-detail {
  color: inherit; font-size: inherit; font-weight: inherit; letter-spacing: inherit;
}

/* -- Strength / XP meters --------------------------------------------------- */
.meter { height: 12px; border-radius: 999px; background: var(--surface-2); border: 1px solid var(--border); overflow: hidden; }
.meter-fill { height: 100%; border-radius: 999px; background: var(--gradient-power); position: relative; }
.meter-fill.anim { transition: width 1.1s cubic-bezier(.2,.9,.25,1); }
.meter-row { display: flex; justify-content: space-between; font-family: var(--font-display); font-weight: 700; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); margin-bottom: 6px; }

.level-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 34px; padding: 0 8px;
  border-radius: 10px;
  background: var(--purple);
  font-family: var(--font-display); font-weight: 700; font-size: 0.95rem;
  color: #fff;
}

/* -- Blob avatar ----------------------------------------------------------- */
.blob-avatar { filter: drop-shadow(0 4px 10px rgba(53, 24, 107, 0.18)); }
.blob-img { display: block; max-width: 100%; height: auto; filter: drop-shadow(0 10px 18px rgba(53, 24, 107, 0.2)); }
@media (prefers-reduced-motion: no-preference) {
  .blob-idle { animation: blobBreathe 2.6s ease-in-out infinite; }
  .blob-run { animation: blobRun 0.55s ease-in-out infinite; }
  .blob-punch { animation: blobPunch 0.5s ease-in-out infinite; }
  .blob-victory { animation: blobVictory 0.9s ease-in-out infinite; }
  .blob-koed { animation: blobKo 2.4s ease-in-out infinite; }
}
@keyframes blobBreathe { 0%,100% { transform: translateY(0) scale(1); } 50% { transform: translateY(-2px) scale(1.02); } }
@keyframes blobRun { 0%,100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-6px) rotate(2deg); } }
@keyframes blobPunch { 0%,100% { transform: translateX(0) scale(1); } 40% { transform: translateX(3px) scale(1.05); } 60% { transform: translateX(-2px); } }
@keyframes blobVictory { 0%,100% { transform: translateY(0) rotate(0); } 25% { transform: translateY(-8px) rotate(-6deg); } 75% { transform: translateY(-8px) rotate(6deg); } }
@keyframes blobKo { 0%,100% { transform: rotate(-8deg); } 50% { transform: rotate(-4deg); } }

/* -- Leaderboard / waitlist queue -------------------------------------------- */
.leaderboard { display: flex; flex-direction: column; gap: 8px; }
.leaderboard-empty { text-align: center; color: var(--text-dim); padding: 24px 12px; }
.lb-row {
  display: grid;
  grid-template-columns: 44px 50px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.lb-row.is-player { border-color: var(--purple); box-shadow: 0 0 0 1px var(--purple); background: #f4effc; }
.lb-row.top1 { border-color: var(--yellow-deep); box-shadow: 0 0 0 1px var(--yellow-deep); background: #fff8e6; }
.lb-rank { font-family: var(--font-display); font-weight: 700; color: var(--text-dim); font-size: 1.05rem; }
.lb-row.top1 .lb-rank, .lb-row.is-player .lb-rank { color: var(--text); }
.lb-handle {
  font-family: var(--font-display); font-weight: 600; letter-spacing: 0.02em;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lb-power {
  font-family: var(--font-display); font-weight: 700; color: var(--text-dim);
  display: flex; flex-direction: column; align-items: center; text-align: center; line-height: 1.2;
}
.lb-power-value { font-size: 0.92rem; white-space: nowrap; }
.lb-power-label { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.03em; opacity: 0.85; }
.lb-row.is-player .lb-power { color: var(--purple); }

/* Miniature of the row's actual Blob character (assets/blobs/*.png), scoped
   to leaderboard rows only — used in place of the procedural colored-ball
   avatar so every row clearly reads as one of the 5 real Blobs. */
.lb-blob-icon {
  width: 46px; height: 46px; flex-shrink: 0; position: relative;
  display: flex; align-items: center; justify-content: center;
}
.lb-blob-icon img {
  width: 100%; height: 100%; object-fit: contain; display: block; position: relative; z-index: 2;
  animation: lbIconFloat 2.6s ease-in-out infinite;
}
@keyframes lbIconFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}
/* Tiny per-character particle (flame/bolt/leaf/bubble/stone) drifting in a
   loose ellipse around the icon — z-index dips below the artwork (z-index:2
   above) for the "back" half of the loop so it reads as orbiting behind the
   character, even though nothing renders the occlusion precisely. Transform
   + opacity only (no shadows/filters) to keep this cheap across many rows. */
.lb-orbit-particle {
  position: absolute; left: 50%; top: 50%; margin: -7px 0 0 -7px;
  width: 14px; height: 14px; font-size: 11px; line-height: 14px; text-align: center;
  pointer-events: none; z-index: 1;
  animation-name: lbOrbit; animation-timing-function: linear; animation-iteration-count: infinite;
}
@media (prefers-reduced-motion: reduce) {
  .lb-blob-icon img, .lb-orbit-particle { animation: none; }
}
@keyframes lbOrbit {
  0%   { transform: translate(15px, 2px) scale(0.85); opacity: 0.95; z-index: 3; }
  20%  { transform: translate(8px, -7px) scale(1); opacity: 1; z-index: 3; }
  40%  { transform: translate(-8px, -7px) scale(0.9); opacity: 0.9; z-index: 3; }
  50%  { transform: translate(-15px, 2px) scale(0.55); opacity: 0.3; z-index: 0; }
  60%  { transform: translate(-8px, 9px) scale(0.55); opacity: 0.28; z-index: 0; }
  80%  { transform: translate(8px, 9px) scale(0.75); opacity: 0.55; z-index: 1; }
  100% { transform: translate(15px, 2px) scale(0.85); opacity: 0.95; z-index: 3; }
}

.queue-panel { position: relative; }
.queue-scroller { max-height: 420px; overflow-y: auto; padding-right: 4px; }
.queue-scroller::-webkit-scrollbar { width: 6px; }
.queue-scroller::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

/* -- Mobile: narrower gutters so the leaderboard card (and the rows inside
   it) gets meaningfully wider — the scrollbar is pulled in close to the
   card's own right edge instead of sitting well inside it. */
@media (max-width: 719px) {
  #leaderboard .container { padding-left: 12px; padding-right: 12px; }
  .queue-panel { padding: 16px 6px 16px 14px; }
  .lb-row { padding: 10px 10px; }
  .queue-scroller { padding-right: 2px; }
}

/* -- VS / Battle preview ------------------------------------------------------ */
.vs-card {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 16px;
}
.vs-fighter { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.vs-fighter .blob-img { width: 84px; }
.vs-fighter .fighter-stats { width: 100%; text-align: left; font-size: 0.8rem; }
.vs-fighter .fighter-stats div { display: flex; justify-content: space-between; margin-bottom: 4px; color: var(--text-dim); }
.vs-fighter .fighter-stats strong { color: var(--text); font-family: var(--font-display); }
.vs-badge {
  font-family: var(--font-display); font-weight: 800; font-size: 1.4rem;
  color: var(--yellow-deep);
}
.vs-fighter.winner { position: relative; }
.vs-fighter.winner::before {
  content: "WINNER"; position: absolute; top: -14px; left: 50%; transform: translateX(-50%);
  background: var(--yellow); color: var(--purple); font-family: var(--font-display); font-weight: 700;
  font-size: 0.65rem; letter-spacing: 0.08em; padding: 3px 10px; border-radius: 999px;
}
.battle-result { margin-top: 18px; text-align: center; font-family: var(--font-display); font-weight: 600; color: var(--text-dim); }

/* -- Camera preview mock ------------------------------------------------------ */
.phone-mock {
  width: 100%; max-width: 300px; margin: 0 auto;
  aspect-ratio: 9/17.5;
  background: var(--surface);
  border-radius: 34px;
  border: 3px solid var(--purple);
  padding: 10px;
  position: relative;
  box-shadow: 0 30px 70px -30px rgba(53, 24, 107, 0.4);
}
.phone-screen {
  width: 100%; height: 100%; border-radius: 24px; overflow: hidden; position: relative;
  background: var(--bg-alt);
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  padding: 14px;
}
.rep-counter {
  font-family: var(--font-display); font-weight: 800; font-size: 2.6rem; color: var(--purple);
  z-index: 1;
}
.rep-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-faint); z-index: 1; margin-bottom: 12px; }
.form-badge {
  z-index: 1; margin-bottom: auto; margin-top: 14px;
  font-family: var(--font-display); font-weight: 700; font-size: 0.7rem;
  background: var(--yellow); color: var(--purple);
  padding: 4px 10px; border-radius: 999px; text-transform: uppercase; letter-spacing: 0.06em;
}

/* -- Live camera-tracking mock (photo + skeleton overlay) -------------------- */
.cam-frame {
  width: 100%; max-width: 300px; margin: 0 auto;
  aspect-ratio: 1152 / 1300;
  background: var(--surface);
  border-radius: 30px;
  border: 3px solid var(--purple);
  padding: 8px;
  box-shadow: 0 30px 70px -30px rgba(53, 24, 107, 0.4);
}
.cam-mock { width: 100%; height: 100%; border-radius: 22px; overflow: hidden; position: relative; background: #1c1330; }
.cam-mock img.cam-photo { width: 100%; height: 100%; object-fit: cover; display: block; }
.cam-mock svg.cam-skeleton { position: absolute; inset: 0; width: 100%; height: 100%; filter: drop-shadow(0 0 4px rgba(53,24,107,0.55)); }
.cam-skel-line { stroke: var(--yellow); stroke-width: 6; stroke-linecap: round; opacity: 0.92; }
.cam-skel-dot { fill: var(--yellow); stroke: var(--purple); stroke-width: 3; }
.cam-skel-dot.head { fill: none; stroke: var(--yellow); stroke-width: 6; }
.cam-mock .cam-badge {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  font-family: var(--font-display); font-weight: 700; font-size: 0.62rem;
  background: var(--yellow); color: var(--purple);
  padding: 4px 10px; border-radius: 999px; text-transform: uppercase; letter-spacing: 0.06em;
  display: inline-flex; align-items: center; gap: 5px;
}
.cam-mock .cam-badge .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--purple); }
@media (prefers-reduced-motion: no-preference) { .cam-mock .cam-badge .dot { animation: pulse 1.4s ease-in-out infinite; } }
.cam-mock .cam-rep {
  position: absolute; top: 12px; right: 12px; z-index: 2;
  background: rgba(36, 21, 57, 0.72); color: #fff; backdrop-filter: blur(4px);
  border-radius: 14px; padding: 6px 12px; text-align: center;
}
.cam-mock .cam-rep .n { font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; line-height: 1; }
.cam-mock .cam-rep .l { font-size: 0.55rem; text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.8; }
.cam-mock .cam-blob { position: absolute; bottom: 10px; right: 10px; width: 30%; z-index: 2; filter: drop-shadow(0 8px 14px rgba(0,0,0,0.35)); }
.cam-mock .cam-player-tag {
  position: absolute; bottom: 12px; left: 12px; z-index: 2;
  background: rgba(36, 21, 57, 0.72); color: #fff; backdrop-filter: blur(4px);
  border-radius: 999px; padding: 5px 12px; font-family: var(--font-display); font-weight: 700; font-size: 0.7rem;
}

/* -- Evolution composite (Progression section) ---------------------------------
   Single pre-composited artwork (levels/arrows baked in) instead of 3 separate
   stage images — a tall composite on mobile, a wide one on desktop, swapped
   via <picture> so only the needed asset downloads. */
.evolution-composite-wrap { max-width: 640px; margin: 0 auto; }
.evolution-composite { width: 100%; display: block; border-radius: var(--radius-lg); box-shadow: 0 20px 50px -28px rgba(53, 24, 107, 0.4); }

/* Progression phone mock (level/strength readout) */
.progression-screen { justify-content: center; gap: 14px; }
.progression-screen .blob-img { width: 96px; }
.prog-stat-row {
  width: 100%; display: flex; justify-content: space-between; align-items: baseline;
  font-family: var(--font-display); font-weight: 700; font-size: 0.75rem;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim); z-index: 1;
}
.prog-stat-row strong { font-size: 1.1rem; color: var(--text); }
.progression-screen .meter { width: 100%; z-index: 1; }

/* -- Customization card ---------------------------------------------------------- */
.customization-blobs { display: flex; justify-content: space-around; gap: 16px; flex-wrap: wrap; padding: 10px 0; }
.customization-blob { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.customization-blob .blob-img { width: 128px; }
.customization-caption {
  font-family: var(--font-display); font-weight: 700; font-size: 0.75rem;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); text-align: center;
}

/* -- Hero trio (final CTA) -------------------------------------------------------- */
.hero-trio { display: flex; align-items: flex-end; justify-content: center; position: relative; margin: 8px 0 28px; }
.hero-trio .blob-img { width: 110px; }
.hero-trio-front { z-index: 2; }
.hero-trio-front .blob-img { width: 156px; }
.hero-trio-back { z-index: 1; margin: 0 -22px; opacity: 0.95; transform: translateY(14px); }

/* -- Signup / dashboard card --------------------------------------------------- */
.signup-card { position: relative; overflow: hidden; }
.signup-card::before {
  content: ""; position: absolute; top: -60%; right: -30%; width: 70%; height: 220%;
  background: var(--yellow); opacity: 0.14; filter: blur(60px);
}
.prize-banner {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 700; font-size: 1.1rem;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  padding: 14px 16px; border-radius: var(--radius-sm);
  margin-bottom: 18px;
}
.prize-note { font-size: 0.72rem; color: var(--text-faint); margin-top: 10px; }

.field { margin-bottom: 14px; position: relative; z-index: 1; }
.field input {
  width: 100%; padding: 15px 16px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-family: var(--font-body); font-size: 1rem;
}
.field input:focus { outline: none; border-color: var(--purple); box-shadow: 0 0 0 3px rgba(53, 24, 107, 0.14); }
.form-note { font-size: 0.78rem; color: var(--text-faint); }
.legal-links { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 0.75rem; color: var(--text-faint); margin-top: 14px; }
.legal-links a { color: var(--text-faint); text-decoration: underline; }
.legal-links a:hover { color: var(--purple); }

/* -- Dashboard (post-signup) --------------------------------------------------- */
.dash-grid { display: grid; gap: 16px; grid-template-columns: repeat(2, 1fr); margin: 20px 0; }
.dash-stat { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px; }
.dash-stat .n { font-family: var(--font-display); font-weight: 700; font-size: 1.6rem; }
.dash-stat .l { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-faint); }
.dash-progress { margin: 14px 0; }
.share-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.ref-link-box {
  display: flex; gap: 8px; align-items: center;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; font-size: 0.85rem; color: var(--text-dim);
}
.ref-link-box code { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--purple); }

/* -- FAQ accordion ----------------------------------------------------------- */
.faq-item { border-bottom: 1px solid var(--border); }
.faq-q {
  width: 100%; text-align: left; background: none; border: none; color: var(--text);
  font-family: var(--font-display); font-weight: 600; font-size: 1.05rem;
  padding: 18px 4px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 12px;
}
.faq-q .plus { transition: transform 0.2s ease; color: var(--purple); font-size: 1.3rem; }
.faq-item[open] .faq-q .plus { transform: rotate(45deg); }
.faq-a { padding: 0 4px 18px; color: var(--text-dim); }

/* -- Footer -------------------------------------------------------------------- */
footer { border-top: 1px solid var(--border); padding: 48px 0 32px; background: var(--purple); color: #fff; }
footer .brand { color: #fff; }
footer .brand-mark { background: var(--yellow); box-shadow: none; }
footer p, footer .form-note { color: #cabdec; }
.footer-grid { display: grid; gap: 28px; grid-template-columns: 1fr; margin-bottom: 28px; }
@media (min-width: 720px) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.footer-links { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.footer-links a { text-decoration: none; color: #d9cff0; font-size: 0.9rem; }
.footer-links a:hover { color: var(--yellow); }
.footer-legal { font-size: 0.75rem; color: #b4a4dd; border-top: 1px solid rgba(255,255,255,0.14); padding-top: 20px; }
.disclaimer-banner {
  background: rgba(255, 198, 41, 0.12); border: 1px solid rgba(255, 198, 41, 0.4);
  border-radius: var(--radius-sm); padding: 12px 16px; font-size: 0.78rem; color: #fff;
  margin-bottom: 20px;
}

/* -- Utility -------------------------------------------------------------------- */
.text-center { text-align: center; }
.mx-auto { margin-left: auto; margin-right: auto; }
.mt-24 { margin-top: 24px; }
.hidden { display: none !important; }
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-display); font-weight: 700; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: 5px 12px; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim);
}
.pill-live { color: #a67a00; border-color: rgba(255, 163, 0, 0.35); background: #fff6e0; }
.pill-live .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--yellow-deep); }
@media (prefers-reduced-motion: no-preference) { .pill-live .dot { animation: pulse 1.4s ease-in-out infinite; } }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
.auth-loading { display: flex; align-items: center; justify-content: center; gap: 8px; }
.auth-loading .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--purple); }
@media (prefers-reduced-motion: no-preference) { .auth-loading .dot { animation: pulse 1.4s ease-in-out infinite; } }
.section-head .pill { margin-top: 14px; }

.section-alt { background: var(--bg-alt); }

/* -- Leaderboard friend search ------------------------------------------------ */
.leaderboard-search { max-width: 360px; margin: 24px auto 0; }
.leaderboard-search-input {
  width: 100%; padding: 12px 18px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-family: var(--font-body); font-size: 0.9rem;
}
.leaderboard-search-input::placeholder { color: var(--text-faint); }
.leaderboard-search-input:focus { outline: none; border-color: var(--purple-2); }
.lb-row-hidden { display: none !important; }
/* A row found by exact name sits anywhere in the field, so it must never read
   as the next line of the contiguous neighborhood above it: dashed rule and
   its own label are the visual break. */
.leaderboard-search-result {
  margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--border);
  animation: lbFoundIn 0.24s ease-out;
}
.leaderboard-search-note {
  font-size: 0.78rem; color: var(--text-dim); text-align: center; margin-bottom: 8px;
}
@keyframes lbFoundIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .leaderboard-search-result { animation: none; }
}

/* -- Legal pages ----------------------------------------------------------- */
.legal-doc { max-width: 760px; margin: 0 auto; padding: 60px 0 90px; }
.legal-doc h1 { font-size: 2rem; }
.legal-doc h2 { font-size: 1.2rem; margin-top: 2em; color: var(--purple); }
.legal-doc p, .legal-doc li { color: var(--text-dim); }
.legal-meta-grid {
  display: grid; grid-template-columns: 1fr; gap: 10px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px 18px; margin-bottom: 20px;
}
@media (min-width: 560px) { .legal-meta-grid { grid-template-columns: 1fr 1fr; } }
.legal-meta-grid div { display: flex; flex-direction: column; gap: 2px; }
.legal-meta-grid span { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-faint); }
.legal-meta-grid strong { font-family: var(--font-display); font-weight: 700; font-size: 0.92rem; color: var(--text); }
.legal-doc table { width: 100%; border-collapse: collapse; margin: 14px 0 22px; font-size: 0.9rem; }
.legal-doc th, .legal-doc td { border: 1px solid var(--border); padding: 8px 10px; text-align: left; color: var(--text-dim); }
.legal-doc th { background: var(--surface-2); color: var(--text); font-family: var(--font-display); }
@media (max-width: 560px) { .legal-doc table { font-size: 0.78rem; } .legal-doc th, .legal-doc td { padding: 6px 7px; } }

/* -- Missions-page header: force subtitle to one line on tablet+; the H1 is
   now a longer sentence (not a short tagline) so it wraps naturally instead. */
.missions-hero-tight h1, .missions-hero-tight .hero-sub { white-space: normal; }
@media (min-width: 600px) {
  .missions-hero-tight h1 { font-size: clamp(1.5rem, 3.6vw, 2.6rem) !important; }
  .missions-hero-tight .hero-sub { white-space: nowrap; font-size: clamp(0.95rem, 1.6vw, 1.15rem); }
}
/* This header is a compact in-app page title (not a full-bleed marketing
   hero), so it shouldn't inherit .hero's near-fullscreen min-height/centering
   or its generous top padding — that's what was pushing the mission cards
   so far below the title/eyebrow. */
/* .hero's display:flex (needed elsewhere for vertical centering) makes a
   flex item's cross-axis auto margins CENTER it at content width instead of
   stretching it to fill — that silently collapsed .container here to the
   grid's content width instead of the intended max-width:1180px. Reset back
   to plain block layout, same fix .hero-splash already applies. */
.missions-hero-tight { display: block; min-height: auto; padding-top: 24px; padding-bottom: 4px; }

/* -- Status hero (get-to-the-top.html): the first thing a signed-up visitor
   sees — position/blob/name card, same centered treatment at every width
   (the blob lives inside the card's top row now, not floating beside it). */
.status-hero { padding-top: 28px; }
.status-card { max-width: 420px; margin: 0 auto; display: flex; flex-direction: column; gap: 14px; }
.status-card-top { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 10px; padding-bottom: 3px; }
.status-blob { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.status-blob .blob-img { width: 52px; flex-shrink: 0; }
.status-blob-name {
  font-family: var(--font-display); font-weight: 800; font-size: 0.8rem;
  color: var(--purple); background: #f1ecfb; border-radius: 999px; padding: 3px 12px;
  white-space: nowrap;
}
.status-position { display: flex; flex-direction: column; gap: 2px; }
.status-position--left { align-items: flex-start; text-align: left; }
.status-position--right { align-items: flex-end; text-align: right; }
.status-position-label { font-family: var(--font-display); font-weight: 700; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); }
.status-position-value { font-family: var(--font-display); font-weight: 800; font-size: clamp(1.35rem, 5.4vw, 1.8rem); color: var(--purple); line-height: 1.15; }
.status-stats-row { display: flex; justify-content: space-between; align-items: stretch; gap: 10px; border-top: 1px solid var(--border); padding-top: 16px; }
/* flex-column + space-between stretches all 3 cells to the row's tallest
   (the tickets cell now has 3 lines vs. the other two's 2), then spreads
   each cell's own lines top-to-bottom — so every cell's top line lands on
   the same row, and every cell's last line (the caption) lands on the same
   row too, regardless of how many lines sit in between. */
.status-stats-row > div { display: flex; flex-direction: column; justify-content: space-between; text-align: center; flex: 1; }
.status-stats-row strong { display: block; font-family: var(--font-display); font-weight: 800; font-size: 1.1rem; color: var(--text); line-height: 1.2; }
.status-stats-row span { display: block; font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-faint); line-height: 1.15; margin-top: 1px; }
.status-countdown { border-top: 1px solid var(--border); padding-top: 12px; text-align: center; }
/* Overrides .checklist-single's own 16px vertical margin: the status-card flex
   column already spaces every row via its own `gap`, so the two stacking would
   otherwise double the space above/below this one row versus its siblings. */
.status-reminder-toggle { margin: 0; }
/* Collapses to a one-line "Daily reminder" title once opted in, to spare
   space in the card — unchecking brings the full explanation back. */
.status-reminder-toggle .reminder-toggle-compact { display: none; }
.status-reminder-toggle input:checked ~ .reminder-toggle-full { display: none; }
.status-reminder-toggle input:checked ~ .reminder-toggle-compact { display: inline; }
.status-cta-stamp { max-width: 420px; margin: 14px auto 0; opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .status-cta-stamp { opacity: 0; animation: heroStampIn 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.2) 0.3s 1 both; }
}

/* -- Mission page specific ---------------------------------------------------- */
.mission-card .btn { margin-top: 4px; }
.mission-card #goldenStatus { margin: 0; }

/* -- Click/tap spark effect (get-to-the-top.html) ---------------------------- */
.click-spark {
  position: fixed; z-index: 9999; pointer-events: none;
  font-size: 1.5rem; line-height: 1; transform: translate(-50%, -50%);
  filter: drop-shadow(0 0 6px rgba(255, 198, 41, 0.8));
  animation: clickSparkPop 0.5s ease-out forwards;
}
@keyframes clickSparkPop {
  0% { opacity: 1; transform: translate(-50%, -50%) scale(0.4) rotate(-12deg); }
  55% { opacity: 1; transform: translate(-50%, -62%) scale(1.25) rotate(10deg); }
  100% { opacity: 0; transform: translate(-50%, -85%) scale(0.85) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .click-spark { display: none; }
}

.checklist { list-style: none; padding: 0; margin: 16px 0; display: flex; flex-direction: column; gap: 10px; }
.checklist li {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px 14px; font-size: 0.95rem; cursor: pointer;
}
.checklist input { accent-color: var(--purple); width: 18px; height: 18px; }
.checklist li.checked { border-color: var(--yellow-deep); color: var(--text); background: #fff8e6; }
.countdown { font-family: var(--font-display); font-weight: 700; font-size: 1.4rem; color: var(--purple); }
.dev-panel {
  position: fixed; bottom: 14px; right: 14px; z-index: 100;
  background: var(--surface); border: 1px solid var(--error); border-radius: var(--radius-sm);
  padding: 10px; font-size: 0.75rem; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; max-width: 220px;
  box-shadow: 0 10px 24px -12px rgba(53, 24, 107, 0.3);
}

/* -- Modal (Train / Recruit mission detail) ------------------------------------ */
.modal-overlay {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(36, 21, 57, 0.6);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.modal-overlay.hidden { display: none; }
.modal-box {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 30px 26px; max-width: 440px; width: 100%; position: relative;
  box-shadow: 0 30px 70px -20px rgba(36, 21, 57, 0.45);
  max-height: calc(100vh - 40px); overflow-y: auto;
}
.modal-box h3 { font-size: 1.3rem; margin-bottom: 16px; padding-right: 24px; }
.modal-close {
  position: absolute; top: 16px; right: 16px;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim);
  font-size: 0.95rem; cursor: pointer; display: grid; place-items: center;
}
.modal-close:hover { color: var(--purple); border-color: var(--purple); }
#newsPopupBody { margin-bottom: 16px; }
#newsPopupBody p + p { margin-top: 10px; }

/* -- Reward pop-up animations: squash & stretch pop-in / pop-out, shared by
   every "you got a reward" celebration box (recruit, newcomer bonus, welcome
   back, share reward, golden ticket reel, exercise reward). The pop-out only
   plays once .is-closing is added — see closeModal() in missions.js and the
   matching close handlers in gacha-reel.js / train-session.js, which wait
   for it to finish before actually hiding the box. -- */
@media (prefers-reduced-motion: no-preference) {
  .celebration-modal-box,
  .newcomer-modal-box,
  .welcome-back-modal-box,
  .gacha-modal-box,
  .exercise-reward-card,
  .weekly-bonus-modal-box {
    animation: rewardPopIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
  }
  .celebration-modal-box.is-closing,
  .newcomer-modal-box.is-closing,
  .welcome-back-modal-box.is-closing,
  .gacha-modal-box.is-closing,
  .exercise-reward-card.is-closing,
  .weekly-bonus-modal-box.is-closing {
    animation: rewardPopOut 0.28s cubic-bezier(0.5, 0, 0.75, 0) both;
  }
  /* No static fallback for this one (unlike the pop in/out above, whose
     un-animated end state is just the box at rest): the whole rule, not
     just the animation, has to stay inside this guard, or a reduced-motion
     visitor gets a permanently visible, off-center white blur instead of
     nothing. */
  .celebration-modal-box::after,
  .newcomer-modal-box::after,
  .welcome-back-modal-box::after,
  .gacha-modal-box::after,
  .exercise-reward-card::after,
  .weekly-bonus-modal-box::after {
    content: ""; position: absolute; top: 50%; left: 50%; width: 55%; height: 55%;
    border-radius: 50%; pointer-events: none; z-index: 0;
    background: radial-gradient(circle, rgba(255,255,255,0.55), rgba(255,255,255,0) 70%);
    animation: rewardImpactSplash 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
  }
}
@keyframes rewardPopIn {
  0%   { transform: scale(0.3, 0.3); opacity: 0; }
  45%  { transform: scale(1.12, 0.88); opacity: 1; }
  65%  { transform: scale(0.94, 1.06); }
  82%  { transform: scale(1.03, 0.99); }
  100% { transform: scale(1, 1); }
}
@keyframes rewardPopOut {
  0%   { transform: scale(1, 1); opacity: 1; }
  30%  { transform: scale(1.08, 0.92); opacity: 1; }
  100% { transform: scale(0.2, 0.2); opacity: 0; }
}
@keyframes rewardImpactSplash {
  0%, 30% { opacity: 0; transform: translate(-50%, -50%) scale(0.2); }
  45%     { opacity: 0.85; transform: translate(-50%, -50%) scale(1.3); }
  100%    { opacity: 0; transform: translate(-50%, -50%) scale(2); }
}

/* -- Recruit modal: bolder outline + icon-only share row + referrals/art footer */
.recruit-modal-box { border: 3px solid var(--purple); }
.share-icons-row { display: flex; gap: 10px; margin-top: 14px; }
.share-icon-btn {
  width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0;
  display: grid; place-items: center; color: #fff;
  box-shadow: 0 4px 0 rgba(0, 0, 0, 0.15);
  transition: transform 0.15s ease;
}
.share-icon-btn:hover { transform: translateY(-2px); }
.share-icon-btn svg { width: 20px; height: 20px; }
.share-icon-whatsapp { background: #25d366; }
.share-icon-instagram { background: radial-gradient(circle at 30% 110%, #fdf497, #fdf497 5%, #fd5949 45%, #d6249f 60%, #285AEB 90%); }
.share-icon-x { background: #000; }
.share-icon-reddit { background: #ff4500; }
.share-icon-email { background: var(--purple); }
.recruit-modal-note { margin: 14px 0 0; }
.recruit-modal-footer { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; margin-top: 24px; }
.recruit-referrals-block { display: flex; flex-direction: column; gap: 4px; }
.recruit-referrals-block .form-note { margin: 0; }
.recruit-referrals-count { font-family: var(--font-display); font-weight: 800; font-size: clamp(1.9rem, 8vw, 2.6rem); color: var(--purple); line-height: 1; }
.recruit-modal-art { width: 34%; max-width: 150px; min-width: 76px; height: auto; display: block; flex-shrink: 0; }

/* -- Recruit celebration popup: friend joined via your link -------------------- */
.celebration-modal-box { text-align: center; position: relative; overflow: hidden; }
.celebration-particles { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.celebration-art { width: 96px; height: auto; margin: 4px auto 8px; display: block; position: relative; z-index: 1; }
.celebration-modal-box h3, .celebration-modal-box > p { position: relative; z-index: 1; }
.celebration-friend {
  position: relative; z-index: 1; display: flex; align-items: center; gap: 12px; justify-content: center;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 16px; margin: 16px 0 20px; text-align: left;
}
.celebration-friend-img { width: 52px; height: 52px; object-fit: contain; flex-shrink: 0; }
.celebration-friend-name { font-family: var(--font-display); font-weight: 800; font-size: 1.05rem; color: var(--purple); }
.celebration-friend-blob { font-size: 0.72rem; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.04em; }
.celebration-modal-box #celebrationContinueBtn { position: relative; z-index: 1; }

/* -- Train intro (Today's exercise: always Jumping Jacks first) ---------------- */
.train-intro { text-align: center; padding: 6px 0 4px; }
.train-intro-icon { display: block; font-size: 3rem; line-height: 1; margin: 8px 0 18px; }

/* -- Exercise picker (inside the Train modal) ----------------------------------- */
.exercise-picker-grid { display: grid; grid-template-columns: 1fr; gap: 10px; margin-top: 18px; }
@media (min-width: 480px) { .exercise-picker-grid { grid-template-columns: repeat(2, 1fr); } }

.exercise-pick-card {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 4px;
  background: var(--surface-2); border: 2px solid var(--border); border-radius: var(--radius);
  padding: 16px 10px; cursor: pointer; font-family: var(--font-body);
  transition: border-color 0.15s ease, transform 0.15s ease, background 0.15s ease;
}
.exercise-pick-card:hover { border-color: var(--purple-2); transform: translateY(-2px); }
.exercise-pick-icon { font-size: 1.7rem; line-height: 1; margin-bottom: 4px; }
.exercise-pick-name { font-family: var(--font-display); font-weight: 700; color: var(--text); font-size: 0.95rem; }
.exercise-pick-rate { font-size: 0.72rem; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.03em; }
.exercise-pick-max { font-size: 0.68rem; color: var(--text-faint); letter-spacing: 0.03em; }
.exercise-pick-cta {
  margin-top: 8px; font-family: var(--font-display); font-weight: 700; font-size: 0.78rem;
  color: var(--purple); background: var(--yellow); border-radius: 999px; padding: 6px 16px;
}
.exercise-pick-card.done { cursor: default; border-color: var(--yellow-deep); background: #fff8e6; }
.exercise-pick-card.done:hover { transform: none; }
.exercise-pick-done {
  margin-top: 8px; font-family: var(--font-display); font-weight: 700; font-size: 0.72rem;
  color: var(--yellow-deep); text-transform: uppercase; letter-spacing: 0.03em;
}

/* -- Full-screen camera session ------------------------------------------------- */
.exercise-session {
  position: fixed; inset: 0; z-index: 400; background: #0c0716;
  overflow: hidden;
}
.exercise-video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transform: scaleX(-1); /* selfie mirror — purely visual, landmark math is unaffected */
}
.exercise-canvas { position: absolute; inset: 0; width: 100%; height: 100%; transform: scaleX(-1); pointer-events: none; }

.exercise-close {
  position: absolute; top: 16px; left: 16px; z-index: 5;
  width: 40px; height: 40px; border-radius: 50%;
  background: rgba(36, 21, 57, 0.65); border: 1px solid rgba(255,255,255,0.25); color: #fff;
  font-size: 1rem; cursor: pointer; display: grid; place-items: center; backdrop-filter: blur(4px);
}

.exercise-blob-reaction {
  position: absolute; top: 16px; right: 16px; z-index: 5;
  width: clamp(64px, 20vw, 110px);
  filter: drop-shadow(0 8px 16px rgba(0,0,0,0.45));
}
.exercise-blob-reaction img { width: 100%; }

.exercise-permission {
  position: absolute; inset: 0; z-index: 4; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 16px; text-align: center; padding: 32px;
  background: rgba(12, 7, 22, 0.82); color: #fff;
}
.exercise-permission.hidden { display: none; }
.exercise-permission p { color: #e6ddf7; max-width: 340px; margin: 0; }

/* Small looping demo of the exercise itself — shown from the moment the
   camera goes live through the get-ready screen and the Ready/Set/Go
   countdown, so a player who doesn't recognize the exercise name (e.g.
   "burpees") knows what to do before it starts counting. Anchored near the
   top so it doesn't cover the middle of the frame the player is using to
   check their own framing on the get-ready screen. */
.exercise-preview {
  position: absolute; top: 12%; left: 50%; transform: translateX(-50%); z-index: 6;
  width: clamp(110px, 30vw, 170px); height: clamp(140px, 38vw, 200px);
  border-radius: var(--radius); overflow: hidden;
  border: 2px solid rgba(255,255,255,0.35); box-shadow: 0 10px 30px rgba(0,0,0,0.5);
  background: rgba(255,255,255,0.08);
  display: flex; align-items: center; justify-content: center; pointer-events: none;
}
.exercise-preview.hidden { display: none; }
.exercise-preview-gif { width: 100%; height: 100%; object-fit: contain; }
.exercise-preview-icon { display: none; font-size: clamp(2.2rem, 11vw, 3.6rem); }
@media (prefers-reduced-motion: reduce) {
  .exercise-preview-gif { display: none; }
  .exercise-preview-icon { display: block; }
}

/* Get-ready screen: camera is already live behind it (unlike
   .exercise-permission, which covers the camera entirely before it starts)
   so the player can check their framing. Self-paced — advanced by the
   player tapping the CTA rather than a timer. Sized to its own content
   (not a full-screen overlay) and sat at the same bottom: 18% height as
   .exercise-name-card below, reusing that same card look, so it lands
   comfortably above where mobile browser chrome can eat the bottom of the
   viewport instead of hugging the very edge. */
.exercise-prepare {
  position: absolute; left: 50%; bottom: 18%; transform: translateX(-50%); z-index: 5;
  width: 92%; max-width: 360px; box-sizing: border-box;
  display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center;
  background: rgba(36, 21, 57, 0.68); backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,0.18); border-radius: var(--radius);
  padding: 18px 20px;
}
.exercise-prepare.hidden { display: none; }
.exercise-prepare p { color: #fff; margin: 0; font-weight: 600; }

.exercise-hud {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
}
.exercise-hud-top {
  position: absolute; top: 10%; left: 0; right: 0;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.exercise-hud.pre-start .exercise-hud-top { display: none; }
.exercise-timer {
  font-family: var(--font-display); font-weight: 800; color: #fff;
  font-size: clamp(3rem, 15vw, 5rem); line-height: 1;
  text-shadow: 0 4px 18px rgba(0,0,0,0.55);
}
.exercise-timer.danger { color: var(--error); animation: exerciseBeat 1s ease-in-out infinite; }
@keyframes exerciseBeat { 0%, 100% { transform: scale(1); } 30% { transform: scale(1.18); } }

.exercise-reps {
  font-family: var(--font-display); font-weight: 700; color: #fff; font-size: 1.1rem;
  text-transform: uppercase; letter-spacing: 0.04em; text-shadow: 0 2px 8px rgba(0,0,0,0.5);
}
.exercise-reps strong { font-size: 1.8rem; margin-right: 4px; }

.exercise-name-card {
  position: absolute; left: 50%; bottom: 18%; transform: translateX(-50%);
  width: 96%; max-width: 96%; box-sizing: border-box;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: rgba(36, 21, 57, 0.68); backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,0.18); border-radius: var(--radius);
  padding: 8px 14px;
  font-family: var(--font-display); font-weight: 700; color: #fff; text-align: center;
}
.exercise-name-card span:first-child {
  font-size: clamp(0.8rem, 3.4vw, 1rem); line-height: 1.15;
  text-transform: uppercase; letter-spacing: 0.02em; color: var(--yellow);
}
.exercise-name-card span:last-child { font-size: 0.72rem; color: #d9cff0; font-weight: 600; }
.exercise-name-brand { display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap; }
.exercise-name-brand-mark {
  width: 16px; height: 16px; border-radius: 4px; flex-shrink: 0;
  background: var(--yellow); color: var(--purple); box-shadow: 0 2px 0 var(--yellow-deep);
  display: grid; place-items: center; font-size: 0.62rem; line-height: 1;
}

/* -- Rep cheer text: one of 20 branded 1-2 word reactions, shown with a
   randomly-picked entrance animation on every counted rep, never repeating
   until the whole set has been through (see CHEER_TEXTS / nextCheerText in
   train-session.js). Small fire/confetti particles ride along on the word
   itself rather than a separate system, so nothing needs syncing. */
.exercise-cheer { position: absolute; top: 36%; left: 0; right: 0; z-index: 7; display: flex; align-items: center; justify-content: center; pointer-events: none; }
.exercise-cheer-word {
  position: absolute; font-family: var(--font-display); font-weight: 800;
  font-size: clamp(1.6rem, 7vw, 2.6rem); color: #fff; text-transform: uppercase;
  letter-spacing: 0.02em; text-shadow: 0 4px 18px rgba(0, 0, 0, 0.55); white-space: nowrap;
}
.cheer-particle { position: absolute; font-size: 1rem; opacity: 0; }
.cheer-particle-fire { left: -22px; top: -8px; animation: cheerParticleFloat 1.1s ease-out forwards; }
.cheer-particle-confetti { right: -20px; top: -12px; animation: cheerParticleFloat 1.2s ease-out 0.05s forwards; }
.cheer-particle-confetti2 { right: 6px; bottom: -18px; animation: cheerParticleFloat 1.15s ease-out 0.1s forwards; }
@keyframes cheerParticleFloat {
  0% { opacity: 0; transform: translateY(0) scale(0.5) rotate(0deg); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-30px) scale(1.1) rotate(25deg); }
}
@keyframes cheerStamp {
  0% { opacity: 0; transform: scale(2.6) rotate(-10deg); filter: brightness(1.8); }
  40% { opacity: 1; transform: scale(0.92) rotate(3deg); filter: brightness(1.1); }
  55% { transform: scale(1.05) rotate(-1deg); }
  70% { transform: scale(1) rotate(0deg); filter: brightness(1); }
  85% { opacity: 1; }
  100% { opacity: 0; transform: scale(1) rotate(0deg); }
}
.cheer-anim-stamp { animation: cheerStamp 1.2s cubic-bezier(0.2, 0.9, 0.3, 1.2) both; }
@keyframes cheerGrow {
  0% { opacity: 0; transform: scale(0); }
  35% { opacity: 1; transform: scale(1.15); }
  50% { transform: scale(0.95); }
  65% { transform: scale(1.05); }
  80% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1); }
}
.cheer-anim-grow { animation: cheerGrow 1.2s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes cheerFromLeft {
  0% { opacity: 0; transform: translateX(-140px) rotate(-8deg); }
  30% { opacity: 1; transform: translateX(0) rotate(0deg); }
  80% { opacity: 1; }
  100% { opacity: 0; transform: translateX(0); }
}
.cheer-anim-left { animation: cheerFromLeft 1.2s ease-out both; }
@keyframes cheerFromRight {
  0% { opacity: 0; transform: translateX(140px) rotate(8deg); }
  30% { opacity: 1; transform: translateX(0) rotate(0deg); }
  80% { opacity: 1; }
  100% { opacity: 0; transform: translateX(0); }
}
.cheer-anim-right { animation: cheerFromRight 1.2s ease-out both; }
@media (prefers-reduced-motion: reduce) {
  .exercise-cheer-word, .cheer-particle { animation: none !important; opacity: 0; }
}

.exercise-countdown-overlay {
  position: absolute; inset: 0; z-index: 6; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  background: rgba(12, 7, 22, 0.45); pointer-events: none;
}
.exercise-countdown-overlay > span {
  font-family: var(--font-display); font-weight: 800; color: #fff;
  font-size: clamp(3rem, 16vw, 6rem); text-shadow: 0 6px 24px rgba(0,0,0,0.6);
  animation: exerciseCountdownPop 1s ease-out;
}

@keyframes exerciseCountdownPop {
  0% { opacity: 0; transform: scale(0.5); }
  25% { opacity: 1; transform: scale(1.15); }
  40% { transform: scale(1); }
  80% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1); }
}

/* Brief happy/sad reaction on the chosen Blob's real art after each rep —
   there's only one static frame per character, so the "pose change" is
   expressed as a distinct bounce+glow (good) vs shake+desaturate (bad)
   animation on that same art rather than swapping to a nonexistent sprite. */
@keyframes blobHappyPop {
  0% { transform: scale(1) rotate(0deg); }
  30% { transform: scale(1.35) rotate(-6deg); }
  55% { transform: scale(1.15) rotate(4deg); }
  100% { transform: scale(1) rotate(0deg); }
}
.blob-happy-pop { animation: blobHappyPop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); filter: drop-shadow(0 0 18px rgba(255, 198, 41, 0.85)); }

@keyframes blobSadPop {
  0% { transform: translateX(0) scale(1); }
  20% { transform: translateX(-6px) scale(0.94) rotate(-4deg); }
  40% { transform: translateX(6px) scale(0.94) rotate(4deg); }
  60% { transform: translateX(-3px) scale(0.96); }
  100% { transform: translateX(0) scale(1); }
}
.blob-sad-pop { animation: blobSadPop 0.55s ease-in-out; filter: grayscale(0.5) brightness(0.85); }

/* -- Exercise reward screen ------------------------------------------------------ */
.exercise-reward {
  position: fixed; inset: 0; z-index: 410;
  background: rgba(12, 7, 22, 0.82); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.exercise-reward-card {
  background: var(--surface); border-radius: var(--radius-lg); padding: 36px 30px;
  max-width: 380px; width: 100%; text-align: center;
  box-shadow: 0 30px 70px -20px rgba(0,0,0,0.5);
}
.exercise-reward-card .blob-img { width: 120px; margin: 0 auto 12px; position: relative; z-index: 1; }
.exercise-reward-card { position: relative; z-index: 1; }
.exercise-reward-reps, .exercise-reward-pwr, .exercise-reward-bonus, .exercise-reward-total {
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.exercise-reward-reps { font-family: var(--font-display); font-weight: 700; font-size: 1.1rem; color: var(--text-dim); margin-bottom: 4px; }
.exercise-reward-reps strong { font-size: 1.6rem; color: var(--text); margin-right: 4px; }
.exercise-reward-reps.reward-reps-zero,
.exercise-reward-reps.reward-reps-zero strong { color: var(--error); }
.exercise-reward-capped,
.exercise-reward-note {
  font-family: var(--font-display); font-weight: 600; font-size: 0.85rem;
  color: var(--text-dim); margin: 0 0 10px; max-width: 32ch; margin-inline: auto;
}
.exercise-reward.reward-no-celebration .exercise-reward-particles { display: none; }
.exercise-reward-breakdown { margin-bottom: 20px; }
.exercise-reward-rate {
  font-family: var(--font-display); font-weight: 600; font-size: 0.85rem;
  color: var(--text-faint); margin: 0 0 2px; letter-spacing: 0.01em;
}
.exercise-reward-pwr {
  font-family: var(--font-display); font-weight: 800; font-size: 1.6rem; color: var(--yellow-deep);
}
/* With a booster in play the base line stops being the headline: it steps
   down to a summand so the grand total below is the number that reads first. */
.exercise-reward-breakdown.has-bonus .exercise-reward-pwr { font-size: 1.15rem; color: var(--text-dim); }
.exercise-reward-breakdown.has-bonus .exercise-reward-pwr .reward-icon { font-size: 0.95rem; }
.exercise-reward-bonus {
  font-family: var(--font-display); font-weight: 700; font-size: 1.15rem; color: var(--purple-2);
  flex-wrap: wrap; margin-top: 2px;
}
.exercise-reward-bonus .reward-icon { font-size: 0.95rem; }
.exercise-reward-bonus-name {
  font-size: 0.8rem; font-weight: 700; color: var(--purple-2);
  background: rgba(106, 63, 214, 0.1); border-radius: 999px; padding: 2px 10px;
}
.exercise-reward-total {
  font-family: var(--font-display); font-weight: 800; font-size: 1.9rem; color: var(--yellow-deep);
  border-top: 2px solid var(--border); margin-top: 10px; padding-top: 10px;
}
.exercise-reward-total .reward-icon { font-size: 1.4rem; }
.reward-icon { font-size: 1.25rem; line-height: 1; }

/* 0-reps variant: same card, no celebration — the blob holds still instead
   of playing the victory bounce. */
.exercise-reward-card .blob-img.blob-static { animation: none; }

/* Confetti burst + rising fire sparks around the reward card — infinite
   loop (no JS retrigger needed) so it's already mid-celebration the instant
   the reward screen is shown, same materialize/fade technique as
   .hero-spark. Purely transform+opacity, positioned via CSS custom props
   set inline per-particle so this stays a single reusable keyframe. */
.exercise-reward-particles { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.reward-confetti {
  position: absolute; top: 50%; left: 50%; opacity: 0; border-radius: 2px;
  animation-name: rewardConfettiBurst; animation-timing-function: ease-out; animation-iteration-count: infinite;
}
@keyframes rewardConfettiBurst {
  0% { opacity: 0; transform: translate(-50%, -50%) translate(0, 0) rotate(0deg) scale(0.6); }
  12% { opacity: 1; }
  70% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) translate(var(--tx), var(--ty)) rotate(var(--rot)) scale(1); }
}
.reward-fire-particle {
  position: absolute; bottom: 8%; font-size: 1.15rem; opacity: 0;
  animation-name: rewardFireRise; animation-timing-function: ease-in; animation-iteration-count: infinite;
}
@keyframes rewardFireRise {
  0% { opacity: 0; transform: translateY(0) scale(0.6); }
  15% { opacity: 1; transform: translateY(-12px) scale(1); }
  80% { opacity: 0.8; }
  100% { opacity: 0; transform: translateY(-160px) scale(0.7); }
}
@media (prefers-reduced-motion: reduce) {
  .reward-confetti, .reward-fire-particle { animation: none; opacity: 0; }
}

/* -- Accessibility: visually-hidden but still screen-reader/SEO visible --- */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* -- Hero: full-bleed splash art + direct CTA ------------------------------ */
.hero-splash {
  display: block; padding: 0; min-height: auto; overflow: hidden;
}
.hero-splash::before, .hero-splash::after { content: none; }
.hero-splash-media { position: relative; line-height: 0; }
.hero-splash-media img {
  width: 100%; height: auto; display: block;
  max-height: 80vh; object-fit: cover; object-position: top center;
}
/* Mobile: cap the art shorter than before (using svh, the *small* viewport
   height that already assumes the browser's address/tab bar is showing) so
   the CTA + note below always land inside the space that's actually visible
   on first load, instead of being sized against the taller vh box and then
   getting pushed under the mobile browser's UI chrome. vh first as a
   fallback for browsers without svh support, svh after to override it. */
@media (max-width: 859px) {
  .hero-splash-media img {
    max-height: 64vh; max-height: 64svh;
    object-fit: contain; object-position: center;
  }
}
/* Desktop: object-fit:contain so the full artwork (including the wordmark
   baked into it) is always visible instead of getting cropped on wide
   monitors — a fixed viewport-height-based box + side fades keep it feeling
   full-bleed instead of a small letterboxed image floating in space. */
@media (min-width: 860px) {
  .hero-splash-media { height: min(68vh, 700px); }
  .hero-splash-media img { width: 100%; height: 100%; max-height: none; object-fit: contain; object-position: center; }
}
/* Individually-animated embers (same materialize -> float up -> fade
   technique as .final-cta-spark below) instead of a tiled GIF loop — each
   dot appears, drifts up and fades out on its own randomized duration (see
   the near-zero animation-delay spread on each span in the HTML, so several
   are already lit the instant the page paints instead of the hero sitting
   dark for a second first) so the whole set reads as an out-of-sync
   ("descompasado") flicker rather than one synchronized pulse. */
.hero-sparks { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.hero-spark {
  position: absolute; width: 9px; height: 9px; border-radius: 50%;
  background: radial-gradient(circle, #fffbe9 0%, #ffcf5c 45%, rgba(255, 163, 0, 0) 75%);
  box-shadow: 0 0 16px 4px rgba(255, 180, 60, 0.55);
  opacity: 0;
  animation-name: heroSparkRise;
  animation-timing-function: ease-in;
  animation-iteration-count: infinite;
}
@media (prefers-reduced-motion: reduce) {
  .hero-spark { animation: none; opacity: 0; }
}
@keyframes heroSparkRise {
  0% { opacity: 0; transform: translateY(0) scale(0.4); }
  16% { opacity: 0.95; transform: translateY(-8px) scale(1); }
  75% { opacity: 0.6; }
  100% { opacity: 0; transform: translateY(-65px) scale(0.7); }
}
.hero-splash-fade {
  position: absolute; left: 0; right: 0; bottom: 0; height: 22%;
  background: linear-gradient(to bottom, rgba(255, 248, 236, 0) 0%, var(--bg) 90%);
  pointer-events: none;
}
.hero-splash-fade-side {
  display: none; position: absolute; top: 0; bottom: 0; width: 14%; z-index: 1; pointer-events: none;
}
@media (min-width: 860px) {
  .hero-splash-fade-side { display: block; }
}
.hero-splash-fade-side.left { left: 0; background: linear-gradient(to right, var(--bg) 0%, rgba(255, 248, 236, 0) 100%); }
.hero-splash-fade-side.right { right: 0; background: linear-gradient(to left, var(--bg) 0%, rgba(255, 248, 236, 0) 100%); }
.hero-splash-content {
  position: relative; z-index: 2; text-align: center;
  margin-top: -20px; padding-bottom: 40px;
}
/* iOS Safari/Chrome's bottom toolbar sits inside the viewport rather than
   shrinking it (viewport-fit=cover, see <meta name="viewport"> in
   index.html), so the CTA and note need real breathing room above it —
   env(safe-area-inset-bottom) is 0 on browsers without a bottom bar, so
   this is a no-op there. */
@media (max-width: 859px) {
  .hero-splash-content { padding-bottom: calc(40px + env(safe-area-inset-bottom, 0px)); }
}
/* Desktop: pull the CTA block further up over the art's fade-out so it
   (and the prizes note under it) land inside a normal laptop viewport
   without scrolling — a shorter media box already reserves less space
   above, this trims the remaining gap. */
@media (min-width: 860px) {
  .hero-splash-content { margin-top: -110px; }
}
.hero-splash-punchline {
  margin: 0 auto 16px; max-width: 30ch;
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  letter-spacing: 0.01em; line-height: 1.2; color: var(--purple);
  font-size: clamp(1.05rem, 4.4vw, 1.55rem);
}
.hero-splash-note {
  margin: 14px 0 0; font-family: var(--font-display); font-weight: 700;
  color: var(--purple); font-size: 1.105rem; opacity: 1;
}
/* Wraps the hero CTA (rather than animating the <a> itself) so the one-shot
   entrance transform can't fight the button's own :hover transform once it
   finishes — the wrapper's forwards-filled end state is scale(1)/rotate(0),
   leaving the child button free to lift on hover as normal afterward. */
.hero-cta-stamp { display: inline-block; opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .hero-cta-stamp {
    opacity: 0;
    animation: heroStampIn 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.2) 0.5s 1 both;
  }
}
/* Rewards section CTA: same stamp, but triggered by scroll position (see
   wireRewardsCtaReveal() in js/app.js) instead of a fixed page-load delay —
   this section isn't above the fold, so a load-timed delay could fire while
   it's still off-screen. JS adds .in-view ~0.5s after it's actually visible. */
.rewards-cta-stamp { display: inline-block; opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .rewards-cta-stamp { opacity: 0; }
  .rewards-cta-stamp.in-view {
    animation: heroStampIn 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.2) 1 both;
  }
}
/* The CTA snaps in like a rubber stamp hitting the page: oversized +
   skewed, then slammed down to size with a small settle-bounce, instead of
   a gentle fade. */
@keyframes heroStampIn {
  0% { opacity: 0; transform: scale(2.4) rotate(-8deg); filter: brightness(1.8) drop-shadow(0 0 18px rgba(255, 198, 41, 0.9)); }
  45% { opacity: 1; transform: scale(0.92) rotate(4deg); filter: brightness(1.3) drop-shadow(0 0 10px rgba(255, 198, 41, 0.6)); }
  70% { transform: scale(1.06) rotate(-2deg); filter: brightness(1.05) drop-shadow(0 0 4px rgba(255, 198, 41, 0.3)); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); filter: brightness(1) drop-shadow(0 0 0 rgba(255, 198, 41, 0)); }
}

/* -- Signup card: title / form / photo / legal order on mobile, 2-col on
   desktop. Title/subtitle are centered at every width; on desktop the title
   spans the full row alone (not sharing it with the photo). */
.signup-grid { display: grid; grid-template-columns: 1fr; gap: 24px; }
.signup-title-block { text-align: center; }
/* Sits under whichever signup card is showing, so it keeps the cards' own
   centered alignment rather than the block's default. */
#authError { text-align: center; margin-top: 12px; }
.signup-subheading { font-size: 1.05rem; margin-top: -8px; }
@media (min-width: 940px) {
  .signup-grid {
    grid-template-columns: 1.05fr 0.95fr;
    grid-template-areas: "title title" "form photo" "legal legal";
    column-gap: 40px; row-gap: 24px; align-items: stretch;
  }
  .signup-title-block { grid-area: title; max-width: 720px; margin: 0 auto; }
  .signup-form-block { grid-area: form; }
  .legal-links { grid-area: legal; }
  /* The photo's row is shared only with the signup card itself (legal links
     now live in their own row below), so stretching it to the row height and
     letting the image contain-fit — bottom-anchored — spans it from the
     card's top to its bottom with no cropping. */
  .signup-photo { grid-area: photo; display: flex; }
  .signup-photo img {
    width: 100%; height: 100%; object-fit: contain; object-position: bottom;
  }
}

/* -- Customization: single showcase image instead of 2 captioned blobs ---- */
.customization-showcase { width: 100%; max-width: 320px; margin: 0 auto; display: block; border-radius: var(--radius); }

/* -- Battle system: vertical PWR org-chart -------------------------------- */
.pwr-flowchart { display: flex; flex-direction: column; align-items: center; max-width: 640px; margin: 0 auto; }
.pwr-flow-node {
  background: var(--surface); border: 2px solid var(--border); border-radius: var(--radius);
  padding: 16px 26px; text-align: center; min-width: 220px;
  box-shadow: 0 16px 34px -24px rgba(53, 24, 107, 0.35);
  display: flex; flex-direction: column; gap: 4px; position: relative;
}
.pwr-flow-node.pwr-flow-total { border-color: var(--yellow-deep); background: #fff8e6; }
.pwr-flow-label {
  font-family: var(--font-display); font-weight: 700; font-size: 0.76rem;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim);
}
.pwr-flow-value { font-family: var(--font-display); font-weight: 800; font-size: 1.7rem; color: var(--purple); }
.pwr-flow-value small { font-size: 0.85rem; font-weight: 700; color: var(--text-dim); }
.pwr-flow-branch { width: 100%; max-width: 420px; height: 32px; position: relative; }
.pwr-flow-branch::before {
  content: ""; position: absolute; top: 0; left: 50%; width: 2px; height: 16px;
  background: var(--purple-2); transform: translateX(-50%);
}
.pwr-flow-branch::after {
  content: ""; position: absolute; top: 16px; left: 12%; right: 12%; height: 2px; background: var(--purple-2);
}
.pwr-flow-children { display: flex; flex-wrap: wrap; gap: 24px; justify-content: center; width: 100%; }
.pwr-flow-children .pwr-flow-node::before {
  content: ""; position: absolute; top: -16px; left: 50%; width: 2px; height: 16px;
  background: var(--purple-2); transform: translateX(-50%);
}

/* -- Final CTA: single allies illustration -------------------------------- */
.final-cta-section { position: relative; overflow: hidden; }
.final-cta-allies { max-width: 280px; margin: 0 auto 12px; position: relative; z-index: 1; }
.final-cta-allies .blob-img { width: 100%; display: block; }

/* Individually-animated embers spanning the full section width (unlike the
   tiled hero-sparks GIF used in the hero banner) — each spark materializes,
   drifts up and fades out on its own randomized delay/duration so they read
   as scattered, natural embers instead of one synchronized loop. Desaturated
   slightly vs. the hero version per design note ("too burned/intense"). */
.final-cta-sparks { position: absolute; inset: 0; pointer-events: none; filter: saturate(0.9); z-index: 0; }
.final-cta-spark {
  position: absolute; width: 6px; height: 6px; border-radius: 50%;
  background: radial-gradient(circle, #fff6e0 0%, #ffcf5c 45%, rgba(255, 163, 0, 0) 75%);
  box-shadow: 0 0 8px 2px rgba(255, 180, 60, 0.45);
  opacity: 0;
  animation-name: finalCtaSparkRise;
  animation-timing-function: ease-in;
  animation-iteration-count: infinite;
}
@media (prefers-reduced-motion: reduce) {
  .final-cta-spark { animation: none; opacity: 0; }
}
@keyframes finalCtaSparkRise {
  0% { opacity: 0; transform: translateY(0) scale(0.4); }
  14% { opacity: 1; transform: translateY(-8px) scale(1); }
  70% { opacity: 0.85; }
  100% { opacity: 0; transform: translateY(-70px) scale(0.7); }
}

/* -- Footer brand badge: the wordmark's purple text is invisible directly
   on the footer's purple background, so it needs a light "pill" behind it.
   Laid out exactly like the nav header (icon left, wordmark + slogan
   stacked to its right) rather than the header's own absolute-positioned
   slogan trick — that trick relies on overflowing below the wordmark's box
   into empty nav space, which would just spill the slogan out the bottom
   of this pill instead of sitting inside it. */
.footer-brand-badge { display: inline-flex; align-items: center; gap: 10px; background: var(--bg); border-radius: 999px; padding: 8px 20px 8px 8px; }
.footer-brand-text { display: flex; flex-direction: column; gap: 1px; }
.footer-brand-badge .wordmark { transform: none; }
/* Higher specificity than .brand small (position:absolute/color:text-faint,
   tuned for the nav header's fixed-height row) so this actually renders in
   normal flow, in-color, instead of escaping below the pill transparently. */
.footer-brand-badge small.footer-brand-slogan {
  display: block; position: static; max-width: none; overflow: visible; text-overflow: clip;
  font-family: var(--font-display); font-weight: 700; font-size: 0.58rem; line-height: 1.2;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--purple);
  white-space: nowrap;
}

/* -- Quest cards: title-first layout with a bigger, "protagonist" image ---- */
.quest-card-media-first h3 { margin: 0 0 2px; }
.quest-card-image { height: 180px; margin: 4px 0 4px; }

/* -- Get-to-the-top mission cards: much tighter title/subtitle/image rhythm
   (-80%) than the shared .quest-card gap — scoped to .mission-card so
   index.html's teaser/reward cards (also .quest-card) stay untouched. */
.mission-card { gap: 3px; }
.mission-card h3 { margin-bottom: 0; }
.mission-card p { margin-bottom: 0; }
.mission-card .quest-card-image { margin-top: 1px; margin-bottom: 1px; }
.quest-card-image img {
  max-height: 100%; max-width: 100%; width: auto; display: block;
  border-radius: var(--radius-lg); box-shadow: 0 16px 40px -20px rgba(53, 24, 107, 0.35);
}
/* Even squares across a row (Daily Missions / Rewards / The Loop), all the
   same size, with a small white mat + brand-yellow outline so the artwork
   reads clearly against the page background. */
.quest-card-image-square {
  width: 100%; max-width: 340px; height: auto; aspect-ratio: 1 / 1; margin: 0 auto;
  box-sizing: border-box; background: #fff; padding: 10px;
  border: 2px solid var(--yellow); border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px -20px rgba(53, 24, 107, 0.35);
}
.quest-card-image-square img {
  width: 100%; height: 100%; max-width: 100%; max-height: 100%; object-fit: cover;
  display: block; border-radius: var(--radius-sm); box-shadow: none;
}

/* -- "Invisible" cards: keep the card's grid/flex structure (and keep
   title/image/description centered) but drop the visible background/
   border/shadow, so the image reads as the protagonist. Used by Daily
   Missions, Rewards and The Loop. */
.card-plain {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  padding: 12px 4px;
}

/* -- Image → signup link: the whole image area is clickable, wrapped in an
   <a> inside the fixed-size media slot so the image keeps its centering/
   height-cap behavior exactly as before. */
.quest-card-image .quest-card-link {
  display: flex; align-items: flex-end; justify-content: center;
  width: 100%; height: 100%;
}
.quest-card-image-square .quest-card-link { display: block; width: 100%; height: 100%; }
.quest-card-link img { transition: transform 0.2s ease; }
.quest-card-link:hover img, .quest-card-link:focus-visible img { transform: scale(1.04); }

/* -- Daily Missions (index.html and get-to-the-top.html both use #missions):
   this grid needs more room per card than Rewards/The Loop (it carries the
   reward tag text), so it stays single-column a bit longer before switching
   to 3-up. */
@media (min-width: 720px) and (max-width: 1099px) {
  #missions .grid-3 { grid-template-columns: 1fr; }
}

/* -- Holofoil shimmer overlay (Customization showcase) ---------------------
   Pure-CSS "trading card foil" sheen: a diagonal rainbow band swept across
   via background-position, blended with color-dodge so it reads as a tinted
   highlight over the art rather than a flat rainbow decal. No extra image
   asset, so it's effectively free — the deliberately-requested alternative
   (a GIF overlay) would only add file weight for a worse-looking result. */
.holofoil { position: relative; border-radius: var(--radius-lg); overflow: hidden; isolation: isolate; }
.holofoil img { display: block; width: 100%; border-radius: var(--radius-lg); }
.holofoil::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg,
    transparent 0%, transparent 38%,
    rgba(255, 255, 255, 0.55) 45%,
    rgba(255, 198, 41, 0.55) 48%,
    rgba(106, 63, 214, 0.5) 51%,
    rgba(0, 180, 255, 0.4) 54%,
    rgba(255, 255, 255, 0.5) 58%,
    transparent 65%, transparent 100%);
  background-size: 250% 250%;
  background-position: 20% 20%;
  opacity: 0.85;
  mix-blend-mode: color-dodge;
}
@media (prefers-reduced-motion: no-preference) {
  .holofoil::after { animation: holofoilSweep 4.5s ease-in-out infinite; }
}
@keyframes holofoilSweep {
  0%, 100% { background-position: 0% 0%; }
  50% { background-position: 100% 100%; }
}
/* Reward/mission images already have their own white-mat + border frame
   (.quest-card-image-square) with a smaller inner radius than .holofoil
   assumes — restate it here (higher specificity via the compound selector
   wins regardless of source order) so the artwork corners stay tucked
   inside the frame. */
.quest-card-image-square.holofoil img,
.quest-card-image-square.holofoil-gold img,
.quest-card-image-square.holofoil-legendary img {
  border-radius: var(--radius-sm);
}

/* -- Premium/legendary gold holofoil (Rewards) ------------------------------
   Same sweep technique as .holofoil, but warmer/richer tones and a deeper
   amber frame so the prize art reads as "legendary tier" rather than the
   everyday brand-yellow shimmer used on Daily Missions. */
.holofoil-gold {
  position: relative; overflow: hidden; isolation: isolate;
  border-color: var(--yellow-deep); box-shadow: 0 6px 16px -10px rgba(255, 163, 0, 0.4);
}
.holofoil-gold::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg,
    transparent 0%, transparent 34%,
    rgba(255, 245, 200, 0.48) 42%,
    rgba(255, 198, 41, 0.6) 47%,
    rgba(255, 145, 0, 0.65) 50%,
    rgba(150, 90, 10, 0.38) 53%,
    rgba(255, 245, 200, 0.42) 59%,
    transparent 67%, transparent 100%);
  background-size: 260% 260%;
  background-position: 20% 20%;
  opacity: 0.7;
  mix-blend-mode: color-dodge;
}
@media (prefers-reduced-motion: no-preference) {
  .holofoil-gold::after { animation: holofoilSweep 5.5s ease-in-out infinite; }
}

/* -- Legendary lilac holofoil (Share & Earn) --------------------------------
   The purple sibling of .holofoil-gold, for the one card on the missions page
   that isn't a daily mission. Deliberately calmer than the gold original:
   color-dodge over a purple base blows out to near-white, so this blends with
   soft-light at a lower opacity and sweeps more slowly. */
.holofoil-legendary {
  position: relative; overflow: hidden; isolation: isolate;
  border-color: var(--purple-2); box-shadow: 0 6px 16px -10px rgba(53, 24, 107, 0.45);
}
.holofoil-legendary::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg,
    transparent 0%, transparent 36%,
    rgba(255, 255, 255, 0.4) 43%,
    rgba(198, 176, 240, 0.45) 47%,
    rgba(106, 63, 214, 0.4) 50%,
    rgba(53, 24, 107, 0.24) 54%,
    rgba(255, 255, 255, 0.34) 60%,
    transparent 68%, transparent 100%);
  background-size: 260% 260%;
  background-position: 20% 20%;
  opacity: 0.55;
  mix-blend-mode: soft-light;
}
@media (prefers-reduced-motion: no-preference) {
  .holofoil-legendary::after { animation: holofoilSweep 6.5s ease-in-out infinite; }
}

/* -- Share & Earn: the standalone "extra mission" slot ----------------------
   Sits above .grid-3 rather than inside it, capped near one grid column's
   width so it reads as one special extra card instead of a wide banner. The
   grid is already single-column below 720px, so this stacks with it naturally
   and needs no breakpoint of its own. Declared after .card/.card-plain so the
   lilac fill and purple frame win at equal specificity. */
.quest-card-legendary-wrap { max-width: 380px; margin: 0 auto 20px; }
.quest-card-legendary {
  background: #f3edfb;
  border: 2px solid var(--purple-2);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 30px -16px rgba(53, 24, 107, 0.5);
  padding: 16px 14px;
}
.quest-card-eyebrow {
  font-family: var(--font-display); font-weight: 800; font-size: 0.62rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--purple-2);
  line-height: 1;
}
.quest-card-warning { margin: 2px 0 0; color: var(--text-dim); line-height: 1.35; }

/* -- Share & Earn modal ----------------------------------------------------- */
.share-earn-modal-box { border: 3px solid var(--purple-2); }
.share-earn-step {
  margin: 16px 0 6px;
  font-family: var(--font-display); font-weight: 800; font-size: 0.85rem;
  color: var(--purple);
}
.share-earn-requirements-desc { margin: 0; font-size: 0.82rem; color: var(--text-dim); }
.share-earn-warning {
  margin: 10px 0 0; padding: 10px 12px; border-radius: var(--radius-sm);
  background: #f3edfb; border: 1px solid rgba(106, 63, 214, 0.3);
  font-size: 0.78rem; line-height: 1.4; color: var(--text);
}
.share-earn-form { display: flex; gap: 8px; align-items: center; }
.share-earn-form input {
  flex: 1; min-width: 0;
  padding: 12px 14px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-family: var(--font-body); font-size: 0.9rem;
}
.share-earn-form input:focus {
  outline: none; border-color: var(--purple); box-shadow: 0 0 0 3px rgba(53, 24, 107, 0.14);
}
.share-earn-form .btn { white-space: nowrap; flex-shrink: 0; }
.share-earn-error { color: var(--error); margin: 8px 0 0; }
.share-earn-posts { display: flex; flex-direction: column; gap: 8px; }
.share-earn-post {
  display: flex; flex-direction: column; gap: 3px;
  padding: 8px 10px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: 0.76rem; color: var(--text-dim);
}
.share-earn-post-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.share-earn-post-platform { font-family: var(--font-display); font-weight: 800; color: var(--purple); }
.share-earn-post-status {
  font-family: var(--font-display); font-weight: 700; font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.04em;
  padding: 2px 8px; border-radius: 999px;
  background: var(--bg-alt); color: var(--text-dim);
}
.share-earn-post-verified { border-color: var(--yellow-deep); background: #fff8e6; }
.share-earn-post-verified .share-earn-post-status { background: var(--yellow); color: var(--purple); }
.share-earn-post-invalid .share-earn-post-status { background: #f8e3e1; color: var(--error); }
.share-earn-post-url { color: var(--purple-2); text-decoration: none; overflow-wrap: anywhere; }
.share-earn-post-url:hover { text-decoration: underline; }
.share-earn-post-meta { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 8px; }
.share-earn-post-views { font-family: var(--font-display); font-weight: 700; color: var(--purple); }
.share-earn-post-reason { margin: 0; color: var(--error); }
.share-earn-disclaimer {
  margin: 14px 0 0; padding-top: 10px; border-top: 1px dashed var(--border);
  text-align: center;
}

/* ============================================================================
   Power / Golden Ticket economy
   ========================================================================== */

/* -- Golden Ticket bonus line: secondary to the main purple reward pill,
   light cream/gold container so it reads as a bonus, not the headline. -- */
.quest-bonus-pill {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  width: 100%; box-sizing: border-box;
  background: #fff6e0; border: 1px solid rgba(255, 163, 0, 0.35);
  color: var(--purple);
  font-family: var(--font-display); font-weight: 700;
  padding: 6px 12px; border-radius: var(--radius);
  font-size: clamp(0.62rem, 2.6cqw, 0.78rem);
  line-height: 1.3; text-align: center;
}
/* index.html's "Ranking Daily Missions" teaser only — a bigger, bolder
   variant so these 3 bonus pills read as more important there than the
   get-to-the-top.html mission cards, which keep the base size above. */
.quest-bonus-pill-lg {
  font-size: clamp(0.74rem, 3.1cqw, 0.94rem);
  font-weight: 800;
}

/* -- index.html: compact 3-item economy explainer, above the signup CTA -- */
.economy-explainer {
  display: flex; flex-direction: column; gap: 10px;
  max-width: 900px; margin: 28px auto 0;
}
@media (min-width: 720px) {
  .economy-explainer { flex-direction: row; align-items: stretch; }
}
.economy-item {
  display: flex; align-items: center; justify-content: center; text-align: center;
  gap: 6px; flex: 1;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px 16px;
  font-family: var(--font-display); font-weight: 600; font-size: 0.85rem;
  color: var(--text-dim); line-height: 1.3;
}

/* -- Pre-signup CTA microcopy: smaller, muted, secondary to the button -- */
.cta-microcopy {
  margin: 10px 0 0; font-family: var(--font-body); font-weight: 500;
  font-size: 0.8rem; color: var(--text-faint);
}

/* -- Train/Golden mission-card extra readouts (get-to-the-top.html) -- */
.mission-progress {
  margin: 0; text-align: center;
  font-family: var(--font-display); font-weight: 700; font-size: 0.78rem;
  color: var(--text-dim);
}
/* Shares .mission-progress's box (margin/size) so it lines up with
   trainProgress/recruitProgress across the 3 mission cards — only the
   color differs, to emphasize the ticket count. */
.mission-ticket-balance { color: var(--purple); }

/* -- Status card: Power-decay contextual message ---------------------------
   Three tones, all within the existing cream/purple/gold palette — no red,
   per the "no aggressive/error styling" requirement. */
.status-decay-msg {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  border-radius: var(--radius-sm); padding: 10px 14px;
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: 0.82rem; text-align: center; color: var(--text-dim);
}
.status-decay-text { margin: 0; }
.status-decay-msg.decay-active { background: #f1ecfb; border-color: rgba(106, 63, 214, 0.25); color: var(--text); }
.status-decay-msg.decay-grace { background: #fbefd8; border-color: var(--border); color: var(--text); }
.status-decay-msg.decay-decaying { background: #fff3d6; border-color: var(--yellow-deep); color: var(--text); }
.status-decay-cta {
  display: inline-flex; align-items: center; justify-content: center; align-self: center;
  min-height: 44px; box-sizing: border-box; padding: 8px 18px;
  font-family: var(--font-display); font-weight: 700; font-size: 0.78rem;
  color: var(--purple); background: var(--yellow); border-radius: 999px;
  text-decoration: none;
}

/* -- Leaderboard "Share my position" reward --------------------------------- */
.share-position-block { max-width: 420px; margin: 20px auto 0; text-align: center; }
.share-position-btn { width: 100%; display: flex; align-items: center; justify-content: center; gap: 10px; min-height: 44px; }
.share-position-reward {
  font-family: var(--font-display); font-weight: 800; font-size: 0.85rem;
  background: rgba(53, 24, 107, 0.14); color: var(--purple);
  padding: 3px 10px; border-radius: 999px;
}
.share-position-btn.share-position-done,
.share-position-btn.share-position-pending { opacity: 0.75; animation: none; }
.share-position-microcopy { margin: 10px 0 0; }

/* -- Missions-done ring: replaces the plain "0/3" with a small circular
   progress indicator that fills a third per completed mission, then swaps
   to a solid checkmark badge once all 3 are done. -- */
.mission-ring {
  --ring-pct: 0;
  width: 46px; height: 46px; margin: 0 auto 4px;
  border-radius: 50%;
  background: conic-gradient(var(--green) calc(var(--ring-pct) * 1%), var(--border) 0);
  display: grid; place-items: center;
  transition: background 0.5s ease;
}
.mission-ring-inner {
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--surface);
  display: grid; place-items: center;
}
.mission-ring-inner strong { font-family: var(--font-display); font-weight: 800; font-size: 0.8rem; color: var(--text); line-height: 1; }
.mission-ring-inner .mission-ring-check { display: none; font-size: 1.1rem; color: #fff; line-height: 1; }
.mission-ring.mission-ring-done { background-color: var(--green); background-image: none; }
.mission-ring.mission-ring-done .mission-ring-inner { background: transparent; }
.mission-ring.mission-ring-done .mission-ring-inner strong { display: none; }
.mission-ring.mission-ring-done .mission-ring-inner .mission-ring-check { display: block; }
@media (prefers-reduced-motion: reduce) {
  .mission-ring { transition: none; }
}

/* -- Newcomer Bonus: status-card microcopy (days-left readout) + popup -- */
.status-newcomer-msg {
  margin: 0; text-align: center;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(0.68rem, 3vw, 0.8rem);
  color: var(--purple); background: #fff6e0; border: 1px solid rgba(255, 163, 0, 0.35);
  border-radius: var(--radius-sm); padding: 6px 12px;
}
.newcomer-modal-box { border: 3px solid var(--yellow-deep); text-align: center; position: relative; overflow: hidden; }
.reward-highlight {
  font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; line-height: 1.2;
  color: var(--yellow-deep); margin: 4px 0 10px; position: relative; z-index: 1;
}
/* Same slot, no payout behind it: the referral day cap can confirm a friend
   and grant nothing, and reward styling on that line would read as a reward. */
.reward-highlight-muted { font-size: 0.95rem; font-weight: 700; color: var(--text-dim); }
.newcomer-modal-icon { display: block; font-size: 2.6rem; line-height: 1; margin: 4px 0 10px; }

/* -- Welcome Back: login-reward popup for the "Connecting today" mission,
   same confetti-in-a-box treatment as the recruit celebration popup. -- */
.welcome-back-modal-box { border: 3px solid var(--yellow-deep); text-align: center; position: relative; overflow: hidden; }
.welcome-back-icon { display: block; font-size: 2.6rem; line-height: 1; margin: 4px 0 10px; position: relative; z-index: 1; }
.welcome-back-modal-box h3, .welcome-back-modal-box > p, .welcome-back-modal-box #welcomeBackContinueBtn,
.welcome-back-modal-box #shareRewardContinueBtn, .welcome-back-modal-box #shareEarnCelebrationClaimBtn { position: relative; z-index: 1; }

/* -- Weekly Golden Ticket bonus bar (status card) --------------------------- */
.status-weekly-block { display: flex; flex-direction: column; gap: 5px; }
.status-weekly-row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.status-weekly-label { font-family: var(--font-display); font-weight: 700; font-size: 0.72rem; color: var(--text-dim); }
.status-weekly-reward { font-family: var(--font-display); font-weight: 800; font-size: 0.78rem; color: var(--purple); white-space: nowrap; }
/* Taller than a plain progress bar so the value can sit centered on top of
   the fill instead of floating in the row above it — same solid gold as
   .mission-ring's completed state, for one consistent "progress" color. */
.status-weekly-bar { position: relative; height: 24px; border-radius: 999px; background: var(--surface-2); border: 1px solid var(--border); overflow: hidden; }
.status-weekly-fill { height: 100%; width: 0; border-radius: 999px; background-color: var(--green); background-image: none; transition: width 0.5s ease; }
.status-weekly-value {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-weight: 800; font-size: 0.7rem; color: var(--purple);
  white-space: nowrap; pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .status-weekly-fill { transition: none; }
}
/* Fallback for whenever the celebration popup never found a clear moment to
   open on its own (see maybeShowWeeklyBonusPopup() in js/missions.js) —
   same pill shape as .status-decay-cta, the other small CTA in this card. */
.status-weekly-claim-btn {
  display: inline-flex; align-items: center; justify-content: center; align-self: center;
  min-height: 36px; box-sizing: border-box; padding: 6px 18px; border: none; cursor: pointer;
  font-family: var(--font-display); font-weight: 700; font-size: 0.74rem;
  color: var(--purple); background: var(--yellow); border-radius: 999px;
}

/* -- Weekly Goal celebration popup: same confetti-in-a-box treatment as the
   Welcome Back / Share reward popups, plus the status card's own progress
   bar (reused as-is, fully filled) to show the completed PWR total. -- */
.weekly-bonus-modal-box { border: 3px solid var(--yellow-deep); text-align: center; position: relative; overflow: hidden; }
.weekly-bonus-modal-box h3,
.weekly-bonus-modal-box > p,
.weekly-bonus-modal-box .status-weekly-bar,
.weekly-bonus-modal-box #weeklyBonusPopupContinueBtn { position: relative; z-index: 1; }
.weekly-bonus-modal-box .status-weekly-bar { max-width: 260px; margin: 4px auto 14px; }

/* -- Gacha Reward Reel modal: gold/yellow outline (vs. the recruit modal's
   purple one), per the brief. The reel's landing reward is decided and
   persisted before this ever opens (see useGoldenTicket() in js/waitlist.js
   and openGachaReel() in js/gacha-reel.js) — this is a pure visual reveal. */
.gacha-modal-box { border: 3px solid var(--yellow-deep); text-align: center; position: relative; overflow: hidden; }
.gacha-reel-viewport {
  position: relative; overflow: hidden; height: 96px; margin: 18px 0 6px;
  border-radius: var(--radius); border: 2px solid var(--border); background: var(--surface-2);
}
.gacha-reel-track { position: relative; display: flex; align-items: center; height: 100%; will-change: transform; }
.gacha-reel-cell { flex-shrink: 0; width: 92px; height: 100%; display: flex; align-items: center; justify-content: center; position: relative; padding: 6px; box-sizing: border-box; }
/* Each cell is now a small labeled box (icon + 1-2 word reward name), not a
   bare icon — the box is inset from the cell's own edges so the reel's
   cell-to-cell spacing/landing math (all offsetLeft/offsetWidth-driven,
   untouched) keeps working exactly as before. */
.gacha-reel-box {
  width: 100%; height: 100%; box-sizing: border-box;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  padding: 6px 3px; border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--border);
}
/* Color emoji render with extra visual bleed below their nominal line box —
   the gap above (and this icon's own line-height) is deliberately generous
   so that bleed never reads as overlapping the label underneath it. */
.gacha-reel-icon { font-size: 1.7rem; line-height: 1.3; filter: drop-shadow(0 2px 4px rgba(53, 24, 107, 0.25)); }
.gacha-reel-label {
  font-family: var(--font-display); font-weight: 700; font-size: 0.68rem; line-height: 1.2;
  color: var(--text-dim); text-align: center;
}
.gacha-reel-pointer {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 3px;
  background: var(--yellow-deep); transform: translateX(-50%);
  box-shadow: 0 0 10px 2px rgba(255, 163, 0, 0.6); pointer-events: none; z-index: 2;
}
/* Deliberately-placed high-value cards (top1/+100/+50) shown just before the
   reel lands (a "so close!" near-miss) and just after it (a teaser of what's
   still in the pool) — a lighter, static glow so they read as desirable
   without being mistaken for the actual win (see .gacha-cell-win below). */
.gacha-reel-cell-upcoming .gacha-reel-box {
  border-color: var(--yellow-deep);
  box-shadow: 0 0 8px 1px rgba(255, 198, 41, 0.4);
}
.gacha-reel-cell-upcoming .gacha-reel-icon {
  filter: drop-shadow(0 0 6px rgba(255, 198, 41, 0.7));
}
.gacha-reel-cell.gacha-cell-win { z-index: 3; }
.gacha-reel-cell.gacha-cell-win::after {
  content: ""; position: absolute; inset: -10px; border-radius: 50%; z-index: -1;
  background: radial-gradient(circle, rgba(255, 198, 41, 0.55), transparent 70%);
}
.gacha-reel-cell.gacha-cell-win .gacha-reel-icon {
  animation: gachaWinPop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  filter: drop-shadow(0 0 16px rgba(255, 198, 41, 0.9));
}
@keyframes gachaWinPop {
  0% { transform: scale(1); }
  50% { transform: scale(1.5) rotate(-4deg); }
  100% { transform: scale(1.25) rotate(0deg); }
}
.gacha-reel-particles { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 1; }
.gacha-result { margin-top: 18px; }
.gacha-result-text { margin: 0 0 16px; font-family: var(--font-display); font-weight: 800; font-size: 1.3rem; color: var(--purple); }
@media (prefers-reduced-motion: reduce) {
  .gacha-reel-cell.gacha-cell-win .gacha-reel-icon { animation: none; }
}

/* -- Reward feedback: toasts + fly-to-counter ------------------------------- */
.toast-stack {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  z-index: 500; width: min(92vw, 360px);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  pointer-events: none;
}
/* The toast can land over any surface, including the purple footer it would
   otherwise be invisible against, so its own edge has to carry the contrast. */
.toast {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: var(--purple); color: #fff; border-radius: var(--radius);
  border: 2px solid var(--yellow);
  padding: 10px 18px;
  box-shadow: 0 0 0 1px rgba(12, 7, 22, 0.55), 0 14px 30px -10px rgba(12, 7, 22, 0.6);
  font-family: var(--font-display);
  animation: toastIn 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
}
.toast-main { font-weight: 800; font-size: 0.95rem; }
.toast-sub { font-weight: 600; font-size: 0.72rem; color: #e3d9f7; }
.toast.toast-out { animation: toastOut 0.35s ease-in forwards; }
@keyframes toastIn {
  0% { opacity: 0; transform: translateY(16px) scale(0.9); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes toastOut {
  0% { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(8px) scale(0.95); }
}
@media (prefers-reduced-motion: reduce) {
  .toast, .toast.toast-out { animation: none; }
}

.ticket-fly {
  position: fixed; z-index: 9999; pointer-events: none;
  font-size: 1.6rem; line-height: 1; transform: translate(-50%, -50%);
  filter: drop-shadow(0 0 8px rgba(255, 198, 41, 0.85));
  animation: ticketFly 0.7s cubic-bezier(0.3, 0.1, 0.3, 1) forwards;
}
@keyframes ticketFly {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
  15% { opacity: 1; transform: translate(-50%, -50%) scale(1.1); }
  100% { opacity: 0.2; transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0.4); }
}
.ticket-bump { animation: ticketBump 0.38s ease-out; }
@keyframes ticketBump {
  0% { transform: scale(1); }
  40% { transform: scale(1.25); color: var(--yellow-deep); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .ticket-fly { display: none; }
  .ticket-bump { animation: none; }
}

.cookie-consent {
  position: fixed;
  inset: auto 12px 12px 12px;
  z-index: 90;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 18px;
  max-width: 720px;
  margin: 0 auto;
  padding: 14px 18px;
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 14px 40px -12px rgba(0, 0, 0, 0.55);
}
.cookie-consent-text {
  flex: 1 1 320px;
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.45;
}
.cookie-consent-text a { color: var(--purple); text-decoration: underline; }
.cookie-consent-actions {
  display: flex;
  gap: 10px;
  margin-left: auto;
}
/* The pulse is a CTA affordance; on a consent prompt it reads as nagging. */
.cookie-consent .btn-primary { animation: none; }
