/* ==========================================================================
   DailyPlay — shared game shell
   The board, cover well, guess pips, search box, history and results screen.
   Every guessing game uses these; per-game tweaks live in its own file.
   Builds on tokens.css and app.css.
   ========================================================================== */

/* --- Header --------------------------------------------------------------- */
.game-bar {
  display: flex;
  align-items: center;
  min-height: 62px;
}
.game-bar__actions {
  display: flex;
  flex: 0 0 94px;
  align-items: center;
  gap: 6px;
}
.game-bar__actions--right { justify-content: flex-end; }
.game-bar__actions--right .site-volume {
  position: relative;
  inset: auto;
  transform: none;
}
.game-title {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.1rem, 4vw, 1.45rem);
  letter-spacing: -0.025em;
}
.game-bar .game-title { padding-right: 0; }
@media (max-width: 700px) {
  .game-bar .game-title { font-size: clamp(.85rem, 3.8vw, 1.2rem); line-height: 1.15; }
}

.game-main { padding-block: clamp(16px, 4vw, 30px) 56px; }

.loading { color: var(--ink-muted); text-align: center; padding-block: 60px; }

/* --- Board ---------------------------------------------------------------- */
.board {
  display: grid;
  gap: clamp(18px, 4vw, 34px);
  align-items: start;
}
@media (min-width: 860px) {
  .board {
    grid-template-columns: minmax(0, 420px) minmax(0, 420px);
    justify-content: center;
    max-width: 880px;
    margin-inline: auto;
  }
}

.board__cover { display: grid; gap: 12px; }
.board__panel { display: grid; gap: 18px; align-content: start; }

/* --- Cover ---------------------------------------------------------------
   `container-type` lets the blur be a share of the cover's own width, so a
   phone and a desktop see exactly the same amount of the picture. */
.cover {
  position: relative;
  container-type: inline-size;
  aspect-ratio: 1;
  max-width: 100%;
  overflow: hidden;
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background: var(--surface-sunk);
}

.cover__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* The scale hides the soft edge a blur leaves, and eases off as the
     picture sharpens. */
  transform: scale(calc(1 + var(--stage) * 0.02));
  filter: blur(calc(var(--stage) * 1cqw));
  transition: opacity 240ms var(--ease), filter 420ms var(--ease), transform 420ms var(--ease);
}
.cover__img.is-ready { opacity: 1; }

/* Artwork that fills its frame (an album sleeve) crops; a cut-out subject
   on transparency (a weapon) is contained instead. */
.cover--contain .cover__img { object-fit: contain; }

/* Applied for one frame while a new cover is loaded, so the blur jumps to
   full instead of easing down from the last round's reveal. */
.cover.is-snapping .cover__img { transition: none; }

/* --- Flag Guesser ----------------------------------------------------------
   Every flag ships pre-composited onto the same 3:2 canvas (transparent
   padding, so the frame's own themed background shows through), so this well
   is wider and shorter than the square `.cover` the other games use. */
.cover--flag { aspect-ratio: 3 / 2; }

/* Hint 1: a circle of the flag's own colours, hard edges — set inline as a
   conic-gradient by game.js from the manifest's colour breakdown. */
.flag-circle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  height: 62%;
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}

/* Hints 3 (the whole flag in black and white) and 5 (full colour) share
   this plain image well. */
.flag-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 240ms var(--ease);
}
.flag-img.is-ready { opacity: 1; }

/* Hint 2: one quarter of the black-and-white flag. A background image at
   200% × 200% with the position pinned to a corner shows exactly one
   quadrant at 2× — game.js sets which corner (0%/100% on each axis, seeded
   so every player gets the same one for a given flag). This is a plain
   background on the well itself rather than an absolutely-positioned <img>
   sized in a percentage of its own box, which several browsers resolve
   inconsistently once `inset` and an explicit width/height both apply. */
.flag-crop {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  background-size: 200% 200%;
}

.cover__reveal {
  position: absolute;
  inset: auto 0 0 0;
  padding: 16px;
  display: grid;
  gap: 2px;
  background: linear-gradient(to top, rgba(4, 6, 4, 0.92), rgba(4, 6, 4, 0));
  color: #f4f6f4;
}
.cover__verdict {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.cover__verdict[data-ok="true"]  { color: var(--accent); }
.cover__verdict[data-ok="false"] { color: #ff8080; }
.cover__answer {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(0.98rem, 3vw, 1.15rem);
  line-height: 1.25;
  text-wrap: balance;
}

.roundline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 0.8rem;
  color: var(--ink-2);
}
.roundline__dots { display: flex; gap: 6px; }
.roundline__dots i {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--line-hi);
}
.roundline__dots i[data-on="current"] { background: var(--accent); }
.roundline__dots i[data-on="done"]    { background: var(--ink-muted); }

/* Says in plain words what the current hint picture actually is — a circle
   of colours, a cropped quarter, and so on aren't self-explanatory the first
   time you see them. Sits right under the round line so it reads as a
   caption for the cover above it. Styled like the `.hints` box below (same
   dashed border, same bold-label-plus-value shape) so it reads as the same
   kind of thing instead of a quieter afterthought.

   The continent hint (once earned) lives in here too, as a second row
   rather than a second box of its own — two dashed yellow boxes stacked
   right on top of each other read as visual clutter, one box with two
   rows reads as one hint panel that grows. */
.stage-note {
  display: grid;
  gap: 8px;
  padding: 10px 14px;
  border: 1px dashed color-mix(in srgb, var(--pending) 45%, transparent);
  border-radius: var(--r-md, 12px);
  background: color-mix(in srgb, var(--pending) 8%, transparent);
}
.stage-note__row {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
/* Only a divider between rows that actually exist — a hidden continent row
   contributes no border, so the box doesn't reserve blank space for it. */
.stage-note__row:not([hidden]) ~ .stage-note__row:not([hidden]) {
  padding-top: 8px;
  border-top: 1px dashed color-mix(in srgb, var(--pending) 30%, transparent);
}
.stage-note b {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pending);
  white-space: nowrap;
}
.stage-note span {
  font-size: 0.88rem;
  color: var(--ink);
}

