/* Daily Geo — "Open Sky" visual system.
   A geography game in daylight: sky-blue ocean (#36b2ff) with white pixel
   continents binned from the game's own cities dataset, white cards, deep navy
   ink, compass vermilion for action, amber for standings. Display type is tall
   condensed (scoreboard vernacular); map-label eyebrows are tracked uppercase. */

* { box-sizing: border-box; margin: 0; }

:root {
  --sky: #36b2ff;          /* page: the ocean */
  --well: #0a1626;         /* pano/imagery wells stay dark — imagery needs it */
  --chart: #ffffff;        /* cards, panels */
  --chart-edge: #bfe0f7;   /* rules, borders */
  --paper: #10304f;        /* primary text: deep navy ink */
  --paper-dim: #48719c;    /* secondary text */
  --compass: #e4572e;      /* action, urgency */
  --meridian: #b07d18;     /* highlights, gold ranks (readable on white) */
  --font-display: "Big Shoulders Display", "Arial Narrow", sans-serif;
  --font-body: "Familjen Grotesk", system-ui, sans-serif;
  --font-flavor: "Lora", Georgia, serif;
}

html, body { height: 100%; }
body {
  color: var(--paper);
  font-family: var(--font-body);
  /* The signature: a chunky pixel world map binned from the game's own cities
     dataset (scripts/build-world-pixels.js) — every lit cell is somewhere the
     game could send you. */
  background:
    url("world.svg?v=20260723sb") no-repeat center 38% / min(1500px, 165vmax),
    radial-gradient(1200px 800px at 50% -15%, #5fc3ff 0%, rgba(95, 195, 255, 0) 55%),
    linear-gradient(180deg, #45b9ff 0%, #2aa3f0 100%),
    var(--sky);
}

h1, h2 { font-family: var(--font-display); font-weight: 800; letter-spacing: 0.01em; }
h1 { font-size: 2.6rem; text-transform: uppercase; }
h2 { font-size: 1.7rem; }

/* map-label eyebrow: tracked caps, like a label on a school-atlas map */
.eyebrow {
  font-family: var(--font-body); font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.22em; color: var(--paper-dim);
}

#topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 14px; background: rgba(255, 255, 255, 0.92); backdrop-filter: blur(4px);
  border-bottom: 1px solid var(--chart-edge);
}
#brand {
  font-family: var(--font-display); font-weight: 800; font-size: 1.15rem;
  text-transform: uppercase; letter-spacing: 0.06em; cursor: pointer;
}
#user-box { display: flex; align-items: center; gap: 10px; }
#user-pic { width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--chart-edge); }
.link-btn { background: none; border: none; color: var(--paper-dim); cursor: pointer; text-decoration: underline; font-family: var(--font-body); }
.player-link { cursor: pointer; }
.player-link .player-name:hover { text-decoration: underline; }
.player-link .player-name { color: var(--compass); }
.player-link .player-name::after { content: ' ›'; opacity: .55; }
#admin-link { color: var(--paper-dim); }

