/* ============================================================
   Ankit's Game Night — design system — dark party theme, mobile-first
   ============================================================ */
:root {
  --bg: #0b0e1a;
  --bg-2: #11152b;
  --ink: #eef0ff;
  --ink-dim: #a7abce;
  --ink-faint: #6b6f96;
  --glass: rgba(255, 255, 255, 0.055);
  --glass-2: rgba(255, 255, 255, 0.09);
  --stroke: rgba(255, 255, 255, 0.1);
  --stroke-2: rgba(255, 255, 255, 0.18);
  --violet: #8b5cf6;
  --cyan: #22d3ee;
  --pink: #f472b6;
  --amber: #fbbf24;
  --emerald: #34d399;
  --red: #f87171;
  --grad-brand: linear-gradient(135deg, #8b5cf6 0%, #6366f1 45%, #22d3ee 100%);
  --grad-uc: linear-gradient(135deg, #f472b6 0%, #8b5cf6 100%);
  --grad-island: linear-gradient(135deg, #34d399 0%, #22d3ee 60%, #fbbf24 130%);
  --grad-danger: linear-gradient(135deg, #f87171, #f43f5e);
  --shadow-lg: 0 20px 50px rgba(0, 0, 0, 0.5);
  --shadow-sm: 0 4px 16px rgba(0, 0, 0, 0.35);
  --display: 'Baloo 2', 'Outfit', ui-rounded, system-ui, sans-serif;
  --r-lg: 22px;
  --card-pad: clamp(18px, 4vw, 26px);
  --r-md: 16px;
  --r-sm: 12px;
  --font: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --safe-b: env(safe-area-inset-bottom, 0px);
}

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html { height: 100%; }
body {
  min-height: 100%;
  font-family: var(--font);
  background: var(--bg);
  color: var(--ink);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  /* This is a game played with your hands, so the cursor is a hand throughout: an open
     hand over the table, a pointing hand over anything you can press, a closed fist while
     you are dragging a card. All three are the browser's own, which means they are the
     hands the player already recognises and they render on every platform. */
  cursor: grab;
}
button, input, textarea, select { font-family: inherit; font-size: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }
a, [role="button"], label, summary, select { cursor: pointer; }
/* Typing needs a caret, not a hand. */
input:not([type="range"]):not([type="checkbox"]):not([type="radio"]), textarea { cursor: text; }
input[type="range"] { cursor: grab; }
input[type="range"]:active { cursor: grabbing; }
button:disabled, [aria-disabled="true"] { cursor: not-allowed; }
::selection { background: rgba(139, 92, 246, 0.45); }

/* ---------- animated backdrop ---------- */
.bg-scene { position: fixed; inset: 0; z-index: -2; overflow: hidden; background: radial-gradient(1200px 800px at 70% -10%, #171d3e 0%, var(--bg) 55%); }
.blob { position: absolute; border-radius: 50%; filter: blur(90px); opacity: 0.5; animation: drift 26s ease-in-out infinite alternate; }
.blob-1 { width: 44vmax; height: 44vmax; left: -12vmax; top: -14vmax; background: radial-gradient(circle, #4c2f9e, transparent 65%); }
.blob-2 { width: 38vmax; height: 38vmax; right: -10vmax; top: 26%; background: radial-gradient(circle, #0e5b76, transparent 65%); animation-delay: -8s; animation-duration: 32s; }
.blob-3 { width: 30vmax; height: 30vmax; left: 22%; bottom: -16vmax; background: radial-gradient(circle, #7a2555, transparent 65%); animation-delay: -16s; animation-duration: 22s; }
@keyframes drift {
  from { transform: translate(0, 0) scale(1); }
  to { transform: translate(6vmax, 4vmax) scale(1.15); }
}
.grain { position: absolute; inset: 0; opacity: 0.25; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E"); }
@media (prefers-reduced-motion: reduce) {
  .blob { animation: none; }
  * { animation-duration: 0.001s !important; transition-duration: 0.001s !important; }
}

/* ---------- app chrome ---------- */
.app-bar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between;
  padding: calc(12px + env(safe-area-inset-top, 0px)) clamp(14px, 4vw, 28px) 12px;
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  background: rgba(11, 14, 26, 0.55);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.brand { display: flex; align-items: center; gap: 9px; }
.brand-text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1; }
.brand-byline {
  font-size: 9px; font-weight: 600; letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--ink-faint); margin-top: 3px;
}
.brand-mark {
  width: 34px; height: 34px; display: block; border-radius: 10px;
  filter: drop-shadow(0 3px 10px rgba(229, 205, 126, 0.35));
  transition: transform 0.3s cubic-bezier(0.2, 1.4, 0.4, 1);
}
.brand:hover .brand-mark { transform: rotate(-7deg) scale(1.07); }
.brand-name {
  font-weight: 800; font-size: 19px; letter-spacing: 0.2px;
  background: linear-gradient(180deg, #fffdf6 30%, #efe2b0 72%, #d9bc63 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.app-bar-right { display: flex; align-items: center; gap: 8px; }

.icon-btn {
  width: 38px; height: 38px; border-radius: 12px; display: grid; place-items: center;
  background: var(--glass); border: 1px solid var(--stroke); font-size: 17px;
  transition: transform 0.15s, background 0.2s;
}
.icon-btn:hover { background: var(--glass-2); transform: translateY(-1px); }
.icon-btn:active { transform: scale(0.93); }

.code-chip {
  display: flex; align-items: center; gap: 7px; padding: 7px 12px;
  border-radius: 12px; background: var(--glass); border: 1px solid var(--stroke);
  font-weight: 700; letter-spacing: 2.5px; font-size: 14px;
  transition: background 0.2s, transform 0.15s;
}
.code-chip:hover { background: var(--glass-2); }
.code-chip:active { transform: scale(0.95); }
.code-chip .code-label { font-size: 10px; letter-spacing: 1px; color: var(--ink-faint); font-weight: 600; }

/* ---------- layout ---------- */
.app-main {
  max-width: 620px; margin: 0 auto;
  padding: clamp(16px, 3vh, 30px) clamp(14px, 4vw, 24px) calc(40px + var(--safe-b));
  animation: screen-in 0.35s cubic-bezier(0.2, 0.9, 0.3, 1);
}
.app-main.wide { max-width: 860px; }
/* Landing sections are a mix of cards, grids and text, so space them uniformly
   instead of leaning on the .card + .card adjacency rule. */
.app-main.landing > * + * { margin-top: 20px; }
.app-main.landing > .hero + * { margin-top: 8px; }
@keyframes screen-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* ---------- generic components ---------- */
.card {
  background: var(--glass); border: 1px solid var(--stroke);
  border-radius: var(--r-lg); padding: var(--card-pad);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--shadow-sm);
}
.card + .card, .stack > * + * { margin-top: 14px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px 22px; border-radius: var(--r-md); font-weight: 700; font-size: 15.5px;
  border: 1px solid transparent; color: var(--ink); position: relative; overflow: hidden;
  transition: transform 0.16s cubic-bezier(0.3, 1.4, 0.5, 1), box-shadow 0.2s, opacity 0.2s, filter 0.2s;
  user-select: none; -webkit-user-select: none;
}
.btn:active { transform: scale(0.96); }
.btn:disabled { opacity: 0.45; pointer-events: none; }
.btn-primary { background: var(--grad-brand); box-shadow: 0 8px 24px rgba(99, 102, 241, 0.35); }
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 12px 30px rgba(99, 102, 241, 0.45); }
.btn-island { background: var(--grad-island); color: #06281f; box-shadow: 0 8px 24px rgba(52, 211, 153, 0.3); }
.btn-island:hover { transform: translateY(-2px); }
.btn-bi { background: var(--grad-uc); box-shadow: 0 8px 24px rgba(139, 92, 246, 0.35); }
.btn-bi:hover { transform: translateY(-2px); }
/* The three card games share the table's champagne-and-midnight identity, tinted apart:
   Silent Order candle-gold, Swap or Stay amber-rose, Sleepless moonlit indigo. */
.btn-so { background: linear-gradient(135deg, #d4af37 0%, #a87f1f 100%); color: #241a02; box-shadow: 0 8px 24px rgba(212, 175, 55, 0.3); }
.btn-so:hover { transform: translateY(-2px); }
.btn-ss { background: linear-gradient(135deg, #fbbf24 0%, #f472b6 110%); color: #2b1502; box-shadow: 0 8px 24px rgba(251, 191, 36, 0.3); }
.btn-ss:hover { transform: translateY(-2px); }
.btn-sl { background: linear-gradient(135deg, #6366f1 0%, #312e81 100%); box-shadow: 0 8px 24px rgba(99, 102, 241, 0.35); }
.btn-sl:hover { transform: translateY(-2px); }
.btn-danger { background: var(--grad-danger); box-shadow: 0 8px 24px rgba(244, 63, 94, 0.3); }
.btn-ghost { background: var(--glass); border-color: var(--stroke); }
.btn-ghost:hover { background: var(--glass-2); }
.btn-lg { padding: 16px 26px; font-size: 17px; border-radius: 18px; }
.btn-sm { padding: 8px 14px; font-size: 13.5px; border-radius: 10px; }
.btn-block { width: 100%; }

.input, .textarea {
  width: 100%; padding: 14px 16px; border-radius: var(--r-md);
  background: rgba(0, 0, 0, 0.28); border: 1.5px solid var(--stroke);
  color: var(--ink); font-size: 16px; outline: none; transition: border-color 0.2s, box-shadow 0.2s;
}
.input:focus, .textarea:focus { border-color: var(--violet); box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.18); }
.input::placeholder, .textarea::placeholder { color: var(--ink-faint); }
.textarea { resize: vertical; min-height: 74px; line-height: 1.45; }
.input-code { text-transform: uppercase; letter-spacing: 8px; font-weight: 800; text-align: center; font-size: 22px; }

.label { display: block; font-size: 12.5px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--ink-faint); margin: 0 0 8px 2px; }
.hint { font-size: 13.5px; color: var(--ink-dim); line-height: 1.5; }
.divider { height: 1px; background: var(--stroke); margin: 18px 0; border: none; }

h1.title { font-size: clamp(30px, 7vw, 42px); font-weight: 800; line-height: 1.08; letter-spacing: -0.5px; }
h2.subtitle { font-size: clamp(19px, 4.6vw, 24px); font-weight: 800; }
.gradient-text { background: var(--grad-brand); -webkit-background-clip: text; background-clip: text; color: transparent; }

.badge {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.6px; text-transform: uppercase;
  background: var(--glass-2); border: 1px solid var(--stroke);
}
.badge.live { color: var(--emerald); border-color: rgba(52, 211, 153, 0.4); }
.badge.warn { color: var(--amber); border-color: rgba(251, 191, 36, 0.4); }
.badge.bi { color: var(--pink); border-color: rgba(244, 114, 182, 0.4); }

/* ---------- toasts ---------- */
.toasts {
  position: fixed; top: calc(64px + env(safe-area-inset-top, 0px)); left: 50%; transform: translateX(-50%);
  z-index: 90; display: flex; flex-direction: column; gap: 8px; width: min(92vw, 420px); pointer-events: none;
}
.toast {
  padding: 12px 18px; border-radius: 14px; font-weight: 600; font-size: 14.5px; text-align: center;
  background: rgba(20, 24, 46, 0.92); border: 1px solid var(--stroke-2); box-shadow: var(--shadow-lg);
  backdrop-filter: blur(12px); animation: toast-in 0.3s cubic-bezier(0.2, 1.2, 0.4, 1);
}
.toast.error { border-color: rgba(248, 113, 113, 0.5); color: #fecaca; }
.toast.success { border-color: rgba(52, 211, 153, 0.5); color: #bbf7d0; }
.toast.leaving { animation: toast-out 0.25s ease forwards; }
@keyframes toast-in { from { opacity: 0; transform: translateY(-12px) scale(0.94); } }
@keyframes toast-out { to { opacity: 0; transform: translateY(-10px) scale(0.95); } }

/* ---------- modal ---------- */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 80; display: grid; place-items: center; padding: 18px;
  background: rgba(5, 7, 15, 0.72); backdrop-filter: blur(6px); animation: fade-in 0.22s ease;
}
.modal {
  width: min(94vw, 560px); max-height: min(86vh, 720px); overflow-y: auto;
  background: linear-gradient(180deg, #181d38, #12162c); border: 1px solid var(--stroke-2);
  border-radius: 24px; padding: 26px; box-shadow: var(--shadow-lg);
  animation: modal-in 0.32s cubic-bezier(0.2, 1.25, 0.4, 1);
}
.modal-title { font-size: 22px; font-weight: 800; margin-bottom: 6px; display: flex; align-items: center; gap: 10px; }
.modal-close { position: sticky; float: right; top: 0; margin-left: 10px; }
@keyframes fade-in { from { opacity: 0; } }
@keyframes modal-in { from { opacity: 0; transform: translateY(24px) scale(0.96); } }

/* ---------- home screen ---------- */
.hero { text-align: center; padding: clamp(8px, 4vh, 36px) 0 20px; }
.hero-emoji { font-size: clamp(52px, 12vw, 74px); display: inline-block; animation: bounce-slow 3.2s ease-in-out infinite; filter: drop-shadow(0 8px 24px rgba(139, 92, 246, 0.45)); }
.hero-logo {
  width: clamp(76px, 17vw, 104px); height: auto; display: inline-block; border-radius: 24px;
  filter: drop-shadow(0 14px 34px rgba(99, 102, 241, 0.5));
  /* Lands first with a spin and an overshoot, then settles into the idle float. The float
     is delayed rather than blended so the two never fight over transform. */
  animation:
    logo-land 0.85s cubic-bezier(0.16, 1.5, 0.36, 1) backwards,
    logo-float 5s ease-in-out 0.85s infinite;
}
@keyframes logo-land {
  0%   { opacity: 0; transform: translateY(-70px) rotate(-160deg) scale(0.4); }
  60%  { opacity: 1; transform: translateY(8px) rotate(12deg) scale(1.1); }
  80%  { transform: translateY(-4px) rotate(-5deg) scale(0.97); }
  100% { opacity: 1; transform: none; }
}
@keyframes logo-float {
  0%, 100% { transform: translateY(0) rotate(-2.5deg); }
  50% { transform: translateY(-11px) rotate(2.5deg); }
}
@keyframes bounce-slow { 0%, 100% { transform: translateY(0) rotate(-4deg); } 50% { transform: translateY(-10px) rotate(4deg); } }
.hero p { margin-top: 10px; color: var(--ink-dim); font-size: 16px; }

.avatar-row { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.avatar-opt {
  width: 46px; height: 46px; border-radius: 14px; display: grid; place-items: center; font-size: 24px;
  background: var(--glass); border: 2px solid transparent; transition: transform 0.15s, border-color 0.15s, background 0.2s;
}
.avatar-opt:hover { transform: scale(1.12) rotate(-4deg); background: var(--glass-2); }
.avatar-opt.selected { border-color: var(--violet); background: rgba(139, 92, 246, 0.2); transform: scale(1.1); }

.home-actions { display: grid; gap: 12px; margin-top: 16px; }
.join-row { display: grid; grid-template-columns: 1fr auto; gap: 10px; }

/* ---------- player tiles ---------- */
.players-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px; }
.player-tile {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 14px 8px 12px; border-radius: var(--r-md);
  background: var(--glass); border: 1px solid var(--stroke);
  transition: border-color 0.2s, background 0.2s, opacity 0.3s, transform 0.2s;
}
/* Opt-in entrance animations: added only the first time an element appears, so a
   re-render caused by someone else's move doesn't re-animate the whole board. */
.anim-pop { animation: pop-in 0.35s cubic-bezier(0.2, 1.4, 0.4, 1) backwards; }
.anim-slide { animation: slide-in-left 0.3s ease backwards; }
.player-tile .pt-avatar { font-size: 30px; line-height: 1; transition: transform 0.2s; }
.player-tile .pt-name { font-size: 13px; font-weight: 700; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.player-tile .pt-sub { font-size: 10.5px; color: var(--ink-faint); font-weight: 600; min-height: 13px; }
.player-tile.offline { opacity: 0.42; filter: grayscale(0.7); }
.player-tile.dead { opacity: 0.5; }
.player-tile.dead .pt-avatar { filter: grayscale(1); }
.player-tile.current-turn { border-color: var(--cyan); background: rgba(34, 211, 238, 0.1); box-shadow: 0 0 0 1px rgba(34, 211, 238, 0.35), 0 0 24px rgba(34, 211, 238, 0.25); }
.player-tile.current-turn .pt-avatar { animation: wiggle 1.4s ease-in-out infinite; }
.player-tile.you { border-color: rgba(139, 92, 246, 0.55); }
.player-tile.selectable { cursor: pointer; }
.player-tile.selectable:hover { transform: translateY(-3px); border-color: var(--stroke-2); background: var(--glass-2); }
.player-tile.vote-selected { border-color: var(--pink); background: rgba(244, 114, 182, 0.14); box-shadow: 0 0 0 1px rgba(244, 114, 182, 0.5); }
.crown { position: absolute; top: -9px; left: 50%; transform: translateX(-50%) rotate(-12deg); font-size: 15px; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5)); }
.pt-mark { position: absolute; top: 6px; right: 7px; font-size: 13px; }
.kick-btn {
  position: absolute; top: -7px; right: -7px; width: 22px; height: 22px; border-radius: 50%;
  background: var(--grad-danger); color: white; font-size: 11px; font-weight: 800;
  display: grid; place-items: center; opacity: 0; transition: opacity 0.15s, transform 0.15s;
}
.player-tile:hover .kick-btn { opacity: 1; }
.kick-btn:hover { transform: scale(1.2); }
/* Touch devices have no hover: keep the control visible and finger-sized. */
@media (hover: none), (pointer: coarse) {
  .kick-btn { opacity: 1; width: 26px; height: 26px; font-size: 13px; }
}
@keyframes pop-in { from { opacity: 0; transform: scale(0.6); } }
@keyframes wiggle { 0%, 100% { transform: rotate(-6deg); } 50% { transform: rotate(6deg); } }

/* ---------- game picker ---------- */
.game-cards { display: grid; gap: 14px; }
@media (min-width: 560px) { .game-cards { grid-template-columns: 1fr 1fr; } }
.game-card {
  position: relative; text-align: left; padding: 20px; border-radius: var(--r-lg); overflow: hidden;
  border: 2px solid var(--stroke); background: var(--glass);
  transition: transform 0.2s cubic-bezier(0.3, 1.3, 0.5, 1), border-color 0.2s, box-shadow 0.25s;
}
.game-card:hover { transform: translateY(-4px); }
.game-card.selected-bi { border-color: var(--pink); box-shadow: 0 0 0 1px rgba(244, 114, 182, 0.4), 0 14px 40px rgba(244, 114, 182, 0.18); }
.game-card.selected-island { border-color: var(--emerald); box-shadow: 0 0 0 1px rgba(52, 211, 153, 0.4), 0 14px 40px rgba(52, 211, 153, 0.18); }
.game-card.selected-so { border-color: #d4af37; box-shadow: 0 0 0 1px rgba(212, 175, 55, 0.4), 0 14px 40px rgba(212, 175, 55, 0.18); }
.game-card.selected-ss { border-color: var(--amber); box-shadow: 0 0 0 1px rgba(251, 191, 36, 0.4), 0 14px 40px rgba(251, 191, 36, 0.18); }
.game-card.selected-sl { border-color: #818cf8; box-shadow: 0 0 0 1px rgba(129, 140, 248, 0.4), 0 14px 40px rgba(129, 140, 248, 0.18); }
.game-card .gc-emoji { font-size: 38px; }
.game-card h3 { font-size: 19px; font-weight: 800; margin: 10px 0 4px; }
.game-card p { font-size: 13.5px; color: var(--ink-dim); line-height: 1.5; }
.game-card .gc-meta { margin-top: 10px; display: flex; gap: 6px; flex-wrap: wrap; }
.game-card .gc-glow { position: absolute; inset: -40%; opacity: 0; transition: opacity 0.3s; pointer-events: none; }
.game-card:hover .gc-glow, .game-card.selected-bi .gc-glow, .game-card.selected-island .gc-glow,
.game-card.selected-so .gc-glow, .game-card.selected-ss .gc-glow, .game-card.selected-sl .gc-glow { opacity: 0.14; }
.gc-glow.bi { background: radial-gradient(circle at 70% 20%, var(--pink), transparent 60%); }
.gc-glow.island { background: radial-gradient(circle at 70% 20%, var(--emerald), transparent 60%); }
.gc-glow.so { background: radial-gradient(circle at 70% 20%, #d4af37, transparent 60%); }
.gc-glow.ss { background: radial-gradient(circle at 70% 20%, var(--amber), transparent 60%); }
.gc-glow.sl { background: radial-gradient(circle at 70% 20%, #818cf8, transparent 60%); }

/* ---------- word card (blendin reveal) ---------- */
.word-card-wrap { perspective: 1200px; margin: 8px auto; max-width: 340px; }
.word-card {
  position: relative; width: 100%; aspect-ratio: 8 / 5; transform-style: preserve-3d;
  transition: transform 0.65s cubic-bezier(0.35, 1.2, 0.4, 1); cursor: pointer;
}
.word-card.flipped { transform: rotateY(180deg); }
.word-face {
  position: absolute; inset: 0; border-radius: 20px; backface-visibility: hidden; -webkit-backface-visibility: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 18px;
  border: 1px solid var(--stroke-2); box-shadow: var(--shadow-lg);
}
.word-face.front { background: linear-gradient(145deg, #232a54, #171c3a); }
.word-face.front .wf-logo { font-size: 40px; animation: bounce-slow 3s ease-in-out infinite; }
.word-face.front .wf-tip { font-size: 13px; color: var(--ink-dim); font-weight: 600; letter-spacing: 0.4px; }
.word-face.back { background: var(--grad-uc); transform: rotateY(180deg); text-align: center; }
.word-face.back.blank { background: linear-gradient(145deg, #f3f4f6, #d1d5db); color: #111; }
.word-face .wf-label { font-size: 11px; font-weight: 800; letter-spacing: 2px; text-transform: uppercase; opacity: 0.75; }
.word-face .wf-word { font-size: clamp(26px, 7vw, 38px); font-weight: 800; letter-spacing: 0.5px; text-shadow: 0 2px 12px rgba(0,0,0,0.25); }
.word-face .wf-sub { font-size: 13px; font-weight: 600; opacity: 0.85; line-height: 1.4; }

/* ---------- clue board ---------- */
.clue-round { margin-top: 14px; }
.clue-round-label { font-size: 11.5px; font-weight: 800; letter-spacing: 1.5px; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 8px; }
.clue-row { display: flex; align-items: flex-start; gap: 9px; padding: 7px 0; }
.clue-row .cr-avatar { font-size: 20px; flex: none; width: 30px; text-align: center; }
.clue-row .cr-body { flex: 1; min-width: 0; }
.clue-row .cr-name { font-size: 11.5px; font-weight: 700; color: var(--ink-faint); }
.clue-bubble {
  display: inline-block; margin-top: 2px; padding: 8px 14px; border-radius: 4px 16px 16px 16px;
  background: var(--glass-2); border: 1px solid var(--stroke); font-weight: 600; font-size: 15px;
}
.clue-row.dead-clue { opacity: 0.45; }
.clue-row.dead-clue .clue-bubble { text-decoration: line-through; }
@keyframes slide-in-left { from { opacity: 0; transform: translateX(-14px); } }

/* ---------- turn banner / status strip ---------- */
.status-strip {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  padding: 12px 16px; border-radius: var(--r-md);
  background: var(--glass); border: 1px solid var(--stroke); margin-bottom: 14px;
}
.status-strip .ss-label { font-weight: 700; font-size: 14.5px; display: flex; align-items: center; gap: 8px; }
.turn-banner {
  text-align: center; padding: 14px; border-radius: var(--r-md); font-weight: 800; font-size: 16px;
  border: 1px dashed var(--stroke-2); margin: 14px 0; animation: pulse-soft 2.2s ease-in-out infinite;
}
.turn-banner.your-turn { border: 1.5px solid var(--cyan); background: rgba(34, 211, 238, 0.09); color: #a5f3fc; }
@keyframes pulse-soft { 0%, 100% { box-shadow: 0 0 0 0 rgba(34, 211, 238, 0); } 50% { box-shadow: 0 0 24px 0 rgba(34, 211, 238, 0.14); } }

/* ---------- island attempt log ---------- */
.attempt-log { display: flex; flex-direction: column; gap: 8px; max-height: 46vh; overflow-y: auto; padding-right: 4px; }
.attempt {
  display: flex; align-items: center; gap: 10px; padding: 10px 13px; border-radius: 14px;
  background: var(--glass); border: 1px solid var(--stroke);
}
.attempt .at-verdict { font-size: 19px; flex: none; }
.attempt .at-text { font-weight: 600; font-size: 14.5px; }
.attempt .at-remark { font-size: 12.5px; color: var(--ink-dim); margin-top: 1px; }
.attempt .at-who { margin-left: auto; font-size: 11px; color: var(--ink-faint); font-weight: 700; white-space: nowrap; flex: none; }
.attempt.v-yes { border-color: rgba(52, 211, 153, 0.4); }
.attempt.v-no { border-color: rgba(248, 113, 113, 0.35); }
.attempt.v-correct { border-color: rgba(251, 191, 36, 0.55); background: rgba(251, 191, 36, 0.08); }
.attempt.v-pending { border-style: dashed; }
.attempt.v-pending .at-verdict { animation: spin 1.1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* island packing list: allowed vs rejected columns */
.packing-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 12px; align-items: start; }
.pack-col { border-radius: var(--r-md); padding: 10px; display: flex; flex-direction: column; gap: 7px; align-content: start; }
.pack-col.allowed { background: rgba(52, 211, 153, 0.07); border: 1px solid rgba(52, 211, 153, 0.28); }
.pack-col.rejected { background: rgba(248, 113, 113, 0.06); border: 1px solid rgba(248, 113, 113, 0.24); }
.pack-head { font-size: 11.5px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase; margin-bottom: 3px; }
.pack-col.allowed .pack-head { color: #86efac; }
.pack-col.rejected .pack-head { color: #fca5a5; }
.pack-item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 7px 10px; border-radius: 10px; background: rgba(0, 0, 0, 0.22); border: 1px solid var(--stroke);
}
.pack-item .pack-word { font-weight: 700; font-size: 14px; overflow-wrap: anywhere; }
.pack-item .pack-by { font-size: 10.5px; color: var(--ink-faint); font-weight: 600; white-space: nowrap; flex: none; }
@media (max-width: 430px) { .pack-item { flex-direction: column; gap: 1px; } }

.starter-banner {
  text-align: center; padding: 18px; border-radius: var(--r-lg); line-height: 1.55;
  background: linear-gradient(145deg, rgba(52, 211, 153, 0.12), rgba(34, 211, 238, 0.07));
  border: 1px solid rgba(52, 211, 153, 0.3); font-size: 15.5px;
}
.starter-banner .sb-items { font-size: 19px; font-weight: 800; color: #86efac; }

/* ---------- result overlays ---------- */
.reveal-pop { text-align: center; padding: 10px 0; }
.reveal-pop .rp-avatar { font-size: 64px; display: inline-block; animation: drop-bounce 0.7s cubic-bezier(0.3, 1.6, 0.4, 1); }
.reveal-pop .rp-role {
  display: inline-block; margin-top: 12px; padding: 8px 20px; border-radius: 999px; font-weight: 800; font-size: 15px;
  letter-spacing: 1px; text-transform: uppercase; animation: pop-in 0.5s 0.25s cubic-bezier(0.2, 1.5, 0.4, 1) backwards;
}
.rp-role.insider { background: rgba(52, 211, 153, 0.18); color: #86efac; border: 1px solid rgba(52, 211, 153, 0.45); }
.rp-role.outsider { background: rgba(244, 114, 182, 0.16); color: #f9a8d4; border: 1px solid rgba(244, 114, 182, 0.5); }
.rp-role.blank { background: rgba(255, 255, 255, 0.9); color: #111; }
.reveal-pop .rp-quip { margin-top: 12px; color: var(--ink-dim); font-size: 14.5px; }
@keyframes drop-bounce { 0% { transform: translateY(-60px) scale(0.6); opacity: 0; } 60% { transform: translateY(6px) scale(1.05); opacity: 1; } 100% { transform: none; } }

.win-screen { text-align: center; padding: 8px var(--card-pad); }
.win-screen .ws-emoji { font-size: 72px; display: inline-block; animation: drop-bounce 0.8s cubic-bezier(0.3, 1.6, 0.4, 1); }
.win-screen h2 { font-size: clamp(26px, 7vw, 36px); font-weight: 800; margin-top: 8px; }
.win-screen .ws-reason { color: var(--ink-dim); margin-top: 6px; font-size: 15px; }

.role-list { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; text-align: left; }
.role-line { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 12px; background: var(--glass); border: 1px solid var(--stroke); animation: slide-in-left 0.3s ease backwards; }
.role-line .rl-name { font-weight: 700; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.role-line .rl-role { font-size: 12px; font-weight: 800; letter-spacing: 0.8px; text-transform: uppercase; }
.role-line .rl-role.insider { color: #86efac; }
.role-line .rl-role.outsider { color: #f9a8d4; }
.role-line .rl-role.blank { color: #e5e7eb; }

/* ---------- leaderboard ---------- */
.lb-row { display: flex; align-items: center; gap: 12px; padding: 11px 14px; border-radius: 13px; background: var(--glass); border: 1px solid var(--stroke); animation: slide-in-left 0.3s ease backwards; }
.lb-row .lb-rank { font-size: 18px; font-weight: 800; width: 32px; text-align: center; flex: none; }
.lb-row .lb-name { font-weight: 700; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-row .lb-pts { font-weight: 800; color: var(--amber); }
.lb-row.first { border-color: rgba(251, 191, 36, 0.5); background: rgba(251, 191, 36, 0.07); }

/* ---------- vote progress ---------- */
.vote-progress { height: 8px; border-radius: 99px; background: rgba(0,0,0,0.3); overflow: hidden; margin-top: 10px; }
.vote-progress .vp-fill { height: 100%; border-radius: 99px; background: var(--grad-uc); transition: width 0.45s cubic-bezier(0.3, 1, 0.4, 1); }

/* ---------- action bar (sticky bottom on mobile) ---------- */
.action-bar { position: sticky; bottom: calc(10px + var(--safe-b)); z-index: 30; display: grid; gap: 10px; margin-top: 18px; }
.action-bar .card { box-shadow: var(--shadow-lg); background: rgba(20, 24, 46, 0.88); backdrop-filter: blur(16px); }
.inline-form { display: grid; grid-template-columns: 1fr auto; gap: 9px; }

/* ---------- settings ---------- */
.setting-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 0; }
.setting-row + .setting-row { border-top: 1px solid var(--stroke); }
.setting-row .sr-title { font-weight: 700; font-size: 14.5px; }
.setting-row .sr-sub { font-size: 12px; color: var(--ink-faint); margin-top: 2px; }
.stepper { display: flex; align-items: center; gap: 4px; }
.stepper button { width: 34px; height: 34px; border-radius: 10px; background: var(--glass-2); border: 1px solid var(--stroke); font-size: 17px; font-weight: 800; transition: transform 0.12s, background 0.15s; }
.stepper button:hover { background: rgba(255,255,255,0.14); }
.stepper button:active { transform: scale(0.9); }
.stepper button:disabled { opacity: 0.35; pointer-events: none; }
.stepper .stepper-val { min-width: 34px; text-align: center; font-weight: 800; font-size: 17px; }
.switch { position: relative; width: 50px; height: 29px; border-radius: 99px; background: rgba(0,0,0,0.4); border: 1px solid var(--stroke-2); transition: background 0.2s; flex: none; }
.switch::after { content: ''; position: absolute; top: 3px; left: 3px; width: 21px; height: 21px; border-radius: 50%; background: #fff; transition: transform 0.22s cubic-bezier(0.3, 1.4, 0.5, 1); }
.switch.on { background: var(--violet); }
.switch.on::after { transform: translateX(21px); }

/* ---------- rules ---------- */
.rules-body { line-height: 1.65; font-size: 14.8px; color: #d7daf5; }
.rules-body h4 { margin: 18px 0 6px; font-size: 15.5px; color: var(--ink); display: flex; align-items: center; gap: 8px; }
.rules-body ul { padding-left: 20px; margin: 6px 0; }
.rules-body li { margin: 5px 0; }
.rules-body .example { padding: 12px 14px; border-radius: 12px; background: rgba(139, 92, 246, 0.1); border: 1px solid rgba(139, 92, 246, 0.3); margin: 10px 0; font-size: 14px; }
.rules-tabs { display: flex; gap: 8px; margin-bottom: 14px; }
.rules-tab { flex: 1; padding: 10px; border-radius: 12px; font-weight: 700; font-size: 14px; background: var(--glass); border: 1px solid var(--stroke); transition: all 0.2s; }
.rules-tab.active { background: rgba(139, 92, 246, 0.22); border-color: var(--violet); }

/* ---------- judge panel (island host) ---------- */
.judge-panel { border: 1.5px solid rgba(251, 191, 36, 0.5); background: rgba(251, 191, 36, 0.06); animation: pulse-soft 2s infinite; }
.judge-buttons { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 12px; }
.gm-secret { border: 1px dashed rgba(251, 191, 36, 0.45); border-radius: var(--r-md); padding: 12px 14px; background: rgba(251, 191, 36, 0.05); font-size: 14px; }
.gm-secret .gs-label { font-size: 10.5px; font-weight: 800; letter-spacing: 1.5px; text-transform: uppercase; color: var(--amber); margin-bottom: 4px; }

/* ---------- misc effects ---------- */
.shake { animation: shake 0.4s ease; }
@keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-7px); } 50% { transform: translateX(6px); } 75% { transform: translateX(-4px); } }
#fx-canvas { position: fixed; inset: 0; z-index: 70; pointer-events: none; }
.pulse-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--emerald); animation: pulse-dot 1.6s ease infinite; }
@keyframes pulse-dot { 0%, 100% { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.5); } 50% { box-shadow: 0 0 0 6px rgba(52, 211, 153, 0); } }
.spin-emoji { display: inline-block; animation: spin 1.1s linear infinite; }
.copy-flash { color: var(--emerald) !important; }

.connection-lost {
  position: fixed; inset: 0; z-index: 100; display: grid; place-items: center;
  background: rgba(5, 7, 15, 0.85); backdrop-filter: blur(8px); text-align: center; padding: 24px;
}

/* ---------- landing: the wordmark entrance ---------- */
/* The name is set in a rounded display face and dropped in letter by letter. Each letter
   arrives on a spring with its own tilt, the gradient keeps drifting once they land, and a
   highlight sweeps across on cue. Transforms and opacity only, so it stays cheap. */
h1.wordmark {
  position: relative; display: inline-block;
  font-family: var(--display);
  /* Bigger than the old headline because a rounded display face carries it, and this is
     the one piece of type that should feel like a logo rather than a heading. */
  font-size: clamp(40px, 11vw, 78px); font-weight: 800; line-height: 1.02;
  letter-spacing: -1.5px; padding: 0 6px;
}
.wordmark .wm-word { display: inline-block; white-space: nowrap; }
.wordmark .wm-word + .wm-word { margin-left: 0.28em; }

.wm-ch {
  display: inline-block;
  background: var(--grad-brand); background-size: 260% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation:
    wm-drop 0.72s cubic-bezier(0.16, 1.5, 0.36, 1) var(--d, 0ms) backwards,
    wm-flow 9s ease-in-out calc(var(--d, 0ms) + 700ms) infinite;
  will-change: transform;
}
@keyframes wm-drop {
  0%   { opacity: 0; transform: translateY(-58px) rotate(var(--r, 8deg)) scale(0.62); }
  55%  { opacity: 1; transform: translateY(6px) rotate(calc(var(--r, 8deg) * -0.15)) scale(1.06); }
  75%  { transform: translateY(-3px) scale(0.98); }
  100% { opacity: 1; transform: none; }
}
/* Keeps the gradient alive instead of frozen, so the name never looks like a static PNG. */
@keyframes wm-flow {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

/* A highlight that sweeps once the letters have landed. */
.wm-shine {
  position: absolute; inset: 0; pointer-events: none; border-radius: 10px;
  background: linear-gradient(105deg, transparent 38%, rgba(255,255,255,0.42) 50%, transparent 62%);
  background-size: 260% 100%; background-repeat: no-repeat;
  mix-blend-mode: overlay; opacity: 0;
  animation: wm-shine 2.6s ease-out 900ms 2;
}
@keyframes wm-shine {
  0%   { background-position: -140% 0; opacity: 0; }
  12%  { opacity: 1; }
  55%  { background-position: 240% 0; opacity: 0; }
  100% { background-position: 240% 0; opacity: 0; }
}

/* Reduced motion: the name simply appears, gradient and all, with nothing moving. */
@media (prefers-reduced-motion: reduce) {
  .wm-ch { animation: none; opacity: 1; }
  .wm-shine { display: none; }
}
.hero-tag { margin-top: 10px; font-size: 17.5px; color: var(--ink); font-weight: 500; animation: screen-in 0.6s 1.15s backwards; }
.hero p { animation: screen-in 0.6s 1.3s backwards; }
.hero-byline {
  font-size: clamp(12.5px, 3.2vw, 15px); font-weight: 600; letter-spacing: 2.6px;
  color: var(--ink-dim); margin-top: 8px;
  animation: byline-in 0.9s 0.95s cubic-bezier(0.2, 1.2, 0.4, 1) backwards;
}
@keyframes byline-in { from { opacity: 0; letter-spacing: 10px; transform: translateY(-4px); } }

/* floating emoji field behind the hero */
.hero { position: relative; }
.hero-float-field { position: absolute; inset: -10px 0; pointer-events: none; overflow: visible; }
.hero-float {
  position: absolute; font-size: 22px; opacity: 0;
  animation: float-up 11s linear infinite; animation-delay: calc(var(--i) * 1.8s);
  left: calc(8% + var(--i) * 15%);
  filter: saturate(0.9);
}
@keyframes float-up {
  0% { opacity: 0; transform: translateY(90px) scale(0.6) rotate(-8deg); }
  12% { opacity: 0.5; }
  70% { opacity: 0.35; }
  100% { opacity: 0; transform: translateY(-130px) scale(1.1) rotate(10deg); }
}

/* shimmer sweep on the main CTA */
.shimmer::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(115deg, transparent 30%, rgba(255, 255, 255, 0.35) 50%, transparent 70%);
  transform: translateX(-120%); animation: shimmer 3.4s ease-in-out infinite;
}
@keyframes shimmer { 0%, 55% { transform: translateX(-120%); } 85%, 100% { transform: translateX(120%); } }

/* made-by + developer links: animated gradient border, shimmering name, brand hovers */
.made-by {
  margin-top: 18px; display: inline-flex; align-items: center; gap: 11px; flex-wrap: wrap; justify-content: center;
  padding: 10px 18px; border-radius: 999px;
  border: 1.5px solid transparent;
  background:
    linear-gradient(rgba(17, 21, 43, 0.92), rgba(17, 21, 43, 0.92)) padding-box,
    linear-gradient(120deg, #8b5cf6, #22d3ee, #f472b6, #fbbf24, #8b5cf6) border-box;
  background-size: 100% 100%, 320% 100%;
  animation: screen-in 0.5s 0.7s backwards, border-flow 7s linear infinite;
  font-size: 13.5px; color: var(--ink-dim);
  box-shadow: 0 8px 30px rgba(99, 102, 241, 0.18);
}
@keyframes border-flow { to { background-position: 0 0, 320% 0; } }
.made-by-label { font-size: 11px; font-weight: 700; letter-spacing: 1.6px; text-transform: uppercase; color: var(--ink-faint); }
.dev-name {
  font-weight: 800; letter-spacing: 0.2px; white-space: nowrap;
  background: linear-gradient(100deg, #eef0ff 0%, #eef0ff 32%, #a78bfa 46%, #67e8f9 54%, #eef0ff 68%, #eef0ff 100%);
  background-size: 240% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: name-sheen 4.5s ease-in-out infinite;
}
@keyframes name-sheen { 0%, 25% { background-position: 120% 0; } 70%, 100% { background-position: -120% 0; } }
.dev-links { display: inline-flex; gap: 8px; flex-wrap: wrap; }
.dev-link {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 10px;
  background: var(--glass); border: 1px solid var(--stroke); color: var(--ink); text-decoration: none;
  font-size: 12.5px; font-weight: 700;
  transition: transform 0.18s cubic-bezier(0.3, 1.6, 0.5, 1), background 0.2s, border-color 0.2s, box-shadow 0.2s, color 0.2s;
}
.dev-link:hover { transform: translateY(-3px) scale(1.04); }
.dev-link:hover .dev-ico { animation: ico-wave 0.5s ease; }
.dev-link.dl-in:hover { background: rgba(10, 102, 194, 0.25); border-color: #38bdf8; color: #7dd3fc; box-shadow: 0 8px 22px rgba(14, 165, 233, 0.35); }
.dev-link.dl-gh:hover { background: rgba(255, 255, 255, 0.12); border-color: #e5e7eb; color: #f9fafb; box-shadow: 0 8px 22px rgba(255, 255, 255, 0.15); }
.dev-link.dl-mail:hover { background: rgba(251, 191, 36, 0.16); border-color: var(--amber); color: #fde68a; box-shadow: 0 8px 22px rgba(251, 191, 36, 0.25); }
@keyframes ico-wave { 0%, 100% { transform: rotate(0); } 30% { transform: rotate(-14deg) scale(1.15); } 65% { transform: rotate(10deg); } }
.dev-ico { display: inline-flex; }
.dev-links.sm .dev-link { padding: 5px 10px; }
.dev-card {
  display: block; padding: 16px; border-radius: var(--r-md);
  border: 1.5px solid transparent;
  background:
    linear-gradient(rgba(17, 21, 43, 0.95), rgba(17, 21, 43, 0.95)) padding-box,
    linear-gradient(120deg, #8b5cf6, #22d3ee, #f472b6, #8b5cf6) border-box;
  background-size: 100% 100%, 320% 100%;
  animation: border-flow 7s linear infinite;
}
.dev-card .dev-links { margin-top: 9px; }
.dev-avatar {
  width: 60px; height: 60px; flex: none; border-radius: 50%; display: grid; place-items: center;
  padding: 3px; background: var(--grad-brand); background-size: 200% 200%;
  box-shadow: 0 6px 18px rgba(99, 102, 241, 0.45);
  animation: avatar-glow 4s ease-in-out infinite;
}
.dev-avatar .dev-photo { width: 100%; height: 100%; }
@keyframes avatar-glow {
  0%, 100% { background-position: 0% 0%; box-shadow: 0 6px 18px rgba(99, 102, 241, 0.45); }
  50% { background-position: 100% 100%; box-shadow: 0 6px 26px rgba(34, 211, 238, 0.5); }
}
.landing-footer { text-align: center; margin-top: 22px; }
.footer-link { color: var(--ink-dim); font-weight: 700; }
.footer-link:hover { color: var(--ink); }

/* preview card — clearly an example, never dressed up as a live game */
.demo-card { overflow: hidden; }
.demo-head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.demo-chip {
  font-size: 10.5px; font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase;
  padding: 4px 9px; border-radius: 999px; color: var(--ink-dim);
  background: rgba(255, 255, 255, 0.07); border: 1px solid var(--stroke);
}
.demo-label { font-size: 13px; font-weight: 700; letter-spacing: 0.3px; color: var(--ink); }
.demo-progress { margin-left: auto; display: flex; gap: 5px; }
.demo-dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255, 255, 255, 0.18); transition: background 0.3s, transform 0.3s; }
.demo-dot.on { background: var(--violet); transform: scale(1.3); }
.demo-stage { display: flex; flex-direction: column; gap: 8px; }
.demo-bubble {
  align-self: flex-start; padding: 8px 13px; border-radius: 4px 15px 15px 15px; max-width: 92%;
  background: var(--glass-2); border: 1px solid var(--stroke); font-size: 13.5px; line-height: 1.35;
  animation: demo-bubble-in 0.42s cubic-bezier(0.2, 1.3, 0.4, 1) backwards;
}
.demo-bubble b { color: var(--cyan); font-size: 11.5px; display: block; margin-bottom: 2px; font-weight: 700; }
.demo-verdict {
  margin-top: 4px; padding: 10px 13px; border-radius: 13px; font-weight: 700; font-size: 13.5px; text-align: center;
  background: linear-gradient(135deg, rgba(139, 92, 246, 0.2), rgba(34, 211, 238, 0.14));
  border: 1px solid rgba(139, 92, 246, 0.45);
  animation: demo-verdict-in 0.5s cubic-bezier(0.2, 1.5, 0.4, 1) backwards;
}
.demo-caption { margin-top: 12px; font-size: 12px; color: var(--ink-faint); text-align: center; }
@keyframes demo-bubble-in { from { opacity: 0; transform: translateX(-14px) scale(0.94); } }
@keyframes demo-verdict-in { from { opacity: 0; transform: translateY(10px) scale(0.94); } }

/* Narrow phones: the app bar carries the logo, the room code and up to 4 controls,
   so shed the wordmark and tighten the chrome instead of overflowing. */
@media (max-width: 560px) {
  .app-bar { gap: 8px; padding-left: 12px; padding-right: 12px; }
  .app-bar-right { gap: 6px; }
  .brand-name, .brand-byline { display: none; }
  .brand-mark { width: 30px; height: 30px; }
  .icon-btn { width: 34px; height: 34px; font-size: 16px; }
  .code-chip { padding: 6px 9px; letter-spacing: 1.6px; font-size: 13px; gap: 5px; }
  .code-chip .code-label { display: none; }
}
@media (max-width: 420px) {
  .players-grid { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
  .demo-stage { min-height: 172px; }
}

/* ---------- stepped onboarding ---------- */
.step-card { position: relative; }
.step-dots { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
.step-dot {
  display: flex; align-items: center; gap: 7px; padding: 5px 12px 5px 6px; border-radius: 999px;
  background: var(--glass); border: 1px solid var(--stroke); transition: all 0.25s;
}
.step-dot .sd-num {
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  font-size: 12px; font-weight: 800; background: rgba(255, 255, 255, 0.1); color: var(--ink-dim);
}
.step-dot .sd-label { font-size: 11.5px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; color: var(--ink-faint); }
.step-dot.active { border-color: var(--violet); background: rgba(139, 92, 246, 0.14); }
.step-dot.active .sd-num { background: var(--grad-brand); color: #fff; box-shadow: 0 2px 10px rgba(139, 92, 246, 0.5); }
.step-dot.active .sd-label { color: var(--ink); }
.step-dot.done .sd-num { background: rgba(52, 211, 153, 0.25); color: #86efac; }

.identity-chip {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: var(--r-md);
  background: var(--glass-2); border: 1px solid var(--stroke);
  animation: pop-in 0.35s cubic-bezier(0.2, 1.4, 0.4, 1) backwards;
}
.identity-chip .ic-avatar {
  width: 44px; height: 44px; flex: none; border-radius: 14px; display: grid; place-items: center;
  font-size: 26px; background: rgba(139, 92, 246, 0.18); border: 1px solid rgba(139, 92, 246, 0.4);
}
.identity-chip .ic-body { flex: 1; min-width: 0; }
.identity-chip .ic-name { font-weight: 800; font-size: 16.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.choice-grid { display: grid; gap: 12px; }
@media (min-width: 520px) { .choice-grid { grid-template-columns: 1fr 1fr; } }
.choice-card {
  text-align: left; padding: 18px; border-radius: var(--r-lg); border: 2px solid var(--stroke);
  background: var(--glass); position: relative; overflow: hidden;
  transition: transform 0.2s cubic-bezier(0.3, 1.3, 0.5, 1), border-color 0.2s, box-shadow 0.25s, background 0.2s;
}
.choice-card::after {
  content: ''; position: absolute; inset: -50% -50% auto auto; width: 180px; height: 180px;
  border-radius: 50%; opacity: 0; transition: opacity 0.3s; pointer-events: none;
}
.choice-card.create::after { background: radial-gradient(circle, rgba(139, 92, 246, 0.55), transparent 65%); }
.choice-card.join::after { background: radial-gradient(circle, rgba(34, 211, 238, 0.5), transparent 65%); }
.choice-card:hover { transform: translateY(-4px); border-color: var(--stroke-2); background: var(--glass-2); }
.choice-card:hover::after { opacity: 1; }
.choice-card:active { transform: scale(0.98); }
.choice-card:disabled { opacity: 0.5; pointer-events: none; }
.choice-card.create { border-color: rgba(139, 92, 246, 0.45); }
.choice-card.join.open { border-color: var(--cyan); background: rgba(34, 211, 238, 0.1); }
.choice-card .cc-emoji { font-size: 30px; display: block; }
.choice-card .cc-title { font-size: 17.5px; font-weight: 800; margin: 8px 0 3px; }
.choice-card .cc-sub { font-size: 13px; color: var(--ink-dim); line-height: 1.45; }

.join-panel { margin-top: 14px; animation: screen-in 0.3s cubic-bezier(0.2, 0.9, 0.3, 1); }

.conn-notice {
  display: flex; align-items: center; gap: 12px; padding: 13px 16px; margin-bottom: 14px;
  border-radius: var(--r-md); font-size: 14px;
  background: rgba(251, 191, 36, 0.1); border: 1px solid rgba(251, 191, 36, 0.4);
  animation: pop-in 0.3s ease backwards;
}
.conn-notice > span:first-child { font-size: 22px; }

/* ---------- lobby exit ---------- */
.lobby-exit { text-align: center; margin-top: 20px; }
.lobby-exit .btn { min-width: 240px; }
.icon-btn.danger-btn:hover { background: rgba(248, 113, 113, 0.18); border-color: rgba(248, 113, 113, 0.5); }

/* ---------- reveal on scroll ---------- */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity 0.6s ease, transform 0.7s cubic-bezier(0.2, 0.9, 0.3, 1); }
.reveal.revealed { opacity: 1; transform: none; }

/* ---------- pointer parallax in the hero ---------- */
.hero { --px: 0; --py: 0; }
.hero .hero-logo {
  transform: translate3d(calc(var(--px) * 10px), calc(var(--py) * 8px), 0);
  transition: transform 0.35s cubic-bezier(0.2, 0.9, 0.3, 1);
  animation: none; /* parallax owns the transform while the pointer is over the hero */
}
.hero:not(:hover) .hero-logo { animation: logo-land 0.85s cubic-bezier(0.16, 1.5, 0.36, 1) backwards, logo-float 5s ease-in-out 0.85s infinite; }
.hero-float-field { transform: translate3d(calc(var(--px) * -16px), calc(var(--py) * -10px), 0); transition: transform 0.6s ease-out; }
/* The parallax lives on a wrapper axis the letters do not use, so it cannot cancel the
   per-letter drop animation. */
.hero .title { transition: transform 0.4s ease-out; }
.hero .title .wm-word { transform: translate3d(calc(var(--px) * 5px), calc(var(--py) * 3px), 0); transition: transform 0.4s ease-out; }

/* ---------- card tilt ---------- */
.tilt { --tx: 0deg; --ty: 0deg; transform-style: preserve-3d; }
.tilt:hover { transform: perspective(700px) rotateX(var(--tx)) rotateY(var(--ty)) translateY(-4px); }

/* ---------- music button ---------- */
.music-btn.playing { border-color: rgba(139, 92, 246, 0.5); background: rgba(139, 92, 246, 0.16); }
.music-btn.playing::after {
  content: ''; position: absolute; inset: -3px; border-radius: 14px; pointer-events: none;
  box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.5); animation: music-pulse 2.6s ease-out infinite;
}
.icon-btn { position: relative; }
@keyframes music-pulse {
  0% { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.45); }
  70% { box-shadow: 0 0 0 9px rgba(139, 92, 246, 0); }
  100% { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0); }
}

/* ---------- extra life ---------- */
.btn-lg:hover .btn-arrow { transform: translateX(4px); }
.choice-card .cc-emoji { transition: transform 0.3s cubic-bezier(0.2, 1.4, 0.4, 1); }
.choice-card:hover .cc-emoji { transform: scale(1.16) rotate(-8deg); }
.game-card:hover .gc-emoji { animation: emoji-hop 0.6s ease; }
@keyframes emoji-hop { 0%, 100% { transform: translateY(0) } 40% { transform: translateY(-9px) rotate(-8deg) } }
.starter-banner .sb-items { animation: items-glow 3.4s ease-in-out infinite; }
@keyframes items-glow { 0%, 100% { text-shadow: 0 0 0 rgba(134, 239, 172, 0) } 50% { text-shadow: 0 0 18px rgba(134, 239, 172, 0.55) } }
.step-card { animation: step-in 0.45s cubic-bezier(0.2, 1.1, 0.3, 1); }
@keyframes step-in { from { opacity: 0; transform: translateY(16px) scale(0.985); } }

/* ---------- share fallback ---------- */
.share-code {
  text-align: center; font-size: 34px; font-weight: 800; letter-spacing: 10px;
  padding: 16px; border-radius: var(--r-md);
  background: rgba(0, 0, 0, 0.3); border: 1.5px dashed var(--stroke-2);
}

/* ---------- optional background loop (public/media/bg.mp4) ---------- */
.bg-video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 1.4s ease; pointer-events: none;
}
.bg-video.ready { opacity: 0.42; }
/* With a video behind everything, the gradient blobs only need to tint it. */
body.has-bg-video .blob { opacity: 0.28; }
body.has-bg-video .bg-scene::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(11,14,26,0.55) 0%, rgba(11,14,26,0.78) 60%, rgba(11,14,26,0.92) 100%);
}
@media (prefers-reduced-motion: reduce) { .bg-video { display: none; } }

/* ---------- preview: real components, scaled down ---------- */
.demo-clues { display: flex; flex-direction: column; }
.demo-stage .clue-round-label { margin-bottom: 2px; }
.demo-stage .reaction-bar { gap: 4px; margin-top: 4px; }
.demo-stage .react-chip { padding: 2px 6px; font-size: 11.5px; }
.demo-stage .react-chip .rc-count { font-size: 9.5px; }
.demo-stage .react-trigger { width: 19px; height: 19px; font-size: 11px; }
.demo-tiles { grid-template-columns: repeat(3, 1fr); gap: 7px; margin-top: 2px; }
.demo-tiles .player-tile { padding: 8px 5px; gap: 2px; }
.demo-tiles .player-tile .pt-avatar { font-size: 22px; }
.demo-tiles .player-tile .pt-name { font-size: 11.5px; }
.demo-tiles .player-tile .pt-sub { font-size: 9px; }
.demo-tiles .pt-mark { font-size: 12px; }
.demo-stage .clue-row { padding: 4px 0; }
.demo-stage .clue-bubble { font-size: 13.5px; padding: 6px 12px; }
.demo-stage .cr-avatar { font-size: 18px; width: 26px; }
.demo-starter { padding: 12px; font-size: 13px; }
.demo-starter .sb-items { font-size: 16px; }
.demo-packing { margin-top: 10px; gap: 8px; }
.demo-packing .pack-col { padding: 8px; gap: 5px; }
.demo-packing .pack-head { font-size: 10px; }
.demo-packing .pack-item { padding: 5px 8px; }
.demo-packing .pack-word { font-size: 12.5px; }
.demo-packing .pack-by { font-size: 9.5px; }
.demo-stage .attempt { margin-top: 8px; padding: 8px 11px; }
.demo-stage .attempt .at-text { font-size: 13px; }
.demo-stage .attempt .at-remark { font-size: 11.5px; }
@media (max-width: 430px) { .demo-packing .pack-item { flex-direction: row; } }

/* ---------- living canvas backdrop ---------- */
.bg-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* The canvas carries the motion, so the static CSS blobs only need to tint behind it. */
body:has(.bg-canvas) .blob { opacity: 0.22; }

/* ---------- soundtrack picker ---------- */
.theme-list { display: flex; flex-direction: column; gap: 8px; }
.theme-row {
  display: flex; align-items: center; gap: 12px; text-align: left; width: 100%;
  padding: 12px 14px; border-radius: var(--r-md);
  background: var(--glass); border: 1px solid var(--stroke); color: var(--ink);
  transition: background 0.2s, border-color 0.2s, transform 0.15s;
}
.theme-row:hover { background: var(--glass-2); transform: translateX(2px); }
.theme-row .tr-dot {
  width: 10px; height: 10px; border-radius: 50%; flex: none;
  background: rgba(255, 255, 255, 0.18); transition: background 0.2s, box-shadow 0.2s;
}
.theme-row .tr-name { font-weight: 700; font-size: 15px; }
.theme-row .tr-blurb { font-size: 12.5px; color: var(--ink-dim); margin-top: 2px; line-height: 1.4; }
.theme-row .tr-check { margin-left: auto; color: var(--violet); font-size: 13px; }
.theme-row.active { border-color: var(--violet); background: rgba(139, 92, 246, 0.14); }
.theme-row.active .tr-dot {
  background: var(--violet); box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.22);
  animation: music-pulse 2s ease-out infinite;
}

/* ---------- sound panel ---------- */
.sound-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 4px 0; }
.sound-row.master .sr-title { font-size: 16px; }
.sound-row .sr-title { font-weight: 700; font-size: 14.5px; }
.sound-row .sr-sub { font-size: 12.5px; color: var(--ink-faint); margin-top: 3px; line-height: 1.4; }
.sound-row.dimmed, .vol-row.dimmed, .theme-row.dimmed { opacity: 0.45; }
.theme-row.dimmed { pointer-events: auto; }  /* tapping a theme re-enables the music */

.vol-row { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.vol-row .vol-icon { font-size: 13px; opacity: 0.7; }
.vol-row .vol-value { font-size: 12.5px; font-weight: 700; color: var(--ink-dim); min-width: 38px; text-align: right; }

.vol-slider {
  -webkit-appearance: none; appearance: none; flex: 1;
  height: 6px; border-radius: 99px; outline: none;
  background: linear-gradient(90deg, var(--violet), var(--cyan));
  background-color: rgba(255, 255, 255, 0.12);
}
.vol-slider:disabled { filter: grayscale(0.8); }
.vol-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 20px; height: 20px; border-radius: 50%;
  background: #fff; border: 2px solid var(--violet);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  cursor: pointer; transition: transform 0.15s;
}
.vol-slider::-webkit-slider-thumb:hover { transform: scale(1.15); }
.vol-slider::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%;
  background: #fff; border: 2px solid var(--violet); cursor: pointer;
}
.switch:disabled { opacity: 0.5; }

/* ---------- rotating backdrop (two cross-fading layers) ---------- */
.bg-layer {
  position: absolute; inset: -4% -4%;
  background-size: cover; background-position: center 42%;
  opacity: 0; transition: opacity 1.6s ease;
  will-change: transform, opacity;
  animation: ken-burns 46s ease-in-out infinite alternate;
}
.bg-layer.ready { opacity: 0.62; }
.bg-layer-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: none;
}
.bg-layer.is-video .bg-layer-video { display: block; }
/* A clip has its own motion, so the slow pan would fight it. */
.bg-layer.is-video { animation: none; }

@keyframes ken-burns {
  0%   { transform: scale(1.04) translate3d(0, 0, 0); }
  50%  { transform: scale(1.11) translate3d(-1.6%, -1.1%, 0); }
  100% { transform: scale(1.06) translate3d(1.4%, 0.8%, 0); }
}

/* Anything behind the UI gets a scrim; the gradient blobs only tint from here on. */
body.has-bg-media .blob { opacity: 0.2; }
body.has-bg-media .bg-canvas { opacity: 0.62; }
body.has-bg-media .bg-scene::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(11,14,26,0.55) 0%, rgba(11,14,26,0.78) 60%, rgba(11,14,26,0.92) 100%);
}

/* Faces are the hard case: blur them and darken, or the headline is unreadable. */
body.has-people-media .bg-layer.ready { opacity: 0.72; filter: blur(2.5px) saturate(1.02) brightness(0.92); }
body.has-people-media .bg-layer.is-video.ready { opacity: 0.6; filter: blur(4px) saturate(0.95) brightness(0.8); }
body.has-people-media .bg-scene::after {
  background:
    radial-gradient(90% 55% at 50% 16%, rgba(11,14,26,0.72) 0%, rgba(11,14,26,0.18) 78%),
    linear-gradient(180deg, rgba(11,14,26,0.42) 0%, rgba(11,14,26,0.62) 46%, rgba(11,14,26,0.9) 100%);
}
body.has-people-media .blob { opacity: 0.1; }
body.has-people-media .bg-canvas { opacity: 0.3; }

@media (prefers-reduced-motion: reduce) { .bg-layer { animation: none; } }

/* ---------- footer legal line ---------- */
.legal-line { margin-top: 10px; font-size: 12px; color: var(--ink-faint); }
.footer-link.as-link {
  background: none; border: none; padding: 0; font: inherit; cursor: pointer;
  color: var(--ink-dim); font-weight: 700; text-decoration: underline;
  text-decoration-color: rgba(255, 255, 255, 0.25); text-underline-offset: 2px;
}
.footer-link.as-link:hover { color: var(--ink); text-decoration-color: var(--violet); }

/* ---------- "tap for sound" hint ---------- */
.sound-hint {
  position: fixed; left: 50%; bottom: calc(22px + var(--safe-b)); transform: translateX(-50%);
  z-index: 60; display: inline-flex; align-items: center; gap: 9px;
  padding: 11px 20px; border-radius: 999px; color: var(--ink);
  font-size: 14px; font-weight: 700; cursor: pointer;
  background: rgba(20, 24, 46, 0.92); border: 1px solid var(--stroke-2);
  box-shadow: var(--shadow-lg); backdrop-filter: blur(12px);
  animation: hint-in 0.45s cubic-bezier(0.2, 1.3, 0.4, 1);
  transition: opacity 1s ease, transform 1s ease;
}
.sound-hint .sh-icon { font-size: 16px; animation: hint-pulse 1.8s ease-in-out infinite; }
.sound-hint.fade { opacity: 0; transform: translateX(-50%) translateY(8px); }
@keyframes hint-in { from { opacity: 0; transform: translateX(-50%) translateY(16px); } }
@keyframes hint-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.18); } }

/* ============================================================
   Developer credit — hero jump link + showcase card
   (restored: an earlier range edit removed this block)
   ============================================================ */
.meet-dev {
  margin-top: 18px; display: inline-flex; align-items: center; gap: 10px;
  padding: 7px 16px 7px 8px; border-radius: 999px; color: var(--ink);
  border: 1.5px solid transparent;
  background:
    linear-gradient(rgba(17, 21, 43, 0.92), rgba(17, 21, 43, 0.92)) padding-box,
    linear-gradient(120deg, #8b5cf6, #22d3ee, #f472b6, #fbbf24, #8b5cf6) border-box;
  background-size: 100% 100%, 320% 100%;
  animation: screen-in 0.5s 0.7s backwards, border-flow 7s linear infinite;
  box-shadow: 0 8px 26px rgba(99, 102, 241, 0.2);
  transition: transform 0.2s cubic-bezier(0.3, 1.5, 0.5, 1), box-shadow 0.25s;
}
.meet-dev:hover { transform: translateY(-2px); box-shadow: 0 12px 32px rgba(99, 102, 241, 0.35); }
.meet-dev .md-text { font-size: 14px; font-weight: 700; }
.meet-dev .md-arrow { font-size: 13px; color: var(--cyan); font-weight: 800; animation: nudge-down 1.8s ease-in-out infinite; }
@keyframes nudge-down { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(3px); } }

.dev-showcase {
  position: relative; overflow: hidden; scroll-margin-top: 90px;
  border: 1.5px solid transparent;
  background:
    linear-gradient(rgba(18, 22, 44, 0.96), rgba(14, 17, 35, 0.96)) padding-box,
    linear-gradient(120deg, #8b5cf6, #22d3ee, #f472b6, #8b5cf6) border-box;
  background-size: 100% 100%, 320% 100%;
  animation: border-flow 8s linear infinite;
}
.dev-showcase .ds-glow {
  position: absolute; inset: -60% 40% 30% -30%; pointer-events: none;
  background: radial-gradient(circle, rgba(139, 92, 246, 0.28), transparent 62%);
}
.dev-showcase.spotlight { animation: border-flow 8s linear infinite, spotlight 1.5s ease-out; }
@keyframes spotlight {
  0% { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.65); }
  100% { box-shadow: 0 0 0 26px rgba(139, 92, 246, 0); }
}
.ds-label {
  position: relative; font-size: 11px; font-weight: 800; letter-spacing: 2.4px;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 18px;
}

/* The layout that went missing: portrait beside the copy. */
.ds-body { position: relative; display: flex; gap: 22px; align-items: flex-start; }
.ds-portrait { position: relative; flex: none; width: 128px; height: 128px; }
.ds-photo {
  width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block;
  border: 3px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.55);
  animation: portrait-float 6s ease-in-out infinite;
}
@keyframes portrait-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
.ds-ring {
  position: absolute; inset: -7px; border-radius: 50%; pointer-events: none; opacity: 0.9;
  background: conic-gradient(from 0deg, #8b5cf6, #22d3ee, #f472b6, #fbbf24, #8b5cf6);
  mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  animation: ring-spin 9s linear infinite;
}
@keyframes ring-spin { to { transform: rotate(360deg); } }

.ds-copy { flex: 1; min-width: 0; }
.ds-name { font-size: clamp(21px, 5vw, 26px); font-weight: 800; line-height: 1.15; }
.ds-role { font-size: 13.5px; font-weight: 600; color: var(--cyan); margin-top: 4px; }
.ds-origin {
  position: relative; font-size: 14.5px; line-height: 1.6; color: var(--ink-dim);
  margin: 14px 0 16px; padding-left: 14px; font-style: italic;
  border-left: 2px solid rgba(139, 92, 246, 0.55);
}
.ds-origin .ds-quote { color: var(--violet); font-weight: 800; font-style: normal; }

.dev-links.lg { gap: 10px; }
.dev-links.lg .dev-link { padding: 9px 16px; font-size: 13.5px; }

/* Compact variant, used inside the About modal. */
.ds-body.compact { gap: 16px; }
.ds-body.compact .ds-portrait { width: 84px; height: 84px; }
.ds-body.compact .ds-photo { border-width: 2px; animation: none; }
.ds-body.compact .ds-name { font-size: 18px; font-weight: 800; }
.ds-body.compact .ds-role { font-size: 12.5px; }
.ds-body.compact .ds-origin { font-size: 13px; margin: 10px 0 12px; }
.dev-card.dev-showcase .ds-label { margin-bottom: 14px; }

@media (max-width: 560px) {
  .ds-body, .ds-body.compact { flex-direction: column; align-items: center; text-align: center; }
  .ds-origin { padding-left: 0; border-left: none; text-align: center; }
  .dev-showcase .dev-links { justify-content: center; }
}

/* ============================================================
   Author photo, small sizes — pill, footer, inline
   (restored: the same range edit removed these)
   ============================================================ */
.dev-photo {
  border-radius: 50%; object-fit: cover; flex: none; display: block;
  border: 1.5px solid rgba(255, 255, 255, 0.35);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
/* Inside the hero pill: a touch brighter so it reads against the gradient border. */
.meet-dev .dev-photo {
  border-color: rgba(255, 255, 255, 0.5);
  transition: transform 0.25s cubic-bezier(0.3, 1.5, 0.5, 1);
}
.meet-dev:hover .dev-photo { transform: scale(1.08); }
/* In the footer it sits in a line of text, so it needs baseline alignment and breathing
   room on both sides — without these it collides with the name. */
.landing-footer .dev-photo {
  display: inline-block; vertical-align: -7px;
  margin: 0 7px 0 3px; border-width: 1px;
}

/* ============================================================
   Reactions, standings, and player-tile states
   ============================================================ */
.pt-you { color: var(--cyan); font-weight: 700; }
.pt-offline { color: var(--amber); font-weight: 700; }

/* ---------- clue reactions ---------- */
.reaction-bar { display: flex; align-items: center; gap: 5px; margin-top: 5px; flex-wrap: wrap; }
.react-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 8px; border-radius: 999px; font-size: 13px; line-height: 1;
  background: var(--glass); border: 1px solid var(--stroke);
  transition: transform 0.15s cubic-bezier(0.3, 1.6, 0.5, 1), background 0.2s, border-color 0.2s;
}
.react-chip:hover { transform: translateY(-2px) scale(1.06); background: var(--glass-2); }
.react-chip:active { transform: scale(0.92); }
.react-chip.mine { border-color: var(--violet); background: rgba(139, 92, 246, 0.2); }
.react-chip .rc-count { font-size: 11px; font-weight: 800; color: var(--ink-dim); }

.react-add { position: relative; display: inline-flex; }
.react-trigger {
  width: 24px; height: 24px; border-radius: 50%; font-size: 13px; font-weight: 800;
  color: var(--ink-faint); background: var(--glass); border: 1px dashed var(--stroke-2);
  display: grid; place-items: center; transition: all 0.18s;
}
.react-trigger:hover { color: var(--ink); border-style: solid; transform: scale(1.12); }
.react-menu {
  position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%) scale(0.85);
  display: flex; gap: 2px; padding: 5px; border-radius: 999px; z-index: 20;
  background: rgba(22, 26, 50, 0.97); border: 1px solid var(--stroke-2);
  box-shadow: var(--shadow-lg); backdrop-filter: blur(10px);
  opacity: 0; pointer-events: none; transition: opacity 0.16s, transform 0.16s cubic-bezier(0.2, 1.4, 0.4, 1);
}
/* An invisible bridge across the 6px gap, so travelling from the ＋ to the palette never
   leaves the hover area. Without it the menu closed mid-reach. */
.react-menu::after {
  content: ''; position: absolute; left: -6px; right: -6px; top: 100%; height: 12px;
}
/* JS owns the open state (see reactionBar) so the palette can linger after the pointer
   leaves. The hover rule stays as a no-JS fallback. */
.react-add:hover .react-menu,
.react-add:focus-within .react-menu,
.react-add.open .react-menu {
  opacity: 1; pointer-events: auto; transform: translateX(-50%) scale(1);
}
.react-add.pinned .react-trigger { color: var(--ink); border-style: solid; background: var(--glass-2); }
.react-opt {
  width: 30px; height: 30px; border-radius: 50%; font-size: 16px; display: grid; place-items: center;
  transition: transform 0.15s cubic-bezier(0.3, 1.7, 0.5, 1), background 0.15s;
}
.react-opt:hover { transform: scale(1.35) translateY(-2px); background: var(--glass-2); }
.react-opt.mine { background: rgba(139, 92, 246, 0.28); }
/* Touch has no hover, so keep the palette open. */
@media (hover: none) {
  .react-menu { position: static; transform: none; opacity: 1; pointer-events: auto; background: none; border: none; box-shadow: none; padding: 0; }
  .react-trigger { display: none; }
  .react-opt { width: 26px; height: 26px; font-size: 14px; }
}

/* ---------- standings ---------- */
.standings-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.standings-sub { font-size: 11.5px; color: var(--ink-faint); margin-top: 3px; }
.standings-list { display: flex; flex-direction: column; gap: 7px; }
.standings .lb-row.gone { opacity: 0.5; }
.lb-title { font-size: 11.5px; color: var(--ink-faint); font-weight: 600; }
.lb-split { font-size: 11px; color: var(--ink-faint); font-weight: 700; white-space: nowrap; }

.hall-of-fame {
  margin-top: 16px; padding-top: 14px; border-top: 1px dashed var(--stroke-2);
}
.hof-label {
  font-size: 10.5px; font-weight: 800; letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--amber); margin-bottom: 9px;
}
.hof-row {
  display: flex; align-items: center; gap: 9px; padding: 6px 10px; border-radius: 10px;
  background: rgba(251, 191, 36, 0.06); border: 1px solid rgba(251, 191, 36, 0.18);
  font-size: 13px; margin-bottom: 5px;
}
.hof-medal { width: 22px; text-align: center; }
.hof-name { font-weight: 700; }
.hof-title { color: var(--ink-faint); font-size: 11.5px; margin-left: auto; white-space: nowrap; }
.hof-pts { font-weight: 800; color: var(--amber); min-width: 30px; text-align: right; }

/* ---------- how points work ---------- */
.score-rules { display: flex; flex-direction: column; gap: 6px; margin: 8px 0 4px; }
.score-rule {
  display: flex; align-items: center; gap: 10px; padding: 7px 11px; border-radius: 10px;
  background: var(--glass); border: 1px solid var(--stroke); font-size: 13.5px;
}
.score-rule .sr-icon { font-size: 15px; width: 20px; text-align: center; }
.score-rule .sr-pts { font-weight: 800; color: var(--emerald); min-width: 30px; }
.score-rule .sr-text { color: var(--ink-dim); line-height: 1.35; }
.danger-hint { color: #fca5a5 !important; font-weight: 700; }

/* ---------- feedback (bug / appreciation) ---------- */
.feedback-grid { display: grid; gap: 12px; }
@media (min-width: 520px) { .feedback-grid { grid-template-columns: 1fr 1fr; } }
.choice-card.bug { border-color: rgba(248, 113, 113, 0.4); }
.choice-card.bug::after { background: radial-gradient(circle, rgba(248, 113, 113, 0.5), transparent 65%); }
.choice-card.love { border-color: rgba(251, 191, 36, 0.45); }
.choice-card.love::after { background: radial-gradient(circle, rgba(251, 191, 36, 0.5), transparent 65%); }


/* ---------- owner-only controls, seen by everyone else ---------- */
/* A guest looking at a host-only card should be able to tell at a glance that the
   controls are not theirs, instead of tapping a switch that will never move. */
.host-lock {
  display: flex; align-items: center; gap: 11px;
  margin: 8px 0 4px; padding: 10px 13px; border-radius: var(--r-md);
  background: rgba(251, 191, 36, 0.08); border: 1px solid rgba(251, 191, 36, 0.28);
}
.host-lock .hl-crown { font-size: 19px; line-height: 1; }
.host-lock .hl-title { font-size: 13px; font-weight: 700; color: var(--amber); }
.host-lock .hl-sub { font-size: 11.5px; color: var(--ink-faint); margin-top: 1px; }

/* Controls inside a guest's copy of an owner card: visible, clearly inert. */
.card.guest-view .switch,
.card.guest-view .stepper,
.card.guest-view .stepper-val {
  opacity: 0.5; pointer-events: none; filter: saturate(0.5);
}
.card.guest-view .setting-row { cursor: default; }
/* The chosen difficulty still reads clearly; the ones they cannot pick recede. */
.card.guest-view .diff-chip:not(.on) { opacity: 0.45; }

.game-card.read-only { cursor: default; }
.game-card.read-only:hover { transform: none; }
.game-card.read-only .gc-glow { opacity: 0.35; }
.gc-picked {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.6px; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px;
  background: rgba(251, 191, 36, 0.18); border: 1px solid rgba(251, 191, 36, 0.4);
  color: var(--amber);
}

.waiting-note {
  margin-top: 12px; text-align: center; font-size: 13.5px; font-weight: 600;
  color: var(--ink-dim); padding: 11px; border-radius: var(--r-md);
  background: var(--glass); border: 1px dashed var(--stroke-2);
}

/* ---------- island hints ---------- */
.hint-countdown { margin-top: 10px; text-align: center; font-size: 12.5px; }
.hint-btn { border-color: rgba(251, 191, 36, 0.35); color: var(--amber); }
.hint-btn:hover:not(:disabled) { background: rgba(251, 191, 36, 0.12); border-color: rgba(251, 191, 36, 0.55); }

/* ---------- blendin: difficulty picker ---------- */
.diff-block { padding: 4px 0 2px; }
.diff-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-top: 6px; }
.diff-chip {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 9px 4px; border-radius: var(--r-md);
  background: var(--glass); border: 1px solid var(--stroke);
  transition: transform 0.15s cubic-bezier(0.3, 1.5, 0.5, 1), background 0.2s, border-color 0.2s;
}
.diff-chip:hover:not(.locked) { transform: translateY(-2px); background: var(--glass-2); }
.diff-chip.on { border-color: var(--violet); background: rgba(139, 92, 246, 0.16); }
.diff-chip.locked { cursor: default; opacity: 0.75; }
.diff-chip.locked.on { opacity: 1; }
.dc-emoji { font-size: 17px; line-height: 1; }
.dc-label { font-size: 11.5px; font-weight: 700; color: var(--ink-dim); }
.diff-chip.on .dc-label { color: var(--ink); }
.diff-brief { font-size: 11.5px; color: var(--ink-faint); line-height: 1.45; margin: 8px 2px 0; }
.diff-eg { color: var(--cyan); font-weight: 600; }

/* ---------- blendin: dealing screen ---------- */
.dealing-card { text-align: center; padding: 30px 20px; }

/* ---------- "the AI is working" state ---------- */
.ai-think { display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 10px 0 4px; }

.ai-core { position: relative; width: 96px; height: 96px; display: grid; place-items: center; }
.ai-core .ai-face {
  font-size: 40px; line-height: 1; z-index: 2;
  animation: ai-bob 2.4s ease-in-out infinite;
  filter: drop-shadow(0 0 14px rgba(139, 92, 246, 0.55));
}
/* Three rings expanding outward on a stagger: reads as scanning, costs one transform. */
.ai-core .ai-ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px solid rgba(139, 92, 246, 0.55);
  animation: ai-pulse 2.4s cubic-bezier(0.2, 0.6, 0.3, 1) infinite;
}
.ai-core .ai-ring.r2 { animation-delay: 0.8s; border-color: rgba(34, 211, 238, 0.5); }
.ai-core .ai-ring.r3 { animation-delay: 1.6s; border-color: rgba(244, 114, 182, 0.4); }
/* A horizontal line sweeping the face, like something being read. */
.ai-core .ai-scan {
  position: absolute; left: 8%; right: 8%; height: 2px; z-index: 3; border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--cyan), transparent);
  animation: ai-scan 1.9s ease-in-out infinite;
}

.ai-copy { text-align: center; }
.ai-title { font-weight: 700; font-size: 15.5px; }
.ai-sub { font-size: 12.5px; color: var(--ink-faint); margin-top: 4px; }

.ai-dots { display: inline-flex; gap: 3px; margin-left: 3px; vertical-align: baseline; }
.ai-dots i {
  width: 4px; height: 4px; border-radius: 50%; background: var(--cyan);
  animation: ai-dot 1.4s ease-in-out infinite;
}
.ai-dots i:nth-child(2) { animation-delay: 0.18s; }
.ai-dots i:nth-child(3) { animation-delay: 0.36s; }

@keyframes ai-bob {
  0%, 100% { transform: translateY(0) scale(1); }
  50% { transform: translateY(-5px) scale(1.05); }
}
@keyframes ai-pulse {
  0% { transform: scale(0.55); opacity: 0; }
  35% { opacity: 0.9; }
  100% { transform: scale(1.12); opacity: 0; }
}
@keyframes ai-scan {
  0%, 100% { transform: translateY(-30px); opacity: 0; }
  50% { transform: translateY(30px); opacity: 1; }
}
@keyframes ai-dot {
  0%, 100% { transform: translateY(0); opacity: 0.35; }
  50% { transform: translateY(-4px); opacity: 1; }
}

/* Reduced motion: keep the badge, drop every moving part. */
@media (prefers-reduced-motion: reduce) {
  .ai-core .ai-face, .ai-core .ai-ring, .ai-core .ai-scan, .ai-dots i { animation: none; }
  .ai-core .ai-ring { opacity: 0.35; transform: scale(1); }
  .ai-core .ai-scan { opacity: 0; }
}

/* ---------- arrived on an invite link ---------- */
.invited-banner {
  display: flex; align-items: center; gap: 11px;
  margin: 4px 0 16px; padding: 11px 13px; border-radius: var(--r-md);
  background: rgba(34, 211, 238, 0.09); border: 1px solid rgba(34, 211, 238, 0.3);
}
.invited-banner .ib-key { font-size: 19px; line-height: 1; }
.invited-banner .ib-title { font-size: 13.5px; font-weight: 700; color: var(--cyan); letter-spacing: 0.4px; }
.invited-banner .ib-sub { font-size: 11.5px; color: var(--ink-faint); margin-top: 1px; }

/* ---------- vote confirmation ---------- */
/* A vote cannot be undone, so it gets a deliberate second step rather than firing on the
   first tap. */
.vote-confirm {
  margin-top: 14px; padding: 14px; border-radius: var(--r-md); text-align: center;
  background: rgba(244, 114, 182, 0.08); border: 1px solid rgba(244, 114, 182, 0.32);
  animation: vc-in 0.22s cubic-bezier(0.2, 1.4, 0.4, 1);
}
.vote-confirm .vc-ask { font-size: 15.5px; font-weight: 600; }
.vote-confirm .vc-warn { font-size: 11.5px; color: var(--ink-faint); margin-top: 3px; }
.vote-confirm .vc-actions { display: flex; gap: 10px; margin-top: 12px; }
.vote-confirm .vc-actions .btn { flex: 1; }
@keyframes vc-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .vote-confirm { animation: none; } }

/* ---------- abandoned round ---------- */
.stalled-card {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 4px;
  border-color: rgba(251, 191, 36, 0.4); background: rgba(251, 191, 36, 0.07);
}
.stalled-card .sc-emoji { font-size: 40px; line-height: 1; }
.stalled-card .sc-title { font-size: 17px; font-weight: 800; margin-top: 6px; }
.stalled-card .sc-sub { font-size: 13px; color: var(--ink-dim); margin-top: 3px; }

/* ---------- the card table ---------- */
/* Drawn from the viewer's chair, not from above. Three things sell it: the felt is tipped
   away on the X axis so it recedes, each seat is scaled by how far off it sits, and dealt
   cards arc up over the table rather than sliding flat.
   Seats use left/top percentages, which resolve against the table. The flight vector has
   to be a length, because a transform percentage resolves against the card itself. */
/* ---------------------------------------------------------------------------
   THE CARD TABLE
   Three device sizes, decided here and nowhere else. Everything below derives
   from these four numbers, and the bands they produce cannot overlap: the deck
   sits on --mid, the far seats ride a ring of radius --ry around the same
   point, and the viewer's card owns the bottom of the stage. The caption is a
   row of its own beneath the stage, not a label floating over the felt.
   --------------------------------------------------------------------------- */
.card-table {
  --gold: 212, 175, 55;
  --cw: 52px;          /* a far-side card */
  --my: 2.0;           /* how much bigger your card gets once it is up at your eye */
  --mid: 48%;          /* the middle of the table: deck centre and ring centre */
  /* The ring has to stay well inside the felt. Wider than this and the cards straddle the
     rim, which is what made them look airborne rather than lying on a table. */
  /* Cards sit in from the rim; players sit outside it. The gap between the two rings is
     what stops a player being drawn on top of their own card. */
  /* The two rings are near-concentric on purpose: a card sits just inside its owner the
     whole way round, rather than being flung towards the middle at the far seats. */
  --rx: 43%;           /* where the cards sit: right in front of their owner */
  --ry: 30%;
  --px: 50%;           /* where the players sit: outside the table edge */
  --py: 43%;
  display: flex; flex-direction: column; gap: 2px;
  width: 100%; margin: 2px auto 0;
  /* The strip under the table where the stock parks. Reserved rather than overlapped, so
     nothing below the table ever has to share space with it. */
  padding-bottom: calc(var(--cw) * 2.15);
}
@media (min-width: 600px)  { .card-table { --cw: 62px; --my: 2.05; } }
@media (min-width: 1024px) { .card-table { --cw: 72px; --my: 2.05; } }

.ct-stage {
  position: relative; width: 100%;
  /* Deep enough that the far seat is genuinely far away. A flatter table crowds whoever
     sits at twelve o'clock right up against the middle, which is the one part of the felt
     the games need kept clear. Never crops: a card held up at the viewer's eye overhangs
     the felt, and should. */
  aspect-ratio: 1.58 / 1; max-height: 58vh; overflow: visible;
  perspective: 900px; perspective-origin: 50% 92%;
}
@media (max-width: 560px) { .ct-stage { aspect-ratio: 1.04 / 1; } }

.ct-felt {
  /* Wider and shallower than the ring, so there is table visible on every side of every
     card. A card whose edge crosses the rim reads as floating over the table, not on it. */
  /* The rim has to pass close outside the cards, because that rim is the table edge the
     players sit at. A much wider ellipse leaves them floating in open space. */
  position: absolute; inset: -12% 6% 20%;
  transform: rotateX(30deg); transform-origin: 50% 100%;
  border-radius: 50%;
  background:
    radial-gradient(ellipse at 50% 32%, rgba(var(--gold), 0.07) 0%, transparent 58%),
    radial-gradient(ellipse at 50% 50%, #101634 0%, rgba(11, 14, 26, 0.5) 72%, transparent 100%);
  border: 1px solid rgba(var(--gold), 0.14);
  box-shadow: inset 0 0 80px rgba(0, 0, 0, 0.6), 0 20px 55px rgba(0, 0, 0, 0.5);
}

/* ---- cards: midnight backs with a champagne rule, ivory faces ---- */
.ct-card { position: absolute; left: 0; top: 0; width: 100%; height: 100%; border-radius: 7px; }
.ct-face {
  position: absolute; inset: 0; border-radius: 7px; overflow: hidden;
  display: grid; place-items: center;
  font-weight: 800; font-size: 46cqi; line-height: 1; text-align: center;
  letter-spacing: -0.02em;
  /* Tight and close. The big soft shadow that used to be here is what a card hovering
     well above a surface casts, and that is exactly how the table looked. */
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.14) inset,
    0 1px 2px rgba(0, 0, 0, 0.55),
    0 3px 6px rgba(0, 0, 0, 0.3);
}
.ct-face.back {
  background:
    /* fine lattice, barely there */
    repeating-linear-gradient(45deg, rgba(var(--gold), 0.10) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-45deg, rgba(var(--gold), 0.10) 0 1px, transparent 1px 7px),
    linear-gradient(158deg, #232a52 0%, #141a3a 46%, #1c2247 100%);
  border: 1px solid rgba(var(--gold), 0.34);
}
/* An inset rule and a small centre mark, the way a real deck is finished. */
.ct-face.back::before {
  content: ''; position: absolute; inset: 4px; border-radius: 4px;
  border: 1px solid rgba(var(--gold), 0.22);
}
.ct-face.back::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 28%; aspect-ratio: 1;
  transform: translate(-50%, -50%) rotate(45deg);
  border: 1px solid rgba(var(--gold), 0.4);
  background: rgba(var(--gold), 0.07);
}
.ct-face.front {
  background: linear-gradient(170deg, #fbf9f4 0%, #f0ece1 55%, #e4dfd2 100%);
  color: #141a33; border: 1px solid rgba(var(--gold), 0.5);
  opacity: 0;                       /* revealed by the turn */
  /* The front is stored already turned over, which is where a real card's face is: on the
     other side. Once the card has rotated its 180 degrees the two rotations cancel and the
     face reads the right way round.
     It has to be rotateY and not scaleX(-1). They look equivalent, and they are, but only
     while the card is being flattened to a 2-D matrix. When it genuinely renders in 3-D,
     scaleX(-1) is a second mirror on top of the rotation instead of a cancellation, and
     the number comes out backwards. rotateY is correct in both cases. */
  transform: rotateY(180deg);
  backface-visibility: hidden;
}
.ct-face.back { backface-visibility: hidden; }
/* The faces are two sides of one card, so the card has to keep them in the same 3-D
   space rather than flattening them into itself. */
.ct-card { transform-style: preserve-3d; }
.ct-face.front::before {
  content: ''; position: absolute; inset: 4px; border-radius: 4px;
  border: 1px solid rgba(var(--gold), 0.35);
}

/* ---- the deck, on the middle of the table ---- */
.ct-deck {
  position: absolute; left: 50%; top: var(--mid);
  width: var(--cw); aspect-ratio: 5 / 7;
  /* Held large while shuffling so the motion is the thing you notice, then settling to
     table size as the deal begins. The percentages resolve against the deck itself,
     which is exactly what centring on --mid needs. */
  transform: translate(-50%, -50%) rotateX(24deg) scale(1.45);
  transform-origin: 50% 50%;
  transition: transform 0.6s cubic-bezier(0.3, 0.9, 0.3, 1), opacity 0.45s ease;
  z-index: 12;
  filter: drop-shadow(0 16px 22px rgba(0, 0, 0, 0.6));
}
.card-table.dealt .ct-deck { transform: translate(-50%, -50%) rotateX(24deg) scale(1); }
/* Once the round is out the deck is set down out of the way, the way a dealer parks the
   stock beside them, rather than being left in the middle of the table where the play is
   or vanishing into thin air. */
.card-table.spent .ct-deck {
  /* Below the whole table, in the strip reserved for it. Off the felt entirely, so it is
     in the same place on a phone as on a desktop, it can stay a readable size, and it can
     never crowd a seat or be crowded by one. Left of centre because the near seat and the
     card it lifts own the middle of that strip. */
  top: calc(100% + var(--cw) * 0.92); left: 15%;
  transform: translate(-50%, -50%) rotateX(16deg) rotate(-5deg) scale(0.9);
  opacity: 0.95;
  transition: top 0.8s cubic-bezier(0.3, 0.8, 0.25, 1),
              left 0.8s cubic-bezier(0.3, 0.8, 0.25, 1),
              transform 0.8s cubic-bezier(0.3, 0.8, 0.25, 1),
              opacity 0.4s ease;
}

/* The label under the parked stock. It only appears once the deck has a corner of its own
   to sit in; while it is being shuffled the deck is the middle of the table and needs no
   name. */
.ct-stock {
  /* Directly under the stock it names, centred on it. */
  position: absolute; left: 15%; top: calc(100% + var(--cw) * 1.72);
  transform: translateX(-50%);
  font-size: 12.5px; font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase;
  color: rgba(var(--gold), 0.8); white-space: nowrap;
  opacity: 0; transition: opacity 0.5s ease 0.3s;
  pointer-events: none; z-index: 12;
}
.card-table.spent .ct-stock { opacity: 1; }

/* When a game lets you draw from the stock, the deck says so the same way your own card
   does: the same slow gold pulse, and a hand cursor over it. Off by default, because a
   deck nobody may touch should not be inviting a press. */
.ct-deck { pointer-events: none; }
.ct-deck.pickable {
  pointer-events: auto; cursor: grab;
  animation: ct-yours 2.8s ease-in-out infinite;
}
.ct-deck.pickable:active { cursor: grabbing; }

/* Each card in the stack sits a fraction above the one below, which is the whole reason
   a deck reads as a deck and not as one card. */
.ct-card.in-deck { animation: ct-shuffle 2.3s cubic-bezier(0.4, 0, 0.35, 1) both; }
.ct-card.in-deck { animation-delay: calc(var(--i) * 18ms); }
/* The top card is the brightest and the ones underneath fall away into the stack, so the
   deck has depth rather than looking like one flat sheet. The floor matters as much as the
   falloff: any steeper and the fanned-out cards read as a shadow instead of as cards. */
.ct-card.in-deck .ct-face.back {
  filter: brightness(calc(0.76 + var(--i) * 0.019));
}

/* --cut is 1 for the top half and 0 for the bottom, set per card in JS, because the cut
   phase needs a branch and calc() has no conditionals.
   Three motions, in the order a person does them: riffle, riffle, cut, square up. */
@keyframes ct-shuffle {
  0%   { transform: translate3d(0, calc(var(--i) * -1.1px), 0) rotate(0deg); }
  /* riffle one: the halves fan apart, then interleave back together */
  12%  { transform: translate3d(calc((var(--i) - 6.5) * 5.4px), calc(var(--i) * -1.1px - 9px), 0) rotate(calc((var(--i) - 6.5) * 2.1deg)); }
  24%  { transform: translate3d(calc((var(--i) - 6.5) * -3.2px), calc(var(--i) * -1.1px - 2px), 0) rotate(calc((var(--i) - 6.5) * -1.1deg)); }
  34%  { transform: translate3d(0, calc(var(--i) * -1.1px), 0) rotate(0deg); }
  /* riffle two: shorter, faster, the way a second pass actually goes */
  44%  { transform: translate3d(calc((var(--i) - 6.5) * 3.4px), calc(var(--i) * -1.1px - 5px), 0) rotate(calc((var(--i) - 6.5) * 1.4deg)); }
  54%  { transform: translate3d(0, calc(var(--i) * -1.1px), 0) rotate(0deg); }
  /* the cut: the top half lifts clear, swings across and drops underneath */
  66%  { transform: translate3d(calc(var(--cut) * 30px), calc(var(--i) * -1.1px - var(--cut) * 16px), 0) rotate(calc(var(--cut) * 7deg)); }
  78%  { transform: translate3d(calc(var(--cut) * 34px), calc(var(--i) * -1.1px + var(--cut) * 13px), 0) rotate(calc(var(--cut) * 4deg)); }
  88%  { transform: translate3d(calc(var(--cut) * 6px), calc(var(--i) * -1.1px + var(--cut) * 3px), 0) rotate(calc(var(--cut) * 1deg)); }
  /* square up: knocked flat on the table */
  94%  { transform: translate3d(0, calc(var(--i) * -1.1px + 3px), 0) rotate(0deg); }
  100% { transform: translate3d(0, calc(var(--i) * -1.1px), 0) rotate(0deg); }
}



/* ---------------------------------------------------------------------------
   HANDS
   Two working the deck through the shuffle; the right one then carries every
   card out to its seat, keyframed in JS because each leg goes somewhere
   different and a keyframe rule cannot take an argument.
   --------------------------------------------------------------------------- */
.ct-hand {
  position: absolute; left: 50%; top: var(--mid);
  width: calc(var(--cw) * 0.92);
  transform: translate(-50%, -50%);
  pointer-events: none;
  /* Hidden by default and shown only by the phase that puts something in it. A hand
     hovering over an empty table holding nothing is worse than no hand at all. */
  opacity: 0; transition: opacity 0.35s ease;
  filter: drop-shadow(0 5px 9px rgba(0, 0, 0, 0.6));
}
.ct-hand img { display: block; width: 100%; height: auto; }
/* One hand behind the deck and one in front, which is how a deck is actually held.
   Both in front and they cover the cards they are supposed to be shuffling. */
.ct-hand.h-left { z-index: 11; }
.ct-hand.h-right { z-index: 40; }
.card-table.shuffling .ct-hand { opacity: 0.97; }
/* While dealing, the left hand keeps hold of the deck and the right one carries the
   cards. Both are named here rather than left to the JS animation, so that when the
   phase ends the stylesheet is what decides they are gone. */
.card-table.dealing .ct-hand.h-left,
.card-table.dealing .ct-hand.h-right { opacity: 0.97; }

/* Working the deck: the left hand holds it and the right one riffles. The two keyframe
   sets are deliberately out of step, because two hands doing the same thing at the same
   moment read as one mirrored hand rather than as a pair. */
.ct-hand.h-left  { animation: ct-hold 2.3s cubic-bezier(0.4, 0, 0.35, 1) both; }
.ct-hand.h-right { animation: ct-riffle 2.3s cubic-bezier(0.4, 0, 0.35, 1) both; }
@keyframes ct-hold {
  0%, 100% { opacity: 0.97; transform: translate(-50%, -50%) translate(-72%, 34%) rotate(52deg) scale(0.95); }
  14%  { transform: translate(-50%, -50%) translate(-78%, 30%) rotate(47deg) scale(0.95); }
  36%  { transform: translate(-50%, -50%) translate(-72%, 34%) rotate(52deg) scale(0.95); }
  66%  { transform: translate(-50%, -50%) translate(-68%, 38%) rotate(56deg) scale(0.95); }
  88%  { transform: translate(-50%, -50%) translate(-72%, 32%) rotate(52deg) scale(0.95); }
}
@keyframes ct-riffle {
  0%   { opacity: 0.97; transform: translate(-50%, -50%) translate(70%, 36%) rotate(-50deg) scale(0.95); }
  12%  { transform: translate(-50%, -50%) translate(80%, 26%) rotate(-60deg) scale(0.95); }
  24%  { transform: translate(-50%, -50%) translate(66%, 38%) rotate(-46deg) scale(0.95); }
  44%  { transform: translate(-50%, -50%) translate(76%, 29%) rotate(-56deg) scale(0.95); }
  54%  { transform: translate(-50%, -50%) translate(68%, 37%) rotate(-48deg) scale(0.95); }
  /* the cut: it lifts the top half clear and brings it back down */
  70%  { transform: translate(-50%, -50%) translate(94%, 12%) rotate(-68deg) scale(0.97); }
  84%  { transform: translate(-50%, -50%) translate(74%, 40%) rotate(-50deg) scale(0.95); }
  100% { opacity: 0.97; transform: translate(-50%, -50%) translate(70%, 36%) rotate(-50deg) scale(0.95); }
}
/* Dealing: the riffle keyframes stop, the left hand simply holds the deck where it is. */
.card-table.dealt .ct-hand { animation: none; }
.card-table.dealt .ct-hand.h-left {
  transform: translate(-50%, -50%) translate(-72%, 34%) rotate(52deg) scale(0.95);
}

/* ---------------------------------------------------------------------------
   THE GRIP
   Four fingers laid across the back of your card — drawn inside the card element
   itself, which is the whole trick. The card's own turn carries the hand round
   with it, so fingers that were lying on the back end up behind the face, and
   backface-visibility drops them from view at exactly the halfway point where
   the card is edge-on and the change cannot be seen happening. One drawing, one
   animation, and nothing that can drift out of register with the card.
   --------------------------------------------------------------------------- */
.ct-grip {
  position: absolute; right: -20%; bottom: -14%;
  width: 78%; z-index: 4;
  opacity: 0; pointer-events: none;
  transform: rotate(-12deg) scale(1.02);
  transform-origin: 60% 80%;
  backface-visibility: hidden;
  transition: opacity 0.18s ease;
  filter: drop-shadow(-3px 4px 7px rgba(0, 0, 0, 0.55));
}
.ct-grip img { display: block; width: 100%; height: auto; }
.ct-seat.mine.showing .ct-grip,
.ct-seat.mine.dragging .ct-grip { opacity: 1; }

/* ---- seats on the far arc ---- */
.ct-ring { position: absolute; inset: 0; }
.ct-seat {
  position: absolute;
  left: calc(50% + var(--xf, 0) * var(--rx));
  top: calc(var(--mid) + var(--yf, 0) * var(--ry));
  transform: translate(-50%, -50%);
  z-index: var(--z, 1);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
/* The contact shadow: a flat smear on the felt directly under the card, squashed the way
   a shadow is when the thing casting it is touching the surface. This is what says "on the
   table" more than any amount of card detail does. */
.ct-slot::after {
  content: ''; position: absolute; z-index: 0;
  left: 4%; right: 4%; bottom: -3%; height: 16%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 50%, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 72%);
  filter: blur(2px);
  /* Nothing is casting it yet. A shadow sitting on the felt with no card above it is a
     smudge, and there was one at every empty seat all through the shuffle. */
  opacity: 0;
}
/* It arrives on the same beat as the card that casts it: --d is the seat's deal delay. */
.card-table.dealt .ct-slot::after {
  animation: ct-shadow-in 0.28s ease var(--d, 0ms) both;
}
@keyframes ct-shadow-in { 0% { opacity: 0; } 100% { opacity: 1; } }
.ct-slot > .ct-card { z-index: 1; }
.ct-slot {
  position: relative; width: var(--cw); aspect-ratio: 5 / 7;
  /* --rot squares the card to the player it belongs to. It comes after rotateX so the
     turn happens in the plane of the table rather than in the plane of the screen. */
  transform: scale(var(--s, 1)) rotateX(18deg) rotate(var(--rot, 0deg));
  /* The card is the container, so the number on its face scales with it and reads the
     same on a phone as on a desktop without a single media query. */
  container-type: inline-size;
  /* Perspective has to live here: the slot's own transform flattens anything above it,
     so this is the only place a child rotateY can be given real depth. */
  perspective: 620px;
}
/* The players sit on their own ring, wider than the one the cards are dealt to, so each
   one is at the table edge with their card in front of them rather than drifting near it.
   They only arrive with the cards, so the table is empty while the deck is being shuffled
   rather than showing a ring of players around nothing. Everyone shrinks with the same
   depth scale the cards use, so the far side of the table is genuinely further away. */
.ct-who {
  position: absolute;
  left: calc(50% + var(--xf, 0) * var(--px));
  top: calc(var(--mid) + var(--yf, 0) * var(--py));
  /* The chair itself is not scaled. Depth is applied to the figure only, because a name
     scaled to a third of its size is a name nobody can read — perspective is worth having
     on a silhouette and worth nothing on a label. */
  transform: translate(-50%, -50%);
  z-index: var(--z, 1);
  opacity: 0; transition: opacity 0.4s ease 0.1s;
}
/* You are on the near edge looking in, so your chair is below your card — and further out
   than anyone else's, because the near edge of a table is the closest thing to the viewer.
   Below the seat in the stack, so a card lifted up to your eye passes in front of you
   rather than being drawn behind your own shoulders. */
.ct-who.mine {
  left: 50%; top: calc(var(--mid) + var(--py) * 1.05);
  z-index: 29;
}
.card-table.dealt .ct-who { opacity: 1; }

/* The deal. --fx / --fy are the deck's offset from this seat, measured in JS the moment
   the deal begins, so the card genuinely leaves the deck. --lean is which way it swings,
   so cards going left and right do not both lean the same way.
   The last 20% is the settle: it overshoots its resting place slightly and drops back,
   which is what a card does when it stops sliding on cloth. */
.ct-card.dealt { opacity: 0; }
.card-table.dealt .ct-card.dealt {
  animation: ct-deal 0.3s cubic-bezier(0.34, 0.02, 0.28, 1) var(--d, 0ms) both;
}
/* Once the round is down, the animation is retired and the resting pose is plain CSS.
   An animation with fill:both never stops asserting its last keyframe, and an animation
   beats every static declaration, so leaving it in place would freeze each card where the
   deal left it and no later rule could ever move it. */
.card-table.laid .ct-card.dealt { animation: none; opacity: 1; transform: none; }
/* A thrown card carries an inline transform that must not be overridden by the rule above. */
.ct-seat.played .ct-card.dealt { transform: none; }
@keyframes ct-deal {
  0%   {
    opacity: 0;
    transform: translate(var(--fx, 0px), var(--fy, 0px)) scale(0.82)
      rotate(calc(var(--lean, 1) * -10deg));
  }
  16%  { opacity: 1; }
  /* Barely off the felt: it is being carried, not thrown. */
  62%  {
    transform: translate(calc(var(--fx, 0px) * 0.3), calc(var(--fy, 0px) * 0.3 - 5px))
      scale(0.95) rotate(calc(var(--lean, 1) * -4deg));
  }
  100% { opacity: 1; transform: translate(0, 0) scale(1) rotate(0deg); }
}

/* ---- the viewer's own seat: six o'clock, on the felt, largest ---- */
/* Placed on the same ring as everyone else rather than pinned to the bottom of the stage.
   Pinning put it below the felt entirely, so the one card the player owns was the one card
   not on the table. */
.ct-seat.mine {
  left: 50%; right: auto; bottom: auto;
  top: calc(var(--mid) + var(--ry) * 0.88);
  transform: translate(-50%, -50%); z-index: 30;
  /* The seat's own transform flattens anything above it, so the card's approach towards
     the viewer has to be given its depth here or translateZ does nothing at all. */
  perspective: 760px;
}
/* At rest your card lies on the felt on the same plane as everyone else's. Picking it up
   is what brings it upright: the difference between lying down and being held is the
   animation, so the resting pose has to be honestly flat. */
/* At rest your card is one of the cards on the table: same width, same tilt, and the same
   near-side scale the closest seats get, because it is sitting where the closest seat sits.
   The size difference is earned by picking it up, not granted for being yours. */
.ct-seat.mine { --s: 0.95; }
.ct-seat.mine .ct-slot {
  width: var(--cw);
  transform: rotateX(18deg) scale(var(--s));
  perspective: 900px;
  transition: transform 0.5s cubic-bezier(0.24, 0.86, 0.28, 1), filter 0.4s ease;
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.6));
}
/* But it is the one card you may touch, so it is lit. The slow pulse is the invitation.
   A played card gets neither: .played has no glow and no cursor, because the invitation
   should stop the moment the card can no longer be picked up. */
.ct-seat.mine.live .ct-slot { animation: ct-yours 2.8s ease-in-out infinite; }
/* A played card lies face up in the middle: that is the whole point of playing it, and
   the pile has to be readable by everyone. */
.ct-seat.mine.played { cursor: default; }
.ct-seat.mine.played .ct-face.back { opacity: 0; }
.ct-seat.mine.played .ct-face.front { opacity: 1; }
/* Anyone's played card, once it has passed edge-on. */
.ct-seat.face-up .ct-face.back { opacity: 0; }
.ct-seat.face-up .ct-face.front { opacity: 1; }
/* A card in the middle belongs to the pile, not to the seat it came from, so it is lifted
   clear of every seat's stacking order and lands on top of whatever is already there. */
.ct-seat.played { z-index: 25; }
.ct-seat.played .ct-slot::after { opacity: 0; }
.ct-seat.mine.played .ct-slot {
  animation: none;
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.6));
}
@keyframes ct-yours {
  0%, 100% { filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.6)) drop-shadow(0 0 6px rgba(var(--gold), 0.42)); }
  50%      { filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.6)) drop-shadow(0 0 13px rgba(var(--gold), 0.78)); }
}
/* Picked up: off the table, square to your eye, and lit. Fires for the dealt reveal and
   for every hold afterwards, so looking at your card is one gesture with one look. */
/* Picked up: it leaves the table, comes up square to your eye and gets a lot bigger,
   because it is now a hand's length from your face rather than lying at arm's reach. The
   growth is the distance; without it the turn reads as a card spinning on the felt. */
.card-table.flipped .ct-seat.mine .ct-slot,
.ct-seat.mine.showing .ct-slot {
  animation: none;
  /* Forwards and down, not up: the card is coming towards the viewer, and towards is down
     the screen as well as bigger. */
  transform: rotateX(0deg) translateY(20px) translateZ(90px) scale(calc(var(--s) * var(--my)));
  filter: drop-shadow(0 28px 36px rgba(0, 0, 0, 0.72)) drop-shadow(0 0 26px rgba(var(--gold), 0.34));
}
.ct-seat.mine .ct-card.dealt { --tilt: 0deg; --d: 0ms; }

/* ---- your card, once it is yours to handle -------------------------------
   The scripted reveal is a keyframe animation; from here on the same poses are
   expressed as transitions, so the handover mid-air does not move the card.
   Hold it and it comes up face towards you; let go and it lies back down. */
.ct-seat.mine.live { cursor: grab; outline: none; touch-action: none; }
.ct-seat.mine.live .ct-slot { cursor: inherit; }
.ct-seat.mine.live:active,
.ct-seat.mine.dragging { cursor: grabbing; }
.ct-seat.mine.live .ct-card.dealt {
  /* Carries .dealt so it outranks the .card-table.laid freeze above: this card is the one
     thing on the table that still has to be able to move. */
  animation: none; opacity: 1;
  transition: transform 0.44s cubic-bezier(0.36, 0.06, 0.22, 1);
  transform: rotateY(0deg);
}
.ct-seat.mine.live.showing .ct-card.dealt { transform: rotateY(180deg); }
/* The faces swap at the midpoint of the turn, where the card is edge-on. */
.ct-seat.mine.live .ct-face.back { opacity: 1; transition: opacity 0s linear 0.22s; }
.ct-seat.mine.live .ct-face.front { opacity: 0; transition: opacity 0s linear 0.22s; }
.ct-seat.mine.live.showing .ct-face.back { opacity: 0; }
.ct-seat.mine.live.showing .ct-face.front { opacity: 1; }
/* A card being dragged must track the pointer exactly, so nothing may ease it. */
.ct-seat.mine.dragging .ct-card.dealt { transition: none; }
.ct-seat.mine.dragging .ct-slot { transition: none; }
/* Far enough to count as a throw: the card lights up so the player knows letting go
   now will play it, rather than finding out afterwards. */
.ct-seat.mine.armed .ct-slot {
  filter: drop-shadow(0 24px 30px rgba(0, 0, 0, 0.6)) drop-shadow(0 0 26px rgba(var(--gold), 0.65));
}
.ct-seat.mine.live:focus-visible .ct-slot {
  filter: drop-shadow(0 16px 22px rgba(0, 0, 0, 0.6)) drop-shadow(0 0 0 3px rgba(var(--gold), 0.9));
}

/* The reveal: an honest turn through edge-on. The card rotates the full 180 degrees and
   the two faces are swapped at the halfway point, where the card is side-on and neither
   is legible anyway. Swapping by opacity rather than relying on backface-visibility is
   deliberate: a backface flip renders the wrong side the moment any ancestor flattens
   the 3D context, and several of these ancestors do. */
.card-table.flipped .ct-seat.mine .ct-card {
  animation: ct-turn3d 0.78s cubic-bezier(0.36, 0.06, 0.22, 1) both;
}
.card-table.flipped .ct-seat.mine .ct-face.back { animation: ct-hide 0.78s steps(1, end) both; }
.card-table.flipped .ct-seat.mine .ct-face.front { animation: ct-show 0.78s steps(1, end) both; }
/* Every frame restates opacity. This animation replaces ct-deal on the same element, so
   without it the card falls back to the .ct-card.dealt base of opacity 0 and disappears
   at the exact moment it is meant to be revealed. */
/* Turned to the right, which is the way a hand actually turns a card over: the right edge
   goes back and the left comes forward, rather than the other way about. */
@keyframes ct-turn3d {
  0%   { opacity: 1; transform: rotateY(0deg) translateZ(0) translateY(0) rotateZ(0deg); }
  20%  { opacity: 1; transform: rotateY(18deg) translateZ(26px) translateY(-17px) rotateZ(3.5deg); }
  50%  { opacity: 1; transform: rotateY(90deg) translateZ(36px) translateY(-23px) rotateZ(0deg); }
  80%  { opacity: 1; transform: rotateY(162deg) translateZ(26px) translateY(-15px) rotateZ(-3.5deg); }
  100% { opacity: 1; transform: rotateY(180deg) translateZ(0) translateY(0) rotateZ(0deg); }
}
@keyframes ct-hide { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
@keyframes ct-show { 0%, 49% { opacity: 0; } 50%, 100% { opacity: 1; } }

/* ---- the people ----------------------------------------------------------
   A head over a pair of shoulders. Two CSS shapes, not a drawing: at this size a
   silhouette only has to read as a person, and a circle sitting on a rounded slab
   does that at every scale the table asks for. */
/* Ring seats read outward: name, then player, then their card nearer the middle. Your own
   seat is the other way up, because you are on the near edge looking in. */
.ct-who {
  display: flex; flex-direction: column-reverse; align-items: center; gap: 3px;
  font-size: 13px; white-space: nowrap;
}
.ct-who.mine { flex-direction: column; font-size: 15px; }
.ct-body {
  position: relative;
  width: calc(var(--cw) * 0.9 * var(--s, 1)); height: calc(var(--cw) * 0.34 * var(--s, 1));
  margin-top: calc(var(--cw) * 0.42 * var(--s, 1));
  border-radius: 999px 999px calc(var(--cw) * 0.1) calc(var(--cw) * 0.1);
  background: linear-gradient(168deg, #2b3363 0%, #1b2148 62%, #161b3c 100%);
  border: 1px solid rgba(var(--gold), 0.26);
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
}
.ct-head {
  position: absolute; left: 50%; bottom: 58%;
  transform: translateX(-50%);
  width: calc(var(--cw) * 0.52 * var(--s, 1)); height: calc(var(--cw) * 0.52 * var(--s, 1));
  border-radius: 50%;
  background: linear-gradient(160deg, #333c72 0%, #1e2550 100%);
  border: 1px solid rgba(var(--gold), 0.34);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
/* Name and avatar together, always upright and always on the outside of the table, where
   they can be read whichever way the player is turned. */
.ct-who b {
  display: flex; align-items: center; gap: 3px;
  font-weight: 700; color: var(--ink-dim); letter-spacing: 0.2px;
  max-width: calc(var(--cw) * 2.4); overflow: hidden; text-overflow: ellipsis;
}
.ct-emoji { font-size: 1.15em; line-height: 1; }

/* Whose turn it is. The arrow sits outside the chair pointing in at the player, so it never
   lands on a card; and the player it belongs to sees it over themselves, which is how
   everyone at the table ends up looking at the same person. */
.ct-arrow {
  position: absolute; left: 50%; bottom: calc(100% + 5px);
  width: 0; height: 0;
  border-left: 11px solid transparent;
  border-right: 11px solid transparent;
  border-top: 15px solid rgb(var(--gold));
  transform: translateX(-50%);
  opacity: 0; transition: opacity 0.25s ease;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.7));
}
/* Your own chair is the other way up, so the arrow points up at you from below. */
.ct-who.mine .ct-arrow {
  bottom: auto; top: calc(100% + 5px);
  border-top: none; border-bottom: 15px solid rgb(var(--gold));
}
.ct-who.turn .ct-arrow { opacity: 1; animation: ct-nudge 1.15s ease-in-out infinite; }
@keyframes ct-nudge {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%      { transform: translateX(-50%) translateY(3px); }
}
.ct-who.mine.turn .ct-arrow { animation-name: ct-nudge-up; }
@keyframes ct-nudge-up {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%      { transform: translateX(-50%) translateY(-3px); }
}
.ct-who.turn .ct-head {
  border-color: rgb(var(--gold));
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.55), 0 0 12px rgba(var(--gold), 0.6);
}
.ct-who.turn .ct-body { border-color: rgba(var(--gold), 0.6); }
.ct-who.turn b { color: rgb(var(--gold)); }
.ct-who.mine { transition: opacity 0.25s ease; }
/* Your card fills this space once it is up, so the label gets out of its way. */

.ct-who.mine b { color: rgb(var(--gold)); letter-spacing: 1px; }
/* You are the nearest chair, so your own figure is the largest at the table. */
.ct-who.mine .ct-head { border-color: rgba(var(--gold), 0.55); }

/* Its own row under the stage, so it can never sit on top of a card. */
.ct-caption {
  text-align: center; min-height: 1.3em; margin-top: 26px;
  font-size: 13.5px; color: var(--ink-dim); font-weight: 700; letter-spacing: 0.3px;
}

/* ---------------------------------------------------------------------------
   HOLD TO LOOK
   A card you shield with your hand: it lifts and turns over while pressed and
   goes face down the moment you let go, so a card is only ever readable while
   a finger is on it.
   --------------------------------------------------------------------------- */
.peek-card {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  background: none; border: 0; padding: 6px 0 0; cursor: pointer;
  --cw: 78px; --gold: 212, 175, 55;
  touch-action: none;                /* holding the card must not scroll the page */
  -webkit-user-select: none; user-select: none;
}
@media (min-width: 600px) { .peek-card { --cw: 96px; } }
.peek-card .ct-slot {
  width: calc(var(--cw) * 1.4); transform: rotateX(-6deg);
  perspective: 800px;
  transition: transform 0.3s cubic-bezier(0.3, 0.9, 0.3, 1);
  filter: drop-shadow(0 14px 20px rgba(0, 0, 0, 0.55));
}
.peek-card .ct-card {
  transition: transform 0.44s cubic-bezier(0.36, 0.06, 0.22, 1);
  transform: rotateY(0deg);
}
.peek-card .ct-face.back { opacity: 1; transition: opacity 0s linear 0.22s; }
.peek-card .ct-face.front { transition: opacity 0s linear 0.22s; }
.peek-card .pk-mark { font-size: 52cqi; color: rgba(var(--gold), 0.5); }
.peek-card.showing .ct-slot { transform: rotateX(-6deg) scale(1.06); }
.peek-card.showing .ct-card { transform: rotateY(180deg) translateY(-8px); }
.peek-card.showing .ct-face.back { opacity: 0; }
.peek-card.showing .ct-face.front { opacity: 1; }
.pk-hint {
  font-size: 12.5px; font-weight: 800; letter-spacing: 0.6px;
  color: var(--ink-dim); text-transform: uppercase;
}
.peek-card.showing .pk-hint { color: rgb(var(--gold)); text-transform: none; font-size: 14px; }

/* Reduced motion: everything is simply already dealt and turned over, and the hold-to-look
   card still works, it just does not animate on the way. */
@media (prefers-reduced-motion: reduce) {
  .card-table .ct-deck .ct-card,
  .card-table.dealt .ct-card.dealt,
  .card-table.flipped .ct-seat.mine .ct-card,
  .card-table.flipped .ct-seat.mine .ct-face { animation: none; }
  .card-table.dealt .ct-card.dealt { opacity: 1; }
  .card-table.dealt .ct-who,
  .card-table.still .ct-who { opacity: 1; }
  .card-table.flipped .ct-seat.mine .ct-face.back { opacity: 0; }
  .card-table.flipped .ct-seat.mine .ct-face.front { opacity: 1; transform: none; }
  .ct-seat.mine .ct-slot, .ct-seat.mine .ct-card { transition: none; }
  .ct-seat.mine.live .ct-slot { animation: none; }
  .ct-who.turn .ct-arrow { animation: none; }
  .ct-seat.mine.live.showing .ct-card.dealt { transform: none; }
  .peek-card .ct-slot, .peek-card .ct-card { transition: none; }
  .peek-card.showing .ct-card { transform: none; }
  .peek-card .ct-face.front,
  .ct-seat.mine.live.showing .ct-face.front { transform: none; }
}

/* ---------- Island Rules: the boat's self-audit ---------- */
.audit-box { margin-top: 4px; }
.audit-note { border: 1px solid rgba(52, 211, 153, 0.35); font-size: 14px; }
.audit-note.warn { border-color: rgba(251, 191, 36, 0.5); }

/* A reaction floats up out of whoever sent it, so the table sees the joke's author. */
.react-float {
  position: absolute; left: 50%; top: -4px;
  font-size: 26px; line-height: 1; pointer-events: none; z-index: 40;
  transform: translateX(-50%);
  animation: react-float 2.5s cubic-bezier(0.2, 0.6, 0.3, 1) forwards;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
}
@keyframes react-float {
  0%   { opacity: 0; transform: translate(-50%, 6px) scale(0.6); }
  12%  { opacity: 1; transform: translate(-50%, -6px) scale(1.15); }
  70%  { opacity: 0.9; }
  100% { opacity: 0; transform: translate(-46%, -46px) scale(1); }
}
@media (prefers-reduced-motion: reduce) { .react-float { animation: react-fade 2.5s forwards; } }
@keyframes react-fade { 0% { opacity: 1; } 100% { opacity: 0; } }

.pt-voted {
  position: absolute; top: 6px; left: 6px;
  width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(34, 197, 94, 0.22); border: 1px solid rgba(34, 197, 94, 0.6);
  color: #86efac; font-size: 11px; font-weight: 800; line-height: 1;
}

/* Reveal rows: name and word on the left, role and score on the right; the two halves
   stack on a phone so nothing is squeezed into a column of its own. */
.bi-reveal-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bi-reveal-row .rl-av { font-size: 20px; line-height: 1; }
.bi-reveal-row .rl-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.bi-reveal-row .rl-name { font-weight: 700; }
.bi-reveal-row .rl-word { font-size: 12.5px; color: var(--ink-faint); }
.bi-reveal-row .rl-tail { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; margin-left: auto; }
.bi-reveal-row .rl-pts {
  font-size: 12.5px; font-weight: 700; color: var(--ink-dim);
  padding: 3px 9px; border-radius: 999px; background: rgba(255, 255, 255, 0.06);
}
@media (max-width: 460px) {
  .bi-reveal-row .rl-tail { width: 100%; margin-left: 30px; }
}

/* The boat re-reading itself: a slow ring, because the pass runs several judgements. */
.audit-working { display: flex; align-items: center; gap: 12px; text-align: left; }
.audit-spinner {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid rgba(212, 175, 55, 0.25); border-top-color: rgba(212, 175, 55, 0.9);
  animation: audit-spin 0.9s linear infinite;
}
@keyframes audit-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .audit-spinner { animation-duration: 2.4s; } }

/* ---------- game key art ----------
   Shot on pure black, composited with a screen blend: black contributes nothing, so
   the lit figures rise off the card with no cutout and no rectangular seam. */
.gc-art {
  position: absolute; inset: 0; display: block;
  overflow: hidden; border-radius: inherit; pointer-events: none; z-index: 0;
}
.gc-art img {
  position: absolute; right: -6%; bottom: 0; width: 86%; height: 100%;
  object-fit: cover; object-position: right center;
  mix-blend-mode: screen; opacity: 0.85;
  transition: opacity 0.3s ease, transform 0.45s cubic-bezier(0.22, 0.8, 0.26, 1);
}
.game-card:hover .gc-art img { opacity: 1; transform: scale(1.04); }
/* The copy gets its own ground so a word never lands on a face. */
.gc-art::after {
  content: ''; position: absolute; inset: 0;
  background:
    linear-gradient(94deg, var(--bg) 0%, var(--bg) 40%, rgba(11, 14, 26, 0.84) 58%, rgba(11, 14, 26, 0.28) 82%, transparent 100%),
    linear-gradient(0deg, rgba(11, 14, 26, 0.7) 0%, transparent 45%);
}
.game-card > :not(.gc-art) { position: relative; z-index: 1; }

/* The rules sheet leads with the same art as a wide banner. */
.rules-art {
  position: relative; margin: 12px 0 4px;
  height: clamp(110px, 22vw, 172px);
  border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--stroke);
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.05), transparent 60%), var(--bg);
}
.rules-art:empty { display: none; }
.rules-art img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: right 38%;
  mix-blend-mode: screen; opacity: 0.9;
}
.rules-art::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(0deg, rgba(11, 14, 26, 0.85) 0%, transparent 55%);
}
@media (prefers-reduced-motion: reduce) {
  .game-card:hover .gc-art img { transform: none; }
}

/* ---------- shared scene art ----------
   Same screen blend as the game cards. `side` keeps the left clear for copy;
   `band` sits behind a headline and fades out downward. */
.scene-art {
  position: absolute; inset: 0; display: block;
  overflow: hidden; border-radius: inherit; pointer-events: none; z-index: 0;
}
.scene-art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; mix-blend-mode: screen; }
.scene-art::after { content: ''; position: absolute; inset: 0; }

.scene-side img { right: -6%; left: auto; width: 86%; object-position: right center; opacity: 0.85; }
.scene-side::after {
  background:
    linear-gradient(94deg, var(--bg) 0%, var(--bg) 40%, rgba(11, 14, 26, 0.84) 58%, rgba(11, 14, 26, 0.28) 82%, transparent 100%),
    linear-gradient(0deg, rgba(11, 14, 26, 0.7) 0%, transparent 45%);
}
.scene-band img { object-position: center 34%; opacity: 0.8; }
.scene-band::after {
  background: linear-gradient(0deg, var(--bg) 4%, rgba(11, 14, 26, 0.86) 34%, rgba(11, 14, 26, 0.42) 66%, rgba(11, 14, 26, 0.2) 100%);
}
.has-art { position: relative; overflow: hidden; }
.has-art > :not(.scene-art) { position: relative; z-index: 1; }

/* A live screen keeps its art well back: the cards and the grid are the thing being
   read, and a photograph behind them competes for the same eyes. */
.art-faint .scene-art img { opacity: 0.42; }
.art-faint .scene-art::after {
  background: linear-gradient(94deg, var(--bg) 0%, rgba(11, 14, 26, 0.92) 46%, rgba(11, 14, 26, 0.72) 74%, rgba(11, 14, 26, 0.5) 100%);
}

/* The waiting note carries its own art: it is the single most-seen idle state in the
   app, shown to every guest in every game while the owner decides something. */
.waiting-wrap { position: relative; overflow: hidden; border-radius: var(--r-md); padding: 14px 12px; }
.waiting-wrap .waiting-note { margin: 0; }

/* Reveal and verdict heroes need room for the art band behind them. */
.reveal-pop.has-art, .sl-verdict-hero.has-art { position: relative; overflow: hidden; border-radius: var(--r-md); padding: 16px 14px; }

/* The packing list's two columns each carry their own side of the boat's rail, so the
   rule reads as a boundary even before you work out what it is. */
.pack-col.has-art { position: relative; overflow: hidden; }
.pack-col.has-art > :not(.scene-art) { position: relative; z-index: 1; }
.pack-col .scene-art img { opacity: 0.3; }
