/* Fonts and icons are loaded via <link> tags in index.html — more reliable than @import. */

/* ===== Design tokens ===== */
:root {
  /* FIFA-shirt palette (2026-08-10) */
  --linen: #AADD00;        /* Lime Chartreuse — accent / energy + CORRECT answers (as a fill) */
  --linen-deep: #4F6B00;   /* Dark lime — border/edge companion to --linen, used to outline lime
                              fills so they separate from the resting button state. Not used for
                              text: correct-answer text is cobalt. */
  --linen-soft: #F4F6EE;   /* near-white resting button bg (stays legible on white cards) */
  --shutters: #3333AA;     /* Cobalt Purple-Blue — headers & UI chrome */
  --shutters-soft: #5555CC; /* lighter cobalt */
  --terracotta: #EE3311;   /* Cherry Red — active states, logo, CTAs (correct answers are lime) */
  --terracotta-deep: #D42400; /* pressed/hover cherry */
  --lavender: #B3B9E8;     /* light cobalt (replaced lavender) — muted labels/placeholders + Pop Culture card */
  --brick: #CC2200;        /* Dark Cherry — Current Events card, timer-danger, form errors, share CTA */
  --clay: #44CCBB;         /* Teal Aqua — secondary accents */
  --wrong: #44CCBB;        /* Teal — WRONG answers (clearly distinct from cherry-red correct) */
  --wrong-deep: #0F8A7B;   /* darker teal — wrong feedback text, readable on white */
  --cream: #FFFFFF;        /* white background */
  --card: #FFFFFF;         /* white card background */
  --gold: #E6B33C;         /* tribe-lead ring (unchanged) */
  --ink: #2E3842;          /* primary text (unchanged) */
  --ink-soft: #6B7785;     /* secondary text (unchanged) */
  --border: #E3E4EE;       /* light cool border on white */

  --radius-sm: 10px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --radius-xl: 28px;

  --shadow-sm: 0 1px 2px rgba(51, 51, 170, 0.06);
  --shadow-md: 0 4px 16px rgba(51, 51, 170, 0.08);

  --font-display: 'Happy Monkey', 'Comic Sans MS', cursive;
  --font-ui: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

  /* ===== Safe-area insets =====
     One set of tokens for both targets, because the two platforms deliver the
     same numbers by different routes:

       Web / iOS PWA — env() resolves natively (0px everywhere on desktop, and
       the notch/home-indicator values on a phone).

       Android native — Capacitor 8's built-in SystemBars plugin measures the
       window insets and sets --safe-area-inset-* as INLINE styles on <html>.
       Inline styles beat this rule, so on a device these definitions are the
       fallback and Capacitor's live values win, updating as the bars change.

     Everything below should reference var(--safe-area-inset-*), never env()
     directly — env() alone is not reliable in the Android WebView, which is
     the whole reason the plugin injects the variables. */
  --safe-area-inset-top: env(safe-area-inset-top, 0px);
  --safe-area-inset-right: env(safe-area-inset-right, 0px);
  --safe-area-inset-bottom: env(safe-area-inset-bottom, 0px);
  --safe-area-inset-left: env(safe-area-inset-left, 0px);
}

/* ===== Reset ===== */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
button {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
  -webkit-appearance: none;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
i.ti {
  font-style: normal;
}

/* ===== Page ===== */
html, body {
  height: 100%;
}
body {
  font-family: var(--font-ui);
  color: var(--ink);
  background: #E5E6EC;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  min-height: 100dvh;   /* dynamic viewport: matches the visible area on mobile */
  padding: 0;
  font-size: 16px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
}

/* Phone frame so it previews at phone size on desktop, but fills screen on mobile */
.phone-frame {
  width: 100%;
  max-width: 420px;
  height: 100vh;
  height: 100dvh;   /* shrinks with the mobile toolbar so nothing is stranded off-screen */
  max-height: 900px;
  background: var(--cream);
  position: relative;
  overflow: hidden;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
  border-radius: 0;
  /* Horizontal insets only — a cutout in landscape, or a curved edge. The
     vertical ones are handled by the header and the bottom nav, which need the
     background to run UNDER the bars rather than stop short of them. */
  padding-left: var(--safe-area-inset-left);
  padding-right: var(--safe-area-inset-right);
}
@media (min-width: 480px) {
  .phone-frame {
    border-radius: 28px;
    margin: 20px;
    height: calc(100vh - 40px);
    height: calc(100dvh - 40px);
  }
}

#app {
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--cream);
}

/* ===== Header ===== */
.app-header {
  /* The cream fill deliberately extends up behind the status bar — targetSdk 36
     means Android 15+ draws this app edge-to-edge and there is no opaque bar to
     sit below. Only the padding grows, so the logo clears the clock and the
     colour still reaches the top of the screen. */
  padding: calc(18px + var(--safe-area-inset-top)) 20px 12px;
  background: var(--cream);
  flex-shrink: 0;
}
.logo {
  display: flex;
  align-items: center;
  gap: 12px;
}
.logo-square {
  width: 36px;
  height: 36px;
  background: var(--terracotta);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.logo-dot {
  width: 9px;
  height: 9px;
  background: var(--linen);
  border-radius: 50%;
}
.logo-text h1 {
  font-size: 22px;
  font-weight: 700;
  color: var(--shutters);
  letter-spacing: -0.3px;
  line-height: 1;
}
.tagline {
  font-family: var(--font-display);
  color: var(--terracotta);
  font-size: 14px;
  margin-top: 2px;
  font-weight: 400;
  letter-spacing: 0.3px;
}

/* ===== Screens ===== */
#screens {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 8px;
}
.screen {
  display: none;
  padding: 4px 20px 20px;
  animation: fadeIn 0.18s ease-out;
}
.screen.active {
  display: block;
}
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

.section-heading {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--ink-soft);
  font-weight: 600;
  margin: 18px 2px 10px;
}

/* ===== Home: streak banner ===== */
.streak-banner {
  background: var(--shutters);
  color: var(--cream);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 6px;
  box-shadow: var(--shadow-md);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.streak-banner:active {
  transform: scale(0.985);
}
.streak-banner:focus-visible {
  outline: 2px solid var(--terracotta);
  outline-offset: 2px;
}
.streak-banner-cta {
  margin-top: 6px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.4px;
  color: var(--terracotta);
  display: flex;
  align-items: center;
  gap: 4px;
}
.streak-banner-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  color: var(--lavender);
  font-weight: 600;
}
.streak-banner-theme {
  font-size: 22px;
  font-weight: 700;
  margin-top: 2px;
  color: var(--linen);
}
.streak-banner-right {
  text-align: right;
}
.streak-count {
  font-size: 28px;
  font-weight: 700;
  color: var(--terracotta);
  display: flex;
  align-items: center;
  gap: 6px;
  justify-content: flex-end;
  line-height: 1;
}
.streak-count i {
  font-size: 24px;
}

