/* wedding_site — base stylesheet */
/* Mobile-first; one column; warm wedding palette. */

/* Self-hosted CJK fonts (subset to the ~290 Chinese glyphs the site actually
   uses) so 中文 renders everywhere — incl. the venue projector — instead of
   tofu. Latin keeps Cormorant/system fonts; the browser only falls through to
   these for glyphs the Latin fonts lack. */
@font-face {
  font-family: "Noto Serif SC";
  src: url("/static/fonts/noto-serif-sc-subset.woff2") format("woff2");
  font-display: swap;
  unicode-range: U+2E80-9FFF, U+3000-303F, U+FF00-FFEF, U+3400-4DBF;
}
@font-face {
  font-family: "Noto Sans SC";
  src: url("/static/fonts/noto-sans-sc-subset.woff2") format("woff2");
  font-display: swap;
  unicode-range: U+2E80-9FFF, U+3000-303F, U+FF00-FFEF, U+3400-4DBF;
}

:root {
  --bg: #fffaf3;
  --fg: #2a2520;
  --muted: #6b5f54;
  --border: #e8e1d8;
  --card-bg: #fff;
  --accent: #c97862;      /* terracotta */
  --accent-fg: #fff;
  --accent-soft: #f4d9d1;
  --link: #8a5a47;
  --success: #6b8e5a;
  --danger: #b04438;
  /* Typography */
  --font-display: "Cormorant Garamond", "Noto Serif SC", "Georgia", "Times New Roman", serif;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, "Noto Sans SC", sans-serif;
  /* Type scale — major-third (1.25×) on a 17px base, with h1 boosted for hero weight. */
  --fs-body: 17px;
  --fs-h3: 1.25rem;
  --fs-h2: 1.6rem;
  --fs-h1: 2.4rem;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.5;
}

body {
  max-width: 640px;
  margin: 0 auto;
  padding: 1rem 1rem 4rem;
}

h1, h2, h3 {
  font-family: var(--font-display);
  letter-spacing: -0.005em;
  line-height: 1.15;
  color: var(--fg);
}
h1 {
  font-size: var(--fs-h1);
  font-weight: 600;
  margin: 0.5rem 0 1rem;
}
h2 {
  font-size: var(--fs-h2);
  font-weight: 600;
  margin: 1.5rem 0 0.75rem;
}
h3 {
  font-size: var(--fs-h3);
  font-weight: 600;
  margin: 1rem 0 0.5rem;
}

p { margin: 0.5rem 0; }

a {
  color: var(--link);
  text-decoration: none;
}
a:hover, a:focus { text-decoration: underline; }

/* Header bar (handle chip, nav) */
header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1rem;
}

.handle {
  display: inline-block;
  padding: 0.25rem 0.625rem;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 999px;
  font-size: 0.875rem;
  font-weight: 600;
}

/* Distinguished couple (Max/Tabby) — gold chip so their picks read instantly
   apart from the color-animal guest crowd. */
.handle-couple {
  background: linear-gradient(135deg, #f6e27a, #e6b800);
  color: #4a3500;
  box-shadow: 0 0 0 1px #d4a017 inset;
}
.admin-guest-chips li.guest-couple {
  background: #fffaf0;
  border-radius: 8px;
  padding: 0.15rem 0.3rem;
}

/* Banner strip */
#banner { min-height: 0; }
.banner {
  background: var(--accent-soft);
  color: var(--link);
  padding: 0.625rem 0.875rem;
  border-radius: 8px;
  margin: 0 0 0.75rem !important;
  font-weight: 500;
}

/* Targeted prompt */
.prompt {
  background: var(--accent);
  color: var(--accent-fg);
  padding: 0.875rem 1rem;
  border-radius: 8px;
  margin: 0 0 0.75rem !important;
  cursor: pointer;
  font-weight: 500;
}
.prompt::after {
  content: " (tap to dismiss)";
  opacity: 0.7;
  font-size: 0.8em;
  font-weight: 400;
}

/* Main content slot */
main { display: block; }
#main { padding: 0.5rem 0; }

/* Section/card */
section {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1rem 1.125rem;
  margin: 0.75rem 0;
}

article {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.875rem 1rem;
  margin: 0.5rem 0;
}

nav {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 0.5rem 0 1rem;
  font-size: 0.875rem;
}

/* Forms */
form { margin: 0; }
/* Side-by-side action forms (admin row buttons) */
.form-inline { display: inline; }
label {
  display: block;
  font-weight: 500;
  margin: 0.625rem 0 0.25rem;
}
input[type="text"],
input[type="password"],
input[type="number"],
textarea,
select {
  width: 100%;
  padding: 0.625rem 0.75rem;
  font-size: 1rem;
  font-family: inherit;
  color: var(--fg);
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 8px;
}
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
textarea {
  min-height: 5rem;
  resize: vertical;
}

button, input[type="submit"] {
  display: inline-block;
  padding: 0.625rem 1.125rem;
  font-size: 1rem;
  font-weight: 600;
  font-family: inherit;
  color: var(--accent-fg);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 8px;
  cursor: pointer;
  min-height: 44px;            /* touch target */
}
button:hover, button:focus {
  filter: brightness(0.95);
}
button:disabled { opacity: 0.5; cursor: not-allowed; }

/* Link styled as a full-width primary button — guest-home CTA into a live
   game. Mirrors the button rules above; kept separate so plain links keep
   their text styling. */
.cta-button {
  display: block;
  padding: 0.875rem 1.125rem;
  font-size: 1.1rem;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  color: var(--accent-fg);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 10px;
  min-height: 44px;            /* touch target */
}
.cta-button:hover, .cta-button:focus {
  filter: brightness(0.95);
  color: var(--accent-fg);
}

/* Phase nudge on game pages — hide the link when it points at the page the
   guest is already on (each page sets a page-* body class). */
#phase-nudge .cta-button { margin: 0.5rem 0; }
.page-game1 .nudge-game1,
.page-game2 .nudge-game2,
.page-game3 .nudge-game3,
.page-game4 .nudge-game4 { display: none; }

/* Inline / row-of-button forms (Game 3 vote buttons, choice grid) */
.choice-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.5rem 0;
}
.choice-row form { display: inline-block; margin: 0; }

