/* Indexle — vintage atlas theme. Sepia paper, ink + faded teal accents,
   hairline double-rules, tracked-out Fraunces caps. */

:root {
  /* White, academic. No tint, no gradient — flat sheet. */
  --bg: #ffffff;
  --paper: #ffffff;
  --paper-shade: #f1f1ee;
  --paper-card: rgba(255, 255, 255, 0.85);
  --paper-card-shade: rgba(241, 241, 238, 0.85);
  --ink: #1c1c1c;
  --ink-soft: #4a4a48;
  --ink-faint: #8a8a87;
  --rule: #d2d1cc;
  --rule-soft: #e1e0db;
  --accent: #3d6e76;        /* faded teal — target */
  --warm: #b08948;           /* muted ochre — close */
  --calm: #6e7c8a;           /* slate */
  --match: #3a8043;          /* muted green — categorical match */
  --match-soft: rgba(58, 128, 67, 0.14);
  --loss: #a8493d;           /* muted terracotta — loss messaging */
  --serif: 'Fraunces', 'Iowan Old Style', 'Charter', Georgia, serif;
  --sans: 'Inter', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --maxw: 640px;
  /* Multiplier applied to every duration + delay in the radar entrance.
     Raise to slow down, lower to speed up. 1 = the original baseline. */
  --radar-anim-scale: 2;
}

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

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* Bottom padding clears the fixed keypad bar so scrolling content
   doesn't end up hidden behind it. Tuned for the 3-row keypad + display.
   On game end the keypad is removed (--gone) so the padding collapses too. */
body { padding-bottom: 210px; }
body:has(.keypad-bar--gone) { padding-bottom: 24px; }

/* Ambient world-map backdrop. Two side-by-side equirectangular world maps
   drift left over ~4 minutes for an imperceptible pan; the loop is seamless
   because the second copy is identical. Sits behind everything via z-index 0
   + #app's z-index:1 above. Cards keep solid paper backgrounds so the map
   only shows through gutters, gaps, and around edges. */
.world-backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  opacity: 0.09;
  background: var(--accent);
}
.world-backdrop__scroller {
  display: flex;
  width: 200%;
  height: 100%;
  animation: world-pan 180s linear infinite;
}
.world-backdrop__map {
  width: 50%;
  height: 100%;
  flex: 0 0 auto;
  display: block;
}
.world-backdrop__land path {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.4;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
@keyframes world-pan {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .world-backdrop__scroller { animation: none; }
}

/* Paper grain — a single tiled SVG feTurbulence rendered over the entire
   viewport at low opacity. Adds tactile "tooth" to every surface (paper,
   cards, keypad) without per-element work. Multiply blend so it darkens
   the paper subtly where the noise lands; pointer-events:none so it never
   intercepts taps. Tile is small enough that the eye reads it as grain,
   not pattern. */
body::before { display: none; }

button { font-family: inherit; }

#app {
  display: grid;
  grid-template-columns: 1fr min(var(--maxw), 100%) 1fr;
  grid-template-areas:
    "head head head"
    ".    main side"
    "foot foot foot";
  min-height: 100vh;
  /* Sit above the .world-backdrop fixed layer (z-index: 0). */
  position: relative;
  z-index: 1;
}
.masthead { grid-area: head; }
#view { grid-area: main; padding: 24px 20px 60px; }
.ad-slot--side { grid-area: side; }
.footer { grid-area: foot; }

.nav {
  /* Three equal-weight grid columns guarantee the wordmark is exactly
     centered regardless of how wide the left/right button groups are. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 4px 16px 8px;
  max-width: var(--maxw);
  margin: 0 auto;
  width: 100%;
  position: relative;
}
.nav { grid-template-columns: 1fr 1fr; }
.nav > #rules-btn { justify-self: start; }
.nav > .nav__right { justify-self: end; }
.nav::after { display: none; }
.nav__title {
  margin: 0;
  padding: 14px 20px 2px;
  max-width: var(--maxw);
  margin-left: auto;
  margin-right: auto;
  width: 100%;
  text-align: center;
  line-height: 0;
}
.nav__title-logo {
  display: inline-block;
  height: 96px;
  width: auto;
  max-width: 100%;
}
.nav__btn {
  background: none;
  border: 1px solid transparent;
  color: var(--ink-soft);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 2px 8px;
  cursor: pointer;
  border-radius: 3px;
  font-family: var(--sans);
  font-weight: 500;
}
.nav__btn:hover { color: var(--ink); }
.nav__right { display: flex; align-items: center; gap: 6px; }
/* The Reset button is the production control on desktop; on mobile it
   swaps for the Random button (since the floating dev panel is hidden). */
.nav__btn--mobile { display: none; }
@media (max-width: 700px) {
  .nav__btn--desktop { display: none; }
  .nav__btn--mobile  { display: inline-flex; }
}
.nav__dev {
  font-family: var(--sans);
  font-size: 11px;
  color: var(--ink-faint);
  background: var(--paper);
  border: 1px dashed var(--rule);
  border-radius: 4px;
  padding: 4px 6px;
  cursor: pointer;
  text-transform: lowercase;
  letter-spacing: 0.04em;
}
.nav__dev:hover { color: var(--ink); border-color: var(--ink-faint); }

/* Floating dev panel — kept out of the masthead so the production header
   reads cleanly. Pinned to the top-left under the top ad slot. Remove the
   #dev-panel element before launch. */
.dev-panel {
  position: fixed;
  top: 84px;
  left: 12px;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  background: rgba(255, 255, 255, 0.92);
  border: 1px dashed var(--rule);
  border-radius: 4px;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 0.7;
  transition: opacity 140ms ease;
}
.dev-panel:hover { opacity: 1; }
.dev-panel__label {
  font-family: var(--sans);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding-right: 4px;
  border-right: 1px solid var(--rule);
}
.dev-panel .nav__btn { font-size: 10px; padding: 4px 6px; letter-spacing: 0.04em; }
@media (max-width: 700px) {
  /* Hide on mobile — the keypad bar owns the bottom and overlays at the
     top fight with the radar/ad. Dev work happens on desktop. */
  .dev-panel { display: none; }
}

/* Dev-only flash: when picking a random country we briefly show the answer
   in the middle of the screen so the dev knows what to type. The element
   self-destructs after location.reload() fires. */
.dev-flash {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  z-index: 200;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  pointer-events: none;
  animation: dev-flash-show 1200ms ease forwards;
}
.dev-flash__flag { font-size: 64px; line-height: 1; }
.dev-flash__name {
  font-family: var(--serif);
  font-size: 36px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -0.01em;
  text-align: center;
  padding: 0 24px;
}
@keyframes dev-flash-show {
  0%   { opacity: 0; }
  12%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { opacity: 0; }
}

.footer {
  padding: 22px 20px 28px;
  text-align: center;
  font-size: 12px;
  color: var(--ink-faint);
  font-family: var(--serif);
  font-style: italic;
  font-variant-numeric: tabular-nums;
  position: relative;
}
.footer::before {
  content: '';
  display: block;
  height: 3px;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin: 0 auto 16px;
  max-width: var(--maxw);
}
.footer::after {
  content: '⁂';
  display: block;
  color: var(--rule);
  font-size: 18px;
  letter-spacing: 0.4em;
  margin-top: 8px;
}
.footer__sep { margin: 0 8px; color: var(--rule); }
.footer__source em { font-style: normal; color: var(--ink-faint); opacity: 0.7; }
.footer__link { color: var(--ink-faint); text-decoration: none; }
.footer__link:hover { color: var(--ink); }
.footer__edition {
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-style: normal;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  color: var(--ink-soft);
}

/* ad slots */
.ad-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  background: repeating-linear-gradient(
    45deg,
    var(--paper-shade),
    var(--paper-shade) 6px,
    var(--bg) 6px,
    var(--bg) 12px
  );
  border: 1px dashed var(--rule);
  border-radius: 3px;
  color: var(--ink-faint);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-family: var(--serif);
  font-style: italic;
}
.ad-slot__label { padding: 6px 10px; }