.view { min-height: 100vh; padding-top: 56px; }
.hidden { display: none !important; }
.card {
  background: var(--chart);
  border: 1px solid var(--chart-edge);
  border-radius: 12px; padding: 24px;
  max-width: 560px; margin: 18px auto;
  box-shadow: 0 12px 32px rgba(10, 48, 90, 0.22);
}
.center { text-align: center; }
.error { color: var(--compass); margin-top: 12px; }
.huge { font-family: var(--font-display); font-size: 3.4rem; font-weight: 800; margin: 10px 0; }
.big-btn {
  background: var(--compass); color: #fff; border: none; border-radius: 8px;
  font-family: var(--font-body); font-size: 1.05rem; font-weight: 700;
  padding: 12px 28px; cursor: pointer; margin-top: 14px;
}
.big-btn:hover { filter: brightness(1.08); }
.big-btn:disabled { background: #dcebf7; color: #7fa3c4; cursor: not-allowed; filter: none; }
.big-btn:focus-visible, .tab:focus-visible, .link-btn:focus-visible, button:focus-visible {
  outline: 2px solid var(--paper); outline-offset: 2px;
}
#gsi-button { display: flex; justify-content: center; margin-top: 18px; }

/* login flavor line reads like a map annotation */
#view-login .card > p { font-family: var(--font-flavor); font-style: italic; color: var(--paper-dim); }

/* lobby: two-game registry cards, side by side */
#lobby-games { display: flex; flex-wrap: wrap; justify-content: center; gap: 16px; max-width: 1100px; margin: 0 auto 18px; }
.lobby-game { flex: 1 1 260px; max-width: 380px; margin: 0; display: flex; flex-direction: column; }
.lobby-game h2 { margin-bottom: 6px; }
.lobby-game p { flex: 1; color: var(--paper-dim); }
.lobby-game .big-btn { display: block; width: 100%; margin-top: auto; text-decoration: none; text-align: center; }

/* leaderboards — one stacked card per game */
.lobby-board h3 {
  margin-bottom: 12px; font-size: 0.78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.2em; color: var(--paper-dim);
}
.tabs { display: flex; gap: 8px; margin-bottom: 12px; }
.tab {
  background: none; border: 1px solid var(--chart-edge); color: var(--paper-dim);
  border-radius: 6px; padding: 5px 14px; cursor: pointer; font-family: var(--font-body);
  text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.72rem; font-weight: 700;
}
.tab.active { border-color: var(--meridian); color: var(--meridian); }
.board-meta { margin: 2px 0 8px; font-size: 0.85rem; color: #5b7a99; }
.board-meta:empty { display: none; }
.board-table { width: 100%; border-collapse: collapse; }
.board-table th {
  text-align: left; padding: 7px 8px; border-bottom: 1px solid var(--chart-edge);
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.18em; color: var(--paper-dim);
}
.board-table td { text-align: left; padding: 7px 8px; border-bottom: 1px solid #e7f1fa; }
.board-table td.num, .board-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* the avatar-wrap is the sized box so frame overlays can encircle an inset avatar */
.board-table .avatar-wrap { width: 24px; height: 24px; margin-right: 8px; vertical-align: middle; }
.board-table .avatar-wrap img { width: 100%; height: 100%; border-radius: 50%; display: block; object-fit: cover; }

/* round view: full-screen pano in a dark well (imagery needs dark surround).
   dvh = the *visible* viewport height, so the guess/submit button never lands
   under the mobile browser toolbar. min-height:0 is load-bearing: .view sets
   min-height:100vh, and used height = max(height, min-height) — on mobile with
   the toolbar shown 100vh (large viewport) > 100dvh, which silently defeated
   the dvh height and pushed bottom-anchored controls below the visible fold. */
#view-round { padding-top: 0; position: relative; height: 100vh; height: 100dvh; min-height: 0; overflow: hidden; background: var(--well); }
#pano { position: absolute; inset: 0; }
#sat-img { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; pointer-events: none; background: var(--well); }
#sat-error {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 10px; background: var(--well); color: #fff;
}
#hud {
  position: absolute; top: 64px; left: 50%; transform: translateX(-50%); z-index: 20;
  display: flex; gap: 18px; background: rgba(255, 255, 255, 0.93); border: 1px solid var(--chart-edge);
  border-radius: 10px; padding: 8px 18px; color: var(--paper);
  font-family: var(--font-display); font-weight: 700; font-size: 1.2rem;
  letter-spacing: 0.04em; font-variant-numeric: tabular-nums;
}
#hud-timer.low { color: var(--compass); }

#guess-box {
  position: absolute; right: 16px; bottom: 24px; z-index: 20;
  width: 260px; transition: width .15s ease, height .15s ease;
}
#guess-box.pinned { width: min(560px, 90vw); }
#guess-map { width: 100%; height: 180px; border-radius: 10px; border: 2px solid #fff; box-shadow: 0 8px 24px rgba(4, 10, 20, 0.4); }
/* Expanded map: cap against the *visible* viewport so on short screens the box
   still leaves room for the HUD above (~110px) and the button + offset below
   (~90px). Floored at the collapsed 180px so "expanded" never shrinks below
   "collapsed". Plain-vh line first = fallback for browsers without dvh. */
#guess-box.pinned #guess-map { height: min(400px, 60vh); height: min(400px, max(180px, 100dvh - 200px)); }
#guess-collapse {
  position: absolute; top: 8px; left: 8px; z-index: 21; display: none;
  width: 30px; height: 30px; border-radius: 50%; border: none; cursor: pointer;
  background: rgba(255, 255, 255, 0.95); color: var(--paper); font-size: 15px; line-height: 1;
  align-items: center; justify-content: center;
}
#guess-box.pinned #guess-collapse { display: flex; }
@media (hover: hover) {
  #guess-box:hover { width: min(560px, 90vw); }
  #guess-box:hover #guess-map { height: min(400px, 60vh); height: min(400px, max(180px, 100dvh - 200px)); }
  #guess-box:hover #guess-collapse { display: flex; }
}
#guess-btn {
  width: 100%; margin-top: 8px; padding: 12px; border: none; border-radius: 8px;
  background: var(--compass); color: #fff; font-family: var(--font-body);
  font-size: 1rem; font-weight: 700; cursor: pointer;
}
#guess-btn:disabled { background: rgba(255, 255, 255, 0.85); color: #7fa3c4; cursor: not-allowed; }

/* Non-map modes (renderer registry, needsMaps: false): the guess box is just the
   submit button — no mini map, no hover/pin growth. */
#guess-box.maps-off #guess-map, #guess-box.maps-off #guess-collapse { display: none; }
#guess-box.maps-off, #guess-box.maps-off.pinned { width: 260px; }
@media (hover: hover) {
  #guess-box.maps-off:hover { width: 260px; }
}
/* coming-soon note on lobby cards for modes not live server-side yet */
.game-soon-note { color: var(--paper-dim); font-size: 0.85rem; font-style: italic; }

/* ---- Fermi Daily (fermi-renderer.js) ----
   Round stage: one white question card centered in the dark #pano well,
   clear of the HUD above and the shared submit button below. */
.fermi-stage {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  padding: 76px 16px 120px; overflow-y: auto;
}
.fermi-card { max-width: 480px; width: 100%; margin: 0; }
.fermi-question { margin: 6px 0 18px; }
.fermi-input-row { display: flex; align-items: baseline; gap: 10px; }
.fermi-input {
  flex: 1; min-width: 0; padding: 12px; text-align: right;
  font-family: var(--font-body); font-size: 1.35rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--paper); background: #fff;
  border: 2px solid var(--chart-edge); border-radius: 8px;
}
.fermi-input:focus { outline: none; border-color: var(--compass); }
.fermi-unit { color: var(--paper-dim); font-weight: 700; white-space: nowrap; }
.fermi-hint { margin-top: 12px; font-size: 0.85rem; color: var(--paper-dim); font-style: italic; }
/* reveal card body */
.fermi-reveal { margin-top: 6px; }
.fermi-reveal-question { color: var(--paper-dim); }
.fermi-reveal-answer { font-family: var(--font-display); font-size: 2.1rem; font-weight: 800; margin: 6px 0; }
.fermi-reveal-guess { font-variant-numeric: tabular-nums; }
.fermi-reveal-expl { font-family: var(--font-flavor); font-style: italic; color: var(--paper-dim); margin-top: 10px; }
.fermi-reveal-src { font-size: 0.8rem; color: var(--paper-dim); margin-top: 6px; }