/* ===== Home: theme cards ===== */
.theme-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.theme-card {
  border-radius: var(--radius-lg);
  padding: 18px 16px;
  min-height: 124px;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
  box-shadow: var(--shadow-sm);
}
.theme-card:active {
  transform: scale(0.97);
}
.theme-card-day {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  opacity: 0.85;
}
.theme-card-name {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.15;
  margin-top: 4px;
}
.theme-card-badge {
  position: absolute;
  top: 12px;
  right: 12px;
  background: rgba(255, 255, 255, 0.9);
  color: var(--shutters);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  padding: 4px 8px;
  border-radius: 12px;
}
.theme-card-icon {
  font-size: 26px;
  align-self: flex-end;
  opacity: 0.85;
}
/* Card color variants — text color chosen for contrast */
.theme-card.linen { background: var(--linen); color: var(--shutters); }
.theme-card.shutters { background: var(--shutters); color: var(--linen); }
.theme-card.terracotta { background: var(--terracotta); color: #FFFFFF; }
.theme-card.brick { background: var(--brick); color: #FFFFFF; }
.theme-card.clay { background: var(--clay); color: #FFFFFF; }
.theme-card.lavender { background: var(--lavender); color: var(--shutters); }
/* Locked = not today's puzzle (only today's is playable) */
.theme-card.locked { opacity: 0.5; cursor: default; }
.theme-card.locked:active { transform: none; }
.theme-card-lock {
  position: absolute; top: 12px; right: 12px;
  background: rgba(255, 255, 255, 0.9); color: var(--shutters);
  width: 24px; height: 24px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center; font-size: 13px;
}
.theme-card.wildcard {
  background: linear-gradient(135deg, #3333AA 0%, #EE3311 55%, #CC2200 100%);
  color: #FFFFFF;
}
/* Wildcard spans the full row as a special end-of-week card */
.theme-card--wide {
  grid-column: 1 / -1;
  min-height: 96px;
  flex-direction: row;
  align-items: center;
}
.theme-card--wide .theme-card-icon {
  align-self: center;
}

/* ===== Game: theme strip ===== */
.theme-strip {
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin-bottom: 14px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.theme-strip-day {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  opacity: 0.85;
}
.theme-strip-name {
  font-size: 16px;
  font-weight: 700;
}

/* ===== Game: timer ===== */
.timer-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.timer-bar {
  flex: 1;
  height: 8px;
  background: var(--linen-soft);
  border-radius: 8px;
  overflow: hidden;
}
.timer-fill {
  height: 100%;
  width: 100%;
  background: var(--shutters);
  border-radius: 8px;
  transition: width 0.95s linear, background 0.3s ease;
}
.timer-fill.warn { background: var(--terracotta); }
.timer-fill.danger { background: var(--brick); }
.timer-seconds {
  font-size: 14px;
  font-weight: 700;
  color: var(--ink-soft);
  min-width: 22px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.question-progress {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 1.4px;
  color: var(--ink-soft);
  font-weight: 600;
  margin-bottom: 10px;
}

/* ===== Question card ===== */
.question-card {
  background: var(--card);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  padding: 20px 18px;
  box-shadow: var(--shadow-sm);
}
.question-text {
  font-size: 18px;
  font-weight: 600;
  color: var(--shutters);
  margin-bottom: 16px;
  line-height: 1.35;
}

/* ===== Answer buttons ===== */
.answer-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.answer-btn {
  background: var(--linen-soft);
  border: 1.5px solid var(--linen);
  color: var(--shutters);
  padding: 14px 10px;
  border-radius: var(--radius-md);
  font-size: 15px;
  font-weight: 600;
  text-align: center;
  min-height: 58px;
  transition: transform 0.08s ease, background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1.2;
}
.answer-btn:active:not(:disabled) {
  transform: scale(0.97);
}
.answer-btn.correct {
  /* Lime fill with cobalt text — white on lime is 1.61:1 and unreadable.
     The darker lime border separates it from the resting state, which already
     carries a plain --linen border. */
  background: var(--linen);
  border-color: var(--linen-deep);
  color: var(--shutters);
}
.answer-btn.wrong {
  background: var(--wrong);
  border-color: var(--wrong);
  color: #FFFFFF;
}
.answer-btn.dimmed {
  opacity: 0.5;
}
.answer-btn:disabled {
  cursor: default;
}

.feedback-text {
  margin-top: 14px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  min-height: 20px;
  text-align: center;
}
.feedback-text.correct { color: var(--shutters); }   /* cobalt — 9.57:1 on white */
.feedback-text.wrong { color: var(--wrong-deep); }

/* ===== Primary buttons ===== */
.primary-btn,
.secondary-btn,
.ghost-btn {
  width: 100%;
  padding: 14px 16px;
  border-radius: var(--radius-md);
  font-size: 15px;
  font-weight: 700;
  margin-top: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: transform 0.08s ease, opacity 0.15s ease;
}
.primary-btn {
  background: var(--shutters);
  color: var(--linen);
}
.secondary-btn {
  background: var(--linen-soft);
  color: var(--shutters);
  border: 1.5px solid var(--linen);
}
.ghost-btn {
  background: transparent;
  color: var(--shutters);
  font-weight: 600;
  margin-top: 6px;
}
.primary-btn:active,
.secondary-btn:active,
.ghost-btn:active {
  transform: scale(0.98);
}
/* Result-screen charity CTA — cherry so it stands apart from the cobalt Copy result */
.result-vote-btn {
  background: var(--terracotta);
  color: #FFFFFF;
  box-shadow: 0 8px 22px rgba(238, 51, 17, 0.35);
}
.full-width {
  width: 100%;
}
.hidden {
  display: none !important;
}

/* ===== Connection screen ===== */
.answer-pills-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  color: var(--ink-soft);
  font-weight: 600;
  margin: 4px 2px 8px;
}
.answer-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 16px;
}
.pill {
  background: var(--linen-soft);
  border: 1px solid var(--linen);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--shutters);
}
.pill.correct {
  background: var(--linen);
  border-color: var(--linen-deep);
  color: var(--shutters);
}
.pill.missed {
  background: var(--lavender);
  border-color: var(--lavender);
  color: var(--shutters);
  opacity: 0.85;
}

/* Connection text-input round */
.connection-hint {
  font-size: 13px;
  color: var(--ink-soft);
  line-height: 1.5;
  margin: -8px 0 14px;
}
.connection-bonus {
  color: var(--terracotta-deep);
  font-weight: 700;
}
.connection-input-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.connection-input-row input {
  flex: 1;
  min-width: 0;
  padding: 13px 14px;
  font-size: 15px;
  font-family: inherit;
  color: var(--shutters);
  background: var(--linen-soft);
  border: 1.5px solid var(--linen);
  border-radius: var(--radius-md);
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.connection-input-row input::placeholder {
  color: var(--ink-soft);
  opacity: 0.7;
}
.connection-input-row input:focus {
  border-color: var(--terracotta);
  background: var(--card);
}
.connection-input-row input:disabled {
  opacity: 0.7;
}
.connection-input-row .primary-btn {
  width: auto;
  flex: 0 0 auto;
  margin-top: 0;
  padding: 0 18px;
}
.connection-reveal-inline {
  margin-top: 14px;
  background: var(--linen-soft);
  border: 1.5px solid var(--linen);
  border-radius: var(--radius-md);
  padding: 12px 14px;
}
.connection-reveal-inline .connection-reveal-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  font-weight: 600;
  color: var(--ink-soft);
}
.connection-reveal-inline .connection-reveal-text {
  font-size: 15px;
  font-weight: 700;
  color: var(--shutters);
  margin-top: 4px;
  line-height: 1.3;
}
.connection-reveal-inline .connection-reveal-explanation {
  font-size: 13px;
  color: var(--ink);
  margin-top: 6px;
  line-height: 1.45;
}

/* Bonus banner on vote tab */
.bonus-banner {
  background: var(--terracotta);
  color: #FFFFFF;
  border-radius: var(--radius-md);
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  margin-bottom: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  box-shadow: var(--shadow-sm);
}
.bonus-banner i {
  font-size: 16px;
}

/* ===== Result screen ===== */
.result-hero {
  text-align: center;
  padding: 24px 0 18px;
}
.result-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 1.4px;
  color: var(--ink-soft);
  font-weight: 600;
}
.result-score {
  font-size: 64px;
  font-weight: 800;
  color: var(--shutters);
  line-height: 1;
  margin-top: 4px;
  letter-spacing: -2px;
}
.result-score-total {
  font-size: 32px;
  color: var(--lavender);
  font-weight: 700;
}
.result-tagline {
  font-family: var(--font-display);
  color: var(--terracotta);
  font-size: 20px;
  margin-top: 12px;
}

.stats-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
  margin-bottom: 18px;
}
.stat {
  background: var(--linen);          /* Lime green */
  border: 1px solid var(--linen);
  border-radius: var(--radius-md);
  padding: 12px 8px;
  text-align: center;
}
.stat i {
  font-size: 20px;
  color: var(--shutters);            /* cobalt icon reads calmly on lime (cherry would be garish) */
}
.stat-value {
  font-size: 18px;
  font-weight: 700;
  color: var(--shutters);
  margin-top: 4px;
}
.stat-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--ink);                 /* dark — readable on lime (grey was too low-contrast) */
  font-weight: 600;
  margin-top: 2px;
}

.connection-reveal {
  border-radius: var(--radius-lg);
  padding: 16px 18px;
  margin-bottom: 16px;
  background: var(--clay);            /* Teal Aqua */
  border: 1.5px solid var(--clay);
  color: var(--shutters);             /* Cobalt Blue text on teal */
}
/* Was --clay, which is the SAME hex as --wrong (#44CCBB) — so solving the
   commonality was being celebrated in the wrong-answer colour. Now lime, like
   every other correct state. */
.connection-reveal.correct {
  background: var(--linen);
  border-color: var(--linen-deep);
  color: var(--shutters);
}
.connection-reveal-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  font-weight: 600;
  opacity: 0.85;
}
.connection-reveal-text {
  font-size: 17px;
  font-weight: 700;
  margin-top: 4px;
  line-height: 1.3;
}
.connection-reveal-explanation {
  font-size: 13px;
  margin-top: 6px;
  opacity: 0.9;
  line-height: 1.4;
}

.share-card {
  background: var(--shutters);
  color: var(--linen);
  border-radius: var(--radius-lg);
  padding: 18px;
  text-align: center;
  margin-bottom: 4px;
}
.share-card-title {
  font-size: 13px;
  font-weight: 600;
  opacity: 0.85;
  margin-bottom: 10px;
  letter-spacing: 0.5px;
}
.share-card-emoji {
  font-size: 28px;
  letter-spacing: 4px;
  line-height: 1;
}
.share-card-score {
  font-size: 18px;
  font-weight: 700;
  margin-top: 10px;
  color: var(--cream);
}

.result-actions {
  margin-top: 18px;
}

/* ===== Charity screen ===== */
.charity-tabs {
  display: flex;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px;
  margin: 8px 0 18px;
}
.tab-btn {
  flex: 1;
  padding: 10px 8px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  transition: background 0.15s ease, color 0.15s ease;
}
.tab-btn.active {
  background: var(--shutters);
  color: var(--linen);
}

.charity-tab-content {
  display: none;
}
.charity-tab-content.active {
  display: block;
}

.month-banner {
  background: var(--shutters);
  color: var(--cream);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 14px;
  box-shadow: var(--shadow-md);
}
.month-banner-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  color: var(--lavender);
  font-weight: 600;
}
.month-banner-amount {
  font-size: 26px;
  font-weight: 800;
  color: var(--linen);
  margin-top: 2px;
}
.month-banner-amount span {
  font-size: 14px;
  font-weight: 600;
  margin-left: 4px;
  opacity: 0.7;
}
.month-banner-month {
  text-align: right;
  font-size: 18px;
  font-weight: 700;
  color: var(--linen);
}
.month-banner-year {
  font-size: 12px;
  color: var(--lavender);
  font-weight: 600;
  margin-top: 2px;
}
.vote-intro {
  font-size: 13px;
  color: var(--ink-soft);
  line-height: 1.5;
  margin-bottom: 14px;
  padding: 0 2px;
}
.vote-intro strong {
  color: var(--shutters);
  font-weight: 700;
}
.vote-intro em {
  font-family: var(--font-display);
  font-style: normal;
  color: var(--terracotta);
  font-size: 14px;
}

