/* ============================================================
   GameRoom — design system
   Dark green + cream + amber. iOS-native feel, offline-safe.
   ============================================================ */

:root {
  /* Color tokens */
  --bg: #0B1710;
  --surface: rgba(255,255,255,.06);
  --surface-2: rgba(255,255,255,.10);
  --text: #F2EAD8;
  --text-dim: rgba(242,234,216,.62);
  --accent: #F6B049;
  --accent-deep: #E08E24;
  --danger: #E25C48;
  --success: #6FCB77;

  /* Shape */
  --radius-s: 10px;
  --radius-m: 16px;
  --radius-l: 22px;

  /* Spacing scale */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  /* Card language: hairline inset border + layered depth */
  --card-border: inset 0 0 0 1px rgba(255,255,255,.07);
  --card-shadow: inset 0 0 0 1px rgba(255,255,255,.07),
                 0 1px 2px rgba(0,0,0,.25),
                 0 8px 24px rgba(0,0,0,.25);

  /* Type scale: 34 / 26 / 20 / 17 / 15 / 13 / 11 */
  --fs-xxl: 34px;
  --fs-xl: 26px;
  --fs-l: 20px;
  --fs-m: 17px;
  --fs-s: 15px;
  --fs-xs: 13px;
  --fs-xxs: 11px;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/InterVariable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html { background: var(--bg); }
body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-optical-sizing: auto;
  background: var(--bg);
  color: var(--text);
  font-size: var(--fs-m);
  font-weight: 400;
  line-height: 1.35;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  -webkit-font-smoothing: antialiased;
}
/* Decorative glow pinned to the viewport — putting it on body stretches it over
   tall pages (visible banding) and exposes white in iOS overscroll. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(900px 520px at 82% 108%, rgba(246,176,73,.05), transparent 62%),
    radial-gradient(1200px 800px at 50% -10%, #16301F, var(--bg) 60%);
}
/* Gameplay locks the page so drags never scroll; every other screen scrolls freely.
   `overflow:hidden` on <body> alone does NOT stop iOS Safari scrolling — the scroller
   is <html>. Without this, a tap mid-game could scroll the document and shove the
   canvas half off the top of the screen. Pinning body to the visual viewport means
   there is physically nothing to scroll. */
html.lock { overflow: hidden; height: 100%; }
body.lock {
  overflow: hidden;
  position: fixed; inset: 0; width: 100%; height: 100%;
  overscroll-behavior: none;
}
body.lock .screen { min-height: 0; height: 100%; }

.screen {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: var(--sp-5) 20px calc(96px + env(safe-area-inset-bottom));
}
.screen > * { flex-shrink: 0; }
.hidden { display: none !important; }
.screen.enter { animation: screenIn .26s cubic-bezier(.2,.8,.3,1); }
@keyframes screenIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* ---------- Typography ---------- */
.logo {
  font-size: 56px;
  filter: drop-shadow(0 6px 18px rgba(246,176,73,.22));
}
h1 {
  font-size: var(--fs-xxl); font-weight: 800; letter-spacing: -0.02em;
  line-height: 1.1;
}
h2 { font-size: var(--fs-xl); font-weight: 800; letter-spacing: -0.02em; line-height: 1.15; }
h3 {
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-dim); margin-top: var(--sp-3);
}
.tagline { color: var(--text-dim); font-size: var(--fs-s); text-align: center; }

.stack { display: flex; flex-direction: column; gap: 10px; width: min(320px, 86vw); margin-top: var(--sp-2); }

/* ---------- Buttons ---------- */
.btn {
  border: 0; border-radius: var(--radius-m);
  min-height: 48px; padding: 13px 18px;
  font-family: inherit;
  font-size: var(--fs-m); font-weight: 700; letter-spacing: -0.01em;
  cursor: pointer;
  transition: transform .07s ease, box-shadow .07s ease, filter .15s, background .15s;
}
.btn:active { transform: scale(.97); }
.btn.primary {
  background: linear-gradient(180deg, #FFC463, var(--accent) 38%, var(--accent-deep));
  color: #3C2504;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.35),
    0 4px 0 #A5620D,
    0 8px 18px rgba(0,0,0,.35);
  text-shadow: 0 1px 0 rgba(255,255,255,.25);
}
.btn.primary:active {
  transform: translateY(3px) scale(.99);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.25),
    0 1px 0 #A5620D,
    0 3px 8px rgba(0,0,0,.3);
}
.btn.ghost {
  background: var(--surface);
  box-shadow: var(--card-border);
  color: var(--text);
}
.btn.ghost:active { background: var(--surface-2); }
.btn.busy { pointer-events: none; filter: brightness(.85); }
.btn[disabled] { pointer-events: none; opacity: .5; }
.btn.busy::after {
  content: ''; display: inline-block; width: 15px; height: 15px; margin-left: 10px;
  border: 2.5px solid currentColor; border-top-color: transparent; border-radius: 50%;
  vertical-align: -2px; animation: spin .6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.me-line { font-size: var(--fs-xs); color: var(--text-dim); margin-top: 10px; }
.me-line a { color: var(--accent); text-decoration: none; }

/* ---------- Game catalog cards ---------- */
.game-card {
  width: min(340px, 90vw);
  background: var(--surface);
  border-radius: var(--radius-l);
  box-shadow: var(--card-shadow);
  padding: var(--sp-4);
  display: flex; flex-direction: column; gap: var(--sp-4);
}
.game-card-head { display: flex; align-items: center; gap: var(--sp-4); }
.game-card-icon {
  font-size: 32px; line-height: 1;
  width: 60px; height: 60px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-m);
  background:
    radial-gradient(36px 36px at 50% 42%, rgba(246,176,73,.28), transparent 72%),
    rgba(255,255,255,.05);
  box-shadow: var(--card-border);
}
.game-card-title { font-size: var(--fs-l); font-weight: 800; letter-spacing: -0.02em; }
.game-card-sub { font-size: var(--fs-xs); color: var(--text-dim); margin-top: 2px; }
.game-card-actions { display: flex; gap: 10px; }
.game-card-actions .btn { flex: 1; min-height: 48px; padding: 12px; font-size: var(--fs-s); }

.modal-sub { font-size: var(--fs-xs); margin: -6px 0 2px; }
.auth-error { color: var(--danger); font-size: var(--fs-xs); font-weight: 600; }
#recaptcha-container { position: fixed; bottom: 0; left: 0; }

/* ---------- Word Rush HUD ---------- */
.hud {
  display: flex; align-items: center; justify-content: space-between;
  width: min(440px, 94vw);
  padding: 10px 14px;
  border-radius: var(--radius-m);
  background: rgba(11,23,16,.45);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  box-shadow: var(--card-border);
}
.hud-score { font-size: var(--fs-xxl); font-weight: 800; letter-spacing: -0.02em; min-width: 90px; font-variant-numeric: tabular-nums; }
.hud-count { font-size: var(--fs-xs); color: var(--text-dim); font-weight: 600; min-width: 90px; text-align: right; }
.hud-timer { position: relative; flex: 1; height: 10px; margin: 0 14px; background: rgba(255,255,255,.12); border-radius: 5px; overflow: visible; }
#timer-bar { height: 100%; width: 100%; border-radius: 5px; background: linear-gradient(90deg, var(--success), var(--accent)); transition: width .2s linear; }
#timer-num { position: absolute; top: 14px; right: 0; font-size: var(--fs-xxs); color: var(--text-dim); font-variant-numeric: tabular-nums; }