/* --- Guess pips ----------------------------------------------------------- */
.pips { display: flex; gap: 7px; }
.pips li {
  flex: 1;
  max-width: 46px;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  border: 1.5px solid var(--line-hi);
  background: var(--surface-sunk);
  color: var(--ink-muted);
  font-size: 0.8rem;
  font-weight: 600;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.pips li[data-state="active"]  { border-color: var(--accent); border-style: dashed; }
.pips li[data-state="wrong"]   { background: #ff5f5f; border-color: #ff5f5f; color: #2a0505; }
.pips li[data-state="skipped"] { background: var(--pending); border-color: var(--pending); color: #2a1c02; }
.pips li[data-state="correct"] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* --- Guess box ------------------------------------------------------------ */
.guessbox { display: grid; gap: 10px; }

.combo { position: relative; }
.combo__input {
  width: 100%;
  padding: 13px 15px;
  border-radius: var(--r-md);
  border: 1px solid var(--line-hi);
  background: var(--surface-sunk);
  color: var(--ink);
  font: inherit;
}
.combo__input::placeholder { color: var(--ink-muted); }
.combo__input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.combo__list {
  position: absolute;
  z-index: 20;
  top: calc(100% + 6px);
  left: 0; right: 0;
  max-height: 264px;
  overflow-y: auto;
  padding: 5px;
  border-radius: var(--r-md);
  border: 1px solid var(--line-hi);
  background: var(--surface);
  box-shadow: var(--shadow-hi);
}
.combo__list li {
  padding: 9px 11px;
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1.3;
}
.combo__list li[aria-selected="true"],
.combo__list li:hover { background: var(--surface-hi); }
.combo__list b { font-weight: 500; }
.combo__list span { color: var(--ink-muted); display: block; font-size: 0.8rem; }
.combo__empty { padding: 11px; color: var(--ink-muted); font-size: 0.86rem; }

.guessbox__actions { display: grid; grid-template-columns: 1fr auto; gap: 9px; }

/* --- Buttons -------------------------------------------------------------- */
.btn {
  padding: 13px 22px;
  border-radius: var(--r-pill);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.92rem;
  text-align: center;
  transition: filter var(--dur) var(--ease), background-color var(--dur) var(--ease),
              color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn--primary { background: var(--accent); color: var(--accent-ink); }
.btn--primary:hover { filter: brightness(1.08); }
.btn--primary:disabled { opacity: 0.4; cursor: not-allowed; filter: none; }
.btn--home { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
.btn--home svg { width: 18px; height: 18px; flex: none; }
.btn--quiet { border: 1px solid var(--line-hi); color: var(--ink-2); }
.btn--quiet:hover { color: var(--ink); background: var(--surface-hi); border-color: var(--ink-muted); }
.btn--wide { width: 100%; }

/* --- Guess history -------------------------------------------------------- */
.history { display: grid; gap: 1px; }
.history li {
  display: flex;
  align-items: baseline;
  gap: 11px;
  padding: 9px 2px;
  border-bottom: 1px solid var(--line);
  font-size: 0.88rem;
  min-height: 39px;
}
.history li b {
  width: 14px;
  color: var(--ink-muted);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}
.history li[data-kind="skip"]    { color: var(--pending); }
.history li[data-kind="wrong"]   { color: var(--ink-2); }
.history li[data-kind="correct"] { color: var(--accent); font-weight: 500; }
.history li[data-kind="empty"]   { color: var(--ink-muted); }

/* --- Hints ----------------------------------------------------------------
   Earned, not always-on: each row appears as a guess is spent, so the panel
   grows down the page rather than reserving space for hints nobody has. */
.hints {
  display: grid;
  /* The labels share one column across every row, so it sizes to the longest
     of them — "Collection" — instead of wrapping inside a fixed width. */
  grid-template-columns: max-content 1fr;
  gap: 9px 14px;
  align-items: baseline;
  padding: 12px 14px;
  border: 1px dashed color-mix(in srgb, var(--pending) 45%, transparent);
  border-radius: var(--r-md, 12px);
  background: color-mix(in srgb, var(--pending) 8%, transparent);
  list-style: none;
}
.hints li {
  display: contents;
  font-size: 0.88rem;
  color: var(--ink);
}
.hints li span { font-size: 0.88rem; color: var(--ink); }
.hints li b {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pending);
}
/* The blanked name needs air between the characters or the underscores run
   together into one rule. */
.hints__mask {
  font-family: var(--font-mono, ui-monospace, "SFMono-Regular", Menlo, monospace);
  letter-spacing: 0.22em;
  white-space: pre;
}

/* --- Versus board (Peak Players) ------------------------------------------
   Two cards and a running tally. On a phone they stack with the tally between
   them, so the thing you're comparing against is always the card above. */
.versus {
  display: grid;
  gap: 14px;
  align-items: start;
  position: relative;
}
@media (min-width: 820px) {
  .versus {
    /* Breaks out of the site's 1000px measure: two pieces of key art side by
       side want the room, and the page is otherwise mostly empty. Bounded by
       the viewport so it can never cause a horizontal scroll. */
    --board: min(1320px, calc(100vw - 64px));
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    gap: 20px;
    align-items: start;
    width: var(--board);
    margin-inline: calc((100% - var(--board)) / 2);
  }
}
/* Only give way to the ad rails when they are actually on the page —
   ads.js sets --ad-rail-space to the room each rail needs. */
@media (min-width: 820px) {
  body.has-ad-rails .versus {
    --board: min(1320px, calc(100vw - 2 * var(--ad-rail-space, 0px)));
  }
}

.card {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background: var(--surface-sunk);
  transition: border-color 260ms var(--ease);
}
/* The art alone sets the card's height, so both cards match without the grid
   stretching them — a stretched <img> ignores its aspect-ratio and would crop
   further than the build already did. 4:3 is what the build ships. */
.card__art {
  display: block;
  width: 100%;
  /* `height: auto` is load-bearing: the img's height attribute lands as a
     presentation hint that otherwise outranks aspect-ratio. */
  height: auto;
  /* 3:2 is close to the 1.75:1 of the store capsule the build ships, so the
     crop takes very little off the sides. */
  aspect-ratio: 3 / 2;
  object-fit: cover;
}
/* The scrim is what makes white text safe over artwork we don't control. */
.card__body {
  position: absolute;
  inset: auto 0 0 0;
  padding: clamp(14px, 3vw, 20px);
  display: grid;
  gap: 8px;
  justify-items: center;
  text-align: center;
  color: #f4f6f4;
  background: linear-gradient(to top, rgba(4, 6, 4, 0.94) 38%, rgba(4, 6, 4, 0.55) 72%, rgba(4, 6, 4, 0));
}
.card__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.05rem, 3.4vw, 1.5rem);
  line-height: 1.15;
  text-wrap: balance;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.7);
}
.card__count {
  display: grid;
  gap: 1px;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(244, 246, 244, 0.72);
}
.card__count b {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.6rem, 6vw, 2.6rem);
  letter-spacing: 0;
  text-transform: none;
  font-variant-numeric: tabular-nums;
  color: var(--accent-hi);
}

.card__ask { display: grid; gap: 10px; justify-items: center; }
.card__prompt {
  font-size: 0.82rem;
  color: rgba(244, 246, 244, 0.8);
  text-wrap: balance;
}
.card__calls { display: flex; gap: 9px; flex-wrap: wrap; justify-content: center; }
.call {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 10px 20px;
  border-radius: var(--r-pill);
  border: 1.5px solid rgba(244, 246, 244, 0.5);
  background: rgba(8, 12, 8, 0.6);
  backdrop-filter: blur(6px);
  color: #f4f6f4;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.92rem;
  cursor: pointer;
  transition: background 160ms var(--ease), border-color 160ms var(--ease), transform 160ms var(--ease);
}
.call svg { width: 17px; height: 17px; }
.call:hover:not(:disabled) {
  background: var(--accent);
  border-color: var(--accent);
  color: #06140a;
}
.call:disabled { opacity: 0.45; cursor: default; }