/* Milestone + play stats card */
.milestone-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
  margin-bottom: 14px;
  box-shadow: var(--shadow-sm);
}
.milestone-progress {
  margin-bottom: 12px;
}
.milestone-bar {
  height: 10px;
  background: var(--linen-soft);
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 6px;
}
.milestone-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--terracotta), var(--terracotta-deep));
  border-radius: 8px;
  transition: width 0.5s ease;
}
.milestone-text {
  font-size: 12px;
  color: var(--ink-soft);
  font-weight: 600;
}
.milestone-text strong {
  color: var(--shutters);
  font-weight: 700;
}
.play-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.play-stat {
  text-align: center;
}
.play-stat-num {
  font-size: 22px;
  font-weight: 800;
  color: var(--terracotta-deep);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.play-stat-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--ink-soft);
  font-weight: 600;
  margin-top: 4px;
}

.charity-cards {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.charity-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.charity-photo {
  width: 100%;
  height: 140px;
  overflow: hidden;
  background: var(--linen-soft);
}
.charity-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.charity-card-body {
  padding: 16px;
}
.charity-card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
.charity-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: #FFFFFF;
  flex-shrink: 0;
}
.charity-icon.smith { background: var(--terracotta); }
.charity-icon.redcross { background: var(--brick); }
.charity-icon.wwf { background: var(--shutters); }
.charity-name {
  font-size: 16px;
  font-weight: 700;
  color: var(--shutters);
  line-height: 1.2;
}
.charity-location {
  font-size: 12px;
  color: var(--ink-soft);
  margin-top: 2px;
}
.charity-desc {
  font-size: 13px;
  color: var(--ink);
  line-height: 1.5;
  margin-bottom: 10px;
}
.video-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  color: var(--terracotta-deep);
  font-size: 13px;
  font-weight: 600;
  padding: 0;
  margin-bottom: 12px;
  text-decoration: none;
}
.video-btn i {
  font-size: 16px;
}
.vote-bar-wrap {
  margin-bottom: 12px;
}
.vote-bar-info {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--ink-soft);
  font-weight: 600;
  margin-bottom: 4px;
}
.vote-bar {
  height: 6px;
  background: var(--linen-soft);
  border-radius: 8px;
  overflow: hidden;
}
.vote-bar-fill {
  height: 100%;
  background: var(--terracotta);
  border-radius: 8px;
  transition: width 0.4s ease;
}
.vote-btn {
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: var(--linen-soft);
  border: 1.5px solid var(--linen);
  color: var(--shutters);
  font-size: 14px;
  font-weight: 700;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.vote-btn.voted {
  background: var(--terracotta);
  border-color: var(--terracotta-deep);
  color: #FFFFFF;
}
/* Locked = already voted today. Uses a class rather than the `disabled`
   attribute so the tap still fires and can explain the rule ("already voted
   today") instead of silently doing nothing. */
.vote-btn.locked,
.vote-btn:disabled {
  opacity: 0.5;
  cursor: default;
}
.vote-btn.voted.locked,
.vote-btn.voted:disabled {
  opacity: 1;
}

/* ===== Streak tab ===== */
.streak-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin: 8px 0 4px;
}
.streak-stat {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 16px 14px;
  text-align: center;
}
.streak-stat-num {
  font-size: 28px;
  font-weight: 800;
  color: var(--terracotta-deep);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.streak-stat-num i {
  color: var(--terracotta);
  font-size: 22px;
}
.streak-stat-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--ink-soft);
  font-weight: 600;
  margin-top: 6px;
  line-height: 1.3;
}

.day-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
  margin-bottom: 18px;
}
.day-cell {
  aspect-ratio: 1;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  position: relative;
}
.day-cell.played {
  background: var(--terracotta);
  border-color: var(--terracotta-deep);
  color: #FFFFFF;
}
.day-cell.today {
  outline: 2px solid var(--shutters);
  outline-offset: 1px;
}
.day-cell.future {
  background: transparent;
  border-style: dashed;
  color: var(--lavender);
}

/* ===== Impact tab ===== */
.impact-hero {
  background: var(--terracotta);
  color: #FFFFFF;
  border-radius: var(--radius-lg);
  padding: 22px 18px;
  text-align: center;
  margin: 8px 0 6px;
  box-shadow: var(--shadow-md);
}
.impact-hero-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  font-weight: 600;
  opacity: 0.9;
}
.impact-hero-amount {
  font-size: 42px;
  font-weight: 800;
  line-height: 1;
  margin-top: 6px;
  letter-spacing: -1px;
}
.impact-hero-amount span {
  font-size: 18px;
  font-weight: 600;
  opacity: 0.85;
  margin-left: 4px;
}
.impact-hero-sub {
  font-size: 12px;
  margin-top: 8px;
  opacity: 0.9;
  font-weight: 500;
}
.impact-history {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-bottom: 8px;
}
.impact-row {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.impact-row-month {
  font-size: 14px;
  font-weight: 700;
  color: var(--shutters);
}
.impact-row-charity {
  font-size: 12px;
  color: var(--ink-soft);
  margin-top: 2px;
}
.impact-row-right {
  text-align: right;
}
.impact-row-amount {
  font-size: 16px;
  font-weight: 800;
  color: var(--terracotta-deep);
}
.impact-row-receipt {
  font-size: 11px;
  color: var(--ink-soft);
  text-decoration: none;
  font-weight: 600;
  margin-top: 2px;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}

/* ===== Bottom nav ===== */
.bottom-nav {
  display: flex;
  background: var(--card);
  border-top: 1px solid var(--border);
  /* Was env() directly, which the Android WebView does not reliably resolve —
     the token falls back to env() on the web and is overwritten by Capacitor's
     measured value on a device. Keeps the tab labels clear of the gesture bar. */
  padding: 8px 12px calc(8px + var(--safe-area-inset-bottom));
  flex-shrink: 0;
}
.nav-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px;
  color: var(--ink-soft);
  font-size: 11px;
  font-weight: 600;
  border-radius: 10px;
  transition: color 0.15s ease, background 0.15s ease;
}
.nav-btn i {
  font-size: 22px;
}
.nav-btn.active {
  color: var(--terracotta-deep);
}

/* ===== Splash screen ===== */
/* ===== Credits screen ===== */
.home-share-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  margin-top: 18px;
  padding: 14px 16px;
  background: var(--terracotta);
  color: #FFFFFF;
  border: none;
  border-radius: var(--radius-md);
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(238, 51, 17, 0.38);
  transition: transform 0.08s ease;
}
.home-share-btn:active { transform: scale(0.98); }

.home-credits-link {
  display: block;
  width: 100%;
  background: transparent;
  border: none;
  color: var(--ink-soft);
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  padding: 18px 0 6px;
  cursor: pointer;
  opacity: 0.8;
  transition: opacity 0.15s ease;
}
.home-credits-link:hover { opacity: 1; }
.credits-back {
  background: transparent;
  border: none;
  color: var(--shutters);
  font-size: 14px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  padding: 4px 0;
  margin-bottom: 6px;
}
.credits-title {
  font-size: 26px;
  font-weight: 700;
  color: var(--shutters);
  margin-bottom: 8px;
}
.credits-intro {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink);
  margin-bottom: 22px;
}
.credits-section {
  background: var(--card);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
  margin-bottom: 12px;
  box-shadow: var(--shadow-sm);
}
.credits-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.3px;
  color: var(--ink-soft);
  font-weight: 700;
  margin-bottom: 8px;
}
.credits-value {
  font-size: 17px;
  font-weight: 700;
  color: var(--shutters);
}
.credits-note {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink-soft);
}
.credits-howit {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink);
}
.credits-tiers { list-style: none; margin: 12px 0 0; padding: 0; }
.credits-tiers li {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 14px; color: var(--ink); padding: 7px 0;
  border-top: 1px solid var(--border);
}
.credits-tiers li span:last-child { font-weight: 700; color: var(--shutters); }
.credits-tiers-note { font-size: 12px; color: var(--ink-soft); margin-top: 8px; }
.credits-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.credits-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 15px;
  color: var(--ink);
  padding: 6px 0;
}
.credits-list li i {
  color: var(--terracotta);
  font-size: 18px;
}
/* The Legal card's link. Matches .credits-list li so the card reads as one of
   the same set, but with a 44px minimum height — it is a tap target, not a list
   row — and a trailing external-link glyph, because this is the only tap in the
   app that hands the player to their browser. */
.credits-link {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  font-size: 15px;
  font-weight: 600;
  color: var(--shutters);
  text-decoration: none;
}
.credits-link i:first-child {
  color: var(--terracotta);
  font-size: 18px;
}
.credits-link-ext {
  margin-left: auto;
  color: var(--ink-soft);
  font-size: 15px;
}
.credits-link:active { opacity: 0.7; }

.credits-tagline {
  font-family: var(--font-display);
  font-size: 22px;
  color: var(--terracotta);
  text-align: center;
  margin: 24px 0 4px;
}
.credits-version {
  text-align: center;
  font-size: 12px;
  color: var(--ink-soft);
  margin-bottom: 8px;
}

/* ===== Avatars ===== */
.avatar {
  border-radius: 50%;
  object-fit: cover;
  background: var(--cream);
  flex-shrink: 0;
  display: block;
}
.avatar--xl { width: 104px; height: 104px; }
.avatar--lg { width: 72px; height: 72px; }
.avatar--md { width: 48px; height: 48px; }
.avatar--sm { width: 40px; height: 40px; }
/* Tribe lead gets a gold ring with a small gap */
.avatar--lead { box-shadow: 0 0 0 2px var(--card), 0 0 0 5px var(--gold); }

/* ===== Account: header avatar, sign-up, profile ===== */
.app-header { display: flex; align-items: center; justify-content: space-between; }
.header-avatar {
  background: transparent; border: none; padding: 0; cursor: pointer;
  border-radius: 50%; line-height: 0; box-shadow: 0 0 0 2px var(--border);
}
.header-avatar:active { transform: scale(0.95); }
.header-avatar .avatar { background: var(--linen-soft); }