/* sample ad creatives — replace with real AdSense markup at launch.
   These exist so the layout shows the slot occupied; they are static
   pseudo-classifieds styled to fit the atlas theme. */
.ad-slot:has(.ad-sample) {
  background: #f5efe2;
  border-style: solid;
  border-color: #d9c9a8;
  position: relative;
  text-transform: none;
  letter-spacing: 0;
  font-style: normal;
  padding: 0;
}
.ad-slot:has(.ad-sample) .ad-slot__label {
  position: absolute;
  top: 4px;
  right: 8px;
  font-size: 8px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink-faint);
  padding: 0;
  pointer-events: none;
}
.ad-sample {
  display: flex;
  width: 100%;
  height: 100%;
  text-decoration: none;
  color: var(--ink);
  cursor: default;
}

/* top leaderboard */
.ad-sample--top {
  align-items: center;
  gap: 14px;
  padding: 10px 18px;
  font-family: var(--serif);
}
.ad-sample__mark {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border: 1px solid var(--ink);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--serif);
  font-weight: 600;
  font-size: 20px;
  color: var(--ink);
  background: #ede4d0;
}
.ad-sample__body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  min-width: 0;
}
.ad-sample__title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.ad-sample__sub {
  font-size: 12px;
  color: var(--ink-soft);
  font-style: italic;
}
.ad-sample__cta {
  flex: 0 0 auto;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent);
  padding: 6px 10px;
  border-radius: 2px;
}

/* side skyscraper */
.ad-sample--side {
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 22px 14px 16px;
  gap: 10px;
  font-family: var(--serif);
}
.ad-sample__eyebrow {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-style: italic;
}
.ad-sample__headline {
  font-size: 18px;
  line-height: 1.2;
  font-weight: 600;
  color: var(--ink);
}
.ad-sample__rule {
  width: 32px;
  height: 1px;
  background: var(--rule);
  margin: 4px 0;
}
.ad-sample__pitch {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-soft);
  font-style: italic;
}
.ad-sample__price {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  margin-top: auto;
}
.ad-sample__cta--block {
  display: block;
  width: 100%;
  padding: 8px 0;
  text-align: center;
  border: 1px solid var(--accent);
  color: var(--accent);
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-radius: 2px;
}
.ad-sample__foot {
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-style: italic;
}

/* mobile-first ad rendering — phones first, desktop later.
   Top banner: stack vertically when narrow, hide the round mark.
   Side rail (becomes a horizontal banner under 880px): two-line layout
   with headline + CTA, hide pitch/eyebrow/rule/foot/price — those don't
   fit at 375px portrait. */
@media (max-width: 880px) {
  .ad-slot--top { height: auto; min-height: 64px; }
  .ad-sample--top {
    padding: 10px 14px;
    gap: 10px;
  }
  .ad-sample--top .ad-sample__mark { display: none; }
  .ad-sample--top .ad-sample__title { font-size: 13px; }
  .ad-sample--top .ad-sample__sub { font-size: 11px; }
  .ad-sample--top .ad-sample__cta {
    font-size: 10px;
    padding: 5px 8px;
    white-space: nowrap;
  }

  .ad-slot--side { height: auto; min-height: 72px; }
  .ad-sample--side {
    flex-direction: row;
    text-align: left;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 14px;
  }
  .ad-sample--side .ad-sample__eyebrow,
  .ad-sample--side .ad-sample__rule,
  .ad-sample--side .ad-sample__pitch,
  .ad-sample--side .ad-sample__price,
  .ad-sample--side .ad-sample__foot {
    display: none;
  }
  .ad-sample--side .ad-sample__headline {
    font-size: 14px;
    line-height: 1.25;
    flex: 1 1 auto;
    min-width: 0;
  }
  .ad-sample__cta--block {
    display: inline-block;
    width: auto;
    padding: 6px 10px;
    font-size: 10px;
    white-space: nowrap;
  }
}