.card__verdict {
  position: absolute;
  inset: 12px 12px auto auto;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  backdrop-filter: blur(6px);
}
.card__verdict[data-ok="true"]  { background: var(--accent); color: #06140a; }
.card__verdict[data-ok="false"] { background: #ff8080; color: #2a0505; }

.card[data-state="correct"] { border-color: var(--accent); }
.card[data-state="wrong"]   { border-color: #ff8080; }

/* The tally sits between the cards: a badge on desktop, a slim bar on a
   phone, where a floating circle would just push the cards apart. */
.versus__score {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 5px;
  align-self: center;
  padding: 8px 16px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--surface);
  font-family: var(--font-display);
  color: var(--ink-2);
  font-size: 0.8rem;
  white-space: nowrap;
}
.versus__score b {
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.roundline--center { justify-content: center; gap: 16px; margin-top: 18px; }

/* Peak Players scores every call, so its dots carry a result rather than just
   "played". Scoped to this modifier so the other games' dots are untouched. */
.roundline__dots--calls i[data-on="hit"]    { background: var(--accent); }
.roundline__dots--calls i[data-on="missed"] { background: #ff8080; }
/* A ring, not a fill — otherwise "where you are" and "you got it" are the
   same green dot. */
.roundline__dots--calls i[data-on="current"] {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* `display: block` is what makes the auto margins bite — .btn is inline-block,
   and auto margins on an inline-level box compute to zero. */
.btn--centred {
  display: block;
  max-width: 320px;
  margin: 16px auto 0;
}

/* --- Results -------------------------------------------------------------- */
.results {
  display: grid;
  gap: 16px;
  justify-items: center;
  text-align: center;
  max-width: 560px;
  margin-inline: auto;
  padding-block: clamp(14px, 4vw, 28px);
}
.results__eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.results__score {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3rem, 14vw, 4.6rem);
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--accent);
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.results__outof {
  font-size: 0.3em;
  font-weight: 600;
  color: var(--ink-muted);
  letter-spacing: 0;
}
.results__line {
  color: var(--ink-2);
  text-wrap: balance;
  opacity: 0;
  transition: opacity 420ms var(--ease);
}
.results__line.is-in { opacity: 1; }

#score-value { display: inline-block; font-variant-numeric: tabular-nums; }
#score-value.is-landed { animation: score-pop 460ms var(--ease); }
@keyframes score-pop {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.14); }
  100% { transform: scale(1); }
}

/* Perfect-score burst. Decorative, never in the way. */
.confetti {
  position: fixed;
  inset: 0;
  z-index: 70;
  pointer-events: none;
}

.results__list {
  display: grid;
  gap: 8px;
  width: 100%;
  margin-top: 6px;
}
.results__list li {
  display: grid;
  grid-template-columns: 54px minmax(0, 1fr) auto;
  gap: 13px;
  align-items: center;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  text-align: left;
}
.results__list img {
  width: 54px; height: 54px;
  border-radius: var(--r-sm);
  object-fit: cover;
  display: block;
}
.results__list li:has(.peak-results__thumb) { grid-template-columns: 64px minmax(0, 1fr) auto; padding-block: 8px; }
.peak-results__thumb { display: block; width: 64px; height: 44px; border-radius: var(--r-sm); overflow: hidden; background: var(--surface-sunk); }
.peak-results__thumb img { width: 64px; height: 44px; }
.results__meta { min-width: 0; display: grid; gap: 1px; }
.results__album {
  display: block;
  font-weight: 500;
  font-size: 0.92rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.results__artist { display: block; color: var(--ink-muted); font-size: 0.8rem; }
.results__points {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.86rem;
  white-space: nowrap;
  padding: 5px 11px;
  border-radius: var(--r-pill);
  background: var(--surface-sunk);
  color: var(--ink-muted);
}
.results__points[data-ok="true"] { background: var(--accent-soft); color: var(--accent); }

.results__actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 4px; }
.results__next { font-size: 0.82rem; color: var(--ink-muted); }
.results__next time { font-variant-numeric: tabular-nums; color: var(--ink-2); }

/* --- Rarity tag ----------------------------------------------------------
   Colour comes from the item itself, set inline as --tag. */
.tag {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tag, var(--ink-2));
  border: 1px solid color-mix(in srgb, var(--tag, var(--ink-2)) 55%, transparent);
  background: color-mix(in srgb, var(--tag, var(--ink-2)) 14%, transparent);
}
.cover__reveal .tag { justify-self: start; margin-bottom: 3px; }

/* --- Rating Guesser --------------------------------------------------------
   Reuses the Peak Players `.versus`/`.card` board wholesale — same layout,
   same call buttons — with one change: the art is a portrait poster instead
   of a landscape capsule. */
.card__art--poster {
  aspect-ratio: 2 / 3;
  object-fit: cover;
}

/* Portrait posters stay side by side at every width — stacked, a phone
   showed one poster per screen and the comparison scrolled out of view.
   --poster-w caps each column so a poster (1.5x as tall as it is wide) plus
   the header, segment pill and roundline fit the viewport height; that is
   what scales them down on tablets and short laptop screens. */
.versus--posters {
  --poster-w: clamp(130px, calc((100dvh - 290px) / 1.5), 560px);
  grid-template-columns: minmax(0, var(--poster-w)) auto minmax(0, var(--poster-w));
  justify-content: center;
}
@media (max-width: 819.98px) {
  /* No room for the tally between two posters: it sits above them instead. */
  .versus--posters {
    grid-template-columns: repeat(2, minmax(0, var(--poster-w)));
    gap: 10px;
  }
  .versus--posters .versus__score {
    grid-column: 1 / -1;
    grid-row: 1;
    justify-self: center;
    padding: 4px 14px;
  }
  .versus--posters .versus__score b { font-size: 1.2rem; }
}
@media (max-width: 559.98px) {
  .versus--posters .card__body { padding: 10px 8px; gap: 6px; }
  .versus--posters .card__name { font-size: 0.9rem; }
  .versus--posters .card__count { font-size: 0.6rem; }
  .versus--posters .card__count b { font-size: 1.5rem; }
  /* The left poster sits right beside it, so the "than X?" line is the
     first thing to go when space is this tight. */
  .versus--posters .card__prompt { display: none; }
  .versus--posters .card__calls { flex-direction: column; gap: 6px; width: 100%; }
  .versus--posters .call { justify-content: center; padding: 7px 10px; font-size: 0.82rem; }
  .versus--posters .call svg { width: 15px; height: 15px; }
  .versus--posters .card__badge-circle { width: 44px; height: 44px; }
  .versus--posters .card__badge-circle svg { width: 22px; height: 22px; }
}

/* Segment progress: shows which of the two chains is live and which is
   finished — read-only, not a picker. There's no way to jump segments by
   hand any more, so these are plain spans (not buttons) and never take a
   click, a hover state, or a focus ring. See updateSegmentIndicator() in
   game.js. */
.segmented--ratingguesser {
  max-width: 280px;
  margin: 0 auto 18px;
}
.segmented--ratingguesser .segment-indicator__item {
  padding: 8px 4px;
  border-radius: calc(var(--r-md) - 4px);
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--ink-2);
  text-align: center;
}
.segmented--ratingguesser .segment-indicator__item[aria-current="true"] {
  background: var(--accent);
  color: var(--accent-ink);
}
/* A finished segment keeps showing here (dimmed back to the default look,
   not highlighted, once it's no longer the current one) with a small check
   so "done" reads at a glance even after focus has moved on to the other
   chain. */
.segmented--ratingguesser .segment-indicator__item[data-done="true"]::after {
  content: " \2713";
}

/* Verdict flash: a colour wash over the art plus a big centred badge, so a
   call's outcome reads at a glance instead of needing the small corner pill
   read out. Scoped to .card--poster so Peak Players' cards are untouched. */
