/* ============================================================
   NFL BIG PICKER — STYLE #1: "Scoreboard"
   ------------------------------------------------------------
   A dark, stadium-under-the-lights aesthetic: charcoal base,
   turf-green accent, red for wrong picks specifically.

   TYPOGRAPHY RULE (revised — supersedes the original Archivo
   Narrow system used when this file was first built):
     - OSWALD = identity/label text. Anything naming a thing —
       team names, matchup text ("BUF @ KC"), player/room names,
       page and section titles, modifier labels (LOCK, BLOWOUT,
       etc.), nav branding. Primary/most-important instances
       (e.g. a selected team) are bold (600); secondary instances
       (e.g. a modifier tag) are regular/medium (400-500) with
       WIDER letter-spacing than the bold instances — this is a
       deliberate way to visually rank importance without relying
       on color alone.
     - INTER = every number. Points, scores, percentages, ±N
       modifier values, ranks, win totals. Numbers were tried in
       Oswald/Archivo Narrow and read worse — Inter's numerals are
       clearer at small sizes. This is a hard rule: if it's a
       digit-driven value, it's Inter, full stop, even inside an
       otherwise-Oswald component.
   Any class below still importing 'Archivo Narrow' is a bug —
   flag it for cleanup rather than assuming it's intentional.

   Origin: this is the exact CSS proven in the Room Stats page
   mockups (stats_page_mockup.html), lifted out as-is so it can
   be linked as a stylesheet instead of copy-pasted inline. Class
   names are unchanged from the mockup — dropping this file in
   and pointing existing markup at it should require no HTML
   changes.

   HOW TO USE / HOW TO ADD MORE STYLES LATER
   ------------------------------------------------------------
   1. Save this as e.g. /static/css/themes/style-1-scoreboard.css
   2. In your Flask base template, load ONE theme file based on a
      config value, e.g.:
        <link rel="stylesheet"
              href="{{ url_for('static', filename='css/themes/' + THEME_STYLESHEET) }}">
      where THEME_STYLESHEET is set once in config
      (e.g. THEME_STYLESHEET = "style-1-scoreboard.css"). Switching
      the active site style becomes a one-line config change.
   3. When you design "Style #2", copy this file's STRUCTURE
      (section order, the same class names: .table-card,
      .streak-pill, .points-cell, etc.) and change only the
      VALUES — different colors, different fonts, different
      radii. Because markup only ever references class names
      (never raw colors), templates never need to change when you
      swap which style file is active.
   4. Everything under "Section 11: Generic UI" below did NOT come
      from the Stats page mockup — it's a starting point for
      buttons/inputs elsewhere on the site, added in the same
      token system so it stays visually consistent. Expand this
      section as more of the site gets built in this style.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');

/* ---------- 1. Design tokens ---------- */
:root,
[data-theme="style-1"] {
  --bg: #14171a;
  --surface: #1b1f22;
  --surface-raised: #21262a;
  --hairline: #2b3135;
  --text-primary: #ece9e2;
  --text-secondary: #8b9298;
  --text-tertiary: #5c6469;
  --positive: #5fb87a;
  --positive-dim: #3a5e46;
  --negative: #e8a33d;
  --negative-dim: #7a5a2b;
  --accent-field: #2f6b45;
  --gold: #d68e28;  /* richer gold, warmed further toward orange (was #d4af37 -> #c8a02a -> #cf972a) -- money/status accent site-wide */

  /* Reserved for pass/fail grading only (correct vs. wrong pick) —
     distinct from --negative (amber), which stays the color for
     "unfavorable stat" contexts (Modifier tab misses, streak pills,
     negative point totals). A graded right/wrong result is a
     different kind of signal than a stat trending down, so it gets
     its own color rather than overloading amber for both meanings. */
  --danger: #e2504a;
  --danger-dim: #7a2f2c;

  /* "At stake" orange -- its OWN meaning (upside points on the line for a
     pick-in-progress), deliberately distinct from every existing token: not
     --gold (#d68e28, money/status), not --negative (#e8a33d, unfavorable stat),
     not --danger (#e2504a, wrong pick), not --positive green (selected). A
     brighter, more saturated orange so it reads clearly as its own signal. */
  --at-stake: #ff7a2f;
}

/* ---------- 2. Reset & base ---------- */
* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  background: var(--bg);
  color: var(--text-primary);
  font-family: 'Inter', sans-serif;
  min-height: 100vh;
  padding: 32px 20px 80px;
  /* Site-wide +30% (2026-08-09): on a real desktop monitor the whole
     app was reading small, with a lot of dead space around a
     tight-looking column of content -- `zoom` (not `transform: scale`)
     bumps every px value in this file at once -- fonts, padding, gaps,
     radii, icons, everything -- without having to touch each of the
     hundreds of individual declarations by hand. `zoom` genuinely
     participates in layout (unlike `transform`), so sticky-column
     offsets computed from real offsetWidth in JS (Scoreboard/Results-
     style pages) stay correct automatically. */
  zoom: 1.3;
}

/* MOBILE RULE: never carry the +30% onto small screens. Every mobile
   layout in this file (tables, pick grids, emblem cards) was verified
   to already fit exactly at 100% on a 375px viewport -- stacking a 30%
   zoom on top would shrink the *effective* layout width to ~288px
   (375 / 1.3), which is what real desktop content was designed around,
   not a phone. Desktop has room to spare; phones don't -- so phones
   keep the scale that was already tuned for them. 768px matches this
   codebase's own mobile/tablet/desktop breakpoint convention. */
@media (max-width: 768px) {
  body { zoom: 1; }
}

.wrap { max-width: 1080px; margin: 0 auto; }

/* ---------- 3. Page header ---------- */
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  padding-bottom: 20px;
  margin-bottom: 24px;
  border-bottom: 2px solid var(--hairline);
}

.eyebrow {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-weight: 600;
  margin-bottom: 6px;
}

.room-name {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 34px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.header-meta {
  text-align: right;
  font-size: 12px;
  color: var(--text-secondary);
}
.header-meta strong { color: var(--text-primary); }

/* ---------- 4. Tabs ---------- */
.tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--hairline);
}

.tab {
  background: none;
  border: none;
  color: var(--text-secondary);
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 10px 16px 12px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.tab:hover { color: var(--text-primary); }

.tab.active {
  color: var(--text-primary);
  border-bottom-color: var(--positive);
}

.panel { display: none; }
.panel.active { display: block; animation: fadein 0.2s ease; }

@keyframes fadein {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---------- 5. Table shell ---------- */
.table-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  overflow: hidden;
  margin-bottom: 28px;
}

/* MOBILE (2026-08-12): the content cards break out of <main>'s 12px side
   padding to span the FULL screen width -- negative margins to the exact
   edges, square corners, no side borders (nothing needs to show the rounded
   edges on a phone). Vertical margins are preserved. Matches main's 12px pad
   in style.css; keep the two in sync. */
@media (max-width: 768px) {
  .table-card,
  .picks-table-card,
  .scoring-explainer {
    margin-left: -12px;
    margin-right: -12px;
    border-radius: 0;
    border-left-width: 0;
    border-right-width: 0;
  }
}

/* Charcoal override for the base style.css `.card` (a leftover blue-theme
   surface). Only used on Room Info's tier-upgrade prompt today, but this
   keeps any `.card` on a Style #1 page on-palette instead of navy. */
.card { background: var(--surface); border-color: var(--hairline); }

.table-card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--hairline);
}

.table-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 16px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.table-sub {
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 600;
  color: var(--text-tertiary);
  margin-top: 2px;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

thead th {
  text-align: right;
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-weight: 600;
  padding: 10px 14px;
  border-bottom: 1px solid var(--hairline);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}
thead th:first-child, thead th:nth-child(2) { text-align: left; }
thead th:hover { color: var(--text-secondary); }
thead th.sorted { color: var(--positive); }
thead th .arrow { font-size: 9px; margin-left: 3px; opacity: 0.7; }

tbody tr {
  border-bottom: 1px solid var(--hairline);
  transition: background 0.12s ease;
}
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: var(--surface-raised); }

td {
  padding: 12px 14px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
td:first-child, td:nth-child(2) { text-align: left; }

.rank-cell {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  color: var(--text-tertiary);
  width: 36px;
}
.rank-cell.top3 { color: var(--gold); }

.player-cell {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  /* The base stylesheet's global th/td rule sets a border-bottom on
     every cell; a flex-display td doesn't stretch to the row's full
     height the way a normal table-cell box does, so that per-cell
     border rendered as a short stray line under just the name instead
     of lining up with the row's own divider. */
  border-bottom: none;
}

/* Room Info's Members section -- name is identity text (Oswald, same
   rule as team/player/room names everywhere else), email is data (Inter,
   same rule as every number). The Commissioner badge reuses --gold
   rather than introducing a new color, matching the top-3 rank
   highlight's use of gold for "special status" elsewhere on Stats. */
.member-name { font-family: 'Oswald', sans-serif; font-weight: 600; font-size: 15px; }
.commissioner-badge {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 5px;
  background: rgba(212,175,55,0.15);
  color: var(--gold);
  font-family: 'Inter', sans-serif;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
}
.member-email { font-family: 'Inter', sans-serif; font-size: 13px; color: var(--text-secondary); }

/* ---------- 6. Data display primitives ---------- */
/* Every number in the Overview row -- Points, Record, Win %, Streak,
   Best/Worst Wk -- shares this one size (16px) so the row reads as a
   single consistent scale instead of Points standing out arbitrarily
   larger than the stats next to it. */
.points-cell {
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 16px;
}
/* Room Stats Overview: negative Points is red (--danger), not amber --
   THIS table only. Zero/positive keep the neutral inherited td color
   (positive is left untouched per spec). Career Stats and the Scoreboard
   Season Total keep their own gold/amber treatment elsewhere. */
.points-cell.neg { color: var(--danger); }

.record {
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  font-size: 16px;
}
.record .w { color: var(--positive); font-weight: 600; }
/* Losses are ALWAYS neutral white here (never red/gold), regardless of
   count -- deliberately easier to scan than the Points/Best/Worst rule. */
.record .l { color: var(--text-primary); font-weight: 600; }
.record .m { color: var(--text-tertiary); }

.winpct-label { font-size: 16px; color: var(--text-secondary); }

.streak-pill {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 5px;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.streak-pill.w { background: rgba(95,184,122,0.15); color: var(--positive); }
.streak-pill.l { background: rgba(232,163,61,0.15); color: var(--negative); }

/* Best/Worst Wk carry only sizing now; color comes from the value-sign
   classes below (positive green / negative red / zero neutral), applied
   per row via the stats.html sign_class() macro. */
.best { font-weight: 600; font-size: 16px; }
.worst { font-weight: 600; font-size: 16px; }

/* Value-sign coloring for the Room Stats Overview (Best/Worst Wk). Red
   is --danger to match the Points rule; zero is plain neutral text so it
   reads as neither a win nor a loss. */
.val-pos { color: var(--positive); }
.val-neg { color: var(--danger); }
.val-zero { color: var(--text-primary); }

/* ---------- 7. Modifier breakdown cells ---------- */
.tag-group-head {
  display: flex;
  align-items: baseline;
  gap: 6px;
  justify-content: center;
}
.tag-code {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.04em;
}
.tag-name { font-size: 9.5px; color: var(--text-tertiary); text-transform: none; letter-spacing: 0; }

.tag-stat {
  font-size: 12px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  line-height: 1.2;
}
.tag-record { color: var(--text-tertiary); font-size: 11px; }
.tag-net { font-family: 'Inter', sans-serif; font-weight: 800; font-size: 16px; }
.tag-net.pos { color: var(--positive); }
.tag-net.neg { color: var(--negative); }
.tag-none { color: var(--text-tertiary); font-size: 12px; }

/* ---------- 8. Legend / footnote row ---------- */
.legend {
  display: flex;
  gap: 18px;
  padding: 12px 20px;
  font-size: 11px;
  font-style: italic;
  color: var(--text-tertiary);
  border-top: 1px solid var(--hairline);
  flex-wrap: wrap;
}
.legend strong { color: var(--text-secondary); }

/* ---------- 9. Trend / sparkline rows ---------- */
.trend-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 18px 20px;
  margin-bottom: 14px;
  display: flex;
  align-items: center;
  gap: 20px;
}

.trend-player {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 170px;
  flex-shrink: 0;
  font-weight: 600;
  font-size: 13px;
}

.trend-spark-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}

/* margin-bottom (not padding) reserves flow space below the chart for
   the point-value labels without changing what the dots'/labels' own
   top:N% resolve against -- percentages on an absolutely-positioned
   child are computed off the frame's padding box, so padding would
   have shifted every dot up; margin sits outside that box and doesn't. */
.trend-spark-frame { position: relative; margin-bottom: 34px; }
.trend-spark { height: 46px; width: 100%; display: block; overflow: visible; }

/* Plain HTML dot, not an SVG <circle> -- the chart's viewBox stretches
   non-uniformly to fill the container (much more horizontally than
   vertically), which would squash an SVG circle into an ellipse. A
   CSS circle placed by percentage isn't subject to that distortion. */
.trend-dot-marker {
  position: absolute;
  top: 0; left: 0;
  width: 3px; height: 3px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 1.5px var(--surface);
  cursor: default;
}
.trend-dot-marker.current { width: 5px; height: 5px; }
.trend-dot-marker.pos { background: var(--positive); }
.trend-dot-marker.neg { background: var(--negative); }
.trend-dot-marker.zero { background: var(--text-secondary); }

/* Sits directly under its own dot (same left/top, pushed down past the
   dot's radius) rather than in a shared row at a fixed height -- a dot
   near the top of the chart and one near the bottom both need their
   value right next to them, not equidistant from a common baseline. */
.trend-point-label {
  position: absolute;
  top: 0; left: 0;
  transform: translate(-50%, 7px);
  font-family: 'Inter', sans-serif;
  font-size: 7px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A single connected line (stock-ticker style) rather than discrete
   per-week bars -- one polyline per unbroken run of weeks the player
   has data for (a gap only appears if they joined mid-season), plus a
   dot on every point. Colored by season-to-date sign, same rule as
   .trend-total, so the line's color always agrees with the number
   sitting next to it. */
.trend-line {
  fill: none;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.trend-line.pos { stroke: var(--positive); }
.trend-line.neg { stroke: var(--negative); }

.trend-weeknums {
  display: flex;
  justify-content: space-between;
  font-size: 10px;
  font-family: 'Inter', sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--text-tertiary);
  margin-top: 6px;
}
.trend-weeknums span { width: 14px; text-align: center; }
.wn-pos { color: var(--positive); }
.wn-neg { color: var(--negative); }

.trend-total-wrap {
  flex-shrink: 0;
  text-align: right;
}
.trend-total {
  text-align: right;
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 18px;
}
.trend-total-label {
  margin-top: 3px;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
  white-space: nowrap;
}

/* MOBILE (2026-08-12 sweep): the 170px fixed name column squeezed the graph
   into ~45px on a phone -- unreadable. REFLOW (cascade step 3): the player
   name + season total share the top row, and the graph drops to its own
   full-width row below, so it's the focus and finally readable. */
@media (max-width: 500px) {
  .trend-card { flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 14px 12px; }
  .trend-player { width: auto; order: 1; }
  .trend-total-wrap { order: 2; margin-left: auto; text-align: right; }
  .trend-spark-wrap { order: 3; flex: 1 1 100%; }
}

.trend-week-labels {
  display: flex;
  justify-content: space-between;
  padding: 0 20px 20px;
  font-size: 10px;
  color: var(--text-tertiary);
  max-width: 1080px;
}
.trend-week-labels span { width: 14px; text-align: center; }

/* ---------- 10. Footer ---------- */
footer {
  text-align: center;
  color: var(--text-tertiary);
  font-size: 11px;
  margin-top: 20px;
  letter-spacing: 0.02em;
}

/* ============================================================
   Section 11: Generic UI
   ------------------------------------------------------------
   Not derived from the Stats page — a starting point for
   applying this style to the rest of the site (buttons, forms).
   Uses the same tokens above, so it stays consistent. Expand
   this section as more pages adopt Style #1.
   ============================================================ */

/* Buttons are green OUTLINES, not filled -- matching the ROOMS/STATS/PROFILE
   nav pills. Secondary (.btn): green border, white Oswald label. Primary
   (.btn-primary): same outline but a green label for extra emphasis. Both fill
   green on hover. */
.btn {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 9px 18px;
  border-radius: 6px;
  border: 1px solid var(--positive);
  background: transparent;
  color: var(--text-primary);
  cursor: pointer;
  transition: all 0.15s ease;
}
.btn:hover { background: var(--positive-dim); }

.btn-primary {
  background: transparent;
  border-color: var(--positive);
  color: var(--positive);
}
.btn-primary:hover { background: var(--positive); color: var(--bg); }

.btn-danger {
  background: var(--negative-dim);
  border-color: var(--negative);
  color: var(--negative);
}
/* .btn-small carries full styling and is defined AFTER .btn-danger, so a
   `btn-small btn-danger` pairing would otherwise lose the danger colors to
   .btn-small at equal specificity -- re-assert them here (0,2,0) so a small
   destructive button (e.g. Delete room) still reads danger-colored. */
.btn-small.btn-danger {
  background: var(--negative-dim);
  border-color: var(--negative);
  color: var(--negative);
}

/* Room sub-nav (Make Picks / Scoreboard / Stats / Room Info). Desktop: an
   inline row. Mobile: an even 2x2 grid so the four tabs are equal, tappable
   rectangles stacked two-up (Make Picks + Scoreboard / Stats + Room Info). */
.room-subnav { margin-bottom: 16px; display: flex; flex-wrap: wrap; gap: 10px; }
/* Frosted-glass treatment on the INACTIVE sub-nav tabs (2026-09-10), so the
   whole row reads glassy -- a faint tint + top rim over the green outline.
   Scoped to .room-subnav so the global outlined .btn is untouched. The ACTIVE
   tab (below) keeps its stronger solid-green gloss, so it still stands out as
   "you are here". Hover still fills green (kept explicitly so the frost doesn't
   win the cascade). The top site nav (Rooms/Stats/Profile) is left flat on
   purpose -- the room tabs are the interactive layer. */
.room-subnav .btn {
  background-color: rgba(255,255,255,0.05);
  background-image: linear-gradient(180deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.0) 52%, rgba(0,0,0,0.10) 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.18), 0 2px 7px rgba(0,0,0,0.22);
}
.room-subnav .btn:hover { background-color: var(--positive-dim); }
/* Active sub-nav tab (Make Picks / Scoreboard / Stats / Room Info): stays
   filled dark green until you navigate to a different tab. */