.signup-overlay {
  position: absolute; inset: 0; z-index: 180;
  background: var(--cream);
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  /* Scrolls, and ends in the "Create account & play" button — the bottom inset
     is what keeps that button clear of the gesture bar rather than tucked
     behind it. */
  padding: calc(28px + var(--safe-area-inset-top)) 22px calc(32px + var(--safe-area-inset-bottom));
  opacity: 0; visibility: hidden;
  transition: opacity 0.4s ease, visibility 0.4s ease;
}
.signup-overlay.show { opacity: 1; visibility: visible; }
.signup-inner { max-width: 360px; margin: 0 auto; }
.signup-head { text-align: center; margin-bottom: 18px; }
.logo-square--lg { width: 56px; height: 56px; margin: 0 auto 10px; }
.signup-brand { font-size: 26px; font-weight: 700; color: var(--shutters); }
.signup-tagline { font-family: var(--font-display); font-size: 16px; color: var(--terracotta); }
.signup-title { font-size: 22px; font-weight: 700; color: var(--shutters); margin-top: 6px; }
.signup-sub { font-size: 14px; color: var(--ink-soft); margin: 4px 0 18px; }
.signup-fine { font-size: 12px; color: var(--ink-soft); text-align: center; margin-top: 12px; }
.signup-toggle { font-size: 13px; color: var(--ink-soft); text-align: center; margin-top: 14px; }
.signup-toggle button {
  background: none; border: none; color: var(--terracotta);
  font-weight: 700; font-size: 13px; cursor: pointer; padding: 0;
}

.field { display: block; margin-bottom: 14px; }
.field-label {
  display: block; font-size: 12px; font-weight: 700; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 6px;
}
/* type=password was missing from this list, so every password field in the app
   (the log-in screen's, and now the reset screen's two) rendered as an unstyled
   browser default — narrow, wrong border, wrong font — next to correctly styled
   email and date inputs. Pre-existing; the reset screen just made it obvious. */
.field input[type=text], .field input[type=email], .field input[type=date],
.field input[type=password],
#profile-name-input {
  width: 100%; padding: 12px 14px; font-size: 15px;
  border: 1.5px solid var(--border); border-radius: var(--radius-md);
  background: var(--card); color: var(--ink); font-family: var(--font-ui);
  -webkit-appearance: none;
}
.field input:focus, #profile-name-input:focus { outline: none; border-color: var(--terracotta); }
.form-error { font-size: 13px; color: var(--brick); font-weight: 600; margin: 2px 0 8px; }

.avatar-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; }
.avatar-pick {
  background: transparent; border: none; padding: 2px; cursor: pointer;
  border-radius: 50%; line-height: 0; aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
}
.avatar-pick .avatar { width: 100%; height: auto; aspect-ratio: 1; }
.avatar-pick.selected { box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--terracotta); }
.avatar-pick:active { transform: scale(0.94); }

.profile-head { text-align: center; margin: 6px 0 18px; }
.profile-head .avatar { margin: 0 auto 12px; }
.profile-name { font-size: 22px; font-weight: 700; color: var(--shutters); }
.profile-meta { font-size: 13px; color: var(--ink-soft); margin-top: 2px; }

.nav-btn { position: relative; }
.nav-badge {
  position: absolute; top: 3px; left: calc(50% + 7px);
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--brick); box-shadow: 0 0 0 2px var(--card);
}

/* ===== Charity: tiers + How Our Giving Grows ===== */
.month-banner-icon { font-size: 30px; color: var(--linen); }
.tier-card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 16px; margin-bottom: 14px;
  box-shadow: var(--shadow-sm);
}
.tier-line { font-size: 16px; color: var(--ink); }
.tier-line strong { color: var(--terracotta); }
.pledge-statement { font-size: 13.5px; color: var(--ink-soft); line-height: 1.5; margin-top: 8px; }
.giving-tile {
  width: 100%; margin-top: 12px; padding: 12px 14px;
  background: var(--linen-soft); border: 1.5px solid var(--linen);
  border-radius: var(--radius-md); color: var(--shutters);
  font-size: 14px; font-weight: 700; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.giving-tile:active { transform: scale(0.99); }

.giving-title { font-size: 24px; font-weight: 700; color: var(--shutters); margin-bottom: 6px; }
.giving-intro { font-size: 14px; color: var(--ink-soft); line-height: 1.55; margin-bottom: 18px; }
.tier-table {
  width: 100%; border-collapse: collapse; background: var(--card);
  border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden;
  font-size: 14px;
}
.tier-table th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.8px;
  color: var(--ink-soft); padding: 10px 14px; background: var(--linen-soft);
}
.tier-table td { padding: 11px 14px; color: var(--ink); border-top: 1px solid var(--border); }
.tier-table td:last-child { font-weight: 700; text-align: right; color: var(--shutters); }
.tier-current { background: rgba(238, 51, 17, 0.12); }
.tier-current td { color: var(--terracotta-deep); }
.tier-you {
  display: inline-block; margin-left: 6px; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.5px; color: #FFFFFF;
  background: var(--terracotta); padding: 2px 6px; border-radius: 8px; vertical-align: middle;
}
.giving-fine { font-size: 12px; color: var(--ink-soft); line-height: 1.5; margin: 16px 0; }

/* ===== Tribes ===== */
.tribes-head { display: flex; align-items: center; justify-content: space-between; }
.tribes-title { font-size: 24px; font-weight: 700; color: var(--shutters); }
.tribes-create-btn {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--shutters); color: var(--linen); border: none;
  border-radius: var(--radius-md); padding: 8px 12px;
  font-size: 13px; font-weight: 700; cursor: pointer;
}
.tribes-create-btn:active { transform: scale(0.97); }
.tribes-sub { font-size: 13px; color: var(--ink-soft); margin: 4px 0 16px; }
.tribes-empty { font-size: 14px; color: var(--ink-soft); text-align: center; padding: 30px 10px; line-height: 1.5; }

.tribe-card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 14px; margin-bottom: 14px;
  box-shadow: var(--shadow-sm);
}
.tribe-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.tribe-name { font-size: 17px; font-weight: 700; color: var(--shutters); }
.tribe-meta { font-size: 11px; color: var(--ink-soft); margin-top: 2px; }
.tribe-name-btn {
  background: var(--linen-soft); border: 1.5px solid var(--linen); color: var(--shutters);
  border-radius: 10px; padding: 6px 10px; font-size: 12px; font-weight: 700; cursor: pointer; flex-shrink: 0;
}

.scope-toggle { display: flex; gap: 4px; background: var(--linen-soft); border-radius: 12px; padding: 3px; margin: 12px 0; }
.scope-toggle button {
  flex: 1; background: transparent; border: none; border-radius: 9px;
  padding: 7px 4px; font-size: 12px; font-weight: 700; color: var(--ink-soft); cursor: pointer;
}
.scope-toggle button.active { background: var(--card); color: var(--shutters); box-shadow: var(--shadow-sm); }

.tribe-members { display: flex; flex-direction: column; }
.member-row { display: flex; align-items: center; gap: 10px; padding: 8px 2px; }
.member-row + .member-row { border-top: 1px solid var(--border); }
.member-info { flex: 1; min-width: 0; }
.member-name { font-size: 14px; font-weight: 700; color: var(--ink); display: flex; align-items: center; gap: 5px; }
.you-tag {
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;
  background: var(--shutters); color: var(--linen); padding: 1px 5px; border-radius: 6px;
}
.member-crown { color: var(--gold); font-size: 14px; }
.member-streak { font-size: 12px; color: var(--ink-soft); margin-top: 1px; }
.streak-badge { margin-left: 2px; }
.member-score { min-width: 46px; text-align: right; }
.member-pts { font-size: 15px; font-weight: 700; color: var(--shutters); }
.member-pts small { font-size: 10px; font-weight: 600; color: var(--ink-soft); margin-left: 1px; }
.member-dash { color: var(--lavender); font-weight: 700; }
.nudge-btn {
  flex-shrink: 0; background: var(--terracotta); color: #FFFFFF; border: none;
  border-radius: 10px; padding: 7px 11px; font-size: 12px; font-weight: 700; cursor: pointer;
}
.nudge-btn:active { transform: scale(0.95); }
.nudge-btn.nudged { background: var(--linen-soft); color: var(--ink-soft); }

.tribe-actions { display: flex; gap: 8px; margin-top: 12px; align-items: center; }
.tribe-invite-btn {
  flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  background: var(--linen-soft); border: 1.5px solid var(--linen); color: var(--shutters);
  border-radius: var(--radius-md); padding: 9px; font-size: 13px; font-weight: 700; cursor: pointer;
}
.tribe-leave-btn {
  background: transparent; border: none; color: var(--ink-soft);
  font-size: 13px; font-weight: 600; cursor: pointer; padding: 9px 12px;
}

/* ===== Vote celebration overlay ===== */
.celebration {
  position: absolute;
  inset: 0;
  z-index: 150;
  background: var(--cream);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Content is centred so it rarely reaches the bars, but the confetti canvas
     and the share CTA both run to the edges on a short screen. */
  padding: calc(32px + var(--safe-area-inset-top)) 28px calc(32px + var(--safe-area-inset-bottom));
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s ease, visibility 0.5s ease;
}
.celebration.show {
  opacity: 1;
  visibility: visible;
}

/* ===== Confirm dialog (hardware back button) =====
   Sits above the sign-up gate (180) and onboarding (185) but below the splash
   (200). In practice nothing else is open when it appears — it only shows
   mid-game — but the ordering is set so it can never end up behind something.

   Unlike the other overlays this one dims rather than replaces: the question
   stays visible behind it, which is what makes "keep playing" feel like
   returning rather than restarting. */