.card--poster .card__flash {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 220ms var(--ease);
  pointer-events: none;
}
.card--poster[data-state="correct"] .card__flash {
  opacity: 1;
  background: linear-gradient(rgba(127, 224, 127, 0.05), rgba(10, 40, 16, 0.55));
}
.card--poster[data-state="wrong"] .card__flash {
  opacity: 1;
  background: linear-gradient(rgba(255, 95, 95, 0.06), rgba(40, 6, 6, 0.55));
}

.card--poster .card__badge {
  display: none;
  position: absolute;
  inset: 0;
  place-items: center;
  pointer-events: none;
}
/* Matching only the two real values, not [data-state] alone — renderRight()
   resets the attribute to "" rather than removing it, which the bare
   attribute-presence selector would still treat as a match. */
.card--poster[data-state="correct"] .card__badge,
.card--poster[data-state="wrong"] .card__badge { display: grid; }

.card--poster .card__badge-circle {
  width: 56px; height: 56px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.4);
}
.card--poster[data-state="correct"] .card__badge-circle { background: var(--accent); color: #06140a; }
.card--poster[data-state="wrong"]   .card__badge-circle { background: #ff5f5f; color: #2a0505; }
.card--poster .card__badge-circle svg { width: 28px; height: 28px; }

.card--poster .badge__icon { display: none; }
.card--poster[data-state="correct"] .badge__icon--check { display: block; }
.card--poster[data-state="wrong"]   .badge__icon--cross { display: block; }

/* The pop-and-settle flourish, added by JS only when motion is welcome —
   the badge is already fully visible without it (rules above), so reduced
   motion just loses the bounce, never the feedback itself. */
.card--poster .card__badge-circle.is-in { animation: rating-guesser-badge-pop 480ms var(--ease); }
@keyframes rating-guesser-badge-pop {
  0%   { transform: scale(0.3); opacity: 0; }
  55%  { transform: scale(1.2); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* Segment switch: a snapshot of the old board ("the ghost") slides out and
   fades WHILE the real board — already carrying the new segment's content
   — slides in from the opposite edge, at the same time rather than after.
   See captureSwitchGhost()/playSwitchTransition() in game.js — it clones
   and positions the ghost and sets which way each half moves via the
   --switch-exit-x/--switch-enter-x custom properties; this just styles
   both halves of the motion. `prefers-reduced-motion` skips it entirely
   (no ghost is ever created, so none of this runs).
   #board, not .board — the element's actual class is .versus (the shared
   layout component); .board is a different, unrelated component defined
   above for other games, so an .board.is-switching rule would silently
   never match here. Scoping to the id keeps this specific to Rating
   Guesser rather than reaching into the shared .versus class. */
.board--ghost {
  position: fixed;
  z-index: 55;
  pointer-events: none;
  transition: opacity 650ms ease-in-out, transform 650ms ease-in-out;
}
.board--ghost.is-leaving {
  opacity: 0;
  /* 24px, matching game.js: the board's own centring only guarantees a 32px
     margin on each side (min(1320px, 100vw - 64px)), so anything larger
     overshoots that margin on common desktop widths and runs off-screen. */
  transform: translateX(var(--switch-exit-x, -24px));
}

#board.is-switching { animation: rating-guesser-board-in 650ms ease-in-out; }
@keyframes rating-guesser-board-in {
  0%   { opacity: 0; transform: translateX(var(--switch-enter-x, 24px)); }
  100% { opacity: 1; transform: translateX(0); }
}

/* Advance transition: a floating clone of the answered card ("the ghost")
   slides from the right slot to the left one, WHILE the next question's
   card fades in on the right at the same time — not after, so there's no
   gap where the right side just sits blank. See playAdvanceTransition() in
   game.js — it positions and moves the ghost and times the reveal; this
   just styles both halves of the motion. The two run on matching
   durations/easing so they read as one continuous move, not two steps. */
.card--ghost {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  /* Deliberately not var(--ease) — that curve is tuned for snappy UI
     micro-interactions and front-loads almost all its motion into the
     first fifth of the duration, so a "slide across and cover" read as a
     near-instant snap no matter how long the duration was set to.
     ease-in-out spreads the movement evenly across the full run instead,
     so it's actually visible as an unhurried glide. Matches
     ADVANCE_SLIDE_MS in game.js — that's the fallback timer that
     force-settles the transition if this one somehow never fires. */
  transition: transform 750ms ease-in-out;
}
/* The ghost is standing in for the *known* card it's about to become — the
   correct/wrong chrome from the call that was just answered shouldn't
   carry over with it. */
.card--ghost .card__verdict,
.card--ghost .card__flash,
.card--ghost .card__badge {
  display: none;
}

/* Shared with Peak Players, which runs the same advance transition. */
.card.is-incoming { animation: rating-guesser-card-in 750ms ease-in-out; }
@keyframes rating-guesser-card-in {
  0%   { opacity: 0; transform: translateX(24px); }
  100% { opacity: 1; transform: translateX(0); }
}

/* --- Results, split by segment ---------------------------------------------
   Movies and Series each get their own headed list rather than one merged
   one, since a call's index means "call 3 of 5 in this segment", not a
   position in the combined ten. */
.results__section {
  display: grid;
  gap: 8px;
  width: 100%;
}
.results__section-title {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  text-align: left;
}

/* Rating Guesser's rows carry a rank number AND a poster — one column more
   than the shared .results__list li rule (image, meta, points) the other
   games' results rows use — so this needs its own column here rather than
   changing the shared rule they all rely on. Without it, the unstyled <b>
   rank silently consumed one of the three tracks, so the points pill wrapped
   onto its own row underneath the poster instead of sitting beside it. */
.results__section .results__list li {
  grid-template-columns: auto 54px minmax(0, 1fr) auto;
}
.results__rank {
  color: var(--ink-muted);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  min-width: 1.1em;
}

/* --- How to play ---------------------------------------------------------- */
.howto { display: grid; gap: 11px; font-size: 0.88rem; color: var(--ink-2); }
.howto li { padding-left: 15px; position: relative; line-height: 1.5; }
.howto li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.62em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent);
}
.howto b { color: var(--ink); font-weight: 600; }

/* --- How to play dialog (first visit) ------------------------------------ */
/* A native <dialog> opened with showModal(): the browser handles the focus
   trap, Escape and inert page behind it. All padding lives on the inner
   wrapper so a click that lands on the <dialog> itself is always the
   backdrop (howto.js closes on that). */
.howto-dialog {
  width: min(460px, calc(100vw - 32px));
  max-height: calc(100dvh - 40px);
  margin: auto;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-hi);
  overflow: auto;
  overscroll-behavior: contain;
}
.howto-dialog::backdrop {
  background: var(--bg-veil);
  backdrop-filter: blur(3px);
}
.howto-dialog[open] { animation: howto-in var(--dur-slow) var(--ease); }
@keyframes howto-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
}
.howto-dialog__inner {
  display: grid;
  gap: 18px;
  padding: 18px 20px 20px 22px;
}
.howto-dialog__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.howto-dialog__eyebrow {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
.howto-dialog__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.4rem;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.howto-dialog__head .icon-btn { margin: -4px -6px 0 0; }
.howto-dialog__body { display: grid; gap: 16px; }

/* --- About this game panel ----------------------------------------------- */
/* The crawlable description of each game, in the header's (i) panel. */
.about { gap: 14px; }
.about__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.12rem;
  line-height: 1.3;
}
.about p { font-size: 0.88rem; color: var(--ink-2); line-height: 1.55; }
.about .btn { justify-self: stretch; }
.about__q {
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: -8px;          /* sit the answer right under its question */
}
.about p.about__a { font-size: 0.84rem; }
.about__a + .about__q { margin-top: 6px; }
.about a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.about a:hover { color: var(--accent-hi); }