.room-subnav .btn.active {
  /* Glass sheen on the ACTIVE sub-nav tab only (2026-09-10): a top-down
     highlight over the green fill, a thin inset top rim, and a soft drop shadow
     so it reads as raised. NO hover sweep (this is chrome, kept calm); only one
     tab is ever active, so it reinforces "you are here" rather than competing.
     Deliberately scoped to the room sub-nav -- not the top site nav, and never
     the Make Picks team/booster pills (kept flat on purpose). */
  background-color: var(--positive-dim);
  background-image: linear-gradient(180deg, rgba(255,255,255,0.26) 0%, rgba(255,255,255,0.05) 48%, rgba(0,0,0,0.14) 100%);
  border-color: var(--positive);
  color: #ffffff;  /* green fill -> bright white letters (rule 2026-08-13) */
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.38), 0 3px 10px rgba(0,0,0,0.35);
}
@media (max-width: 500px) {
  .room-subnav { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .room-subnav .btn { text-align: center; padding: 12px 8px; }
}

/* .btn-small is usually used standalone (Admin's row-action buttons --
   Save/Delete/Finalize/Reopen), not paired with .btn -- the base
   stylesheet's `button, .btn` rule covers plain <button> elements, but
   this file only ever styled the `.btn` class, so a lone `.btn-small`
   fell through to the old theme's blue. Full button styling here, not
   just size, so it never depends on also carrying `.btn`. */
.btn-small {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 13px;
  padding: 5px 12px;
  border-radius: 6px;
  border: 1px solid var(--hairline);
  background: var(--surface-raised);
  color: var(--text-primary);
  cursor: pointer;
}
.btn-small:hover { border-color: var(--text-tertiary); }
/* A button carrying BOTH .btn and .btn-small (Account Settings, Log out, Edit
   Picks, Done editing, Regenerate link) keeps the green .btn outline at all
   times. .btn-small (defined after .btn, equal specificity) otherwise mutes the
   border to hairline gray; this 0,2,0 rule wins without touching the standalone
   muted .btn-small buttons (admin row actions) that never carry .btn. */
.btn.btn-small { border-color: var(--positive); }
.btn.btn-small:hover { border-color: var(--positive); background: var(--positive-dim); }

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
select,
textarea {
  font-family: 'Inter', sans-serif;
  font-size: 15px;
  color: var(--text-primary);
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  border-radius: 6px;
  padding: 9px 12px;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--positive);
}

/* Flat wrapping list of week links (Admin's week pickers) -- deliberately
   NOT named .week-nav, which Section 13 below already claims for a
   different component (prev/next arrows around a single centered label).
   Reusing that name here would have made every week past the first
   overflow off-screen, since that rule has no flex-wrap and centers a
   fixed small set of children instead of wrapping an open-ended list. */
.week-links-list { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.week-links-list a {
  padding: 6px 12px;
  border-radius: 6px;
  border: 1px solid var(--hairline);
  background: var(--surface-raised);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 600;
}
.week-links-list a:hover { color: var(--text-primary); border-color: var(--text-tertiary); }
.week-links-list a.active { background: var(--positive-dim); border-color: var(--positive); color: #ffffff; }  /* green fill -> bright white (rule 2026-08-13) */

/* Long-form prose pages (Terms, Privacy) -- the universal `* { margin:0 }`
   reset at the top of this file means plain h1/h2/p/ul/li have zero
   default spacing, which reads fine for tightly-composed UI components
   but crushes a wall of legal paragraphs into an unreadable block. This
   restores a normal reading rhythm without hand-annotating every single
   element with an inline margin. */
.prose h1 { margin-bottom: 10px; }

/* Make Picks page title ("[Room] · Week N Picks"): Oswald bold, matching
   the team-name typeface -- but deliberately NOT uppercased (team names
   are Oswald bold + uppercase; this keeps its normal title case). Only
   the typeface/weight change; size and color stay inherited. */
.picks-page-title { font-family: 'Oswald', sans-serif; font-weight: 700; margin-bottom: 16px; }
/* The PLAYER half of the title takes the warm orange-gold accent (2026-08-16),
   so "whose picks" reads out of the line at a glance while the room name and
   week stay primary text. Reuses --gold, the accent already carrying this page
   ("BONUS POINT BOOSTERS", "N of 3 Locks used") -- deliberately NOT --at-stake
   (#ff7a2f), which is reserved for the potential-points indicator alone; see
   the design-system note in CLAUDE.md. */
.picks-page-player { color: var(--gold); }
.prose h2 { margin-top: 26px; margin-bottom: 8px; font-size: 19px; }
.prose h2:first-of-type { margin-top: 20px; }
.prose p { margin-top: 10px; line-height: 1.55; }
.prose ul { margin-top: 8px; padding-left: 22px; }
.prose li { margin-top: 6px; line-height: 1.5; }
.prose li:first-child { margin-top: 0; }

/* ============================================================
   Section 12: Compact stat blocks
   ------------------------------------------------------------
   For aggregate/summary stats shown as a few big tiles rather
   than a full table — e.g. a "Career Stats" card on a Profile
   page. Distinct from the table-based components above (which
   are for room-scoped, multi-player comparisons); this is for
   single-player, cross-room summaries.
   ============================================================ */

.stat-block-row {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  padding: 24px 20px;
}

.stat-block { min-width: 90px; }

.stat-block-label {
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-weight: 600;
  margin-bottom: 6px;
}

.stat-block-value {
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 28px;
  line-height: 1;
}
.stat-block-value.neg { color: var(--negative); }

.stat-block-sub {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: 4px;
}

.modifier-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  padding: 20px;
}

.modifier-chip { min-width: 80px; }

.modifier-chip-head {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 6px;
}
.modifier-chip-head .tag-code { font-size: 13px; }
.modifier-chip-head .tag-name { font-size: 10px; }

.modifier-chip-pct {
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 20px;
}

.modifier-chip-sub {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-top: 2px;
}

/* ============================================================
   Section 13: Week navigation
   ------------------------------------------------------------
   Prev/next week arrows, used on the Scoreboard. (This section
   originally held a full duplicate pick-grid implementation too
   — .pick-team, .game-cell, .week-summary-row, etc. That was a
   parallel, older build of the same page Section 17 now owns.
   The duplicate classes were removed entirely rather than kept
   "just in case," since two differently-named systems for the
   same page is exactly the kind of ambiguity that caused a real
   mix-up this session. Section 17 is the only pick-grid system —
   use .gc-pick, .matchup-text, .matchup-header, .week-total-row,
   .season-total-row, not any pick-team/game-cell-style names.)
   ============================================================ */

.week-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin-bottom: 20px;
}

.week-nav-arrow {
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  color: var(--text-secondary);
  width: 32px;
  height: 32px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s ease;
}
.week-nav-arrow:hover:not(:disabled) { color: var(--text-primary); border-color: var(--text-tertiary); }
.week-nav-arrow:disabled { opacity: 0.3; cursor: not-allowed; }

.week-nav-label {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  min-width: 130px;
  text-align: center;
}
.week-nav-label .current-badge {
  display: block;
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 9.5px;
  letter-spacing: 0.1em;
  color: var(--positive);
  margin-top: 2px;
}

/* ============================================================
   Section 14: Site navigation
   ------------------------------------------------------------
   Top nav bar, shared across every page. Not derived from the
   Stats mockups — first defined here since it's genuinely global
   chrome.
   ============================================================ */

/* TRANSPARENT since the wallpaper went site-wide (2026-08-16): the solid bar
   + hairline read as a lid sitting on top of the photo. Dropping both lets the
   logo and pills float directly over it. The pills carry their own borders and
   the logo is bold white, so nothing depends on the bar for legibility. */
