@font-face {
  font-family: "Anton";
  src: url("../assets/fonts/Anton-Regular.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/Inter-Regular.ttf") format("truetype");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/Inter-Bold.ttf") format("truetype");
  font-weight: 700;
  font-display: swap;
}

/* Same design system as quizebra.com/geography (geoquiz/css/style.css, kept
   byte-for-byte identical below) - only --accent/--accent-dim and the rgba()
   tints derived from them differ, swapped to this project's own blue
   (#7ec6ff) in place of geography's mint (#6ee7b7). Never hand-adapt this
   file again without also re-diffing against geography's - see chat,
   2026-09-10: an earlier hand-ported version silently drifted (missing the
   narrow start-panel card width, the game-mode header shrink, the hub's
   375-511px responsive tier, and more), which is exactly what byte-for-byte
   copying + a mechanical color swap avoids. */
:root {
  --bg: #0f0f0f; /* direct instruction, 2026-09-14 - was #0a0a0a */
  --panel: #161616;
  --panel-edge: #2a2a2a;
  --accent: #7ec6ff;
  --accent-dim: #395973;
  --good: #6ee7b7;
  --bad: #ff4d5e;
  --text: #f4f4f4;
  --text-soft: #cfcfcf;
  --text-dim: #999999;
  color-scheme: dark;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

/* Hides the ENTIRE page instantly when arriving via a hub card's own
   ?autostart=1 link, so nothing flashes before quiz.js's async init() gets
   a chance to decide what to show itself (that has to wait on a data fetch
   first). visibility (not display) so .wrap keeps its normal layout box -
   no reflow/scroll jump when it reappears. The .autostarting class is set
   synchronously by a tiny inline <script> in each game's own <head>, before
   first paint, and removed by quiz.js's own init() in the same synchronous
   pass as the .hidden toggles that follow it (never across an await), so
   whatever becomes visible when this class is dropped is already the
   final, correct state - not just the intro screen specifically (an
   earlier version of this rule only targeted #start-panel, which still let
   the CARD's own narrower "intro" width rule apply during the gap, since
   that rule keys off the hidden ATTRIBUTE, not display - a visible width
   jump right as the intro was replaced; found via review, 2026-09-13).
   Same fix applied to geoquiz's own style.css. */
html.autostarting .wrap { visibility: hidden; }

body {
  margin: 0;
  background: #000000; /* pure black (direct instruction, 2026-09-14 - was #1a1a1a) */
  color: var(--text);
  font-family: "Inter", system-ui, sans-serif;
  min-height: 100vh; /* fallback for browsers without dvh */
  min-height: 100dvh; /* mobile address-bar-safe viewport height */
  display: flex;
  justify-content: center;
  padding: 40px 16px 32px;
}

.wrap { width: 100%; max-width: 480px; }

.site-header { text-align: center; margin-bottom: 29px; }
.site-logo-mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0 auto 10px;
  text-decoration: none; /* it's an <a> on the hub page (2026-09-15, links home) -
    no visible text inside to underline anyway, but explicit rather than relying
    on that - the portal's own logo stays a plain <div>, no link-to-self needed */
}
.site-logo-icon { display: block; width: 61.425px; height: auto; }
.kicker {
  display: inline-block;
  font: 700 12px/1 "Inter", sans-serif;
  letter-spacing: 0.28em;
  color: var(--accent);
  margin-bottom: 6px;
}
/* "See all games" - a way back to the portal (2026-09-15, direct instruction:
   the hub page had NO link back to "/" at all before this - only the site
   logo above, now also made a link, see .site-logo-mark's own comment).
   Sits below the game grid, above the divider/intro paragraph below it -
   deliberately the CROSS-topic accent color (geography's mint here, on the
   football hub) rather than this page's own blue, as a small visual nod
   to "the other topic" you're one click away from. */
.hub-see-all {
  display: block;
  text-align: center;
  margin-top: 24px;
  color: #6ee7b7; /* geography's own --accent - deliberately NOT this page's blue, see comment above */
  font: 700 13px/1 "Inter", sans-serif;
  text-decoration: none;
}
.hub-see-all:hover { text-decoration: underline; }

/* Hub-only short intro paragraph (2026-09-15, Phase 2 SEO/LLM work) - real
   written content BELOW the game grid (moved down from above it, direct
   instruction - the games stay the first thing seen, prose comes after),
   for a crawler/reader to get real sentences, not just card labels. */