/* --- More games (results screen) --------------------------------------- */
/* Plain links to the other games, baked in by scripts/seo-build.mjs. Shown
   on the results screen of Cover, Skin and Flag Guesser. */
.more-links {
  justify-self: stretch;
  display: grid;
  gap: 12px;
  margin-top: 10px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  text-align: left;
}
.more-links__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-align: center;
}
.more-links__list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
/* Only three boxes, side by side; the rest stay in the HTML for search
   engines. They stack on narrow phones. */
.more-links__list > li:nth-child(n + 4) { display: none; }
@media (max-width: 479px) {
  .more-links__list { grid-template-columns: 1fr; }
}
.more-links__list a {
  display: grid;
  gap: 3px;
  align-content: start;
  height: 100%;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.more-links__list a:hover { background: var(--surface-hi); border-color: var(--line-hi); }
.more-links__name { font-family: var(--font-display); font-weight: 600; color: var(--ink); }
.more-links__blurb { font-size: 0.78rem; color: var(--ink-muted); line-height: 1.45; }

/* --- Bullseye ------------------------------------------------------------- */
/* One question at a time: category chip, the question, a big editable
   number, then a slider with − / + either side. Locking in drops a marker
   where the real answer sits and shows the ring you hit. */
.bull {
  display: grid;
  gap: 16px;
  max-width: 620px;
  margin-inline: auto;
  text-align: center;
}
.bull__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.bull__cat {
  padding: 6px 13px;
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  color: var(--accent);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.bull__tally { font-size: 0.84rem; color: var(--ink-2); }
.bull__tally b { color: var(--ink); font-family: var(--font-display); font-variant-numeric: tabular-nums; }

.bull__question {
  overflow-wrap: anywhere;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.15rem, 3.6vw, 1.5rem);
  line-height: 1.3;
  letter-spacing: -0.01em;
  min-height: 2.6em;
  display: grid;
  place-items: center;
  text-wrap: balance;
}

.bull__value {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.3rem, 9vw, 3.4rem);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.bull__affix { color: var(--ink-2); font-weight: 700; font-size: 0.55em; }
.bull__affix:empty { display: none; }
.bull__input {
  width: 4ch;               /* game.js sizes this to the number */
  min-width: 2ch;
  max-width: 100%;
  padding: 2px 8px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: center;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.bull__input:hover:not(:disabled) { border-color: var(--line-hi); }
.bull__input:focus { outline: none; border-color: var(--accent); background: var(--surface); }
.bull__input:disabled { color: var(--ink); opacity: 1; }
.bull__hint { margin-top: -10px; font-size: 0.76rem; color: var(--ink-muted); }
.is-revealed .bull__hint { visibility: hidden; }

.bull__slider {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 12px;
}
.bull__nudge {
  width: 40px; height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line-hi);
  color: var(--ink-2);
  touch-action: manipulation;
  user-select: none;
  transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.bull__nudge svg { width: 18px; height: 18px; }
.bull__nudge:hover:not(:disabled) { color: var(--ink); background: var(--surface-hi); }
.bull__nudge:disabled { opacity: 0.35; cursor: default; }

/* The native range input, restyled: a sunken track with an accent fill up
   to the thumb (the fill width comes from --fill, set by game.js). */
.bull__track { position: relative; height: 40px; display: grid; align-items: center; --fill: 50%; }
.bull__track::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 10px;
  border-radius: var(--r-pill);
  background: linear-gradient(to right, var(--accent) var(--fill), var(--surface-hi) var(--fill));
  pointer-events: none;
}
/* Once revealed, the fill from the left goes: the scoring bands take over. */
.is-revealed .bull__track::before { background: var(--surface-hi); }

/* The scoring rings as bands on the track, centred on the answer — the
   closer the ring, the stronger the colour. Clipped to the track's shape. */
.bull__bands {
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  height: 10px;
  margin-top: -5px;
  border-radius: var(--r-pill);
  overflow: hidden;
  pointer-events: none;
}
.bull__bands span { position: absolute; top: 0; bottom: 0; }
.bull__bands span[data-ring="1"] { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.bull__bands span[data-ring="3"] { background: color-mix(in srgb, var(--accent) 34%, transparent); }
.bull__bands span[data-ring="5"] { background: color-mix(in srgb, var(--accent) 56%, transparent); }
.bull__bands span[data-ring="7"] { background: color-mix(in srgb, var(--accent) 85%, transparent); }
.bull__bands:not([hidden]) { animation: bull-bands var(--dur-slow) var(--ease) both; }
@keyframes bull-bands { from { opacity: 0; } }

/* Which values scored what for this question. */
.bull__legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 14px;
  margin-top: -6px;
  font-size: 0.76rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.bull__legend li { display: inline-flex; align-items: center; gap: 6px; }
.bull__legend li::before {
  content: "";
  width: 10px; height: 10px;
  border-radius: 3px;
  background: var(--surface-hi);
}
.bull__legend li[data-ring="10"]::before { background: var(--accent); border-radius: 50%; }
.bull__legend li[data-ring="7"]::before { background: color-mix(in srgb, var(--accent) 85%, transparent); }
.bull__legend li[data-ring="5"]::before { background: color-mix(in srgb, var(--accent) 56%, transparent); }
.bull__legend li[data-ring="3"]::before { background: color-mix(in srgb, var(--accent) 34%, transparent); }
.bull__legend li[data-ring="1"]::before { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.bull__legend b { color: var(--ink-2); font-weight: 600; }
.bull__legend li[data-hit="true"] { color: var(--ink); }
.bull__legend li[data-hit="true"] b { color: var(--accent); }
.bull__range {
  position: relative;
  width: 100%;
  height: 40px;
  margin: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  touch-action: pan-y;
}
.bull__range:disabled { cursor: default; }
.bull__range::-webkit-slider-runnable-track { height: 10px; background: transparent; }
.bull__range::-moz-range-track { height: 10px; background: transparent; }
.bull__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 28px; height: 28px;
  margin-top: -9px;
  border-radius: 50%;
  background: var(--accent);
  border: 4px solid var(--bg);
  box-shadow: 0 0 0 1px var(--accent-line), var(--shadow);
  transition: transform var(--dur) var(--ease);
}
.bull__range::-moz-range-thumb {
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--accent);
  border: 4px solid var(--bg);
  box-shadow: 0 0 0 1px var(--accent-line), var(--shadow);
}
.bull__range:active::-webkit-slider-thumb { transform: scale(1.12); }
.bull__range:disabled::-webkit-slider-thumb { background: var(--ink-2); }
.bull__range:disabled::-moz-range-thumb { background: var(--ink-2); }
.bull__range:focus-visible { outline: none; }
.bull__range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--accent-glow), var(--shadow); }