/* Game 3 text answers: an even grid (short options pair up, long ones go full
   width) instead of ragged inline pills. */
.choices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.6rem;
  margin: 1rem 0;
}
.choices form { display: block; margin: 0; }
.choice-opt {
  width: 100%;
  font-size: 1.05rem;
  font-weight: 600;
  padding: 0.95rem 1rem;
  min-height: 3.25rem;
}
/* Selected: darken the filled button + inset ring + check, so multi-select reads
   clearly without flipping to a different (white-card) style. */
.choice-opt-selected {
  filter: brightness(0.88);
  box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.85);
}
.choice-opt-selected::before { content: "✓ "; }

/* Game 4 Save pin: until a pin is dropped the button is a clear "pending"
   ghost (not a faded primary that reads as broken); the map JS removes
   [disabled], snapping it to the solid action style. */
.g4-save { width: 100%; margin-top: 0.6rem; }
.g4-save:disabled {
  opacity: 1;
  background: var(--card-bg);
  color: var(--muted);
  border: 2px dashed var(--border);
  cursor: not-allowed;
}

/* Leaderboards / lists */
ol, ul {
  padding-left: 1.25rem;
  margin: 0.5rem 0;
}
li { margin: 0.25rem 0; }

/* Subtle / utility */
em { color: var(--muted); font-style: normal; }
small, .muted { color: var(--muted); font-size: 0.875rem; }

/* Footer link "back" */
.back { font-size: 0.875rem; color: var(--muted); }
.error-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1.5rem;
  margin-top: 1rem;
  background: var(--card, transparent);
}
.error-card h1 { margin-top: 0; }

/* Admin dashboard chips */
.admin-dashboard h2 { margin-bottom: 0.4rem; }
.admin-chips {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.6rem;
}
.admin-chip {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.55rem 0.7rem;
  background: var(--card, transparent);
}
.admin-chip-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.admin-chip-value {
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1.1;
}
.admin-chip-suffix {
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--muted);
  margin-left: 0.15rem;
}
.admin-chip-sub {
  font-size: 0.78rem;
  color: var(--muted);
}

/* Inline form whose button looks like a small text link
   (used for the Reroll handle action in the header). */
.reroll-form { margin: 0; }
/* Tap-your-name menu: the name is the <summary>; reroll drops in underneath,
   so it isn't always sitting in the header. */
.handle-menu { display: inline-block; }
.handle-menu > summary {
  cursor: pointer;
  list-style: none;        /* Firefox: hide the default disclosure triangle */
  user-select: none;
}
.handle-menu > summary::-webkit-details-marker { display: none; } /* Safari/Chrome */
.handle-menu > summary::after { content: " ▾"; font-size: 0.7em; opacity: 0.6; }
.handle-menu[open] > summary::after { content: " ▴"; }
.handle-menu .reroll-form { margin-top: 0.4rem; }
.link-button {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font-size: 0.875rem;
  color: var(--link);
  cursor: pointer;
  min-height: 0;
  text-decoration: underline;
  font-weight: 400;
}
.link-button:hover, .link-button:focus { filter: none; color: var(--accent); }

/* Language toggle (EN / 中文). Lives in the guest header and at the top of
   each game page via .page-bar. */
.page-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1rem;
}
/* Always-visible home link in the guest page-bar. */
.page-home {
  font-size: 0.8125rem;
  font-weight: 600;
  text-decoration: none;
  color: var(--link);
  white-space: nowrap;
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--accent-soft, #e0d4c4);
  border-radius: 999px;
}
.page-home:hover,
.page-home:focus { border-color: var(--link); }