/* reveal */
#view-reveal { display: flex; flex-direction: column; height: 100vh; height: 100dvh; min-height: 0; }
#reveal-map { flex: 1; min-height: 0; }
#reveal-card { margin: 12px auto; }
#reveal-points { color: var(--meridian); }
/* Non-map modes: no reveal map — center the card; renderer output fills #reveal-body */
#view-reveal.maps-off #reveal-map { display: none; }
#view-reveal.maps-off { justify-content: center; }
#reveal-body:empty { display: none; }

/* ---------- Timeline (timeline-renderer.js) ---------- */
/* Round stage: a light column floating on the dark round well, cards in the
   card language (white, chart-edge borders), grip + category chip. */
#tl-stage {
  position: absolute; inset: 0; overflow-y: auto;
  padding: 14px 12px 150px; /* room above the floating submit button */
  display: flex; flex-direction: column; align-items: center;
}
#tl-list { list-style: none; padding: 0; margin: 4px 0; width: min(560px, 100%); }
.tl-edge {
  font-family: var(--font-body); font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.22em; color: #9fc4e8;
}
#tl-hint { color: #9fc4e8; font-size: 0.85rem; margin-top: 10px; font-style: italic; }
.tl-card {
  display: flex; align-items: center; gap: 10px;
  background: var(--chart); border: 1px solid var(--chart-edge); border-radius: 10px;
  padding: 10px 12px; margin: 8px 0; cursor: grab; user-select: none;
  -webkit-user-select: none; touch-action: manipulation;
  box-shadow: 0 6px 16px rgba(10, 48, 90, 0.25);
}
.tl-card:active { cursor: grabbing; }
.tl-grip { color: var(--paper-dim); font-size: 1.05rem; flex: none; }
.tl-card-main { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.tl-label { font-weight: 700; }
.tl-blurb { color: var(--paper-dim); font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-cat {
  flex: none; font-size: 0.62rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.14em; color: var(--paper-dim);
  border: 1px solid var(--chart-edge); border-radius: 999px; padding: 3px 8px;
}
.tl-card.tl-dragging { opacity: 0.45; }
.tl-card.tl-armed { border-color: var(--compass); box-shadow: 0 0 0 2px var(--compass); }

/* Reveal: true chronological list with year rail + per-item verdict chip */
#tl-reveal-list { list-style: none; padding: 0; margin: 12px 0 4px; text-align: left; }
.tl-reveal-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid #e7f1fa;
}
.tl-year {
  flex: none; width: 74px; text-align: right;
  font-family: var(--font-display); font-weight: 800; font-size: 1.05rem;
  color: var(--meridian); font-variant-numeric: tabular-nums;
}
.tl-chip {
  flex: none; font-size: 0.72rem; font-weight: 700; border-radius: 999px;
  padding: 3px 8px; font-variant-numeric: tabular-nums;
}
.tl-chip-good { background: #e4f5e8; color: #1c7a3d; }
.tl-chip-mid { background: #fdf3dc; color: var(--meridian); }
.tl-chip-bad { background: #fde7df; color: var(--compass); }

#summary-rounds { text-align: left; margin: 14px auto; max-width: 320px; }
#summary-rounds li { padding: 4px 0; border-bottom: 1px solid #e7f1fa; font-variant-numeric: tabular-nums; }
/* the XP earn moment: gold like every other standing/reward accent */
#summary-xp { color: var(--meridian); font-weight: 700; font-variant-numeric: tabular-nums; }

/* ---- award celebrations (2026-08-01 design) ----
   Two tiers of spectacle: the souvenir overlay below is the centerpiece; this
   summary XP block stays quieter — gold accents, a small count-up, chips that
   pop in as the counter passes them, and a copy of the lobby pass bar. Every
   entrance animation carries its END state on the class itself (opacity/width
   set by the class or by JS), so the global reduced-motion kill switch just
   removes the motion, never the content. The count-up number and bar width are
   JS-driven (game.js renderSummaryAwards) and snap under prefers-reduced-motion. */
#summary-awards { margin: 16px auto 4px; max-width: 340px; }
#awards-count {
  font-family: var(--font-display); font-weight: 800; font-size: 2rem;
  color: var(--meridian); font-variant-numeric: tabular-nums; margin: 2px 0 8px;
}
#awards-chips {
  display: flex; justify-content: center; flex-wrap: wrap; gap: 8px;
  min-height: 26px; margin-bottom: 12px;
}
.awards-chip {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.02em;
  padding: 3px 10px; border: 1px solid var(--meridian); border-radius: 999px;
  color: var(--meridian); font-variant-numeric: tabular-nums;
  opacity: 0; /* revealed by .in as the counter passes this chip's XP */
}
.awards-chip.in { opacity: 1; animation: chip-in .25s ease; }
@keyframes chip-in { from { opacity: 0; transform: translateY(6px) scale(.85); } }
#awards-pass-row { display: flex; align-items: center; gap: 10px; }
#awards-tier { font-weight: 700; color: var(--meridian); white-space: nowrap; display: inline-block; }
#awards-tier.tier-pop { animation: tier-pop .5s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes tier-pop { 40% { transform: scale(1.4); } }
/* same geometry/palette as the lobby's #pass-bar; width is JS-set per frame */
#awards-bar { flex: 1; height: 10px; border-radius: 5px; background: #dceefb; overflow: hidden; }
#awards-bar-fill { height: 100%; border-radius: 5px; background: var(--compass); width: 0; }
#awards-reward {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-top: 14px; color: var(--paper); font-weight: 700;
}
#awards-reward.reward-in { animation: reward-in .35s ease; }
@keyframes reward-in { from { opacity: 0; transform: translateX(18px); } }
/* frame rewards reuse the pass rail's framed-dot preview, sized down */
#awards-reward .awards-frame-preview { width: 52px; height: 52px; }
/* furniture/style reward art: house-art sprites sit in a small dark well —
   they're authored against the night-cabin scenes, not white cards */
.awards-art-well {
  display: flex; align-items: flex-end; justify-content: center;
  width: 52px; height: 56px; padding: 4px; border-radius: 8px; background: var(--well);
}
.awards-art-well svg { display: block; width: 100%; height: 100%; }

/* ---- souvenir reveal overlay (the centerpiece moment) ----
   Dimmed navy backdrop over the whole viewport (fixed: sits above the reveal
   map, card, and topbar); the souvenir's own house-art sprite drawn LARGE over
   a sparkle burst of pixel squares, with passport-stamp wording beneath. One
   tap anywhere removes the whole overlay (game.js), so it never blocks the
   next-round button beyond that tap. Sparkles are opacity-0 at rest and only
   ever shown BY their animation — under reduced motion they simply never
   appear (decoration, not content); the art/text carry their end states. */
#souvenir-overlay {
  position: fixed; inset: 0; z-index: 70; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  /* near-opaque at the edges, a touch lighter behind the sprite: the vignette
     keeps busy/light reveal maps from competing with the centerpiece */
  background: radial-gradient(ellipse 90% 70% at 50% 42%,
    rgba(10, 22, 38, 0.87) 0%, rgba(7, 17, 31, 0.96) 72%);
  animation: souvenir-dim .25s ease;
}
@keyframes souvenir-dim { from { opacity: 0; } }
.souvenir-stage { position: relative; text-align: center; padding: 24px; }
.souvenir-art {
  width: min(224px, 46vw); height: min(272px, 56vw); margin: 0 auto;
  animation: souvenir-pop .45s cubic-bezier(.2, 1.4, .4, 1);
  transform-origin: 50% 90%;
}
.souvenir-art svg { display: block; width: 100%; height: 100%; }
@keyframes souvenir-pop { from { opacity: 0; transform: scale(.3); } 65% { transform: scale(1.07); } }
/* stamped wording: the passport-stamp language — tracked caps in a dashed
   roundel-style pill, gold on navy, set at a slight rubber-stamp tilt */
