/* common.css provides shared design tokens (:root vars) */
:root {
  --r: 14px;
}
* { margin:0; padding:0; box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
html, body { height:100%; touch-action: manipulation; }
/* Stop iOS rubber-band scrolling at the boundaries from feeling like the
   page is loose. `overscroll-behavior: contain` also prevents pull-to-
   refresh from triggering inside the game. */
html, body { overscroll-behavior: contain; }
/* Defensive overflow guards: images shouldn't force a horizontal scrollbar
   (Dicebear avatars, venue logos), and any wide media in answers stays
   within the column. */
img, video, canvas, svg { max-width:100%; height:auto; }
/* UI chrome (logo, labels, score numbers, buttons) shouldn't be selectable
   on long-press — answer text + leaderboard names stay selectable so users
   can copy them if they want. */
.logo, .field-label, .my-score-label, .waiting-title, .answer-result-title,
.answer-points, .answer-result-icon, .my-score-num, .break-player-countdown,
.btn, .choice-letter, .stats-chip, .player-count-chip {
  -webkit-user-select:none; user-select:none;
}
/* Use dynamic viewport units so in-app browsers (Instagram, TikTok, etc.)
   that overlay chrome on top of the page don't crop our layout. dvh adapts
   as the URL bar shows/hides; vh/% would lock us to the initial height. */
@supports (height: 100dvh) {
  html, body { height:100dvh; }
}
body {
  font-family:'DM Sans',sans-serif; background:var(--bg); color:var(--text);
  display:flex; flex-direction:column; overflow-y:auto; overflow-x:hidden;
  width:100%; max-width:100vw;
  background-image:
    radial-gradient(ellipse 90% 50% at 20% 0%, rgba(64,128,255,0.10) 0%, transparent 60%),
    radial-gradient(ellipse 80% 60% at 80% 100%, rgba(240,192,64,0.07) 0%, transparent 60%);
  background-size: 200% 200%;
  animation: bgDrift 24s ease-in-out infinite alternate;
}
@keyframes bgDrift {
  0%   { background-position: 0% 0%, 100% 100%; }
  100% { background-position: 40% 20%, 60% 80%; }
}
@media (prefers-reduced-motion: reduce) {
  body { animation: none; }
}

.screen {
  display:none; flex-direction:column;
  /* Grow with content instead of being locked to the viewport.
     When new elements (vote section, avatar regen button, presence
     chips, leaderboard rows) appear mid-round the screen now
     expands and the body scrolls, instead of elements overlapping
     or getting hidden below a fixed-height clip. */
  min-height:100%;
  padding:52px 16px 16px; gap:14px;
  -webkit-overflow-scrolling:touch;
}
/* Respect iOS bottom home-indicator / safe area so content (Share button,
   leaderboards) isn't clipped under the notch/home bar on iPhone 14+/17 Pro. */
.screen { padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)); padding-top: calc(52px + env(safe-area-inset-top, 0px)); }
.screen.active { display:flex; animation:screenIn 0.22s ease-out both; }
@keyframes screenIn { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .screen.active { animation:none; }
}
/* `safe center` keeps content centered when it fits, but flips to
   flex-start when it overflows — so items that would have been
   pushed above the viewport stay reachable by scrolling. */
#join, #waiting, #answered, #opBreak, #score, #roundEnd, #nightEnd, #error { justify-content: safe center; }

.logo { font-family:'Bebas Neue',sans-serif; font-size:1.8rem; letter-spacing:4px; background:linear-gradient(135deg,var(--gold),var(--amber)); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }

.btn { width:100%; padding:18px; border-radius:var(--r); border:none; font-family:'DM Sans',sans-serif; font-size:1.05rem; font-weight:700; cursor:pointer; transition:all 0.15s; min-height:48px; touch-action:manipulation; display:inline-flex; align-items:center; justify-content:center; gap:8px; }
.btn:disabled { opacity:0.65; cursor:not-allowed; transform:none !important; filter:none !important; }
.btn-primary { background:linear-gradient(135deg,var(--gold),var(--amber)); color:#080b14; }
.btn-primary:active:not(:disabled) { filter:brightness(0.9); transform:scale(0.98); }
.btn-ghost { background:transparent; color:var(--muted); border:1px solid var(--border); }
.btn-ghost:active:not(:disabled) { background:var(--surface); transform:scale(0.98); }
/* Inline spinner used in the Join button while we're connecting */
.btn-spinner { width:16px; height:16px; border:2px solid rgba(0,0,0,0.25); border-top-color:#080b14; border-radius:50%; animation:spin 0.6s linear infinite; display:inline-block; vertical-align:middle; }

input[type=text] {
  background:var(--surface2); border:2px solid var(--border); border-radius:var(--r);
  color:var(--text); font-family:'DM Sans',sans-serif; font-size:1.1rem;
  padding:16px 18px; outline:none; width:100%; transition:border-color 0.2s; min-height:48px;
}
input:focus { border-color:var(--gold); }

/* Player-specific "Powered by" override — the shared badge styling is
   fine on the TV (which has acres of real estate) but on a phone mid-
   game it's intrusive. Dim and shrink it, and hide it once past the
   join screen (toggled via .playing on <body> by showScreen). */
.powered-by {
  font-size:0.55rem; opacity:0.35; letter-spacing:1px;
  bottom:calc(4px + env(safe-area-inset-bottom, 0px));
}
.powered-by .pb-brand { font-size:0.65rem; letter-spacing:1.5px; }
body.playing .powered-by { display:none; }

/* Persistent identity strip — fixed at the very top of the player
   page during gameplay so the patron can always see who the device
   is logged in as. Pre-fix the name lived only in localStorage and
   on the lobby waiting screen, so a player coming back from the bar
   mid-night had no way to verify they were on their own phone. Tiny
   pill so it doesn't compete with the question/answer UI. Only
   visible when body.playing is set (toggled by showScreen). */
.player-identity {
  position:fixed; top:calc(4px + env(safe-area-inset-top, 0px)); right:8px;
  z-index:40; display:none; align-items:center; gap:6px;
  padding:4px 10px 4px 4px; border-radius:999px;
  background:rgba(8,11,20,0.85); border:1px solid var(--border);
  font-size:0.75rem; color:var(--muted); font-weight:600;
  max-width:50vw; pointer-events:none;
}
body.playing .player-identity { display:flex; }
.player-identity img, .player-identity svg { width:22px; height:22px; border-radius:50%; flex-shrink:0; }
.player-identity-name { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
/* Make the chip tappable — opens the avatar picker. pointer-events
   was 'none' on the wrapper (cosmetic-only); switching to 'auto' lets
   clicks land. The dedicated cursor + hover gives the affordance. */
.player-identity { pointer-events:auto; cursor:pointer; }
.player-identity:hover { background:rgba(20,30,55,0.92); }
/* When the connection bar is visible (.disconnected / .reconnecting),
   it spans the full top of the screen with z-index:100 — high enough
   to stack OVER .player-identity (z:40). But the pill's fixed-position
   anchor sometimes leaks pixels around the bar's right edge on Safari
   + some Android browsers, looking like they overlap. Push the pill
   below the bar's bottom edge so it's always cleanly out of its way.
   The bar's height is ~36–42px (padding + a tiny button); 48px clears
   it on every viewport tested. body:has(.connection-bar.disconnected)
   (vs the general-sibling ~) is required because .player-identity
   comes BEFORE .connection-bar in the DOM, so ~ doesn't match it. */
body:has(.connection-bar.disconnected) .player-identity,
body:has(.connection-bar.reconnecting) .player-identity {
  top: calc(48px + env(safe-area-inset-top, 0px));
}

/* Welcome-back banner takes over the join screen. Hides the auth
   CTA, the auth form, the email-verify nag, and the actual join
   fields (name input + emoji + Join button) — so a returning
   patron sees ONE clear set of choices (Continue / Use different
   name / Log out) instead of four competing surfaces. Cleared by
   forgetPreviousSession / welcomeBackLogout, which both surface
   the regular join form. */
body.welcome-back-active #playerAuthStatus,
body.welcome-back-active #playerEmailNotice,
body.welcome-back-active #playerAuthCTA,
body.welcome-back-active #playerAuthForm,
body.welcome-back-active #joinFields {
  display: none !important;
}

/* Team affiliation chip on the question + answered screens. Inline
   above the category tag. JS sets --team-color (hex) inline on the
   chip element from state.teams[i].color so a single CSS rule can
   tint the background (low alpha) + border + team-name color from
   one source of truth. Hidden by default (solo mode); player.js sets
   display:flex when the venue is in team mode.
   Pre-fix this chip was a small muted-grey pill with just a colored
   dot — easy to miss at a glance. Now the whole chip reads in the
   team's color so a patron knows instantly which team owns their
   points. */
.player-team-chip {
  display:none; align-items:center; gap:8px;
  margin: 8px auto 10px;
  padding: 6px 14px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--team-color, #888) 18%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--team-color, #888) 60%, transparent);
  font-size: 0.85rem; font-weight: 700;
  color: var(--text);
  max-width: max-content;
}
.player-team-chip .dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--team-color, #888);
  flex-shrink: 0;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--team-color, #888) 25%, transparent);
}
.player-team-chip .team-name {
  color: color-mix(in srgb, var(--team-color, #fff) 85%, white);
  letter-spacing: 0.02em;
}

/* Welcome-back banner: shown on the join screen when a btSession
   exists in localStorage. Replaces the old silent-auto-reconnect on
   page load. Patron picks Continue (reclaim slot) or "Use a different
   name" (clears session). */
.welcome-back-banner {
  display:flex; flex-direction:column; gap:10px;
  background:rgba(95,201,122,0.10);
  border:1px solid rgba(95,201,122,0.32);
  border-radius:12px; padding:12px 14px; margin-bottom:6px;
}
.welcome-back-text { font-size:0.95rem; color:var(--text); text-align:center; }
.welcome-back-text strong { color:var(--gold); }
.welcome-back-actions {
  display:flex; flex-direction:column; align-items:center;
  gap:10px;
}
.welcome-back-actions .btn { width:auto; padding:8px 16px; min-height:36px; }

/* ── Player auth ── */
.auth-cta {
  display:flex; align-items:center; justify-content:center; flex-wrap:wrap;
  gap:8px 10px; font-size:0.85rem; color:var(--muted); padding:4px 0 4px;
}
.auth-cta-sub { flex-basis:100%; text-align:center; font-size:0.72rem; opacity:0.7; margin-top:-2px; }
/* .auth-link, .auth-dismiss, .auth-form-foot live in common.css now since
   the venue login/register modals on / use the same footer pattern. */
.auth-status {
  display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:0.88rem; color:var(--text);
  background:rgba(48,204,112,0.12); border:1px solid rgba(48,204,112,0.28);
  border-radius:12px; padding:10px 14px;
}
.auth-status strong { color:var(--gold); }
.auth-status .auth-link { margin-left:auto; font-size:0.8rem; }
/* Pre-fix the actions wrapped to two lines mid-word ("My / Stats",
   "Log / out") on narrow phones because the parent had neither
   flex-wrap nor a nowrap rule on the children. flex-wrap on the
   parent now drops the action group to a second row when there's
   no space; nowrap on each link keeps each label intact. */
.auth-form {
  display:flex; flex-direction:column; gap:8px;
  background:var(--surface); border:1px solid var(--border);
  border-radius:14px; padding:14px; margin:4px 0;
}
.auth-form .field-label { margin-top:6px; }
.auth-form .field-label:first-of-type { margin-top:0; }
.auth-form .btn { margin-top:10px; }
/* .auth-form-tabs and .auth-tab live in common.css now since the venue
   login/register modals on / use the same toggle pattern. */
.auth-status-actions { display:flex; gap:12px; margin-left:auto; }
.auth-status-actions .auth-link { font-size:0.8rem; white-space:nowrap; }
.email-notice {
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  background:rgba(240,192,64,0.12); border:1px solid rgba(240,192,64,0.35);
  color:#ffe0a0; border-radius:12px; padding:10px 14px; font-size:0.88rem;
}
.email-notice .auth-link { margin-left:auto; color:var(--gold); font-size:0.82rem; }

/* ── OAuth row ──────────────────────────────────────────────────────── */
.oauth-row { display:flex; flex-direction:column; gap:12px; margin-bottom:4px; }
.btn-oauth {
  display:flex; align-items:center; justify-content:center; gap:12px;
  width:100%; padding:12px 14px; border-radius:12px; font-weight:600;
  font-family:inherit; font-size:0.95rem; cursor:pointer; min-height:48px;
  border:1px solid var(--border); background:#ffffff; color:#1f1f1f;
  transition:transform 0.1s ease, filter 0.15s ease;
  touch-action:manipulation;
}
.btn-oauth:active:not(:disabled) { transform:scale(0.985); filter:brightness(0.94); }
.btn-oauth:disabled { opacity:0.6; cursor:not-allowed; }
.btn-oauth .oauth-icon { flex-shrink:0; }
.oauth-divider {
  display:flex; align-items:center; gap:10px; color:var(--muted);
  font-size:0.78rem; text-transform:uppercase; letter-spacing:2px; font-weight:600;
}
.oauth-divider::before, .oauth-divider::after {
  content:''; flex:1; height:1px; background:var(--border);
}
.oauth-divider span { flex-shrink:0; }

/* ── Clean auth inputs + password toggle ─────────────────────────────── */
/* Shared .auth-input class applies a tighter, more modern style to auth
   form fields (email, password, display name) than the global input
   rule. Taller tap targets, softer border, gold focus ring. */
.auth-input {
  width:100%; padding:12px 14px; min-height:46px;
  background:var(--surface2); border:1px solid var(--border); border-radius:10px;
  color:var(--text); font-family:inherit; font-size:16px; /* 16px kills iOS zoom */
  transition:border-color 0.15s ease, background 0.15s ease;
  -webkit-appearance:none; appearance:none;
}
.auth-input:focus { outline:none; border-color:var(--gold); background:var(--surface); box-shadow:0 0 0 3px rgba(240,192,64,0.15); }
.auth-input::placeholder { color:var(--muted); opacity:0.7; letter-spacing:2px; }

.password-wrap { position:relative; display:flex; align-items:stretch; }
.password-wrap .auth-input { padding-right:44px; /* room for toggle */ letter-spacing:2px; }
.password-toggle {
  position:absolute; right:4px; top:50%; transform:translateY(-50%);
  width:40px; height:40px; border-radius:8px; border:none; background:transparent;
  color:var(--muted); cursor:pointer; display:flex; align-items:center; justify-content:center;
  font-size:1rem; touch-action:manipulation;
}
.password-toggle:hover { color:var(--text); background:var(--surface); }
.password-toggle:focus-visible { outline:2px solid var(--gold); outline-offset:-2px; }
.password-toggle-icon { line-height:1; pointer-events:none; }

/* Password strength meter (.pw-meter) lives in common.css now since the
   venue register on / uses it too. */

/* ── Avatar actions + favorites ──────────────────────────────────────── */
.avatar-actions {
  display:flex; gap:8px; flex-wrap:wrap; justify-content:center;
  width:100%; max-width:360px;
}
.avatar-actions .btn { flex:1; min-width:140px; padding:10px 14px; min-height:40px; }
.avatar-favorite-toggle.is-saved { border-color:var(--gold); color:var(--gold); background:rgba(240,192,64,0.1); }

.avatar-favorites {
  display:flex; flex-wrap:wrap; gap:8px; justify-content:center;
  width:100%; max-width:360px;
}
.favorite-chip {
  position:relative; width:52px; height:52px;
}
.favorite-use {
  width:100%; height:100%; border-radius:12px; padding:0;
  background:var(--surface); border:2px solid var(--border);
  display:flex; align-items:center; justify-content:center;
  cursor:pointer; font-family:inherit; -webkit-appearance:none; appearance:none;
  transition:border-color 0.12s ease, transform 0.1s ease;
  touch-action:manipulation; overflow:hidden;
}
.favorite-use:active { transform:scale(0.95); }
.favorite-use img { width:42px; height:42px; border-radius:8px; display:block; pointer-events:none; }
.favorite-chip.active .favorite-use { border-color:var(--gold); box-shadow:0 0 0 2px rgba(240,192,64,0.25); }
.favorite-remove {
  position:absolute; top:-8px; right:-8px;
  width:26px; height:26px; border-radius:50%; padding:0;
  background:var(--red); color:#fff; border:none; font-size:1rem; line-height:1;
  cursor:pointer; display:flex; align-items:center; justify-content:center;
  font-family:inherit; -webkit-appearance:none; appearance:none;
  opacity:0; transition:opacity 0.12s ease;
  touch-action:manipulation;
  /* Invisible hit-area extender — visible 26x26 pulls double duty as
     a 38x38 tap target for thumbs without dominating the chip layout. */
}
.favorite-remove::before {
  content:''; position:absolute; inset:-6px; border-radius:50%;
}
.favorite-chip:hover .favorite-remove,
.favorite-chip:focus-within .favorite-remove { opacity:1; }

/* ← / → reorder buttons revealed the same way the × is. Positioned
   outside the chip edge so the avatar stays fully visible. */
.favorite-move {
  position:absolute; top:50%; transform:translateY(-50%);
  width:24px; height:34px; border-radius:6px; padding:0;
  background:var(--surface2); color:var(--text); border:1px solid var(--border);
  font-size:0.95rem; line-height:1; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  font-family:inherit; -webkit-appearance:none; appearance:none;
  opacity:0; transition:opacity 0.12s ease, background 0.12s ease;
  touch-action:manipulation; z-index:2;
}
/* Invisible hit-area extender — same trick as .favorite-remove. Visible
   24x34 stays inside the chip's outer edge; tap target reaches ~36x46. */
.favorite-move::before {
  content:''; position:absolute; inset:-6px; border-radius:8px;
}
.favorite-move:hover { background:var(--gold); color:#080b14; border-color:var(--gold); }
.favorite-move:active { transform:translateY(-50%) scale(0.9); }
.favorite-move-left  { left:-10px; }
.favorite-move-right { right:-10px; }
.favorite-chip:hover .favorite-move,
.favorite-chip:focus-within .favorite-move { opacity:1; }
/* On touch devices always show the remove button (no hover to trigger it).
   The reorder arrows stay hidden — they hang outside the chip and overlap
   adjacent chips' arrows in the 8px gap, which read as broken UI on mobile.
   Desktop hover still reveals them for keyboard/mouse reordering. */
@media (hover: none) {
  .favorite-remove { opacity:0.85; }
  .favorite-move   { display:none; }
}

/* ── Player stats screen ─────────────────────────────────────────────── */
.stats-header { display:flex; align-items:center; gap:12px; margin-bottom:14px; }
.stats-title { font-family:'Bebas Neue',sans-serif; font-size:1.8rem; letter-spacing:3px; color:var(--gold); line-height:1; }
.stats-top { display:flex; align-items:center; gap:16px; background:var(--surface); border:1px solid var(--border); border-radius:14px; padding:16px; margin-bottom:14px; }
.stats-avatar { width:72px; height:72px; border-radius:50%; overflow:hidden; flex-shrink:0; background:var(--surface2); }
.stats-avatar img { width:100%; height:100%; display:block; }
.stats-name { font-size:1.2rem; font-weight:700; color:var(--text); }
.stats-email { font-size:0.85rem; color:var(--muted); margin-top:2px; }
.stats-totals { display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin-bottom:18px; }
.stats-total-card { background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:14px 10px; text-align:center; }
.stats-total-num { font-family:'Bebas Neue',sans-serif; font-size:2rem; color:var(--gold); line-height:1; }
.stats-total-label { font-size:0.7rem; color:var(--muted); letter-spacing:1.5px; text-transform:uppercase; font-weight:600; margin-top:4px; }
.stats-section-label { font-size:0.75rem; font-weight:700; letter-spacing:2px; text-transform:uppercase; color:var(--muted); margin-bottom:8px; }
.stats-venue-list { display:flex; flex-direction:column; gap:10px; }
.stats-venue-card {
  display:grid; grid-template-columns:1fr auto; gap:4px 12px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:14px 16px;
}
.stats-venue-name { font-weight:700; color:var(--text); font-size:1rem; }
.stats-venue-code { font-size:0.72rem; color:var(--muted); letter-spacing:2px; text-transform:uppercase; }
.stats-venue-stats { display:flex; gap:14px; grid-column:1 / -1; flex-wrap:wrap; margin-top:8px; font-size:0.82rem; color:var(--muted); }
.stats-venue-stats strong { color:var(--gold); font-variant-numeric:tabular-nums; }
.stats-venue-rank {
  display:inline-flex; align-items:center; gap:4px; background:rgba(240,192,64,0.15);
  border:1px solid rgba(240,192,64,0.4); border-radius:999px; padding:2px 10px;
  font-size:0.78rem; font-weight:700; color:var(--gold); align-self:start; justify-self:end;
}
/* Cross-night streak badge — gentle red/orange to differentiate from
   the gold "this month" rank pill while reading as "achievement". Uses
   the same chip shape so the two badges stack nicely on the right side
   of the venue card when both apply. */
.stats-venue-streak {
  display:inline-flex; align-items:center; gap:4px;
  background:rgba(238,51,85,0.12);
  border:1px solid rgba(238,51,85,0.4);
  border-radius:999px; padding:2px 10px;
  font-size:0.78rem; font-weight:700; color:#ffb5c2;
  align-self:start; justify-self:end;
  grid-column:2;
}
.stats-empty { text-align:center; color:var(--muted); padding:32px 16px; font-size:0.9rem; }

/* ── JOIN ── */
/* `safe center` flips to flex-start when content overflows — critical on
   small phones where the join form (logo + inputs + avatar + style picker
   + button) is taller than the viewport. Plain `center` clips the top
   and hides the avatar; `safe center` keeps it reachable by scrolling. */
#join { justify-content:safe center; align-items:center; text-align:center; gap:24px; }
.join-tagline { font-size:0.85rem; color:var(--muted); letter-spacing:2px; text-transform:uppercase; }
/* Cap the form width so it doesn't span a full laptop monitor. Patrons
   are usually on phones, but when they hit /play on a laptop browser
   (during dev, or because they typed it in instead of scanning) we want
   a centered column rather than a half-mile-wide form. 520px matches
   common signup-form widths and keeps inputs at a comfortable touch
   target. width:100% on narrow viewports stays mobile-correct. */
.join-form { display:flex; flex-direction:column; gap:14px; width:100%; max-width:520px; margin:0 auto; }
.field-label { font-size:0.75rem; font-weight:700; letter-spacing:2px; text-transform:uppercase; color:var(--muted); text-align:left; margin-bottom:6px; }
/* Bumped opacity from 0.75 → 0.95 so the hint stays readable in
   dark bar lighting + on sun-glare-affected phone screens. The
   muted color already provides the visual hierarchy (lower contrast
   than --text); the previous opacity stack on top of that pushed
   the contrast ratio under WCAG AA for some phone screens. */
.field-hint { font-weight:400; letter-spacing:0.5px; text-transform:none; color:var(--muted); opacity:0.95; font-size:0.72rem; margin-left:4px; }
.join-fields { display:flex; flex-direction:column; gap:14px; }

.emoji-row { display:flex; flex-direction:column; align-items:center; gap:12px; }
.avatar-preview { width:160px; height:160px; border-radius:50%; background:var(--surface2); border:3px solid var(--border); overflow:hidden; display:flex; align-items:center; justify-content:center; position:relative; box-sizing:border-box; }
/* width:100% + max-width:none so the global `img { max-width:100% }`
   doesn't cap us to (160 - 2*3 = 154)px and produce a slight oval. */
.avatar-preview-img { width:100%; height:100%; max-width:none; border-radius:50%; }
/* Loading state — shown while the Dicebear SVG is fetching on a cold
   TLS connection. Pulsing gold border + ✨ glyph tells the player "your
   avatar is coming", rather than an empty circle that feels broken. */
.avatar-preview.loading { border-color:var(--gold); animation:avatarPulse 1.2s ease-in-out infinite; }
.avatar-preview.loading::before {
  content:'✨';
  position:absolute; inset:0;
  display:flex; align-items:center; justify-content:center;
  font-size:3rem; opacity:0.6; pointer-events:none;
  animation:avatarTwinkle 1.2s ease-in-out infinite;
}
@keyframes avatarPulse { 0%,100% { box-shadow:0 0 0 0 rgba(240,192,64,0.35); } 50% { box-shadow:0 0 0 10px rgba(240,192,64,0); } }
@keyframes avatarTwinkle { 0%,100% { opacity:0.3; } 50% { opacity:0.8; } }
@media (prefers-reduced-motion: reduce) {
  .avatar-preview.loading { animation:none; }
  .avatar-preview.loading::before { animation:none; }
}
.avatar-regen { width:auto !important; min-width:0 !important; padding:10px 20px !important; font-size:0.95rem !important; }
.avatar { border-radius:50%; vertical-align:middle; }
.avatar.avatar-photo { object-fit:cover; }
.avatar-preview-img.avatar-photo { object-fit:cover; border-radius:50%; }
.avatar-photo-section { display:flex; flex-direction:column; gap:8px; padding:8px 0 0; border-top:1px solid rgba(255,255,255,0.06); margin-top:8px; }
.avatar-inline { vertical-align:middle; margin-right:6px; }

/* ── WAITING ── */
#waiting { justify-content:safe center; align-items:center; text-align:center; gap:20px; }
.waiting-title { font-family:'Bebas Neue',sans-serif; font-size:2.2rem; letter-spacing:3px; color:var(--gold); }
.waiting-sub { color:var(--muted); font-size:0.9rem; line-height:1.5; }
.player-count-chip { background:var(--surface); border:1px solid var(--border); border-radius:99px; padding:8px 20px; font-size:0.88rem; color:var(--muted); }
.player-count-chip span { color:var(--text); font-weight:700; }
.pulse-ring { width:80px; height:80px; border-radius:50%; border:3px solid var(--gold); animation:ring 2s ease infinite; position:relative; }
.pulse-ring::after { content:'⚡'; position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:2rem; }
@keyframes ring { 0%{box-shadow:0 0 0 0 rgba(240,192,64,0.4)} 70%{box-shadow:0 0 0 20px rgba(240,192,64,0)} 100%{box-shadow:0 0 0 0 rgba(240,192,64,0)} }

/* ── QUESTION ── */
/* Use long-form padding so the safe-area-inset from .screen survives
   (a `padding:` shorthand at higher specificity would wipe the env() values
   and leave the topbar hidden behind the iPhone notch / dynamic island). */
#question {
  padding-top:    calc(52px + env(safe-area-inset-top, 0px));
  padding-right:  14px;
  padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  padding-left:   14px;
  gap:12px;
}
/* Top bar can get tight on narrow phones with a 3-digit bonus + 2-digit timer.
   `gap` + `flex-wrap:nowrap` keeps them from colliding, and `min-width:0`
   on the children lets them shrink rather than push out of the screen. */
.q-topbar { display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:nowrap; }
.q-topbar > * { min-width:0; }
.q-progress { font-size:0.78rem; font-weight:700; letter-spacing:2px; color:var(--muted); text-transform:uppercase; }
/* font-variant-numeric: tabular-nums keeps each digit the same width so
   the countdown doesn't visibly jitter as digits roll over (Bebas Neue
   has variable-width numerals — 15 and 10 occupy noticeably different
   widths). Without this the timer text rect shifts left/right on every
   tick which reads as a stutter. */
.q-timer { font-family:'Bebas Neue',sans-serif; font-size:2rem; letter-spacing:2px; transition:color 0.3s; font-variant-numeric:tabular-nums; }
/* Big countdown on the #answered screen — same Bebas-numerals styling
   as the TV's qBigTimer so the player sees the time-to-reveal at a
   glance. Color states .warn/.danger mirror .q-timer via the shared
   timer engine in player.js (setQuestionTimerColor). */
.answered-big-timer { font-family:'Bebas Neue',sans-serif; font-size:4rem; letter-spacing:3px; line-height:1; margin:8px auto 12px; color:var(--gold); transition:color 0.3s; font-variant-numeric:tabular-nums; }
.answered-big-timer.warn { color:var(--amber); }
.answered-big-timer.danger { color:var(--red); animation:pulse 0.5s ease infinite alternate; }
.q-bonus { font-family:'Bebas Neue',sans-serif; font-size:1.1rem; letter-spacing:1px; color:var(--gold); padding:2px 10px; border-radius:99px; background:rgba(240,192,64,0.12); border:1px solid rgba(240,192,64,0.3); transition:all 0.3s; font-variant-numeric:tabular-nums; }
.q-bonus.low { color:var(--muted); background:transparent; border-color:var(--border); }
/* Bonus window has effectively elapsed (≤10 points worth of bonus
   left). Visually mute the chip so a patron rushing the tap doesn't
   misread "+10" as still-bonus-worthy. JS adds .expired when the
   bonus value drops below the floor — see player.js timer tick. */
.q-bonus.expired { color:var(--muted); background:transparent; border-color:var(--border); opacity:0.55; }
.q-answered-count { font-size:0.75rem; color:var(--muted); text-align:right; height:14px; letter-spacing:1px; }

/* ── Round-end stats/rank/streak ── */
.stats-chip { font-size:0.78rem; color:var(--muted); padding:6px 12px; background:var(--surface); border:1px solid var(--border); border-radius:99px; display:inline-block; }
.stats-chip strong { color:var(--text); }
.rank-move { font-size:0.75rem; margin-left:4px; font-weight:700; }
.rank-move.up { color:var(--green); }
.rank-move.down { color:var(--red); }
.streak-badge { color:var(--amber); font-size:0.78rem; margin-left:4px; font-weight:700; }

/* ── Waiting room presence ── */
.waiting-presence { display:flex; flex-wrap:wrap; gap:6px; justify-content:center; max-width:320px; margin-top:8px; }
.waiting-presence .presence-chip { display:inline-flex; align-items:center; gap:4px; padding:4px 8px; background:var(--surface); border:1px solid var(--border); border-radius:99px; font-size:0.75rem; color:var(--muted); max-width:140px; }
.waiting-presence .presence-chip.me { border-color:var(--gold); color:var(--gold); }
.waiting-presence .presence-chip img { width:18px; height:18px; border-radius:50%; }
.waiting-presence .presence-chip span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
@keyframes presence-pop { 0% { transform:scale(0.6); opacity:0; } 100% { transform:scale(1); opacity:1; } }
.waiting-presence .presence-chip { animation:presence-pop 0.3s ease-out; }
@media (prefers-reduced-motion: reduce) { .waiting-presence .presence-chip { animation:none; } }

/* ── Avatar style picker ── */
.avatar-style-label { font-size:0.7rem; font-weight:700; letter-spacing:2px; text-transform:uppercase; color:var(--muted); margin-top:6px; }
.avatar-style-picker { display:flex; flex-wrap:wrap; gap:8px; justify-content:center; margin-top:4px; max-width:360px; width:100%; padding:2px; }
.style-chip { background:var(--surface); border:2px solid var(--border); border-radius:12px; padding:4px; cursor:pointer; transition:all 0.15s; min-width:50px; min-height:50px; display:inline-flex; align-items:center; justify-content:center; touch-action:manipulation; -webkit-appearance:none; appearance:none; font-family:inherit; flex:0 0 auto; }
.style-chip.active { border-color:var(--gold); background:rgba(240,192,64,0.12); box-shadow:0 0 0 2px rgba(240,192,64,0.2); }
.style-chip:active { transform:scale(0.94); }
.style-chip img { display:block; width:38px; height:38px; pointer-events:none; }
.avatar-regen { touch-action:manipulation; }
.q-timer.warn { color:var(--amber); }
.q-timer.danger { color:var(--red); animation:pulse 0.5s ease infinite alternate; }
@keyframes pulse { from{opacity:1} to{opacity:0.4} }

.progress-bar { height:4px; background:var(--border); border-radius:2px; overflow:hidden; }
.progress-bar-fill { height:100%; background:linear-gradient(90deg,var(--gold),var(--amber)); border-radius:2px; transition:width 1s linear; }

.round-header {
  font-family:'Bebas Neue',sans-serif; font-size:1.1rem; letter-spacing:2px;
  color:var(--gold); text-align:center; padding:6px 0;
  border-bottom:1px solid var(--border);
}

.category-tag { display:inline-flex; align-items:center; background:rgba(64,128,255,0.1); border:1px solid rgba(64,128,255,0.25); color:var(--blue); font-size:0.7rem; font-weight:700; letter-spacing:2px; text-transform:uppercase; padding:4px 12px; border-radius:99px; }

/* hyphens removed (was hyphens:auto) — soft hyphenation was splitting
   common words mid-prompt ("whis-tled", "fran-chise", "at-tendant")
   on phone widths, which read as broken at trivia speeds. The TV CSS
   never had hyphenation; this brings the player CSS in line. */
.q-text { font-size:clamp(1.05rem,4.3vw,1.4rem); font-weight:700; line-height:1.35; padding:8px 0; overflow-wrap:break-word; word-wrap:break-word; }

.choices { display:flex; flex-direction:column; gap:10px; }
.choice-btn {
  background:var(--surface); border:2px solid var(--border); border-radius:var(--r);
  color:var(--text); font-family:'DM Sans',sans-serif; font-size:1.02rem; font-weight:500; line-height:1.3;
  padding:14px 14px; text-align:left; cursor:pointer; transition:all 0.15s;
  display:flex; align-items:center; gap:12px; width:100%; min-height:64px;
  word-break:break-word; touch-action:manipulation;
}
.choice-btn:active:not(:disabled) { transform:scale(0.97); }
.choice-btn:disabled { cursor:default; }
.choice-btn:focus-visible { outline:2px solid var(--gold); outline-offset:2px; }
/* Brief green tint on the just-tapped button when the server's
   player:answer_ack lands — gives the patron a "yes, locked in"
   signal before the screen flips to #answered. ~350ms is enough
   to register without delaying the transition. */
.choice-btn.ack-flash { border-color:var(--green) !important; background:rgba(48,204,112,0.12) !important; transition:border-color 0.15s, background 0.15s; }
.choice-letter { width:34px; height:34px; border-radius:9px; background:var(--surface2); display:flex; align-items:center; justify-content:center; font-family:'Bebas Neue',sans-serif; font-size:1.1rem; flex-shrink:0; transition:all 0.2s; }
.choice-btn.selected { border-color:var(--gold); background:rgba(240,192,64,0.06); }
.choice-btn.correct { border-color:var(--green); background:rgba(48,204,112,0.1); animation:bounceIn 0.4s ease; }
.choice-btn.correct .choice-letter { background:var(--green); color:#080b14; }
.choice-btn.wrong { border-color:var(--red); background:rgba(238,51,85,0.08); }
.choice-btn.wrong .choice-letter { background:var(--red); color:#fff; }
/* #26: short-answer treatment — when every choice is ≤4 chars (numeric,
   "Yes"/"No", "True"/"False"), the default left-aligned text leaves a
   near-empty button. Center + bump font-size; pin the letter chip to
   the corner so the answer claims the full button. JS toggles
   .choices-short on the #choices container. */
.choices-short .choice-btn { position:relative; justify-content:center; min-height:84px; padding:18px 14px; font-size:2.2rem; font-weight:700; }
.choices-short .choice-btn .choice-letter { position:absolute; top:8px; left:8px; }
@keyframes bounceIn { 0%{transform:scale(1)} 40%{transform:scale(1.04)} 100%{transform:scale(1)} }

/* ── ANSWERED ── */
#answered, #opBreak { justify-content:center; align-items:center; text-align:center; gap:18px; }
.answer-result-icon { font-size:clamp(3.5rem, 14vw, 4.5rem); animation:popIn 0.4s cubic-bezier(0.34,1.56,0.64,1); }
@keyframes popIn { from{opacity:0;transform:scale(0.4)} to{opacity:1;transform:scale(1)} }
.answer-result-title { font-family:'Bebas Neue',sans-serif; font-size:clamp(2.2rem, 9vw, 3rem); letter-spacing:clamp(2px, 1vw, 4px); line-height:1; }
.answer-result-title.correct { color:var(--green); }
.answer-result-title.wrong { color:var(--red); }
/* Distinct treatment for "didn't answer in time" — amber not red so
   the player can tell timeout apart from a wrong pick at a glance. */
.answer-result-title.timeout { color:var(--amber); }
.answer-points { font-family:'Bebas Neue',sans-serif; font-size:clamp(1.6rem, 6vw, 2rem); color:var(--gold); letter-spacing:2px; }
.answer-wait { color:var(--muted); font-size:0.88rem; }

/* ── SCORE / ROUND END / NIGHT END ── */
#score, #roundEnd, #nightEnd { align-items:center; text-align:center; gap:14px; }
#roundEnd > *, #nightEnd > * { max-width:400px; width:100%; }
#roundEnd .lb-mini, #nightEnd .lb-mini { max-width:360px; }
#score { justify-content:center; gap:18px; }
/* Ensure the Share button on nightEnd never gets clipped under the iOS
   home indicator, even on tall phones. */
#nightEnd { padding-bottom: calc(32px + env(safe-area-inset-bottom, 0px)); }
/* Floating "Report bad question" button is position:fixed at bottom 14px;
   reserve clearance on screens that also have a Change Avatar button so
   the two don't stack. */
#answered, #waiting, #roundEnd { padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px)); }
#nightShareBtn {
  margin: 4px auto 0;
  animation: shareNudge 2.4s ease-in-out 0.6s infinite;
}
@keyframes shareNudge {
  0%, 100% { transform: translateY(0); box-shadow: 0 4px 0 rgba(240,192,64,0.0); }
  50%      { transform: translateY(-3px); box-shadow: 0 10px 24px rgba(240,192,64,0.25); }
}
@media (prefers-reduced-motion: reduce) {
  #nightShareBtn { animation: none; }
}
.my-score-label { font-size:0.72rem; font-weight:700; letter-spacing:3px; text-transform:uppercase; color:var(--muted); }
.my-score-num {
  /* Clamp so 4–5 digit scores never overflow on narrow phones (320–360px).
     At 4rem (64px) a 5-digit score is ~330px, fits with side padding. */
  font-family:'Bebas Neue',sans-serif; font-size:clamp(4rem, 18vw, 6rem); letter-spacing:clamp(2px, 1vw, 4px);
  color:var(--gold); line-height:1;
  text-shadow: 0 0 24px rgba(240,192,64,0.25);
  animation: scoreGlow 3s ease-in-out infinite;
}
@keyframes scoreGlow {
  0%, 100% { text-shadow: 0 0 18px rgba(240,192,64,0.18); }
  50%      { text-shadow: 0 0 34px rgba(240,192,64,0.45); }
}
@media (prefers-reduced-motion: reduce) {
  .my-score-num { animation: none; }
}
.lb-mini { width:100%; display:flex; flex-direction:column; gap:8px; }
.lb-mini-row { display:flex; align-items:center; gap:12px; padding:12px 14px; background:var(--surface); border:1px solid var(--border); border-radius:10px; animation:slideUp 0.3s ease both; min-width:0; }
@keyframes slideUp { from{opacity:0;transform:translateY(12px)} to{opacity:1;transform:translateY(0)} }
.lb-mini-rank { font-family:'Bebas Neue',sans-serif; font-size:1.4rem; width:30px; text-align:center; color:var(--muted); flex-shrink:0; }
.lb-mini-name { flex:1 1 auto; min-width:0; font-size:0.95rem; font-weight:600; text-align:left; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.lb-mini-score { font-family:'Bebas Neue',sans-serif; font-size:1.4rem; color:var(--gold); letter-spacing:1px; flex-shrink:0; }
.lb-mini-row.mine { border-color:var(--gold); background:rgba(240,192,64,0.06); }
/* "You finished #X of Y" pin — surfaces the player's final rank
   above the leaderboard list so a mid-pack player who otherwise
   couldn't see their own row still closes the night with their
   placement. Same gold-on-dark treatment as a .mine row but without
   the avatar/score cells, since the score sits right above. */
/* position:sticky so a patron ranked mid-pack who scrolls the
   leaderboard to find their name doesn't lose the "You're #17 of 42"
   anchor at the top. top:0 pins it against the scroll container. */
.rank-pin { width:100%; max-width:360px; padding:10px 14px; background:rgba(240,192,64,0.08); border:1px solid var(--gold); border-radius:10px; color:var(--gold); font-family:'Bebas Neue',sans-serif; font-size:1.15rem; letter-spacing:2px; text-align:center; margin:6px 0 4px; position:sticky; top:0; z-index:5; backdrop-filter:blur(6px); }

/* ── PAUSED overlay ── */
.paused-overlay {
  position:fixed; inset:0; background:rgba(8,11,20,0.85); z-index:50;
  display:flex; align-items:center; justify-content:center; flex-direction:column; gap:12px;
}
.paused-overlay .paused-text {
  font-family:'Bebas Neue',sans-serif; font-size:3rem; letter-spacing:4px; color:var(--gold);
}
.paused-overlay .paused-sub { color:var(--muted); font-size:0.9rem; }

/* ── ERROR ── */
#error { justify-content:center; align-items:center; text-align:center; gap:16px; }
.error-icon { font-size:3.5rem; }
.error-title { font-family:'Bebas Neue',sans-serif; font-size:2rem; letter-spacing:2px; color:var(--red); }
.error-msg { color:var(--muted); font-size:0.9rem; line-height:1.5; max-width:280px; }

/* ── Open Ended Answer ── */
.open-answer-input {
  background:var(--surface2); border:2px solid var(--border); border-radius:var(--r);
  color:var(--text); font-family:'DM Sans',sans-serif; font-size:1.1rem;
  padding:16px 18px; outline:none; width:100%; transition:border-color 0.2s;
  min-height:56px;
}
.open-answer-input:focus { border-color:var(--gold); }
.open-answer-input:disabled { opacity:0.5; }

/* ── Category Voting ── */
.vote-options { display:flex; flex-direction:column; gap:8px; }
.vote-option {
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:14px 16px; cursor:pointer; transition:all 0.18s;
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  min-height:52px; touch-action:manipulation;
}
.vote-option:hover { border-color:var(--gold); background:var(--surface2); }
.vote-option:active { transform:scale(0.985); }
.vote-option.selected { border-color:var(--gold); background:rgba(240,192,64,0.12); }
.vote-option-name { font-weight:600; font-size:0.95rem; flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.vote-option-meta { font-size:0.78rem; color:var(--muted); flex-shrink:0; }
.vote-option-check { color:var(--gold); font-size:1.2rem; display:none; }
.vote-option.selected .vote-option-check { display:inline; }
/* Vote-for-reroll button — collective "let's see other categories" trigger.
   Lives at the bottom of every voteOptions container; majority of connected
   players triggers the server to re-pick. */
.vote-reroll-btn {
  display:block; width:100%; margin-top:10px;
  background:transparent; border:1px dashed var(--border);
  color:var(--muted); padding:10px 14px; border-radius:10px;
  font-family:inherit; font-size:0.85rem; cursor:pointer;
  transition:border-color 0.15s, color 0.15s, background 0.15s;
}
.vote-reroll-btn:hover { border-color:var(--gold); color:var(--text); }
.vote-reroll-btn.active { border-color:var(--gold); border-style:solid; background:rgba(240,192,64,0.12); color:var(--text); }

/* In-game avatar picker overlay (#14/#7) — modal that hosts the same
   style+seed picker the join screen renders, available during waiting,
   answered, and round-end so patrons can change avatar mid-game. */
.ingame-avatar-overlay {
  position:fixed; inset:0; z-index:50;
  background:rgba(8, 11, 20, 0.78);
  display:flex; align-items:center; justify-content:center;
  padding:env(safe-area-inset-top) 16px env(safe-area-inset-bottom) 16px;
}
.ingame-avatar-card {
  background:var(--surface); border:1px solid var(--border); border-radius:14px;
  padding:18px 16px 16px; max-width:420px; width:100%; max-height:80vh;
  overflow-y:auto; position:relative; -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.ingame-avatar-card::-webkit-scrollbar { display:none; }
.ingame-avatar-close {
  position:absolute; top:8px; right:10px; width:30px; height:30px;
  background:transparent; border:none; color:var(--muted);
  font-size:1.4rem; line-height:1; cursor:pointer; border-radius:6px;
}
.ingame-avatar-close:hover { background:rgba(255,255,255,0.06); color:var(--text); }
.ingame-avatar-title {
  font-size:0.78rem; font-weight:700; letter-spacing:2px; text-transform:uppercase;
  color:var(--muted); margin-bottom:14px; padding-right:30px;
}
.ingame-avatar-done { margin-top:14px; width:100%; }

/* ── Connection status ── */
/* Pad-top with the safe-area-inset so the bar sits below the iOS notch
   instead of behind it. */
/* Notification-bar layout: message text on the left, action button
   pushed to the right edge — same pattern as a typical mobile
   snackbar / system banner. Pre-fix the bar used text-align:center
   which put the message right under the pills' visual zone (which
   live at top-right), and on iOS Safari the pills' anti-alias
   pixels could leak around the bar's z-index stack, looking like an
   overlap. Flex layout cleanly separates "what's going on" (left)
   from "what to do about it" (right). */
.connection-bar {
  position:fixed; top:0; left:0; right:0; z-index:100;
  padding:calc(8px + env(safe-area-inset-top, 0px)) 16px 8px;
  font-size:0.82rem; font-weight:600;
  display:none; align-items:center; gap:12px;
  /* flex-wrap so the "Reconnecting…(attempt 3/10)" message + button
     reflow onto a second line on 320px phones instead of clipping
     the button off-screen. */
  flex-wrap:wrap;
}
.connection-bar.disconnected { display:flex; background:rgba(238,51,85,0.15); color:var(--red); border-bottom:1px solid rgba(238,51,85,0.3); }
.connection-bar.reconnecting { display:flex; background:rgba(240,112,32,0.15); color:var(--amber); border-bottom:1px solid rgba(240,112,32,0.3); }
#connectionMsg { flex:1 1 auto; min-width:0; text-align:left; }
.connection-bar button { background:var(--surface); border:1px solid var(--border); border-radius:6px; padding:6px 14px; font-size:0.78rem; font-weight:600; color:var(--text); cursor:pointer; margin-left:8px; white-space:nowrap; flex-shrink:0; min-height:36px; }

/* ── Form validation ── */
input.invalid { border-color:var(--red) !important; }
.field-error { font-size:0.78rem; color:var(--red); margin-top:4px; display:none; }
.field-error.show { display:block; }

/* ── Sending indicator ── */
.sending-indicator { display:flex; align-items:center; justify-content:center; gap:8px; color:var(--muted); font-size:0.88rem; padding:8px 0; }
.sending-indicator .spinner { width:16px; height:16px; border:2px solid var(--border); border-top-color:var(--gold); border-radius:50%; animation:spin 0.6s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }

/* ── Voting explanation ── */
.vote-explain { font-size:0.78rem; color:var(--muted); text-align:center; line-height:1.4; margin-bottom:8px; }

/* ── Break screen ── */
.break-player-countdown { font-family:'Bebas Neue',sans-serif; font-size:clamp(2.6rem, 12vw, 3.5rem); color:var(--text); letter-spacing:clamp(2px, 1vw, 4px); margin:8px 0; }
.break-player-next { color:var(--muted); font-size:0.9rem; }

/* ── Short viewport (Instagram/TikTok in-app browsers, landscape phones) ── */
/* When height is constrained, compress padding/gaps and shrink the avatar so
   the join screen and other vertically-centered screens don't get cramped.
   Long-form padding so we don't wipe out the safe-area-inset values. */
@media (max-height: 700px) {
  .screen {
    padding-top:    calc(44px + env(safe-area-inset-top, 0px));
    padding-right:  20px;
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    padding-left:   20px;
    gap:12px;
  }
  #question {
    padding-top:    calc(44px + env(safe-area-inset-top, 0px));
    padding-right:  16px;
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    padding-left:   16px;
    gap:10px;
  }
  #join { gap:14px; }
  .avatar-preview, .avatar-preview-img { width:96px; height:96px; }
  .waiting-title { font-size:1.8rem; }
  .my-score-num { font-size:4rem; }
  .answer-result-icon { font-size:3rem; }
  .answer-result-title { font-size:2.2rem; letter-spacing:3px; }
  .pulse-ring { width:60px; height:60px; }
  .pulse-ring::after { font-size:1.5rem; }
  .break-player-countdown { font-size:2.6rem; margin:4px 0; }
}

/* ── Mobile: keep the same generous join layout as desktop ── */
/* We intentionally do NOT shrink the avatar, logo, style chips, or button on
   small viewports — the desktop sizing (160px avatar, 1.8rem logo, 50px chips,
   18px button padding) reads great on phones too because the join screen is
   single-column and `safe center` lets it scroll if it overflows. We only
   tighten horizontal padding and ensure inputs use 16px to suppress iOS zoom. */
@media (max-width: 480px) {
  .screen { padding-left:14px; padding-right:14px; }
  input[type=text] { font-size:16px; }
  /* Allow style chips to fill the row but cap so we get ~6 per line */
  .avatar-style-picker { gap:8px; max-width:100%; }
}

/* Extra-small phones (iPhone SE-class, 375px and under) — only nudge paddings */
@media (max-width: 380px) {
  .screen { padding-left:12px; padding-right:12px; }
  .avatar-style-picker { gap:6px; }
  /* Avatar action buttons (Regenerate / Save avatar) had min-width:140px
     which forced a slight overflow that wrapped "Save avatar" to two
     lines on iPhone SE-class phones. Drop the floor so the label fits
     the available width. white-space:nowrap is the safety net for any
     future label edits. */
  .avatar-actions .btn { min-width:120px; padding:10px 8px; white-space:nowrap; }
  .avatar-actions .btn .btn-spinner { margin-right:4px; }
  /* Shrink avatar style chips a bit so all 6 fit one row on 320×568
     iPhone SE (was wrapping 5+1 with an awkward orphan). */
  .style-chip { min-width:42px; min-height:42px; padding:3px; }
  .style-chip img { width:30px; height:30px; }
  .avatar-style-picker { gap:5px; flex-wrap:nowrap; justify-content:center; }
}

/* Short viewports (iPhone SE class portrait, landscape phones, in-app
   browsers where Safari/Chrome chrome eats ~120-150px). Compact the join
   screen so the avatar preview + style picker + Join button all fit
   above the keyboard on short phones. */
@media (max-height: 720px) {
  #join { gap:14px; }
  .avatar-preview, .avatar-preview-img { width:120px; height:120px; }
  .emoji-row { gap:8px; }
  .avatar-regen { padding:10px 14px; font-size:0.9rem; min-height:40px; }
  #join .logo { font-size:1.4rem; }
  .join-tagline { font-size:0.75rem; }
}
@media (max-height: 640px) {
  #join { gap:10px; }
  .avatar-preview, .avatar-preview-img { width:96px; height:96px; }
  .style-chip { min-width:44px; min-height:44px; }
  .style-chip img { width:32px; height:32px; }
}

/* ── Accessibility ── */
*:focus-visible { outline:2px solid var(--gold); outline-offset:2px; }
.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); border:0; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration:0.01ms !important; animation-iteration-count:1 !important; transition-duration:0.01ms !important; }
  .pulse-ring { animation:none !important; box-shadow:none !important; }
  .q-timer.danger { animation:none !important; }
}

.kicked-banner { background:rgba(255,68,102,0.15); border:1px solid rgba(255,68,102,0.5); color:#ffb5c2; padding:12px 16px; border-radius:12px; margin:0 0 16px; text-align:center; font-weight:600; }

/* ── Recent venues picker (logged-in player, no urlCode) ─────────────── */
.recent-venues { display:flex; flex-direction:column; gap:8px; }
.recent-venues-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
@media (max-width: 420px) { .recent-venues-grid { grid-template-columns:1fr; } }
.venue-chip {
  display:flex; flex-direction:column; align-items:flex-start; gap:2px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:10px 12px; cursor:pointer; transition:border-color 0.15s, transform 0.1s;
  font-family:inherit; color:var(--text); text-align:left; position:relative;
}
.venue-chip:hover, .venue-chip:focus-visible { border-color:var(--gold); outline:none; }
.venue-chip:active { transform:scale(0.98); }
.venue-chip-name { font-weight:700; font-size:0.9rem; line-height:1.2; }
.venue-chip-meta { font-size:0.72rem; color:var(--muted); }
.venue-chip-code { font-family:'Bebas Neue',sans-serif; letter-spacing:1px; color:var(--gold); font-size:0.85rem; }
.venue-chip-live {
  position:absolute; top:6px; right:8px;
  font-size:0.6rem; font-weight:700; letter-spacing:1px;
  color:#15c272; background:rgba(21,194,114,0.12); border:1px solid rgba(21,194,114,0.35);
  border-radius:8px; padding:2px 6px;
  animation:livePulse 1.6s ease-in-out infinite;
}
@keyframes livePulse {
  0%, 100% { box-shadow:0 0 0 0 rgba(21,194,114,0.35); }
  50% { box-shadow:0 0 0 4px rgba(21,194,114,0); }
}
.venue-chip.is-live { border-color:rgba(21,194,114,0.5); }

/* ── Mode-aware Recent Venues chips ────────────────────────────────────
   When a venue is running BOTH Game Night and Open Play, the chip is
   a <div> wrapper instead of a single <button>, with two stacked CTAs
   so the patron can opt into either mode. */
.venue-chip-split { cursor:default; padding-bottom:8px; }
.venue-chip-split:hover, .venue-chip-split:focus-visible { border-color:rgba(21,194,114,0.5); }
.venue-chip-cta-row { display:flex; gap:6px; margin-top:8px; width:100%; }
.venue-chip-cta {
  flex:1;
  font-family:inherit; font-size:0.78rem; font-weight:700;
  padding:6px 8px; border-radius:8px; cursor:pointer;
  border:1px solid var(--border); background:rgba(255,255,255,0.03);
  color:var(--text);
  transition:background 0.12s, border-color 0.12s, transform 0.08s;
}
.venue-chip-cta:hover, .venue-chip-cta:focus-visible {
  border-color:var(--gold); background:rgba(255,255,255,0.06); outline:none;
}
.venue-chip-cta:active { transform:scale(0.97); }
.venue-chip-cta-gn { color:var(--gold); }
.venue-chip-cta-op { color:#15c272; }

/* ── Recent venues picker (logged-in player, no urlCode) ─────────────── */
.recent-venues { display:flex; flex-direction:column; gap:8px; }
.recent-venues-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
@media (max-width: 420px) { .recent-venues-grid { grid-template-columns:1fr; } }
.venue-chip {
  display:flex; flex-direction:column; align-items:flex-start; gap:2px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:10px 12px 10px; cursor:pointer; transition:border-color 0.15s, transform 0.1s;
  font-family:inherit; color:var(--text); text-align:left; position:relative;
}
.venue-chip:hover, .venue-chip:focus-visible { border-color:var(--gold); outline:none; }
.venue-chip:active { transform:scale(0.98); }
.venue-chip-name { font-weight:700; font-size:0.92rem; line-height:1.2; }
.venue-chip-code { font-family:'Bebas Neue',sans-serif; letter-spacing:1.5px; color:var(--gold); font-size:0.85rem; }
.venue-chip-meta { font-size:0.7rem; color:var(--muted); margin-top:2px; }
.venue-chip-live {
  position:absolute; top:6px; right:8px;
  font-size:0.6rem; font-weight:700; letter-spacing:1px;
  color:#15c272; background:rgba(21,194,114,0.12); border:1px solid rgba(21,194,114,0.35);
  border-radius:8px; padding:2px 6px;
  animation:livePulse 1.6s ease-in-out infinite;
}
@keyframes livePulse {
  0%, 100% { box-shadow:0 0 0 0 rgba(21,194,114,0.35); }
  50% { box-shadow:0 0 0 4px rgba(21,194,114,0); }
}
.venue-chip.is-live { border-color:rgba(21,194,114,0.5); }

/* ── Mode-aware Recent Venues chips ────────────────────────────────────
   When a venue is running BOTH Game Night and Open Play, the chip is
   a <div> wrapper instead of a single <button>, with two stacked CTAs
   so the patron can opt into either mode. */
.venue-chip-split { cursor:default; padding-bottom:8px; }
.venue-chip-split:hover, .venue-chip-split:focus-visible { border-color:rgba(21,194,114,0.5); }
.venue-chip-cta-row { display:flex; gap:6px; margin-top:8px; width:100%; }
.venue-chip-cta {
  flex:1;
  font-family:inherit; font-size:0.78rem; font-weight:700;
  padding:6px 8px; border-radius:8px; cursor:pointer;
  border:1px solid var(--border); background:rgba(255,255,255,0.03);
  color:var(--text);
  transition:background 0.12s, border-color 0.12s, transform 0.08s;
}
.venue-chip-cta:hover, .venue-chip-cta:focus-visible {
  border-color:var(--gold); background:rgba(255,255,255,0.06); outline:none;
}
.venue-chip-cta:active { transform:scale(0.97); }
.venue-chip-cta-gn { color:var(--gold); }
.venue-chip-cta-op { color:#15c272; }

/* Tablet breakpoint — phones cap out at the 4.3vw clamp on .q-text and
   the small font sizes on chips/buttons, so on iPad-class viewports
   (700–1024px) the screen ends up using "small phone" sizing. Bump the
   typography floor in the middle range so a player using a tablet
   doesn't squint. Phones (<=520px) hit their own breakpoints; desktop
   (>=1024px) falls back to the existing clamp ceilings. */
@media (min-width: 700px) and (max-width: 1024px) {
  body { font-size: 1.1rem; }
  .q-text { font-size: clamp(1.4rem, 3vw, 1.9rem); }
  .choice-btn { font-size: 1.15rem; padding: 18px; }
  .choice-letter { width: 42px; height: 42px; font-size: 1.3rem; }
  .q-progress, .q-answered-count { font-size: 0.95rem; letter-spacing: 2px; }
  .q-timer { font-size: 1.6rem; }
  .answer-result-title { font-size: clamp(2.6rem, 7vw, 3.4rem); }
  .answer-points { font-size: 1.4rem; }
  .answer-wait { font-size: 1.05rem; }
  .btn { font-size: 1.15rem; padding: 20px; }
  .field-label { font-size: 0.95rem; letter-spacing: 2px; }
  .logo { font-size: 2.2rem; }
  /* On iPad-class viewports the .choices column stretched to full width,
     making answer buttons feel cavernous and disconnected from the
     question text above. Cap to ~680px so the column sits at a
     readable width and the question text + buttons feel related. */
  .choices, .q-text { max-width: 680px; width: 100%; margin-left: auto; margin-right: auto; }
}

/* ═══ Emcee Batch (sheet round) ═══ */
.emcee-batch-q {
  display:flex; gap:12px; align-items:flex-start;
  padding:14px; background:var(--surface2);
  border:1px solid var(--border); border-radius:12px;
}
.emcee-batch-q-num {
  min-width:28px; height:28px; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  background:var(--gold); color:var(--bg);
  border-radius:50%; font-weight:700; font-size:0.85rem;
}
.emcee-batch-q-body { flex:1; min-width:0; display:flex; flex-direction:column; gap:8px; }
.emcee-batch-q-text { font-size:1rem; line-height:1.4; color:var(--text); }
.emcee-batch-q-photo { max-width:100%; max-height:200px; border-radius:8px; object-fit:contain; }
.emcee-batch-q-input {
  width:100%; font-size:16px; padding:10px 12px;
  border:1px solid var(--border); border-radius:8px;
  background:var(--bg); color:var(--text);
}
.emcee-batch-q-input:disabled { opacity:0.7; }
#emceeBatchTimer { font-size:2rem; font-weight:700; color:var(--text); text-align:center; font-variant-numeric:tabular-nums; }
#emceeBatchTimer.urgent { color:var(--gold); }