.word-pill {
  /* Fixed height: empty vs. filled must compute identically or the board shifts 1px on first letter */
  height: 46px; min-width: 120px; padding: 0 22px; line-height: 1;
  border-radius: 23px; font-size: 24px; font-weight: 800; letter-spacing: 2px;
  background: var(--surface);
  box-shadow: var(--card-border);
  display: flex; align-items: center; justify-content: center;
  text-transform: uppercase; transition: background .12s, color .12s, transform .12s, box-shadow .12s;
}
.word-pill.valid { background: #3F9D43; color: #fff; box-shadow: inset 0 0 0 1px rgba(255,255,255,.14), 0 4px 16px rgba(63,157,67,.35); }
.word-pill.dup { background: #C9A63C; color: #fff; box-shadow: inset 0 0 0 1px rgba(255,255,255,.14); }
.word-pill.pop { animation: pop .35s ease; }
@keyframes pop { 30% { transform: scale(1.15); } 100% { transform: scale(1); } }

/* ---------- Board (layout is load-bearing: square wrap, absolute tiles) ---------- */
#board-wrap { position: relative; width: min(92vw, 440px, 62dvh); aspect-ratio: 1; }
#board { position: absolute; inset: 0; }
#trail { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; }
.tile {
  position: absolute; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, rgba(255,246,222,.28), rgba(122,86,32,.24)), url(/img/tile_wood.png) center/cover, #E8CF9D;
  color: #4A3312; font-weight: 800; border-radius: 16%;
  text-shadow: 0 1px 0 rgba(255,255,255,.35);
  box-shadow:
    0 3px 0 rgba(90,60,10,.45),
    0 6px 12px rgba(0,0,0,.22),
    inset 0 1px 0 rgba(255,255,255,.5),
    inset 0 -2px 4px rgba(90,60,10,.25);
  transition: background .1s, transform .12s, color .1s, box-shadow .12s;
  text-transform: uppercase;
}
/* Selection: grow via transform only (never affects layout); yellow while forming
   (or already-found word), green only for a new valid word */
.tile.sel {
  background: linear-gradient(180deg, rgba(240,210,104,.62), rgba(217,171,53,.68)), url(/img/tile_wood.png) center/cover, #D9AB35;
  color: #4A3312; transform: scale(1.12); z-index: 3;
  box-shadow:
    0 5px 0 rgba(90,60,10,.4),
    0 10px 18px rgba(0,0,0,.32),
    inset 0 1px 0 rgba(255,255,255,.5);
}
.tile.sel.valid {
  background: linear-gradient(180deg, rgba(98,201,104,.68), rgba(63,157,67,.74)), url(/img/tile_wood.png) center/cover, #3F9D43;
  color: #fff; text-shadow: 0 1px 2px rgba(0,60,10,.45);
}
.trail-line { fill: none; stroke: rgba(255,255,255,.65); stroke-width: 10; stroke-linecap: round; stroke-linejoin: round; }

.float-score {
  position: absolute; font-size: 26px; font-weight: 800; color: var(--success); z-index: 5;
  animation: floatUp .8s ease forwards; pointer-events: none; text-shadow: 0 2px 6px rgba(0,0,0,.5);
}
@keyframes floatUp { 0% { opacity: 0; transform: translateY(6px);} 20% {opacity: 1;} 100% { opacity: 0; transform: translateY(-46px); } }

.countdown {
  position: fixed; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 110px; font-weight: 900; letter-spacing: -0.02em;
  background: rgba(8,15,10,.82);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  z-index: 20;
}

/* ---------- Mini board preview ---------- */
.mini-board { display: grid; gap: 6px; width: min(60vw, 260px); aspect-ratio: 1; }
.mini-board .mini-tile {
  background: rgba(232,207,157,.16); border-radius: 16%;
  box-shadow: inset 0 0 0 1px rgba(242,234,216,.08);
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: var(--fs-l); color: var(--text); text-transform: uppercase;
}
.mini-board.row { display: flex; width: auto; aspect-ratio: auto; gap: 10px; }
.hole-pill {
  background: var(--surface); padding: 11px var(--sp-3); border-radius: var(--radius-m);
  box-shadow: var(--card-shadow);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: -0.005em;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  text-align: center;
}
.hole-pill span {
  font-size: 10px; color: var(--text-dim); font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}
.mini-board.hero {
  display: block; width: min(80vw, 310px); aspect-ratio: 3/2;
  background: var(--surface); border-radius: var(--radius-l);
  box-shadow: var(--card-shadow);
  padding: var(--sp-2);
}
.mini-board.hero canvas { width: 100%; height: 100%; display: block; border-radius: calc(var(--radius-l) - var(--sp-2)); }

/* ---------- Results ----------
   Rebuilt around one question: did I win, and by how much. The verdict card
   answers it before you read anything else. The standings below are a real
   table (fixed rank / score / delta columns) so the numbers you are comparing
   share a right edge instead of being pushed around by variable-width names
   and suffixes. */
#screen-results { gap: var(--sp-3); padding-top: 0; }

/* ---- Post-game hero ----
   The game's key art caps the screen (the fixed speaker button floats over it
   like the sheet's close button does), a scrim carries it down into the
   verdict block, and the tone of the outcome — mint, rose, amber — lives in
   the eyebrow, the numeral and one hairline, never in a wall of colour. */
.res-hero {
  position: relative; width: 100vw; max-width: 100vw; overflow: hidden; isolation: isolate;
  min-height: 300px; display: flex; flex-direction: column; justify-content: flex-end;
  padding: calc(56px + env(safe-area-inset-top, 0px)) 0 0;
  border-radius: 0 0 28px 28px; background: linear-gradient(158deg, #255138, #0d2016 62%);
  animation: heroIn .5s cubic-bezier(.2,.8,.3,1) both;
}
@keyframes heroIn { from { opacity: 0; transform: translateY(-10px); } }
.res-art {
  position: absolute; inset: 0; background-size: cover; background-position: center 38%;
  animation: artIn 1.2s cubic-bezier(.2,.8,.3,1) both;
}
.res-art-scrim {
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(180deg, rgba(11,23,16,.18) 0%, rgba(11,23,16,0) 22%,
                                     rgba(11,23,16,.78) 58%, var(--bg) 100%);
}
.res-hero .res-verdict {
  position: relative; z-index: 3; width: 100%; box-shadow: none; background: none;
  padding: 0 var(--sp-4) 18px; border-radius: 0;
}
.res-hero .vd-eyebrow { color: var(--text-dim); text-shadow: 0 1px 6px rgba(0,0,0,.5); }
.vd-title {
  font-family: 'Bricolage', 'Inter', -apple-system, sans-serif;
  font-size: 34px; font-weight: 800; letter-spacing: -0.035em; line-height: 1.02;
  margin: 6px 0 0; text-shadow: 0 2px 12px rgba(0,0,0,.5);
  animation: dmIn .4s cubic-bezier(.2,.8,.3,1) both .12s;
}
.res-hero .vd-value {
  margin-top: 8px; font-size: 62px; text-shadow: 0 2px 14px rgba(0,0,0,.45);
  position: relative; animation: dmIn .4s cubic-bezier(.2,.8,.3,1) both .2s;
}
.res-hero .vd-value i { color: var(--text-dim); }
.res-hero .vd-margin {
  border-top: 1px solid rgba(255,255,255,.10); margin-top: 10px; padding-top: 10px;
  font-weight: 600; color: var(--text-dim); animation: dmIn .4s cubic-bezier(.2,.8,.3,1) both .28s;
}
.res-hero.is-win .vd-eyebrow, .res-hero.is-win .vd-num { color: var(--success); }
.res-hero.is-win .vd-margin { border-top-color: rgba(111,203,119,.35); color: var(--text); }
.res-hero.is-loss .vd-eyebrow { color: #E0787C; }
.res-hero.is-loss .vd-margin { border-top-color: rgba(224,120,124,.32); }
.res-hero.is-tie .vd-eyebrow, .res-hero.is-tie .vd-num, .res-hero.is-lead .vd-num { color: var(--accent); }
.res-hero.is-solo .vd-num { color: var(--accent); }
/* One flourish, on a win only: two rings drawn in the app's own line, bursting
   from the numeral and gone in under a second. */
.res-hero.is-win .vd-value::before, .res-hero.is-win .vd-value::after {
  content: ''; position: absolute; left: 34px; top: 36px; width: 26px; height: 26px;
  border-radius: 50%; border: 2px solid var(--success); opacity: 0; pointer-events: none;
  animation: burst .9s cubic-bezier(.2,.8,.3,1) .35s both;
}
.res-hero.is-win .vd-value::after { border-color: var(--accent); border-style: dashed; animation-delay: .5s; }
@keyframes burst { 0% { transform: scale(.4); opacity: .9; } 100% { transform: scale(5.5); opacity: 0; } }

.res-actions {
  width: min(380px, 94vw); display: grid; grid-template-columns: 1fr; gap: 10px;
  margin-top: calc(-1 * var(--sp-1)); animation: dmIn .4s cubic-bezier(.2,.8,.3,1) both .34s;
}
.res-actions .btn { width: 100%; margin: 0; }

/* Two players: a scoreline. Faces facing, scores between, the better one lit. */
.res-duel {
  width: min(380px, 94vw); display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px;
  padding: 14px 12px; background: var(--surface); border-radius: var(--radius-l); box-shadow: var(--card-shadow);
  animation: dmIn .4s cubic-bezier(.2,.8,.3,1) both .4s;
}
.duel-side { display: flex; flex-direction: column; align-items: center; gap: 7px; min-width: 0; }
.duel-side[data-player] { cursor: pointer; }
.duel-side b { font-size: var(--fs-xs); font-weight: 800; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.av-duel { width: 52px; height: 52px; font-size: 20px; box-shadow: 0 0 0 2px var(--bg), 0 0 0 3.5px rgba(255,255,255,.10); }
.duel-score { display: flex; align-items: baseline; gap: 10px; font-variant-numeric: tabular-nums; }
.duel-score span { font-size: 30px; font-weight: 800; letter-spacing: -0.03em; color: var(--text-dim); }
.duel-score span i { font-style: normal; font-size: var(--fs-xs); font-weight: 700; margin-left: 3px; }
.duel-score span.is-win { color: var(--success); }
.duel-score span.is-tie { color: var(--accent); }
.duel-score em { font-style: normal; font-size: var(--fs-xxs); font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); opacity: .7; }

/* Golf scorecard as pips. */
.scorecard { display: flex; flex-direction: column; gap: 6px; padding: 2px 0 4px; }
.sc-head, .sc-row { display: grid; grid-template-columns: minmax(58px, 1fr) auto 40px; align-items: center; gap: 8px; }
.sc-row { padding: 6px 8px; border-radius: var(--radius-s); }
.sc-row.me { background: rgba(246,176,73,.10); box-shadow: inset 0 0 0 1px rgba(246,176,73,.22); }
.sc-head { padding: 0 8px; }
.sc-name { font-size: var(--fs-xs); font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-pips { display: flex; gap: 5px; justify-content: flex-end; }
.pip {
  width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  font-style: normal; font-size: var(--fs-xs); font-weight: 800; font-variant-numeric: tabular-nums;
  background: rgba(255,255,255,.07); box-shadow: inset 0 0 0 1px rgba(255,255,255,.08); color: var(--text);
}
.pip.under { background: rgba(111,203,119,.18); box-shadow: inset 0 0 0 1px rgba(111,203,119,.45); color: var(--success); }
.pip.over { background: rgba(224,120,124,.14); box-shadow: inset 0 0 0 1px rgba(224,120,124,.38); color: #E0787C; }
.pip.head { background: none; box-shadow: none; color: var(--text-dim); font-size: 10px; font-weight: 800; letter-spacing: .06em; flex-direction: column; line-height: 1.1; height: auto; }
.pip.head small { font-size: 9px; font-weight: 600; letter-spacing: 0; opacity: .75; }
.sc-tot { text-align: right; font-size: var(--fs-s); font-weight: 800; font-variant-numeric: tabular-nums; }
.sc-tot.dim { font-size: 10px; font-weight: 800; letter-spacing: .08em; color: var(--text-dim); text-transform: uppercase; }

.res-verdict {
  width: min(380px, 94vw);
  background: var(--surface);
  border-radius: var(--radius-l);
  box-shadow: var(--card-shadow);
  padding: 20px var(--sp-4) 18px;
  display: flex; flex-direction: column; align-items: flex-start;
  text-align: left;
}
.res-verdict.won {
  background: linear-gradient(168deg, rgba(246,176,73,.17), rgba(246,176,73,.04) 64%), var(--surface);
  box-shadow: inset 0 0 0 1px rgba(246,176,73,.30), 0 1px 2px rgba(0,0,0,.25), 0 10px 28px rgba(0,0,0,.3);
}
.vd-eyebrow {
  font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-dim);
}
.res-verdict.won .vd-eyebrow { color: var(--accent); }
/* A DNF verdict. No number to show — inventing one would be the same lie that
   keeping it out of everyone else's histogram exists to prevent — so the game's
   name takes the hero slot and the card goes quiet rather than celebratory. */
.res-verdict.is-dnf {
  box-shadow: inset 0 0 0 1px rgba(214,120,124,.26), 0 1px 2px rgba(0,0,0,.25), 0 10px 28px rgba(0,0,0,.3);
}
.res-verdict.is-dnf .vd-value { font-size: 34px; color: rgba(242,234,216,.86); }
.vd-value {
  font-size: 56px; font-weight: 800; letter-spacing: -0.035em; line-height: 1.02;
  font-variant-numeric: tabular-nums; margin-top: 5px;
}
.vd-value i {
  font-style: normal; font-size: var(--fs-l); font-weight: 700;
  letter-spacing: -0.01em; color: var(--text-dim); margin-left: 7px;
}
.vd-meta {
  font-size: var(--fs-xs); color: var(--text-dim); margin-top: 8px;
  text-align: left; line-height: 1.35;
}
.vd-margin {
  margin-top: 12px; padding-top: 11px; width: 100%;
  border-top: 1px solid rgba(255,255,255,.08);
  font-size: var(--fs-s); font-weight: 600; color: var(--text);
}

/* Section shell shared by Standings and every detail table, so the tables no
   longer float below the buttons with no heading and no container. */
.res-card {
  width: min(380px, 94vw);
  background: var(--surface);
  border-radius: var(--radius-l);
  box-shadow: var(--card-shadow);
  padding: var(--sp-3) var(--sp-3) var(--sp-2);
  display: flex; flex-direction: column; align-items: stretch;
}
.res-h {
  font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-dim);
  margin: 2px 0 6px var(--sp-1); text-align: left;
}

#leaderboard { display: flex; flex-direction: column; }
.lb-row {
  display: grid;
  grid-template-columns: 22px 30px minmax(0,1fr) auto 52px;
  align-items: center; gap: 10px;
  padding: 11px var(--sp-2);
  font-size: var(--fs-m);
  border-radius: var(--radius-s);
}
.lb-row.is-link { cursor: pointer; transition: background .12s, transform .07s; }
.lb-row.is-link:active { transform: scale(.985); background: var(--surface-2); }
.lb-row + .lb-row { box-shadow: inset 0 1px 0 rgba(255,255,255,.06); }
/* Identification, not emphasis — the amber ring used to shout louder than the
   actual winner whenever you had come second. */
.lb-row.me { background: rgba(255,255,255,.055); box-shadow: none; }
.lb-row.me + .lb-row { box-shadow: none; }
.lb-rank {
  font-size: var(--fs-s); font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--text-dim); text-align: right;
}
/* The winner's rank is a lit disc, the podium's one medal. */
.lb-row.lead .lb-rank {
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  background: linear-gradient(180deg, #FFC463, var(--accent-deep)); color: #3C2504;
  font-size: var(--fs-xs); box-shadow: inset 0 1px 0 rgba(255,255,255,.35);
}
.lb-row.lead .lb-rank .gicon { width: 14px; height: 14px; vertical-align: 0; stroke-width: 2; }
.lb-row.lead .lb-score { color: var(--accent); }
.lb-name {
  font-weight: 600; min-width: 0;
  display: flex; align-items: baseline; gap: 7px;
  overflow: hidden; white-space: nowrap;
}
.lb-name { text-overflow: ellipsis; }
.lb-row.lead .lb-name { font-weight: 700; }
.lb-you {
  font-style: normal; font-size: 9px; font-weight: 800; letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-dim);
  box-shadow: inset 0 0 0 1px rgba(242,234,216,.26); border-radius: 4px;
  padding: 2px 4px 1px; flex-shrink: 0;
}
.lb-score {
  font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -0.015em;
  text-align: right; white-space: nowrap;
}
.lb-unit {
  font-style: normal; font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-dim); margin-left: 4px;
}
.lb-delta {
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-dim);
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap;
}

/* Head to head: the running record against the people in THIS game. It is the
   reason the app exists, so it sits under the verdict, not on another tab. */
.res-h2h {
  width: min(380px, 94vw);
  background: var(--surface); border-radius: var(--radius-l);
  box-shadow: var(--card-shadow);
  padding: var(--sp-3) var(--sp-4) 14px;
  display: flex; flex-direction: column; gap: 13px;
}
.h2h-row {
  display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: baseline;
  column-gap: var(--sp-3); row-gap: 7px;
}
/* Rivals rows are now buttons that open that player's profile. The avatar takes
   a new leading column and the chevron a trailing one; the bar still spans the
   width underneath, starting after the avatar.

   The chevron must come BEFORE the bar in the markup. The bar is explicitly
   placed at grid-column 2/-1, so if the chevron follows it the auto-placement
   algorithm has already moved past that row and drops the chevron onto a third
   row of its own, under the bar. */
.h2h-row.is-link {
  grid-template-columns: 30px minmax(0,1fr) auto 9px;
  align-items: center;
  width: 100%; background: none; border: 0; padding: 0;
  color: inherit; font: inherit; text-align: left; cursor: pointer;
  transition: opacity .12s, transform .07s;
}
.h2h-row.is-link:active { transform: scale(.985); opacity: .75; }
.h2h-row.is-link .h2h-bar { grid-column: 2 / -1; }
.h2h-row.is-link .chev { margin-left: 0; }
.h2h-name {
  font-size: var(--fs-s); font-weight: 700; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.h2h-games {
  font-style: normal; font-size: var(--fs-xxs); font-weight: 600;
  color: var(--text-dim); margin-left: 7px;
}
.h2h-rec {
  font-size: var(--fs-m); font-weight: 800; font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em; white-space: nowrap;
}
.h2h-rec b { color: var(--success); font-weight: 800; }
.h2h-rec i { font-style: normal; color: var(--danger); font-weight: 800; }
.h2h-rec u { text-decoration: none; color: var(--text-dim); font-weight: 800; }
.h2h-rec em { font-style: normal; color: var(--text-dim); font-weight: 500; margin: 0 1px; opacity: .65; }
.h2h-bar {
  grid-column: 1 / -1;
  display: flex; height: 5px; border-radius: 3px; overflow: hidden;
  background: rgba(255,255,255,.07); gap: 2px;
}
.h2h-w { background: var(--success); }
.h2h-l { background: var(--danger); opacity: .85; }
.h2h-t { background: rgba(242,234,216,.32); }
.h2h-empty { flex: 1; }

/* Penalty rounds: drawn marks, so the column keeps a fixed rhythm and the
   outcomes stay in the app's own palette. */
.pk-cell { text-align: left !important; white-space: nowrap; }
.pk {
  display: inline-block; width: 11px; height: 11px; border-radius: 50%;
  margin-right: 7px; vertical-align: -1px; position: relative;
}
.pk-goal { background: var(--success); }
.pk-save { box-shadow: inset 0 0 0 1.6px rgba(242,234,216,.45); }
.pk-miss { box-shadow: inset 0 0 0 1.6px rgba(226,92,72,.7); }
.pk-miss::before, .pk-miss::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 1.6px; height: 10px;
  background: var(--danger); border-radius: 1px;
}
.pk-miss::before { transform: translate(-50%,-50%) rotate(45deg); }
.pk-miss::after  { transform: translate(-50%,-50%) rotate(-45deg); }

.tag-clean {
  font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--success);
}
#word-lists { width: min(380px, 94vw); margin-top: 14px; padding-bottom: 30px; }
.wl-section { margin-bottom: var(--sp-3); }
.wl-title { font-weight: 600; font-size: var(--fs-s); color: var(--text-dim); margin-bottom: 6px; cursor: pointer; }
.wl-words { display: flex; flex-wrap: wrap; gap: 6px; }
.wl-word {
  background: var(--surface); box-shadow: var(--card-border);
  padding: 4px 10px; border-radius: var(--radius-s);
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 1px;
}
.wl-word.big { background: rgba(63,157,67,.32); }