/* very narrow phones — drop the subtitle on top banner so title + CTA fit */
@media (max-width: 420px) {
  .ad-sample--top .ad-sample__sub { display: none; }
  .ad-sample--top .ad-sample__title { font-size: 12px; }
}
.ad-slot--top {
  max-width: var(--maxw);
  margin: 12px auto 0;
  height: 72px;
}
.ad-slot--side {
  align-self: start;
  margin: 24px 20px 0 12px;
  width: 160px;
  height: 600px;
  position: sticky;
  top: 24px;
}
@media (max-width: 880px) {
  #app {
    grid-template-columns: 1fr;
    grid-template-areas:
      "head"
      "main"
      "side"
      "foot";
  }
  .ad-slot--side {
    width: auto;
    height: 100px;
    position: static;
    margin: 0 20px 24px;
  }
}

/* board — one card that holds everything: radar, strip, form, share. */
.board {
  display: grid;
  grid-template-columns: 1fr;
  margin: 8px 0 18px;
}
.board__radar, .board__globe {
  background: var(--paper);
  border: 0;
  border-radius: 0;
  padding: 12px 0 14px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  box-shadow: none;
  position: relative;
  overflow: hidden;
}
/* Vignette fades the map to white at the edges */
.board__radar::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  background:
    linear-gradient(to right, rgba(255,255,255,0.97) 0%, transparent 12%, transparent 88%, rgba(255,255,255,0.97) 100%),
    linear-gradient(to bottom, rgba(255,255,255,0.97) 0%, transparent 12%, transparent 88%, rgba(255,255,255,0.97) 100%);
}
/* Radar content floats above the backdrop and vignette */
.board__radar > :not(.world-backdrop) {
  position: relative;
  z-index: 2;
}
#radar, #globe {
  width: 100%;
  height: auto;
  max-height: 520px;
  display: block;
  align-self: center;
}
/* Mobile: let the radar grow to fill the screen. No height cap, no inner
   padding eating into the SVG's drawable area. */
@media (max-width: 600px) {
  #radar { max-height: none; }
}

/* Guess cards — each guess unfurls with axis pips + 7 category chips.
   Latest card gets the staggered flip-reveal animation. */
.guesses {
  margin: 0 0 20px;
  display: flex;
  flex-direction: column;
  /* Hairline dividers do the work; no gap between rows. */
  gap: 0;
}
.guess-row {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "name"
    "cards";
  row-gap: 10px;
  padding: 14px 0;
  background: transparent;
  border: 0;
  /* Soft rule between each guess; first row sits flush with the radar's
     bottom rule so the page reads as one continuous column. */
  border-top: 1px solid var(--rule-soft);
  position: relative;
}
.guess-row:first-child { border-top: 0; }
.guess-row::before { display: none; }
.guess-row__name {
  grid-area: name;
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 8px;
}
.guess-row__flag { font-size: 20px; line-height: 1; }
.guess-row__cards {
  grid-area: cards;
  /* 3 equal columns × 2 rows for the 6 categorical tiles — guarantees the
     stack never exceeds 2 rows regardless of value length. */
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}
.cat-card {
  background: var(--paper-card-shade);
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 6px 8px 7px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  text-align: center;
  min-width: 0;
}
.cat-card--match {
  background: var(--match-soft);
  border-color: var(--match);
}
.cat-card--match .cat-card__val { color: var(--match); font-weight: 600; }
.cat-card__head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  color: var(--ink-faint);
}
.cat-card__icon { font-size: 12px; }
.cat-card__label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  line-height: 1.3;
}
.cat-card__val {
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1.2;
  color: var(--ink);
  /* Cards are 1/3 row wide now — values like "parliamentary monarchy"
     must be allowed to wrap onto a second line within the cell. */
  overflow-wrap: anywhere;
}
.guess-row--win .guess-row__name { color: var(--warm); }

/* Latest card flip-reveals each category chip in sequence. */
.guess-row--reveal .guess-row__cards { perspective: 700px; }
.guess-row--reveal .cat-card {
  /* Synced 1:1 with the radar guess polygon's per-vertex pop animation —
     chip i flips at the same moment vertex i pops in. Duration + stagger
     use the same --radar-anim-scale multiplier so they stay aligned even
     if the radar animation speed changes. Mirror CARD_FLIP_DURATION and
     CARD_FLIP_STAGGER in app.js so the post-animation morph fires on time. */
  animation: cat-card-flip calc(240ms * var(--radar-anim-scale, 1)) cubic-bezier(0.4, 0, 0.2, 1) backwards;
  animation-delay: calc(var(--i, 0) * 90ms * var(--radar-anim-scale, 1));
  transform-origin: center;
  backface-visibility: hidden;
}
@keyframes cat-card-flip {
  0%   { opacity: 0; transform: rotateX(-90deg); }
  50%  { opacity: 1; }
  100% { opacity: 1; transform: rotateX(0); }
}

/* Reveal card (appended at end of guesses on win/loss). Same hairline
   rhythm as the guess rows above. */
.reveal {
  margin: 0;
  padding: 22px 0 20px;
  background: transparent;
  border: 0;
  border-top: 1px solid var(--rule);
  border-radius: 0;
  position: relative;
  text-align: center;
  box-shadow: none;
}
.reveal__eyebrow {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent);
  margin: 0 0 6px;
  font-weight: 600;
  font-family: var(--sans);
}
.reveal--loss .reveal__eyebrow { color: var(--ink-faint); }
.reveal__word {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 32px;
  letter-spacing: -0.01em;
  margin: 0 0 6px;
  line-height: 1.1;
}
.reveal__pos {
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-soft);
  margin: 0 0 14px;
  line-height: 1.4;
}
.reveal__share {
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: stretch;
}

/* ghost polygons for prior guesses */
.radar-ghost__edge {
  fill: none;
  stroke: var(--ink-soft);
  stroke-width: 1.1;
  stroke-dasharray: 2 2;
  pointer-events: none;
}
.radar-grid       { fill: none; stroke: var(--rule); stroke-width: 1; stroke-dasharray: 1 2; }
.radar-grid--outer { stroke: var(--ink-faint); stroke-width: 1.4; stroke-dasharray: none; filter: drop-shadow(0 0 4px rgba(0,0,0,0.18)); }
.radar-axis       { stroke: var(--rule); stroke-width: 0.5; }
.radar-axis-label { font-family: var(--sans); fill: var(--ink-soft); }
/* Primary axis label: short descriptive blurb ("GDP per capita") in italic
   serif so it reads as a caption to the icon above it. */