.souvenir-stamp {
  display: inline-block; margin: 18px 0 10px;
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.24em;
  color: #ffd98a; border: 2px dashed rgba(255, 217, 138, 0.75); border-radius: 999px;
  padding: 6px 16px 6px 19px; /* extra left: optically recentres the tracked caps */
  transform: rotate(-4deg);
  animation: souvenir-stamp-in .3s cubic-bezier(.2, 1.4, .4, 1) .18s backwards;
}
@keyframes souvenir-stamp-in { from { opacity: 0; transform: rotate(-4deg) scale(1.6); } }
.souvenir-label {
  font-family: var(--font-display); font-weight: 800; font-size: 2rem;
  text-transform: uppercase; letter-spacing: 0.02em; color: #fff; margin: 0;
  animation: souvenir-label-in .3s ease .26s backwards;
}
@keyframes souvenir-label-in { from { opacity: 0; transform: translateY(8px); } }
.souvenir-hint {
  margin: 14px 0 0; font-size: 0.8rem; color: #9db8cf;
  animation: souvenir-label-in .3s ease .9s backwards;
}
/* sparkle burst: absolutely-positioned pixel squares thrown outward from
   behind the sprite (per-spark --dx/--dy/--spark-delay set in game.js).
   fill-mode both keeps them hidden before their delay and after the burst. */