.site-nav {
  background: transparent;
  /* Explicit: style.css (the fallback layer, loaded BEFORE this file) also
     sets .site-nav's border-bottom, so simply omitting it here leaves the
     hairline in place -- confirmed by computed style. */
  border-bottom: none;
  padding: 16px 24px;
}
.site-nav-inner {
  /* Match <main>'s desktop max-width (style.css) so the logo/nav align with the
     content edges and scale with it. Was a hardcoded 1200px, which only
     coincided with the content width near ~1280px; now it tracks main exactly
     at every width (content scaled to 0.80x / 20% thinner, 2026-08-15). */
  max-width: calc(60vw + 192px);
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.site-nav-logo {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 16px;
  letter-spacing: 0.03em;
  display: flex;
  align-items: center;
  gap: 8px;
}
.site-nav-links {
  display: flex;
  gap: 28px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.site-nav-links a {
  color: #ffffff;  /* Rooms / Stats / Profile: bright white (matches active/admin pills) */
  text-decoration: none;
  font-family: 'Oswald', sans-serif;
  font-size: 16px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transition: color 0.15s ease;
}
.site-nav-links a:hover { color: var(--text-primary); }
/* Selected/active pill has a green fill -> letters stay bright white (rule
   2026-08-13): white reads cleaner on the dark-green fill than green-on-green. */
.site-nav-links a.active { color: #ffffff; }

/* ============================================================
   Section 15: Full-cell pick coloring (results tables)
   ------------------------------------------------------------
   For results-style tables where the ENTIRE cell is tinted by
   correct/wrong (not just the text, as in .points-cell etc.
   above). Used on room results / weekly grade pages.
   ============================================================ */

td.pick-correct { background: rgba(95,184,122,0.14); color: var(--positive); font-weight: 700; }
td.pick-wrong { background: rgba(226,80,74,0.14); color: var(--danger); font-weight: 700; }
.pick-tag-inline { font-size: 11px; font-weight: 600; opacity: 0.85; margin-left: 2px; }

/* ============================================================
   Section 16: Notable pick/week tiles
   ------------------------------------------------------------
   For single-record highlights -- one specific pick (biggest
   hit/miss) or one specific week's total (best/worst week) -- as
   distinct from the season-wide aggregate stats (.stat-block
   above). .notable-tile-pick is only used by the pick-level
   highlights (there's no single "pick" to name for a whole week);
   the other classes are shared by both (2026-08-09).
   ============================================================ */

.notable-row {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  padding: 22px 20px;
}

.notable-tile { flex: 1; min-width: 180px; }

.notable-tile-label {
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-weight: 600;
  margin-bottom: 6px;
}

.notable-tile-value {
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 26px;
  line-height: 1;
}
.notable-tile-value.positive { color: var(--positive); }
.notable-tile-value.negative { color: var(--danger); }

.notable-tile-pick {
  font-size: 13px;
  font-weight: 600;
  margin-top: 6px;
}

.notable-tile-context {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-top: 2px;
}

/* ============================================================
   Section 17: Scoreboard pick grid (three-color system)
   ------------------------------------------------------------
   Games as rows, players as columns — the live/social Scoreboard
   page inside a Room. Deliberately only THREE colors for pick
   judgment, decided after trying a four-color per-modifier
   scheme that read as too busy:
     --pending   = locked, game in progress, no result yet
     --positive  = correct (green)
     --danger    = wrong (red)
   Modifier tags (X/Y/Z/O) are ALWAYS rendered in plain muted gray
   (.gc-pick-tag), never colored by hit/miss — the team code's
   color already tells you that; the tag only needs to say WHICH
   modifier was used. Do not reintroduce per-modifier hues here.

   Game column (team names): NO real logos without confirmed
   licensing — using team logos/marks without a license is
   trademark/copyright infringement regardless of the "not
   affiliated with the NFL" disclaimer elsewhere in the app; the
   two are unrelated protections. Two supported presentations:
     .matchup-text   = default fallback: plain "AWAY @ HOME" text,
                       no circles, no team colors. Use this unless
                       and until real licensed logos are secured.
     .team-badge /
     .matchup-badges = reserved for future licensed logos (a
                       small badge/crest per team). Not in active
                       use — do not fill these with generated
                       placeholder colors again; that was the
                       "random colors" version this replaced.
   ============================================================ */

:root { --pending: #9a9fa3; }
/* --pending is neutral gray — NOT blue-tinted. Deliberately distinct
   from --text-secondary/--text-tertiary (UI/label grays) so a pending
   pick reads as its own state, not just dim text. */

.matchup-text {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.matchup-header {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 6px 4px;
}
/* .matchup-status holds the kickoff time / live status / final score —
   stays Inter (not Oswald) since a final score is numbers-first. */
.matchup-status { font-size: 11px; color: var(--text-tertiary); font-family: 'Inter', sans-serif; }
.matchup-status.final strong { color: var(--text-primary); font-weight: 600; }
/* The winning team code in the final-score line -- gold so it pops against
   the muted score. Only the actual winner (a TIE result keeps neutral). */
.matchup-status strong.matchup-winner { color: var(--gold); font-weight: 700; }

.team-badge {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 10px;
  color: var(--bg);
  flex-shrink: 0;
}
.matchup-badges { display: flex; align-items: center; gap: 4px; }

/* Player column headers — NAMES ONLY, no avatar. An avatar circle was
   tried here and explicitly removed: "remove the avatar line, just the
   names is fine." Do not reintroduce a .player-avatar circle in this
   header — if avatars are wanted on some OTHER page later, that's a
   new, separate decision, not a default to fall back on here. */
.player-col-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}
.player-col-header .name {
  font-family: 'Oswald', sans-serif;
  /* Unbold + italic (2026-09-11, Montana) -- the bold uppercase name read too
     heavy against the muted rank beneath it. */
  font-weight: 400;
  font-style: italic;
  font-size: 15.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* Standings rank under each player's name (1st = most points). A number-
   driven value, so Inter, muted -- the name above it carries the identity. */
.player-col-header .player-rank {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 11px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

/* "Week winner" + "season leader" callouts, stacked and centered above the
   player names. White Oswald sentence; the player's name and the point number
   carry the accent color that separates the two lines -- GOLD for the weekly
   winner, GREEN for the season leader, matching the gold Week Total / green
   Season Total rows at the bottom of the board (one color language). The two
   lines sit side by side on the same row as the week dropdown. */
.scoreboard-week-winner,
.scoreboard-leader {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  letter-spacing: 0.02em;
  font-size: 18px;
  color: var(--text-primary);
  text-align: center;
}
/* Name: Oswald caps, accent-colored per line. */
.scoreboard-week-winner strong,
.scoreboard-leader strong {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.scoreboard-week-winner strong { color: var(--gold); }
.scoreboard-leader strong { color: var(--positive); }
/* Point number: Inter (house rule), matching accent color per line. */
.scoreboard-week-winner .lead-pts,
.scoreboard-leader .lead-pts { font-family: 'Inter', sans-serif; }
.scoreboard-week-winner .lead-pts { color: var(--gold); }
.scoreboard-leader .lead-pts { color: var(--positive); }

/* The bar holding the week dropdown + the two callouts. On mobile it was
   eating too much height above the grid -- shrink its padding and the callout
   text so the actual scoreboard is the priority, not the leader lines. */
/* Symmetric top/bottom padding (2026-09-11): the week dropdown is absolutely
   positioned (centers in the padding box) while the leader lines are flex-
   centered (in the content box) -- with the old 22/6 asymmetry those two
   centers didn't match, so the dropdown floated ~10px above the leader text.
   Equal top/bottom makes both centers coincide, so the dropdown lines up with
   the leaders (1 or 2 lines). Mobile keeps its own override below. */
.scoreboard-topbar { padding: 16px 20px; display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
/* The two leader callouts, stacked and each auto-shrunk to fit (fitLeaderLines()
   in scoreboard.html). width:100% + nowrap + overflow:hidden lets the fit script
   measure real overflow (scrollWidth vs clientWidth) and shrink the font to fit. */
.scoreboard-leaders { display: flex; flex-direction: column; align-items: stretch; gap: 2px; min-width: 0; flex: 1 1 auto; max-width: 100%; }
.scoreboard-leaders > span { display: block; width: 100%; text-align: center; white-space: nowrap; overflow: hidden; }
/* Desktop/tablet: center the winner/leader line across the full width by
   pinning the week dropdown to the left edge (out of the flex flow) so it
   doesn't push the messages off-center. */
@media (min-width: 501px) {
  /* Reserve room on the LEFT for the absolutely-positioned week dropdown so the
     leaders start just past it (never tuck under it); a light right pad keeps
     it from hugging the edge. fitLeaderLines() shrinks within this width. */
  .scoreboard-topbar { position: relative; justify-content: center; padding-left: 165px; padding-right: 24px; }
  .scoreboard-topbar #scoreboard-week-select {
    position: absolute;
    left: 20px;
    top: 50%;
    transform: translateY(-50%);
    margin: 0;
  }
}
@media (max-width: 500px) {
  .scoreboard-topbar { padding: 10px 8px 4px; gap: 6px 14px; }
  .scoreboard-week-winner,
  .scoreboard-leader { font-size: 12.5px; }
  .scoreboard-week-winner .lead-pts,
  .scoreboard-leader .lead-pts { font-size: 12.5px; }
  #scoreboard-week-select { font-size: 15px !important; }
}

/* The Stakes (2026-09-14, Montana): same Oswald/size as the card title and on
   the title's vertical line (top:16px = the header's padding-top), but
   horizontally CENTERED where the leader line below sits. Absolute so it
   doesn't shift the title's own layout; pointer-events:none since it's a label. */
.scoreboard-stakes {
  position: absolute;
  top: 16px;
  left: 0;
  right: 0;
  text-align: center;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 16px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--gold);
  pointer-events: none;
}
@media (min-width: 501px) {
  /* Match the topbar's 165px left reserve (for the week dropdown) so the stakes
     centers over the SAME region as the leader line, directly above it. */
  .scoreboard-stakes { padding-left: 165px; padding-right: 24px; }
}
@media (max-width: 500px) {
  /* Phone: the title fills the row, so drop the stakes into its own centered
     line below the header rather than overlapping the title. */
  .scoreboard-stakes { position: static; top: auto; padding: 0 12px 8px; }
}

/* Player pick cells center-align (overriding Section 5's default
   text-align:right for generic td) -- every cell here carries
   data-user-id, which the Game/Winner pinned column never does, so this
   can't accidentally catch that column too. */
#scoreboard-table td[data-user-id] { text-align: center; }

.gc-pick {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 16px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.gc-pick.correct { color: var(--positive); }
.gc-pick.wrong { color: var(--danger); }
.gc-pick.pending { color: var(--pending); }
/* "No pick yet" before kickoff (2026-09-04): dimmed to secondary gray so a
   real staged pick (bright --text-primary) stands out against the unfilled
   cells. Deliberately NOT --pending -- that gray means "locked, awaiting
   verdict"; an empty pre-lock cell is a different fact. */
.gc-pick.empty { color: var(--text-secondary); }
/* A wrong PLAIN pick nets 0 -- graded, but no points awarded or subtracted,
   so it's the neutral gray "No Change" (same --pending gray as the legend
   swatch), not the red "loss" verdict. */
.gc-pick.even { color: var(--pending); }
.gc-pick-tag {
  font-family: 'Oswald', sans-serif;
  font-weight: 400;
  font-size: 12px;
  color: var(--text-tertiary);
  margin-left: 2px;
  letter-spacing: 0.02em;
}
.gc-hidden { color: var(--text-tertiary); font-size: 14px; }
/* A pick concealed until its game kicks off (pick-privacy, 2026-09-09). Muted
   like gc-hidden but its own class so the padlock can size independently. */
.gc-concealed { color: var(--text-tertiary); font-size: 12px; opacity: 0.75; }
/* Team logo above the abbreviation in each Scoreboard pick cell (2026-09-10).
   Small; object-fit:contain so the varied logo shapes fit one height. Only
   rendered for real team picks (not TIE/miss/concealed -- see scoreboard.html). */
.gc-pick-logo {
  display: block;
  height: 26px;
  width: auto;
  max-width: 42px;
  object-fit: contain;
  margin: 0 auto 3px;
}
/* Team code + its modifier tags share ONE line under the logo, tags BESIDE the
   code (2026-09-11, Montana -- boosters next to the team name, not beneath it).
   FIXED height (min-height + line-height), so a cell carrying (X)/(Y) is the
   same height as one with no booster and the logos line up across the row
   regardless of who added a modifier. white-space:nowrap keeps the code+tags on
   the single reserved line rather than wrapping and growing the cell. */
.gc-pick-codeline {
  display: block;
  white-space: nowrap;
  min-height: 18px;
  line-height: 18px;
  text-align: center;
}
/* With a logo above it, the abbreviation reads smaller (2026-09-10, Montana:
   -3px). Scoped to the real scoreboard (#scoreboard-table) so the landing
   marketing showcase (.lp-scoreboard, no logos) keeps the original 16px. */
#scoreboard-table .gc-pick { font-size: 13px; }

/* Hover-to-reveal-points: for a GRADED pick only (state correct/wrong --
   never pending or pre-kickoff, since there's no point value yet for
   those), hovering the cell swaps the team+tags display for the exact
   points that pick scored, colored the same green/red as the win/loss
   verdict itself. .gc-pick/.gc-pick-tag go visibility:hidden (not
   display:none) so they keep occupying their normal layout space --
   .gc-pick-points is absolutely positioned over that same footprint,
   so nothing shifts or reflows on hover. Touch/mobile has no hover
   state, so this is desktop-only by nature, same as the Picks page's
   team-option hover -- there's no tap-triggered equivalent needed.

   Trigger is the whole <td>, not just this inline wrap (2026-08-08,
   Montana) -- hovering anywhere in the cell reveals the points, so the
   cursor never has to sit directly on top of the (small) text to see
   them. */
.gc-pick-hover-wrap { position: relative; display: inline-flex; flex-direction: column; align-items: center; justify-content: center; }
/* Desktop reveals the points on hover; mobile has no hover, so a press-and-
   hold on a cell adds .points-held (JS in scoreboard.html) for the same
   reveal. Both drive the identical swap below. */
td[data-user-id]:hover .gc-pick-hover-wrap .gc-pick,
td[data-user-id]:hover .gc-pick-hover-wrap .gc-pick-tag,
td[data-user-id]:hover .gc-pick-hover-wrap .gc-pick-logo,
td[data-user-id].points-held .gc-pick-hover-wrap .gc-pick,
td[data-user-id].points-held .gc-pick-hover-wrap .gc-pick-tag,
td[data-user-id].points-held .gc-pick-hover-wrap .gc-pick-logo { visibility: hidden; }
.gc-pick-points {
  display: none;
  position: absolute;
  inset: 0;
  align-items: center;
  justify-content: center;
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 16px;
  font-variant-numeric: tabular-nums;
}
.gc-pick-points.positive { color: var(--positive); }
.gc-pick-points.negative { color: var(--danger); }
.gc-pick-points.even { color: var(--pending); }
td[data-user-id]:hover .gc-pick-hover-wrap .gc-pick-points,
td[data-user-id].points-held .gc-pick-hover-wrap .gc-pick-points { display: flex; }

/* Color-key swatches — replaces the earlier approach of naming colors
   in a sentence ("green = correct"). A small colored square reads
   faster and doesn't require the color to also be described in words. */
.table-sub-swatches { display: flex; align-items: center; gap: 14px; margin-top: 6px; flex-wrap: wrap; }
.swatch-item { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--text-tertiary); font-family: 'Inter', sans-serif; }
.swatch-box { width: 9px; height: 9px; border-radius: 2px; flex-shrink: 0; }

/* Two footer rows, same visual weight — NOT a primary/secondary pair.
   Week Total is what just happened; New Season Total is the running
   total including this week. Both must be derived from the same
   per-player-per-week computation used on the Stats pages (see
   STATS_BUILD_PROMPT) — never a separately-tracked running counter
   that could drift out of sync with what Stats shows. */
.week-total-row td,
.season-total-row td {
  background: var(--surface-raised);
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 16px;
}
/* Negative totals in these two rows read as RED (--danger), not the amber
   --negative the generic .negative utility uses elsewhere -- scoped here so
   the global utility is unaffected. */
.week-total-row td.negative,
.season-total-row td.negative { color: var(--danger); }
/* A POSITIVE Week Total is GOLD -- deliberately distinct from the green
   positive Season Total right below it, so the "this week" row pops against
   the "running total" row. Scoped to the week row only; Season Total positives
   stay green via the generic .positive. */
.week-total-row td.positive { color: var(--gold); }
.week-total-row td { border-top: 2px solid var(--hairline); }
/* td.total-row-label, not .total-row-label -- .week-total-row td above is
   (0,1,1), one class more specific than a bare .total-row-label (0,1,0),
   so without the extra `td` here its own font/size would silently lose
   the cascade on the very cell it's meant to style. */
td.total-row-label {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-tertiary);
  text-align: left;
}
/* Apply .positive / .negative to each total cell based on that row's own
   value -- not a fixed color per row. These were referenced by comment
   here before actually existing as generic utilities; --negative (amber),
   not --danger (red), matches how a total is a stat trend, not a graded
   pick verdict -- same distinction the design tokens at the top of this
   file already draw for e.g. .points-cell.neg. */
.positive { color: var(--positive); }
.negative { color: var(--negative); }

/* ============================================================
   Section 18: Sticky Game/You columns (Room Scoreboard)
   ------------------------------------------------------------
   2026-08-05 UX note: in a wide room, a player scrolling right to
   compare against everyone else was losing track of both which
   game a row was for AND their own picks. Fix: the Game column
   and the viewer's own column both stay pinned on the left while
   every other member's column scrolls independently underneath.
   The pinned offset is set via a CSS var (see rooms/scoreboard.html's
   script) computed from the Game column's *actual* rendered width --
   a hardcoded px guess drifts out of sync with real content/font
   metrics, same reasoning as the Winner column's offset on the
   Results page (app/static/css/style.css's .col-pinned), which this
   mirrors exactly.

   2026-08-05 follow-up: the viewer's own column also gets a light
   background tint (--surface-raised, the same tone already used for
   row hover elsewhere) so the eye lands on it immediately -- the
   header now shows the player's real name rather than literally
   "You", so the highlight is what actually signals "this one's mine."

   2026-08-05 second follow-up: that highlight is now its own class
   (.gc-highlighted), independent of pinning -- clicking any other
   player's header moves the spotlight there instead (see
   rooms/scoreboard.html's script). Game and the viewer's own column
   stay pinned/sticky regardless of which column is currently
   highlighted -- pinning is a fixed navigational aid, highlighting is
   a togglable comparison spotlight; they're related but not the same
   thing, and defaulting them together (viewer's own column starts
   both pinned AND highlighted) was the only place they overlapped.
   ============================================================ */

.gc-pinned {
  position: sticky;
  z-index: 1;
  background: var(--surface);
}
.gc-pinned-game { left: 0; }
.gc-pinned-you {
  left: var(--you-col-left, 140px);
  box-shadow: 2px 0 0 var(--hairline);
}
tbody tr:hover .gc-pinned { background: var(--surface-raised); }
tfoot .gc-pinned { background: var(--surface-raised); }

.gc-highlighted { background: var(--surface-raised); }

/* MOBILE (2026-08-12 sweep): on a phone the Game column + each player column
   were too wide, so only 1-2 players fit before the (correctly pinned) Game +
   You columns. Thin everything -- smaller cell padding + fonts, and cap the
   pinned Game column width -- so several players are visible at once. The
   --you-col-left offset is JS-computed from the Game column's real rendered
   width, so capping it keeps the sticky "You" column correctly placed. Placed
   after the grid base rules so it wins on source order. Points are revealed on
   mobile by press-and-hold (see .points-held above + scoreboard.html). */
@media (max-width: 500px) {
  #scoreboard-table th, #scoreboard-table td { padding: 7px 5px; }
  .gc-pinned-game { max-width: 84px; }
  .matchup-text { font-size: 11.5px; letter-spacing: 0.02em; }
  .matchup-status { font-size: 9px; }
  .player-col-header .name { font-size: 9.5px; letter-spacing: 0.02em; }
  .player-col-header .player-rank { font-size: 9px; }
  .gc-pick { font-size: 12.5px; }
  .gc-pick-tag { font-size: 9px; }
  .gc-pick-codeline { min-height: 16px; line-height: 16px; }
  .gc-pick-points { font-size: 12.5px; }
  /* Suppress the iOS long-press text-selection callout while holding a cell. */
  #scoreboard-table td[data-user-id] { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
}

/* ============================================================
   Section 19: Picks table (THE Picker interface — final)
   ------------------------------------------------------------
   Where a player selects a team, TIE, and modifier for every game
   in the week. This replaces an earlier "card per game" Picker
   design entirely (the old .pick-card/.select-pill/.modifier-card
   system has been removed, not just deprecated — it was a real
   design that got tried, reacted to, and superseded through a
   long iteration; keeping it around invites Code to build the
   wrong one). If anything ever references .pick-card, .select-pill,
   .modifier-card, .team-pick-row, or .pick-matchup, that's stale
   and should be migrated to the classes below.

   KEY DECISIONS, so future changes don't accidentally reverse them:

   1. TEAM SELECTION — full-fill highlight, not outline-only:
      .team-option.selected gets a tinted background AND border,
      not just a border. Unselected team names are BRIGHT
      (--text-primary), not dim — team choice is the primary
      decision on this page, so it gets the most contrast. Only
      the SELECTED team turns green; the unselected option next to
      it stays bright white, not dimmed — the fill/border alone
      carries the "which one is picked" signal.

   2. "PREDICT TIE" IS NOT A MODIFIER. It's a third pick option,
      same tier as the two teams, mutually exclusive with picking
      either team. It scores ±10 (scoring_engine.py) — a much
      bigger swing than any modifier (±1 to ±3) — and its ±10
      value is shown under the label for exactly that reason: to
      remind the player how much bigger this swing is. Do not
      move TIE into the modifier pill row.

   3. MODIFIERS ARE DELIBERATELY LESS BOLD than the team names —
      Oswald weight 400-500 vs. the team names' 600, plus wider
      kerning (0.09em vs. 0.04-0.05em elsewhere) — a visual way of
      saying "this is the secondary decision," reinforcing the
      hierarchy through weight instead of relying on color alone.
      Do not bump modifier weight up to match team names.

   4. POINT VALUES use ± NOTATION (e.g. "±2"), not "+2 / -2".
      This is exact, not a simplification — every current modifier
      (X, Y, Z, O) is symmetric (hit and miss are equal magnitude).
      If an asymmetric modifier is ever added, ± stops being
      accurate for that one and must revert to spelling both signs
      out. Point values are Inter (numbers, not identity text),
      same color as the label text next to them — not bold, not a
      separate/brighter color from the label.

   5. KICKOFF TIME lives on EACH game row (.pick-row-kickoff), not
      once in a shared page header — every game in a week's slate
      legitimately kicks off at a different time, so a single
      header time would be misleading.

   6. HOVER vs. SELECTED must be two distinct shades of the same
      green, not two different colors — close enough to read as
      related states, different enough that a player can't mistake
      "I'm hovering" for "I already picked this." This matters more
      here than on purely cosmetic pages since real stakes (entry
      fees) are attached to this specific screen. --positive-hover
      below is a lighter tint for that purpose; --positive itself
      is reserved for the actual selected/committed state. Hover
      has no equivalent on touch/mobile — the tap-to-select
      feedback there should just show the full selected fill
      immediately, not attempt to simulate a hover step.
   ============================================================ */

:root { --positive-hover: rgba(95,184,122,0.28); }

.picks-table-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  overflow: hidden;
}

.picks-table-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 18px 22px;
  border-bottom: 1px solid var(--hairline);
}
.picks-table-week {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 23px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* The WEEK N heading doubles as the week switcher: a <select> carrying
   .picks-table-week (for the font) + .week-jump, which strips native select
   chrome and adds a caret so it still reads as the heading, just clickable.
   The caret color is the literal --text-primary value (SVG data URIs can't
   read CSS vars). */
.week-jump {
  appearance: none;
  -webkit-appearance: none;
  /* Hug the WEEK N text (override the base select { width:100% }) so the
     caret sits right after the heading, not across the row. */
  width: auto;
  max-width: 100%;
  flex: 0 0 auto;
  background-color: transparent;
  border: none;
  color: var(--text-primary);
  cursor: pointer;
  margin: 0;
  padding: 0 27px 0 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23ece9e2' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0 center;
  background-size: 17px;
}
.week-jump:focus { outline: none; }
.week-jump:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 4px; }
/* Dropdown list items render in OS chrome -- keep them legible in dark mode
   and drop the heading's uppercase/spacing/size so the list reads normally. */
.week-jump option {
  color: var(--text-primary);
  background: var(--surface);
  font-size: 15px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
}
.locks-used-counter {
  font-size: 13px;
  letter-spacing: 0.05em;
  color: var(--text-tertiary);
  font-weight: 600;
  text-transform: uppercase;
  font-family: 'Inter', sans-serif;
}
.locks-used-counter strong { color: var(--gold); font-family: 'Inter', sans-serif; }

/* "N of M picked" progress counter (2026-08-16). Deliberately styled as a TWIN
   of .locks-used-counter, not as a callout: it's orientation for a player
   returning mid-week ("where did I leave off?"), never a nudge -- Auto-Pick,
   the submit-time missing-picks modal and the reminder emails already own
   prompting. Numeral is Inter per the numbers rule. Neutral while incomplete
   (it is not a warning) and green once every eligible game is picked, which is
   the only state worth actively signalling. */
.picks-made-counter {
  font-size: 13px;
  letter-spacing: 0.05em;
  color: var(--text-tertiary);
  font-weight: 600;
  text-transform: uppercase;
  font-family: 'Inter', sans-serif;
}
.picks-made-counter strong { color: var(--text-secondary); font-family: 'Inter', sans-serif; }
.picks-made-counter.complete strong { color: var(--positive); }
/* Autosave reassurance line (2026-08-16), directly under the picks-table
   header. Quiet by construction: --text-tertiary fine print, Inter (it's prose,
   not an identity label), and the ONLY color is the green check -- reusing
   --positive, no new token. Deliberately static: a per-save "Saved!" flash
   would announce an event repeatedly across 16 games AND would have to lie,
   since autosave fails silently by design (Submit stays the guaranteed path).
   A permanent statement of how the page works answers the real question --
   "am I safe if I leave?" -- once, before the first pick is even made. */
.autosave-note {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 22px;
  border-bottom: 1px solid var(--hairline);
  font-family: 'Inter', sans-serif;
  font-size: 12.5px;
  color: var(--text-tertiary);
}
.autosave-note-check { color: var(--positive); font-size: 14px; line-height: 1; }
@media (max-width: 560px) {
  .autosave-note { padding: 9px 16px; font-size: 12px; }
}

/* The header now carries three items in both modes (week selector + this +
   Locks used / Edit Picks). At 375px that lands within ~1px of the available
   width, which would overflow on a 320px device -- let it wrap instead of
   pushing the row sideways. Measured, not guessed. */
@media (max-width: 560px) {
  .picks-table-header { flex-wrap: wrap; row-gap: 8px; }
}

.pick-row { padding: 18px 22px; border-bottom: 1px solid var(--hairline); }
.pick-row:last-of-type { border-bottom: none; }
.pick-row-kickoff {
  font-size: 12.5px;
  /* EXPERIMENTAL (2026-08-15, Montana may revert): kickoff time brightened 50%
     from --text-tertiary (#5c6469) -> #8a969e. Set as a value (not a filter) so
     the gold .matchup-winner child keeps its own color. Revert = var(--text-tertiary). */
  color: #8a969e;
  letter-spacing: 0.02em;
  margin-bottom: 8px;
  font-family: 'Inter', sans-serif;
  font-weight: 500;
}
/* The winning team code in a final-game's score line (submitted-picks summary
   + locked rows) -- gold so it pops, matching the Scoreboard. TIE stays
   neutral (rendered as a plain <strong>). */
.pick-row-kickoff .matchup-winner { color: var(--gold); }

.team-option-row { display: flex; gap: 10px; margin-bottom: 12px; }

.team-option {
  flex: 1;
  min-width: 0; /* override the flex default (min-width:auto), which
    would otherwise let the nowrap .team-option-name inside refuse to
    shrink below its full-text width and force the whole row to overflow
    -- see the auto-fit script in picks/week.html, which depends on this
    box actually being allowed to compress so its real available width
    is what gets measured. */
  /* Vertical stack (2026-09-10): team LOGO on top, then the name, then the
     W-L record -- all centered. Was a horizontal name+record row before logos. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 6px;
  padding: 12px 10px;
  border-radius: 8px;
  /* Unselected border is the team's outline color (set inline as
     --team-border per pill; see picks.week_view + nfl_teams.game_pill_borders),
     falling back to the neutral hairline when unset (e.g. the landing
     preview). hover/.selected below override border-color to green at
     higher specificity, so the team color only shows on a resting,
     unselected pill -- never on hover or the selected state. */
  border: 1px solid var(--team-border, var(--hairline));
  background: var(--surface-raised);
  cursor: pointer;
  transition: all 0.15s ease;
}
/* Hover is scoped to real-mouse pointers ONLY. On touch, :hover STICKS after a
   tap until you tap something else -- so a deselected pill kept the lighter
   --positive-hover tint and read as "still selected in a secondary state."
   That's exactly what rule 6 in this section's header calls for ("Hover has no
   equivalent on touch/mobile"); it just was never enforced. Same @media
   (hover: hover) guard the Submit button already uses (see Section 19 footer). */
@media (hover: hover) {
  .team-option:hover { background: var(--positive-hover); border-color: var(--positive); }
}
/* Selected pick: SOLID green fill + bright white text (rule 2026-08-13, same
   as the nav/tab active state) -- a picked team reads as "filled green," not a
   faint tint, and its name stays white rather than turning green-on-green. */
.team-option.selected {
  background: var(--positive-dim);
  border-color: var(--positive);
}
/* Team logo above the name. object-fit:contain so the varied aspect ratios
   (wide Seahawks/Rams, tall Vikings) each fit a fixed HEIGHT without stretching.
   pointer-events:none so taps land on the label, not the image. */
.team-option-logo {
  height: 40px;
  max-width: 82%;
  object-fit: contain;
  display: block;
  pointer-events: none;
}
.team-option-name {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 18px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-primary);
  /* Full pill width in the column so the auto-fit script (see picks/week.html)
     measures the real available width via clientWidth and squeezes long names
     (e.g. "Jacksonville Jaguars") down to fit one line, rather than overflowing. */
  align-self: stretch;
  width: 100%;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
}
.team-option.selected .team-option-name { color: #ffffff; }
.team-option.selected .team-option-record { color: rgba(255,255,255,0.72); }
.team-option-record { font-size: 13px; color: var(--text-tertiary); font-family: 'Inter', sans-serif; text-align: center; }

/* DESKTOP (2026-08-13): larger team names, CENTERED in the pill, with the pill's
   footprint UNCHANGED -- a fixed height + no vertical padding so the bigger text
   just centers in the same box (auto-fit cap raised to 24 in picks/week.html).
   The W-L record tucks into the right corner (absolute) so the name centers
   cleanly. Stroke bumped 1px -> 2px. Scoped to >=501px so the mobile 2-line
   pill is untouched. */
@media (min-width: 501px) {
  /* Desktop: the vertical logo/name/record stack, taller than the old
     fixed-height horizontal pill to give the logo room. Record now sits
     under the name (no longer absolute-cornered). */
  .team-option {
    padding: 14px 16px;
    border-width: 2px;
  }
  .team-option-logo { height: 56px; }
  .team-option-name { text-align: center; }
}

/* Full team name ("Jacksonville Jaguars") on desktop; the abbreviation
   code ("JAX") below the same ~500px breakpoint the emblem row (Section
   21) uses. A full name plus the "@" plus the Predict TIE box just
   don't fit on one row on a phone screen even shrunk to a legible font
   floor -- confirmed on a real 375px viewport (it collapsed to single
   letters) -- so this swaps the CONTENT instead of keeping the point
   further; the auto-fit script in picks/week.html still runs harmlessly
   on top of whichever text is showing. */
.team-name-short { display: none; }
@media (max-width: 500px) {
  .team-name-full { display: none; }
  .team-name-short { display: inline; }

  /* PICKS PAGE (2026-08-13): show the FULL name stacked on two centered lines
     (CHICAGO / BEARS -- city on top, mascot below) instead of the abbreviation.
     Scoped to .pick-row so the landing hero preview (which reuses these classes
     with plain text) keeps its short-code behavior from the rules just above.
     The auto-fit script shrinks .team-option-name until the wider of the two
     lines fits, so long names (SAN FRANCISCO / 49ERS) stay on one line each. */
  .pick-row .team-name-full { display: block; }
  .pick-row .team-name-short { display: none; }
  .pick-row .team-name-city,
  .pick-row .team-name-mascot { display: block; line-height: 1.02; }

  /* Even with just a 2-4 letter code, the ROW itself was too tight on a
     real 375px phone: .tie-option's fixed padding/font (never designed
     to share a row with two other flex items at this width) ate most of
     the space, squeezing both .team-option boxes down to a few px of
     actual content room -- confirmed by measuring clientWidth directly,
     not just eyeballing it. Shrinks the fixed-size neighbors so the two
     team boxes actually get enough of the row to work with.

     BUG FIX (2026-08-12 mobile sweep): .team-option-at / .tie-option /
     .tie-option-points are scoped to `.team-option-row` here so these
     mobile shrinks (0,2,0) beat their own BASE rules, which are defined
     LATER in this section (0,1,0) and would otherwise win on source
     order -- leaving the tie box full-size and collapsing the two team
     pickers on a phone. .team-option / .team-option-record don't need
     the scope: their base rules sit BEFORE this block, so source order
     already lets these win. */
  /* Squarer team pills with the name CENTERED: stack the name over the record
     and add vertical padding so each pill reads more square on a phone.
     (Predict TIE left this row, so the two team pills now split it evenly.) */
  .team-option {
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: 20px 8px;
    gap: 3px;
  }
  .team-option-name { justify-content: center; text-align: center; width: 100%; }
  .team-option-record { margin-left: 0; }
  .team-option-row .team-option-at { font-size: 11px; }
}

.team-option-at {
  flex: 0 0 auto;
  align-self: center;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 13px;
  color: var(--text-tertiary);
}

/* Predict TIE is a PICK, not a booster -- pulled OUT of the booster grid and
   rendered as a small, understated toggle on its own line under the boosters
   (2026-08-13). Ties are rare in the NFL, so this keeps the option available
   without the visual weight of a full-size pill. It's inline-flex + content-
   width so it sits on its own short line (the grid before it is a block).
   Selection state (.selected) is still driven by the same syncSelectedClasses()
   JS as the team picks, so no JS change was needed. */
/* Predict TIE shares the booster grid's bottom row with None -- same-size pill
   as the four boosters above (2026-08-13). It stays a .tie-option (NOT a
   .modifier-pill) so the "TIE picked -> boosters disabled" rule leaves it
   interactive. Its box sizing (width/min-height/column-centering) comes from
   the shared `.modifier-pill-row .tie-option` rule below; this block is just
   the border/background/label styling, matched to .modifier-pill. */
.tie-option {
  border-radius: 7px;
  border: 1px solid var(--hairline);
  background: var(--surface-raised);
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.15s ease;
  gap: 3px;
}
/* Real-mouse pointers only -- see the .team-option:hover note above. */
@media (hover: hover) {
  .tie-option:hover { border-color: var(--positive); background: var(--positive-hover); }
}
/* Selected: solid green fill + white text, same rule as teams/boosters. */
.tie-option.selected { background: var(--positive-dim); border-color: var(--positive); }
.tie-option-text {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.tie-option.selected .tie-option-text { color: #ffffff; }
.tie-option-points {
  font-family: 'Inter', sans-serif;
  font-weight: 400;
  font-size: 13px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.tie-option.selected .tie-option-points { color: #ffffff; }

/* NOTE: the sizes below (134px modifier pills + 72px NONE + 13/15px
   labels) are tuned for the DESKTOP container width. The row (4x134 + 72
   + 4x8 gaps ~= 640px) fits on one line down to ~1024px viewport, but
   still wraps to a second line on portrait tablets/phones (~<=768px).
   That narrow-width wrap is a KNOWN, ACCEPTED limitation to be handled in
   the planned responsive/mobile sweep -- e.g. smaller pills and/or a
   multi-row layout at narrower breakpoints. There's no modifier-pill rule
   in the @media (max-width:500px) block yet; add the responsive overrides
   there (and/or a new intermediate breakpoint) when that pass happens.
   Do not shrink these values piecemeal before then. */
.modifier-row-label {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 8px;
}
/* Desktop: center the label over the centered pill grid (mobile keeps it
   left-aligned above its full-width 2-col grid) and enlarge it as a title. */
@media (min-width: 501px) {
  .modifier-row-label { text-align: center; font-size: 15px; }  /* 11 -> 15 (+4px) */
}
/* All 6 pills (Lock/Steady/Blowout/Nailbiter + Predict TIE + None) in one
   uniform grid. Desktop: 3 columns x 2 rows filled COLUMN-first, so each column
   is a pair -- Lock/Steady, Blowout/Nailbiter, TIE/None -- and every pill is the
   same size. */
.modifier-pill-row {
  display: grid;
  grid-template-columns: repeat(3, 134px);
  grid-template-rows: repeat(2, auto);
  grid-auto-flow: column;
  gap: 8px;
  justify-content: center;   /* desktop: center the 6-pill grid under the teams
                                (mobile overrides to a full-width 2-col grid) */
  align-items: stretch;
}
/* Every pill is the EXACT same box: same width (fills its cell), same height
   (min-height), same centered content -- so all six read perfectly uniform,
   whether Lock/Steady/Blowout/Nailbiter, Predict TIE, or None. */
.modifier-pill-row .modifier-pill,
.modifier-pill-row .tie-option {
  width: auto;
  min-height: 62px;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.modifier-pill {
  flex: 0 0 134px;   /* the four scored modifiers share one width, not
                        content-sized; NONE is overridden narrower below */
  width: 134px;
  padding: 8px 12px;
  border-radius: 7px;
  border: 1px solid var(--hairline);
  background: var(--surface-raised);
  cursor: pointer;
  text-align: left;
  transition: all 0.15s ease;
}
/* Real-mouse pointers only -- see the .team-option:hover note above. */
@media (hover: hover) {
  .modifier-pill:hover { background: var(--positive-hover); border-color: var(--positive); }
}
/* Selected booster: SOLID green fill + bright white label/points (rule
   2026-08-13, same as teams + nav tabs). */
.modifier-pill.selected {
  border-color: var(--positive);
  background: var(--positive-dim);
}
.modifier-pill-label {
  display: block;
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  font-size: 13px;   /* Lock/Steady/Blowout/Nailbiter all one size; longest
                        label ("NAILBITER (Z)") fits the 134px pill here. */
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 3px;
}
/* NONE fills its grid cell like every other pill, so it's the SAME SIZE as
   Predict TIE beside it in the bottom row (2026-08-13, per Montana -- was a
   deliberately-narrower 72px box before). Its label stays 15px (short word). */
.modifier-pill-none .modifier-pill-label { font-size: 15px; }
/* None has no points -- drop the empty &nbsp; points placeholder so "None" is
   truly centered (vertically + horizontally), not nudged high by an invisible
   second line. */
.modifier-pill-none .modifier-pill-points { display: none; }
.modifier-pill.selected .modifier-pill-label { color: #ffffff; }
.modifier-pill-points {
  display: block;
  font-family: 'Inter', sans-serif;
  font-weight: 400;
  font-size: 13px;
  color: var(--text-secondary);
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
}
.modifier-pill.selected .modifier-pill-points { color: #ffffff; }
/* "PTS" unit reuses the How-Scoring-Works .pts-unit treatment (muted,
   secondary), sized down here since these values (13/12px) are smaller
   than the values it sits beside in that section. */
.modifier-pill-points .pts-unit,
.tie-option-points .pts-unit { font-size: 9px; }

/* Desktop: booster pill text +4px (label/points 13 -> 17, None 15 -> 19) so the
   pills read larger; grid columns widened so the longest label ("NAILBITER (Z)")
   still fits one line. The row stays centered (justify-content: center above). */
@media (min-width: 501px) {
  .modifier-pill-row { grid-template-columns: repeat(3, 176px); }
  .modifier-pill-label,
  .modifier-pill-points,
  .tie-option-text,
  .tie-option-points { font-size: 17px; }
  .modifier-pill-none .modifier-pill-label { font-size: 19px; }
}

/* Disabled-state treatment for modifier pills -- not in the original
   mockup (which only showed selected/unselected/hover), but needed to
   implement two real scoring_engine.py rules: a TIE pick can't carry
   any modifier (so the whole row disables when TIE is selected), and
   Lock (X) has a weekly cap of 3 uses (so individual Lock pills disable
   once a player hits it). Same opacity/no-interaction treatment already
   used for a disabled .week-nav-arrow elsewhere in this file. */
.pill-row-disabled .modifier-pill,
.modifier-pill.pill-disabled {
  opacity: 0.35;
  pointer-events: none;
}

/* MOBILE (2026-08-12 sweep): the four scored pills are a fixed 134px each, so
   on a phone they wrap raggedly (2 fit, 2 spill onto ragged extra rows). This
   is a REFLOW (cascade step 3), not a reshape -- the pills stay pills, they
   just fill the row 2-across so the wrap looks intentional. All six pills
   (incl. Predict TIE + None in the bottom row) are the same size now. Placed
   AFTER the base .modifier-pill rules above so it wins on source
   order (same lesson as the .tie-option fix in Section 19). No font shrink
   needed -- a ~50%-width pill is wider than the 134px it replaces, so the
   longest label still fits at 13px. */
@media (max-width: 500px) {
  /* Mobile: 2 columns filling the width, row-first (Lock Steady / Blowout
     Nailbiter / TIE None) -- the 2-up layout already approved on phones. */
  .modifier-pill-row {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: none;
    grid-auto-flow: row;
    justify-content: stretch;
  }
}

/* Live "points on the line" indicator (2026-08-13) -- upside for a pick in
   progress. The booster pill row stays centered; this sits to its RIGHT on
   desktop (absolute, in the right margin) and BELOW it on mobile. Italic label
   + italic orange (--at-stake) number. [hidden] until a team/TIE is picked. */
.modifier-stake-wrap { position: relative; }
/* Stacked on BOTH layouts: the big orange number centered over a small orange
   "potential points!" caption. Only the placement differs by breakpoint. */
.points-on-line {
  font-family: 'Inter', sans-serif;
  font-style: italic;
  text-align: center;
  line-height: 1.1;
}
.pol-value {
  display: block;
  color: var(--at-stake);
  font-weight: 700;
  font-size: 22px;
  font-variant-numeric: tabular-nums;
}
.pol-label {
  display: block;
  color: var(--at-stake);
  font-size: 10px;
  margin-top: 1px;
}
@media (min-width: 501px) {
  /* Desktop: tucked into the right margin beside the centered pill grid. */
  .points-on-line {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    max-width: 132px;
  }
}
@media (max-width: 500px) {
  /* Mobile: same stacked block, dropped below the full-width pill grid. */
  .points-on-line { margin-top: 12px; }
  .pol-label { font-size: 11px; }
}


/* Visually hides the real <input> inside a .team-option/.tie-option/
   .modifier-pill label while keeping it in the accessibility tree and
   keyboard-operable (unlike display:none) -- the label itself carries
   the actual visual design; the input is what the form submits. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.picks-table-footer {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  padding: 14px 12px;
  font-size: 13px;
  color: var(--text-secondary);
  font-family: 'Inter', sans-serif;
}

/* Submit button (2026-08-13, mobile + desktop): spans the full table width,
   bigger text, a bright dark-red fill with a white outline, flipping GREEN
   while pressed (:active) -- a bold "this is the submit moment" cue. Overrides
   the site-wide green-outline .btn-primary + its green :hover (which can
   "stick" after a touch) so the resting state stays red. */
.picks-table-footer .btn-primary {
  width: 100%;
  padding: 16px 18px;
  font-size: 21px;
  background: #a83a34;   /* dark red, a touch brighter than --danger-dim */
  border: 2px solid #ffffff;   /* white outline around the pill */
  color: #ffffff;   /* bright white on both the red + green fills */
}
.picks-table-footer .btn-primary:hover { background: #a83a34; border-color: #ffffff; color: #ffffff; }
/* Desktop (real-mouse) hover: fill shifts to a DARK red, outline turns red.
   Scoped to hover-capable pointers so a touch tap can't stick this on mobile. */
@media (hover: hover) {
  .picks-table-footer .btn-primary:hover { background: #7a2f2c; border-color: var(--danger); color: #ffffff; }
}
/* Pressed (both platforms): green. Placed AFTER the hover rules so a desktop
   click (hover + active at once) shows green, not the hover black. */
.picks-table-footer .btn-primary:active { background: var(--positive); border-color: #ffffff; color: #ffffff; }

/* ============================================================
   Section 20: Scoring Rules reference card
   ------------------------------------------------------------
   Sits above the Picks table. Two groups: Base Scoring (always
   applies) and Modifiers (optional, tag-based) — same row
   structure throughout, no exceptions. Every rule, including the
   base Pick/TIE rules, is shown as a single ±N value rather than
   two separate colored rows for the win/loss case — this was a
   deliberate simplification: every value in this table (±1 for a
   pick, ±10 for TIE, ±1 to ±3 for modifiers) is symmetric, so a
   single ± number loses no information and reads faster than
   green/red pairs. Do not reintroduce colored +/- pairs here —
   that was tried and replaced for both the modifiers and the base
   rows. All row names (including "Pick" and "TIE Call", not just
   the modifier names) use the same Oswald label treatment as the
   modifier pills on the Picks table (Section 19), so this
   reference card and the page it sits above read as one system.
   ============================================================ */

.rules-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: 10px; overflow: hidden; }
.rules-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 26px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 20px 22px 16px;
  border-bottom: 1px solid var(--hairline);
}

.rules-group-label {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  padding: 14px 22px 4px;
}

.rule-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 22px;
  border-bottom: 1px solid var(--hairline);
  gap: 16px;
}
.rule-row:last-of-type { border-bottom: none; }

.rule-name {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.rule-desc {
  font-family: 'Inter', sans-serif;
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 3px;
}
.rule-cap {
  font-family: 'Inter', sans-serif;
  font-size: 11px;
  color: var(--text-tertiary);
  font-weight: 500;
}

/* Every value in this card uses this single treatment — no
   positive/negative color split. See section note above. */
.rule-value {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 15px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

/* Stacked ± symbol, shared by Section 20 and Section 21 — a single
   "±" glyph can't be colored in two halves via CSS, so this builds
   the same idea from two real characters: + green on top, − red
   below, stacked tightly, sitting right before the (neutral-colored)
   number. Used to "over-explain the stakes" deliberately — this is
   the one place in the app where the plus/minus symbol itself is
   colored rather than just the number, on purpose, since it's a
   reference table teaching the rules, not a graded result. */
.pm-stack {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  line-height: 0.75;
  margin-right: 3px;
  vertical-align: middle;
}
.pm-plus { color: var(--positive); font-size: 0.6em; font-weight: 800; }
.pm-minus { color: var(--danger); font-size: 0.6em; font-weight: 800; }

.rules-footnote {
  padding: 14px 22px;
  font-size: 11.5px;
  color: var(--text-tertiary);
  font-family: 'Inter', sans-serif;
  border-top: 1px solid var(--hairline);
}

/* ============================================================
   Section 22: The Stakes banner (class names keep the older 'pot'
   wording -- the 2026-08-21 rename was display copy only)
   ------------------------------------------------------------
   Shown on the Picks page, between the page header and the
   Scoring Rules card, only when a Room's Commissioner set the stakes
   amount at room creation (Room.pot_amount, self-reported, not a
   real payment -- see app/blueprints/rooms.py's _parse_pot_amount).
   Gold border/accent reuses --gold rather than a new color, same
   token as the Commissioner badge and top-3 rank highlight, so
   "money is on the line" reads as the same kind of special-status
   signal as those, not a new visual language. Renders nothing at
   all (not a "$0" or empty state) when no stakes were set.
   ============================================================ */

.pot-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 20px;
  margin-bottom: 20px;
  border: 1px solid var(--gold);
  border-radius: 10px;
  background: rgba(212, 175, 55, 0.08);
}
/* Payout disclaimer under The Stakes banner (2026-08-19). Italic, centered
   to match the banner above it, and in the same Inter/11px/--text-tertiary
   treatment .form-note uses for this exact sentence on Create Room and Room
   Info. The negative top margin tucks it under the banner's own 20px
   margin-bottom (leaving ~6px) without touching the .pot-banner rule itself. */
.pot-banner-note {
  margin: -14px 0 20px;
  text-align: center;
  font-family: 'Inter', sans-serif;
  font-style: italic;
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-tertiary);
}
.pot-banner-label {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 22px; /* match the $500 value size (2026-08-15, Montana) */
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-secondary);
}
.pot-banner-value {
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 22px;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

/* Inline money amount (e.g. the pro-rated upgrade cost on Room Info) --
   same money treatment as The Stakes banner (Inter, gold), but sized to sit
   inside a sentence rather than as a standalone figure. Inter here honors
   the "every number is Inter" rule against the card's system-sans prose. */
.upgrade-cost-amount {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   Section 23: Create Room form fine print
   ------------------------------------------------------------
   The Create Room form (rooms/_new_form.html) is otherwise still
   on the page's original/pre-Style-#1 styling -- this class is a
   deliberate, narrow exception for just its two field-level notes
   (tier-upgrade note, stakes disclaimer), picking up Style #1's Inter
   typeface and a genuinely dimmer fine-print color than the page's
   normal .muted text, without pulling the rest of the form along.
   ============================================================ */

.form-note {
  font-family: 'Inter', sans-serif;
  font-style: italic;
  font-size: 11px;
  color: var(--text-tertiary);
}

/* Create Room form: Oswald throughout (Montana's call, 2026-08-12) -- labels,
   the input fields + their placeholders, and the fine-print notes all in the
   identity typeface rather than the page's default Inter. */
.create-room-form label,
.create-room-form input,
.create-room-form .form-note {
  font-family: 'Oswald', sans-serif;
}
.create-room-form input::placeholder { font-family: 'Oswald', sans-serif; }

/* ============================================================
   Section 24: Profile info card field rows
   ------------------------------------------------------------
   Migrates the Profile page's top account-info card onto the
   same .table-card shell as Career Stats below it (2026-08-09).
   Pure typography/color -- no layout or content change from what
   it showed before.
   ============================================================ */

.profile-field-list { padding: 4px 20px 18px; }

.profile-field {
  display: flex;
  gap: 8px;
  padding: 8px 0;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
}
.profile-field-label { color: var(--text-secondary); min-width: 120px; }
.profile-field-value { color: var(--text-primary); font-weight: 500; }

/* ============================================================
   Section 25: Invite link box (Room Info, Add a Player)
   ------------------------------------------------------------
   The single place a Room's invite link appears (2026-08-09) --
   replaces both the old top-of-page invite banner and the manual
   add-by-username form, since joining is invite-link-only now.
   Raised surface (one step lighter than the .table-card it sits
   inside) so the link box reads as its own component, not just
   another row of the card.
   ============================================================ */

.invite-link-box {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 12px 14px;
}
.invite-link-box code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  color: var(--text-primary);
  word-break: break-all;
}

/* ============================================================
   Section 26: Account Settings form (Profile page)
   ------------------------------------------------------------
   The change-password form on the Profile page (2026-08-09),
   the first real account-management control (previously just
   "coming soon" copy). Stacked label-over-input rows -- label
   is Oswald-agnostic identity text in muted secondary, matching
   the .profile-field label treatment in Section 24; the inputs
   themselves inherit the shared input styling above. Full-width
   fields inside the narrow (max-width:480px) card.
   ============================================================ */

.account-form { padding: 4px 20px 18px; }
.account-form-field { margin-bottom: 14px; }
.account-form-label {
  display: block;
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  color: var(--text-secondary);
  margin-bottom: 5px;
}
.account-form input[type="password"] { width: 100%; box-sizing: border-box; }
.account-form .btn-primary { margin-top: 4px; }

/* ============================================================
   Section 27: "How Scoring Works" collapsible (2026-08-10 rebuild)
   ------------------------------------------------------------
   One collapsible <details> between The Stakes banner and the
   game list, in TWO tiers:
     Tier 1 "Make Your Pick"     -- Standard Pick + Predict Tie
                                    (TIE is a pick option, a peer of
                                    a standard pick, NOT a modifier)
     divider "then, optionally"
     Tier 2 "Bonus Point Boosters" -- Lock/Steady/Blowout/Nailbiter
                                    four cards + a Stack card
   Both tiers share ONE card component (.scoring-card, 24px badges)
   so they read as one harmonized scale, not two sizes. The card
   shell matches the other dark cards on the page. Default open/
   closed state is server-driven per player (first visit expanded,
   later collapsed -- see picks.week_view); <details>'s native
   toggle handles per-visit open/close, deliberately not persisted.

   Supersedes the earlier flat five-card emblem row (Section 21,
   now unused) and its standalone reminder line. .pts-unit is the
   small muted "PTS" label after every point value -- secondary to
   the number, never equal weight to it.

   FONT SIZING SCOPE (2026-08-10): every font-size below is tuned for
   the DESKTOP/TABLET layout only (bumped uniformly +5px on this date).
   Mobile sizing for this component is INTENTIONALLY OUT OF SCOPE here
   and will be set separately in a dedicated mobile UI sweep -- that
   pass will likely give these elements different values at narrower
   breakpoints. The `@media (max-width: 500px)` block at the end of
   this section is where those narrow-width overrides live; start there
   when the mobile pass happens.
   ============================================================ */

.scoring-explainer {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  overflow: hidden;
}
.scoring-explainer-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px 22px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
/* Remove the native disclosure triangle (both engines). */
.scoring-explainer-summary::-webkit-details-marker { display: none; }
.scoring-explainer-summary::marker { content: ""; }
.scoring-explainer-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 23px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.scoring-explainer-chevron {
  color: var(--text-secondary);
  transition: transform 0.18s ease;
  flex-shrink: 0;
}
.scoring-explainer[open] .scoring-explainer-chevron { transform: rotate(180deg); }
/* Hairline under the summary only while open, so the collapsed state
   reads as a single flat row and the open state as a header + body. */
.scoring-explainer[open] .scoring-explainer-summary { border-bottom: 1px solid var(--hairline); }

/* --- Tier section labels + subtitle ------------------------- */
.scoring-tier { padding: 16px 18px 0; }
.scoring-tier-label {
  text-align: center;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 17px;
  letter-spacing: 0.06em;
  color: var(--positive);
  margin-bottom: 10px;
}
.scoring-tier-subtitle {
  text-align: center;
  font-family: 'Inter', sans-serif;
  font-size: 14.5px;
  line-height: 1.4;
  color: var(--text-tertiary);
  margin: -6px 0 12px;
}

/* --- Shared card component (both tiers) ---------------------- */
.scoring-card-row { display: flex; gap: 7px; flex-wrap: nowrap; }
.scoring-card {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  padding: 10px 9px;
}
/* 24px badge, identical in both tiers and in the Stack equation. */
.scoring-card-badge {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid var(--text-tertiary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 17px;
  color: var(--text-primary);
  margin-bottom: 6px;
  flex-shrink: 0;
}
.scoring-card-name {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  font-size: 14.5px;         /* Tier 1 */
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-primary);
  margin-bottom: 4px;
}
.scoring-card-row-4 .scoring-card-name { font-size: 14px; }  /* Tier 2 */
.scoring-card-desc {
  font-family: 'Inter', sans-serif;
  font-size: 13.5px;
  line-height: 1.35;
  color: var(--text-tertiary);
  margin-bottom: 8px;
}
/* Tier 2 (Bonus Point Boosters) descriptions run 3px smaller than the
   Tier 1 pick descriptions -- their four cards are narrower, so the
   longer booster copy reads better a touch smaller. */
.scoring-card-row-4 .scoring-card-desc { font-size: 10.5px; }

/* DESKTOP Tier 1 (Standard Pick / Predict Tie): keep the two cards side by
   side, but put the badge INLINE to the RIGHT of a bigger name (was stacked
   above it, leaving dead space), with the description + value below. Mobile
   overrides these (full-width, even bigger) in its media block. */
.scoring-card-row-2 .scoring-card {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
}
.scoring-card-row-2 .scoring-card-name { order: 0; flex: 0 0 auto; margin: 0; font-size: 22px; }
.scoring-card-row-2 .scoring-card-badge { order: 1; margin: 0 0 0 10px; }
.scoring-card-row-2 .scoring-card-desc { order: 2; flex: 1 1 100%; margin: 8px 0 6px; font-size: 14px; }
.scoring-card-row-2 .scoring-card-value { order: 3; flex: 1 1 100%; }

/* DESKTOP/tablet (501px+). */
@media (min-width: 501px) {
  /* Tier 1 (Standard Pick / Predict Tie): CENTER everything (name+badge,
     description, value), matching the boosters. The 2-line desc min-height
     keeps a 1-line card's +PTS value aligned with a 2-line neighbour -- the
     value sits right after the desc in the wrap flow, so equal desc heights
     mean the values land at the same height. */
  .scoring-card-row-2 .scoring-card { justify-content: center; text-align: center; }
  .scoring-card-row-2 .scoring-card-desc { text-align: center; min-height: 2.8em; }
  .scoring-card-row-2 .scoring-card-value { text-align: center; }

  /* Tier 2 (Lock/Steady/Blowout/Nailbiter): badge INLINE to the right of a
     bigger name, everything CENTERED (name+badge, description, value), with the
     same 2-line desc min-height so all four values align. */
  .scoring-card-row-4 .scoring-card { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; text-align: center; }
  .scoring-card-row-4 .scoring-card-name { order: 0; flex: 0 0 auto; margin: 0; font-size: 21px; }
  .scoring-card-row-4 .scoring-card-badge { order: 1; margin: 0 0 0 8px; }
  .scoring-card-row-4 .scoring-card-desc { order: 2; flex: 1 1 100%; margin: 6px 0 6px; text-align: center; min-height: 2.8em; }
  .scoring-card-row-4 .scoring-card-value { order: 3; flex: 1 1 100%; text-align: center; }
}

/* margin-top:auto pins the value to the card bottom, so values line up
   across cards of unequal description length -- no fixed desc height. */
.scoring-card-value {
  margin-top: auto;
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 17px;
  color: var(--text-secondary);
  white-space: nowrap;
}
.pts-unit {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-tertiary);
  margin-left: 3px;
  letter-spacing: 0.02em;
}

/* --- "then, optionally" divider between tiers --------------- */
.scoring-tier-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 22px 2px;
}
/* Pull "Bonus Point Boosters" (the .scoring-tier right after the divider) up
   close to the "then, optionally" line -- the divider's bottom margin plus the
   tier's top padding otherwise left a large awkward gap. Higher specificity
   (0,2,0) so it beats both the base and the mobile .scoring-tier padding. */
.scoring-tier-divider + .scoring-tier { padding-top: 8px; }
.scoring-tier-divider::before,
.scoring-tier-divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--hairline);
}
.scoring-tier-divider span {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.08em;
  color: var(--gold);
}

/* --- Stack card (inside Tier 2) ----------------------------- */
.scoring-stack-card {
  margin: 14px 18px 18px;
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  /* Thinned vertical padding to shrink the pill's height. */
  padding: 9px 16px;
}
.scoring-stack-title {
  text-align: center;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 17px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 7px;
}
.scoring-stack-equation {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-wrap: nowrap;
}
.scoring-stack-op {
  font-family: 'Inter', sans-serif;
  font-size: 20px;
  color: var(--text-tertiary);
}
.scoring-stack-result {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 18px;
  color: var(--positive);
  white-space: nowrap;
}
.scoring-stack-result .pts-unit { color: var(--text-tertiary); }
.scoring-stack-caption {
  text-align: center;
  font-family: 'Inter', sans-serif;
  font-size: 13.5px;
  line-height: 1.3;
  color: var(--text-tertiary);
  margin-top: 7px;
}

@media (max-width: 500px) {
  /* Keep the 4-card Tier 2 row on one line at narrow phone widths -- shrink
     padding/badge/PTS AND bring the text back down. The Section 27 desktop
     font sizes are the +5px-bumped ones; these are the narrow-width values the
     component always needed (cascade step 1, shrink first, ~8px legibility
     floor). This block sits AFTER Section 27's base rules, so it wins on
     source order; the Tier-2 selectors (.scoring-card-row-4 ...) are repeated
     so they beat their higher-specificity base rules too. */
  .scoring-tier { padding: 14px 10px 0; }
  .scoring-card { padding: 8px 5px; }
  .scoring-card-badge { width: 22px; height: 22px; font-size: 11px; }
  .pts-unit { font-size: 7.5px; margin-left: 2px; }
  .scoring-stack-card { margin: 14px 10px 16px; }

  .scoring-tier-label { font-size: 12px; }
  .scoring-tier-subtitle { font-size: 9.5px; }
  .scoring-card-name { font-size: 11px; }             /* Tier 1 */
  .scoring-card-row-4 .scoring-card-name { font-size: 9.5px; }   /* Tier 2 */
  .scoring-card-desc { font-size: 8.5px; }            /* Tier 1 */
  .scoring-card-row-4 .scoring-card-desc { font-size: 8px; }     /* Tier 2 */
  /* Point value made as large as fits without growing the pill -- the number
     and the stacked +/- (0.6em, scales with this) read big; "PTS" (.pts-unit)
     keeps its own fixed small size, so only the number + symbol grow. */
  .scoring-card-value { font-size: 19px; }
  .scoring-tier-divider span { font-size: 9px; }
  .scoring-stack-title { font-size: 12px; }
  .scoring-stack-op { font-size: 15px; }
  .scoring-stack-result { font-size: 13px; }
  .scoring-stack-caption { font-size: 9px; }
}

/* How Scoring Works -- mobile density + card-internal rework (2026-08-12,
   from device feedback): tighten wasted vertical space and change the two
   card layouts. Later than the block above so it wins on source order. */
@media (max-width: 500px) {
  /* Tier 1 (Standard Pick / Predict Tie): each gets its OWN full-width line --
     Standard Pick on top, Predict Tie below -- with a BIG, easy-to-read name
     and the badge inline to its right. Name is nowrap so the badge never gets
     pushed off. */
  .scoring-card-row-2 { flex-direction: column; }
  .scoring-card-row-2 .scoring-card {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;  /* center the name + badge across the pill */
    padding: 14px 16px;
  }
  .scoring-card-row-2 .scoring-card-name { order: 0; flex: 0 0 auto; margin: 0; font-size: 32px; line-height: 1.05; white-space: nowrap; }
  .scoring-card-row-2 .scoring-card-badge { order: 1; margin: 0 0 0 10px; width: 30px; height: 30px; font-size: 15px; }
  .scoring-card-row-2 .scoring-card-desc { order: 2; flex: 1 1 100%; margin: 8px 0 4px; font-size: 13px; text-align: center; }
  .scoring-card-row-2 .scoring-card-value { order: 3; flex: 1 1 100%; text-align: center; }

  /* Tier 2 (boosters): CENTER the badge + the name, and make the name as big
     as fits the fixed card width. Description stays left/as-is. */
  /* Boosters as a 2x2 grid (Lock/Steady on top, Blowout/Nailbiter below) --
     each pill is now ~half width, so bump the text way up for visibility and
     center everything for symmetry. */
  .scoring-card-row-4 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .scoring-card-row-4 .scoring-card { padding: 12px 10px; align-items: center; text-align: center; }
  .scoring-card-row-4 .scoring-card-badge { align-self: center; width: 30px; height: 30px; font-size: 15px; }
  .scoring-card-row-4 .scoring-card-name { text-align: center; font-size: 22px; }
  .scoring-card-row-4 .scoring-card-desc { font-size: 11px; }

  /* Tighten "then, optionally" -> "Bonus Point Boosters" -> subtitle. */
  .scoring-tier-divider { margin: 12px 22px 0; }
  .scoring-tier-divider + .scoring-tier { padding-top: 4px; }
  .scoring-tier-label { margin-bottom: 4px; }
  .scoring-tier-subtitle { margin: -2px 0 8px; }
}

/* ============================================================
   Section 28: "Remember me" checkbox row (login form, 2026-08-10)
   ------------------------------------------------------------
   A horizontal checkbox + label, deliberately NOT the block/bold
   treatment the base form <label> gets (that's for field labels
   above inputs). Muted secondary text so it reads as a small
   option, not a field heading.
   ============================================================ */
.remember-me {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0 4px;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 400;
  color: var(--text-secondary);
  cursor: pointer;
}
.remember-me input[type="checkbox"] {
  width: auto;
  margin: 0;
  cursor: pointer;
  accent-color: var(--positive);
}

/* ============================================================
   Section 29: Account Settings extras -- change email + delete
   account (2026-08-10)
   ------------------------------------------------------------
   Adds a change-email section under change-password in the
   Account Settings card, and a separate delete-account card.
   The delete card uses --negative (amber), NOT --danger (red):
   the design system reserves --danger for graded pass/fail only,
   so a destructive-action accent reuses the same amber the
   existing .btn-danger already uses.
   ============================================================ */
.account-form input[type="email"],
.account-form input[type="text"] { width: 100%; box-sizing: border-box; }
.account-form .btn-danger { margin-top: 4px; }

.account-form-heading {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-primary);
  margin-bottom: 10px;
}
.account-form-note {
  font-family: 'Inter', sans-serif;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-secondary);
  margin: 0 0 12px;
}
.account-form-divider {
  border-top: 1px solid var(--hairline);
  margin: 4px 20px 18px;
}

.danger-zone { border-color: var(--negative); }
.danger-zone .table-title { color: var(--negative); }

/* Reminder-emails opt-in toggle on Account Settings. */
.reminder-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: 'Inter', sans-serif;
  font-size: 15px;
  color: var(--text-primary);
  cursor: pointer;
  margin-bottom: 6px;
}
.reminder-toggle input { width: auto; margin: 0; flex-shrink: 0; }

/* ============================================================
   Section 30: Public landing page (2026-08-10)
   ------------------------------------------------------------
   The pre-login marketing page (main.index for logged-out
   visitors). Its own nav + footer (overriding base.html's blocks)
   and single-accent hero, all on the existing token/font system:
   Oswald for identity/label text, Inter for numbers and body,
   --positive as the one accent, --gold only on the price. The
   hero product preview deliberately reuses the live Picks-row
   classes (.picks-table-card/.team-option/.modifier-pill) rather
   than restyling, so it stays visually in sync with the real
   component; only .selected is applied statically and the whole
   preview is pointer-events:none (a showcase, not a form).
   ============================================================ */

/* --- Nav (overrides base .site-nav for this page only) ------ */
/* Transparent to match .site-nav -- same reasoning, see there. */
.lp-nav { background: transparent; padding: 16px 24px; }
.lp-nav-inner {
  max-width: 1000px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.lp-logo {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 20px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-primary);
  text-decoration: none;
}
.lp-nav-actions { display: flex; gap: 10px; }
/* Both pills: identical radius / padding / weight -- neither larger
   than the other. Create-a-Room's prominence comes from order + color,
   not size (per spec). */
.lp-pill {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 14px;
  padding: 8px 18px;
  border-radius: 100px;
  border: 1px solid transparent;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s ease;
}
.lp-pill:hover { text-decoration: none; }
.lp-pill-green { border-color: var(--positive); color: var(--positive); }
.lp-pill-green:hover { background: rgba(95, 184, 122, 0.12); }
.lp-pill-white { border-color: var(--text-primary); color: var(--text-primary); }
.lp-pill-white:hover { background: rgba(236, 233, 226, 0.08); }

/* --- Shared page container + primary CTA -------------------- */
/* Scale the whole landing content up 15% (2026-08-15, Montana -- page read as
   too thin/small). zoom (not transform) so it reflows and stays centered; the
   Create-a-Room / Log-in nav pills live in .lp-nav OUTSIDE .lp, so they are
   deliberately excluded from this scale-up. */
/* ============================================================
   Landing wallpaper (2026-08-16)
   ------------------------------------------------------------
   Stadium photo fixed behind the whole landing page, content scrolling
   over it. Applied to a body.landing-bg class (set only by landing.html)
   so no other page picks it up.

   The overlay is FLAT and UNIFORM (rgba(20,23,26,0.88)), deliberately not
   a top-to-bottom gradient: with background-attachment: fixed the
   gradient is painted against the VIEWPORT, not the document, so it
   would NOT darken with real scroll depth -- it would just re-render the
   same ramp on every screenful. A flat overlay keeps legibility constant
   regardless of how long the page grows.

   It's layered as a second background LAYER on the same element (colour
   first, image second) rather than a ::before overlay, so nothing sits
   in the stacking context above the page content.
   ============================================================ */
/* SITE-WIDE as of 2026-08-16 (was landing-only). body.landing-bg is kept as a
   synonym so the landing template's body_class stays harmless. */
body,
body.landing-bg {
  /* Longhand on purpose: in the shorthand only the LAST layer picked up
     position/size/repeat/attachment, leaving the overlay at scroll/repeat/auto.
     Harmless for a solid fill, but it made the two layers disagree and the
     mobile override below ambiguous. Both layers now behave identically. */
  background-image:
    linear-gradient(rgba(20, 23, 26, 0.88), rgba(20, 23, 26, 0.88)),
    url("../../img/stadium-bg.jpg");
  background-position: center 55%, center 55%;
  background-size: cover, cover;
  background-repeat: no-repeat, no-repeat;
  background-attachment: fixed, fixed;
  background-color: var(--bg);   /* fallback before the photo loads */
}
/* background-attachment: fixed is unreliable on mobile browsers -- iOS
   Safari in particular has long ignored or janked it, and Android Chrome
   drops it during URL-bar collapse. Rather than ship a broken effect,
   touch devices get the same image and the same flat overlay, scrolling
   normally with the page. Keyed off pointer/hover (the device), not
   width, so a narrow desktop window keeps the fixed effect. */
@media (hover: none), (pointer: coarse) {
  body, body.landing-bg { background-attachment: scroll, scroll; }
}

.lp { max-width: 820px; margin: 0 auto; zoom: 1.15; }
.lp-btn-primary {
  display: inline-block;
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 15px;
  padding: 12px 28px;
  border-radius: 8px;
  /* Glossy "glass" finish (2026-09-10): a top-down sheen layered OVER the base
     color -- brighter at the top, a hair of shade at the bottom -- plus an
     inset top-edge highlight (the glass rim) and a soft drop shadow so the pill
     lifts off the page. Stays entirely within the pill's own color: it's light
     and shadow, not a new hue. background-COLOR (not the shorthand) so the
     gradient background-image isn't wiped, and so .lp-btn-gold can swap only the
     color and keep the same gloss. */
  background-color: var(--positive);
  background-image: linear-gradient(180deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.06) 46%, rgba(0,0,0,0.12) 100%);
  color: var(--bg);
  border: 1px solid var(--positive);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.45), 0 6px 18px rgba(0,0,0,0.30);
  text-decoration: none;
  position: relative;
  overflow: hidden;
  transition: filter 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
/* The gold "Try It" pill: identical gloss, only the color swaps (border + text
   too, matching the inline styles it replaces). */
.lp-btn-gold { background-color: var(--gold); border-color: var(--gold); color: var(--bg); }
/* A light streak that glides across on hover -- the "shine". pointer-events off
   so it never blocks the click; clipped by the pill's overflow:hidden. Parked
   off-screen-left at rest, so on touch (no hover) there's just the static
   gloss, no perpetual glare. */
.lp-btn-primary::after {
  content: "";
  position: absolute;
  top: 0;
  left: -75%;
  width: 50%;
  height: 100%;
  background: linear-gradient(120deg, transparent 0%, rgba(255,255,255,0.45) 50%, transparent 100%);
  transform: skewX(-20deg);
  pointer-events: none;
}
.lp-btn-primary:hover { filter: brightness(1.06); text-decoration: none; transform: translateY(-1px); }
/* Sweep only on a real pointer, and never for reduced-motion users. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .lp-btn-primary::after { transition: left 0.55s ease; }
  .lp-btn-primary:hover::after { left: 125%; }
}

/* --- Hero --------------------------------------------------- */
.lp-hero { text-align: center; padding: 18px 0 39.6px; } /* top pad trimmed 2026-09-10 to center the eyebrow between the nav and the headline (the trim moves into .lp-eyebrow margin-bottom, so the headline stays put) */
.lp-eyebrow {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--text-secondary);
  margin-bottom: 45px; /* centers the eyebrow in the nav->headline band (2026-09-10) */
}
.lp-headline {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 32px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.08;
  margin: 0 0 18px;
}
.lp-headline-line { display: block; color: var(--text-primary); }
.lp-headline-line.lp-accent { color: var(--positive); }
.lp-subhead-1 {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 14.5px;
  line-height: 1.45;
  color: var(--text-primary);
  max-width: 420px;
  margin: 0 auto 5px;
}
.lp-subhead-2 {
  font-family: 'Inter', sans-serif;
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.02em;
  line-height: 1.45;
  color: var(--text-secondary);
  max-width: 420px;
  margin: 0 auto 22px;
}
.lp-hero-secondary {
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  color: var(--text-tertiary);
  margin-top: 14px;
}
.lp-hero-secondary a { color: var(--positive); text-decoration: none; }
.lp-hero-secondary a:hover { text-decoration: underline; }

/* Static product preview -- reuses the real Picks-row classes. */
.lp-preview { max-width: 720px; margin: 0 auto; text-align: left; } /* interactive demo (2026-08-15): pointer-events:none removed so visitors can click the preview */
/* Preview breakout: let the preview extend ~60px past each side of the 820px
   content column so the two team pills are wide enough for the team NAMES to
   render at close to the real Picks size (2026-08-15, Montana -- names read too
   small in the narrow column). The pills keep the real page's centered-name /
   corner-record treatment. Desktop only; the mobile preview is unchanged. */
@media (min-width: 501px) {
  .lp-preview { max-width: none; width: calc(100% + 76px); margin-left: -38px; }
}

/* --- Generic content section ------------------------------- */
.lp-section { text-align: center; padding: 36px 0; border-top: 1px solid var(--hairline); } /* vertical spacing -10% (2026-08-15), was 40px */
.lp-section-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 22px;
  color: var(--text-primary);
  margin: 0 0 28px;
}
.lp-section-title.lp-accent { color: var(--positive); }
.lp-section-title.lp-gold { color: var(--gold); }
.lp-section-link {
  display: inline-block;
  margin-top: 24px;
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 13px;
  color: var(--positive);
  text-decoration: none;
}
.lp-section-link:hover { text-decoration: underline; }