/* Where the real answer is, dropped onto the track after locking in. */
.bull__marker {
  position: absolute;
  top: 50%;
  width: 22px; height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(circle, var(--accent) 0 3px, transparent 3.5px),
    radial-gradient(circle, transparent 0 6px, var(--accent) 6.5px 8px, transparent 8.5px);
  box-shadow: 0 0 0 3px var(--bg);
  animation: bull-drop var(--dur-slow) var(--ease) both;
}
@keyframes bull-drop {
  from { transform: translateY(-14px) scale(0.6); opacity: 0; }
}

/* Once revealed the slider's handle steps aside for two cleaner marks: the
   target (the answer) and a thin tick (your guess), which reads clearly even
   when the two overlap. */
.is-revealed .bull__range::-webkit-slider-thumb { opacity: 0; }
.is-revealed .bull__range::-moz-range-thumb { opacity: 0; }
.bull__marker--guess {
  width: 4px; height: 26px;
  margin: -13px 0 0 -2px;
  border-radius: 2px;
  background: var(--ink);
  box-shadow: 0 0 0 2px var(--bg);
  z-index: 1;
  animation: bull-fade var(--dur) var(--ease) both;
}
@keyframes bull-fade { from { opacity: 0; } }
/* Dead on: just the target, a touch bigger, with a glow. */
.bull__marker--answer.is-hit {
  width: 28px; height: 28px;
  margin: -14px 0 0 -14px;
  background:
    radial-gradient(circle, var(--accent) 0 4px, transparent 4.5px),
    radial-gradient(circle, transparent 0 7.5px, var(--accent) 8px 10px, transparent 10.5px);
  box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--accent-glow), 0 0 18px var(--accent-glow);
}
.bull__ends {
  display: flex;
  justify-content: space-between;
  margin: -10px 52px 0;
  font-size: 0.74rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

.bull__reveal {
  display: grid;
  gap: 12px;
  justify-items: center;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.bull__reveal.is-in { animation: bull-rise var(--dur-slow) var(--ease); }
@keyframes bull-rise { from { opacity: 0; transform: translateY(8px); } }
.bull__ring { display: flex; align-items: center; gap: 12px; }
.bull__ring-text { display: grid; text-align: left; line-height: 1.25; }
.bull__ring-text b { font-family: var(--font-display); font-size: 1.15rem; }
.bull__ring-text span { font-size: 0.84rem; color: var(--ink-2); }
.bull__ring[data-points="0"] .bull__ring-text b { color: var(--ink-2); }
.bull__ring:not([data-points="0"]) .bull__ring-text b { color: var(--accent); }

/* The little target: every ring outlined, the one you hit filled. */
.target { width: 44px; height: 44px; flex: none; }
.target circle { fill: var(--surface-sunk); stroke: var(--line-hi); stroke-width: 1.2; }
.bull__ring[data-points="1"]  .target circle[data-ring="1"],
.bull__ring[data-points="3"]  .target circle[data-ring="3"],
.bull__ring[data-points="5"]  .target circle[data-ring="5"],
.bull__ring[data-points="7"]  .target circle[data-ring="7"],
.bull__ring[data-points="10"] .target circle[data-ring="10"] { fill: var(--accent); stroke: var(--accent); }

.bull__answer { font-size: 0.95rem; color: var(--ink-2); }
.bull__answer b { color: var(--ink); font-family: var(--font-display); }
.bull__note { font-size: 0.86rem; color: var(--ink-muted); max-width: 46ch; overflow-wrap: anywhere; }

/* Progress dots: filled when a question scored, hollow-muted when missed. */
.bull .roundline__dots i[data-on="hit"]    { background: var(--accent); }
.bull .roundline__dots i[data-on="missed"] { background: transparent; box-shadow: inset 0 0 0 1.5px var(--ink-muted); }
.bull .roundline__dots i[data-on="current"] { background: transparent; box-shadow: inset 0 0 0 2px var(--accent); }
.bull .roundline { margin-top: 4px; }

/* Results rows: number, category + question + your answer, points. */
.bull-results li { grid-template-columns: auto minmax(0, 1fr) auto; }
.bull-results__cat {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
}

@media (max-width: 480px) {
  .bull__slider { gap: 8px; }
  .bull__nudge { width: 44px; height: 44px; }
  .bull__ends { margin-inline: 52px; }
}

/* ==========================================================================
   City Guesser
   --------------------------------------------------------------------------
   A screen-sized photo and map board. After a guess, the map shows both
   pins joined by a dashed line beside the result and next action.
   ========================================================================== */
.city {
  display: grid;
  gap: 14px;
  max-width: 900px;
  margin-inline: auto;
}
.city__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.city__kind {
  padding: 6px 13px;
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  color: var(--accent);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.city__tally { font-size: 0.84rem; color: var(--ink-2); }
.city__tally b { color: var(--ink); font-family: var(--font-display); font-variant-numeric: tabular-nums; }

/* Photos stay whole in a fixed frame. A soft copy fills any spare space.
   viewer.js zooms and pans the foreground image with a transform. */
.city__photo {
  position: relative;
  margin: 0;
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background: #0b0c0b;
  overflow: hidden;
  aspect-ratio: 3 / 2;
  max-height: 62vh;
  width: 100%;
  user-select: none;
  -webkit-user-select: none;
}
.city__stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  touch-action: pan-y;          /* vertical page scroll still works; sideways is ours */
  cursor: zoom-in;
}
.city__photo.is-zoomed .city__stage { touch-action: none; cursor: grab; }
.city__photo.is-zoomed .city__stage:active { cursor: grabbing; }
.city__stage .city__backdrop {
  position: absolute;
  inset: -24px;
  width: calc(100% + 48px);
  height: calc(100% + 48px);
  object-fit: cover;
  filter: blur(20px) brightness(.4) saturate(.8);
  pointer-events: none;
  -webkit-user-drag: none;
}
.city__stage img[data-img] {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform-origin: 0 0;
  transition: transform 0.22s var(--ease), opacity var(--dur) var(--ease);
  -webkit-user-drag: none;
  pointer-events: none;
}
.city__photo.is-loading img[data-img] { opacity: 0; }
.city__photo.is-loading .city__stage::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, 0.05) 50%, transparent 70%) 0 0 / 200% 100%;
  animation: city-shimmer 1.4s linear infinite;
}
@keyframes city-shimmer { to { background-position: -200% 0; } }