.souvenir-spark {
  position: absolute; left: 50%; top: 42%; width: 10px; height: 10px;
  background: #ffd98a; opacity: 0; pointer-events: none;
  animation: spark-burst .95s ease-out var(--spark-delay, 0s) both;
}
.souvenir-spark:nth-child(3n) { width: 7px; height: 7px; background: #fff; }
.souvenir-spark:nth-child(4n) { background: #e4572e; }
@keyframes spark-burst {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(.4); }
  15% { opacity: 1; }
  100% { opacity: 0; transform: translate(calc(-50% + var(--dx, 0px)), calc(-50% + var(--dy, 0px))) scale(1); }
}

@media (max-width: 640px) {
  #guess-box { width: 46vw; }
  #guess-box.pinned { width: 94vw; right: 3vw; }
}

#view-review { position: relative; height: 100vh; height: 100dvh; min-height: 0; }
#review-map { position: absolute; inset: 0; }
#review-tile {
  position: absolute; left: 16px; top: 64px; z-index: 10;
  width: 160px; height: 160px; object-fit: cover; border-radius: 10px;
  border: 2px solid #fff; background: var(--well); box-shadow: 0 8px 24px rgba(4, 10, 20, 0.3);
}
#review-panel {
  position: absolute; left: 50%; bottom: 20px; transform: translateX(-50%);
  width: min(720px, 94vw); max-height: 46vh; overflow: auto; margin: 0; z-index: 5;
  background: rgba(255, 255, 255, 0.94); backdrop-filter: blur(3px);
  border: 1px solid var(--chart-edge); border-radius: 12px;
}
#review-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
#review-table { width: 100%; border-collapse: collapse; }
#review-table th, #review-table td { padding: 6px 8px; border-bottom: 1px solid #e7f1fa; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
#review-table th { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.14em; color: var(--paper-dim); }
#review-table th.player, #review-table td.player { text-align: left; }
#review-table th.round { cursor: pointer; user-select: none; }
#review-table th.round.sel, #review-table td.sel { background: rgba(176, 125, 24, 0.12); color: var(--meridian); }
#review-table .swatch { display: inline-block; width: 20px; height: 20px; border-radius: 50%; margin-right: 8px; vertical-align: middle; border: 2px solid; object-fit: cover; }
.review-pin { width: 34px; height: 34px; border-radius: 50%; border: 3px solid #fff; overflow: hidden; background: #5b7b9c; display: flex; align-items: center; justify-content: center; color: #fff; font: 600 14px var(--font-body); }
.review-pin img { width: 100%; height: 100%; object-fit: cover; }
.review-pin.actual { border-color: #fff; background: none; font-size: 22px; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* battlepass */
#pass-card { cursor: pointer; }
#pass-card-head { display: flex; justify-content: space-between; align-items: baseline; }
#pass-tier-badge { font-weight: 700; color: var(--meridian); }
#pass-bar { height: 10px; border-radius: 5px; background: #dceefb; overflow: hidden; margin: 8px 0 4px; }
#pass-bar-fill { height: 100%; border-radius: 5px; background: var(--compass); transition: width .3s ease; }
#pass-card-foot { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
#pass-bar-label { margin: 0; font-size: 0.85rem; color: #5b7a99; }
/* tap affordance: cursor:pointer means nothing on a phone — say it in words,
   link-styled to match the "Yesterday's recap →" pattern */
#pass-card-hint { font-size: 0.85rem; color: var(--paper-dim); text-decoration: underline; white-space: nowrap; }

/* street entry point (Task 8b): a clickable lobby card, same "whole card
   taps, hint text names the action" language as #pass-card above. */
#street-card { cursor: pointer; }
#street-card-head { display: flex; justify-content: space-between; align-items: baseline; }
#street-card-hint { font-size: 0.85rem; color: var(--paper-dim); text-decoration: underline; white-space: nowrap; }
#street-card-blurb { margin: 4px 0 0; font-size: 0.85rem; color: #5b7a99; }
#view-pass { padding-top: 72px; }
#pass-header { display: flex; justify-content: space-between; align-items: baseline; }
/* how-XP-works flavor line: deliberately number-free (season configs own the
   amounts), set like the login card's map-annotation voice */
#pass-howto { font-family: var(--font-flavor); font-style: italic; color: var(--paper-dim); font-size: 0.92rem; margin-top: 6px; }
#pass-tiers { list-style: none; margin: 12px 0 0; padding: 0; }
.pass-tier { display: flex; align-items: center; gap: 12px; padding: 8px 4px; border-bottom: 1px solid #e3f0fa; }
.pass-tier.locked { opacity: 0.45; }
.pass-tier-no { width: 2em; font-weight: 800; text-align: right; }
.pass-reward { flex: 1; }
.pass-reward-none { color: #9db8cf; }
.pass-reward-mystery { opacity: 0.75; letter-spacing: 0.08em; }
.pass-equipped { color: var(--meridian); font-weight: 700; }
/* furniture/style reward rows: unlocked automatically, no Equip button — see
   showPass's reward-type check in game.js */
.pass-reward-note { color: #9db8cf; font-style: italic; font-size: 0.88rem; }

/* cosmetic frames — engraved-passport SVG overlays (art lives in game.js FRAMES).
   Geometry contract: the frame is a 48×48 viewBox whose avatar circle is r15 at
   the centre, so a framed wrap shows the avatar at 62.5% size, inset 18.75%,
   with the overlay filling the whole box (ring + motif, transparent centre). */
.avatar-wrap { position: relative; display: inline-block; }
.avatar-wrap .frame-overlay { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.avatar-wrap .frame-overlay svg { display: block; width: 100%; height: 100%; }
.board-table .avatar-wrap.framed img { position: absolute; inset: 18.75%; width: 62.5%; height: 62.5%; }
/* review table: the wrap grows to frame size; the colour swatch stays the avatar */
#review-table .avatar-wrap { width: 24px; height: 24px; margin-right: 8px; vertical-align: middle; }
#review-table .avatar-wrap .swatch { position: absolute; inset: 2px; margin: 0; }
#review-table .avatar-wrap.framed .swatch { inset: 18.75%; width: 62.5%; height: 62.5%; }
/* review map: framed pins keep the player-colour border on an inset core circle */
.review-pin.framed { position: relative; width: 40px; height: 40px;
  border: none; background: none; overflow: visible; }
.review-pin.framed .frame-overlay { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.review-pin.framed .frame-overlay svg { display: block; width: 100%; height: 100%; }
.review-pin-core { position: absolute; inset: 18.75%; border-radius: 50%; border: 2px solid;
  overflow: hidden; background: #5b7b9c; display: flex; align-items: center; justify-content: center;
  font-size: 11px; }
.review-pin-core img { width: 100%; height: 100%; object-fit: cover; }
/* battlepass tier rail: frame art around a neutral avatar dot */
.pass-reward { display: flex; align-items: center; gap: 12px; }
.pass-frame-preview { width: 96px; height: 96px; flex: none; }
/* furniture tier rewards: the overlay's dark well, sized to the frame previews */
.pass-art-well { width: 96px; height: 96px; flex: none; }
.pass-ava-dot { position: absolute; inset: 18.75%; border-radius: 50%; background: #d7e9f7; }

/* passport: cover + one page per season, reachable only programmatically
   until Task 6 wires openers (board-row taps, "My passport" button). */
#view-passport { padding-top: 72px; }
#passport-panel { max-width: 480px; }
#passport-header { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
#passport-nav { display: flex; justify-content: space-between; align-items: center; margin-top: 16px; }
#passport-page-no { font-size: 0.8rem; color: var(--paper-dim); font-variant-numeric: tabular-nums; }
#passport-prev:disabled, #passport-next:disabled { opacity: 0.35; cursor: default; text-decoration: none; }

@keyframes passport-fade { from { opacity: 0; } to { opacity: 1; } }
#passport-page.passport-fade { animation: passport-fade .2s ease; }

/* cover: navy "passport booklet" card; silver/gold border variants track the
   traveler's highest season tier (see server/pass/passport.js cover.level). */
.passport-cover {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  background: var(--paper); color: #fff; border-radius: 10px;
  padding: 36px 20px; text-align: center;
  border: 4px solid #6f8aa6;
}
.passport-cover.cover-silver { border-color: #d3dde5; }
.passport-cover.cover-gold { border-color: var(--meridian); }
.passport-cover-avatar { width: 96px; height: 96px; margin-bottom: 4px; }
.passport-cover-avatar img,
.passport-cover-avatar .passport-cover-avatar-fallback {
  width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block;
}
.passport-cover-avatar.framed img,
.passport-cover-avatar.framed .passport-cover-avatar-fallback {
  position: absolute; inset: 18.75%; width: 62.5%; height: 62.5%;
}
.passport-cover-avatar-fallback {
  background: #365a7d; display: flex; align-items: center; justify-content: center;
  color: #fff; font-family: var(--font-display); font-size: 2.1rem;
}
.passport-cover-name { font-family: var(--font-display); font-size: 1.4rem; text-transform: uppercase; letter-spacing: 0.03em; margin: 0; }
.passport-crest-row { font-size: 1.1rem; letter-spacing: 0.14em; margin: 0; }
/* The passport door (Task 8b): opens the same player's basecamp. */
.passport-door-btn { margin-top: 4px; }
.passport-cover-seasons { color: #b9cbde; font-size: 0.85rem; margin: 0; }

/* season pages: dashed-border roundels with a slight per-stamp rotation for
   the rubber-stamp feel (a static transform, not an animation — no
   prefers-reduced-motion interaction needed beyond the page fade above). */
.passport-season-name { margin-bottom: 4px; }
.passport-days-traveled { color: var(--paper-dim); margin-bottom: 16px; }
.passport-stamps { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }
.passport-no-stamps { color: var(--paper-dim); font-style: italic; }
.passport-stamp {
  width: 92px; height: 92px; border-radius: 50%; border: 2px dashed var(--paper-dim);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; padding: 6px;
}
.passport-stamp-icon { font-size: 1.5rem; line-height: 1; }
.passport-stamp-label { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--paper-dim); text-align: center; }
.passport-stamp-date { font-size: 0.58rem; color: #9db8cf; font-variant-numeric: tabular-nums; }
.passport-stamps .passport-stamp:nth-child(3n+1) { transform: rotate(-6deg); }
.passport-stamps .passport-stamp:nth-child(3n+2) { transform: rotate(4deg); }
.passport-stamps .passport-stamp:nth-child(3n) { transform: rotate(-3deg); }

/* house: Stardew-style 3/4 basecamp room + exterior facade (Battlepass Phase
   C). Task 9b: real pixel art — static/house-art.js paints the firelit
   log-cabin room / cabin-front backdrops and every furniture/souvenir sprite
   as crisp SVG at an exact 3x pixel scale; the styles below only place,
   frame, and tint that art.

   Mobile: #house-room is a fixed 480px-wide grid (10 cols × CELL 48px),
   which can exceed a phone viewport (~390px). Chose an overflow-x scroll
   container over a CSS scale() transform: game.js's screenToCell (reused
   as-is by the editor) maps raw pointer px 1:1 to grid cells, and a scale()
   would need every pointer coordinate corrected by the inverse scale factor
   first. Scrolling keeps that math untouched; game.js centers the initial
   scrollLeft so the room doesn't open cropped to its left edge. */
#house-panel { max-width: 720px; }
#house-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }
#house-scene-toggle { display: flex; gap: 4px; }
#house-size-badge {
  font-family: var(--font-display); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--meridian); border: 1px solid var(--meridian);
  border-radius: 999px; padding: 2px 9px;
}
.house-tab {
  font-family: var(--font-display); font-size: 0.78rem; font-weight: 700;
  padding: 5px 12px; border-radius: 999px; border: 1px solid var(--compass);
  background: transparent; color: var(--compass); cursor: pointer;
}
.house-tab.active { background: var(--compass); color: #fff; }
/* The viewport is the dark "well" the painted scene sits in — same imagery
   surround the reveal map and panorama use. */
#house-viewport {
  overflow-x: auto; overflow-y: hidden; padding: 10px 0 10px;
  -webkit-overflow-scrolling: touch;
  background: var(--well); border-radius: 12px;
}
/* .house-room (not #house-room): shared by the house view's own room AND
   each read-only facade card on the street screen (Task 8b) — both build
   through the same renderHouseShell/renderHouseExterior functions. */
.house-room { position: relative; margin: 0 auto; isolation: isolate; }

/* Painted scene backdrop (house-art.js interior/exterior scenes). */
.house-backdrop { position: absolute; pointer-events: none; }
.house-backdrop svg { display: block; width: 100%; height: 100%; }

/* Grid cells are invisible over the painted floor; while editing they show
   faint cream lines so placement targets stay legible against the dark art. */
.house-tile { position: absolute; }
.house-editing .house-tile { border: 1px solid rgba(242, 232, 208, 0.16); }

.house-sprite { position: absolute; }
/* overflow visible: facade sprites (door, porch lantern) deliberately draw
   below their slot box so they touch the ground line of the painted wall. */
.house-sprite svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* Editor chrome (Task 7b): edit-mode toggle/save/cancel, the ghost preview
   that follows the pointer while something's selected, the selected-placed
   highlight, the inventory tray, and the selection action bar (rotate/
   remove/cancel). Tap-tap placement only — no drag requirement. */
#house-edit-controls { display: flex; align-items: center; gap: 10px; }
#house-edit-btn { font-weight: 700; }

/* Ghost tinting over the real art: the sprite itself rides along at reduced
   opacity while an ::after veil + border carries the valid/invalid verdict —
   bright green/vermilion chosen to stay legible against the night-dark
   scenes. */
.house-ghost { opacity: 0.85; pointer-events: none; }
.house-ghost::after {
  content: ''; position: absolute; inset: 0;
  border: 2px solid #49d17c; background: rgba(73, 209, 124, 0.28);
}
.house-ghost.house-ghost-invalid::after { border-color: #ff5c33; background: rgba(255, 92, 51, 0.34); }
.house-ghost.hidden { display: none; }

.house-sprite-selected { outline: 2px solid var(--meridian); outline-offset: 2px; }

#house-tray {
  display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--chart-edge);
}
.house-tray-item {
  font-family: var(--font-body); font-size: 0.82rem; font-weight: 600;
  padding: 5px 12px 5px 8px; border-radius: 999px; border: 1px solid var(--compass);
  background: #fff; color: var(--compass); cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px;
}
.house-tray-item.selected { background: var(--compass); color: #fff; }
/* mini sprite preview inside a chip — bottom-aligned like the placed art */
.house-tray-icon { width: 26px; height: 26px; flex: 0 0 auto; }
.house-tray-icon svg { display: block; width: 100%; height: 100%; }
.house-tray-empty { color: var(--paper-dim); font-size: 0.85rem; margin: 14px 0 0; }

#house-selection-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
.house-selection-label { font-family: var(--font-display); font-weight: 700; color: var(--paper); }
.house-action-btn {
  font-family: var(--font-body); font-size: 0.8rem; font-weight: 700;
  padding: 5px 12px; border-radius: 6px; border: 1px solid var(--compass);
  background: #fff; color: var(--compass); cursor: pointer;
}
.house-action-btn.house-action-danger { border-color: #a8391c; color: #a8391c; }

#house-toast {
  /* Fixed to the viewport (not in-flow after the tray) so it stays visible
     regardless of scroll position or room height (Fix I1: was rendering
     below the fold on tall interior scenes at phone heights). */
  position: fixed; left: 50%; bottom: max(16px, env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%); z-index: 60; max-width: min(92vw, 420px);
  padding: 8px 12px; border-radius: 8px;
  background: var(--paper); color: #fff; font-size: 0.85rem;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
}

/* Customize house (Phase D Task 6, real art by Task 7): per-slot style-chip
   rows, shown only while editing the exterior tab. Every chip carries a
   generator-drawn thumbnail (the approved page's style-row crop, crisp 2x).
   Locked chips are real disabled <button>s (never fire click) with their
   art greyed and the unlock hint printed underneath the label. */
#house-customize {
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--chart-edge);
}
#house-customize-title { font-family: var(--font-display); font-size: 1rem; margin-bottom: 8px; }
.house-customize-row {
  display: flex; align-items: flex-start; gap: 10px; padding: 8px 0; flex-wrap: wrap;
}
.house-customize-row + .house-customize-row { border-top: 1px dashed var(--chart-edge); }
.house-customize-slot-label {
  font-family: var(--font-display); font-weight: 700; font-size: 0.82rem;
  width: 68px; flex: 0 0 auto; color: var(--paper-dim); text-transform: uppercase; letter-spacing: 0.03em;
  padding-top: 4px;
}
.house-customize-chips { display: flex; flex-wrap: wrap; gap: 8px; flex: 1 1 auto; }
.house-style-chip {
  font-family: var(--font-body); font-size: 0.78rem; font-weight: 600;
  padding: 5px; border-radius: 10px; border: 1px solid var(--compass);
  background: #fff; color: var(--compass); cursor: pointer;
  display: inline-flex; flex-direction: column; align-items: center; gap: 3px; line-height: 1.2;
}
/* thumbnail crop: dark night-scene art gets its own rounded well */
.house-style-thumb { display: block; border-radius: 6px; overflow: hidden; line-height: 0; background: var(--well); }
.house-style-thumb svg { display: block; }
.house-style-chip.selected { border-width: 2px; padding: 4px; background: #fdeee7; color: var(--compass); }
.house-style-chip.locked {
  cursor: not-allowed; border-color: var(--chart-edge); background: #f4f7fb; color: var(--paper-dim);
}
.house-style-chip.locked .house-style-thumb { filter: grayscale(1) brightness(0.75); opacity: 0.6; }
.house-style-chip-hint { font-size: 0.68rem; font-weight: 500; opacity: 0.85; }

/* street (Task 8b; real size-tier art by Phase D Task 7): every player's
   facade on one night street — cards render through the same
   renderHouseExterior() path as the house view, then scale down 0.32x to a
   browsable cabin size (~2 per phone-width). Read-only, so the transform
   needs no pointer-math correction. One UNIFORM scale for every tier: the
   art itself carries the size progression (narrow/low camp cabin -> tall
   homestead) and every card shares the tile's single ground line, like the
   approved page's street hero. Card paintings leave their sky transparent
   (streetCard flag in game.js), so the row's sky/grass tile + moon + pines
   show through behind them; the 31px top padding and the tile geometry are
   mirrored by game.js's streetTileCss(112, 207) call — change them
   together. */
#street-panel { max-width: 720px; }
#street-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 14px; }
#street-viewport {
  overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
  background: var(--well); border-radius: 12px;
}
#street-row {
  position: relative; display: flex; gap: 10px; align-items: flex-start;
  padding: 31px 16px 10px; width: max-content; min-width: 100%; box-sizing: border-box;
  background-color: #35543f; background-repeat: repeat-x;
}
.street-house-card {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  cursor: pointer; flex: 0 0 auto;
}
/* 480x448.8 room (sky band + facade + yard) scaled to 0.32, then CENTER-
   CROPPED to 118px (Task 7 density pass): the compact cabins sit centered
   on their canvas, so trimming the outer sky/grass margins packs ~3 cabins
   into a phone frame (shipped street fit <2). Outermost yard columns crop
   with the margins — read-only decoration, judged acceptable. */
.street-house-viewport { width: 118px; height: 143.6px; overflow: hidden; }
.street-house-viewport .house-room {
  transform: scale(0.32); transform-origin: 0 0;
  position: relative; left: -17.8px; /* (153.6 - 118) / 2 */
}
.street-house-name {
  font-family: var(--font-display); font-weight: 700; font-size: 0.78rem;
  color: #f2e8d0; margin: 0; text-align: center; white-space: nowrap;
}
.street-house-card:hover .street-house-name,
.street-house-card:focus-visible .street-house-name { color: #ffd98a; }
.street-pine { width: 44px; height: 78px; flex: 0 0 auto; align-self: flex-start; margin-top: 86px; }
.street-pine svg, .street-moon svg { display: block; width: 100%; height: 100%; }
.street-moon { position: absolute; top: 7px; left: 13px; width: 28px; height: 28px; pointer-events: none; }
.street-empty { color: #cfe3d8; font-style: italic; margin: 26px auto; padding: 0 16px; }

/* ---------- Guess the Year (year-renderer.js) ---------- */
/* Round stage: full-bleed letterboxed photo over the dark imagery well
   (same treatment as #sat-img), with a floating slider card kept clear of
   the shared guess-box (bottom-right, 260px in maps-off mode). */
.year-photo {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; pointer-events: none; background: var(--well);
}
.year-controls {
  position: absolute; left: 16px; right: 296px; bottom: 24px; z-index: 20;
  max-width: 640px; margin: 0 auto;
  background: rgba(255, 255, 255, 0.93); border: 1px solid var(--chart-edge);
  border-radius: 10px; padding: 10px 18px 8px; text-align: center;
}
.year-value {
  display: block; color: var(--paper);
  font-family: var(--font-display); font-weight: 700; font-size: 1.6rem;
  letter-spacing: 0.05em; font-variant-numeric: tabular-nums; line-height: 1.1;
}
.year-slider { width: 100%; accent-color: var(--compass); margin: 6px 0 2px; cursor: pointer; }
.year-scale {
  display: flex; justify-content: space-between;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em; color: var(--paper-dim);
  font-variant-numeric: tabular-nums;
}
@media (max-width: 640px) {
  /* stack: slider card sits above the 260px submit box instead of beside it */
  .year-controls { left: 12px; right: 12px; bottom: 88px; }
}

/* Reveal card additions */
.year-reveal-photo {
  display: block; max-width: 100%; max-height: 38vh; margin: 10px auto 6px;
  border-radius: 8px; background: var(--well); object-fit: contain;
}
.year-reveal-title { font-family: var(--font-flavor); font-style: italic; color: var(--paper); }
.year-reveal-attr { color: var(--paper-dim); font-size: 0.85rem; margin: 4px 0 10px; }
.year-source-link { color: var(--paper-dim); margin-left: 6px; }
.year-report-btn { font-size: 0.78rem; margin-bottom: 10px; }
.year-report-btn:disabled { text-decoration: none; cursor: default; opacity: 0.7; }

/* timeline mobile reorder buttons (2026-09-03) */
.tl-move { display: flex; flex-direction: column; gap: 2px; flex: none; }
.tl-move-btn {
  border: 1px solid var(--paper-dim, #888); background: transparent;
  color: inherit; border-radius: 6px; font-size: 0.72rem;
  padding: 2px 7px; line-height: 1.2; cursor: pointer; touch-action: manipulation;
}
.tl-move-btn:active { transform: scale(0.94); }