/* --- How It Works steps ------------------------------------ */
.lp-steps { display: flex; gap: 20px; justify-content: center; }
.lp-step { flex: 1; max-width: 230px; }
.lp-step-num {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 2px solid var(--positive);
  color: var(--positive);
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 12px;
}
.lp-step-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-primary);
  margin-bottom: 6px;
}
.lp-step-desc {
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-secondary);
}

/* --- Why It's Different cards ------------------------------ */
.lp-why-cards { display: flex; gap: 12px; }
.lp-why-card {
  flex: 1;
  min-width: 0; /* let four cards share the row without content forcing overflow */
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 18px 15px; /* trimmed from 20/18 so four across doesn't feel cramped */
  text-align: left;
}
.lp-why-card-featured { border-color: var(--positive); }
.lp-why-card-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 16px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-primary);
  margin-bottom: 8px;
}
.lp-why-card-featured .lp-why-card-title { color: var(--positive); }
.lp-why-card-desc {
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* --- Optional Point Boosters table (2026-08-14) ------------
   A detail table on the landing page, between the product preview and
   "Why It's Different". Names/points/limits render live from the scoring
   registry (see landing.html). Signed point values reuse the app-wide
   value semantic (positive = green --positive, negative = red --danger),
   NOT a per-booster color scheme -- the boosters themselves stay neutral,
   distinguished only by label, per the design rules. The Predict Tie row
   is styled IDENTICALLY (no TIE color exception). */
.lp-boost-intro {
  max-width: 640px;
  margin: -6px auto 24px;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-secondary);
  text-align: center;
}
.lp-boost-intro strong { color: var(--text-primary); font-weight: 600; }
.lp-boost-table-wrap {
  max-width: 720px;
  margin: 0 auto;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  overflow: hidden;
}
/* --- Scoreboard showcase (landing, 2026-08-15) ------------
   Static preview of the real Room Scoreboard. Reuses the live .gc-pick /
   .matchup-text / .player-col-header / week-total-row classes; only the wrapper
   and column alignment are landing-specific (the real table gets its cell
   centering from an #scoreboard-table rule this preview doesn't carry). */