.lb-sub { font-size: var(--fs-xs); color: var(--text-dim); font-weight: 600; }
.golf-table { width: 100%; border-collapse: collapse; font-size: var(--fs-s); margin-top: 6px; font-variant-numeric: tabular-nums; }
.golf-table th, .golf-table td { padding: var(--sp-2) 6px; text-align: center; }
.golf-table th { color: var(--text-dim); font-size: var(--fs-xxs); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.golf-table .par-sub { font-size: 10px; opacity: .7; font-weight: 400; text-transform: none; }
.golf-table tr { border-bottom: 1px solid rgba(255,255,255,.07); }
.golf-table td:first-child { text-align: left; font-weight: 700; max-width: 90px; overflow: hidden; text-overflow: ellipsis; }
.golf-table .me-row { background: rgba(246,176,73,.12); }
.golf-table .under { color: var(--success); font-weight: 700; }
.golf-table .over { color: var(--danger); }

/* ---------- Stats ---------- */
#records, #gs-history { width: min(360px, 92vw); display: flex; flex-direction: column; gap: var(--sp-2); }
#gs-rivals {
  width: min(360px, 92vw); display: flex; flex-direction: column; gap: 13px;
  background: var(--surface); border-radius: var(--radius-l);
  box-shadow: var(--card-shadow); padding: 14px var(--sp-4);
}
#gs-rivals:empty, #gs-rivals:has(> .tagline) { background: none; box-shadow: none; padding: 0; }
.h2h-row, .hist-row, .stat-game { flex-shrink: 0; }
/* Long screens anchor to the top with a nav bar instead of centering */
.top-screen { justify-content: flex-start; padding-top: calc(16px + env(safe-area-inset-top)); }
/* right padding keeps the fixed music toggle from covering the Home button */
.screen-nav { width: min(360px, 92vw); display: flex; justify-content: space-between; padding-right: 54px; }
.nav-link {
  border: 0; background: var(--surface); box-shadow: var(--card-border);
  color: var(--accent);
  font-family: inherit;
  font-size: var(--fs-s); font-weight: 700; cursor: pointer;
  padding: 9px 14px; border-radius: var(--radius-s);
  transition: transform .07s, background .15s;
}
.nav-link:active { transform: scale(.95); background: var(--surface-2); }

.stat-game {
  display: flex; align-items: center; gap: 13px; width: 100%;
  padding: 13px var(--sp-4) 13px var(--sp-3);
  background: var(--surface); border: 0; border-radius: var(--radius-m);
  box-shadow: var(--card-shadow);
  color: inherit; font-family: inherit; text-align: left;
  cursor: pointer; transition: background .12s, transform .07s;
}
.stat-game:active { transform: scale(.985); background: var(--surface-2); }
.stat-info { flex: 1; min-width: 0; font-size: var(--fs-m); display: block; }
.stat-info b { font-weight: 700; }
.stat-info .sub {
  display: block; font-size: var(--fs-xs); color: var(--text-dim); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stat-rec {
  font-size: var(--fs-s); font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--text-dim); white-space: nowrap; flex-shrink: 0;
}
.stat-rec em { font-style: normal; font-weight: 500; opacity: .55; margin: 0 1px; }
/* Chevron drawn from a border, not a '›' glyph — the glyph sat off the optical
   centre and changed size with whatever font actually loaded. */
.chev {
  width: 7px; height: 7px; flex-shrink: 0;
  border-right: 1.8px solid rgba(242,234,216,.34);
  border-top: 1.8px solid rgba(242,234,216,.34);
  transform: rotate(45deg); margin-left: 2px;
}
.hist-row .gicon { color: var(--accent); margin-right: 7px; }
.turn-btn .gicon { vertical-align: -4px; margin-right: 8px; }
.hist-row {
  padding: 10px 14px; background: var(--surface); border-radius: var(--radius-m);
  box-shadow: var(--card-shadow);
  font-size: var(--fs-xs); cursor: pointer;
  transition: background .12s;
}
.hist-row:active { background: var(--surface-2); }
/* Faces on a feed row, overlapped the way a group avatar stack reads. Small on
   purpose: the row's job is still the scoreline. */
.hist-faces { display: flex; margin-top: 7px; }
.hist-faces .av { width: 21px; height: 21px; font-size: 10px; box-shadow: 0 0 0 2px var(--bg); }
.hist-faces .av + .av { margin-left: -6px; }
.hist-row .sub { color: var(--text-dim); font-size: var(--fs-xxs); margin-top: 2px; }
/* The history list is its own bounded query per game type, so it can land a
   beat after the totals above it. Placeholder rows keep the screen the right
   shape while it does, instead of a gap that fills in. */
.hist-row.is-loading { cursor: default; pointer-events: none; }
.hist-skel {
  height: 12px; border-radius: 4px;
  background: linear-gradient(90deg,
    rgba(255,255,255,.05) 20%, rgba(255,255,255,.12) 40%, rgba(255,255,255,.05) 60%);
  background-size: 300% 100%;
  animation: hist-shimmer 1.3s ease-in-out infinite;
}
.hist-skel + .hist-skel { width: 42%; height: 9px; margin-top: 6px; }
@keyframes hist-shimmer { from { background-position: 150% 0; } to { background-position: -150% 0; } }
@media (prefers-reduced-motion: reduce) { .hist-skel { animation: none; } }

/* ---------- Modal + toast ---------- */
.modal {
  position: fixed; inset: 0; background: rgba(4,9,6,.6);
  backdrop-filter: blur(12px) saturate(130%); -webkit-backdrop-filter: blur(12px) saturate(130%);
  display: flex; align-items: center; justify-content: center; z-index: 30;
  animation: fadeIn .18s ease-out;
}
@keyframes fadeIn { from { opacity: 0; } }
.modal-card {
  background: #1B2A1F; border-radius: var(--radius-l); padding: 28px var(--sp-5);
  width: min(340px, 88vw);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08),
              0 2px 6px rgba(0,0,0,.35),
              0 28px 70px rgba(0,0,0,.6);
  display: flex; flex-direction: column; gap: 14px; text-align: center;
  animation: cardIn .24s cubic-bezier(.2,.9,.3,1.15);
}
@keyframes cardIn { from { opacity: 0; transform: scale(.93) translateY(10px); } }
.modal-card > div { display: flex; flex-direction: column; gap: 14px; text-align: center; }
.modal-card h3 {
  font-size: var(--fs-l); font-weight: 800; letter-spacing: -0.02em;
  text-transform: none; color: var(--text); margin: 0;
}
.modal-card input {
  border: 0; border-radius: var(--radius-s); padding: 14px; font-size: 19px; text-align: center;
  font-family: inherit;
  background: rgba(255,255,255,.1); box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
  color: #fff; outline: none; width: 100%;
  letter-spacing: .5px;
}
.modal-card input:focus { box-shadow: inset 0 0 0 1px transparent, 0 0 0 2px rgba(246,176,73,.55); }
.modal-card .modal-sub { margin: 0; line-height: 1.45; }

/* ---- Bottom sheet variant: the game entry chooser ----
   Rises from the bottom edge, where the thumb already is, capped by the
   game's own key art so the sheet reads as "Golf" before a word is read. The
   rise overshoots a hair (the curve, not a bounce library) and the art
   settles from a slightly tighter crop, so opening feels like a push-in
   rather than a pop-up. Drag it back down to dismiss — the grab bar is a
   promise, and app.js keeps it. */
.modal.sheet { align-items: flex-end; }
.modal-card.sheet-card {
  width: 100%; max-width: 520px; gap: 0; text-align: left; touch-action: none;
  padding: 0 var(--sp-4) calc(18px + env(safe-area-inset-bottom, 0px));
  border-radius: 28px 28px 0 0;
  animation: sheetIn .46s cubic-bezier(.22, 1.08, .3, 1);
  transition: transform .26s cubic-bezier(.2,.8,.3,1);
}
@keyframes sheetIn { from { transform: translateY(100%); } }
.sheet-art {
  position: relative; height: 172px; overflow: hidden; isolation: isolate;
  margin: 0 calc(-1 * var(--sp-4)) 14px; border-radius: 28px 28px 0 0;
  background: linear-gradient(158deg, #255138, #0d2016 62%);
}
.sheet-art-img {
  position: absolute; inset: 0; background-size: cover; background-position: center 42%;
  animation: artIn 1.1s cubic-bezier(.2,.8,.3,1) both;
}
@keyframes artIn { from { transform: scale(1.14); } to { transform: scale(1.03); } }
.sheet-art-scrim {
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(180deg, rgba(27,42,31,.10) 0%, rgba(27,42,31,0) 30%,
                                     rgba(27,42,31,.70) 74%, #1B2A1F 100%);
}
.sheet-grab {
  position: absolute; z-index: 3; top: 9px; left: 50%; width: 38px; height: 5px; margin-left: -19px;
  border-radius: 3px; background: rgba(255,255,255,.62); box-shadow: 0 1px 2px rgba(0,0,0,.45);
}
.sheet-x {
  position: absolute; z-index: 3; top: 12px; right: 12px; width: 36px; height: 36px;
  border: 0; border-radius: 50%; display: grid; place-items: center; cursor: pointer;
  color: var(--text); background: rgba(11,23,16,.5);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.14);
  -webkit-tap-highlight-color: transparent; transition: transform .1s;
}
.sheet-x:active { transform: scale(.9); }
.sheet-title { position: absolute; z-index: 3; left: var(--sp-4); right: 60px; bottom: 12px; text-align: left; }
.modal-card .sheet-title h3 { font-size: 34px; letter-spacing: -0.035em; line-height: 1; text-shadow: 0 2px 12px rgba(0,0,0,.5); }
.sheet-tag { margin: 6px 0 0; font-size: var(--fs-xs); font-weight: 600; color: var(--text-dim); text-shadow: 0 1px 6px rgba(0,0,0,.5); }
@media (min-width: 720px) {
  .res-hero { width: min(560px, 100%); border-radius: 0 0 28px 28px; }
  .modal.sheet { align-items: center; }
  .modal-card.sheet-card { border-radius: 28px; padding-bottom: 18px; animation: cardIn .24s cubic-bezier(.2,.9,.3,1.15); }
}

/* ---- Edit profile sheet ----
   Taller than any other modal here (photo + name + four handles), so unlike the
   invite sheet it has to be allowed to scroll rather than run off the screen. */
#edit-modal .modal-card {
  max-height: min(88vh, 780px); overflow-y: auto; -webkit-overflow-scrolling: touch;
}
/* Same card, same inputs, same button stack as the invite sheet. The only new
   parts are the photo row and the handle fields.

   .modal-card > div is a pre-existing rule that forces `flex-direction: column;
   text-align: center` onto EVERY direct div child of a modal card — it exists
   for the auth sheet's stacked steps. It out-specifies a plain class selector,
   which silently turned each of these rows into a vertical stack (icon over @
   over input). These selectors are written to match its specificity rather than
   reaching for !important. */
.modal-card > .edit-photo-row {
  display: flex; flex-direction: row; align-items: center; gap: 15px; text-align: left;
}
.edit-photo-row .pf-avatar { cursor: pointer; }
.edit-photo-actions { display: flex; flex-direction: column; gap: 7px; align-items: flex-start; }
.btn.small { padding: 8px 14px; font-size: var(--fs-xs); width: auto; }
/* Destructive but not alarming: it reads as danger only once you're on it. */
.btn.ghost.danger { color: var(--danger); box-shadow: inset 0 0 0 1px rgba(226,92,72,.35); }
.btn.ghost.danger:active { background: rgba(226,92,72,.14); }
.edit-label {
  font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-dim); text-align: left; margin-top: 2px;
}
.edit-note { text-align: left; margin-top: -8px !important; font-size: var(--fs-xxs); }
/* The @ is furniture, not something the user types. Whatever they enter has the
   leading @ stripped on both sides anyway, so pasting "@bobby" also works. */
.modal-card > .edit-social {
  display: flex; flex-direction: row; align-items: center; gap: 8px; text-align: left;
  background: var(--surface); border-radius: var(--radius-s);
  box-shadow: var(--card-border); padding: 0 12px;
}
.edit-social .gicon { width: 17px; height: 17px; color: var(--text-dim); flex-shrink: 0; }
.edit-social .at { color: var(--text-dim); font-weight: 700; font-size: var(--fs-s); }
.edit-social input {
  flex: 1; min-width: 0; background: none !important; box-shadow: none !important;
  padding: 12px 0 !important; border-radius: 0 !important;
}
.edit-social:focus-within { box-shadow: inset 0 0 0 1px transparent, 0 0 0 2px rgba(246,176,73,.55); }
.modal-card .modal-sub a { color: var(--accent); }

/* ---- Photo framing sheet ----
   Above the edit sheet, and unlike it this card must never scroll: a vertical
   scroller wrapped around a drag surface means every upward drag is a coin
   flip between moving the photo and moving the card. */
#crop-modal { z-index: 40; }
.crop-card { gap: var(--sp-3); }
.crop-hint {
  margin: -4px 0 0 !important; font-size: var(--fs-xxs); color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* The square is the asset; the circle inscribed in it is the audience's view of
   the asset. Sizing the stage off the card's own width keeps the two the same
   shape on every screen, which is the only reason the maths can be square. */
/* Written as `.modal-card > .crop-stage` to out-specify the `.modal-card > div`
   rule above, which forces column-flex + centred text onto every direct div
   child of a card. See the note on .edit-social — same trap, same fix. */
.modal-card > .crop-stage {
  position: relative; display: block; width: 100%; aspect-ratio: 1; gap: 0;
  border-radius: var(--radius-s); overflow: hidden;
  background: #0A140E;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.07);
  cursor: grab;
  /* The whole point of the element. Without this, iOS hands the first finger to
     its own scroller and the photo lurches; `none` claims every touch for the
     pointer handlers, which then do all the panning and pinching themselves. */
  touch-action: none;
  overscroll-behavior: contain;
  user-select: none; -webkit-user-select: none;
}
.crop-stage.dragging { cursor: grabbing; }
.crop-stage:focus { outline: none; }
.crop-stage:focus-visible { box-shadow: inset 0 0 0 1px rgba(255,255,255,.07), 0 0 0 2px rgba(246,176,73,.65); }
/* transform-origin lives at the top-left so `translate then scale` is the exact
   affine map the crop maths writes down, with no half-width correction terms to
   get wrong. Sized to the source's natural pixels for the same reason: one
   image pixel is one unit before scaling. */
#crop-img {
  position: absolute; top: 0; left: 0; transform-origin: 0 0;
  max-width: none; max-height: none;
  will-change: transform; pointer-events: none;
  -webkit-user-drag: none; user-select: none;
}
/* Dims the corners — the part that is stored but never seen once the avatar is
   masked into a circle. A mask rather than four divs so it stays a perfect
   circle at any size, with a 1px feather so the edge is not stair-stepped. */