/* Admin destructive action — the full "Reset everything" control. */
.admin-danger {
  border: 1px solid #e7c4ba;
  border-radius: 0.5rem;
  padding: 0.5rem 0.85rem;
}
button.danger {
  background: #b04438;
  color: #fff;
  border: none;
  padding: 0.45rem 0.9rem;
  border-radius: 0.4rem;
  font-weight: 700;
  cursor: pointer;
}
button.danger:hover { background: #97392f; }
.lang-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.8125rem;
}
.lang-toggle .lang-link,
.lang-toggle .lang-current {
  padding: 0.15rem 0.45rem;
  border-radius: 999px;
}
.lang-toggle .lang-link {
  color: var(--link);
  text-decoration: none;
}
.lang-toggle .lang-link:hover,
.lang-toggle .lang-link:focus { color: var(--accent); background: var(--accent-soft); }
.lang-toggle .lang-current {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

/* Game 3 projector round headline */
.g3-round-headline {
  font-size: 0.8em;
  font-weight: 700;
  color: var(--accent);
  margin: 0 0 0.3em;
  line-height: 1.1;
}

/* Game 3 projector tally bars */
.g3-bars {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  margin: 0.6em 0;
  font-size: 0.55em;
}
.g3-bar {
  display: grid;
  grid-template-columns: minmax(6em, 14em) 1fr 2.5em;
  align-items: center;
  gap: 0.6em;
}
.g3-bar-label { text-align: right; font-weight: 600; }
.g3-bar-track {
  background: var(--border);
  border-radius: 6px;
  height: 1em;
  overflow: hidden;
}
.g3-bar-fill {
  display: block;
  height: 100%;
  background: var(--accent);
  transition: width 200ms ease;
}
.g3-bar-count { text-align: left; }
.g3-bar-correct .g3-bar-label { color: #6b8e5a; }
.g3-bar-correct .g3-bar-label::after { content: " ✓"; }
.g3-bar-correct .g3-bar-fill { background: #6b8e5a; }
.g3-bar-correct .g3-bar-count { color: #6b8e5a; font-weight: 700; }

/* Game 4 per-photo results after reveal */
.g4-breakdown {
  list-style: none;
  padding: 0;
  margin: 0.5rem 0;
}
.g4-breakdown li {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
}
.g4-breakdown li:last-child { border-bottom: none; }
.g4-breakdown-miles {
  white-space: nowrap;
  font-weight: 600;
  font-feature-settings: "tnum" 1;
}
.g4-breakdown-best {
  font-size: 0.85em;
  color: var(--success);
  font-weight: 600;
}

/* Game 4 photo above each pin map */
.g4-photo {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--accent-soft);
}

/* Photo thumbnail inside the projector reveal tooltip */
.g4-target-photo {
  width: 9rem;
  border-radius: 6px;
  display: block;
  margin: 0 auto 0.25rem;
}

/* Game 4 clickable map */
.leaflet-map {
  height: 260px;
  width: 100%;
  border-radius: 8px;
  border: 1px solid var(--border);
  margin: 0.5rem 0;
}

/* Guest's personal reveal map (target + their own pin + distance line). */
.g4-guest-map {
  position: relative;
  height: 240px;
  width: 100%;
  border-radius: 8px;
  border: 1px solid var(--border);
  margin: 0.5rem 0 0.75rem;
  overflow: hidden;
}

/* Admin guest-list chip wrap */
.admin-guest-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0.25rem 0;
}
.admin-guest-chips > span { margin: 0; }

/* PostEvent finale */
.finale h2 { margin-top: 0; }
.finale-block {
  margin: 0.75rem 0;
  background: transparent;
  border: none;
  padding: 0.25rem 0;
}
.finale-block h3 {
  margin: 0.5rem 0 0.25rem;
  color: var(--link);
}
.finale-list {
  list-style: decimal;
  padding-left: 1.5rem;
  margin: 0.25rem 0;
}
.finale-list li { margin: 0.15rem 0; }
.finale-handle { font-weight: 600; color: var(--accent); }
.finale-score { color: var(--muted); }

/* Guest welcome run-of-show */
.agenda-list {
  list-style: none;
  padding: 0;
  margin: 0.5rem 0;
}
.agenda-list li {
  display: flex;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
}
.agenda-list li:last-child { border-bottom: none; }
.agenda-time {
  flex: 0 0 4.5rem;
  font-weight: 600;
  color: var(--accent);
  font-feature-settings: "tnum" 1;
}

/* Game 2 (Cat Hunt) info gallery */
.cat-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.75rem;
  margin: 0.75rem 0;
}
.cat-figure {
  margin: 0;
  text-align: center;
}
.cat-figure img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--accent-soft);
}
.cat-figure figcaption {
  margin-top: 0.25rem;
  font-weight: 500;
  font-size: 0.95rem;
}

/* Projector pin-swarm map. position:relative is load-bearing: htmx's
   settle step rewrites the element's class attribute ~20ms after an SSE
   swap, stripping Leaflet's leaflet-container class — without our own
   positioning context the map panes would anchor to the viewport. */