.confirm-overlay {
  position: absolute;
  inset: 0;
  z-index: 195;
  background: rgba(46, 56, 66, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(24px + var(--safe-area-inset-top)) 24px calc(24px + var(--safe-area-inset-bottom));
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s ease;
}
.confirm-overlay.show {
  opacity: 1;
  visibility: visible;
}
.confirm-card {
  background: var(--card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 24px 22px 18px;
  width: 100%;
  max-width: 320px;
  text-align: center;
}
.confirm-title {
  font-family: var(--font-display);
  font-size: 22px;
  color: var(--shutters);
  margin-bottom: 8px;
}
.confirm-body {
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.45;
  margin-bottom: 18px;
}
.confirm-actions {
  display: flex;
  gap: 10px;
}
.confirm-btn {
  flex: 1;
  border-radius: var(--radius-md);
  padding: 12px 10px;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  border: 1.5px solid transparent;
}
.confirm-btn:active { transform: scale(0.97); }
/* Cobalt fill with lime text, matching the "Let's play" CTA — "keep playing"
   is the safe choice, so it gets the weight. */
.confirm-btn--primary {
  background: var(--shutters);
  color: var(--linen);
}
.confirm-btn--ghost {
  background: transparent;
  border-color: var(--border);
  color: var(--ink-soft);
}
.confetti-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.celebration-inner {
  position: relative;
  z-index: 1;
  max-width: 320px;
}
.celebration-badge {
  width: 88px;
  height: 88px;
  border-radius: 50%;
  background: var(--terracotta);
  color: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 22px;
  font-size: 42px;
  box-shadow: 0 10px 26px rgba(238, 51, 17, 0.45);
}
.celebration-heading {
  font-family: var(--font-display);
  font-size: 34px;
  color: var(--shutters);
  margin-bottom: 14px;
}
.celebration-body,
.celebration-streak {
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  margin-bottom: 10px;
}
.celebration-body strong,
.celebration-streak strong {
  color: var(--terracotta);
  font-weight: 700;
}
.celebration-champ {
  font-family: var(--font-display);
  font-size: 21px;
  color: var(--terracotta);
  margin: 16px 0 8px;
}
.celebration-seeya {
  font-family: var(--font-display);
  font-size: 17px;
  color: var(--shutters);
  margin: 0 0 24px;
}
/* Dominant action — red, prominent */
.celebration-share-btn {
  width: 100%;
  padding: 16px 18px;
  border-radius: var(--radius-md);
  font-size: 16px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--brick);
  color: #FFFFFF;
  box-shadow: 0 8px 22px rgba(204, 34, 0, 0.42);
  transition: transform 0.08s ease, opacity 0.15s ease;
}
.celebration-share-btn:active { transform: scale(0.98); }
/* Understated secondary escape hatch — deliberately not prominent */
.celebration-home-link {
  width: auto;
  margin: 16px auto 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: transparent;
  border: none;
  color: var(--ink-soft);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.15s ease;
}
.celebration-home-link:hover { opacity: 1; }

/* Entrance animations — play when .show is added */
.celebration.show .celebration-badge { animation: celebPop 0.6s cubic-bezier(0.18, 0.89, 0.32, 1.28) both; }
.celebration.show .celebration-heading { animation: celebRise 0.5s ease both 0.18s; }
.celebration.show .celebration-body { animation: celebRise 0.5s ease both 0.30s; }
.celebration.show .celebration-streak { animation: celebRise 0.5s ease both 0.42s; }
.celebration.show .celebration-champ { animation: celebRise 0.5s ease both 0.54s; }
.celebration.show .celebration-seeya { animation: celebRise 0.5s ease both 0.66s; }
.celebration.show .celebration-share-btn { animation: celebRise 0.5s ease both 0.78s; }
.celebration.show .celebration-home-link { animation: celebRise 0.5s ease both 0.90s; }

@keyframes celebPop {
  0%   { transform: scale(0) rotate(-25deg); opacity: 0; }
  60%  { transform: scale(1.12) rotate(6deg); opacity: 1; }
  100% { transform: scale(1) rotate(0); opacity: 1; }
}
@keyframes celebRise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .celebration.show .celebration-badge,
  .celebration.show .celebration-heading,
  .celebration.show .celebration-body,
  .celebration.show .celebration-streak,
  .celebration.show .celebration-champ,
  .celebration.show .celebration-seeya,
  .celebration.show .celebration-share-btn,
  .celebration.show .celebration-home-link { animation: none; }
}

.splash {
  position: absolute;
  inset: 0;
  background: var(--cream);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: opacity 0.45s ease;
}
.splash.hide {
  opacity: 0;
  pointer-events: none;
}
.splash-inner {
  text-align: center;
}
.splash-logo {
  display: flex;
  justify-content: center;
  margin-bottom: 18px;
}
.splash-logo-square {
  width: 96px;
  height: 96px;
  background: var(--terracotta);
  border-radius: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8px 24px rgba(238, 51, 17, 0.35);
  transform-origin: center bottom;
  /* Per-keyframe easing below (falls accelerate, rises decelerate) does the
     shaping, so the animation itself stays linear — a curve here would fight it. */
  animation: logoBounce 1.6s linear both;
}
.splash-logo-dot {
  width: 22px;
  height: 22px;
  background: var(--linen);
  border-radius: 50%;
}
/* One drop, then a settling bounce.

   The previous version sank to +90px on impact and then sprang all the way up to
   -110px before falling 159px again — and that second fall read as the logo being
   dropped in from the top a second time. It also never actually rested on a floor:
   the values oscillated either side of 0, so there was no impact to bounce off.

   Now translateY(0) IS the floor and the logo returns to it after every hop,
   never passing below it. Rebounds decay 78 → 30 → 10 → 0, so each one is
   plainly smaller than the last and only the opening descent reads as a drop.
   Squash lives in scaleY at each impact rather than in downward travel, which is
   what transform-origin: center bottom is there for.

   Falls accelerate and rises decelerate, set per keyframe — with one curve across
   the whole animation the hops drift out of step with gravity. */
@keyframes logoBounce {
  0%   { transform: translateY(-360px) scaleY(1.25) scaleX(0.85); animation-timing-function: cubic-bezier(0.45, 0, 0.75, 0.35); }
  30%  { transform: translateY(0)      scaleY(0.62) scaleX(1.34); animation-timing-function: cubic-bezier(0.25, 0.65, 0.5, 1); }
  46%  { transform: translateY(-78px)  scaleY(1.14) scaleX(0.92); animation-timing-function: cubic-bezier(0.45, 0, 0.75, 0.35); }
  60%  { transform: translateY(0)      scaleY(0.8)  scaleX(1.16); animation-timing-function: cubic-bezier(0.25, 0.65, 0.5, 1); }
  72%  { transform: translateY(-30px)  scaleY(1.07) scaleX(0.97); animation-timing-function: cubic-bezier(0.45, 0, 0.75, 0.35); }
  82%  { transform: translateY(0)      scaleY(0.91) scaleX(1.06); animation-timing-function: cubic-bezier(0.25, 0.65, 0.5, 1); }
  90%  { transform: translateY(-10px)  scaleY(1.03) scaleX(0.99); animation-timing-function: cubic-bezier(0.45, 0, 0.75, 0.35); }
  96%  { transform: translateY(0)      scaleY(0.97) scaleX(1.02); }
  100% { transform: translateY(0)      scale(1); }
}
.splash-title {
  font-size: 38px;
  font-weight: 700;
  color: var(--shutters);
  letter-spacing: -0.6px;
  line-height: 1;
  opacity: 0;
  animation: splashFadeIn 0.5s ease-out 1.7s forwards;
}
.splash-tagline {
  font-family: var(--font-display);
  color: var(--terracotta);
  font-size: 24px;
  margin-top: 12px;
  letter-spacing: 0.3px;
  opacity: 0;
  animation: splashFadeIn 0.5s ease-out 1.95s forwards;
}
@keyframes splashFadeIn {
  to { opacity: 1; }
}

/* A 360px drop followed by four bounces is exactly the motion this query exists
   to suppress. The logo fades in place instead — no travel, no squash.

   The title and tagline can NOT simply take `animation: none` the way the
   celebration block above does. Those elements have no resting opacity, so
   removing their animation is harmless; these two set `opacity: 0` in their base
   rule and the animation is the only thing that ever brings them back, so
   dropping it would leave the splash permanently blank. They keep the fade and
   lose the long delays instead — 1.7s and 1.95s were timed to the bounce that no
   longer plays, and on a splash that dismisses itself at 2.5s that would leave
   the wordmark on screen for barely half a second. */
@media (prefers-reduced-motion: reduce) {
  .splash-logo-square {
    opacity: 0;
    animation: splashFadeIn 0.3s ease-out forwards;
  }
  .splash-title   { animation-duration: 0.3s; animation-delay: 0.12s; }
  .splash-tagline { animation-duration: 0.3s; animation-delay: 0.24s; }
}

/* ===== Toast ===== */
.toast {
  position: fixed;
  bottom: 80px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--shutters);
  color: var(--linen);
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  box-shadow: var(--shadow-md);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 100;
}
.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(-4px);
}

/* ===== Onboarding (first run, before the sign-up gate) ===== */
/* z-index sits above the sign-up overlay (180) but below the splash (200),
   so the splash still covers it on open. */
.onboarding-overlay {
  position: absolute; inset: 0; z-index: 185;
  background: var(--cream);
  display: flex;
  flex-direction: column;
  /* The skip button is the first child, so without the top inset it renders
     underneath the status bar — which is exactly what it did. */
  padding: calc(22px + var(--safe-area-inset-top)) 0 calc(28px + var(--safe-area-inset-bottom));
  opacity: 0; visibility: hidden;
  transition: opacity 0.4s ease, visibility 0.4s ease;
}
.onboarding-overlay.show { opacity: 1; visibility: visible; }