/* Controls sit on the photo in dark glass so they read on any image. */
.city__nav, .city__tool {
  display: grid;
  place-items: center;
  border: 0;
  color: #fff;
  background: rgba(0, 0, 0, 0.5);
  cursor: pointer;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  transition: background var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.city__nav:hover, .city__tool:hover:not(:disabled) { background: rgba(0, 0, 0, 0.72); }
.city__tool:disabled { opacity: 0.4; cursor: default; }
.city__nav svg, .city__tool svg { width: 20px; height: 20px; }
.city__nav {
  position: absolute;
  top: 50%;
  width: 42px;
  height: 42px;
  margin-top: -21px;
  border-radius: 50%;
  z-index: 2;
}
.city__nav--prev { left: 10px; }
.city__nav--next { right: 10px; }
.city__tools {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  gap: 6px;
  z-index: 2;
}
.city__tool { width: 36px; height: 36px; border-radius: var(--r-md); }
.city__counter {
  position: absolute;
  top: 12px;
  left: 12px;
  padding: 4px 9px;
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  z-index: 2;
  pointer-events: none;
}
.city__dots {
  position: absolute;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
  z-index: 2;
}
.city__dots button {
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.city__dots button::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.5);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
  transition: background var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.city__dots button[aria-current="true"]::before { background: #fff; transform: scale(1.25); }
.city__dots[hidden], .city__nav[hidden], .city__counter[hidden] { display: none; }
@media (pointer: coarse) {
  .city__nav { width: 38px; height: 38px; margin-top: -19px; }
}

.city__credit {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 16px 12px 6px;
  font-size: 0.7rem;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.78);
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  z-index: 1;
}
.city__credit a { color: inherit; text-decoration: underline; text-underline-offset: 2px; pointer-events: auto; }

/* The map is an overlay while guessing, then joins the result alongside the photo. */
.city__play { position: relative; display: grid; gap: 12px; }
.city__mapwrap { position: relative; display: grid; gap: 10px; }
.city__mapbox { position: relative; }
.city__map {
  height: clamp(280px, 46vh, 480px);
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background: #e9eef0;
  overflow: hidden;
  isolation: isolate;
}
.city__hint {
  position: absolute;
  left: 50%;
  top: 12px;
  transform: translateX(-50%);
  padding: 6px 12px;
  border-radius: var(--r-pill);
  background: rgba(20, 24, 20, 0.78);
  color: #fff;
  font-size: 0.8rem;
  pointer-events: none;
  z-index: 5;
  white-space: nowrap;
}
.city__pin-map {
  position: absolute;
  left: 8px;
  top: 8px;
  z-index: 5;
  display: none;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 0;
  border-radius: 8px;
  background: rgba(20, 24, 20, 0.7);
  color: #fff;
  cursor: pointer;
}
.city__pin-map svg { width: 16px; height: 16px; }
.city__pin-map[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); }
.city__map .maplibregl-canvas { cursor: crosshair; }
.city.is-revealed .city__map .maplibregl-canvas { cursor: grab; }
.city__map .maplibregl-ctrl-attrib { font-size: 10px; }
.city__map .maplibregl-ctrl-group { border-radius: var(--r-md); }

/* Hints */
.city__hints { display: flex; flex-wrap: wrap; gap: 8px; }
.city__hints[hidden] { display: none; }
.city-hint {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 6px 12px;
  border-radius: var(--r-pill);
  border: 1px dashed var(--line-hi);
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: 0.84rem;
}
button.city-hint { cursor: pointer; transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease); }
button.city-hint:hover { border-color: var(--accent-line); color: var(--ink); }
.city-hint__name { font-weight: 600; }
.city-hint__cost { color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.city-hint.is-shown { border-style: solid; border-color: var(--line); background: var(--surface); color: var(--ink); }
.city-hint.is-used { border-color: var(--accent-line); background: var(--accent-soft); }
.city-hint.is-shown .city-hint__name { color: var(--ink-muted); font-weight: 500; }
.city-hint__value { font-weight: 600; overflow-wrap: anywhere; }
.city-hint__value img { display: block; height: 22px; width: auto; border-radius: 3px; box-shadow: 0 0 0 1px var(--line); }
.city.is-revealed .city-hint:not(.is-used) { opacity: 0.75; }
.city__cost { font-size: 0.84rem; color: var(--ink-muted); margin-top: -4px; }
.city__guess:disabled { opacity: 1; background: var(--surface-sunk); color: var(--ink-muted); border: 1px solid var(--line); }

/* Pins — shared by both map engines. The element sits exactly on the
   point; the dot is centred on it. */
.city-pin { position: relative; width: 0; height: 0; }
.city-pin span {
  position: absolute;
  left: -9px;
  top: -9px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 3px solid #fff;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.45);
}
.city-pin--guess span { background: var(--accent); }
.city-pin--answer span {
  left: -11px;
  top: -11px;
  width: 22px;
  height: 22px;
  background: #fff;
  border-color: #111;
  box-shadow: 0 0 0 3px #fff, 0 1px 8px rgba(0, 0, 0, 0.5);
}
.city-pin--answer span::after {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  background: #111;
}

/* Score card */
.city__reveal {
  display: grid;
  gap: 8px;
  justify-items: center;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  text-align: center;
}
.city__reveal.is-in { animation: bull-rise var(--dur-slow) var(--ease); }
.city__reaction {
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
}
.city__answer {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.2rem, 4vw, 1.55rem);
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.city__distance { color: var(--ink-2); font-size: 0.92rem; }
.city__points { display: flex; align-items: baseline; gap: 6px; font-variant-numeric: tabular-nums; }
.city__points b { font-family: var(--font-display); font-size: 1.9rem; font-weight: 800; }
.city__points span { color: var(--ink-muted); font-size: 0.9rem; }
.city__bar {
  --fill: 0;
  width: min(100%, 380px);
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--surface-sunk);
  overflow: hidden;
  margin-bottom: 6px;
}
.city__bar i {
  display: block;
  height: 100%;
  width: 100%;
  border-radius: inherit;
  background: var(--accent);
  transform-origin: left;
  transform: scaleX(var(--fill));
  transition: transform 0.9s var(--ease);
}

.city .roundline { margin-top: 2px; }
.city .roundline__dots i[data-on="hit"]    { background: var(--accent); }
.city .roundline__dots i[data-on="missed"] { background: transparent; box-shadow: inset 0 0 0 1.5px var(--ink-muted); }
.city .roundline__dots i[data-on="current"] { background: transparent; box-shadow: inset 0 0 0 2px var(--accent); }

/* Results rows: photo thumb, round type + city + distance, points. */
.city-results li { grid-template-columns: auto minmax(0, 1fr) auto; }
.city-results img { width: 64px; height: 44px; object-fit: cover; }
.city-results__kind {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
}

/* City Guesser fills the viewport. The compact square map overlays the photo
   while guessing; after a guess it shares a side panel with the result. */