.g4-projector-map {
  position: relative;
  height: 60vh;
  width: 90vw;
  max-width: 100%;
  margin: 0.5rem auto;
  border-radius: 12px;
  border: 2px solid var(--accent-soft);
}

/* Projector QR code — shown during PreEvent / Intermission / PostEvent */
.projector-qr {
  max-width: 30vmin;
  margin: 1rem auto;
}
.projector-qr svg {
  width: 100%;
  height: auto;
  display: block;
}

/* Projector welcome layout — PreEvent / Intermission */
.projector-welcome {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5em;
  padding: 0.5em 0;
}
.projector-welcome-cta {
  font-size: 1em;       /* inherits projector body scale */
  font-weight: 700;
  letter-spacing: 0.02em;
  margin: 0;
  color: var(--accent);
}
.projector-welcome-qr {
  width: 40vmin;
  max-width: 40vmin;
}
.projector-welcome-qr svg {
  width: 100%;
  height: auto;
  display: block;
}
.projector-welcome-title {
  font-size: 0.6em;
  font-weight: 600;
  margin: 0.25em 0 0;
}
.projector-welcome-body {
  font-size: 0.45em;
  color: var(--muted);
  margin: 0;
}

/* Projector PostEvent finale — podium layout */
.projector-finale {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1em;
  padding: 0.5em 0;
}
/* Hero needs its own size (it's an h2, which the projector h2 rule otherwise
   shrinks); specificity beats body.projector-mode h2. */
body.projector-mode .projector-finale-hero {
  font-size: 1.5em;
  font-weight: 700;
  color: var(--accent);
  margin: 0;
}
.projector-finale-section {
  width: 100%;
}
.projector-finale-section h3 {
  font-size: 0.55em;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.4em;
}
.projector-finale-podium {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 0.75em;
  --gold:   #c9a063;   /* warm gold, in palette */
  --silver: #b9b6ad;
  --bronze: #b4856b;
}
.podium-entry {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15em;
  position: relative;
  padding: 0.6em 0.7em 0.5em;
  border-radius: 12px;
  background: var(--card-bg);
  border: 1px solid var(--border);
  box-shadow: 0 4px 14px rgba(42, 37, 32, 0.06);
  min-width: 5em;
}
.podium-rank {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4em;
  height: 1.4em;
  border-radius: 999px;
  font-size: 0.35em;
  font-weight: 700;
  color: var(--accent-fg);
  background: var(--muted);
  margin-bottom: 0.15em;
}
.podium-handle {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--fg);
}
.podium-score {
  color: var(--muted);
  font-feature-settings: "tnum" 1;
}
/* Tiered heights — gold tallest, bronze shortest. */
.podium-1 { padding-top: 1.1em; padding-bottom: 0.7em; }
.podium-3 { padding-top: 0.3em; padding-bottom: 0.4em; }
/* Rank 1 — gold */
.podium-1 .podium-rank   { background: var(--gold); color: #5a3f10; }
.podium-1 .podium-handle { font-size: 0.65em; color: var(--accent); }
.podium-1 .podium-score  { font-size: 0.45em; }
/* Rank 2 — silver */
.podium-2 .podium-rank   { background: var(--silver); color: #2a2520; }
.podium-2 .podium-handle { font-size: 0.5em; }
.podium-2 .podium-score  { font-size: 0.38em; }
/* Rank 3 — bronze */
.podium-3 .podium-rank   { background: var(--bronze); color: #3a1d10; }
.podium-3 .podium-handle { font-size: 0.45em; color: var(--muted); }
.podium-3 .podium-score  { font-size: 0.35em; }

/* Staggered entrance: 3rd in first, then 2nd, then 1st as the crescendo. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes podium-rise {
    0%   { opacity: 0; transform: translateY(20px) scale(0.96); }
    100% { opacity: 1; transform: translateY(0) scale(1); }
  }
  .podium-entry {
    animation: podium-rise 420ms cubic-bezier(0.34, 1.4, 0.64, 1) backwards;
  }
  .podium-3 { animation-delay: 0ms; }
  .podium-2 { animation-delay: 150ms; }
  .podium-1 { animation-delay: 320ms; }
}

/* ── Game 3 image/multi-select UI ───────────────────────────────────────── */
.choices-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 0.75rem;
  margin: 1rem 0;
}
.choice-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  border: 2px solid var(--border);
  border-radius: 8px;
  padding: 0.5rem;
  background: var(--card-bg);
  cursor: pointer;
  text-align: center;
}
.choice-tile img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 4px;
}
.choice-selected {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.choice-correct {
  border-color: var(--success);
  background: #eaf3e5;
}
/* Reveal-state marks: the guest's own pick + the correct answer */
.choice-picked {
  border-color: var(--accent);
}
.choice-correct.choice-picked {
  border-color: var(--success);
}
.choice-tag {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--accent);
  margin-top: 0.25rem;
}
.choice-tag-correct {
  color: var(--success);
}