/* Always reachable — never gated behind the last slide.
   Cherry Red (--terracotta, #EE3311) per the FIFA palette, chosen deliberately
   over the darker --brick. White on #EE3311 is 4.11:1, under the 4.5:1 WCAG AA
   floor for body text — the two-line layout affords a larger lead line, which
   is what keeps the label readable here.
   Happy Monkey ships at a single weight, so no bold — synthesised bold muddies
   the display face at these sizes. */
/* The skip button repaints to the current page's paired colour. It lives outside
   .onboarding-track — one button for all five slides — so it can't read the
   slide's nth-child tokens; renderOnboarding() stamps data-slide on the overlay
   and these rules key off that. Slide 5 repeats slide 1, matching the 4n cycle
   the slides themselves use.

   Label colour is the page's own background wherever that clears AA against the
   fill, which is the same inversion the design already used (lime fill / cobalt
   label). Slide 3 is the exception: turquoise on tomato is only 2.1:1, so the
   label goes white — 4.1:1, the same trade-off this button already carried, and
   the reason it stays at a single line with generous padding.
     lime fill + cobalt label 6.0:1 · cobalt fill + lime label 6.0:1
     tomato fill + white label 4.1:1 · turquoise fill + cobalt label 4.8:1 */
.onboarding-overlay { --ob-btn: var(--linen); --ob-btn-text: var(--shutters); }
.onboarding-overlay[data-slide="1"],
.onboarding-overlay[data-slide="5"] { --ob-btn: var(--linen);      --ob-btn-text: var(--shutters); }
.onboarding-overlay[data-slide="2"] { --ob-btn: var(--shutters);   --ob-btn-text: var(--linen); }
.onboarding-overlay[data-slide="3"] { --ob-btn: var(--terracotta); --ob-btn-text: #FFFFFF; }
.onboarding-overlay[data-slide="4"] { --ob-btn: var(--clay);       --ob-btn-text: var(--shutters); }

/* Mirrors the "Let's play" CTA — same width, padding and radius.
   nowrap keeps it to a single line; the font is a step down from the CTA's 15px
   because this label is far longer and has to fit the same 320px width.
   Happy Monkey ships at a single weight, so no bold — synthesised bold muddies
   the display face at these sizes. */
.onboarding-skip {
  align-self: center;
  width: 100%;
  max-width: 320px;
  margin: 0 auto 6px;
  padding: 14px 16px;
  background: var(--ob-btn);
  border: none;
  color: var(--ob-btn-text);
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.2;
  white-space: nowrap;
  text-align: center;
  cursor: pointer;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  /* Colour eases over roughly the slide's own 0.32s travel so the button changes
     with the page rather than snapping ahead of it. */
  transition: transform 0.08s ease, filter 0.15s ease,
              background-color 0.3s ease, color 0.3s ease;
}
.onboarding-skip:active {
  transform: scale(0.98);
  filter: brightness(0.94);
}

.onboarding-viewport { flex: 1; overflow: hidden; }
.onboarding-track {
  display: flex;
  height: 100%;
  transition: transform 0.32s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}
.onboarding-slide {
  flex: 0 0 100%;
  min-width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Narrower than it was (34px, then 20px) so the card can grow toward the edges
     while still leaving white down both sides. 15px is about the floor — below
     that the card stops reading as sitting on the page. */
  padding: 8px 15px;
}

/* ===== Per-page palette =====
   Each slide declares three tokens and every child reads from them, so a colour
   change is a one-line edit here rather than a hunt through the rules below.

     --ob-bg      the card fill (the "background" for that page)
     --ob-accent  the paired colour — skip button fill AND the page's symbol
     --ob-text    copy colour, chosen for contrast against --ob-bg

   The cycle is four long (cobalt → lime → turquoise → tomato), so nth-child(4n+…)
   lands slide 5 back on cobalt on its own, and a sixth slide would pick up lime
   with no extra CSS. Turquoise is --clay, already in the palette.

   Contrast against --ob-bg, all past the 4.5:1 AA floor for body text:
     white on cobalt 9.6:1 · cobalt on lime 6.0:1
     cobalt on turquoise 4.8:1 · white on tomato 4.1:1 (see the note on the copy) */
.onboarding-slide:nth-child(4n+1) { --ob-bg: var(--shutters);   --ob-accent: var(--linen);      --ob-text: #FFFFFF; }
.onboarding-slide:nth-child(4n+2) { --ob-bg: var(--linen);      --ob-accent: var(--shutters);   --ob-text: var(--shutters); }
.onboarding-slide:nth-child(4n+3) { --ob-bg: var(--clay);       --ob-accent: var(--terracotta); --ob-text: var(--shutters); }
.onboarding-slide:nth-child(4n+4) { --ob-bg: var(--terracotta); --ob-accent: var(--clay);       --ob-text: #FFFFFF; }

/* The rounded rectangle that carries the page colour. The slide's side padding
   is what leaves white showing around it — the fill is deliberately not
   full-bleed.

   Every card is the SAME size on every page: the height is fixed rather than
   driven by how much copy a page happens to carry, which is what used to make
   them range from 295px (page 2) to 411px (page 5). Content centres inside
   whatever slack is left.

   360x486 covers ~53% of a 390x844 handset. It has grown twice: ~34% originally,
   then 350x416 (~44%), and now a further 20% of area on top of that. Width is
   near its ceiling — the 15px of white down each side is what keeps this a card
   on a page rather than a full-bleed background — so most of this increase went
   into height. Height is a fixed px rather than a percentage on purpose: the
   footer grows on the last slide when the CTA appears, and a percentage would
   visibly shrink every card at that moment. max-height is the small-screen
   escape hatch — below ~500px of slide the cards cap together, so they stay
   equal to each other. */
.onboarding-card {
  width: 100%;
  max-width: 360px;
  height: 486px;
  max-height: 100%;
  padding: 30px 24px;
  border-radius: var(--radius-xl);
  background: var(--ob-bg);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.onboarding-mark { margin-bottom: 22px; }
/* Slide 1's symbol is the logo mark rather than an icon font, and it keeps the
   brand's own colours rather than the page pairing the other slides use: a red
   square with a lime dot. The inverse — lime square, red dot — read as an olive,
   which is what this replaces. Size is the only thing scaled up here. */
.onboarding-mark .logo-square { width: 72px; height: 72px; border-radius: 18px; background: var(--terracotta); }
.onboarding-mark .logo-dot { width: 14px; height: 14px; background: var(--linen); }

/* The icon sits directly on the card now — the old near-white circular chip
   would have fought the colour, and a lime glyph on it would have all but
   vanished. Tabler is an outline face with no bold cut, so weight comes from
   text-stroke thickening the strokes in place. */
.onboarding-icon {
  margin-bottom: 22px;
  display: flex; align-items: center; justify-content: center;
}
.onboarding-icon i {
  font-size: 60px;
  color: var(--ob-accent);
  -webkit-text-stroke: 1.25px currentColor;
}

/* Bigger and heavier than before (17px regular → 20px bold), on the page colour
   rather than in an outlined box — the border is gone deliberately.
   20px/700 clears the 18.66px-bold threshold for WCAG "large text", so tomato's
   4.1:1 is measured against the 3:1 large-text floor, not the 4.5:1 body floor. */
.onboarding-copy {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.45;
  color: var(--ob-text);
}

/* The welcome line now uses the same face as every other slide rather than the
   Happy Monkey display font, so the five pages read as one set. Dropping that
   font also lifts the no-bold constraint it carried — this can finally be a real
   700 like the rest. It stays a step larger than body copy because it is the
   only line on its page. */
.onboarding-copy--lead {
  font-size: 26px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ob-text);
}

.onboarding-foot {
  padding: 0 34px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.onboarding-dots { display: flex; gap: 8px; margin-bottom: 14px; }
.onboarding-dot {
  width: 8px; height: 8px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--border);
  cursor: pointer;
  transition: background 0.2s ease, width 0.2s ease;
}
.onboarding-dot.active { background: var(--terracotta); width: 20px; border-radius: 4px; }

.onboarding-hint {
  font-size: 12.5px;
  color: var(--lavender);
  min-height: 18px;
}
.onboarding-cta { max-width: 320px; margin-top: 4px; }

/* Motion-sensitive users get a crossfade instead of a horizontal slide. */
@media (prefers-reduced-motion: reduce) {
  .onboarding-track { transition: none; }
  .onboarding-overlay { transition: opacity 0.01ms, visibility 0.01ms; }
  .onboarding-dot { transition: none; }
  /* The skip button's colour cross-fade goes with the slide animation it was
     timed against — keep press feedback, drop the colour tween. */
  .onboarding-skip { transition: transform 0.08s ease, filter 0.15s ease; }
}

/* ============================================================
   Suggest a charity + Admin (v77, Phase 1)
   ============================================================ */

/* The suggest tile reuses .giving-tile's shape but in cherry rather than lime,
   so the two tiles on the Vote tab stay visually distinct. */
.suggest-tile {
  background: var(--card);
  border-color: var(--terracotta);
  color: var(--terracotta-deep);
}

/* url + textarea were missing from the shared field styling — without these the
   new inputs fall back to the browser default and look nothing like the rest
   of the forms. */
.field input[type=url], .field textarea {
  width: 100%; padding: 12px 14px; font-size: 15px;
  border: 1.5px solid var(--border); border-radius: var(--radius-md);
  background: var(--card); color: var(--ink); font-family: var(--font-ui);
  -webkit-appearance: none;
}
.field textarea { resize: vertical; min-height: 88px; line-height: 1.5; }
.field textarea:focus { outline: none; border-color: var(--terracotta); }
.field-optional {
  text-transform: none; letter-spacing: 0; font-weight: 600;
  color: var(--lavender); font-size: 11px;
}

.suggest-thanks { text-align: center; padding: 18px 8px 6px; }
.suggest-thanks .celebration-badge { margin: 0 auto 12px; }
.suggest-thanks-title { font-size: 22px; font-weight: 700; color: var(--shutters); margin-bottom: 6px; }
.suggest-thanks-body {
  font-size: 14px; color: var(--ink-soft); line-height: 1.55;
  margin: 0 auto 16px; max-width: 300px;
}

/* ---------- Admin ---------- */
.admin-tabs {
  display: flex; gap: 4px; background: var(--linen-soft);
  border-radius: 999px; padding: 4px; margin: 10px 0 14px;
}
.admin-tab-content { display: none; }
.admin-tab-content.active { display: block; }

.admin-filter { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }

/* Small pill button, used for both the status filter and the per-card actions. */
.scope-btn {
  padding: 7px 12px; border-radius: 999px;
  border: 1.5px solid var(--border); background: var(--card);
  color: var(--ink-soft); font-size: 13px; font-weight: 600;
  font-family: var(--font-ui); cursor: pointer; -webkit-appearance: none;
}
.scope-btn.active { background: var(--shutters); border-color: var(--shutters); color: var(--linen); }
.scope-btn:active { transform: scale(0.97); }
.scope-btn:disabled { opacity: 0.5; cursor: default; }

.admin-empty { font-size: 14px; color: var(--ink-soft); padding: 18px 2px; text-align: center; }

.admin-card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 14px; margin-bottom: 10px;
  box-shadow: var(--shadow-sm);
}
.admin-card-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px; margin-bottom: 4px;
}
.admin-card-title { font-size: 16px; font-weight: 700; color: var(--shutters); }
.admin-status {
  flex-shrink: 0; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.6px; padding: 4px 8px; border-radius: 999px;
  background: var(--linen-soft); color: var(--ink-soft);
}
.admin-status--new { background: var(--terracotta); color: #fff; }
.admin-status--reviewed { background: var(--linen); color: var(--linen-deep); }
.admin-card-meta { font-size: 12px; color: var(--lavender); margin-bottom: 8px; }
/* Player-written text: wrap hard so one long unbroken string can't widen the card. */
.admin-card-body {
  font-size: 14px; color: var(--ink); line-height: 1.55; margin-bottom: 8px;
  overflow-wrap: anywhere;
}
.admin-card-link {
  display: block; font-size: 13px; color: var(--shutters); font-weight: 600;
  margin-bottom: 10px; overflow-wrap: anywhere;
}
.admin-card-actions { display: flex; flex-wrap: wrap; gap: 6px; }

/* Admin note — the "this data starts from X" caveat that has to travel with
   every funnel number so partial history is never read as all-time. */
.admin-note {
  font-size: 12px; color: var(--ink-soft); line-height: 1.5;
  background: var(--linen-soft); border-radius: var(--radius-md);
  padding: 10px 12px; margin-bottom: 12px;
}
#admin-events .tier-table td:last-child,
#admin-events .tier-table td:nth-child(2) { text-align: right; font-weight: 700; }

/* ============================================================
   Admin charts (v79, Phase 3)

   Colours are validator-approved, not hand-picked:
     categorical pair  #5555CC / #EE3311  (retention: returning / new)
       all six checks PASS on #FFFFFF; CVD worst pair dE 27.7, normal 35.5
     ordinal ramps     single cobalt hue, monotone lightness, dL >= 0.06,
                       light end >= 2:1 contrast on white
   Light mode only, deliberately: the app has no dark mode anywhere, so a dark
   variant for these charts alone would be inconsistent with every other screen.

   Mark specs: bars capped at 24px, 4px rounded data-end and square at the
   baseline, 2px surface gaps doing the separating (never a border), hairline
   recessive gridlines, text always in ink tokens and never a series colour.
   ============================================================ */
:root {
  /* Retention: 2 categorical slots, fixed order, never cycled. */
  --cat-1: #5555CC;   /* returning players */
  --cat-2: #EE3311;   /* new players */
  /* Ordinal ramp, light to dark, 6 steps (streak buckets). */
  --ord-1: #A4AEFF; --ord-2: #8D96F5; --ord-3: #767DE1;
  --ord-4: #6064CE; --ord-5: #4C4BBA; --ord-6: #3A31A6;
  /* Ordinal ramp, 7 steps (funnel uses 6; 7th kept for headroom). */
  --ord7-1: #A4AEFF; --ord7-2: #8D96F4; --ord7-3: #767DE0; --ord7-4: #6065CB;
  --ord7-5: #4C4DB6; --ord7-6: #3A33A2; --ord7-7: #2A158E;
  --grid: #EDEEF5;    /* one step off surface, recessive */
}

/* ---------- Hero figure: exactly one per view ---------- */
.hero-figure { text-align: center; padding: 14px 0 6px; }
.hero-figure-label {
  font-size: 12px; font-weight: 700; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: 0.6px;
}
/* Proportional figures, not tabular: tabular-nums gives every digit the width
   of a zero, which reads loose at display size. Same sans as the rest of the
   UI, never the display face. */
.hero-figure-value {
  font-family: var(--font-ui); font-size: 52px; font-weight: 600;
  color: var(--shutters); line-height: 1.05; margin: 2px 0;
}
.hero-figure-sub { font-size: 13px; color: var(--ink-soft); }

/* ---------- Stat tiles ---------- */
.stat-tiles {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 14px 0 18px;
}
.stat-tile {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 11px 12px;
}
.stat-tile-label { font-size: 11px; color: var(--ink-soft); line-height: 1.3; }
.stat-tile-value {
  font-family: var(--font-ui); font-size: 22px; font-weight: 600;
  color: var(--ink); margin-top: 3px;
}
.stat-tile-value small { font-size: 13px; font-weight: 600; color: var(--ink-soft); }
/* An odd tile count leaves a lone tile on the last row. Let it span the full
   width so it reads as deliberate rather than as a gap. */
.stat-tile:last-child:nth-child(odd) { grid-column: 1 / -1; }

/* ---------- Chart shell ---------- */
.chart-figure { margin: 0 0 26px; }
.chart-figure figcaption { display: block; margin-bottom: 10px; }
.chart-title { display: block; font-size: 15px; font-weight: 700; color: var(--shutters); }
.chart-sub { display: block; font-size: 12px; color: var(--ink-soft); line-height: 1.45; margin-top: 2px; }
.chart-footnote { font-size: 11px; color: var(--ink-soft); line-height: 1.5; margin-top: -8px; }

/* Legend: present whenever there are two or more series. Identity comes from
   the swatch beside the text; the text itself stays in an ink token. */
.chart-legend { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 10px; }
.chart-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-soft); }
.chart-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }

/* ---------- Stacked columns (retention) ----------
   Gridlines are a background gradient rather than elements: 1px, solid, one
   step off surface, sitting behind the marks. */
.chart-plot--cols {
  display: flex; align-items: flex-end; gap: 2px;
  height: 132px; padding-top: 4px;
  background-image: repeating-linear-gradient(to top, var(--grid) 0 1px, transparent 1px 25%);
}
.col {
  flex: 1 1 0; min-width: 0; height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end;
  gap: 2px;
  cursor: default; position: relative;
  max-width: 24px;
  margin: 0 auto;
}
.col-seg { width: 100%; }
/* 4px rounded data-end, square at the baseline: the topmost segment carries
   the rounding, the segment sitting on the axis stays square. */
.col-seg--top { border-radius: 4px 4px 0 0; }
.col-seg--base { border-radius: 0; }
.col-seg--cat1 { background: var(--cat-1); }
.col-seg--cat2 { background: var(--cat-2); }
/* A zero day still needs a hit target, so it keeps a faint baseline tick. */
.col--empty::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--grid); border-radius: 2px;
}
.col:focus-visible { outline: 2px solid var(--shutters); outline-offset: 2px; }

.chart-axis {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--ink-soft); margin-top: 6px;
  font-variant-numeric: tabular-nums;
}

/* ---------- Horizontal bars (streaks, funnel) ---------- */
.chart-plot--rows { display: flex; flex-direction: column; gap: 8px; }
.bar-row { display: grid; grid-template-columns: 92px 1fr auto; align-items: center; gap: 8px; }
.bar-row-label { font-size: 12px; color: var(--ink-soft); line-height: 1.25; }
.bar-track { background: var(--grid); border-radius: 4px; height: 18px; }
.bar-fill {
  height: 100%; min-width: 3px;   /* a tiny non-zero value must still be visible */
  border-radius: 0 4px 4px 0;
  cursor: default;
}
/* ...but ZERO must render as nothing. Without this the min-width painted a 3px
   nub for every empty bucket, so "0 players" read as a small non-zero value —
   the chart asserting something the data doesn't say. Caught by reading the
   computed style: width was 3px at width:0%. */
.bar-fill--zero { min-width: 0; width: 0 !important; }
.bar-fill:focus-visible { outline: 2px solid var(--shutters); outline-offset: 2px; }
/* Value rides the tip of the bar, in an ink token, never the series colour. */
.bar-row-value {
  font-size: 12px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; min-width: 34px; text-align: right;
}
.bar-row-drop { font-size: 10px; color: var(--brick); font-weight: 600; }

/* ---------- Table view ----------
   Always available: the relief channel for contrast, the answer for screen
   readers, and where every unlabelled value still lives. */