/* OPAQUE by requirement (2026-08-16): with the stadium wallpaper behind the
   page, this was the ONE data-dense landing section rendering fully
   transparent -- wrapper, table AND cells all rgba(0,0,0,0), verified by
   computed style, so the photo showed straight through a dense pick grid.
   Matched to .lp-boost-table-wrap's treatment (surface + hairline + radius)
   rather than inventing a new one. */
.lp-scoreboard-wrap {
  overflow-x: auto;
  margin-top: 20px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 8px 10px;
}
.lp-scoreboard { margin: 0 auto; }
.lp-scoreboard thead th { cursor: default; } /* not sortable in the preview */
/* Game column stays left (first-child default); every player column centers,
   overriding the base td:nth-child(2) left-align and the default right-align. */
.lp-scoreboard th[data-user-id],
.lp-scoreboard td[data-user-id] { text-align: center; }
/* Stat Tracker showcase reuses the live .table-card + Overview table; just needs
   spacing and left-aligned content inside the centered landing section. */
.lp-stats-card { margin-top: 20px; text-align: left; max-width: 100%; }

.lp-boost-table { width: 100%; border-collapse: collapse; text-align: left; }
.lp-boost-table thead th {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
  padding: 14px 16px 11px;
  border-bottom: 1px solid var(--hairline);
}
.lp-boost-table thead th.lp-boost-col-num { text-align: center; width: 74px; }
.lp-boost-table tbody td {
  padding: 15px 16px;
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}
.lp-boost-table tbody tr:last-child td { border-bottom: none; }
.lp-boost-name-cell { white-space: nowrap; }
.lp-boost-label {
  display: block;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--text-primary);
}
.lp-boost-when {
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.5;
  color: var(--text-secondary);
  /* MUST be explicit (2026-08-20). The base `td` rule near the top of this
     file sets `white-space: nowrap` for the dense numeric scoreboard cells,
     and it lands on this cell too -- but this one holds a prose sentence.
     Desktop hid it (the 720px card fits one line); on a phone the stacked
     card is ~250px wide and .lp-boost-table-wrap's `overflow: hidden`
     CLIPPED every description mid-word rather than wrapping it. Same trap
     as the style.css/theme overlap: omitting a property does not remove an
     inherited-by-selector one, it has to be overridden. */
  white-space: normal;
}
.lp-boost-hit,
.lp-boost-miss {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 16px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.lp-boost-hit { color: var(--positive); }
.lp-boost-miss { color: var(--danger); }

/* --- Pricing teaser ---------------------------------------- */
.lp-price-line {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  font-size: 18px;
  color: var(--text-secondary);
  margin-bottom: 10px;
}
/* The "$5" is a number -> Inter, per the design system (numbers never Oswald),
   even inside this now-Oswald headline. */
.lp-price-amount { font-family: 'Inter', sans-serif; color: var(--gold); font-weight: 700; font-size: 22px; }
.lp-price-sub {
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-tertiary);
  max-width: 460px;
  margin: 4px auto 0;
}