/* ─────────────────────────────────────────────────────────────────────────
   Projector "big screen" mode
   Toggled by body.projector-mode on /projector. Replaces the old inline
   PROJECTOR_STYLE constant that just bumped font-sizes. Goals:
     - Fill the room: drop the 640px mobile content frame, breathe.
     - Make text legible from the back: hero scale on h1/h2, big banner.
     - Warm depth: subtle radial gradient instead of flat cream.
   ───────────────────────────────────────────────────────────────────────── */
body.projector-mode {
  max-width: none;
  padding: 1.5rem 3rem;
  text-align: center;
  background:
    radial-gradient(ellipse at top, #fff6e9 0%, var(--bg) 55%, #f4ecdf 100%);
  min-height: 100vh;
  /* Center the live content so it's balanced and, crucially, fits older
     720p projectors without the bottom (tally/standings) getting cut off. */
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Type scales to the screen HEIGHT (vh-clamped) and the headings are relative,
   so the whole projector view shrinks to fit 720p and grows on 1080p. */
body.projector-mode main { font-size: clamp(1.9rem, 4.6vh, 3.5rem); width: 100%; }
body.projector-mode h1 { font-size: 1.08em; margin: 0.2em 0 0.3em; }
body.projector-mode h2 { font-size: 0.74em; margin: 0.3em 0; }
body.projector-mode h3 { font-size: 0.52em; margin: 0.3em 0; }
body.projector-mode p { margin: 0.35em 0; }

/* Banner: bigger, softer than the guest version so it reads from the back. */
body.projector-mode .banner {
  font-size: 1.6rem;
  padding: 0.9rem 1.6rem;
  border-radius: 14px;
  max-width: 60%;
  margin: 0 auto 1rem !important;
}

/* QR slot used by the welcome/intermission card on small projector areas. */
body.projector-mode .projector-qr { display: none; }

/* Corner QR during game phases — keeps the join path alive for latecomers
   without competing with the live content. */
.projector-qr-corner {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 10;
  background: #fff;
  padding: 0.6rem 0.6rem 0.4rem;
  border-radius: 12px;
  box-shadow: 0 6px 24px rgba(42, 37, 32, 0.12);
  text-align: center;
}
.projector-qr-corner svg {
  display: block;
  width: 6.5rem;
  height: 6.5rem;
}
.projector-qr-corner span {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
}

/* Cards on the projector should feel like stage objects, not document blocks. */
body.projector-mode section,
body.projector-mode article {
  border: none;
  background: transparent;
  padding: 0;
  margin: 0;
}

/* PreEvent / Intermission welcome card */
body.projector-mode .projector-welcome {
  max-width: 80%;
  margin: 0 auto;
}
body.projector-mode .projector-welcome-cta {
  font-family: var(--font-display);
  font-size: 2.4rem;
  font-weight: 500;
  color: var(--accent);
  letter-spacing: 0.01em;
  margin: 0 0 1rem;
}
body.projector-mode .projector-welcome-qr {
  display: inline-block;
  padding: 1.2rem;
  background: #fff;
  border-radius: 18px;
  box-shadow: 0 8px 32px rgba(42, 37, 32, 0.08);
}
body.projector-mode .projector-welcome-qr svg {
  width: 18rem;
  height: 18rem;
}
body.projector-mode .projector-welcome-title {
  font-size: 3rem;
  margin: 1.2rem 0 0.4rem;
}
body.projector-mode .projector-welcome-body {
  font-size: 1.4rem;
  color: var(--muted);
}
body.projector-mode .projector-welcome-open {
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--accent);
}

/* Game 3 projector — countdown should be unmissable */
body.projector-mode .g3-round-headline {
  font-size: 0.85em;
  color: var(--accent);
  margin: 0 0 0.5em;
}
body.projector-mode .g3-countdown {
  display: inline-block;
  min-width: 2.5em;
  padding: 0.05em 0.3em;
  font-feature-settings: "tnum" 1;     /* tabular numerals so the ticker doesn't jitter */
}
/* Tally bars must read from the back of the room: ~2rem labels/counts and
   bar tracks that scale with them (track height is 1em). */
body.projector-mode .g3-bars {
  font-size: 2rem;
  max-width: 64rem;
  margin: 1.5rem auto;
  gap: 0.6em;
}
body.projector-mode .g3-bar {
  grid-template-columns: minmax(8em, 16em) 1fr 3em;
}

/* Game 2 gallery — centered grid of large figures instead of the mobile
   140px tiles huddling in the corner of a 1920px screen. */
body.projector-mode .cat-gallery {
  grid-template-columns: repeat(auto-fit, minmax(14rem, 18rem));
  justify-content: center;
  gap: 1.5rem;
  max-width: 80rem;
  margin: 1.5rem auto;
}
body.projector-mode .cat-figure figcaption {
  font-size: 1.6rem;
}
body.projector-mode .game2-info > p {
  font-size: 0.6em;            /* body copy: readable but subordinate to the title */
  max-width: 32em;
  margin: 0.4em auto;
}

/* Game 3 standings moment (projector RoundComplete) */
.g3-standings {
  list-style: none;
  counter-reset: standing;
  padding: 0;
  margin: 0.5em auto;
  max-width: 28em;
  font-size: 0.6em;
  text-align: left;
}
.g3-standings li {
  display: grid;
  grid-template-columns: 2em 1fr 3em 4em;
  gap: 0.6em;
  align-items: baseline;
  padding: 0.2em 0;
}
.g3-standing-handle { font-weight: 600; }
.g3-standing-score { text-align: right; font-feature-settings: "tnum" 1; }
.g3-move { font-size: 0.8em; color: var(--muted); }
.g3-move-up { color: var(--success); }
.g3-move-down { color: #b3553f; }
.g3-move-new { color: var(--accent); }

/* Game 3 image rounds — projector shows the actual pictures with counts */
.g3-image-tally {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem;
  margin: 1rem auto;
}
.g3-image-choice {
  margin: 0;
  text-align: center;
}
.g3-image-choice img {
  width: 9rem;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 10px;
  border: 3px solid var(--border);
  background: var(--accent-soft);
}
.g3-image-choice figcaption {
  margin-top: 0.3rem;
  font-weight: 700;
}
.g3-image-correct img { border-color: var(--success); }
.g3-image-correct figcaption { color: var(--success); }

/* Couple picks surfaced on the projector at reveal — named gold chips that sit
   on the choice Max/Tabby landed on, tinted right (green) or wrong (red). */
.g3-couple-picks { display: flex; gap: 0.3rem; justify-content: center; flex-wrap: wrap; margin-top: 0.3rem; }
.g3-couple-pick {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.5em;
  font-weight: 700;
  background: linear-gradient(135deg, #f6e27a, #e6b800);
  color: #4a3500;
  margin-left: 0.3rem;
}
.g3-couple-right { box-shadow: 0 0 0 2px var(--success); }
.g3-couple-wrong { box-shadow: 0 0 0 2px #d9534f; }
/* Head-to-head score line on the standings/reveal moment. */
.g3-duel {
  font-size: 0.7em;
  font-weight: 700;
  text-align: center;
  margin: 0.4rem 0;
  color: #b8860b;
}
.g3-duel-name { color: #4a3500; background: linear-gradient(135deg, #f6e27a, #e6b800); padding: 0.05rem 0.4rem; border-radius: 999px; }
/* Standings duel line: names read as warm-gold text, no pill (the crowns and
   the saturated gold bubble were too heavy on the projector). */
.g3-score-name { color: #7a5c12; font-weight: 800; }
.g3-duel-dash { opacity: 0.7; }

/* Couple head-to-head "whose turn" banner (projector) + guest turn hints. */
.g3-turn-banner {
  display: inline-block;
  font-weight: 700;
  color: #4a3500;
  background: linear-gradient(135deg, #f6e27a, #e6b800);
  padding: 0.1rem 0.7rem;
  border-radius: 999px;
  margin: 0.2rem 0 0.4rem;
}
.g3-turn-you {
  font-weight: 700;
  color: #4a3500;
  background: linear-gradient(135deg, #f6e27a, #e6b800);
  padding: 0.4rem 0.7rem;
  border-radius: 0.6rem;
  text-align: center;
}
.g3-turn-watch { font-style: italic; text-align: center; }

/* Finale duel beat — the head-to-head verdict, gold-framed. */
.finale-duel {
  border: 2px solid #e6b800;
  border-radius: 12px;
  background: linear-gradient(135deg, rgba(246,226,122,0.18), rgba(230,184,0,0.08));
  padding: 0.6rem 1rem;
  margin: 0.6rem 0;
}
.finale-duel-verdict { font-weight: 800; color: #b8860b; }
.finale-duel-breakdown { font-size: 0.85em; opacity: 0.85; }

body.projector-mode .g3-image-tally { gap: 2rem; }
body.projector-mode .g3-image-choice img {
  width: 16rem;
  border-width: 5px;
}
body.projector-mode .g3-image-choice figcaption { font-size: 2rem; }

/* Game 4 swarm map — fills more vertical real estate on the big screen */
body.projector-mode .g4-projector-map {
  height: 55vh;
  width: min(90%, 70rem);
  margin: 0 auto;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 8px 32px rgba(42, 37, 32, 0.08);
}

/* Finale podium */
body.projector-mode .projector-finale {
  font-size: 1rem;
  padding: 1rem 0;
}
body.projector-mode .projector-finale-podium { gap: 1.5rem; }

/* ─────────────────────────────────────────────────────────────────────────
   HTMX/SSE swap motion
   On every SSE swap, the targeted node's innerHTML is replaced. New child
   nodes have no "remembered" state, so a plain CSS animation declaration
   runs fresh each time — a subtle fade + lift that signals "this changed"
   without being distracting. Respects prefers-reduced-motion.
   ───────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  @keyframes sse-fade-in {
    from { opacity: 0; transform: translateY(-3px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  /* NOTE: #game3/#game4 are deliberately absent — they re-swap on every
     vote/pin, and re-running the animation each time reads as stutter on
     the projector. The bar-fill width transition below carries the motion. */
  #banner > *,
  #prompt > *,
  #main > *,
  #g3-admin > *,
  #g4-admin > *,
  #admin-status > * {
    animation: sse-fade-in 180ms ease-out;
  }
  /* Game 3 tally bars deserve a touch more — let the fills animate width too. */
  .g3-bar-fill {
    transition: width 240ms ease-out;
  }
}

/* Guest Game 3 countdown wrapper (in-fragment paragraph). Subtle so the
   tap targets stay primary. */
.g3-countdown-wrap {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0 0 0.4rem;
}
.g3-countdown-wrap .g3-countdown {
  color: var(--accent);
  font-weight: 700;
  font-feature-settings: "tnum" 1;
}

/* ─────────────────────────────────────────────────────────────────────────
   Game 1 — Icebreaker form
   ───────────────────────────────────────────────────────────────────────── */
.g1-progress {
  position: sticky;
  top: 0;
  background: var(--bg);
  padding: 0.5rem 0;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.75rem;
  z-index: 1;
}
.g1-progress #g1-progress-current {
  color: var(--accent);
  font-weight: 700;
}
.g1-draft-saved {
  display: inline-block;
  margin-left: 0.75rem;
  font-size: 0.78rem;
  color: var(--success);
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity 160ms ease-out, transform 160ms ease-out;
}
.g1-draft-saved.visible {
  opacity: 1;
  transform: translateY(0);
}
.g1-draft-saved::before { content: "✓ "; }

.g1-card {
  position: relative;
  padding-top: 1.6rem;
}
.g1-card-num {
  position: absolute;
  top: 0.7rem;
  right: 1rem;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.g1-card-prompt {
  display: block;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--fg);
  margin: 0 0 0.5rem;
}
.g1-card-readonly .g1-card-prompt em { font-style: normal; }

.g1-thanks-card {
  text-align: center;
  padding: 2rem 1.5rem;
  background:
    linear-gradient(180deg, var(--accent-soft) 0%, var(--card-bg) 100%);
  border: 1px solid var(--border);
  border-radius: 14px;
  margin-bottom: 1.5rem;
}
.g1-thanks-card h1 { margin-top: 0; }

/* ─────────────────────────────────────────────────────────────────────────
   Leaflet skin — tint OSM tiles into the warm wedding palette and re-skin
   markers/popups so they don't clash with the rest of the site.
   The filter is moderate enough that tile labels stay readable.
   ───────────────────────────────────────────────────────────────────────── */
.leaflet-tile {
  filter: sepia(0.32) saturate(0.7) hue-rotate(-12deg) brightness(1.02);
}
.leaflet-container {
  font-family: var(--font-body);
  background: #efe4d4;       /* warm fallback while tiles load */
}
.leaflet-popup-content-wrapper {
  background: var(--card-bg);
  color: var(--fg);
  border-radius: 10px;
  box-shadow: 0 4px 14px rgba(42, 37, 32, 0.12);
}
.leaflet-popup-content {
  font-family: var(--font-body);
  margin: 0.7rem 0.9rem;
}
.leaflet-popup-tip { background: var(--card-bg); }
.leaflet-control-attribution {
  background: rgba(255, 250, 243, 0.85) !important;
  color: var(--muted) !important;
  font-size: 0.65rem;
}
.leaflet-control-attribution a { color: var(--link); }

/* Tooltips on the projector swarm pins use the same skin. */
.leaflet-tooltip {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg);
  font-family: var(--font-body);
  box-shadow: 0 2px 8px rgba(42, 37, 32, 0.1);
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before { border-top-color: var(--border); }

/* Bilingual subtitle on the projector (EN primary, zh below, smaller) */
.zh-sub {
  display: block;
  font-size: 0.62em;
  font-weight: 500;
  opacity: 0.8;
  line-height: 1.3;
}

/* ── Finale celebration ─────────────────────────────────────────────────── */
/* Standings rank column (explicit span; replaces the old CSS counter) */
.g3-standing-rank { font-weight: 700; color: var(--accent); }

/* Podium entries rise in, staggered by tier */
body.projector-mode .podium-entry {
  animation: podium-rise 0.7s ease-out backwards;
}
body.projector-mode .podium-2 { animation-delay: 0.15s; }
body.projector-mode .podium-3 { animation-delay: 0.3s; }
@keyframes podium-rise {
  from { transform: translateY(28px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* Falling petals in palette colors — decorative only */
.finale-petals {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}
.petal {
  position: absolute;
  top: -3rem;
  width: 0.9rem;
  height: 1.3rem;
  border-radius: 50% 50% 50% 0;
  opacity: 0.7;
  animation: petal-fall 9s linear infinite;
}
.petal:nth-child(odd)  { background: #e8b87f; }
.petal:nth-child(even) { background: #f5c2cf; }
.petal:nth-child(3n)   { background: var(--accent); }
.petal-0 { left: 6%;  animation-delay: 0s; }
.petal-1 { left: 18%; animation-delay: 2.2s; }
.petal-2 { left: 31%; animation-delay: 4.6s; }
.petal-3 { left: 44%; animation-delay: 1.1s; }
.petal-4 { left: 57%; animation-delay: 5.8s; }
.petal-5 { left: 70%; animation-delay: 3.3s; }
.petal-6 { left: 83%; animation-delay: 0.7s; }
.petal-7 { left: 93%; animation-delay: 6.9s; }
@keyframes petal-fall {
  from { transform: translateY(-3rem) rotate(0deg); }
  to   { transform: translateY(110vh) rotate(300deg); }
}

@media (prefers-reduced-motion: reduce) {
  body.projector-mode .podium-entry { animation: none; }
  .petal { animation: none; display: none; }
}

/* Error feedback toast (htmx:responseError) */
.toast {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  z-index: 100;
  background: var(--fg);
  color: var(--bg);
  padding: 0.6rem 1.1rem;
  border-radius: 10px;
  font-weight: 600;
  box-shadow: 0 6px 24px rgba(42, 37, 32, 0.25);
  animation: sse-fade-in 0.2s ease-out;
}

/* Run-the-night strip */
.night-strip { text-align: center; }
.night-next {
  width: 100%;
  font-size: 1.25rem;
  padding: 1rem;
  border-radius: 12px;
}
.night-state { margin: 0.5rem 0; }
.night-auto {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--accent);
  margin: 0.4rem 0;
}
.night-auto .g3-countdown { font-variant-numeric: tabular-nums; font-weight: 800; }
.night-held { color: #b8860b; }
.night-manual { color: var(--muted); }
.night-controls {
  display: flex;
  gap: 0.75rem;
  justify-content: center;
}
.night-controls button {
  padding: 0.5rem 1.1rem;
  border-radius: 999px;
  font-weight: 600;
}
/* Host overlay: discreet gear + admin-in-a-modal on guest pages. */
#host-fab {
  position: fixed;
  bottom: 14px;
  right: 14px;
  z-index: 60;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: none;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 1.15rem;
  line-height: 1;
  opacity: 0.32;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
  cursor: pointer;
  transition: opacity 0.15s ease;
}
#host-fab:hover,
#host-fab:focus { opacity: 1; }
#host-dialog {
  width: min(96vw, 760px);
  height: min(90vh, 920px);
  max-width: 96vw;
  max-height: 90vh;
  padding: 0;
  border: none;
  border-radius: 12px;
  overflow: hidden;
}
#host-dialog::backdrop { background: rgba(0, 0, 0, 0.5); }
.host-dialog-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.5rem 0.85rem;
  background: var(--accent);
  color: #fff;
  font-weight: 600;
}
.host-dialog-bar button {
  background: transparent;
  border: none;
  color: #fff;
  font-size: 1.1rem;
  cursor: pointer;
}
#host-frame {
  width: 100%;
  height: calc(100% - 2.6rem);
  border: none;
  display: block;
}

.admin-advanced { margin-top: 1.5rem; }
.admin-advanced > summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--muted);
  padding: 0.5rem 0;
  list-style: revert;
}

/* Projector: wall of pinned-guest chips while a Love Map round is open */
.g4-pinned-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  max-width: 70rem;
  margin: 0.75rem auto;
  font-size: 1rem;
}

/* Reveal recap + standings panel. Stacked by default (guest pages); on the
   projector they sit SIDE-BY-SIDE so the standings don't push off the bottom of
   an older 720p screen. */
.results-split { margin: 0 auto; }
.results-panel { margin-top: 0.5em; }
.results-panel h3 {
  font-size: 0.5em;
  margin: 0.4em 0 0.2em;
  color: var(--muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
body.projector-mode .results-split {
  display: grid;
  grid-template-columns: 1fr minmax(20rem, 28rem);
  gap: 2rem;
  align-items: center;
  max-width: 110rem;
}
body.projector-mode .results-panel { margin-top: 0; text-align: left; }
body.projector-mode .results-panel .g3-standings { margin: 0; }

/* Love Map reveal pins need to read from the back of the room. */

/* Leaflet divIcon pins (projector swarm) — kill the default white box */
.g4-pin { background: none; border: none; }
.g4-pin-couple { overflow: visible; }
.g4-couple-label {
  position: absolute;
  bottom: 42px;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 0.7rem;
  font-weight: 700;
  color: #4a3500;
  background: linear-gradient(135deg, #f6e27a, #e6b800);
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