.crop-mask {
  position: absolute; inset: 0; pointer-events: none;
  background: rgba(6,13,9,.62);
  -webkit-mask-image: radial-gradient(circle closest-side at 50% 50%, transparent calc(100% - 1px), #000 100%);
  mask-image: radial-gradient(circle closest-side at 50% 50%, transparent calc(100% - 1px), #000 100%);
}
.crop-ring {
  position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  box-shadow: inset 0 0 0 1.5px rgba(246,176,73,.9), inset 0 0 0 3px rgba(6,13,9,.35);
}

.modal-card > .crop-zoom {
  display: flex; flex-direction: row; align-items: center; gap: 10px;
  text-align: left; padding: 0 2px;
}
.crop-zoom-icon {
  width: 15px; height: 15px; flex-shrink: 0;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round; color: var(--text-dim);
}
.crop-zoom-icon.big { width: 19px; height: 19px; }
/* The app's inputs are all centred text fields, so a range needs its own
   treatment rather than inheriting one that makes no sense for it. */
.modal-card .crop-zoom input[type="range"] {
  -webkit-appearance: none; appearance: none;
  flex: 1; min-width: 0; width: auto; padding: 0;
  height: 4px; border-radius: 2px; background: rgba(255,255,255,.16);
  box-shadow: none; outline: none; touch-action: none;
}
.modal-card .crop-zoom input[type="range"]:focus-visible {
  box-shadow: 0 0 0 2px rgba(246,176,73,.55);
}
.crop-zoom input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 20px; height: 20px; border-radius: 50%; border: 0;
  background: linear-gradient(180deg, #FFC463, var(--accent) 55%, var(--accent-deep));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 1px 3px rgba(0,0,0,.5);
  cursor: pointer;
}
.crop-zoom input[type="range"]::-moz-range-thumb {
  width: 20px; height: 20px; border-radius: 50%; border: 0;
  background: linear-gradient(180deg, #FFC463, var(--accent) 55%, var(--accent-deep));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 1px 3px rgba(0,0,0,.5);
  cursor: pointer;
}

/* True-size proof next to the stage. Small on purpose: an avatar is small, and
   a crop that only survives at 292px is not a crop that survived. */
.modal-card > .crop-out {
  display: flex; flex-direction: row; align-items: center; gap: 13px; text-align: left;
  background: var(--surface); border-radius: var(--radius-s);
  box-shadow: var(--card-border); padding: 11px 13px;
}
#crop-preview {
  width: 46px; height: 46px; border-radius: 50%; flex-shrink: 0; display: block;
  background: #0A140E;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12), 0 2px 8px rgba(0,0,0,.4);
}
.crop-out-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.crop-out-text b { font-size: var(--fs-xs); font-weight: 700; }
.crop-out-text span { font-size: var(--fs-xxs); color: var(--text-dim); line-height: 1.35; }
/* Motion here is either decorative (the sheet's entrance) or a direct echo of
   the user's own finger, and the second kind is never animated anyway — so
   there is nothing to preserve when someone has asked for less of it. */
@media (prefers-reduced-motion: reduce) {
  #crop-modal, #crop-modal .modal-card { animation: none; }
  #crop-img, .crop-ring, .crop-mask { transition: none; }
}

/* ---------- Full-bleed game screens (Golf / Rally / Swoop) ---------- */
.golf-screen { padding: 0 !important; justify-content: stretch !important; gap: 0 !important; height: 100dvh; min-height: 0; }
#golf-canvas { width: 100%; flex: 1; display: block; touch-action: none; }
.golf-hud {
  display: flex; gap: 14px; align-items: center; justify-content: center; flex-wrap: wrap;
  padding: calc(12px + env(safe-area-inset-top)) var(--sp-3) 10px;
  font-size: var(--fs-xs); font-weight: 700;
  background: rgba(11,23,16,.55);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom: 1px solid rgba(255,255,255,.07);
  width: 100%;
  font-variant-numeric: tabular-nums;
}
.golf-hud span { opacity: .9; }
/* ---- Golf: the whole-hole control ----
   Styled like a map control, because that is what it is: a frosted square
   with a drawn icon and a tracked label under it, bottom-right where the thumb
   rests and nothing in the scene lives. It is the same button in both states
   — "Hole" zooms out, and once out it turns amber and reads "Back" — so the
   way out is the way you came in. It settles in and out instead of blinking. */
#screen-golf { position: relative; }
.golf-ov {
  position: absolute; right: 14px; bottom: calc(18px + env(safe-area-inset-bottom, 0px)); z-index: 14;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: none; border: 0; padding: 0; cursor: pointer; -webkit-tap-highlight-color: transparent;
  color: var(--text); transition: opacity .2s ease, transform .28s cubic-bezier(.2,.8,.3,1);
}
.golf-ov.is-hidden { opacity: 0; transform: translateY(10px) scale(.9); pointer-events: none; }
.golf-ov-ic {
  width: 52px; height: 52px; border-radius: 17px; display: grid; place-items: center; color: var(--accent);
  background: rgba(11,23,16,.55);
  backdrop-filter: blur(16px) saturate(140%); -webkit-backdrop-filter: blur(16px) saturate(140%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.13), inset 0 1px 0 rgba(255,255,255,.08), 0 10px 22px rgba(0,0,0,.38);
  transition: transform .12s ease, background .18s ease, color .18s ease, box-shadow .18s ease;
}
.golf-ov-ic .gicon { width: 24px; height: 24px; stroke-width: 1.9; }
.golf-ov:active .golf-ov-ic { transform: scale(.92); }
.golf-ov-lbl {
  font-family: 'Bricolage', 'Inter', -apple-system, sans-serif;
  font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  text-shadow: 0 1px 3px rgba(0,0,0,.6);
}
.golf-ov.is-on .golf-ov-ic {
  background: linear-gradient(180deg, #FFC463, var(--accent) 38%, var(--accent-deep)); color: #3C2504;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 4px 0 #A5620D, 0 10px 22px rgba(0,0,0,.38);
}
.golf-msg {
  position: fixed; top: 38%; left: 50%; transform: translateX(-50%);
  font-size: 40px; font-weight: 900; letter-spacing: -0.02em; color: #fff; z-index: 15;
  text-shadow: 0 3px 14px rgba(0,0,0,.55); pointer-events: none;
}
.golf-msg.pop { animation: pop .4s ease; }
#rally-canvas { width: 100%; flex: 1; display: block; touch-action: none; }
.rally-hint { opacity: .55 !important; font-weight: 400 !important; }
#shootout-canvas { width: 100%; flex: 1; display: block; touch-action: none; }
.so-dots {
  display: flex; justify-content: space-between; width: 100%;
  padding: 6px var(--sp-4);
  background: rgba(11,23,16,.5);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  font-size: var(--fs-s); letter-spacing: 2px;
}
.so-prompt {
  position: fixed; bottom: calc(18px + env(safe-area-inset-bottom)); left: 0; right: 0;
  text-align: center; font-weight: 700; font-size: var(--fs-s); color: #fff;
  text-shadow: 0 2px 8px rgba(0,0,0,.6); pointer-events: none; z-index: 12;
}
.turn-btn { animation: pulseGlow 2s ease-in-out infinite; }
@keyframes pulseGlow { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.15); } }

/* Swoop — follows the .golf-screen full-bleed pattern */
#screen-swoop { padding: 0 !important; justify-content: stretch !important; gap: 0 !important; height: 100dvh; min-height: 0; }
#swoop-canvas { width: 100%; flex: 1; display: block; touch-action: none; }

/* Stack — follows the .golf-screen full-bleed pattern */
#stack-canvas { width: 100%; flex: 1; display: block; touch-action: none; }
#jumpycat-canvas { width: 100%; flex: 1; display: block; touch-action: none; min-height: 0; }
#screen-jumpycat .golf-hud { font-size: var(--fs-l); }
#slalom-canvas { width: 100%; flex: 1; display: block; touch-action: none; }
#sw-exit {
  border: 0; background: var(--surface); box-shadow: var(--card-border);
  color: var(--accent);
  font-family: inherit;
  font-size: var(--fs-xs); font-weight: 700; cursor: pointer;
  padding: 6px 12px; border-radius: var(--radius-s);
  transition: transform .07s, background .15s;
  pointer-events: auto; align-self: center; opacity: 1 !important;
}
#sw-exit:active { transform: scale(.95); background: var(--surface-2); }

/* ---------- Bottom tab bar ---------- */
#tabbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 22;
  display: flex;
  transform: translateZ(0); will-change: transform; contain: layout paint;
  background: rgba(9,18,12,.78);
  backdrop-filter: blur(24px) saturate(160%); -webkit-backdrop-filter: blur(24px) saturate(160%);
  border-top: 1px solid rgba(255,255,255,.08);
  box-shadow: 0 -8px 24px rgba(0,0,0,.25);
  padding: 6px 0 calc(6px + env(safe-area-inset-bottom));
}
#tabbar button {
  flex: 1; min-height: 54px;
  background: none; border: 0; cursor: pointer;
  font-family: inherit;
  /* colour, not opacity: opacity would promote each button to its own layer */
  color: rgba(242,234,216,.52);
  font-size: var(--fs-xxs); font-weight: 500; letter-spacing: .01em;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  transition: color .18s ease;
}
#tabbar button:active .ticon { transform: scale(.9); }
#tabbar button.active { color: var(--accent); font-weight: 700; letter-spacing: .015em; }
body.notab #tabbar { display: none; }

/* Line glyphs used everywhere emoji used to be: they inherit the surrounding
   text colour, so a row can go dim, accent or bold with everything else. */