.radar-axis-label--desc {
  font-family: var(--serif);
  font-size: 12px;
  font-style: italic;
  fill: var(--ink-soft);
  letter-spacing: 0.01em;
}
.radar-axis-label--sep { fill: var(--ink-faint); font-style: italic; }
/* Won-state stacked label: numeric value on top, descriptor below.
   Color is set inline on the parent <text> via vertexColor(). */
.radar-axis-label__value {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
/* Match the playing-state blurb (.radar-axis-label--desc) exactly so the
   descriptor doesn't visually thicken when the value swaps in above it. */
.radar-axis-label__blurb {
  font-family: var(--serif);
  font-size: 12px;
  font-weight: 400;
  font-style: italic;
  fill: var(--ink-soft);
  letter-spacing: 0.01em;
}
/* Mobile: shrink both axis-label tspans so the won-state stack stays inside
   the tighter 400-unit viewBox at every angle. */
@media (max-width: 600px) {
  .radar-axis-label--desc,
  .radar-axis-label__blurb { font-size: 10.5px; }
  .radar-axis-label__value { font-size: 12.5px; }
}
/* Legacy single-element value label (kept in case it's referenced). */
.radar-axis-label--value {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* Radar header — country name + profile sentence reveal in place of the
   old separate "reveal" card. Sits above the SVG inside .board__radar.
   Always occupies a reserved min-height so the radar never shifts when the
   reveal fires; only opacity transitions. */
.radar-header {
  text-align: center;
  padding: 4px 16px 6px;
  position: relative;
}
/* Editorial hairline rule separating the country title from the radar.
   Inset from the header edges and slightly muted so it reads as a divider,
   not a frame. */
.radar-header::after {
  content: '';
  display: block;
  margin: 6px auto 0;
  width: 60%;
  max-width: 280px;
  border-bottom: 1px solid var(--rule);
}
.radar-header__title {
  position: relative;
  margin: 0;
  font-family: var(--serif);
  font-weight: 700;
  font-size: 31px;
  letter-spacing: -0.005em;
  line-height: 1.15;
  /* Reserve the line height so the country name and the placeholder share
     the same slot — flipping between them is a pure opacity crossfade. */
  min-height: 1.15em;
}
/* Stack the two title spans on top of each other. The placeholder claims the
   layout space; the country name overlays it absolutely and fades in. */
.radar-header__placeholder,
.radar-header__country {
  display: inline-block;
  transition: opacity 720ms ease-out;
}
.radar-header__placeholder {
  color: var(--ink-faint);
  letter-spacing: 0.01em;
  font-weight: 400;
  font-style: italic;
}
.radar-header__country {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  color: var(--ink);
  opacity: 0;
}
.radar-header--shown .radar-header__placeholder { opacity: 0; }
.radar-header--shown .radar-header__country     { opacity: 1; }
/* Loss message — stacked over the placeholder, crossfades in once the
   final guess polygon finishes its flip-in animation (handled by the
   --lost class being added only after winRevealed flips). */
.radar-header__loss {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  display: inline-block;
  /* Match the question-mark placeholder's font + weight so the message
     doesn't read as a louder, cartoonish callout. */
  font-family: var(--serif);
  font-weight: 500;
  color: var(--loss);
  opacity: 0;
  transition: opacity 720ms ease-out;
}
.radar-header--lost .radar-header__placeholder { opacity: 0; }
.radar-header--lost .radar-header__loss        { opacity: 1; }
.radar-header--instant .radar-header__loss     { transition: none; }
/* Reload of an already-finished game: country shows instantly, no crossfade.
   Only added when animateReveal is false (i.e. NOT during a live win flow). */
.radar-header--instant .radar-header__placeholder,
.radar-header--instant .radar-header__country {
  transition: none;
}
@media (max-width: 500px) {
  .radar-header__title { font-size: 27px; }
}

/* Share-card / reveal block — inserted into the guess stack as soon as the
   game ends, so layout settles before the morph. Stays at opacity 0 while
   the morph plays; --awaiting drops on revealAfter() to fade it in. */
.reveal { transition: opacity 720ms ease-out; }
.reveal--awaiting { opacity: 0; pointer-events: none; }

/* Axis labels fade in once when the post-morph reveal swaps blurb → value. */
@keyframes radar-label-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.radar-axis-label--fade-in {
  animation: radar-label-fade-in 720ms ease-out both;
}
/* Phosphor regular axis icons. fill: currentColor lets us override per-axis
   on win via style.color (matching the percentile-gradient value text). */
.radar-axis-icon { color: var(--ink-faint); }
.radar-axis-icon path { fill: currentColor; }
/* Invisible hit-rect inside the icon group catches taps over the full
   bounding box (mobile tap-target needs to be bigger than the glyph). */
.radar-axis-icon__hit { fill: transparent; cursor: pointer; }

/* Hovering popover explaining a tapped axis. Positioned absolutely inside
   .board__radar near the tapped icon (JS sets left/top/transform). Does not
   affect layout flow — it floats over the radar. */
.radar-axis-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 30px 8px 10px;
  background: var(--paper);
  border: 1px solid var(--ink);
  border-radius: 4px;
  position: absolute;
  z-index: 5;
  width: max-content;
  max-width: min(260px, 80vw);
  box-shadow: 0 4px 14px rgba(42, 32, 24, 0.18), 0 1px 0 rgba(255, 255, 255, 0.4) inset;
  pointer-events: auto;
}
.radar-axis-card[hidden] { display: none; }
/* Little caret pointing toward the icon. JS sets --caret-x so the caret
   stays aligned with the icon even when the card was clamped to fit. */
.radar-axis-card::before {
  content: '';
  position: absolute;
  left: var(--caret-x, 50%);
  width: 10px; height: 10px;
  background: var(--paper);
  border: 1px solid var(--ink);
  transform: translateX(-50%) rotate(45deg);
}
.radar-axis-card[data-side="below"]::before {
  top: -6px;
  border-right: 0;
  border-bottom: 0;
}
.radar-axis-card[data-side="above"]::before {
  bottom: -6px;
  border-left: 0;
  border-top: 0;
}
.radar-axis-card__icon {
  flex: 0 0 auto;
  width: 28px; height: 28px;
  display: inline-flex;
  color: var(--ink-soft);
}
.radar-axis-card__icon svg { width: 100%; height: 100%; }
.radar-axis-card__icon svg path { fill: currentColor; }
.radar-axis-card__body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.radar-axis-card__blurb {
  font-family: var(--serif);
  font-style: italic;
  font-size: 15px;
  color: var(--ink);
}
.radar-axis-card__value {
  font-family: var(--sans);
  font-weight: 700;
  font-size: 17px;
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.radar-axis-card__value[hidden] { display: none; }
.radar-axis-card__close {
  position: absolute;
  top: 4px; right: 8px;
  background: none;
  border: 0;
  color: var(--ink-faint);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 4px;
}

/* Target treatment — flat dark fill (no gradient, no grain, no dots). */
.radar-bg              { fill: var(--paper); stroke: none; }
.radar-target__fill    { fill: rgba(42, 32, 24, 0.55); stroke: none; }
.radar-target__grain   { display: none; }

/* Win morph: a single path that tweens from the hex guess polygon into
   the country silhouette. Stays warm/ochre throughout (continuation of
   the player's last guess). Only fires on a win — loss never reveals. */
.radar-target__morph {
  fill: rgba(184, 138, 42, 0.30);
  stroke: var(--warm);
  stroke-width: 1.8;
  stroke-linejoin: round;
  stroke-linecap: round;
  pointer-events: none;
}
.radar-target__edge    { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; transition: stroke 300ms ease; }
.radar-target__dot     { stroke: var(--paper); stroke-width: 1; transform-box: fill-box; transform-origin: center; }
.radar-guess__fill     { fill: rgba(184, 138, 42, 0.14); stroke: none; }
.radar-guess__edge     { fill: none; stroke: var(--warm); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.radar-guess__dot      { stroke: var(--paper); stroke-width: 1; transform-box: fill-box; transform-origin: center; }
/* Loss tint: the TARGET polygon (the country's true hex shape) turns
   terracotta once the round closes. The --loss-animate modifier adds a
   tween from the default dark fill/ink to terracotta (synced with the
   "better luck tomorrow" message fade-in); without it, the polygon
   paints in the final color statically (reload of a finished game). */
.radar-target__group--loss .radar-target__fill { fill: rgba(168, 73, 61, 0.30); }
.radar-target__group--loss .radar-target__edge { stroke: var(--loss); }
.radar-target__group--loss .radar-target__dot  { fill: var(--loss); }
.radar-target__group--loss-animate .radar-target__fill {
  animation: radar-target-loss-fill 1200ms ease-out both;
}
.radar-target__group--loss-animate .radar-target__edge {
  animation: radar-target-loss-edge 1200ms ease-out both;
}
.radar-target__group--loss-animate .radar-target__dot {
  animation: radar-target-loss-dot 1200ms ease-out both;
}
@keyframes radar-target-loss-fill {
  from { fill: rgba(42, 32, 24, 0.55); }
  to   { fill: rgba(168, 73, 61, 0.30); }
}
@keyframes radar-target-loss-edge {
  from { stroke: var(--ink); }
  to   { stroke: var(--loss); }
}
@keyframes radar-target-loss-dot {
  from { fill: var(--ink); }
  to   { fill: var(--loss); }
}

/* ── animations ─────────────────────────────────────────────────────────── */
/* Vertex-flip reveal (Wordle-style): dots pop in one-by-one clockwise from
   the top via per-element --i stagger. Once the last dot lands, the polygon
   edges + fill + grain fade in together. */
@keyframes radar-vertex-pop {
  0%   { opacity: 0; transform: scale(0); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes radar-edges-fade { from { opacity: 0; } to { opacity: 1; } }

/* All durations + delays below are base values multiplied by --radar-anim-scale.
   To slow down or speed up the whole entrance, change the scale at :root. */

/* TARGET — slow, ceremonial reveal */
.radar-target__group--flip .radar-target__dot {
  opacity: 0;
  transform: scale(0);
  transform-box: fill-box;
  transform-origin: center;
  animation: radar-vertex-pop calc(320ms * var(--radar-anim-scale, 1)) cubic-bezier(0.2, 0.9, 0.4, 1.15) forwards;
  animation-delay: calc(var(--i, 0) * 200ms * var(--radar-anim-scale, 1));
}
/* Wait for the last dot to finish popping in, then the polygon (edges + fill)
   fades in together. Delay = (last dot stagger) + (its pop duration),
   scaled by --radar-anim-scale; duration stays fixed. */
.radar-target__group--flip .radar-target__edge,
.radar-target__group--flip .radar-target__fill {
  opacity: 0;
  animation: radar-edges-fade 360ms ease-out forwards;
  animation-delay: calc((5 * 200ms + 320ms) * var(--radar-anim-scale, 1));
}

/* GUESS — same shape, snappier so the player isn't left waiting */
.radar-guess__group--flip .radar-guess__dot {
  opacity: 0;
  transform: scale(0);
  transform-box: fill-box;
  transform-origin: center;
  animation: radar-vertex-pop calc(240ms * var(--radar-anim-scale, 1)) cubic-bezier(0.2, 0.9, 0.4, 1.15) forwards;
  animation-delay: calc(var(--i, 0) * 90ms * var(--radar-anim-scale, 1));
}
.radar-guess__group--flip .radar-guess__edge,
.radar-guess__group--flip .radar-guess__fill {
  opacity: 0;
  animation: radar-edges-fade 260ms ease-out forwards;
  animation-delay: calc((5 * 90ms + 240ms) * var(--radar-anim-scale, 1));
}

/* Clock-sweep (kept for when RADAR_ENTRANCE === 'sweep') */
.radar-sweep__spoke {
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-linecap: round;
  opacity: 0.85;
  transition: opacity 240ms ease-out;
  pointer-events: none;
}
.radar-sweep__spoke--fade { opacity: 0; }

/* Win: gold ring expands from radar center; target outline flashes gold. */
.radar-ripple {
  fill: none;
  stroke: var(--warm);
  stroke-width: 2;
  pointer-events: none;
  animation: radar-ripple 850ms ease-out forwards;
}
@keyframes radar-ripple {
  0%   { r: 0;   opacity: 0.9; stroke-width: 2.5; }
  100% { r: 150; opacity: 0;   stroke-width: 0.5; }
}
.radar-target__group--won .radar-target__edge {
  animation: radar-outline-flash 900ms ease-out;
}
@keyframes radar-outline-flash {
  0%   { stroke: var(--ink); }
  30%  { stroke: var(--warm); stroke-width: 2.4; }
  100% { stroke: var(--ink); }
}

/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  .radar-target__group--flip *,
  .radar-guess__group--flip *,
  .radar-sweep__spoke,
  .radar-ripple,
  .radar-target__group--won .radar-target__edge {
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

.legend-dot {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 50%;
  vertical-align: middle;
  margin-right: 2px;
}
.legend-dot--match  { background: var(--accent); }
.legend-dot--close  { background: var(--warm); }
.legend-dot--far    { background: var(--ink-faint); opacity: 0.6; }

/* globe */
.globe-ocean    { fill: #f4f1ea; stroke: var(--rule); stroke-width: 1; }
.globe-grid     { fill: none; stroke: var(--rule); stroke-width: 0.5; opacity: 0.7; }
.globe-country  { fill: #d8d4c8; stroke: var(--paper); stroke-width: 0.4; transition: fill 240ms ease; }
.globe-country--lit { fill: var(--warm); }

/* Fixed keypad bar at the bottom of the viewport. Slides off-screen on
   downward scroll (--hidden) and is removed entirely on game end (--gone).
   Hosts the display, suggestions popover, and on-page keypad. */
.keypad-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  background: var(--bg);
  border-top: 1px solid var(--rule);
  box-shadow: 0 -8px 20px rgba(42, 32, 24, 0.10);
  /* Tight top. Bottom is the larger of a small comfort buffer (so the
     keypad doesn't kiss the viewport on desktop) and the iOS home-indicator
     safe area (so it doesn't get overlapped on phones with no chin). */
  padding: 6px 14px max(10px, env(safe-area-inset-bottom, 0px));
  z-index: 50;
  transition: transform 220ms cubic-bezier(0.4, 0, 0.2, 1);
}
.keypad-bar--gone {
  transform: translateY(100%);
  pointer-events: none;
}
/* Initial-hidden — applied before the radar entrance animation finishes.
   Slides off the bottom edge and uses a slower, more deliberate ease so
   the keypad's arrival reads as a finishing beat after the polygon lands.
   The class is removed in JS once the entrance timing has elapsed. */
.keypad-bar--initial-hidden {
  transform: translateY(100%);
  pointer-events: none;
  transition: transform 520ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* guess pane: display + suggestions + on-page keypad. No real <input>. */
.guess-pane { display: flex; flex-direction: column; gap: 0; max-width: var(--maxw); margin: 0 auto; }
.guess-pane--disabled { opacity: 0.55; pointer-events: none; }

/* Display + suggestions live in a relative-positioned stack so the
   suggestion list can pop UPWARD as an absolute overlay over the strip
   and radar — never displacing the keypad below. */
.guess-input-stack { position: relative; }

/* Display bar — small text strip above the keypad showing the current
   typed query (or placeholder when empty). The suggestions popover sits
   above it. */
.guess-display {
  display: block;
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 500;
  color: var(--ink);
  padding: 8px 12px;
  margin: 0 0 6px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 4px;
  text-align: center;
  letter-spacing: 0.01em;
  min-height: 1.4em;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.guess-display[data-empty="true"] {
  color: var(--ink-faint);
  font-style: italic;
  font-weight: 400;
}

/* Keypad — three rows, QWERTY. Sized to fit the viewport edge to edge on
   mobile; keys minimum 40×42 for thumb taps. Atlas-themed paper buttons. */
.keypad {
  display: flex;
  flex-direction: column;
  gap: 6px;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.keypad__row {
  display: flex;
  gap: 5px;
  justify-content: center;
}
.key {
  flex: 1 1 0;
  min-width: 0;
  min-height: 46px;
  padding: 0 4px;
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 600;
  color: var(--ink);
  background: var(--paper-shade);
  border: 1px solid var(--rule);
  border-radius: 4px;
  cursor: pointer;
  text-transform: uppercase;
  touch-action: manipulation;
  transition: background 80ms ease, transform 60ms ease;
}
.key:active {
  background: var(--rule-soft);
  transform: translateY(1px);
}
/* Doubled-class selector `.key.key--wide` (0,0,2,0) so it beats the later
   @media `.key { font-size: 17px }` rule at (0,0,1,0). Without the
   specificity bump, the mobile breakpoint clobbers this and "ENTER"
   overflows the chip. font-size clamp() guarantees the text mathematically
   cannot outgrow the button across any viewport. */
.key.key--wide {
  flex: 1.6 1 0;
  font-family: var(--sans);
  font-size: clamp(9px, 2.6vw, 12px);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0 2px;
  white-space: nowrap;
}
.key--enter {
  background: var(--accent);
  color: var(--paper);
  border-color: var(--accent);
}
.key--enter:active { background: var(--ink); border-color: var(--ink); }
.key--back { font-size: 18px; letter-spacing: 0; }

.guess-counter {
  text-align: center;
  font-size: 13px;
  font-family: var(--serif);
  font-style: italic;
  padding: 4px 0 2px;
  transition: color 400ms ease;
}
.guess-counter:empty { display: none; }
.guess-counter[data-remaining="5"] { color: #4a7c59; }
.guess-counter[data-remaining="4"] { color: #7a8a3a; }
.guess-counter[data-remaining="3"] { color: #b08948; }
.guess-counter[data-remaining="2"] { color: #b06030; }
.guess-counter[data-remaining="1"] { color: #9a2020; }

.guess-form__err {
  color: var(--ink-soft);
  font-size: 12px;
  text-align: center;
  font-style: italic;
}
.guess-form__err:empty { display: none; }

/* Suggestions — popover that opens UPWARD from the display, overlaying
   the strip/radar. Absolute positioning keeps the keypad below from being
   pushed off the screen as the list grows. Internal scroll caps the
   height to ~5 visible rows. */
.suggestions {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 6px);
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 280px;
  overflow-y: auto;
  padding: 4px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 4px;
  z-index: 8;
  box-shadow: 0 -6px 18px rgba(42, 32, 24, 0.18);
}
.suggestions:empty {
  display: none;
}
.suggestion-row {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: 12px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 8px 12px;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  width: 100%;
  color: var(--ink);
}
.suggestion-row:hover,
.suggestion-row--active {
  background: rgba(61,110,118,0.07);
  border-color: var(--accent);
}
.suggestion-row__flag {
  font-size: 22px;
  line-height: 1;
}
.suggestion-row__name {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 500;
}
.suggestion-row__shape {
  width: 40px;
  height: 28px;
  flex: 0 0 auto;
}
.suggestion-row__shape path {
  fill: var(--ink-soft);
  stroke: var(--ink);
  stroke-width: 0.4;
}
.suggestion-row--active .suggestion-row__shape path,
.suggestion-row:hover .suggestion-row__shape path {
  fill: var(--accent);
}
.guess-form__err.ok { color: var(--accent); }

/* ─── Mobile: simple, scrolling layout ──────────────────────────────────
   The radar fills the screen width; its height follows from the SVG's
   viewBox aspect (square on mobile → square radar). Page scrolls normally
   if the keyboard pushes content; we'll revisit the keyboard interaction
   separately. No flex-chain tricks, no visualViewport sizing — just a
   readable single-column flow. */
@media (max-width: 700px) {
  html, body { font-size: 18px; }
  .nav__title { padding: 8px 12px 2px; }
  .nav__title-logo { height: auto; width: 100%; max-width: 100%; }
  .nav__btn { font-size: 11px; letter-spacing: 0.12em; padding: 4px 8px; }
  .footer { font-size: 13px; padding: 16px 20px 22px; }
  .footer::before { margin-bottom: 12px; }
  .footer__source { display: block; margin-top: 4px; }
  .footer__sep:last-of-type { display: none; }

  #view { padding: 12px 4px 16px; }
  .board { margin: 0; }
  .board__radar { padding: 4px 4px 8px; border-radius: 2px; }

  .guess-row { padding: 12px; }
  .guess-row__name { font-size: 18px; }
  .guess-row__flag { font-size: 22px; }
  /* mobile still 3 columns (max 2 rows for 6 tiles) */
  .cat-card__label { font-size: 11px; }
  .cat-card__val { font-size: 15px; }
  .reveal__word { font-size: 28px; }

  .keypad-bar { padding: 8px 10px calc(8px + env(safe-area-inset-bottom, 0px)); }
  .guess-display { font-size: 20px; padding: 10px 12px; }
  .share__btn { font-size: 13px; padding: 11px 18px; letter-spacing: 0.14em; }
  .key { min-height: 44px; font-size: 17px; }
  /* font-size handled by clamp() on .key.key--wide */
  .keypad { gap: 5px; }
  .keypad__row { gap: 4px; }

  .suggestion-row { padding: 9px 12px; gap: 10px; }
  .suggestion-row__name { font-size: 17px; }
  .suggestion-row__flag { font-size: 22px; }
}
@media (max-width: 500px) {
  .nav { padding: 2px 12px 6px; }
  .nav__title { padding: 6px 10px 2px; }
  .nav__title-logo { height: auto; width: 100%; max-width: 100%; }
  #view { padding: 10px 2px 24px; }
  .board__radar { padding: 2px 2px 6px; }
  .key { min-height: 42px; font-size: 16px; padding: 0 2px; }
  .keypad { gap: 4px; }
  .keypad__row { gap: 3px; }
}
.share__btn {
  background: var(--accent);
  color: var(--paper);
  border: none;
  padding: 11px 22px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  cursor: pointer;
  border-radius: 2px;
  font-family: var(--sans);
  font-weight: 600;
}
.share__btn:hover { filter: brightness(0.95); }

dialog {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 28px 24px;
  max-width: 460px;
  font-family: var(--sans);
}
dialog::backdrop { background: rgba(26,26,26,0.4); }

/* ── page overlay (about / privacy / contact shown as modal) ── */
.page-overlay {
  max-width: min(640px, 94vw);
  max-height: 82vh;
  width: 100%;
  overflow-y: auto;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 4px;
}
.page-overlay__close {
  position: sticky;
  top: 0;
  float: right;
  margin: 12px 12px 0 0;
  background: var(--paper-shade);
  border: 1px solid var(--rule);
  border-radius: 50%;
  width: 30px;
  height: 30px;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  color: var(--ink-soft);
  font-family: var(--sans);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}
.page-overlay__close:hover { color: var(--ink); background: var(--rule-soft); }

/* ── stats modal ──────────────────────────────────────────────────────────── */
.stats-dialog {
  max-width: min(400px, 94vw);
  width: 100%;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 4px;
}
.stats-modal {
  padding: 32px 28px 28px;
}
.stats-modal__heading {
  font-family: var(--serif);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-align: center;
  margin: 0 0 24px;
  color: var(--ink);
}
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 28px;
}
.stats-grid__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.stats-grid__value {
  font-family: var(--serif);
  font-size: 32px;
  font-weight: 600;
  line-height: 1;
  color: var(--ink);
}
.stats-grid__label {
  font-family: var(--sans);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  text-align: center;
  line-height: 1.3;
}
.stats-modal__subheading {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 12px;
  text-align: center;
}
.guess-dist { display: flex; flex-direction: column; gap: 5px; }
.guess-dist__row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.guess-dist__label {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  width: 14px;
  text-align: right;
  flex-shrink: 0;
}
.guess-dist__bar {
  background: var(--ink-faint);
  border-radius: 2px;
  min-width: 24px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 6px;
  transition: width 400ms ease;
}
.guess-dist__bar--current { background: var(--accent); }
.guess-dist__count {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  color: #fff;
}

/* ── shared static page styles (used inside overlay and on standalone pages) */
.static-page { max-width: 640px; margin: 0 auto; padding: 24px 24px 48px; }
.static-page h1 { font-family: var(--serif); font-size: 26px; font-weight: 600; margin: 0 0 6px; }
.static-page .page-date { font-family: var(--sans); font-size: 12px; color: var(--ink-faint); letter-spacing: 0.08em; margin: 0 0 28px; }
.static-page h2 { font-family: var(--serif); font-size: 16px; font-weight: 600; margin: 28px 0 8px; }
.static-page p, .static-page li { font-size: 14px; line-height: 1.65; color: var(--ink-soft); margin: 0 0 12px; }
.static-page ul { padding-left: 20px; }
.static-page a { color: var(--accent); text-decoration: none; }
.static-page a:hover { text-decoration: underline; }
.static-page hr { border: none; border-top: 1px solid var(--rule); margin: 28px 0; }
.static-page .play-cta { display: inline-block; margin-top: 8px; background: var(--accent); color: #fff; font-family: var(--sans); font-size: 11px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; padding: 12px 24px; border-radius: 2px; text-decoration: none; }
.static-page .play-cta:hover { filter: brightness(0.92); text-decoration: none; }
.static-page .contact-email { font-family: var(--serif); font-size: 20px; font-weight: 600; color: var(--ink); display: block; margin: 14px 0 20px; }

/* Visual animated explainer — replaces the text rules. Three beats loop on
   a 9s cycle: (1) target polygon appears, (2) guess overlays, (3) per-axis
   pips light up. Captions crossfade in sync. Pure CSS, no JS. */
.explainer { margin: 14px 0 8px; text-align: center; }
.explainer__svg {
  width: 100%;
  max-width: 280px;
  height: auto;
  display: block;
  margin: 0 auto 8px;
}
.explainer__ring  { fill: none; stroke: var(--rule-soft); stroke-width: 0.6; stroke-dasharray: 1 2; }
.explainer__axes line { stroke: var(--rule); stroke-width: 0.5; opacity: 0.6; }
.explainer__labels text {
  font-family: var(--serif);
  font-style: italic;
  font-size: 7.5px;
  fill: var(--ink-soft);
  letter-spacing: 0.01em;
}
.explainer__target {
  fill: var(--ink);
  fill-opacity: 0.18;
  stroke: var(--ink);
  stroke-width: 1.6;
  animation: explainer-target 9s ease-in-out infinite;
}
.explainer__guess {
  fill: var(--accent);
  fill-opacity: 0.10;
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-dasharray: 3 2;
  animation: explainer-guess 9s ease-in-out infinite;
}
.explainer__pip { opacity: 0; animation: explainer-pip 9s ease-in-out infinite; }
/* Sepia intensity ramp — matches vertexColor() in app.js (hue 28, sat 32%,
   lightness 72% pale → 18% dark). Each pip encodes that axis's percentile
   relative to the world population. */
.explainer__pip--low  { fill: hsl(28, 32%, 64%); }
.explainer__pip--mid  { fill: hsl(28, 32%, 42%); }
.explainer__pip--high { fill: hsl(28, 32%, 22%); }
.explainer-legend-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
}
.explainer-legend-dot--low  { background: hsl(28, 32%, 64%); }
.explainer-legend-dot--mid  { background: hsl(28, 32%, 42%); }
.explainer-legend-dot--high { background: hsl(28, 32%, 22%); }

@keyframes explainer-target {
  0%, 100% { opacity: 0; }
  10%, 95% { opacity: 1; }
}
@keyframes explainer-guess {
  0%, 28%   { opacity: 0; }
  38%, 95%  { opacity: 1; }
  100%      { opacity: 0; }
}
@keyframes explainer-pip {
  0%, 55%   { opacity: 0; transform: scale(0.4); }
  68%, 95%  { opacity: 1; transform: scale(1); }
  100%      { opacity: 0; transform: scale(0.4); }
}
.explainer__pip { transform-box: fill-box; transform-origin: center; }

.explainer__captions {
  position: relative;
  min-height: 2.6em;
  margin: 4px 0 12px;
}
.explainer__caption {
  position: absolute;
  inset: 0;
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: 15px;
  color: var(--ink-soft);
  line-height: 1.35;
  opacity: 0;
}
.explainer__caption--1 { animation: explainer-cap-1 9s ease-in-out infinite; }
.explainer__caption--2 { animation: explainer-cap-2 9s ease-in-out infinite; }
.explainer__caption--3 { animation: explainer-cap-3 9s ease-in-out infinite; }
@keyframes explainer-cap-1 {
  0%, 28%   { opacity: 1; }
  32%, 100% { opacity: 0; }
}
@keyframes explainer-cap-2 {
  0%, 28%   { opacity: 0; }
  32%, 55%  { opacity: 1; }
  60%, 100% { opacity: 0; }
}
@keyframes explainer-cap-3 {
  0%, 55%   { opacity: 0; }
  60%, 95%  { opacity: 1; }
  100%      { opacity: 0; }
}

.explainer__legend {
  list-style: none;
  padding: 0;
  margin: 4px 0 6px;
  display: flex;
  justify-content: center;
  gap: 14px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}
.explainer__legend li { display: flex; align-items: center; gap: 5px; }

.rules__footer {
  margin: 14px 0 18px;
  font-family: var(--serif);
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.5;
  font-style: italic;
}

@media (prefers-reduced-motion: reduce) {
  .explainer__target,
  .explainer__guess,
  .explainer__pip,
  .explainer__caption {
    animation: none;
    opacity: 1;
  }
  .explainer__caption--2,
  .explainer__caption--3 { display: none; }
}
.rules__list { list-style: none; padding: 0; margin: 14px 0; }
.rules__list li {
  display: flex; align-items: center; gap: 12px;
  padding: 6px 0;
  font-family: var(--serif);
  font-size: 16px;
}