.hub-intro {
  color: var(--text-soft);
  font-size: 13px;
  line-height: 1.5;
  text-align: left;
  margin-top: 20px;
  padding-top: 24px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

/* Site-wide footer - see geoquiz/css/style.css's own comment on this rule
   (byte-for-byte identical here, same shared-component convention as the
   rest of this file). */
.site-footer {
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  text-align: center;
  font: 400 12px/1.6 "Inter", sans-serif;
  color: var(--text-dim);
}
.site-footer a { color: var(--text-dim); text-decoration: none; }
.site-footer a:hover { color: var(--accent); text-decoration: underline; }
.site-footer .footer-sep { margin: 0 6px; color: var(--text-dim); }
.site-header h1 {
  font: 400 42.29px/1 "Anton", sans-serif; /* fitted to match .start-title's rendered width, same target as the kicker */
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
  color: var(--text-soft);
}
/* Game-mode-only overrides (while #play-area is visible, i.e. after "Start
   Playing") - must not touch the intro's fitted sizing/spacing. */
.wrap:has(#play-area:not([hidden])) .site-logo-icon { width: 43px; } /* 70% of 61.425px, top stays put since only height shrinks */
.wrap:has(#play-area:not([hidden])) .site-logo-mark { margin: -18px auto 4px; } /* title sits closer to the logo, whole thing shifted up */
.wrap:has(#play-area:not([hidden])) .site-header h1 { font-size: 32px; margin-top: 8px; } /* matches .result-title's size */
.wrap:has(#play-area:not([hidden])) .site-header { margin-bottom: 15px; } /* brings the card closer beneath the title */

.card {
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 20px;
  padding: 10px 24px 24px;
  box-shadow: 0 20px 60px -20px rgba(0, 0, 0, 0.6);
}
/* Narrower, content-hugging width only while the intro screen is showing - the
   play-area screens need the full wrap width, so this must not apply once
   start-panel is hidden after "Start Playing". */
.card:has(#start-panel:not([hidden])) {
  width: 258px; /* 200px content column + 29px inset each side, matching the card-top-to-kicker-top distance */
  margin: 0 auto;
  padding-left: 29px;
  padding-right: 29px;
  padding-bottom: 29px;
  max-width: calc(100% - 20px); /* safety net so this doesn't overflow narrow phones */
}

/* Hub page (quizebra.com/football/) - one card per game, wrapping as more
   get added. */
.hub-wrap .site-header h1 { color: var(--text); }
#hub-kicker {
  display: block; /* .kicker's default inline-block doesn't respond to margin-top the
    normal way; block does */
  margin-top: 2px;
}
.hub-wrap .site-header { margin-bottom: 16px; }
.hub-wrap { max-width: 480px; } /* exactly 2 cards (232px) + the gap between them
  (16px) - forces a strict 2-per-row grid at any viewport width */
.hub-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
}
.hub-card {
  display: block;
  width: 232px;
  max-width: calc(100% - 20px);
  text-decoration: none;
  color: inherit;
  transition: transform 0.12s ease, border-color 0.12s ease;
  box-shadow: 3px 3px 0 0 var(--text);
  border-color: var(--text);
}
.card.hub-card {
  padding: 18px;
  background: #000000; /* pure black (direct instruction, 2026-09-14 - was var(--bg)/#0f0f0f) */
}
.hub-card .start-panel { padding-top: 0; }
.hub-card .start-title { margin-top: 0; }
.hub-card .start-rules { margin-bottom: 19px; white-space: nowrap; }
/* "Rank the Player" - same pattern as geography's "Rank the Country" (direct
   instruction, 2026-09-20) - byte-identical rule copied from geoquiz's own
   style.css (same title height there too, confirmed live: 23.1875px, so the
   same px stops apply unchanged); renders in football's own --accent (blue)
   automatically since it's the same CSS variable, not a literal color. Was
   .hub-title-grid (thin gridlines) - removed, this title's only user in this
   file. */
.hub-title-stripes {
  background: linear-gradient(
    to bottom,
    var(--accent) 0, var(--accent) 7.6375px,
    var(--text) 7.6375px, var(--text) 12.275px,
    var(--accent) 12.275px, var(--accent) 16.9125px,
    var(--text) 16.9125px, var(--text) 21.55px,
    var(--accent) 21.55px, var(--accent) 23.1875px
  );
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* "Who Has More?" - accent color at the very bottom fading to the title's
   normal color at the very top (direct instruction, 2026-09-14). */
.hub-title-vertical-fade {
  background: linear-gradient(to top, var(--accent), var(--text));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* "Guess the Player" - same trick as geography's "Guess the Country"
   (.hub-title-stlucia - a shape centered over the title text, background-
   clip:text over a solid fallback), but a plain filled circle (a ball, nod
   to the game itself) instead of a country silhouette - no separate image
   asset needed, a radial-gradient draws the circle directly (direct
   instruction, 2026-09-14). */
.hub-title-circle {
  /* Stop was 55%/56% (~97px diameter) - shrunk to ~75% of that (direct
     instruction, 2026-09-20, "doesn't have to be exact") by scaling the
     stop itself: 55*0.75≈41. */
  background: radial-gradient(circle, var(--accent) 41%, transparent 42%) center / auto 400% no-repeat, var(--text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* "Guess the Club" - .hub-title-circle's own trick inverted, direct
   instruction, 2026-09-20: the circle itself in var(--text) (white),
   everything outside it in var(--accent) (blue) - opposite of "Guess the
   Player" above. Replaces .hub-title-flag (a 3-band stripe pattern), this
   title's only user in this file - removed that rule. Same 41%/42% shrink
   as .hub-title-circle above, to keep the two visually matched. */
.hub-title-circle-invert {
  background: radial-gradient(circle, var(--text) 41%, transparent 42%) center / auto 400% no-repeat, var(--accent);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* "Build the Team" - same pattern as geography's "Find the Color" (direct
   instruction, 2026-09-20) - byte-identical rule copied from geoquiz's own
   style.css; renders in football's own --accent (blue) automatically since
   it's the same CSS variable. Was .hub-title-diagonal (a diagonal split) -
   removed, this title's only user in this file. */
.hub-title-gradient {
  background: linear-gradient(to right, var(--accent), var(--text));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* "Order the Player" - same pattern as geography's "Order the Country"
   (direct instruction, 2026-09-20): one down arrow behind "Order", one up
   arrow behind "Player" - same shape family as .arrange-arrow's own move-
   up/move-down buttons there. Hand-drawn inline SVG triangles (tight
   viewBox, no padding) so "auto 100%" lands each triangle's own top/bottom
   exactly on the text's own top/bottom. Baked with football's own accent
   hex (#7ec6ff) directly, not var(--accent) - a data-URI SVG can't
   reference a CSS custom property. Was .hub-title-split (a plain 50/50
   horizontal split) - removed, this title's only user in this file. */
.hub-title-arrow-down {
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 87'%3E%3Cpolygon points='0,0 100,0 50,87' fill='%237ec6ff'/%3E%3C/svg%3E") center / auto 100% no-repeat, var(--text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hub-title-arrow-up {
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 87'%3E%3Cpolygon points='50,0 100,87 0,87' fill='%237ec6ff'/%3E%3C/svg%3E") center / auto 100% no-repeat, var(--text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hub-beat-label {
  font: 700 16px "Inter", sans-serif;
  letter-spacing: 2.4106px;
  color: var(--accent);
  margin: 0 0 4px;
}
.hub-card .leaderboard { width: 188px; margin: 0 auto 19px; }
.hub-card .btn-primary {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 188px;
  margin: 0 auto;
  height: 30px;
  padding: 0 14px;
  border-radius: 8px;
}
@media (hover: hover) {
  a.hub-card:hover { border-color: var(--accent); transform: translateY(-2px); }
}

/* Force 2 cards per row down to 375px viewport width instead of dropping to 1
   per row - same tier as geography's own hub. */
@media (min-width: 375px) and (max-width: 511px) {
  .hub-wrap { margin: 0 -11px; width: calc(100% + 22px); }
  .hub-grid { gap: 9px; }
  .hub-card { zoom: 0.7759; }
  .hub-card .start-rules { font-size: 8.5px; }
  .site-footer { font-size: 11px; }
}

.start-panel {
  text-align: center;
  padding: 10px 0 0;
}
.start-panel .kicker { display: block; margin-top: 8px; margin-bottom: 2px; }
.start-title {
  font: 400 26px "Anton", sans-serif; /* shared/fixed across every game on purpose */
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.15;
  margin: 4px 0 2px;
}
.start-rules {
  font: 700 9.8px "Inter", sans-serif;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: -1px 0 22px;
}
.start-hook {
  font: 700 13px "Inter", sans-serif;
  color: var(--accent);
  background: rgba(126, 198, 255, 0.08);
  border: 1px solid var(--accent-dim);
  border-radius: 10px;
  padding: 10px 14px;
  width: 200px; /* matches the card's content column */
  margin: 30px auto 22px;
}
.start-hook-time { font-size: 1.35em; }
.start-hook-line2 { display: inline-block; font-size: 19.3px; margin-top: 4px; }

.result-panel { text-align: center; margin-top: 10px; }
.result-time-label {
  font: 700 12px "Inter", sans-serif;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-soft);
  margin: 0 0 -2px;
}
.result-title {
  font: 400 32px "Anton", sans-serif;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0 0 6px;
  color: var(--accent);
}
.result-title.wrong { color: var(--bad); }
.result-sub {
  font: 400 15px "Inter", sans-serif;
  color: var(--text-soft);
  margin: -10px 0 16px;
}

.btn-primary {
  display: block;
  font: 700 15px "Inter", sans-serif;
  color: #0a0f0c;
  background: var(--accent);
  border: none;
  border-radius: 12px;
  padding: 14px 22px;
  cursor: pointer;
  width: 100%;
  text-align: center;
}
#start-btn { width: 200px; margin-top: 14px; } /* matches .start-hook's width */
@media (hover: hover) {
  .btn-primary:hover { filter: brightness(1.08); }
}

.btn-secondary {
  font: 700 15px "Inter", sans-serif;
  color: var(--text);
  background: #2a2a2a;
  border: none;
  border-radius: 12px;
  padding: 14px 22px;
  cursor: pointer;
  width: 100%;
  margin-bottom: 10px;
}
@media (hover: hover) {
  .btn-secondary:hover { background: #333333; }
}

#share-btn:not([hidden]) {
  background: var(--accent);
  color: #0a0f0c;
}
@media (hover: hover) {
  #share-btn:not([hidden]):hover { filter: brightness(1.08); background: var(--accent); }
}
.result-panel:has(#share-btn:not([hidden])) #next-btn {
  background: #2a2a2a;
  color: var(--text);
}
@media (hover: hover) {
  .result-panel:has(#share-btn:not([hidden])) #next-btn:hover { background: #333333; filter: none; }
}

.result-actions {
  display: flex;
  gap: 10px;
}
.result-actions .btn-primary { width: auto; flex: 1; }
.result-actions .btn-secondary { width: auto; flex: 1; margin-bottom: 0; }
.practice-back-link {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  font: 700 14px "Inter", sans-serif;
  color: var(--text);
  text-decoration: none;
  text-align: center;
  white-space: nowrap;
  background: #2a2a2a;
  border-radius: 12px;
  padding: 14px 22px;
}
#practice-back-link-playing {
  margin-top: 8px;
  font-size: 15px;
}
#hub-link {
  display: block;
  margin-top: 14px;
  text-align: center;
  font: 700 13px "Inter", sans-serif;
  line-height: 1.3;
  color: var(--accent);
  text-decoration: none;
}
.card:has(#hub-link:not([hidden])) { padding-bottom: 14px; }
@media (hover: hover) {
  #hub-link:hover { text-decoration: underline; }
}
@media (hover: hover) {
  .practice-back-link:hover { background: #333333; }
}

.play-area {
  position: relative;
  --title-h: 30px;
}

.practice-badge {
  position: absolute;
  top: 0;
  height: var(--title-h);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 0 7px;
  background: var(--bg);
  border: 1px solid var(--panel-edge);
  border-radius: 6px;
  white-space: nowrap;
  z-index: 2;
}
.practice-badge-left { left: 0; }
.practice-badge-right { right: 0; }
.practice-badge-label {
  font: 700 8px "Inter", sans-serif;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  transform: translateY(1px);
}
.practice-badge-value {
  font: 700 12px "Inter", sans-serif;
  color: var(--accent);
}
@media (max-width: 375px), (max-height: 700px) {
  .practice-badge { padding: 0 6px; }
}

.leaderboard {
  list-style: none;
  margin: 18px 0 20px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
}
.leaderboard-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  background: transparent;
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
}
/* The #1 row always renders first (rows are already rank-sorted) - direct
   instruction, 2026-09-14, applies on every subpage that shows a real
   leaderboard, and also to a hub-card's own single-row "#1" preview (see
   .hub-card .leaderboard) since that's always rank 1 too. */
.leaderboard-row:first-child { background: #181818; }
.leaderboard-row.you { border-color: var(--accent-dim); }
.lb-rank { font: 700 11px "Inter", sans-serif; color: var(--text-soft); width: 20px; }
.leaderboard-row.you .lb-rank { color: var(--accent); }
.lb-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 400 12px "Inter", sans-serif;
  color: var(--text-soft);
}
.lb-time {
  flex-shrink: 0;
  width: 34px;
  text-align: right;
  font: 400 12px "Inter", sans-serif;
  color: var(--text-soft);
}

.lb-name-edit { display: flex; align-items: center; gap: 6px; }
.lb-name-input {
  flex: 1; min-width: 0; font: 700 16px "Inter", sans-serif; color: var(--text);
  background: transparent; border: 1px solid var(--panel-edge); border-radius: 6px;
  padding: 3px 6px;
  transition: font-size 0.15s ease, padding 0.15s ease;
}
.lb-name-input:focus {
  outline: none;
  border-color: var(--accent);
  font-size: 20px;
  padding: 6px 8px;
}
.lb-name-save {
  flex: 0 0 auto; font: 700 11px "Inter", sans-serif; color: var(--accent);
  background: transparent; border: 1px solid var(--accent-dim); border-radius: 6px;
  padding: 3px 8px; cursor: pointer;
}
.lb-name-save:disabled { opacity: 0.6; cursor: default; }
@media (hover: hover) {
  .lb-name-save:hover:not(:disabled) { border-color: var(--accent); }
}

.your-rank-note {
  font: 700 12px "Inter", sans-serif;
  color: var(--text-dim);
  margin: 0 0 16px;
}

@media (max-width: 480px) {
  body { padding: 40px 14px 32px; }
  .kicker { margin-bottom: 7px; }
  .card { padding: 14px 16px 24px; }
  .wrap:has(#play-area:not([hidden])) .site-header h1 { font-size: 24px; }
  .start-rules { margin-bottom: 18px; }
  .start-hook { margin: 0 auto 18px; }
  .result-panel { margin-top: 6px; }
  .result-title { font-size: 24px; margin-bottom: 4px; }
  .result-panel:has(#result-time-label:not([hidden])) .result-title { margin-top: -4px; }
  .result-sub { font-size: 13px; margin-bottom: 8px; }
  .btn-primary { padding: 8px 18px; }
  .btn-secondary { padding: 8px 18px; margin-bottom: 8px; }
  .result-actions:has(#practice-back-link:not([hidden])) .btn-primary { font-size: 13.4px; }
  .result-actions:has(#share-btn:not([hidden])) .btn-primary,
  .result-actions:has(#share-btn:not([hidden])) .btn-secondary { font-size: 13.4px; }
  .practice-back-link { padding: 8px 18px; font-size: 11.5px; }
  #practice-back-link-playing { margin-top: 8px; padding: 8px 18px; font-size: 15px; }
  .leaderboard { margin-top: 10px; }
}

@media (max-width: 375px), (max-height: 700px) {
  body { padding: 22px 12px 18px; }
  .card { padding: 10px 12px; }
  .start-rules { margin-bottom: 14px; }
  .start-hook { padding: 8px 12px; margin-bottom: 14px; }
  #practice-back-link-playing { margin-top: 8px; }
}

/* ---------------------------------------------------------------------
   Rank the Player - 8-player/8-category ranking-assignment puzzle, ported
   from geography's "Rank the Country" (same mechanic/markup, minus flag
   imagery - no player-photo pipeline yet, plain name cards instead).
   --------------------------------------------------------------------- */
.rank-goal {
  font: 400 12px "Inter", sans-serif;
  line-height: 1.4;
  color: var(--text-dim);
  text-align: center;
  margin: 0 0 8px;
  padding-top: 36px;
}
.rank-instructions {
  font: 700 12.5px "Inter", sans-serif;
  color: var(--text-soft);
  text-align: center;
  margin: 0 0 16px;
}

.rank-pool {
  display: grid;
  /* 1fr, not auto+justify-content:center (was: a fixed-width 74px card,
     centered as a narrower block than the full-width category grid below
     it) - 1fr stretches all 4 cards to fill the same full content width
     .pair-module's buckets already span, so the pool's outer left/right
     edges land exactly on the bucket row's own edges (direct instruction,
     2026-09-12). gap stays the fixed px value it always was - only the
     cards themselves grow, not the space between them. */
  grid-template-columns: repeat(4, 1fr);
  /* start, not the grid default (stretch) - without this, once the pool
     drops to a single row (5-8 players placed), the remaining row's track
     stretches to fill the space min-height below reserves for a full 2-row
     pool, and every card's own default align-items:stretch then stretches
     WITH it, making the last few cards visibly taller than the first ones
     were. start keeps every card at its own natural height regardless of
     row count (direct instruction, 2026-09-12). */
  align-content: start;
  align-items: start;
  gap: 9px;
  margin-bottom: 22px;
  /* Single source of truth for every .ccard's own fixed height below (58px -
     enough for a 2-line name at this tier's 12px font + 14px padding, with a
     little slack) - referenced here too for the pool's own row-reservation
     math so the two can never drift out of sync with each other. */
  --ccard-h: 58px;
  /* 1-row height by default - the taller 2-row reservation right below only
     applies while a 5th+ card is still actually in the pool (i.e. more than
     one row's worth remain). So the category grid below stays put through
     8->5 remaining players (still legitimately 2 rows), then deliberately
     moves up once the pool drops to a single row of <=4 (direct
     instruction, 2026-09-12: "when only players of one row are left, move
     the rest up") - not reserved-forever blank space for a vanished 2nd
     row, which is what a flat always-2-rows min-height would do. */
  min-height: var(--ccard-h);
}
.rank-pool:has(.ccard:nth-child(5)) {
  min-height: calc(var(--ccard-h) * 2 + 9px);
}
/* No flag image (no player-photo pipeline yet) - a plain name card, vertically
   centered, rather than geography's fixed flag-wrap + bottom-aligned name-zone
   split. */
.ccard {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #1e1e1e;
  border: 1px solid var(--panel-edge);
  border-radius: 12px;
  padding: 14px 6px;
  /* No explicit width - stretches to fill its 1fr grid column (see
     .rank-pool's own comment), same width for all 4 regardless of name
     length, by construction (a grid column's size, not a per-card fit).
     height (not min-height, direct instruction 2026-09-12) - a 1-word name
     (e.g. "Cafu", 1 line via appendPlayerName) was otherwise shorter than a
     2+-word name (2 lines), sitting at a smaller natural/floor height
     instead of matching every other card. Fixed height + the align-items:
     center already above centers ANY name (1 or 2 lines) within the exact
     same box. */
  height: var(--ccard-h);
  cursor: pointer;
  transition: border-color 0.12s ease, opacity 0.12s ease, background 0.12s ease;
}
.ccard.selected { border-color: var(--accent); }
.ccard.targetable { border-color: var(--accent); background: rgba(126, 198, 255, 0.08); }
.ccard.dimmed { opacity: 0.35; }
.ccard-name-zone { width: 100%; display: flex; align-items: center; justify-content: center; }
.ccard-name {
  /* Matches .pair-bucket's own category text size exactly (12px base / 11px
     under 480px, see that breakpoint override below) - direct instruction,
     2026-09-12. */
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  line-height: 1.2;
  white-space: normal;
  color: var(--text);
}

.pair-stack { display: flex; flex-direction: column; gap: 10px; }
.pair-module { display: flex; flex-direction: row; align-items: stretch; gap: 8px; }
.pair-bucket {
  flex: 1;
  /* 0, not the flex default (auto) - without this, a flex item's minimum
     width is based on its own content's min-content size, which for a
     white-space:nowrap category line (e.g. "Premier League Titles 🏆" on a
     very narrow screen) can exceed this bucket's fair 1-of-2 share of the
     row, forcing the bucket (and the whole pair-module row) wider than
     intended instead of letting the text itself shrink to fit (direct
     instruction, 2026-09-12 - see buildBucket's own shrinkCategoryLabelToFit
     call in quiz.js for the other half of this fix). */
  min-width: 0;
  border: 1.5px solid var(--panel-edge);
  border-radius: 12px;
  padding: 10px 10px 10px 16px;
  font: 600 12px "Inter", sans-serif;
  color: var(--text);
  display: flex;
  align-items: center;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, opacity 0.12s ease;
}
.pair-bucket.targetable { border-color: var(--accent); background: rgba(126, 198, 255, 0.08); }
.pair-bucket.selected { border-color: var(--accent); background: rgba(126, 198, 255, 0.08); }
.pair-bucket.dimmed { opacity: 0.35; }
.pair-bucket.filled { cursor: default; flex-direction: column; align-items: stretch; gap: 6px; background: #1e1e1e; }
.bucket-label {
  font: 600 10px "Inter", sans-serif;
  letter-spacing: 0.02em;
  color: var(--text);
}

/* Every category label everywhere on this page (live-grid buckets - both
   empty and filled - and the results comparison table) is built by quiz.js's
   buildCategoryLabel as this same 2-child structure: "{dir word} {arrow}"
   over "{stat text} {emoji}" - direct instruction, 2026-09-12: always 2
   lines, never all-caps. font-size/color come entirely from the surrounding
   context (.pair-bucket/.bucket-label/.comparison-category below), not set
   here, so the one JS builder renders correctly in all three places. nowrap
   on each line, not the wrapping trick the comparison table used before this
   change - each line is now its own real line by construction (2 flex
   children), not text wrapping to fit a column, so nowrap here is safe and
   keeps a stray extra-long value from silently becoming a 3rd line. */
.category-label {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 1px;
}
/* Direct child of an EMPTY .pair-bucket only (the filled state nests it one
   level deeper, inside .bucket-label, which already stretches via its own
   parent's align-items:stretch) - without flex:1 here, this box never
   actually fills the bucket's width (a flex item's default flex-grow is 0),
   so it sits at its own natural/shrunk content width, left-aligned, with
   the bucket's leftover width just showing as empty space to its right -
   same available width shrinkCategoryLabelToFit already computes from the
   BUCKET, but wasted if this box itself doesn't also claim that width. */
.pair-bucket > .category-label { flex: 1; }
.category-label-dir,
.category-label-stat {
  white-space: nowrap;
}
/* text-dim, not accent - the arrow (a plain child span, no color rule of its
   own) inherits this too, matching the rest of the dir line rather than
   standing out on its own. */
.category-label-dir { font-weight: 700; color: var(--text-dim); }
/* "Career-High" reads as part of "Market Value"/whichever line it's on, not
   its own separate thing - explicit override back to the stat line's own
   color (text), not the dir line's inherited text-dim. */
.stat-callout-small { font-size: 1em; color: var(--text); }
.bucket-country { display: flex; align-items: center; gap: 6px; }
.bucket-country-name {
  flex: 1;
  min-width: 0;
  font: 700 12px "Inter", sans-serif;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bucket-undo {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1px solid var(--panel-edge);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
}
@media (hover: hover) {
  .bucket-undo:hover { border-color: var(--bad); color: var(--bad); }
}

#submit-btn { margin-top: 18px; }
#submit-btn.not-ready {
  background: #2a2a2a;
  color: var(--text-dim);
}
@media (hover: hover) {
  #submit-btn.not-ready:hover { background: #2a2a2a; filter: none; }
}
.submit-hint {
  font: 700 12px "Inter", sans-serif;
  color: var(--bad);
  text-align: center;
  margin: 10px 0 0;
}

/* 0, not 36px - direct instruction, 2026-09-12: "YOUR SCORE:" should start
   flush at the same top as the practice badges (position:absolute; top:0),
   not clear below them. Safe since the badges sit in the corners
   (left:0/right:0) while this text is centered - they occupy the same
   vertical band but don't collide horizontally. */
.play-area:has(#rank-goal):has(#session-badge:not([hidden])) .result-time-label { padding-top: 0; }

#comparison-divider, #recap-divider {
  border: none;
  border-top: 1px solid var(--panel-edge);
  margin: 14px -24px 20px;
}
@media (max-width: 480px) {
  #comparison-divider, #recap-divider { margin-left: -16px; margin-right: -16px; }
}
@media (max-width: 375px), (max-height: 700px) {
  #comparison-divider, #recap-divider { margin-left: -12px; margin-right: -12px; }
}

.comparison-header.comparison-row { margin-bottom: 10.7px; }
.comparison-col-title {
  margin: 0;
  min-width: 0;
  text-align: center;
  font: 700 9px "Inter", sans-serif;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.comparison-col-pct {
  margin: 0 0 2px;
  text-align: center;
  font: 400 17px "Inter", sans-serif;
  color: var(--accent);
}
.comparison-col-pct-muted { color: var(--text-soft); }
.comparison-row {
  display: grid;
  /* A reasonable starting width, not a precise fit - quiz.js's
     shrinkCategoryLabelToFit auto-shrinks this column's own text (down to
     whatever it actually needs) rather than this value having to exactly
     match the longest possible line (e.g. "Career-High Market Value 💰",
     always combined onto one line now). Kept from growing too wide and
     eating into the 2 assignment columns' own share of the row. */
  grid-template-columns: 145px 1fr 1fr;
  align-items: end;
  gap: 6px;
  /* Positive and clearly bigger than .comparison-entry's own 1px internal
     gap (was -2px, a negative margin left over from the old 1-line layout -
     rows were literally packed CLOSER than an entry's own name-to-value
     spacing, the opposite of reading as separate groups; fixed 2026-09-12). */
  margin-bottom: 8px;
}
.comparison-row:last-child { margin-bottom: 0; }
/* Nudges the "Your Assignment"/"Optimal Assignment" clusters apart from
   each other (direct instruction, 2026-09-12) without touching the column
   TRACKS themselves (which would also move the category column/shift row
   widths) - asymmetric padding shrinks each column's own centered content
   box from one side only, pushing its centered text off-true-center toward
   the other side. nth-child(2)/(3) match both the header row's "col" divs
   (buildComparisonColTitle) and every data row's .comparison-entry - same 2
   positions in every row's 3-child structure. */
.comparison-row > *:nth-child(2) { padding-right: 16px; }
.comparison-row > *:nth-child(3) { padding-left: 4px; }
.comparison-category {
  /* Same size as .comparison-country/.comparison-rank below (10px, was 8px)
     and the same line-height (1.25, set on .comparison-entry now too) -
     direct instruction, 2026-09-12: every text size in this table matches
     the player name's own size, and the category cell's 2 lines land
     exactly level with the entry cells' 2 lines (was: a smaller category
     font needed its own translateY(-2px) nudge to fake-align against the
     larger name/rank text on a single shared line - obsolete now that both
     cells are genuinely the same size/line-height/2-line structure, removed
     rather than re-tuned). */
  font: 600 10px "Inter", sans-serif;
  letter-spacing: 0.02em;
  line-height: 1.25;
  color: var(--text);
}
/* Left-aligned via flex, not text-align (direct instruction, 2026-09-12:
   was right-aligned) - buildCategoryLabel's 2 lines are 2 separate flex
   children, so each needs its own edge pinned to the column's left edge
   independently (text-align only aligns text runs within a single line box,
   not sibling flex items). */
.comparison-category .category-label { align-items: flex-start; }
/* 2 lines now (direct instruction, 2026-09-12), not rank-squeezed-after-name
   on one line - flex column, same technique as .category-label. Same
   line-height as .comparison-category (1.25) so both cells' 2 lines land on
   the same vertical positions, not just the same font-size. */
.comparison-entry {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  line-height: 1.25;
}
.comparison-country {
  display: block;
  text-align: center;
  font: 700 10px "Inter", sans-serif;
  color: var(--text);
  white-space: nowrap;
}
.comparison-rank { font: 700 10px "Inter", sans-serif; color: var(--text-soft); white-space: nowrap; }
#rank-percentile + .leaderboard, #percentile-msg + .leaderboard { margin-top: 16px; }

@media (max-width: 480px) {
  .rank-pool { gap: 7px; margin-bottom: 18px; }
  .ccard { padding: 12px 5px; }
  .pair-stack { gap: 8px; }
  .pair-module { gap: 6px; }
  .pair-bucket { padding: 8px 8px 8px 13px; font-size: 11px; }
  .ccard-name { font-size: 11px; }
  .bucket-country-name { font-size: 11px; }
}
@media (max-width: 375px), (max-height: 700px) {
  .pair-bucket { padding: 7px 7px 7px 11px; }
}

/* ============================================================ WHO HAS MORE?
   "Scoreboard" duel screen (chosen 2026-09-11 from 3 mocked variants) - two
   stacked player cards with a circular VS badge pinned on their shared seam;
   on reveal the winning card fills solid (green) with its big number, the
   wrong-tapped card fills solid red, correct/wrong is otherwise unmarked.
   Reuses the same tokens/scale as Rank the Player throughout (--ccard
   equivalent, --accent/--good/--bad, Anton for numbers/headline). */
.round-pill {
  align-self: center;
  font: 700 10px "Inter", sans-serif;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  background: rgba(126, 198, 255, 0.1);
  border: 1px solid var(--accent-dim);
  border-radius: 100px;
  padding: 3px 10px;
  margin: 2px auto 10px;
  width: fit-content;
}
.metric-q {
  font: 400 26px/1.2 "Anton", sans-serif; /* matches #round-label's own size in every geoquiz game */
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--text);
  text-align: center;
  margin: 0 0 36px; /* 16px base + 20px to push the timer/cards cluster down (2026-09-11) */
}
.metric-q em { color: var(--accent); font-style: normal; }

.duel-stack { position: relative; display: flex; flex-direction: column; gap: 10px; margin-top: 16px; margin-bottom: 14px; }
.duel-card {
  font-family: inherit;
  color: var(--text);
  /* Explicit, not left to inherit - a bare <button> has no guaranteed
     inherited text color across browsers; desktop Chrome/Firefox happen to
     inherit dark text with no rule at all, but iOS Safari renders an
     unstyled <button>'s text in the OS accent/tint color (blue) instead,
     which is exactly what showed up as "player names are blue on phone,
     white on laptop" (2026-09-14) - .name/.num both already use
     `color: inherit`, so they only needed this one explicit declaration on
     their own button ancestor to stop depending on a browser default. */
  background: #1e1e1e;
  border: 1.5px solid var(--panel-edge);
  border-radius: 14px;
  padding: 18px 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Sized to already fit the REVEAL state's content (name + a 30px Anton
     number stacked) from the very start, so tapping a card doesn't grow it -
     both states share this exact floor and just center less/more content
     inside it via the same align-items/justify-content (2026-09-11). */
  min-height: 92px;
  width: 100%;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, opacity 0.12s ease;
}
@media (hover: hover) {
  .duel-card:not(:disabled):hover { border-color: var(--accent); }
}
/* Guessing-phase name is 2px larger than its reveal size, and the same
   element (not a recreated one) shrinks down on reveal - see reveal() in
   quiz.js, which only toggles .revealed rather than rebuilding these nodes,
   specifically so this can transition instead of jumping (2026-09-11). */
.duel-card .name {
  font: 700 18px "Inter", sans-serif;
  color: inherit;
  text-align: center;
  transition: font-size 0.25s ease;
}
.duel-card.revealed .name { font-size: 16px; }
.duel-card .revealrow { display: flex; flex-direction: column; align-items: center; gap: 2px; }
/* Starts collapsed - grows into view on reveal, which (since .revealrow
   sits centered inside .duel-card) is what actually produces the "name
   moves up" effect: the row gets taller and re-centers, no transform
   needed on .name itself. */
.duel-card .num {
  font: 400 30px "Anton", sans-serif;
  color: inherit;
  line-height: 1;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height 0.25s ease, opacity 0.2s ease 0.05s;
}
.duel-card.revealed .num { max-height: 40px; opacity: 1; }
.duel-card:disabled { cursor: default; opacity: 0.55; }
/* Same correct/wrong language as every other game's .opt-btn (geoquiz's maps
   quiz, style.css shared design system) - solid fill, not a border/tint, so
   it reads as one unambiguous signal: the winning card is always marked
   correct (whether or not it was the tap), and a wrong tap is marked wrong
   on the exact card tapped. color: inherit on .name/.num (not a hardcoded
   var(--text)) so both go dark together with the card's own fill-driven
   text color - a literal color on a child would otherwise stay light
   against a light green/red fill, unreadable (caught 2026-09-11). */
.duel-card.correct { background: var(--good); border-color: var(--good); color: #06210f; opacity: 1; }
.duel-card.wrong { background: var(--bad); border-color: var(--bad); color: #2a0509; opacity: 1; animation: shake 0.35s ease; }
/* Separate from correct/wrong - marks which card was actually tapped. Same
   highlight color as the hover state (var(--accent)) but visibly thicker,
   and placed after .correct/.wrong above so it wins the border-color
   regardless of which fill state also applies (2026-09-11). */
.duel-card.clicked { border-width: 3px; border-color: var(--accent); }
/* Pre-duel "3...2...1" gate (direct instruction, 2026-09-16 - replaces an
   earlier plain-text .btn-primary version from 2026-09-14) - takes
   #duel-stack's place (see quiz.js's scheduleRoundStart) for the very first
   duel of a session only, so the metric question above is already visible
   while the player gets a moment before the real 8s timer starts. Same
   solid-panel-plus-big-number treatment as geoquiz's own .gate-panel/
   .gate-number (flags/maps quiz.js) - height is fixed at #duel-stack's own
   194px footprint (2 cards + 10px gap) rather than that version's
   aspect-ratio, since this panel replaces a stacked pair of cards, not an
   image. Unlike the flag viewport, #duel-stack's height never changes across
   breakpoints, so .gate-number needs no mobile font-size override to still
   fit. margin-top/bottom match #duel-stack's own so this panel's edges land
   exactly where duel-stack's would - no button, the panel itself is the tap
   target to skip ahead (see scheduleRoundStart in quiz.js). */
.gate-panel {
  width: 100%;
  height: 194px;
  background: var(--bg);
  border-radius: 14px;
  margin-top: 16px;
  margin-bottom: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.gate-number {
  font: 400 120px "Anton", sans-serif;
  color: var(--accent);
  line-height: 1;
}

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-6px); }
  75% { transform: translateX(6px); }
}

.duel-vs {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--panel);
  border: 1.5px solid var(--panel-edge);
  display: flex;
  align-items: center;
  justify-content: center;
  font: 400 12px "Anton", sans-serif;
  color: var(--text-dim);
  z-index: 2;
  pointer-events: none;
}

/* Timer bar - same structure/behavior as geoquiz's maps quiz .timer-wrap
   (byte-identical shape, ported 2026-09-11: track + shrinking fill + a
   time label that travels with the fill's leading edge), sized to sit
   directly above the top duel card rather than above a map. */
/* .timer-wrap and .duel-status occupy the same slot (.duel-status-slot,
   below), only one visible at a time - their own natural heights differ
   wildly (a 10px track vs a 22px Anton line), so the FIXED height and
   vertical centering live on that shared outer slot, not on either of
   these two individually. Doing it on .timer-wrap itself instead would
   silently break #timer-num's calibrated "6px above the track" positioning
   (its bottom:100% is relative to .timer-wrap's own box, which a forced
   height here would inflate) - found while implementing, 2026-09-11. */
.timer-wrap { position: relative; margin: 0; width: 100%; }
.timer-track {
  width: 100%;
  height: 10px;
  border-radius: 6px;
  background: #050505;
  border: 1px solid var(--panel-edge);
  overflow: hidden;
}
.timer-fill {
  height: 100%;
  width: 100%;
  transform-origin: left;
  background: var(--accent);
  transition: background 0.3s linear; /* transform is driven frame-by-frame in JS, not transitioned */
}
#timer-num {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  transform: translateX(-50%);
  font: 700 13px "Inter", sans-serif;
  color: var(--accent);
  white-space: nowrap;
}
#timer-num::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  transform: translateX(-50%);
  width: 2px;
  height: 6px;
  background: currentColor;
}

/* Per-duel "Correct!" / "Wrong" / "Time's Up" - takes the timer bar's exact
   place once a duel resolves (same swap as maps: the reveal state replaces
   the ticking bar with a verdict, not a second element beside it). Same
   accent/bad color language as .result-title (not good/bad green/red - that
   pair is reserved for the card fills themselves). */
.duel-status {
  width: 100%;
  font: 400 32px "Anton", sans-serif; /* matches .result-title's own size exactly */
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-align: center;
  color: var(--good); /* matches the correct-card fill color, not accent (2026-09-11) */
  margin: 0;
}
.duel-status.wrong { color: var(--bad); }

@media (max-width: 480px) {
  .duel-status { font-size: 24px; } /* matches .result-title's own mobile size */
}

/* flex-end, not center (2026-09-11 fix) - centering left extra empty space
   BELOW the (shorter) timer bar before .duel-stack's own 16px margin-top
   kicked in, breaking the exact bar-to-card gap the flags quiz uses.
   Bottom-aligning both states keeps that gap exactly 16px either way, and
   still reserves the same total height so cards never shift on swap. */
/* 40px, not 30px (2026-09-11) - .duel-status grew to match .result-title's
   32px size, which no longer fits the old 30px slot. flex-end still pins
   whichever is showing to the slot's bottom edge, so the exact 16px
   bar-to-card gap holds regardless of the slot's total height. */
.duel-status-slot { height: 40px; display: flex; align-items: flex-end; }

/* End-of-run recap (2026-09-11) - same idea as every geoquiz game's own
   end-of-run recap strip (a row of small per-round results shown above the
   score), adapted from a horizontal row of tiny flag/map thumbnails to a
   side-by-side "Name Value" pair per duel, since a duel's content (two
   player names + real values) doesn't compress into a small icon the way a
   flag does. Only the card actually tapped gets colored, and only its
   border (green if that tap was correct, red if it wasn't) - name/value
   text always stays grey, never filled (direct instruction, 2026-09-13). */
.duel-recap {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 18px;
  max-height: 260px;
  overflow-y: auto;
}
/* Side-by-side pair, one duel per line - same shape as the live .duel-stack
   cards (2026-09-13, replacing an earlier stacked-full-width layout). The 8px
   gap is within one duel's own pair; .duel-recap's own larger 6px gap is what
   separates one duel from the next. */
.recap-row { display: flex; flex-direction: row; gap: 8px; }
/* "Name (value)" as one centered line (2026-09-13, replacing an earlier
   name-dot-value split-alignment layout) - shrinks the card's height down to
   one text line of padding instead of two. Neither child has flex:1 (unlike
   the split layout) so the pair sizes to its own content and sits centered
   as a group; .recap-name still shrinks/ellipses first if the pair together
   is wider than the card, via flex's default shrink + min-width:0. */
.recap-entry {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 3px;
  padding: 5px 8px;
  border-radius: 8px;
  background: #1e1e1e;
  border: 1px solid var(--panel-edge);
  color: var(--text-dim);
}
/* Only the card actually tapped gets colored, and only its border - text
   (name/value) stays grey either way (direct instruction, 2026-09-13: color
   is reserved for the outline, not the text). Green when that tap was
   correct, red when it wasn't; a timed-out duel (no tap) leaves both cards
   in the plain untinted border above. */
.recap-entry.correct { border-color: var(--good); }
.recap-entry.wrong { border-color: var(--bad); }
.recap-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 700 11px "Inter", sans-serif;
}
.recap-value { flex-shrink: 0; font: 400 12px "Anton", sans-serif; }

@media (max-width: 480px) {
  .metric-q { font-size: 27px; } /* matches #round-label's own mobile bump in every geoquiz game */
}

/* ============================================================ GUESS THE TEAM
   Reveal a club's ex-players one at a time (fewest games for that club
   first), guess the club via a text search + autocomplete dropdown. Text
   only, no club logos/badges anywhere (they're protected - direct
   instruction) - identity during play is deliberately withheld entirely, and
   only shown on the result screen via name + that club's own accent color
   (set inline per-episode by quiz.js, never hardcoded here).
   Score is INVERTED vs the other two games (lower slot position = better),
   but the visual language (card shapes, Anton numerals, accent/good/bad
   tokens) is the same shared system throughout. */
.gt-goal {
  font: 700 19px "Inter", sans-serif;
  line-height: 1.35;
  color: var(--text-dim);
  text-align: center;
  margin: 12px 0 18px;
}
.gt-goal em { color: var(--accent); font-style: normal; }

/* The "club card" - only used for the REVEALED club chip on the result
   screen now (.gt-club-reveal below) - filled solid in that club's own
   color. The live guessing controls (search input + Guess/Skip) used to sit
   in a matching boxed card above the player list too, but that extra card
   was removed (direct instruction, 2026-09-12: "dont place them in another
   card") - they now sit directly on the page, kept compact instead. */
.gt-club-card {
  background: #1e1e1e;
  border: 1.5px solid var(--panel-edge);
  border-radius: 14px;
  padding: 22px 16px;
  margin-bottom: 14px;
}

/* Search input full-width on its own row, Guess + Next Player in a row
   below it (direct instruction, 2026-09-13 - was a single 50/25/25 row
   before). */
.gt-search { position: relative; margin-bottom: 10px; }
.gt-search-input {
  width: 100%;
  /* 16px, not smaller - below that, iOS Safari auto-zooms the whole page in
     on focus with no reliable way to zoom back out again afterward (same
     issue + fix as geoquiz's own .lb-name-input) - found live on a real
     phone, 2026-09-13. Full-width now anyway, so 16px costs nothing here. */
  font: 400 16px "Inter", sans-serif;
  color: var(--text);
  /* Visibly darker than the page's own card panel (#1e1e1e) - direct
     instruction, 2026-09-12 - so the input reads as a distinct control. */
  background: var(--bg);
  border: 1.5px solid var(--panel-edge);
  border-radius: 10px;
  padding: 0 14px;
  height: 48px;
  box-sizing: border-box;
}
.gt-search-input:focus { outline: none; border-color: var(--accent); }
.gt-suggestions {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 5;
  list-style: none;
  margin: 0;
  padding: 4px;
  max-height: 220px;
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 12px;
  box-shadow: 0 12px 30px -10px rgba(0, 0, 0, 0.7);
}
.gt-suggestion {
  padding: 9px 10px;
  border-radius: 8px;
  font: 400 14px "Inter", sans-serif;
  color: var(--text-soft);
  cursor: pointer;
}
.gt-suggestion.active { background: rgba(126, 198, 255, 0.12); color: var(--text); }

.gt-club-buttons { display: flex; gap: 10px; margin-bottom: 14px; }
.gt-club-buttons .btn-primary,
.gt-club-buttons .btn-secondary {
  flex: 1;
  width: auto;
  margin-bottom: 0;
  padding: 12px 14px;
  font-size: 15px;
}
.gt-guess-btn:disabled { background: #2a2a2a; color: var(--text-dim); cursor: default; }

.gt-history {
  list-style: none;
  margin: 0 0 6px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Individually-bordered single-line cards. Padding trimmed back down a bit
   (direct instruction, 2026-09-12) while keeping the text sizes as they were
   - only the row's own height/breathing room shrinks. */
.gt-history-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 12px;
  background: #1e1e1e;
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
}
/* Every revealed/interactive state uses the SAME accent/highlight border
   color now (direct instruction, 2026-09-12: "borders always highlight
   color") - wrong/won/active/missed no longer get their own red/green/blue
   distinction via color; only the missed row's DOTTED style (below) still
   sets it apart from a normal solid-border row. */
.gt-history-row.wrong { border-color: var(--accent); }
.gt-history-row.won { border-color: var(--accent); }
/* The currently-revealed, not-yet-resolved slot - awaiting a guess/skip,
   rendered inline in the same list rather than in a separate big card
   (2026-09-12). */
.gt-history-row.active { border-color: var(--accent); background: rgba(126, 198, 255, 0.08); }
/* Every one of the 10 slots is visible from the start of the round (direct
   instruction, 2026-09-12) - a slot not yet reached shows a muted "…"
   placeholder instead of disappearing, so the full-length list never
   reflows/jumps in height as the round progresses. */
.gt-history-row.upcoming { opacity: 0.5; }
.gt-history-row.upcoming .gt-history-name { color: var(--text-dim); font-weight: 400; }
.gt-history-pos {
  flex: 0 0 auto;
  width: 20px;
  text-align: center;
  font: 700 11px "Inter", sans-serif;
  color: var(--text-dim);
}
.gt-history-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 700 15px "Inter", sans-serif;
  color: var(--text);
}

/* Guess the Player only - a club's tenure years sits as a smaller, dimmer
   second line directly under its name, rather than competing with games/
   goals for horizontal room in an already-tight row (mobile width was the
   deciding factor for every other gt-history tuning pass on this
   component). .gt-history-main wraps BOTH lines as a flex:1 column; the
   nested .gt-history-name needs flex:none here (not the flex:1 it has as
   Guess the Club's own standalone row child above) so it sizes to its own
   content height instead of stretching to fill the column's cross axis. */
.gt-history-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.gt-history-main .gt-history-name { flex: none; }
.gt-history-years {
  font: 400 12px "Inter", sans-serif;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The player's still-ongoing stint ("since 2025") reads as a highlighted
   fact, not just another line of metadata - accent-colored + bold, same
   language the rest of the site already uses for "this is the live thing"
   (direct instruction, 2026-09-14). */
.gt-history-row.current .gt-history-years { color: var(--accent); font-weight: 700; }

/* Two stat groups (games/goals), each a big Anton number + a small dim
   label, side by side with a gap between them instead of a plain comma-
   separated text line - direct instruction, 2026-09-12: "make the display of
   games and goals a bit nicer, no comma, maybe use the other font". */
.gt-history-stats { flex: 0 0 auto; display: flex; gap: 12px; }
.gt-history-stat { display: inline-flex; align-items: baseline; gap: 3px; white-space: nowrap; }
/* Fixed-width, right-aligned - "GAMES"/"GOALS" is the same text every row, so
   once the NUMBER before it stops varying in width (1 digit vs 3 digits),
   the whole stat block is the same width every row, and both labels land at
   the exact same X position on every card (direct instruction, 2026-09-12).
   min-width covers up to 3 digits at this font size. */
.gt-history-stat-num { font: 400 17px "Anton", sans-serif; color: var(--text); min-width: 27px; text-align: right; }
.gt-history-stat-label {
  font: 700 9px "Inter", sans-serif;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
}
/* The auto-burned "invisible" slot after a wrong guess - never actually
   revealed to the player, shown only as a locked placeholder in the history
   list. Dotted + accent-colored border (direct instruction, 2026-09-12) -
   the dotted style is what sets it apart from every other (solid-border)
   row now that all border colors match. */
.gt-history-row.missed {
  border-style: dotted;
  border-color: var(--accent);
  background: transparent;
  color: var(--text-dim);
}
.gt-history-row.missed .gt-history-name { color: var(--text-dim); font-weight: 700; flex: 1; }
.gt-history-lock { flex: 0 0 auto; font-size: 14px; }

/* Result screen - the answer, revealed as a .gt-club-card (see above) filled
   solid in that club's own color (set inline per-episode by quiz.js, both
   background AND text color - many real club colors are very dark or very
   saturated (Juventus
   black, Chelsea navy, Inter Milan navy...), so using one as a plain
   foreground text color on this page's own dark background is illegible for
   most of the 54 eligible clubs, not just an edge case (found via local
   Playwright test, 2026-09-12: Juventus's reveal text was black-on-near-
   black). A solid fill with its OWN contrasting text (computed from that
   color's luminance, see quiz.js's pickTextColorFor) is legible regardless of
   how dark/light the specific club color is. */
.gt-club-reveal {
  text-align: center;
  font: 400 28px "Anton", sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.2;
}
/* Guess the Player's own reveal chip - players don't have an inherent
   accent color the way clubs do, so this is a fixed accent fill (no
   per-episode inline JS color like Guess the Club's own .gt-club-reveal
   needs) rather than computing readable text per-color at runtime. */
#gt-player-reveal { background: var(--accent); color: #0a0f0c; }

@media (max-width: 480px) {
  .gt-club-card { padding: 16px 12px; }
  .gt-search-input { height: 44px; }
  .gt-club-buttons { gap: 6px; }
  .gt-club-buttons .btn-primary,
  .gt-club-buttons .btn-secondary { padding: 10px 8px; font-size: 14px; }
  .gt-club-reveal { font-size: 22px; }
  .gt-history-row { padding: 6px 10px; }
  .gt-history-name { font-size: 13px; }
  .gt-history-years { font-size: 11px; }
  .gt-history-stat-num { font-size: 15px; min-width: 24px; }
  .gt-history-stats { gap: 8px; }
}

/* --- Order the Player (2026-09-19) --------------------------------------
   Ported from the geography site's own Order the Country (its sibling
   design, right down to the class names - .arrange-* everywhere - kept
   as-is rather than renamed, same as this game's own API endpoints/
   localStorage keys use "order" while the underlying data/scoring is
   still literally the arrange-style footrule-distance mechanic under a
   new name). Two real differences from the geography original: no flag
   icons (players don't have one - .arrange-card here is just a centered
   name, no .arrange-flag rule needed at all), and no highestPointM-style
   secondary "peak" line in the reveal value column (every metric here is
   a single plain number/currency figure, so .reveal-value-peak never
   gets used - .reveal-value-elev alone carries the whole value text). */
.arrange-metric-label {
  font: 700 19px "Inter", sans-serif;
  color: var(--text-dim);
  text-align: center;
  margin: 4px 0 14px;
}
.arrange-metric-label em { color: var(--accent); font-style: normal; }
.arrange-endpoint-label {
  font: 400 12px "Inter", sans-serif;
  color: var(--text-dim);
  text-align: center;
  margin: 0;
}
.arrange-endpoint-label.top { margin-bottom: 4px; }
.arrange-endpoint-label.bottom { margin-top: 4px; margin-bottom: 18px; }

.arrange-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.arrange-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
  position: relative;
  transition: transform 0.15s ease;
}
.arrange-card {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #1e1e1e;
  border: 1.5px solid var(--panel-edge);
  border-radius: 14px;
  padding: 10px;
  cursor: grab;
  touch-action: none;
}
.arrange-row.dragging .arrange-card {
  cursor: grabbing;
  border-color: var(--accent);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
}
.arrange-name {
  font: 700 18px "Inter", sans-serif;
  line-height: 22px;
  color: var(--text);
  text-align: center;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
}
.arrange-arrow {
  width: 22.5px;
  border: 1px solid var(--panel-edge);
  border-radius: 12px;
  background: #1e1e1e;
  color: var(--text);
  font-size: 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.arrange-arrow:disabled { opacity: 0.25; cursor: default; }
@media (hover: hover) {
  .arrange-arrow:not(:disabled):hover { border-color: var(--accent); }
}
.arrange-arrow:not(:disabled):active { border-color: var(--accent); }

/* --- reveal: your order vs. correct order, plus the value ------------- */
.reveal-grid {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  column-gap: 8px;
  row-gap: 6px;
}
.reveal-head {
  text-align: left;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--panel-edge);
  margin-bottom: 4px;
}
.reveal-head-pct {
  margin: 0 0 2px;
  font: 400 15px "Inter", sans-serif;
  color: var(--accent);
}
.reveal-head-pct.muted { color: var(--text-soft); }
.reveal-head-title {
  margin: 0;
  font: 700 9px "Inter", sans-serif;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.reveal-head-value {
  margin: 0;
  font: 700 9px "Inter", sans-serif;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-dim);
  align-self: end;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--panel-edge);
  margin-bottom: 4px;
  text-align: right;
}

.reveal-row {
  display: flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
}
.reveal-rank { font: 700 10px "Inter", sans-serif; color: var(--text-dim); flex-shrink: 0; }
.reveal-name {
  font: 600 10px "Inter", sans-serif;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.reveal-value {
  text-align: right;
  white-space: nowrap;
  font-size: 11px;
  transform: translateY(-1px);
}
.reveal-value-elev {
  font: 700 11px "Inter", sans-serif;
  color: var(--text-dim);
  margin-left: 5px;
}

@media (max-width: 375px), (max-height: 700px) {
  #game:has(.arrange-list) { padding-left: 10px; padding-right: 10px; }
}

/* --- Build the Team (2026-09-19) ---------------------------------------
   Real design pass, ported from a Claude Design Canvas mockup built and
   iterated with the site owner directly (many rounds of pixel-level
   feedback - see [[buildtheteam-format-design]]). Two pieces: the picking
   screen (goal statement showing the real target number up front, one
   round's 4 candidates as 2-line cards, a football pitch showing all of
   that formation's slots at once - filled/current/upcoming) and the reveal
   screen (ported near-verbatim from Rank the Player's own result panel -
   score/percentile/leaderboard/share+keep-playing - with a simplified
   2-column Your Team / Perfect Team comparison below the divider, no
   metric-name column since an episode only ever has one metric). */
.bt-goal {
  font: 700 19px "Inter", sans-serif;
  line-height: 1.35;
  color: var(--text-dim);
  text-align: center;
  margin: 10px 0 20px;
}
.bt-goal #bt-goal-target { color: var(--accent); }
.bt-round-position {
  font: 700 15px "Inter", sans-serif;
  color: var(--text);
  text-align: center;
  margin: 8px 0 14px;
}
.bt-candidates {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 8px;
}
.bt-candidate {
  height: 64px;
  box-sizing: border-box;
  font: 700 15px "Inter", sans-serif;
  color: var(--text);
  background: #1e1e1e;
  border: 1.5px solid var(--panel-edge);
  border-radius: 14px;
  padding: 4px 8px;
  cursor: pointer;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
}
@media (hover: hover) {
  .bt-candidate:not(:disabled):hover { border-color: var(--accent); }
}
.bt-candidate:disabled { opacity: 0.5; cursor: default; }
.bt-candidate-line {
  line-height: 1.2;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- pitch (picking screen) --------------------------------------------
   Field rotated 90 degrees (2026-09-19 design pass): goal-to-goal now runs
   left-to-right instead of top-to-bottom, and the field is shorter overall
   (height is 5:4 of width instead of the old portrait 3:4). aspect-ratio
   alone (no flex-grow - .card/.play-area aren't a fixed-height flex chain
   on this site, so flex-grow here would have nothing to grow into) - width
   comes from the card's own content width. It also runs 4px past the
   card's own padding on each side (a deliberate slight overhang, not a
   bug) via the negative margins below. */
.bt-pitch {
  position: relative;
  width: calc(100% + 8px);
  margin-left: -4px;
  margin-right: -4px;
  aspect-ratio: 5 / 4;
  container-type: inline-size; /* lets the marker text below size itself in cqw (see .bt-pitch-marker) */
}
.bt-pitch-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.bt-pitch-markers {
  position: absolute;
  inset: 0;
}
/* Marker cards SCALE WITH THE PITCH (2026-09-19, real-device report: "the
   bigger the screen, the bigger the field, but the cards stay the same size
   and overlap"). The design was tuned on a 390px-wide phone where the pitch
   renders 336 x 268.8px with 91.8 x 48px cards, so every dimension below is
   that px value as a fraction of the reference pitch: the box in plain
   percentages of the pitch (its containing block), the text in cqw
   (container-query units, .bt-pitch is the container - the px fonts before
   the @supports block are the fallback for browsers without them). left/top
   is the marker's CENTER (set by buildPitchLayout, also in percent), hence
   the translate. Small floor on the fonts so 320px phones stay legible. */
.bt-pitch-marker {
  position: absolute;
  width: 27.32%;   /* 91.8 / 336 */
  height: 17.86%;  /* 48 / 268.8 */
  transform: translate(-50%, -50%);
  cursor: pointer;
}
@media (hover: hover) {
  .bt-pitch-marker:hover .bt-pitch-card { border-color: var(--accent); }
}
.bt-pitch-value {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 1px;
  font: 700 11px "Inter", sans-serif;
  color: var(--accent);
  white-space: nowrap;
}
.bt-pitch-card {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  background: #1e1e1e;
  border: 1.5px solid var(--panel-edge);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 2px 4px;
}
@supports (font-size: 1cqw) {
  .bt-pitch-value { font-size: max(9.5px, 3.274cqw); } /* 11 / 336 */
  .bt-pitch-card { border-radius: 2.976cqw; padding: 0.595cqw 1.19cqw; gap: 0.893cqw; } /* 10 / 336, 2 / 336, 4 / 336, 3 / 336 */
  .bt-pitch-card-name { font-size: max(11px, 3.571cqw); } /* 12 / 336 */
}
.bt-pitch-card.current { background: var(--accent); border-color: var(--accent); }
.bt-pitch-card.filled { background: rgba(126, 198, 255, 0.6); border-color: var(--accent); }
.bt-pitch-card-name {
  font: 700 12px "Inter", sans-serif;
  color: var(--text-dim);
  text-align: center;
  line-height: 1.15;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bt-pitch-card.current .bt-pitch-card-name { color: #0f0f0f; }
.bt-pitch-card.filled .bt-pitch-card-name { color: #0f0f0f; }

/* --- reveal: Your Team / Perfect Team comparison ------------------------ */
.bt-comparison-caption {
  font: 700 15px "Inter", sans-serif;
  line-height: 1.35;
  color: var(--text-dim);
  text-align: center;
  margin: 0 0 10px;
}
.bt-comparison-list { display: flex; flex-direction: column; }
.bt-comparison-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: end;
  margin-bottom: 8px;
}
.bt-comparison-header { margin-bottom: 10.7px; }
.bt-comparison-col-left { padding-right: 16px; }
.bt-comparison-col-right { padding-left: 4px; }
.bt-comparison-col-pct {
  margin: 0 0 2px;
  min-width: 0;
  text-align: center;
  font: 400 17px "Inter", sans-serif;
  color: var(--accent);
}
.bt-comparison-col-pct.muted { color: var(--text-soft); }
.bt-comparison-col-title {
  margin: 0;
  min-width: 0;
  text-align: center;
  font: 700 9px "Inter", sans-serif;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text);
}
.bt-comparison-entry {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  line-height: 1.25;
}
.bt-comparison-name {
  display: block;
  text-align: center;
  font: 700 10px "Inter", sans-serif;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.bt-comparison-entry.wrong .bt-comparison-name { color: #ff8a8a; }
.bt-comparison-value { font: 700 10px "Inter", sans-serif; color: var(--text-soft); white-space: nowrap; }