/* --- Final CTA band ---------------------------------------- */
/* Final CTA: plain text, no surrounding card (2026-08-15, Montana) -- the pill
   background/border/radius were removed and the top spacing tightened so
   "Ready to run your own room?" sits close under the pricing section's
   "See full pricing" link. */
.lp-cta-band {
  text-align: center;
  padding: 12px 24px 24px;
  margin: 0;
}
.lp-cta-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 24px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-primary);
  margin: 0 0 8px;
}
.lp-cta-sub {
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0 0 20px;
}

/* --- Footer (overrides base footer for this page) ---------- */
.lp-footer {
  /* override the base `footer { display:flex }` element rule (style.css),
     which would otherwise lay the links nav and the note out side by side --
     we want the note stacked BELOW the links. */
  display: block;
  text-align: center;
  padding: 26px 24px 40px;
  font-family: 'Inter', sans-serif;
  font-size: 12px;
  color: var(--text-tertiary);
}
/* Site nav tabs at the very bottom of the landing page, matching the
   uppercase-Oswald treatment of the standard site footer (style.css). They
   wrap to multiple lines on a narrow phone rather than overflowing. */
.lp-footer-links {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 20px;
  margin-bottom: 16px;
}
.lp-footer-links a {
  color: var(--text-secondary);
  font-family: 'Oswald', sans-serif;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
}
.lp-footer-links a:hover { color: var(--text-primary); }
.lp-footer-note { color: var(--text-tertiary); }