.chart-table-toggle {
  background: transparent; border: none; padding: 8px 0 0;
  color: var(--shutters); font-size: 12px; font-weight: 600;
  font-family: var(--font-ui); cursor: pointer; text-decoration: underline;
}
.chart-table { margin-top: 8px; }
.chart-table table { width: 100%; border-collapse: collapse; font-size: 12px; }
.chart-table th {
  text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: 0.6px;
  color: var(--ink-soft); padding: 6px 8px; background: var(--linen-soft);
}
.chart-table td {
  padding: 6px 8px; border-top: 1px solid var(--border); color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.chart-table td:not(:first-child) { text-align: right; }

/* ---------- Tooltip ---------- */
.chart-tip {
  position: fixed; z-index: 260; pointer-events: none;
  background: var(--ink); color: #fff; font-size: 11px; line-height: 1.45;
  padding: 7px 9px; border-radius: 8px; max-width: 190px;
  opacity: 0; transform: translateY(3px); transition: opacity 0.12s ease, transform 0.12s ease;
  box-shadow: var(--shadow-md);
}
.chart-tip.show { opacity: 1; transform: translateY(0); }
.chart-tip b { font-weight: 700; }

/* Four admin tabs do not fit one mobile row at flex:1. */
.admin-tabs { flex-wrap: wrap; }
.admin-tabs .tab-btn { flex: 1 1 auto; min-width: 68px; }

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

/* Texture is the backup identity channel where hue cannot carry it. Only the
   two-series retention chart uses hue for identity, so only it needs this. */
@media (forced-colors: active) {
  .col-seg--cat2 {
    background-image: repeating-linear-gradient(45deg, transparent 0 3px, currentColor 3px 5px);
  }
}

/* ============================================================
   Puzzle admin (v81, Phase 5)
   Reuses the validated ordinal/status tokens already in this file. No new
   colours: the schedule uses status meaning (missing / draft / scheduled),
   which is a reserved scale, not series identity.
   ============================================================ */

/* ---------- Queue summary ---------- */
.queue-summary {
  background: var(--linen-soft); border-radius: var(--radius-md);
  padding: 12px 14px; margin-bottom: 14px;
}
.queue-summary-headline {
  font-family: var(--font-ui); font-size: 24px; font-weight: 600; color: var(--shutters);
}
.queue-summary-sub { font-size: 12px; color: var(--ink-soft); line-height: 1.5; margin-top: 3px; }
/* A gap in the queue is the thing that actually hurts — a recorded episode with
   no puzzle behind it. It gets status colour AND words, never colour alone. */
.queue-summary--gap { background: rgba(204, 34, 0, 0.08); }
.queue-summary--gap .queue-summary-headline { color: var(--brick); }

/* ---------- Queue list ---------- */
.queue-day {
  display: grid; grid-template-columns: 54px 1fr auto; gap: 10px;
  align-items: center; padding: 9px 10px;
  border-bottom: 1px solid var(--border);
}
.queue-day:last-child { border-bottom: none; }
.queue-day--missing { background: rgba(204, 34, 0, 0.05); }
.queue-day--today { box-shadow: inset 3px 0 0 var(--shutters); }
.queue-date { font-size: 11px; color: var(--ink-soft); line-height: 1.25; font-variant-numeric: tabular-nums; }
.queue-date strong { display: block; font-size: 14px; color: var(--ink); font-weight: 700; }
.queue-body { min-width: 0; }
/* Both are spans (they sit inside a grid cell), so they need display:block or
   the category runs on from the connection and reads as one sentence —
   "They're all dance crazes So Clever". */
.queue-conn {
  display: block;
  font-size: 13px; color: var(--ink); font-weight: 600;
  overflow-wrap: anywhere; line-height: 1.3;
}
.queue-cat { display: block; font-size: 11px; color: var(--ink-soft); margin-top: 2px; }
.queue-missing-text { display: block; font-size: 13px; color: var(--brick); font-weight: 600; }
.queue-badge {
  flex-shrink: 0; font-size: 9px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; padding: 3px 7px; border-radius: 999px;
  background: var(--linen-soft); color: var(--ink-soft); white-space: nowrap;
}
.queue-badge--draft { background: var(--gold); color: #3a2a00; }
.queue-badge--missing { background: var(--brick); color: #fff; }

/* ---------- Import ---------- */
.field textarea#import-paste { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; }
.import-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.import-actions .primary-btn, .import-actions .secondary-btn { flex: 1 1 auto; width: auto; }

.import-card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px; margin-bottom: 10px;
}
/* A parsed puzzle that needs a human decision before saving. Flagged with a
   heading and a reason, not just a tint. */
.import-card--review { border-color: var(--gold); background: rgba(230, 179, 60, 0.06); }
.import-card--error  { border-color: var(--brick); background: rgba(204, 34, 0, 0.05); }
.import-card-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-bottom: 6px;
}
.import-card-date { font-size: 13px; font-weight: 700; color: var(--shutters); }
.import-card-cat { font-size: 11px; color: var(--ink-soft); }
.import-card-conn { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 8px; overflow-wrap: anywhere; }
.import-card-qs { margin: 0 0 8px; padding-left: 16px; }
.import-card-qs li { font-size: 11px; color: var(--ink-soft); line-height: 1.5; overflow-wrap: anywhere; }
.import-card-qs b { color: var(--ink); }
.import-flag {
  font-size: 11px; font-weight: 600; color: #7a5800; line-height: 1.45;
  background: rgba(230, 179, 60, 0.18); border-radius: 8px; padding: 7px 9px; margin-bottom: 8px;
}
.import-card--error .import-flag { color: var(--brick); background: rgba(204, 34, 0, 0.1); }
.import-field { margin-bottom: 8px; }
.import-field label {
  display: block; font-size: 10px; font-weight: 700; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 3px;
}
.import-field input {
  width: 100%; padding: 8px 10px; font-size: 13px; font-family: var(--font-ui);
  border: 1.5px solid var(--border); border-radius: 10px;
  background: var(--card); color: var(--ink); -webkit-appearance: none;
}
.import-field input:focus { outline: none; border-color: var(--terracotta); }
.import-field .import-hint { font-size: 10px; color: var(--lavender); margin-top: 3px; }

/* ---------- Performance ---------- */
.perf-card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px; margin-bottom: 10px;
}
.perf-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.perf-title { font-size: 13px; font-weight: 700; color: var(--shutters); overflow-wrap: anywhere; }
.perf-sub { font-size: 11px; color: var(--ink-soft); margin-top: 2px; }
.perf-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-top: 10px;
}
.perf-cell { text-align: center; }
.perf-cell-value {
  font-family: var(--font-ui); font-size: 16px; font-weight: 600; color: var(--ink);
}
.perf-cell-label { font-size: 9px; color: var(--ink-soft); text-transform: uppercase; letter-spacing: 0.4px; }
/* Per-question rates as a small ordinal row — question order is meaningful. */
.perf-qs { display: flex; gap: 4px; margin-top: 10px; }
.perf-q { flex: 1; text-align: center; }
.perf-q-bar { height: 4px; border-radius: 2px; background: var(--grid); margin-bottom: 3px; }
.perf-q-fill { height: 100%; border-radius: 2px; background: var(--cat-1); }
.perf-q-label { font-size: 9px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* ============================================================
   Password reset (v84)
   ============================================================ */

/* Above the sign-up overlay (180) and the onboarding slides (185): someone who
   arrived from a reset link is mid-task, so nothing else belongs in front. */
.newpass-overlay { z-index: 190; }

.signup-forgot { font-size: 13px; text-align: center; margin-top: 12px; }
.signup-forgot button {
  background: none; border: none; padding: 4px;
  color: var(--shutters); font-family: var(--font-ui); font-size: 13px;
  font-weight: 600; text-decoration: underline; cursor: pointer;
}

/* Neutral confirmation, distinct from .form-error. Used for "check your email",
   which is deliberately shown whether or not the address has an account —
   saying "no such account" would let anyone test which emails are registered. */
.form-note {
  font-size: 13px; font-weight: 600; line-height: 1.5;
  color: var(--linen-deep); background: var(--linen-soft);
  border-radius: var(--radius-md); padding: 10px 12px; margin: 2px 0 10px;
}

/* ============================================================
   Puzzle preview mode (v86)
   ============================================================ */

/* Sits above the app (which has no z-index of its own) but below the sign-up
   gate (180), the onboarding slides (185) and the splash (200) — none of which
   should ever appear underneath it. Hidden unless .show. */
.preview-bar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 175;
  display: none;
  align-items: center; gap: 10px;
  padding: calc(8px + var(--safe-area-inset-top)) 12px 8px;
  background: var(--terracotta); color: #fff;
  box-shadow: var(--shadow-md);
}
.preview-bar.show { display: flex; }
.preview-bar-text { flex: 1; min-width: 0; line-height: 1.25; }
.preview-bar-text strong {
  display: block; font-size: 13px; font-weight: 700;
}
.preview-bar-text span { display: block; font-size: 10px; opacity: 0.9; }
.preview-bar-exit {
  flex-shrink: 0; background: #fff; color: var(--terracotta-deep);
  border: none; border-radius: 999px; padding: 7px 14px;
  font-family: var(--font-ui); font-size: 12px; font-weight: 700; cursor: pointer;
  -webkit-appearance: none;
}
.preview-bar-exit:active { transform: scale(0.97); }

/* While previewing, push the app down so the banner doesn't cover the header. */
.previewing #app { padding-top: 52px; }

/* Preview button on each schedule row. */
.queue-actions { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.queue-preview-btn {
  background: var(--card); border: 1.5px solid var(--shutters);
  color: var(--shutters); border-radius: 999px; padding: 5px 11px;
  font-family: var(--font-ui); font-size: 11px; font-weight: 700;
  cursor: pointer; white-space: nowrap; -webkit-appearance: none;
}
.queue-preview-btn:active { transform: scale(0.97); }
.queue-preview-btn:disabled { opacity: 0.4; cursor: default; }