.gicon {
  width: 18px; height: 18px; flex-shrink: 0; vertical-align: -3px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.gicon-lg { width: 26px; height: 26px; color: var(--accent); vertical-align: middle; }
.gicon-title { width: 26px; height: 26px; vertical-align: -5px; margin-right: 9px; color: var(--accent); }

#feed-list { width: min(380px, 94vw); display: flex; flex-direction: column; gap: 7px; }

/* ---- Results tab ----
   A segmented switch with a sliding amber thumb splits the two records. Under
   it, friends games are grouped by day and each row ends in its outcome, so
   the list reads as a record rather than a log; dailies lead with the date,
   because the date IS the daily. */
.seg {
  position: relative; display: grid; grid-template-columns: 1fr 1fr; width: min(380px, 94vw);
  padding: 4px; border-radius: 999px; background: var(--surface); box-shadow: var(--card-border);
  margin-bottom: var(--sp-3);
}
.seg button {
  position: relative; z-index: 1; border: 0; background: none; font: inherit; cursor: pointer;
  font-weight: 700; font-size: var(--fs-s); color: var(--text-dim); padding: 9px 10px; border-radius: 999px;
  transition: color .18s; -webkit-tap-highlight-color: transparent;
}
.seg button.active { color: #3C2504; }
.seg button span { font-weight: 700; font-size: var(--fs-xs); margin-left: 6px; opacity: .6; font-variant-numeric: tabular-nums; }
.seg-thumb {
  position: absolute; top: 4px; bottom: 4px; left: 4px; width: calc(50% - 4px); border-radius: 999px;
  background: linear-gradient(180deg, #FFC463, var(--accent-deep));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 2px 8px rgba(246,176,73,.3);
  transition: transform .28s cubic-bezier(.2,.8,.3,1);
}
.seg[data-on="daily"] .seg-thumb { transform: translateX(100%); }

.day-h {
  font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-dim); margin: 8px 4px 1px;
}
.day-h:first-child { margin-top: 0; }
.fr-row, .dl-row {
  width: 100%; display: flex; align-items: center; gap: 12px; padding: 11px 12px;
  background: var(--surface); border: 0; border-radius: var(--radius-m); box-shadow: var(--card-shadow);
  color: var(--text); font: inherit; text-align: left; cursor: pointer;
  transition: transform .09s, background .12s; -webkit-tap-highlight-color: transparent;
  animation: rowIn .3s cubic-bezier(.2,.8,.3,1) both; animation-delay: calc(var(--i, 0) * 35ms);
}
@keyframes rowIn { from { opacity: 0; transform: translateY(8px); } }
.fr-row:active, .dl-row:active { transform: scale(.985); background: var(--surface-2); }
.fr-ic, .dl-date {
  flex-shrink: 0; display: grid; place-items: center; color: var(--accent);
  background: rgba(246,176,73,.12); box-shadow: inset 0 0 0 1px rgba(246,176,73,.22);
}
.fr-ic { width: 40px; height: 40px; border-radius: 13px; }
.fr-ic .gicon { width: 21px; height: 21px; margin: 0; }
.fr-main, .dl-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.fr-main b, .dl-main b { font-size: var(--fs-s); font-weight: 800; letter-spacing: -0.01em; }
.fr-sub, .dl-sub { font-size: var(--fs-xs); color: var(--text-dim); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fr-row .hist-faces { margin: 0; flex-shrink: 0; }
.fr-oc {
  flex-shrink: 0; min-width: 40px; display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  font-size: 10px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--text-dim);
}
.fr-oc i { font-style: normal; font-size: var(--fs-m); font-weight: 800; letter-spacing: -0.02em; color: var(--text); font-variant-numeric: tabular-nums; }
.fr-oc.is-win { color: var(--success); }
.fr-oc.is-loss { color: #E0787C; }
.fr-oc.is-tie { color: var(--accent); }
.fr-oc.is-dnf i, .fr-oc.is-solo i { color: var(--text-dim); }
.dl-date { width: 44px; padding: 5px 0 4px; border-radius: 12px; grid-template-rows: auto auto; }
.dl-date i { font-style: normal; font-size: 9.5px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; line-height: 1; }
.dl-date b { font-family: 'Bricolage', 'Inter', -apple-system, sans-serif; font-size: 20px; font-weight: 800; line-height: 1; margin-top: 2px; }
.dl-main b .daily-chip { margin-left: 2px; vertical-align: 1px; }
.dl-score { flex-shrink: 0; font-size: var(--fs-l); font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.dl-score.is-dnf { font-size: var(--fs-xxs); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--text-dim); }
.feed-streaks { width: min(380px, 94vw); display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: var(--sp-2); }
/* Free play filter: three small text pills, quieter than the game chips above
   them (which navigate) because these only narrow the list in place. */
.feed-filter { width: min(380px, 94vw); display: flex; gap: 6px; margin: -2px 0 var(--sp-2); }
.filt {
  border: 0; border-radius: 999px; padding: 6px 12px; cursor: pointer; font: inherit;
  font-size: var(--fs-xxs); font-weight: 800; letter-spacing: .04em; color: var(--text-dim);
  background: none; box-shadow: var(--card-border); transition: background .15s, color .15s, transform .07s;
  -webkit-tap-highlight-color: transparent;
}
.filt.active { color: var(--text); background: var(--surface-2); box-shadow: inset 0 0 0 1px rgba(246,176,73,.45); }
.filt:active { transform: scale(.95); }
.streak-chip {
  display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px 5px 7px; border-radius: 999px;
  background: rgba(246,176,73,.12); box-shadow: inset 0 0 0 1px rgba(246,176,73,.22);
  font-size: var(--fs-xxs); font-weight: 800; color: var(--text);
}
.streak-chip .gicon { width: 15px; height: 15px; }
.feed-empty {
  width: 100%; padding: 36px 20px 28px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 6px; color: var(--text-dim);
}
.feed-empty .gicon { width: 30px; height: 30px; color: var(--accent); margin-bottom: 6px; opacity: .85; }
.feed-empty b { color: var(--text); font-size: var(--fs-m); font-weight: 800; }
.feed-empty span { font-size: var(--fs-xs); font-weight: 600; max-width: 270px; line-height: 1.45; }
.feed-chips {
  display: flex; gap: var(--sp-2); width: min(360px, 92vw); overflow-x: auto;
  padding-bottom: var(--sp-1); -webkit-overflow-scrolling: touch;
}
.chip {
  border: 0; border-radius: 999px; padding: 8px 14px; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--surface); box-shadow: var(--card-border);
  font-family: inherit;
  color: var(--text); font-size: var(--fs-xs); font-weight: 600; cursor: pointer;
  flex-shrink: 0;
  transition: background .15s, color .15s, transform .07s;
}
.chip span { color: var(--text-dim); font-weight: 600; }
.chip.active {
  background: linear-gradient(180deg, #FFC463, var(--accent-deep));
  color: #3C2504; font-weight: 700;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 2px 8px rgba(246,176,73,.3);
}
.chip.active span { color: rgba(60,37,4,.65); }
.chip .gicon { width: 16px; height: 16px; vertical-align: 0; }
.chip:active { transform: scale(.95); }

/* ---------- Profile ----------
   Everything below the header now shares one left rail. It used to be centred
   headings over left/right-justified card content, so there was no edge for the
   eye to follow down the page. */
#screen-stats { align-items: center; gap: var(--sp-2); }
.pf-head {
  width: min(380px, 94vw);
  display: flex; align-items: center; gap: 15px;
  margin-bottom: var(--sp-2);
  /* Clear the .music-toggle, which is position:fixed at 44px + 14px in the
     top-right corner of EVERY screen at z-index 25. The identity block is the
     only thing on the page tall enough to reach up into it, and the subtitle
     grew long enough to get there when the rank moved onto that line — "Rank #1
     of 646 players" was running underneath the speaker icon. This is the same
     collision that once put the Edit button under the toggle; nothing in the
     markup hints at it, it only shows up in a browser. */
  padding-right: 50px;
}
/* ---------- Avatar ----------
   One component, used at every size the app needs. The amber initial disc is
   unchanged from the old .pf-avatar — most players won't have a photo and it
   already looked right, so it stays as the fallback rather than becoming a grey
   placeholder silhouette.

   The photo is an <img> layered OVER the initial rather than replacing it. That
   is what makes the fallback free: a broken or slow image is a transparent box,
   and the letter underneath simply shows through, with no onerror handler and
   no flash of empty circle. */
.av {
  position: relative; flex-shrink: 0;
  width: 32px; height: 32px; border-radius: 50%;
  display: inline-grid; place-items: center; overflow: hidden;
  background: linear-gradient(180deg, #FFC463, var(--accent-deep));
  color: #3C2504; font-weight: 800; font-size: 14px; line-height: 1;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.4), 0 1px 3px rgba(0,0,0,.3);
  user-select: none;
}
.av i { font-style: normal; }
/* the default face: a line glyph on the disc, sized to the disc */
.av .av-glyph {
  width: 66%; height: 66%; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.av-xs .av-glyph, .av-sm .av-glyph, .hist-faces .av-glyph { stroke-width: 1.9; }
.av img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; border-radius: 50%; display: block;
}
.av-xs { width: 24px; height: 24px; font-size: 11px; }
.av-sm { width: 30px; height: 30px; font-size: 13px; }
.av-xl {
  width: 84px; height: 84px; font-size: var(--fs-xxl);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.4), 0 6px 20px rgba(246,176,73,.24), 0 2px 6px rgba(0,0,0,.35);
}
/* A photo shouldn't glow amber — the halo belongs to the initial disc. */
.av-xl:has(img) { box-shadow: inset 0 0 0 1px rgba(255,255,255,.12), 0 2px 10px rgba(0,0,0,.4); }

/* Now just a positioning wrapper; the disc itself is .av inside it. */
.pf-avatar { flex-shrink: 0; line-height: 0; }
.pf-id { min-width: 0; text-align: left; flex: 1; }
.pf-id h2 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pf-subtitle { font-size: var(--fs-xs); color: var(--text-dim); font-weight: 600; margin-top: 3px; }

/* Socials. The structure is borrowed from the reference apps — the identity
   block is the thing you'd show someone — but rendered in this app's language:
   the same 24x24 line glyphs and the same .chip pill as everywhere else, taking
   --text-dim and going --accent on press. Deliberately NOT brand colours: four
   saturated logos would be four foreign palettes dropped into a dark-green and
   amber page, which is the one thing this app's icon set exists to avoid. */
.pf-socials { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.pf-social {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 10px 5px 8px; border-radius: 99px;
  background: var(--surface); box-shadow: var(--card-border);
  color: var(--text-dim); text-decoration: none;
  font-size: var(--fs-xxs); font-weight: 700;
  transition: color .12s, background .12s, transform .07s;
}
.pf-social .gicon { width: 14px; height: 14px; }
.pf-social:active { transform: scale(.95); color: var(--accent); background: var(--surface-2); }
/* Edit is a pill on the same row as the socials rather than a corner icon —
   the top-right corner is occupied on every screen by .music-toggle, which is
   position:fixed at z-index 25 and would swallow the tap. */
button.pf-edit { border: 0; font-family: inherit; cursor: pointer; color: var(--accent); }
button.pf-edit .gicon { width: 13px; height: 13px; }

/* Another player's profile. Same rail and the same record block as your own —
   it should read as the same object seen from the other side — but with no Edit
   affordance anywhere on it. */
#screen-player { align-items: center; gap: var(--sp-2); }
.pl-body {
  width: min(380px, 94vw);
  display: flex; flex-direction: column; gap: var(--sp-2); align-items: stretch;
}
#pl-head { width: min(380px, 94vw); }
.pl-body .pf-socials { margin: 0 0 var(--sp-2); }
.pl-body .pf-record, .pl-body .pf-block { width: 100%; }
/* Bobby: the games rows on a friend's page had no air between them. */
.pl-body .pf-block { gap: 8px; }
.pl-body .pf-h { margin-top: var(--sp-3); margin-bottom: 4px; }
.pl-body .pf-h { align-self: flex-start; }

/* The old trio of tiles said "18 games / 20 wins / 69% win rate" side by side,
   which reads as a contradiction (plays vs. head-to-head matchups). The record
   is stated once, as a record, and the bar carries the ratio. */
.pf-record {
  width: min(380px, 94vw);
  background: var(--surface); border-radius: var(--radius-l);
  box-shadow: var(--card-shadow);
  padding: 15px var(--sp-4) var(--sp-4);
  display: flex; flex-direction: column; gap: 11px;
}
.pf-rec-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); }
.pf-rec-label {
  font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-dim);
}
.pf-rec-rate {
  font-size: var(--fs-xl); font-weight: 800; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums; color: var(--accent); line-height: 1;
}
.pf-rec-rate i { font-style: normal; font-size: var(--fs-s); font-weight: 700; margin-left: 1px; }
.pf-rec-nums { display: flex; gap: 26px; }
.pf-rec-nums > span { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; }
.pf-rec-nums b {
  font-size: var(--fs-l); font-weight: 800; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.pf-rec-nums > span:nth-child(1) b { color: var(--success); }
.pf-rec-nums > span:nth-child(2) b { color: var(--danger); }
.pf-rec-nums span span {
  font-size: var(--fs-xxs); color: var(--text-dim); font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
}
.pf-rec-bar {
  display: flex; height: 6px; border-radius: 3px; overflow: hidden; gap: 2px;
  background: rgba(255,255,255,.07);
}

/* Global rank. Bobby, on the card version: "it should say just the rank and
   also I dont think it needs such a large card, it is just a number like on
   beli it is small." It was .pf-record's twin — same surface, same radius, same
   uppercase label, same hero accent numeral — parked directly beneath it, which
   gave one number the weight of the head-to-head record and made the two
   compete. The record has to win that fight; it is what the profile is for.

   So this is no longer a container at all. It is a run of text on
   .pf-subtitle, inheriting that line's size, colour and weight, with only the
   numeral lifted to accent — the smallest treatment that still lets your eye
   find the number. Identical on your own profile and on a rival's, because it
   is one league table seen from two sides. */
/* RANK IS A BADGE, NOT A CLAUSE. As an inline run on .pf-subtitle it was the
   fourth fact on one line and wrapped mid-phrase at 402pt — "Rank" ending one
   line, "#1 of 7 players" starting the next. Shortening the string would only
   have moved the wrap to the next longer name.

   So it borrows .pf-social wholesale and joins the chip row, which already
   flex-wraps. That is the fix rather than a patch: the label and the numeral
   are inside ONE inline-flex element, so they cannot be split across lines at
   any name length or rank size — the row moves the whole pill down instead.
   white-space: nowrap makes that explicit rather than incidental.

   It stays deliberately lighter than .pf-record. The head-to-head record is the
   emotional core of this screen and has to keep winning that fight; only the
   numeral takes accent. */
.pf-rankpill { white-space: nowrap; }
.pf-rankpill b {
  font-weight: 800; color: var(--accent); font-variant-numeric: tabular-nums;
  font-size: var(--fs-xxs);
}

.pf-h {
  width: min(380px, 94vw); text-align: left;
  margin-top: var(--sp-4); margin-bottom: 1px;
  display: flex; align-items: baseline; gap: 8px;
}
.pf-h i {
  font-style: normal; letter-spacing: .04em; font-weight: 600;
  color: rgba(242,234,216,.4); text-transform: none;
}
.pf-block { width: min(380px, 94vw); display: flex; flex-direction: column; }
#pf-friends {
  background: var(--surface); border-radius: var(--radius-l);
  box-shadow: var(--card-shadow);
  /* a strip, not a list: faces side by side, scrolled sideways, no cap */
  flex-direction: row; gap: 4px; overflow-x: auto; scroll-snap-type: x proximity;
  padding: 10px 8px 8px; -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
#pf-friends::-webkit-scrollbar { display: none; }
#pf-friends.is-empty { flex-direction: column; padding: 14px var(--sp-4); }
.riv {
  flex: 0 0 auto; width: 88px; display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 8px 4px 7px; border: 0; border-radius: var(--radius-m); background: none;
  color: inherit; font: inherit; cursor: pointer; scroll-snap-align: start;
  -webkit-tap-highlight-color: transparent; transition: transform .09s, background .12s;
}
.riv:active { transform: scale(.95); background: var(--surface-2); }
/* No ring around a rival's face. The record under the name already says
   who is up; a green or red circle said it a second time, louder. */
.riv-av { padding: 3px; border-radius: 50%; margin-bottom: 3px; }
.av-riv { width: 56px; height: 56px; font-size: 22px; }
.riv-name { font-size: var(--fs-xs); font-weight: 800; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.riv-rec { font-size: var(--fs-s); font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; line-height: 1.1; }
.riv-rec b { color: var(--success); }
.riv-rec i { font-style: normal; color: #E0787C; }
.riv-rec u { text-decoration: none; color: var(--text-dim); }
.riv-rec em { font-style: normal; color: var(--text-dim); font-weight: 600; margin: 0 1px; opacity: .7; }
.riv-games { font-size: 10px; font-weight: 700; letter-spacing: .04em; color: var(--text-dim); }
#pf-fav { gap: var(--sp-2); }

.pf-badges {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-2); width: min(380px, 94vw);
}
.pf-badge {
  background: var(--surface); border-radius: var(--radius-m);
  box-shadow: var(--card-shadow);
  padding: 13px var(--sp-3);
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px; text-align: left;
}
.pf-badge b { font-size: var(--fs-xs); font-weight: 700; }
.pf-badge span { font-size: var(--fs-xxs); color: var(--text-dim); line-height: 1.3; }
/* A locked badge dims its OWN glyph. Ten tiles where five showed the same
   padlock was the most template-looking surface in the app. */
.pf-badge-icon { width: 22px; height: 22px; color: var(--accent); margin-bottom: 3px; }
.pf-badge.locked { opacity: .42; }
.pf-badge.locked .pf-badge-icon { color: var(--text); }

#screen-stats .me-line {
  width: min(380px, 94vw); text-align: left;
  margin-top: var(--sp-5); padding-bottom: var(--sp-2);
}

/* ---------- Guess the Song ---------- */
#screen-songguess { gap: 18px; }
.sg-segs {
  display: flex; gap: 3px; width: min(340px, 88vw); height: 12px;
}
.sg-seg { border-radius: 6px; background: rgba(255,255,255,.12); transition: background .15s; }
.sg-seg.used { background: rgba(246,176,73,.45); }
.sg-seg.cur { background: var(--accent); box-shadow: 0 0 10px rgba(246,176,73,.5); }
.sg-controls { display: flex; gap: 10px; width: min(340px, 88vw); }
.sg-controls .btn { flex: 1; min-height: 48px; padding: 13px; font-size: var(--fs-s); }
.sg-hud { border-radius: var(--radius-m); border-bottom: 0; box-shadow: var(--card-border); width: min(340px, 88vw); padding: 12px; }
.sg-disc {
  width: 150px; height: 150px; border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%, #E04F3C 0 26%, #1D242B 27% 100%),
    #1D242B;
  border: 5px solid #333C46;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 4px rgba(0,0,0,.3), 0 14px 38px rgba(0,0,0,.45), inset 0 0 0 1px rgba(255,255,255,.06);
}
.sg-disc-label { font-size: var(--fs-xxl); }
.sg-disc.spinning { animation: spinDisc 2.2s linear infinite; }
@keyframes spinDisc { to { transform: rotate(360deg); } }
.sg-guessbox { width: min(360px, 92vw); display: flex; flex-direction: column; gap: 10px; }
#sg-input {
  border: 0; border-radius: var(--radius-m); padding: 15px; font-size: 18px;
  font-family: inherit;
  background: rgba(255,255,255,.1); box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
  color: #fff; outline: none; width: 100%;
}
#sg-input:focus { box-shadow: inset 0 0 0 1px transparent, 0 0 0 2px rgba(246,176,73,.55); }
.sg-suggest { display: flex; flex-direction: column; gap: 6px; }
.sg-opt {
  border: 0; border-radius: var(--radius-s); padding: var(--sp-3) 14px; text-align: left;
  background: var(--surface); box-shadow: var(--card-border);
  font-family: inherit;
  color: var(--text); font-size: var(--fs-s); font-weight: 700; cursor: pointer;
  transition: background .12s;
}
.sg-opt span { color: var(--text-dim); font-weight: 500; }
.sg-opt:active { background: rgba(246,176,73,.3); }
.sg-reveal {
  width: min(360px, 92vw); background: var(--surface); border-radius: var(--radius-l);
  box-shadow: var(--card-shadow);
  padding: var(--sp-5); text-align: center; display: flex; flex-direction: column; gap: var(--sp-2);
  animation: cardIn .24s cubic-bezier(.2,.9,.3,1.15);
}
.sg-answer { font-size: var(--fs-l); font-weight: 800; letter-spacing: -0.02em; }
.sg-artist { color: var(--text-dim); }
.sg-time { color: var(--accent); font-weight: 700; margin-bottom: 6px; }
.sg-shake { animation: shake .35s ease; color: var(--danger) !important; }
@keyframes shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-6px)} 75%{transform:translateX(6px)} }
/* keep the YT player technically visible (audio requires it) but out of the way */
.yt-holder { position: fixed; bottom: 2px; right: 2px; width: 2px; height: 2px; overflow: hidden; opacity: .01; pointer-events: none; }