/* Minimal "don't visibly break on a phone" guard (NOT the deferred full
   mobile pass): the three-across step/card rows can't shrink below their
   content on a narrow screen and would push a horizontal scrollbar, so
   stack them to one column. Nav actions wrap under the logo if tight. */
@media (max-width: 600px) {
  .lp-steps { flex-direction: column; }
  .lp-step { max-width: none; }
  /* Four "Why It's Different" cards reflow to a 2x2 grid on a phone rather
     than one tall column -- the teasers are short, so two-up reads better and
     keeps the section compact. */
  .lp-why-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  /* Stack + CENTER the landing header on a phone -- the logo on its own
     line, the Create-a-Room / Log-in pills centered under it (were
     right-aligned via space-between). The logo scales with the viewport to
     stretch across (nearly) the full page width as a banner. */
  .lp-nav-inner { flex-direction: column; align-items: center; gap: 28px; }
  .lp-nav-actions { justify-content: center; }
  .lp-logo { font-size: 11.5vw; letter-spacing: 0.03em; white-space: nowrap; text-align: center; }
  /* Fallback team-name size in the hero preview before the auto-fit script
     runs (landing.html caps at 22px on mobile, then shrinks to fit). */
  .lp-preview .team-option-name { font-size: 22px; }
  .lp-headline { font-size: 27px; }
  /* The shared Picks CSS defines base .tie-option AFTER its own
     max-width:500px override, so that override loses on source order and
     the tie box never shrinks on mobile -- collapsing the team boxes in
     this preview. Re-apply the mobile shrink at higher specificity
     (.lp-preview .tie-option) so it wins here without touching the shared
     component. (The same latent bug on the real Picks page is flagged
     separately.) */
  .lp-preview .tie-option { padding: 8px 8px; font-size: 11px; }
  .lp-preview .tie-option-points { font-size: 10px; }
  .lp-preview .team-option-at { font-size: 11px; }

  /* Booster table reflows to a stacked card per row on a phone: the 4-column
     layout is too wide at 375px. Each row becomes a block (name, then the
     condition, then Hit/Miss inline with ::before labels). */
  .lp-boost-table, .lp-boost-table tbody, .lp-boost-table tr, .lp-boost-table td { display: block; }
  .lp-boost-table thead { display: none; }
  .lp-boost-table tbody tr {
    padding: 14px 16px;
    border-bottom: 1px solid var(--hairline);
  }
  .lp-boost-table tbody tr:last-child { border-bottom: none; }
  .lp-boost-table tbody td { padding: 0; border: none; }
  .lp-boost-when { margin: 6px 0 9px; }
  /* Scoped to td so these beat the `.lp-boost-table td { display:block }`
     rule above (higher specificity) and Hit/Miss sit inline side-by-side. */
  .lp-boost-table td.lp-boost-hit, .lp-boost-table td.lp-boost-miss {
    display: inline-block;
    text-align: left;
    margin-right: 22px;
    font-size: 15px;
  }
  .lp-boost-hit::before,
  .lp-boost-miss::before {
    font-family: 'Oswald', sans-serif;
    font-weight: 600;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-tertiary);
    margin-right: 5px;
  }
  .lp-boost-hit::before { content: "Hit"; }
  .lp-boost-miss::before { content: "Miss"; }
}

/* ============================================================
   Section 31: "Pick For Me" / "Auto-Pick For Me" (2026-08-10)
   ------------------------------------------------------------
   Optional convenience controls on the Make Picks form. Both are
   deliberately quiet: a small text link per game and a small outline
   pill for the bulk action -- neither should out-shout the actual
   pick pills or the Submit button. All effect is client-side (staged
   picks); see picks/week.html's script.
   ============================================================ */

/* Kickoff line + per-game link share one row (link right-aligned). */
.pick-row-topline {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}
.pick-for-me {
  flex-shrink: 0;
  background: none;
  border: none;
  padding: 0;
  font-family: 'Inter', sans-serif;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--positive);
  cursor: pointer;
}
.pick-for-me:hover { text-decoration: underline; }

/* Bulk auto-pick: quiet outline pill on its own thin right-aligned row,
   just under the Week/Locks header. Italic muted note beneath it. */
.auto-pick-bar {
  text-align: right;
  padding: 12px 22px 2px;
}
.auto-pick-btn {
  display: inline-block;
  font-family: 'Oswald', sans-serif;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 6px 15px;
  border-radius: 100px;
  border: 1px solid var(--positive);
  color: var(--positive);
  background: transparent;
  cursor: pointer;
  transition: background 0.15s ease;
}
.auto-pick-btn:hover { background: rgba(95, 184, 122, 0.12); }
.auto-pick-note {
  font-family: 'Inter', sans-serif;
  font-size: 11px;
  font-style: italic;
  color: var(--text-tertiary);
  margin-top: 5px;
}

/* ============================================================
   Section 32: App modal -- native <dialog class="app-modal"> (2026-08-10)
   ------------------------------------------------------------
   First used by the Make Picks missing-picks warning. Native <dialog>
   gives Escape + backdrop + focus handling for free; this styles it in
   Style #1. Reusable app-wide -- future modals should use this rather
   than a new pattern. Mobile: the games list scrolls inside the modal so
   the three stacked actions always stay visible.
   ============================================================ */
.app-modal {
  /* Explicit centering -- the site-wide body{zoom:1.3} breaks the native
     dialog's default margin:auto centering, dropping it to the corner. */
  position: fixed;
  inset: 0;
  margin: auto;
  height: fit-content;
  max-height: 90vh;
  max-width: min(92vw, 440px);
  width: 100%;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}
.app-modal::backdrop { background: rgba(0, 0, 0, 0.6); }
.app-modal-body { padding: 22px 22px 20px; }
.app-modal-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 19px;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  margin: 0 0 10px;
}
.app-modal-text {
  font-family: 'Inter', sans-serif;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-secondary);
  margin: 0 0 12px;
}
.app-modal-list {
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
  max-height: 38vh;
  overflow-y: auto;
  border: 1px solid var(--hairline);
  border-radius: 8px;
}
.app-modal-list li {
  font-family: 'Oswald', sans-serif;  /* team names = identity text */
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  padding: 9px 14px;
  border-bottom: 1px solid var(--hairline);
}
.app-modal-list li:last-child { border-bottom: none; }
.app-modal-actions { display: flex; flex-direction: column; gap: 10px; }
.app-modal-actions .btn { width: 100%; text-align: center; }
.app-modal-link {
  align-self: center;
  background: none;
  border: none;
  padding: 4px;
  margin-top: 2px;
  font-family: 'Inter', sans-serif;
  font-size: 12.5px;
  color: var(--text-tertiary);
  text-decoration: underline;
  cursor: pointer;
}
.app-modal-link:hover { color: var(--text-secondary); }
/* Match the "How Scoring Works" heading treatment (Oswald label type)
   for every modal action, including the plain text link. */