.city-page {
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}
.city-page .game-main {
  max-width: none;
  padding-inline: clamp(14px, 2vw, 36px);
}
.city-page .game-main {
  flex: 1;
  min-height: 0;
  padding-block: clamp(10px, 1.5dvh, 22px);
}
.city-page .city {
  width: 100%;
  height: 100%;
  max-width: none;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(10px, 1.4dvh, 20px);
}
.city-page .city__top {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  min-height: 42px;
  gap: clamp(10px, 1.5vw, 24px);
}
.city-page .city__kind { padding: 8px 16px; }
.city-page .city__tally { font-size: 1rem; }
.city-page .city__hints { justify-content: center; gap: 10px; min-width: 0; }
.city-page .city-hint { min-height: 40px; padding: 7px 12px; }
.city-page .city__play {
  --city-photo-width: 65%;
  --city-map-inset: clamp(12px, 1.4vw, 22px);
  --city-map-gap: 14px;
  display: block;
  position: relative;
  min-height: 0;
}
.city-page .city__photo {
  height: 100%;
  min-height: 0;
  max-height: none;
  aspect-ratio: auto;
}
.city-page .city:not(.is-revealed) .city__photo {
  width: var(--city-photo-width);
  height: 100%;
}
.city-page .city__mapwrap {
  position: absolute;
  right: var(--city-map-inset);
  bottom: clamp(12px, 1.4vw, 22px);
  z-index: 6;
  display: grid;
  grid-template-rows: auto auto;
  width: min(300px, 26vw, calc(100dvh - 250px), calc(100% - var(--city-photo-width) - var(--city-map-inset) - var(--city-map-gap)));
  min-height: 0;
  gap: 6px;
  opacity: .9;
  transition: width .2s var(--ease), opacity .2s var(--ease);
}
.city-page .city__mapwrap:has(:focus-visible),
.city-page .city__mapwrap.is-large {
  width: min(810px, 47vw, calc(100dvh - 240px), calc(100% - var(--city-photo-width) - var(--city-map-inset) - var(--city-map-gap)));
  opacity: 1;
}
@media (hover: hover) {
  .city-page .city__mapwrap:hover {
    width: min(810px, 47vw, calc(100dvh - 240px), calc(100% - var(--city-photo-width) - var(--city-map-inset) - var(--city-map-gap)));
    opacity: 1;
  }
}
.city-page .city__mapwrap.no-transition { transition: none; }
.city-page .city__mapbox { min-height: 0; min-width: 0; aspect-ratio: 1; }
.city-page .city__map {
  width: 100%;
  height: 100%;
  min-height: 0;
  box-shadow: none;
}
.city-page .city__guess { font-size: .85rem; }
.city-page .city__guess,
.city-page .city__reveal .btn { min-height: 48px; }
.city-page .city__pin-map { display: grid; }
.city-page .city__credit { right: 0; }
.city-page .city__dots { left: 50%; }
.city-page .city__nav,
.city-page .city.is-revealed .city__nav { top: 50%; }
.city-page .city.is-revealed .city__play {
  display: grid;
  grid-template-columns: minmax(0, 1.8fr) minmax(300px, .82fr);
  gap: clamp(14px, 1.8vw, 30px);
  min-height: 0;
}
.city-page .city.is-revealed .city__photo { width: 100%; }
.city-page .city.is-revealed .city__mapwrap {
  position: relative;
  inset: auto;
  z-index: auto;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  min-width: 0;
  opacity: 1;
  gap: clamp(8px, 1dvh, 14px);
  grid-template-rows: minmax(0, .95fr) minmax(0, 1.05fr);
}
.city-page .city.is-revealed .city__mapbox { aspect-ratio: auto; }
.city-page .city.is-revealed .city__pin-map { display: none; }
.city-page .city__reveal {
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: clamp(2px, .5dvh, 8px);
  padding: clamp(10px, 1.3dvh, 20px);
}
.city-page .city__answer { font-size: clamp(1.1rem, 2.2dvh, 1.65rem); }
.city-page .city__points b { font-size: clamp(1.5rem, 3dvh, 2rem); }
.city-page .city__bar { height: 6px; margin-bottom: 0; }
.city-page .city__reveal .btn { flex: 0 0 auto; }
.city-page .roundline { margin: 0; }
.city-page .results { gap: 8px; padding-block: 8px; }
.city-page .results__score { font-size: clamp(2.8rem, 6dvh, 4rem); }
.city-page .results__list { gap: 5px; margin-top: 0; }
.city-page .city-results li { gap: 10px; padding: 6px 10px; }
.city-page .city-results img { width: 54px; height: 38px; }
.city-page .results__actions { margin-top: 0; }

@media (max-width: 899px) {
  .city-page .game-main { padding-inline: 10px; padding-block: 8px 10px; }
  .city-page .city { gap: 8px; }
  .city-page .city:not(.is-revealed) .city__photo { width: 100%; height: 100%; }
  .city-page .city__top {
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto;
    min-height: 0;
    gap: 5px 8px;
  }
  .city-page .city__kind { padding: 5px 10px; justify-self: start; }
  .city-page .city__hints {
    grid-column: 1 / -1;
    grid-row: 2;
    justify-content: flex-start;
    gap: 6px;
  }
  .city-page .city-hint { min-height: 34px; padding: 5px 9px; font-size: .78rem; }
  .city-page .city-hint[data-hint="flag"] { order: 1; }
  .city-page .city-hint[data-hint="phrase"] { order: 2; }
  .city-page .city.is-revealed .city__play {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, .65fr) minmax(0, 1.35fr);
    gap: 8px;
  }
  .city-page .city__mapwrap {
    right: 8px;
    bottom: 8px;
    width: min(190px, 46vw, calc(100dvh - 220px));
  }
  .city-page .city__mapwrap:has(:focus-visible),
  .city-page .city__mapwrap.is-large {
    width: min(360px, calc(100vw - 40px), calc(100dvh - 240px));
  }
  .city-page .city.is-revealed .city__mapwrap {
    grid-template-rows: minmax(0, .95fr) minmax(0, 1.05fr);
  }
  .city-page .city__guess,
  .city-page .city__reveal .btn { min-height: 40px; }
  .city-page .city__reveal { padding: 8px 10px; gap: 2px; }
  .city-page .city__reaction { font-size: .65rem; }
  .city-page .city__answer { font-size: 1.1rem; }
  .city-page .city__distance,
  .city-page .city__cost { font-size: .78rem; }
  .city-page .city__points b { font-size: 1.45rem; }
  .city-page .city__bar { display: none; }
  .city-page .city__hint {
    left: auto;
    right: 42px;
    top: 7px;
    transform: none;
    max-width: calc(100% - 85px);
    padding: 4px 7px;
    font-size: .68rem;
    white-space: normal;
    text-align: center;
  }
  .city-page .devbar { left: 6px; bottom: 2px; transform: scale(.72); transform-origin: left bottom; }
}
@media (max-width: 899px) and (hover: hover) {
  .city-page .city__mapwrap:hover {
    width: min(360px, calc(100vw - 40px), calc(100dvh - 240px));
  }
}
@media (max-width: 899px) and (max-height: 720px) {
  .city-page .city.is-revealed .city__play {
    grid-template-rows: minmax(0, .68fr) minmax(0, 1.32fr);
  }
  .city-page .city.is-revealed .city__mapwrap {
    grid-template-rows: minmax(0, .85fr) minmax(0, 1.15fr);
  }
  .city-page .city__reveal {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    justify-items: start;
    text-align: left;
    gap: 2px 8px;
    padding: 7px 10px;
  }
  .city-page .city__reaction { grid-column: 1 / -1; }
  .city-page .city__answer { grid-column: 1 / -1; }
  .city-page .city__points { grid-column: 2; grid-row: 3; }
  .city-page .city__cost { grid-column: 1 / -1; margin: 0; }
  .city-page .city__reveal .btn { grid-column: 1 / -1; width: 100%; }
  .city-page .city__distance { font-size: .7rem; }
  .city-page .city__points b { font-size: 1.25rem; }
  .city-page .city__points span { font-size: .7rem; }
  .city-page .city__reveal .btn { min-height: 36px; }
  .city-page .city__map .maplibregl-ctrl-attrib { font-size: 8px; }
  .city-page .results { gap: 5px; }
  .city-page .results__list { gap: 4px; }
  .city-page .city-results li { padding: 4px 8px; }
}

/* The final five-round recap may be taller than a small screen. */
.city-page:has(#results-screen:not([hidden])) { height: auto; min-height: 100dvh; overflow: auto; }
.city-page:has(#results-screen:not([hidden])) .game-main { flex: none; }

@media (prefers-reduced-motion: reduce) {
  .city__bar i { transition: none; }
}
[data-motion="reduced"] .city__bar i { transition: none; }