/* ---------- Floating controls ---------- */
.music-toggle {
  position: fixed; top: calc(14px + env(safe-area-inset-top)); right: 14px; z-index: 25;
  width: 44px; height: 44px; border: 0; border-radius: 50%;
  background: rgba(11,23,16,.5);
  backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%);
  box-shadow: var(--card-border), 0 4px 14px rgba(0,0,0,.3);
  cursor: pointer; color: var(--text);
  display: flex; align-items: center; justify-content: center;
  transition: transform .07s ease, background .15s, color .15s;
}
.music-toggle .gicon { width: 20px; height: 20px; vertical-align: 0; }
.music-toggle:active { transform: scale(.9); }
.music-toggle.off { opacity: .55; }

.toast {
  position: fixed; bottom: calc(30px + env(safe-area-inset-bottom)); left: 50%; transform: translateX(-50%);
  background: linear-gradient(180deg, #FFC463, var(--accent-deep));
  color: #3C2504; font-weight: 700; font-size: var(--fs-s);
  padding: var(--sp-3) 20px; border-radius: var(--radius-m); z-index: 40;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 2px 6px rgba(0,0,0,.3), 0 12px 32px rgba(0,0,0,.4);
  animation: toastIn .28s cubic-bezier(.2,.9,.3,1.25);
}
@keyframes toastIn { from { opacity: 0; transform: translate(-50%, 16px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* reCAPTCHA badge: hidden per Google's allowance when attribution is shown in the auth flow */
.grecaptcha-badge { visibility: hidden !important; }
.recaptcha-attrib { font-size: 10px; color: var(--text-dim); opacity: .7; margin-top: 10px; }
.recaptcha-attrib a { color: var(--text-dim); text-decoration: underline; }

/* ---- Outstanding challenges on Home ---------------------------------- */
/* Sits below the game cards. Rows use the same material as the refined
   game tiles: grounding shadow + hairline top highlight, no flat borders.
   .inbox-art gets a real rendered game tile as a background-image once the
   tile set lands; the colour block is the fallback. */
.inbox { width: min(340px, 90vw); margin-top: 26px; display: flex; flex-direction: column; gap: 9px; }
.inbox-h {
  font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase;
  color: var(--text-dim); font-weight: 700; margin: 12px 2px 3px;
}
.inbox-h:first-child { margin-top: 0; }
.inbox-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  text-align: left; font: inherit; color: inherit; cursor: pointer;
  background: #132018; border: 0; border-radius: 17px; padding: 11px 13px;
  box-shadow: 0 8px 18px -8px rgba(0,0,0,.7), inset 0 1px 0 rgba(255,255,255,.09),
              inset 0 0 0 1px rgba(255,255,255,.045);
  transition: transform .12s ease, filter .12s ease;
}
.inbox-row:active { transform: scale(.985); filter: brightness(1.12); }
.inbox-row.muted { background: #101a14; }
.inbox-art {
  width: 42px; height: 42px; border-radius: 12px; flex: none;
  background-size: cover; background-position: center; background-color: #1d2f24;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06);
}
.inbox-art { background-size: cover; background-position: center; }
.inbox-art.art-golf    { background-color: #23503a; }
.inbox-art.art-rally   { background-color: #5a2c22; }
.inbox-art.art-slalom  { background-color: #2b4f6e; }
.inbox-art.art-stack   { background-color: #25455e; }
.inbox-art.art-jumpycat{ background-color: #6b3a14; }
.inbox-art.art-wordrush{ background-color: #3d2f52; }
.inbox-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.inbox-mid b { font-size: 14.5px; font-weight: 650; }
.inbox-mid span {
  font-size: 11.5px; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.inbox-cta {
  flex: none; font-size: 12.5px; font-weight: 700; color: #0B1710;
  background: var(--accent); padding: 7px 14px; border-radius: 99px;
}
.inbox-cta.ghost { background: transparent; color: #6f8478; box-shadow: inset 0 0 0 1px #2a3f33; }

/* ---- Invite sheet ---------------------------------------------------- */
.inv-recent { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 10px; }
.inv-recent:empty { display: none; }
.inv-chip {
  display: flex; align-items: center; gap: 7px;
  font: inherit; font-size: var(--fs-xs); font-weight: 600; color: var(--text);
  background: var(--surface); box-shadow: var(--card-border);
  border: 0; border-radius: 99px; padding: 7px 13px 7px 7px; cursor: pointer;
  transition: background .12s ease, box-shadow .12s ease;
}
/* .av is the shared avatar component now — the chip only positions it. */
.inv-chip .av { margin: 0; }
.inv-chip[aria-pressed="true"] {
  background: rgba(246,176,73,.18);
  box-shadow: inset 0 0 0 1.5px var(--accent);
}

/* ============================================================
   Home: art-led tile lobby
   Replaces the emoji-icon card list. Each tile is itself the
   button; the art bleeds under a scrim so labels always read.
   .gtile-art background-images swap to /img/tiles/* when the
   rendered tile set lands — nothing else needs to change.
   ============================================================ */
@font-face {
  font-family: 'Bricolage';
  src: url('/fonts/BricolageGrotesque.woff2') format('woff2');
  font-weight: 400 800;
  font-display: swap;
}
.wordmark, .gtile-cap b, .gtile-chip, h1, h2, .game-card-title {
  font-family: 'Bricolage', 'Inter', -apple-system, sans-serif;
}

.screen.home { justify-content: flex-start; gap: 0; }
.home-head { width: 100%; max-width: 940px; margin: 6px auto 20px; }
.wordmark { font-size: clamp(34px, 9vw, 46px); font-weight: 800; letter-spacing: -0.035em; line-height: 1; }
.brand { display: flex; align-items: center; gap: 12px; }
.brand-logo { width: 1.5em; height: 1.5em; font-size: clamp(34px, 9vw, 46px); image-rendering: pixelated; flex: 0 0 auto; }
.home-head .tagline { text-align: left; margin-top: 7px; }

.gtiles {
  width: 100%; max-width: 940px; margin: 0 auto;
  display: grid; grid-template-columns: 1fr 1fr; gap: 13px;
}
.gtile {
  position: relative; overflow: hidden; isolation: isolate;
  border: 0; padding: 0; margin: 0; cursor: pointer;
  border-radius: var(--radius-l); height: 156px;
  background: linear-gradient(158deg, #22402e, #0f2018 65%);
  box-shadow: 0 14px 30px -12px rgba(0,0,0,.8), 0 2px 6px rgba(0,0,0,.4),
              inset 0 1px 0 rgba(255,255,255,.13), inset 0 0 0 1px rgba(255,255,255,.05);
  transition: transform .13s cubic-bezier(.2,.8,.3,1), filter .13s ease;
}
.gtile:active { transform: scale(.982); filter: brightness(1.1); }
.gtile.hero { grid-column: 1 / -1; height: 218px; }
.gtile.wide { grid-column: 1 / -1; height: 150px; }

.alpha-games { width: 100%; max-width: 940px; margin: 30px auto 0; }
.alpha-heading { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 2px 12px; }
.alpha-heading h2 { font-size: 22px; font-weight: 750; letter-spacing: -.025em; }
.alpha-heading p { font-size: 12px; color: var(--text-dim); }
.gtile.alpha-tile { display: block; width: 100%; height: 240px; text-decoration: none; color: var(--text); }
.alpha-tile:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }
.alpha-tile .gtile-cap { right: 90px; }
.alpha-tile .gtile-cap b { font-size: 25px; }
.alpha-tile .gtile-cap i { line-height: 1.45; }
.alpha-tile .art-horde { background-image: url('/img/horde/mobbed-card.webp?v=2'); background-position: center 42%; }   /* 3:1 engine render: the 940x240 band shows the middle 77% of its height, the phone card the middle 50% of its width */
.alpha-play { position: absolute; z-index: 3; right: 18px; bottom: 18px; display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 700; color: var(--accent); }
.alpha-play svg { width: 20px; height: 20px; }

.gtile-art {
  position: absolute; inset: 0; z-index: 0;
  background-repeat: no-repeat; background-position: center; background-size: cover;
}
.gtile-grain {
  position: absolute; inset: 0; z-index: 1; opacity: .13; pointer-events: none; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
.gtile-scrim {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(6deg, rgba(6,12,9,.94) 6%, rgba(6,12,9,.55) 34%, transparent 64%);
}
.gtile-cap {
  position: absolute; z-index: 3; left: 15px; right: 15px; bottom: 13px;
  display: flex; flex-direction: column; gap: 3px; text-align: left;
}
.gtile-cap b { font-size: 18px; font-weight: 800; letter-spacing: -0.025em; color: var(--text); }
.gtile.hero .gtile-cap b { font-size: 30px; }
.gtile-cap i { font-style: normal; font-size: 11.5px; color: rgba(242,234,216,.72); }
.gtile-chip {
  position: absolute; z-index: 3; top: 13px; left: 15px;
  font-size: 10px; font-weight: 800; letter-spacing: .13em;
  color: #3C2504; background: var(--accent); padding: 4px 10px; border-radius: 99px;
  box-shadow: 0 3px 10px rgba(246,176,73,.35);
}
/* DAILY STREAK BADGE. Top-RIGHT, because .gtile-chip already owns top-left and
   because the Blender renders all put their subject centre-low — this is the
   one corner of every tile that is empty sky. It carries its own dark backing
   so it reads at any contrast against art it does not control, and it is small
   enough not to compete with the render, which is the best-looking thing on
   this screen.

   The flame is lit for as long as the streak is alive, played or not. It once
   went quiet after you'd played, on the theory that urgency was the signal
   worth carrying — but that dims your streak at the exact moment you earn it,
   which rewards playing by removing the reward. `.is-todo` adds a brighter
   glow while today is still outstanding: a nudge rather than a demotion. */
.gtile-streak {
  position: absolute; z-index: 4; top: 11px; right: 12px;
  display: inline-flex; align-items: center; gap: 3px;
  padding: 4px 8px 4px 6px; border-radius: 99px;
  background: rgba(6,12,9,.66);
  -webkit-backdrop-filter: blur(8px) saturate(130%); backdrop-filter: blur(8px) saturate(130%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.11);
  font-size: 11.5px; font-weight: 800; font-variant-numeric: tabular-nums;
  line-height: 1; pointer-events: none;
}
.gtile-streak .gicon { width: 13px; height: 13px; }
.gtile-streak.is-live { color: var(--accent); }
/* The lit flame is FILLED, not stroked — the shared .gicon rule strokes and
   leaves fill none, so both bodies are switched over explicitly. The core is
   the same path scaled down and lightened toward white, which is what makes it
   read as a flame with a hot centre rather than as a solid amber blob; and the
   drop-shadow is on the glyph rather than the pill so the glow follows the
   flame's silhouette instead of haloing a rectangle. */
.gtile-streak.is-live .gicon { stroke: none; filter: drop-shadow(0 0 4px rgba(246,176,73,.55)); }
.gtile-streak.is-live .fl-body { fill: currentColor; stroke: none; }
.gtile-streak.is-live .fl-core { fill: #FFF0C6; stroke: none; opacity: .92; }
/* A slow, shallow flicker — 3.1s and barely a tenth of scale, so it catches the
   eye in peripheral vision without becoming a thing that twitches at you while
   you are trying to read the tile behind it. transform-box/origin keep the
   scale anchored at the flame's base, so it burns rather than pulses. */
@keyframes gt-flicker {
  0%, 100% { transform: scaleY(1);    opacity: 1; }
  38%      { transform: scaleY(1.09); opacity: .93; }
  64%      { transform: scaleY(.97);  opacity: 1; }
}
.gtile-streak.is-live .fl-core {
  transform-box: fill-box; transform-origin: 50% 100%;
  animation: gt-flicker 3.1s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .gtile-streak.is-live .fl-core { animation: none; }
}
/* Today still outstanding: same lit flame, just a touch brighter and with a
   wider glow. The difference is a nudge, not a demotion — the streak is lit
   either way, because it belongs to you whether or not you have played yet. */
.gtile-streak.is-todo { box-shadow: inset 0 0 0 1px rgba(246,176,73,.34); }
.gtile-streak.is-todo .gicon { filter: drop-shadow(0 0 6px rgba(246,176,73,.75)); }

.gtile-actions { position: absolute; z-index: 4; right: 14px; bottom: 14px; display: flex; gap: 8px; }
.pill {
  font: inherit; font-size: 13px; font-weight: 700; color: #3C2504; cursor: pointer;
  background: linear-gradient(180deg, #FFC463, var(--accent-deep));
  border: 0; border-radius: 99px; padding: 9px 15px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 3px 8px rgba(0,0,0,.35);
}
.pill:active { transform: translateY(1px); }

/* Per-game key art. Each tile is a composed 1600x1000 scene rendered by
   scripts/render_tiles.py — subject plus a piece of that game's world. They are
   cropped with `cover` at aspect ratios from 1.1:1 (phone half-tile) to 2.6:1
   (desktop hero), so the renders keep everything critical in the middle 60%.
   The gradient under each is the fallback while the image decodes. */
.art-golf     { background-image: url('/img/tiles/tile-golf.png?v=2'); }
.art-rally    { background-image: url('/img/tiles/tile-rally.png'); }
.art-stack    { background-image: url('/img/tiles/tile-stack.png'); }
.art-jumpycat { background-image: url('/img/tiles/tile-jumpycat.png'); }
.art-slalom   { background-image: url('/img/tiles/tile-slalom.png?v=2'); }
.art-wordrush { background-image: url('/img/tiles/tile-wordrush.png'); }

.gtile:has(.art-golf), .gtile.hero { background: linear-gradient(158deg, #255138, #0d2016 62%); }
.gtile:has(.art-rally)    { background: linear-gradient(158deg, #5a2c22, #1d1210 70%); }
.gtile:has(.art-stack)    { background: linear-gradient(158deg, #25455e, #0c161f 70%); }
.gtile:has(.art-jumpycat) { background: linear-gradient(158deg, #6b3a14, #1a0e06 70%); }
.gtile:has(.art-slalom)   { background: linear-gradient(120deg, #2b4f6e, #0e1a26 72%); }
.gtile:has(.art-wordrush) { background: linear-gradient(158deg, #3d2f52, #16101f 70%); }

/* Desktop: the app was a 340px ribbon in the middle of a huge screen. */
@media (min-width: 720px) {
  .gtiles { grid-template-columns: repeat(3, 1fr); gap: 16px; }
  .gtile { height: 190px; }
  .gtile.hero { grid-column: span 2; height: 238px; }
  .gtile.wide { grid-column: span 1; height: 190px; }
  .inbox, .stack { width: 100%; max-width: 940px; margin-left: auto; margin-right: auto; }
}
@media (min-width: 720px) { .screen.home { padding-left: 32px; padding-right: 32px; } }

/* Tab bar: line icons, not emoji. Selection is stroke weight + colour. */
#tabbar .ticon {
  width: 24px; height: 24px; display: block; margin: 0 auto 3px;
  padding: 0; background: none; border-radius: 0;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
  transition: stroke-width .18s ease, transform .12s ease;
}
#tabbar button.active .ticon {
  stroke-width: 2.5;
  /* a hair of amber bloom so the heavier stroke reads on the blurred dark bar */
  filter: drop-shadow(0 0 6px rgba(246,176,73,.26));
}
@media (prefers-reduced-motion: reduce) {
  #tabbar button, #tabbar .ticon { transition: none; }
}
/* keep the caption clear of the size pills on the Word Rush tile */
.gtile.wide .gtile-cap { right: 155px; }

/* ===========================================================================
   DAILY MODE
   ===========================================================================*/

/* The Daily / Normal chooser. Two cards in the existing .modal-card sheet, so
   it reads as the same furniture as the invite and edit sheets rather than a
   new kind of surface. Daily leads because it is the new thing and because it
   expires — the normal game is there tomorrow, today's is not. */
/* ---- Game entry: the two modes ----
   Two cards side by side, one per mode. The daily is the louder one on
   purpose (amber hairline and an amber Play) — it is the one that goes away
   at midnight. Each card is a column: icon, kicker, name, one line of why,
   then a pill CTA pinned to the bottom so both pills sit on one line. They
   are dealt in one after the other, 70ms apart, not dumped. */
.sheet-card > .dm-options { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 0 0 2px; text-align: left; }
.dm-card {
  min-height: 200px; width: 100%; text-align: left; font-family: inherit; color: var(--text);
  background: var(--surface); border: 0; border-radius: var(--radius-l);
  box-shadow: var(--card-border); padding: 15px 14px 12px;
  display: flex; flex-direction: column; gap: 3px; position: relative;
  animation: dmIn .38s cubic-bezier(.2,.8,.3,1) both;
  animation-delay: calc(140ms + var(--i, 0) * 70ms);
}
@keyframes dmIn { from { opacity: 0; transform: translateY(16px) scale(.96); } }
.dm-card.is-btn { cursor: pointer; transition: transform .09s, background .12s; -webkit-tap-highlight-color: transparent; }
.dm-card.is-btn:active { transform: scale(.975); background: var(--surface-2); }
.dm-ic { width: 26px; height: 26px; color: var(--accent); margin-bottom: 9px; stroke-width: 1.6; }
.dm-card b {
  font-family: 'Bricolage', 'Inter', -apple-system, sans-serif;
  font-size: 22px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.1;
}
.dm-kicker {
  font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-dim);
}
.dm-sub { font-size: var(--fs-xs); color: var(--text-dim); font-weight: 600; line-height: 1.35; margin-top: 2px; padding-bottom: 12px; }
/* the two live facts that make a daily feel live, as small chips */
/* Bobby: "there isn't enough padding below the Be the first today button" —
   the chip row keeps its own clearance from the CTA, whatever the card height. */
.dm-stats { display: flex; flex-wrap: wrap; gap: 4px; margin: -2px 0 14px; }
.dm-stats i {
  font-style: normal; font-size: 10.5px; font-weight: 700; letter-spacing: .02em; color: var(--text);
  padding: 3px 8px; border-radius: 999px; background: rgba(255,255,255,.08);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
}
.dm-cta {
  margin-top: auto; padding: 10px 12px; border-radius: 999px; text-align: center;
  font-size: 13px; font-weight: 800; color: var(--text); background: var(--surface-2);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.10);
}
.dm-cta.is-primary {
  background: linear-gradient(180deg, #FFC463, var(--accent-deep)); color: #3C2504;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 3px 0 #A5620D, 0 8px 16px rgba(0,0,0,.3);
}
.dm-card.is-btn:active .dm-cta.is-primary { transform: translateY(2px); box-shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 0 #A5620D, 0 3px 8px rgba(0,0,0,.3); }
.dm-row { display: flex; gap: 6px; margin-top: auto; padding-top: 10px; }
.dm-row .pill { flex: 1; padding: 9px 6px; }
/* Today's game gets the accent; the friends game keeps the plain surface. One
   card is louder than the other on purpose — they are not equal choices. */
.dm-card.is-daily { box-shadow: inset 0 0 0 1px rgba(246,176,73,.45); background: linear-gradient(180deg, rgba(246,176,73,.13), rgba(246,176,73,.05)); }
.dm-card.is-done .dm-cta { color: var(--text-dim); }
/* DID NOT FINISH. Stated, not scolded: a rose hairline rather than the accent,
   because this is information about your day, not an error you made. It must
   look distinctly unlike .is-daily (amber, "play me") so a glance never
   mistakes a spent day for an open one. */
.dm-card.is-dnf { box-shadow: inset 0 0 0 1px rgba(214,120,124,.34); }
.dm-card.is-dnf b { color: rgba(242,234,216,.82); }
.dm-card.is-loading { justify-content: center; gap: 8px; animation: none; }

/* ---- Daily results -------------------------------------------------------*/
#screen-daily { align-items: center; gap: var(--sp-3); padding-top: 0; }
/* the Home link rides the hero art like the sheet's close button does */
.hero-nav { position: absolute; z-index: 3; top: calc(10px + env(safe-area-inset-top, 0px)); left: 12px; margin: 0; width: auto; }
.hero-nav .nav-link {
  background: rgba(11,23,16,.5); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.14);
}
.dr-chart, .dr-tiles, #dr-friends { width: min(380px, 94vw); }
.dr-chart .res-h, #dr-friends .res-h { margin-bottom: var(--sp-2); }

/* THE DISTRIBUTION, as an emphasis chart.
   One series, so there is no legend — the heading names it. Your bar carries
   the accent; every other bar is a single recessive gray. That pairing is
   validated rather than eyeballed: OKLab CVD separation dE 23.0 (protan) and
   27.6 for normal vision, both far above the >=8 / >=15 floors, and both colours
   clear 3:1 against this surface. Identity is never colour-alone either — your
   bar is also directly labelled "You", which is the only direct label on the
   chart. A number on every bar would be noise. */
.hg {
  display: flex; align-items: flex-end; justify-content: center;
  gap: 2px;                     /* the 2px surface gap between adjacent fills */
  height: 132px; padding-top: 18px;
  background: var(--surface); border-radius: var(--radius-m);
  box-shadow: var(--card-border); padding-inline: 10px;
}
.hg-bar {
  flex: 1 1 0; min-width: 3px; max-width: 24px;   /* capped: the leftover is air */
  height: 100%; position: relative;
  display: flex; align-items: flex-end;
}
.hg-fill {
  display: block; width: 100%; background: #6E7A70;
  /* 4px rounded data-end, square where it meets the baseline. */
  border-radius: 4px 4px 0 0;
  min-height: 2px;
}
.hg-bar.mine .hg-fill { background: var(--accent); }
.hg-you {
  position: absolute; top: -17px; left: 50%; transform: translateX(-50%);
  font-size: var(--fs-xxs); font-weight: 800; font-style: normal;
  color: var(--accent); letter-spacing: .04em; white-space: nowrap;
}
/* Recessive axis: only the two ends are labelled. Everything between them is
   readable from the shape, and eleven tick labels on a 402px phone is mush. */
.hg-axis {
  display: flex; justify-content: space-between; margin-top: 6px; padding-inline: 4px;
  font-size: var(--fs-xxs); color: var(--text-dim); font-weight: 600;
}

/* Supporting numbers. Small tiles, deliberately quieter than the hero — the
   percentile is the headline and these are the context for it. */
.dr-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
.dr-tile {
  background: var(--surface); border-radius: var(--radius-m); box-shadow: var(--card-border);
  padding: 11px var(--sp-3) 12px; display: flex; flex-direction: column; gap: 1px; min-width: 0;
}
.dr-tile b {
  font-size: var(--fs-l); font-weight: 800; letter-spacing: -0.02em; color: var(--accent);
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dr-tile-k {
  font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-dim);
}
.dr-tile-s {
  font-size: var(--fs-xxs); color: var(--text-dim); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dr-fscore {
  font-size: var(--fs-s); font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--text); margin-left: auto;
}
/* A friend who started today and never finished. Listed honestly and last —
   dropping them would quietly make the board look like everybody finished —
   but dimmed and at label weight, because it is not a score and must not be
   read as one. */
.dr-fscore.is-dnf { color: var(--text-dim); font-size: var(--fs-xxs); letter-spacing: .08em; }
/* Same word in a history row. */
/* The friends board is a .h2h-row with two EXTRA children — a placing number in
   front and a score instead of a W-L record — so it needs its own template.
   Inheriting the four-column rivals grid pushed the chevron onto a second row
   of its own and clipped the score off the right edge; the same auto-placement
   trap the rivals row comment above warns about. */
#dr-friends .h2h-row.is-link {
  grid-template-columns: 20px 30px minmax(0,1fr) auto 9px;
  padding: 7px 0;
}
#dr-friends .lb-rank {
  font-size: var(--fs-xs); font-weight: 800; color: var(--text-dim);
  font-variant-numeric: tabular-nums; text-align: left;
}
#dr-friends .h2h-name { font-weight: 700; }

/* The "this was a daily" marker, used on history rows and the profile's Games
   list. One chip, one meaning, everywhere it appears. */
.daily-chip {
  display: inline-block; font-style: normal;
  font-size: var(--fs-xxs); font-weight: 800; letter-spacing: .03em;
  color: var(--accent); background: rgba(246,176,73,.13);
  border-radius: 99px; padding: 1px 7px; vertical-align: baseline;
}

/* The per-game hub's two halves. Bobby: "in the results page we should separate
   daily records from other records." Side by side and identically weighted,
   because they are two records of the same game, not a record and a footnote —
   and the header line above them states the total the two add up to. */
.gs-split { width: min(380px, 94vw); display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); margin-bottom: var(--sp-2); }
.gs-half {
  background: var(--surface); border-radius: var(--radius-m); box-shadow: var(--card-border);
  padding: 11px var(--sp-3) 12px; display: flex; flex-direction: column; gap: 1px; min-width: 0;
}
.gs-half b { font-size: var(--fs-xl); font-weight: 800; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; line-height: 1.05; }
.gs-k { font-size: var(--fs-xxs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-dim); }
.gs-s { font-size: var(--fs-xxs); color: var(--text-dim); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stat-rec.dim { color: var(--text-dim); opacity: .5; }

/* "Watch" on a golf scorecard row, for a round that carries a shot recording.
   Deliberately quiet — it sits inside a results table and must not out-shout
   the scores, which are what the page is for. */
.lb-watch {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 12px; font-weight: 700; color: var(--accent);
  background: rgba(246,176,73,.10); border: 0; border-radius: 99px;
  padding: 5px 11px; cursor: pointer; white-space: nowrap;
}
.lb-watch:active { transform: translateY(1px); }
.lb-watch .gicon { width: 14px; height: 14px; vertical-align: 0; }

/* Watch buttons live below the scorecard rather than in it — the table already
   fills a 402pt screen with name + a column per hole + total, and a sixth
   column put the button off the right edge where it could not be tapped. */
.watch-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

/* Leaving a replay. Sits above the safe area rather than at the very bottom,
   because on a notched iPhone the home indicator eats the last ~20pt and this
   is the only way out of a replay. */
/* Watching a round: a chip above Done, frosted like the HUD, with the shot
   count in the app's numerals and a hairline of progress along its foot. */
.golf-rp {
  /* bottom-RIGHT, stacked over Done: the golfer stands at the centre and
     left of the frame, and both controls used to sit on him */
  position: absolute; z-index: 6; right: 14px;
  bottom: calc(68px + env(safe-area-inset-bottom, 0px));
  display: inline-flex; align-items: center; gap: 8px; overflow: hidden;
  padding: 8px 15px 9px 11px; border-radius: 999px; color: var(--text); white-space: nowrap;
  background: rgba(11,23,16,.62);
  backdrop-filter: blur(16px) saturate(140%); -webkit-backdrop-filter: blur(16px) saturate(140%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.13), inset 0 1px 0 rgba(255,255,255,.08), 0 8px 18px rgba(0,0,0,.35);
  animation: dmIn .3s cubic-bezier(.2,.8,.3,1) both;
}
.golf-rp-ic { width: 14px; height: 14px; fill: var(--accent); stroke: none; }
.golf-rp-l { font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--text-dim); }
.golf-rp-n {
  font-family: 'Bricolage', 'Inter', -apple-system, sans-serif;
  font-size: 15px; font-weight: 800; letter-spacing: -0.01em; font-variant-numeric: tabular-nums;
}
.golf-rp-n i { font-style: normal; color: var(--text-dim); font-weight: 700; }
.golf-rp-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: rgba(255,255,255,.08); }
.golf-rp-bar span { display: block; height: 100%; background: var(--accent); transition: width .25s cubic-bezier(.2,.8,.3,1); }

.replay-exit {
  position: absolute; z-index: 6; right: 14px;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  font: inherit; font-size: 14px; font-weight: 800; color: #3C2504;
  background: linear-gradient(180deg, #FFC463, var(--accent-deep));
  border: 0; border-radius: 99px; padding: 11px 26px; cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 4px 14px rgba(0,0,0,.45);
}
.replay-exit:active { transform: translateX(-50%) translateY(1px); }

/* "REPLAY · Bobby" in a game HUD. Inherits .golf-hud span's size and spacing;
   the accent is what separates "you are watching this" from the live readouts
   beside it, which otherwise look identical. */
.replay-tag { color: var(--accent); font-weight: 800; letter-spacing: .02em; }

/* ===========================================================================
   THE FEED
   A post is a card in the app's own material: who, then the result as a chip
   set in the game's art, their words, and the replies under a hairline with
   the reply field last. The chip is the same component the compose sheet
   shows before you post, so what you saw is what they see.
   ===========================================================================*/
#screen-social { align-items: center; gap: var(--sp-2); }
/* right padding clears the fixed music toggle, as the profile header does */
.fd-head {
  width: min(380px, 94vw); display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: var(--sp-1); padding-right: 50px;
}
.fd-sub { font-size: var(--fs-xs); color: var(--text-dim); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#fd-list { width: min(380px, 94vw); display: flex; flex-direction: column; gap: 10px; }
#fd-more { width: min(380px, 94vw); margin-top: var(--sp-1); }
.post {
  background: var(--surface); border-radius: var(--radius-l); box-shadow: var(--card-shadow);
  padding: 13px 14px 12px; display: flex; flex-direction: column; gap: 10px;
  animation: rowIn .3s cubic-bezier(.2,.8,.3,1) both; animation-delay: calc(var(--i, 0) * 35ms);
}
.post-head { display: grid; grid-template-columns: 30px minmax(0,1fr) auto; align-items: center; gap: 10px; }
.post-av, .pm-avatar { line-height: 0; }
.post-av.is-link, .reply-av.is-link { cursor: pointer; }
.post-who { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.post-who b {
  font-size: var(--fs-s); font-weight: 800; letter-spacing: -0.01em;
  display: flex; align-items: baseline; gap: 7px; overflow: hidden; white-space: nowrap;
}
.post-who span { font-size: var(--fs-xxs); color: var(--text-dim); font-weight: 600; }
.post-text { font-size: var(--fs-s); line-height: 1.42; white-space: pre-wrap; overflow-wrap: anywhere; }

/* The result chip. background-color rather than the shorthand, so the
   .art-<type> image (declared earlier in this file) survives the cascade. */
.res-chip {
  display: grid; grid-template-columns: 56px minmax(0,1fr); gap: 12px; align-items: center; width: 100%;
  padding: 8px 12px 8px 8px; border-radius: var(--radius-m); text-align: left; color: inherit; font: inherit; border: 0;
  background: rgba(255,255,255,.05); box-shadow: inset 0 0 0 1px rgba(255,255,255,.07);
}
.res-chip.is-link { cursor: pointer; transition: transform .09s, background .12s; -webkit-tap-highlight-color: transparent; }
.res-chip.is-link:active { transform: scale(.985); background: var(--surface-2); }
.rc-art {
  position: relative; width: 56px; height: 56px; border-radius: 15px; overflow: hidden; isolation: isolate;
  background-color: #1d2f24; background-size: cover; background-position: center 40%; background-repeat: no-repeat;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
}
.rc-art::after { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, transparent 45%, rgba(6,12,9,.5)); }
.rc-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.rc-k { font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
.rc-v {
  font-family: 'Bricolage', 'Inter', -apple-system, sans-serif;
  font-size: 25px; font-weight: 800; letter-spacing: -0.03em; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.rc-v i {
  font-style: normal; font-family: 'Inter', -apple-system, sans-serif;
  font-size: var(--fs-xs); font-weight: 700; color: var(--text-dim); margin-left: 5px; letter-spacing: 0;
}
.rc-s { font-size: var(--fs-xs); font-weight: 600; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }
.rc-s b { font-weight: 800; color: var(--text); }
/* Tone lives in the numeral and the verdict, as on the post-game hero. */
.res-chip.is-win .rc-v, .res-chip.is-win .rc-s b { color: var(--success); }
.res-chip.is-tie .rc-v, .res-chip.is-solo .rc-v { color: var(--accent); }
.res-chip.is-loss .rc-s b { color: #E0787C; }
.res-chip.is-dnf .rc-v { font-family: inherit; font-size: var(--fs-xs); font-weight: 800; letter-spacing: .08em; color: var(--text-dim); }

/* Replies. */
.post-replies { display: flex; flex-direction: column; gap: 9px; padding-top: 10px; border-top: 1px solid rgba(255,255,255,.07); }
.reply { display: grid; grid-template-columns: 24px minmax(0,1fr) auto; gap: 9px; align-items: start; }
.reply-av { line-height: 0; }
.reply-body { min-width: 0; font-size: var(--fs-xs); line-height: 1.4; padding-top: 3px; overflow-wrap: anywhere; white-space: pre-wrap; }
.reply-body b { font-weight: 800; margin-right: 6px; }
.reply-side { display: flex; align-items: center; gap: 4px; padding-top: 3px; }
.reply-when { font-size: 10.5px; color: var(--text-dim); font-weight: 600; white-space: nowrap; }
.reply-form { display: grid; grid-template-columns: 24px minmax(0,1fr) 34px; gap: 9px; align-items: center; }
.reply-form .av { align-self: center; }
.reply-form input {
  min-width: 0; width: 100%; border: 0; border-radius: 999px; padding: 9px 13px; font: inherit; font-size: var(--fs-xs);
  color: var(--text); background: rgba(255,255,255,.07); box-shadow: inset 0 0 0 1px rgba(255,255,255,.08); outline: none;
  user-select: text; -webkit-user-select: text;
}
.reply-form input::placeholder { color: rgba(242,234,216,.42); }
.reply-form input:focus { box-shadow: inset 0 0 0 1px transparent, 0 0 0 2px rgba(246,176,73,.5); }
.reply-send {
  width: 34px; height: 34px; border: 0; border-radius: 50%; display: grid; place-items: center; cursor: pointer;
  color: var(--text-dim); background: var(--surface-2); box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
  transition: transform .07s, background .15s, color .15s, box-shadow .15s; -webkit-tap-highlight-color: transparent;
}
.reply-send .gicon { width: 16px; height: 16px; stroke-width: 2.2; vertical-align: 0; }
.reply-send:active { transform: scale(.92); }
/* Lights amber once there is something to send. */
.reply-form.is-ready .reply-send {
  color: #3C2504; background: linear-gradient(180deg, #FFC463, var(--accent-deep));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 2px 6px rgba(0,0,0,.3);
}

/* Delete: a quiet glyph that, once tapped, turns into a rose "Delete?" pill.
   The second tap is the real one; anywhere else, or 3.5s, disarms it. */
.post-del {
  display: inline-flex; align-items: center; gap: 4px; border: 0; background: none; cursor: pointer;
  padding: 5px; border-radius: 999px; color: rgba(242,234,216,.35); font: inherit;
  font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  transition: color .15s, background .15s, padding .15s; -webkit-tap-highlight-color: transparent;
}
.post-del .gicon { width: 15px; height: 15px; vertical-align: 0; }
.post-del span { display: none; }
.post-del.is-armed { color: var(--danger); background: rgba(226,92,72,.12); box-shadow: inset 0 0 0 1px rgba(226,92,72,.35); padding: 4px 9px 4px 6px; }
.post-del.is-armed span { display: inline; }
.reply .post-del { padding: 3px; }
.reply .post-del.is-armed { padding: 3px 8px 3px 5px; }

/* "Post to Feed" on a results screen: the ghost button with the feed's glyph. */
.res-actions .btn .gicon, #dr-post .gicon { width: 17px; height: 17px; margin-right: 8px; vertical-align: -3px; }

/* The compose sheet. Same card as the game entry sheet; the chip is the one
   the feed will render, and the text box is the only new part. Each block is
   written `.modal-card > .x` to out-specify the `.modal-card > div` rule that
   forces column-flex and centred text — see the note on .edit-social. */
.modal-card > .pm-result { display: block; text-align: left; margin-bottom: 12px; }
.modal-card > .pm-compose { display: grid; grid-template-columns: 30px minmax(0,1fr); gap: 10px; align-items: start; text-align: left; }
.pm-avatar { padding-top: 2px; }
#pm-text {
  width: 100%; min-height: 88px; resize: none; border: 0; border-radius: var(--radius-m); padding: 11px 13px;
  font: inherit; font-size: var(--fs-s); line-height: 1.42; color: var(--text);
  background: rgba(255,255,255,.07); box-shadow: inset 0 0 0 1px rgba(255,255,255,.08); outline: none;
  touch-action: auto; user-select: text; -webkit-user-select: text;
}
#pm-text::placeholder { color: rgba(242,234,216,.42); }
#pm-text:focus { box-shadow: inset 0 0 0 1px transparent, 0 0 0 2px rgba(246,176,73,.55); }
.modal-card > .pm-foot {
  display: flex; flex-direction: row; justify-content: space-between; gap: 10px; text-align: left;
  margin: 8px 2px 12px; font-size: var(--fs-xxs); color: var(--text-dim); font-weight: 600; font-variant-numeric: tabular-nums;
}
.pm-count.is-over { color: var(--danger); }
.post-sheet #pm-err { text-align: left; margin: -6px 0 10px; }
.post-sheet #pm-send { width: 100%; }
@media (prefers-reduced-motion: reduce) { .post { animation: none; } }

/* ===================== Desktop: 960px and up =====================
   The phone layout is the source of truth. This block re-flows it for a
   wide window: the tab bar becomes a top bar with the wordmark, single-column
   pages get a readable column instead of a phone-width ribbon, the profile
   and the two hero screens (post-game, daily) go two-column with the hero
   and its actions pinned on the left. The .res-col / .pf-col wrappers in
   index.html exist only for this: below the breakpoint they are
   display:contents, so the phone layout is exactly what it was before them. */
.res-col, .pf-col { display: contents; }
.res-col > *, .pf-col > * { flex-shrink: 0; }

@media (min-width: 960px) {
  :root { --col: 640px; --col-wide: 760px; --nav-h: 60px; }

  /* --- navigation: a top bar --- */
  #tabbar {
    top: 0; bottom: auto; height: var(--nav-h);
    flex-direction: row; align-items: center; justify-content: center; gap: 6px;
    padding: 0 24px; border-top: 0; border-bottom: 1px solid rgba(255,255,255,.08);
    box-shadow: 0 8px 24px rgba(0,0,0,.25);
  }
  #tabbar::before {
    content: 'GameRoom'; position: absolute; left: 28px; top: 50%; transform: translateY(-50%);
    font-size: 20px; font-weight: 800; letter-spacing: -0.02em; color: var(--text);
  }
  #tabbar button {
    flex: 0 0 auto; min-height: 0; height: 40px; padding: 0 16px 0 12px; border-radius: 999px;
    flex-direction: row; gap: 8px; font-size: var(--fs-s); font-weight: 600;
    transition: color .18s ease, background .18s ease;
  }
  #tabbar button:hover { color: var(--text); background: rgba(255,255,255,.05); }
  #tabbar button.active { background: rgba(246,176,73,.12); letter-spacing: 0; }
  #tabbar .ticon { width: 20px; height: 20px; margin: 0; }
  .music-toggle { top: 10px; right: 20px; width: 40px; height: 40px; }

  /* content sits under the bar, not above a bottom bar */
  .screen { padding-top: calc(var(--nav-h) + 36px); padding-bottom: 56px; }
  .top-screen, #screen-daily, #screen-results { padding-top: calc(var(--nav-h) + 28px); }
  .screen.home { padding-top: calc(var(--nav-h) + 36px); }
  .screen-nav { width: min(var(--col), 94vw); padding-right: 0; }

  /* --- Feed --- */
  .fd-head, #fd-list, #fd-more { width: min(var(--col), 94vw); }
  .fd-head { padding-right: 0; }

  /* --- Results tab --- */
  #screen-feed h2 { width: min(var(--col-wide), 94vw); }
  #feed-list, .feed-streaks, .feed-filter { width: min(var(--col-wide), 94vw); }
  .seg { width: 400px; align-self: flex-start; margin-left: calc((100% - min(var(--col-wide), 94vw)) / 2); }
  .feed-chips { width: min(var(--col-wide), 94vw); flex-wrap: wrap; overflow: visible; }

  /* --- game hub, other players --- */
  #screen-gamestats h2, #gs-sub, #screen-gamestats h3, .gs-split, #gs-actions, #gs-rivals, #gs-history, #gs-more, #records { width: min(var(--col), 94vw); }
  #gs-sub { text-align: left; }
  .pl-body, #pl-head { width: min(var(--col), 94vw); }

  /* --- Profile: identity and record left, rivals, games and badges right --- */
  #screen-stats {
    display: grid; grid-template-columns: minmax(320px, 400px) minmax(0, 560px); column-gap: clamp(24px, 4vw, 48px);
    justify-content: center; align-content: start; align-items: start;
  }
  .pf-col { display: flex; flex-direction: column; align-items: stretch; gap: 14px; min-width: 0; }
  .pf-col-a { position: sticky; top: calc(var(--nav-h) + 28px); }
  #screen-stats .pf-head, #screen-stats .pf-record, #screen-stats .pf-h,
  #screen-stats .pf-block, #screen-stats .pf-badges, #screen-stats .me-line { width: 100%; }
  #screen-stats .pf-head { padding-right: 0; }
  #screen-stats .pf-col-b .pf-h:first-child { margin-top: 0; }

  /* --- post-game and daily: hero and actions left, the record right --- */
  #screen-results, #screen-daily {
    display: grid; grid-template-columns: minmax(340px, 440px) minmax(0, 600px); column-gap: clamp(24px, 4vw, 44px);
    justify-content: center; align-content: start; align-items: start;
  }
  .res-col { display: flex; flex-direction: column; align-items: stretch; gap: 14px; min-width: 0; }
  .res-col-a { position: sticky; top: calc(var(--nav-h) + 28px); }
  .res-hero { width: 100%; max-width: 100%; border-radius: 28px; min-height: 380px; padding-top: 48px; }
  .res-actions, .res-duel, .res-card, .res-h2h, #word-lists, .dr-chart, .dr-tiles, #dr-friends { width: 100%; }
  #word-lists { margin-top: 0; }
}