.app-modal-actions .btn,
.app-modal-actions .app-modal-link {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

/* ============================================================
   Section 33: Per-player pricing UI -- seat reminder + Paid/Unpaid
   (per-player pricing overhaul)
   ------------------------------------------------------------
   Pay-to-submit ($5, one-time per room). The reminder banner sits on
   Make Picks for an unpaid seat -- clear but not blocking, gold-accented
   like The Stakes banner. The seat tags show the Commissioner a Paid/Unpaid
   status on the Room Info members list. Unpaid is "pending", never an
   error -- muted neutral, deliberately NOT --danger red.
   ============================================================ */
.seat-reminder {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  /* Loud red CTA -- deliberately overrides the "danger red = grading only"
     convention to grab an unpaid player's eye (Montana's call). */
  background: rgba(226, 80, 74, 0.15);
  border: 1px solid var(--danger);
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 16px;
}
.seat-reminder-badge {
  flex: none;
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 13px;
  color: var(--text-primary);
  background: var(--danger);
  border-radius: 5px;
  padding: 4px 9px;
  white-space: nowrap;
}
.seat-reminder-text {
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-primary);
  font-style: italic;
}
.seat-reminder-text strong { color: var(--danger); }

.seat-tag {
  display: inline-block;
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.02em;
  border-radius: 5px;
  padding: 3px 8px;
  white-space: nowrap;
  vertical-align: middle;
}
.seat-tag-paid {
  color: var(--positive);
  background: rgba(95,184,122,0.15);
  border: 1px solid rgba(95,184,122,0.45);
}
.seat-tag-unpaid {
  color: var(--text-secondary);
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
}
.seat-tag-note {
  font-weight: 400;
  color: var(--text-tertiary);
}

@media (max-width: 500px) {
  .seat-reminder-badge { font-size: 12px; }
  .seat-reminder-text { font-size: 12px; }
}

/* ============================================================
   Section 34: Login popup -- reuses .app-modal (Section 32).
   Adds the close (x) button + login form field spacing so the
   base "Log in" link opens a modal instead of the /login page.
   ============================================================ */
.login-modal .app-modal-body { position: relative; }
.login-modal-close {
  position: absolute;
  top: 12px;
  right: 14px;
  background: none;
  border: none;
  color: var(--text-tertiary);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}
.login-modal-close:hover { color: var(--text-primary); }
.login-modal-form label,
.login-modal-forgot label {
  display: block;
  margin-top: 12px;
  margin-bottom: 4px;
  font-family: 'Oswald', sans-serif;  /* identity/label font, matches the WEEK heading */
  font-weight: 600;
  letter-spacing: 0.02em;
  font-size: 13px;
  color: var(--text-secondary);
}
.login-modal-form input[type="text"],
.login-modal-form input[type="password"],
.login-modal-forgot input[type="email"] { width: 100%; }
/* The standalone /login page card mirrors the popup: Oswald title + labels. */
.login-card h2 { font-family: 'Oswald', sans-serif; font-weight: 700; text-transform: uppercase; }
.login-card label { font-family: 'Oswald', sans-serif; font-weight: 600; letter-spacing: 0.02em; }
.login-modal .app-modal-title { text-transform: uppercase; }
.login-modal-form .remember-me {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  color: var(--text-secondary);
}
.login-modal-form .btn-primary,
.login-modal-forgot .btn-primary { width: 100%; margin-top: 14px; }
.login-modal-links { margin-top: 16px; font-size: 12.5px; }

/* Mini inline editor (2026-08-16) -- a one-line disclosure for a rare
   commissioner action (currently: rename room). Deliberately NOT a .table-card:
   a permanent card for something touched once a season competes with the
   controls beside it. Collapsed it's a single quiet line; open it reveals the
   form. Native <details>, so no JS. */
.mini-editor { max-width: 680px; margin: 0 0 14px; }
.mini-editor-summary {
  display: inline-block;
  cursor: pointer;
  list-style: none;
  font-family: 'Oswald', sans-serif;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-secondary);
  padding: 4px 0;
}
.mini-editor-summary::-webkit-details-marker { display: none; }
.mini-editor-summary::before { content: "+ "; color: var(--positive); }
.mini-editor[open] .mini-editor-summary::before { content: "\2212  "; }
@media (hover: hover) {
  .mini-editor-summary:hover { color: var(--text-primary); }
}
.mini-editor-body { padding: 10px 0 4px; max-width: 420px; }
/* Current value carried on the collapsed line (e.g. the stakes), so collapsing
   doesn't hide it. Gold per the money/status token; Inter per the numbers
   rule, since it's a figure inside an otherwise-Oswald label. */
.mini-editor-value {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  color: var(--gold);
  letter-spacing: normal;
}
/* Inline login failure, shown INSIDE the popup (2026-08-16) instead of
   re-rendering the full /login page. Matches the site's .flash error treatment
   -- --danger is correct here: this really is a failed action, not a "stat
   trending down", which is the distinction --negative covers. */
.login-modal-error {
  margin: 0 0 14px;
  padding: 10px 12px;
  border: 1px solid var(--danger);
  border-radius: 6px;
  background: rgba(226, 80, 74, 0.12);
  color: var(--danger);
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  line-height: 1.4;
}
/* Same box, success tone -- used when a resend actually goes out, so the
   popup can confirm in place instead of navigating away. */
.login-modal-error.is-success {
  border-color: var(--positive);
  background: rgba(95, 184, 122, 0.12);
  color: var(--positive);
}
.login-modal-resend { width: 100%; margin: 0 0 14px; }
/* REQUIRED, not belt-and-braces: this button carries .btn, whose
   `display: inline-block` is an author rule and therefore BEATS the UA's
   `[hidden] { display: none }`. Without this the button ignores the hidden
   attribute and shows on every modal open. Any .btn toggled via .hidden in JS
   needs the same guard. */
.login-modal-resend[hidden] { display: none; }

/* ============================================================
   Section 35: Make Picks summary -- points on the line
   ------------------------------------------------------------
   The read-only "picks submitted" view shows the +/- magnitude at
   stake per pick (live from scoring_engine, reusing pm_value + PTS)
   and a Total On The Line footer. Scoped to .pick-summary-row so the
   editable form's .pick-row layout is untouched.
   ============================================================ */
.pick-summary-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
/* A game that has already kicked off (locked) can no longer be edited, so its
   summary row is greyed-out -- visually separating the settled games from the
   ones the player can still change via Edit Picks. */
.pick-summary-row.concluded { opacity: 0.5; }
.pick-summary-main { min-width: 0; }
.pick-summary-yourpick { margin-top: 6px; font-size: 15px; }

/* MOBILE: enlarge the submitted-picks summary -- both the result (score line,
   matchup, your pick) and the points-at-stake read bigger, staying within each
   row (rows just grow taller). */
@media (max-width: 500px) {
  .pick-summary-row .pick-row-kickoff { font-size: 15px; }
  .pick-summary-row .matchup-text { font-size: 19px; }
  .pick-summary-row .pick-summary-yourpick { font-size: 17px; }
  .pick-summary-row .pick-stake { font-size: 23px; }
}
.pick-stake {
  flex-shrink: 0;
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 18px;
  color: var(--text-secondary);
  white-space: nowrap;
  text-align: right;
}
.pick-summary-total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 20px;
  border-top: 1px solid var(--hairline);
}
.pick-summary-total-label {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}
.pick-summary-total-value {
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 20px;
  color: var(--text-primary);
  white-space: nowrap;
}
/* "Total Awarded" -- the points actually won this week, colored by result:
   green for a net gain, red for a net loss, neutral for exactly zero. */
.pick-summary-total-value.awarded-pos { color: var(--positive); }
.pick-summary-total-value.awarded-neg { color: var(--danger); }
.pick-summary-total-value.awarded-zero { color: var(--text-primary); }

/* ============================================================
   Section 36: Info/legal pages -- all headers in uppercase Oswald
   ------------------------------------------------------------
   Applied via <body class="doc-page"> on pricing/faq/about/contact/
   terms/privacy (base.html body_class block). Generic h1/h2/h3 on
   those pages otherwise inherit the system body font.
   ============================================================ */
.doc-page h1,
.doc-page h2,
.doc-page h3 {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* ============================================================
   Section 37: Reusable app-page title -- uppercase Oswald
   ------------------------------------------------------------
   For standalone app-page <h1>s that aren't on a .doc-page (e.g.
   the Rooms dashboard). Same treatment as .doc-page headers.
   ============================================================ */
.page-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* Pay-now CTA button inside the red .seat-reminder banner (routes to the $5
   Stripe seat checkout). Right-aligned; wraps below the text on narrow rows. */
.seat-reminder-form { flex: none; margin-left: auto; }
.seat-reminder-btn {
  display: inline-block;
  background: var(--danger);
  color: var(--text-primary);
  border: none;
  border-radius: 6px;
  padding: 9px 18px;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: filter 0.15s ease;
}
.seat-reminder-btn:hover { filter: brightness(1.12); }

/* The team code in the read-only "Your pick: DAL (X)" summary line -- identity
   text, so Oswald bold (Option A: put the app's edge font on the pick). */
.pick-team {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text-primary);
}

/* Edit-mode "Editing Week N picks." bar -- Oswald, but deliberately
   UNDERSTATED (light weight, no uppercase, muted) so it has a little
   character without competing with the loud primary buttons. */
.editing-bar span {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  letter-spacing: 0.03em;
  color: var(--text-secondary);
}
.editing-bar .btn {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  letter-spacing: 0.03em;
  text-transform: none;
  /* Green outline (overrides .btn-small's muted --hairline border) so it reads
     as an action, matching the app's green-outline button language. */
  border-color: var(--positive);
}
.editing-bar .btn:hover { border-color: var(--positive); background: var(--positive-dim); }

/* ============================================================
   Section 38: Invite-link paste bar (2026-08-12)
   ------------------------------------------------------------
   A browser-address-bar-style input on the Rooms dashboard
   ("Have an invite link?"): a player pastes the invite link
   their commissioner sent them and hits JOIN, which routes into
   the normal invite-accept flow (rooms.invite_go ->
   invite_accept). The JOIN button reuses the .btn-primary look
   (Oswald uppercase, --positive) so it matches every other CTA.
   ============================================================ */
.invite-bar {
  display: flex;
  align-items: stretch;
  margin-top: 14px;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface-raised);
  overflow: hidden;
}
.invite-bar:focus-within { border-color: var(--positive); }
/* Descendant selector (0,2,0) so it beats the base stylesheet's
   input[type=text] rule (0,1,1) -- otherwise that rule's own border,
   panel background, border-radius, and margin-bottom:12px win, which is
   what drew the inner "double line" box and left a 12px gap below the
   input that made the stretched JOIN button hang past the bar. Reset all
   four here so the input is just naked text inside the shared bar shell. */
.invite-bar .invite-bar-input {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: none;
  border-radius: 0;
  outline: none;
  margin: 0;
  padding: 9px 14px;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  color: var(--text-primary);
}
.invite-bar .invite-bar-input::placeholder { color: var(--text-tertiary); }
.invite-bar-btn {
  flex: none;
  background: transparent;
  color: var(--positive);
  /* Full green outline, inset from the bar edges so it reads as its own
     outlined pill inside the input bar (was just a left divider). */
  border: 1px solid var(--positive);
  border-radius: 6px;
  margin: 5px;
  padding: 0 22px;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  transition: all 0.15s ease;
}
.invite-bar-btn:hover { background: var(--positive); color: var(--bg); }

/* ============================================================
   Section 39: Error pages (2026-08-14)
   ------------------------------------------------------------
   The 404 page (extends base.html, so it gets nav/footer). The 500 page is
   deliberately standalone with its own inline styles -- see errors/500.html --
   so it never depends on this sheet loading.
   ============================================================ */
.error-page {
  max-width: 460px;
  margin: 64px auto;
  text-align: center;
  padding: 0 24px;
}
.error-code {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 64px;
  line-height: 1;
  color: var(--positive);
  margin-bottom: 8px;
}
.error-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 26px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-primary);
  margin: 0 0 12px;
}
.error-message {
  font-family: 'Inter', sans-serif;
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-secondary);
  margin: 0 0 28px;
}


/* ============================================================
   Section 40: Disabled / mid-submit buttons (2026-08-15)
   ------------------------------------------------------------
   A button locked by the double-submit guard (js-submit-once / the Make Picks
   lockSubmit()) gets `disabled` + `.is-submitting`. Grey it out so it reads as
   inert; the `disabled` attribute already blocks the click natively.
   ============================================================ */
.btn:disabled,
.btn.is-submitting {
  opacity: 0.55;
  cursor: not-allowed;
}

/* ============================================================
   Section 41: Big Pick'em Leaderboard (2026-09-13)
   Site-wide arcade-style high-score board -- a deliberately NEW
   table shape (row list with a big rank number), distinct from the
   dense Scoreboard grid and the Stats overview table. Tokens only:
   --gold for the top-3 ranks, --positive to mark the viewer's row.
   ============================================================ */
.lb-wrap { max-width: 640px; margin: 0 auto; }
.lb-title {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 30px;
  text-align: center;
  color: var(--text-primary);
  margin: 8px 0 4px;
}
.lb-sub {
  text-align: center;
  font-family: 'Oswald', sans-serif;
  letter-spacing: 0.03em;
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0 0 20px;
}
.lb-board {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  overflow: hidden;
}
.lb-row {
  display: grid;
  grid-template-columns: 52px 1fr 66px 62px 74px;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--hairline);
}
/* Keep the "Booster %" header on one line -- the data cells are short (52% /
   —) so the column width is driven by the label. */
.lb-boost { white-space: nowrap; }
.lb-row:last-child { border-bottom: none; }
.lb-head { background: var(--surface-raised); }
.lb-head span {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
/* Numbers = Inter, tabular, per the design system. The rank is the big
   arcade digit; points is the emphasis (it's what the board sorts on). */
.lb-rank {
  font-family: 'Inter', sans-serif;
  font-weight: 800;
  font-size: 20px;
  font-variant-numeric: tabular-nums;
  color: var(--text-tertiary);
  text-align: center;
}
.lb-rank.top1 { color: var(--gold); }
.lb-rank.top2 { color: var(--gold); opacity: 0.72; }
.lb-rank.top3 { color: var(--gold); opacity: 0.5; }
.lb-name {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 16px;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lb-points {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 18px;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  text-align: right;
}
.lb-record {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  text-align: right;
}
.lb-boost {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  text-align: right;
}
/* Header labels reuse the data-cell classes, so re-assert their alignment. */
.lb-head .lb-rank { text-align: center; }
.lb-head .lb-points, .lb-head .lb-record, .lb-head .lb-boost { text-align: right; }
/* The viewer's own row -- a raised fill + a left accent bar so they spot
   themselves instantly (same idea as the Scoreboard's highlighted column). */
.lb-row.is-you {
  background: var(--surface-raised);
  box-shadow: inset 3px 0 0 var(--positive);
}
.lb-you {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 9px;
  letter-spacing: 0.08em;
  color: var(--positive);
  border: 1px solid var(--positive);
  border-radius: 4px;
  padding: 1px 4px;
  margin-left: 6px;
  vertical-align: middle;
}
.lb-youline {
  text-align: center;
  font-family: 'Oswald', sans-serif;
  letter-spacing: 0.02em;
  color: var(--text-secondary);
  margin: 16px 0 0;
}
.lb-youline strong { font-family: 'Inter', sans-serif; color: var(--text-primary); }
.lb-empty { padding: 28px 20px; text-align: center; color: var(--text-secondary); }
.lb-empty p { margin: 0; }

@media (max-width: 520px) {
  .lb-title { font-size: 24px; }
  .lb-row { grid-template-columns: 32px 1fr 44px 52px 48px; gap: 7px; padding: 10px 10px; }
  .lb-rank { font-size: 17px; }
  .lb-name { font-size: 14px; }
  .lb-points { font-size: 16px; }
  .lb-record { font-size: 12.5px; }
  .lb-boost { font-size: 12.5px; }
  /* Phone space is too tight for "Booster %" on one line; let the HEADER wrap
     to two lines rather than steal width from names. Data cells (52% / —) are
     short and never wrap. */
  .lb-head .lb-boost { white-space: normal; line-height: 1.05; }
  /* The viewer's row is already marked by the accent bar + raised fill, so
     drop the redundant "YOU" text badge here rather than let it truncate in
     the tight name column. */
  .lb-you { display: none; }
}
