/* StakeMarker tournament page — standalone dark-themed styles.
   Does NOT extend main.css. Mirrors the app's visual language
   (deep navy bg, teal accent, translucent cards) adapted for
   a list+detail layout rather than a live scorecard. */

:root {
  --t-bg:          #07141f;
  --t-surface:     rgba(13, 26, 39, 0.95);
  --t-surface-alt: rgba(18, 36, 54, 0.98);
  --t-ink:         #eff8ff;
  --t-muted:       #93a9bb;
  --t-accent:      #33d1c6;
  --t-accent-dk:   #18bc9c;
  --t-line:        rgba(143, 170, 192, 0.18);
  --t-shadow:      rgba(0, 0, 0, 0.35);
  --t-danger:      #e05252;
  --t-danger-dk:   #c43939;
  --t-success:     #3ecf8e;
  --t-warn:        #e0a852;
  --t-gold:        #f0c040;
  --t-silver:      #aab8c8;
  --t-bronze:      #c87840;
  --t-radius:      12px;
  /* [style-match] Matches main.css's --radius-lg exactly — the main app's
     .btn uses this radius, not --t-radius (12px, reserved for cards/modals/
     inputs, same as main.css's --radius-md). */
  --t-radius-btn:  16px;
  /* [style-match] Matches main.css's dark --card-shadow exactly (main.css:542). */
  --t-card-shadow: 0 18px 40px -28px rgba(0, 0, 0, 0.82);
  --t-font:        system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
}

/* Light theme — same token names as the app's main.css :root[data-theme="light"]
   block, reusing its exact color values so a user switching between the
   scorecard and this standalone page sees one consistent palette. Toggled by
   docs/js/init/theme-toggle.js, which just flips documentElement[data-theme]
   and persists the choice — no page-specific JS needed here. */
:root[data-theme="light"] {
  --t-bg:          #edf4fb;
  --t-surface:     rgba(255, 255, 255, 0.9);
  --t-surface-alt: rgba(244, 249, 255, 0.96);
  --t-ink:         #0f2233;
  /* Darkened from an earlier #62788b (≈4.13:1 on --t-bg, under the 4.5:1 AA
     floor for normal text) — this shade clears AA against --t-bg directly,
     which matters since .t-empty/.t-muted text sometimes sits straight on
     the page background rather than a .t-card. */
  --t-muted:       #586e80;
  --t-accent:      #0ea5a0;
  --t-accent-dk:   #0c8783;
  --t-line:        rgba(104, 137, 163, 0.22);
  --t-shadow:      rgba(36, 71, 102, 0.14);
  --t-danger:      #e74c3c;
  --t-danger-dk:   #c0392b;
  /* --t-success / --t-warn are darkened well past their dark-theme hue (not
     just re-tinted) — the dark values (#3ecf8e mint, #e0a852 amber) drop
     under 2:1 contrast against this theme's near-white surfaces, so this
     mirrors main.css's own --ok-ink / --warn-line light-theme darkening. */
  --t-success:     #0f5b35;
  --t-warn:        #b45309;
  --t-gold:        #c9960c;
  --t-silver:      #7c8b98;
  --t-bronze:      #a8622c;
  /* [style-match] Matches main.css's light --card-shadow exactly (main.css:1562). */
  --t-card-shadow: 0 20px 38px -26px rgba(53, 84, 112, 0.28);
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--t-bg);
  color: var(--t-ink);
  font: 15px/1.5 var(--t-font);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  transition: background 0.15s, color 0.15s;
}

/* ── Header ─────────────────────────────────────────────────────────── */
.t-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  /* viewport-fit=cover (tournament.html) renders this sticky header under a
     notch/Dynamic Island on iOS — reserve the same inset the main app does
     (main.css's --safe-top-dynamic). */
  padding-top: max(14px, env(safe-area-inset-top, 0px));
  background: var(--t-surface);
  border-bottom: 1px solid var(--t-line);
  position: sticky;
  top: 0;
  z-index: 10;
}
.t-header-left {
  display: flex;
  align-items: center;
  gap: 10px;
}
.t-header h1 {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
}
.t-header-right {
  display: flex;
  align-items: center;
  gap: 10px;
}
.t-current-user {
  font-size: 13px;
  color: var(--t-muted);
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Theme toggle — sun/moon swap mirrors the main app's #themeToggle
   (docs/stylesheet/ui-polish.css): show the icon for the mode a tap switches
   TO, hide the other. docs/js/init/theme-toggle.js drives the [data-theme]
   attribute; this file only reacts to it. */
/* .t-btn.t-theme-toggle (not bare .t-theme-toggle): #tThemeToggle carries
   both classes, and .t-btn's own padding/font rule below is declared LATER
   in this file with equal (single-class) specificity, so a bare
   .t-theme-toggle rule here would lose the cascade tie and render as a
   48x32px pill instead of a compact icon button. */
.t-btn.t-theme-toggle {
  padding: 8px;
  line-height: 0;
  min-width: 44px;
  min-height: 44px;
}
.t-theme-toggle .icon-theme-moon { display: none; }
:root[data-theme="light"] .t-theme-toggle .icon-theme-sun { display: none; }
:root[data-theme="light"] .t-theme-toggle .icon-theme-moon { display: block; }

.t-btn.t-menu-toggle {
  padding: 8px;
  line-height: 0;
  min-width: 44px;
  min-height: 44px;
}

/* ── Side menu (hamburger drawer) ────────────────────────────────────────
   [style-match] Values below are copied verbatim from the main app's real
   .unified-menu component (stylesheet/side-menu.css) — same backdrop blur/
   opacity, same drawer width/shadow/easing, same section-title treatment
   (background tint + border) — not just similar mechanics. Still page-local
   CSS (not an import of side-menu.css itself): that file's menu CONTENT is
   almost entirely round-specific (Cloud Options, Game Visibility, My
   Courses…) and meaningless here, and it depends on the main app's --panel/
   --line/--radius-sm/--radius-md tokens, which this page doesn't define
   under those names (it has its own --t-* dictionary instead). z-index set
   above .t-modal-scrim (40) and .qr-modal (80, below) — the menu is
   top-level page navigation, so it should cover any open modal. */
.t-menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  opacity: 0;
  transition: opacity 0.22s ease;
  pointer-events: none;
}
.t-menu-backdrop.is-open { opacity: 1; pointer-events: all; }
.t-menu-backdrop[hidden] { display: none; }
.t-menu {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: min(88vw, 420px);
  z-index: 101;
  background: var(--t-surface-alt);
  border-right: 1px solid var(--t-line);
  box-shadow: 14px 0 34px var(--t-shadow);
  transform: translateX(-104%);
  transition: transform 0.24s cubic-bezier(0.2, 0.8, 0.2, 1);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.t-menu.is-open { transform: translateX(0); }
.t-menu[hidden] { display: none; }
.t-menu-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: max(14px, env(safe-area-inset-top, 0px)) 16px 14px;
  border-bottom: 1px solid var(--t-line);
}
.t-menu-header h2 { margin: 0; font-size: 18px; font-weight: 600; }
.t-menu-content { padding: 0; display: flex; flex-direction: column; }
.t-menu-section {
  padding: 14px 16px 14px;
  margin: 0;
  border-bottom: 1px solid var(--t-line);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.015), rgba(255, 255, 255, 0));
}
.t-menu-section:last-child { border-bottom: none; }
.t-menu-section-title {
  margin: -14px -16px 12px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--t-muted);
  padding: 12px 16px 10px;
  background: rgba(0, 0, 0, 0.05);
  border-bottom: 1px solid var(--t-line);
}
.t-menu-link {
  display: flex;
  width: 100%;
  justify-content: flex-start;
  text-align: left;
  margin-bottom: 6px;
}
.t-menu-link:last-child { margin-bottom: 0; }

/* ── Main layout ─────────────────────────────────────────────────────── */
.t-main {
  max-width: 680px;
  margin: 0 auto;
  padding: 20px 16px 40px;
}
/* Canonical breakpoint scale (see docs/stylesheet/responsive-layout.css):
   desktop min-width:769px, large min-width:1200px. The 18-column hole grid
   and leaderboard table both benefit from the extra room on wide viewports. */
@media (min-width: 769px) {
  .t-main { max-width: 900px; }
}
@media (min-width: 1200px) {
  .t-main { max-width: 1100px; }
}

/* ── Cards ───────────────────────────────────────────────────────────── */
.t-card {
  background: var(--t-surface);
  border: 1px solid var(--t-line);
  border-radius: var(--t-radius);
  padding: 24px;
  box-shadow: var(--t-card-shadow);
  margin-bottom: 16px;
}

/* ── Buttons ─────────────────────────────────────────────────────────────
   [style-match] Ported from the main app's real .btn component
   (stylesheet/header.css:189-208: 2px border, --radius-lg, min-height
   --touch-min, transform/opacity press state) rather than the flatter
   borderless pill this page used before — same shape/press-feel as every
   button in the round-scoring SPA now. */
.t-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px 16px;
  border: 2px solid var(--t-line);
  background: var(--t-surface);
  color: var(--t-ink);
  border-radius: var(--t-radius-btn);
  min-height: 44px;
  font: 15px/1 var(--t-font);
  font-weight: 500;
  cursor: pointer;
  transition: transform 0.1s ease, opacity 0.1s ease, background 0.15s;
  text-decoration: none;
}
.t-btn:active:not(:disabled) { transform: scale(0.96); opacity: 0.82; }
/* [Phase 4] `.t-btn`'s own `display: inline-flex` is an AUTHOR-origin normal
   declaration — it beats the UA stylesheet's `[hidden] { display: none }`
   rule regardless of specificity (author-normal always outranks UA-normal in
   the cascade), so a plain `hidden` attribute on a `.t-btn` element was
   silently a no-op: it stayed visually visible the whole time (confirmed
   live — tStartDayBtn, tAutoAssignFlightsBtn, tSignOutBtn, and every other
   pre-existing conditionally-hidden `.t-btn` were ALL affected, not just the
   new Finalize/Reopen buttons this phase adds). This explicit `[hidden]`
   override — higher specificity than the bare `.t-btn` rule, so it wins on
   its own merits, no cascade-origin subtlety required — restores the
   intended behavior for every hidden `.t-btn` on this page at once. */
.t-btn[hidden] { display: none; }
.t-btn:disabled { opacity: 0.45; cursor: not-allowed; }
/* [style-match] Matches stylesheet/ui-dialogs.css's .ui-dialog-btn--primary
   exactly (background:var(--accent); color:#fff; font-weight:700) — the
   app's actual bold-CTA treatment, not a dark-on-teal fill. */
.t-btn-primary {
  background: var(--t-accent);
  border-color: var(--t-accent);
  color: #fff;
  font-weight: 700;
}
.t-btn-primary:hover:not(:disabled) { background: var(--t-accent-dk); border-color: var(--t-accent-dk); }
/* [style-match] A ghost button is just the neutral bordered .btn with its
   fill dropped out — ordinary .t-btn already carries the border/radius/
   press-state, so this only needs to swap the surface fill for transparent. */
.t-btn-ghost { background: transparent; }
.t-btn-ghost:hover:not(:disabled) { background: color-mix(in srgb, var(--t-ink) 8%, transparent); }
/* [style-match] Matches .ui-dialog-btn--danger exactly (background:var(--danger); color:white). */
.t-btn-danger {
  background: var(--t-danger);
  border-color: var(--t-danger);
  color: #fff;
}
.t-btn-danger:hover:not(:disabled) { background: var(--t-danger-dk); border-color: var(--t-danger-dk); }

/* [perf] this whole page is touch-capable (native shell + mobile web) but every
   interactive control here relies on :hover, which doesn't fire reliably (or at
   all) on a touch tap. Give each one its own instant coarse-pointer press state —
   notes/PERFORMANCE_UPGRADE.md §Appendix E. .t-btn is excluded here now — its
   own base rule above already carries a universal (not coarse-only) press
   state matching stylesheet/header.css's .btn:active exactly. */
@media (pointer: coarse) {
  .t-mode-btn:active,
  .t-game-chip:active,
  .t-code-badge:active,
  .t-group-remove-btn:active {
    transform: scale(0.95);
    filter: brightness(0.85);
    transition: transform 0ms, filter 0ms;
  }
}

/* ── Toolbar ─────────────────────────────────────────────────────────── */
.t-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
  gap: 12px;
}
.t-section-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
}
.t-detail-actions {
  display: flex;
  gap: 8px;
}

/* ── Loading spinner ─────────────────────────────────────────────────── */
.t-spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--t-line);
  border-top-color: var(--t-accent);
  border-radius: 50%;
  animation: t-spin 0.8s linear infinite;
  margin: 0 auto 12px;
}
@keyframes t-spin { to { transform: rotate(360deg); } }

/* Inline variant — shown next to a status line while a Gemini call is in
   flight (custom-format-modal.js's Generate button; ~10-60s observed in
   prod for the sibling per-round custom-game generator, same backend
   pattern). Reuses the SAME t-spin keyframe as the centered boot .t-spinner
   above, just smaller and laid out inline instead of block-centered. */
.t-generating {
  display: flex;
  align-items: center;
  gap: 8px;
}
.t-generating-spinner {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border: 2px solid var(--t-line);
  border-top-color: var(--t-accent);
  border-radius: 50%;
  animation: t-spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .t-generating-spinner { animation-duration: 2.4s; }
}

/* ── Text helpers ────────────────────────────────────────────────────── */
.t-muted  { color: var(--t-muted); margin: 0 0 8px; }
.t-mono   { font-family: 'SFMono-Regular', Consolas, monospace; font-size: 12px; }
.t-empty  { padding: 32px 0; text-align: center; font-size: 14px; }

/* ── Tournament list ─────────────────────────────────────────────────── */
.t-tournament-list { display: flex; flex-direction: column; gap: 10px; }
.t-tournament-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 18px;
  background: var(--t-surface);
  border: 1px solid var(--t-line);
  border-radius: var(--t-radius);
  /* --t-surface and --t-bg are close in luminance in light mode, so without
     a shadow these rows barely separate from the page background — .t-card
     (the only other top-level surface) already gets one. */
  box-shadow: 0 4px 16px var(--t-shadow);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.t-tournament-item:hover {
  border-color: var(--t-accent);
  background: var(--t-surface-alt);
}
/* min-width:0 overrides the flex-item default (min-width:auto), which would
   otherwise let a long tournament name force this row wider than its
   .t-tournament-item flex parent instead of truncating — same fix as the
   ellipsis truncation already used for .t-current-user / .t-code-badge-value. */
.t-tournament-item-info {
  min-width: 0;
  flex: 1 1 auto;
}
.t-tournament-item-name {
  font-weight: 600;
  font-size: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.t-tournament-item-meta {
  font-size: 12px;
  color: var(--t-muted);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.t-tournament-item-arrow {
  color: var(--t-muted);
  flex-shrink: 0;
}

/* ── Form ────────────────────────────────────────────────────────────── */
.t-field-group {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin-bottom: 14px;
}
.t-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 14px;
}
.t-field > label {
  font-size: 13px;
  font-weight: 500;
  color: var(--t-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
/* [style-match] Ported from stylesheet/forms.css's select/.control input
   treatment (2px border, --radius-md) rather than the thinner 1px/8px
   this page used before. */
.t-input {
  background: var(--t-surface-alt);
  border: 2px solid var(--t-line);
  border-radius: var(--t-radius);
  color: var(--t-ink);
  /* 16px, not 15px — under 16px, iOS Safari auto-zooms the viewport on focus. */
  font: 16px/1.2 var(--t-font);
  padding: 10px 12px;
  width: 100%;
  transition: border-color 0.15s;
  -webkit-appearance: none;
}
.t-input:focus {
  outline: none;
  border-color: var(--t-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--t-accent) 25%, transparent);
}
.t-input-narrow { width: auto; min-width: 90px; }
select.t-input { cursor: pointer; }

/* Scoring mode toggle */
.t-mode-group {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.t-mode-btn {
  padding: 7px 14px;
  border: 1px solid var(--t-line);
  border-radius: 20px;
  background: transparent;
  color: var(--t-muted);
  font: 13px/1 var(--t-font);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.t-mode-btn.is-active {
  background: var(--t-accent);
  border-color: var(--t-accent);
  color: #07141f;
  font-weight: 600;
}
.t-mode-btn:not(.is-active):hover { border-color: var(--t-accent); color: var(--t-ink); }

/* Player section */
.t-players-header {
  display: grid;
  gap: 8px;
  margin-bottom: 6px;
  font-size: 12px;
  color: var(--t-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 500;
}
.t-player-row {
  display: grid;
  grid-template-columns: 1fr 80px 80px;
  gap: 8px;
  margin-bottom: 8px;
  align-items: center;
}
.t-form-actions {
  margin-top: 20px;
  display: flex;
  justify-content: flex-end;
}

/* ── Detail view ─────────────────────────────────────────────────────── */
.t-detail-header {
  margin-bottom: 20px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--t-line);
}
.t-detail-title {
  margin: 0 0 6px;
  font-size: 22px;
  font-weight: 700;
}
.t-detail-meta {
  font-size: 13px;
}

/* ── Activity log ─────────────────────────────────────────────────────── */
.t-audit-hint { font-size: 12px; }
.t-audit-log { display: flex; flex-direction: column; gap: 8px; }
.t-audit-entry {
  border: 1px solid var(--t-line);
  border-radius: var(--t-radius);
  padding: 8px 12px;
  background: var(--t-surface-alt);
}
.t-audit-entry-head {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 4px;
}
.t-audit-who { font-weight: 600; font-size: 13px; }
.t-audit-when { color: var(--t-muted); font-size: 11px; }
.t-audit-what { font-size: 13px; color: var(--t-muted); }
.t-audit-what strong { color: var(--t-ink); font-weight: 600; }

/* ── Leaderboard table ───────────────────────────────────────────────── */
.t-leaderboard-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.t-leaderboard-table th {
  text-align: left;
  padding: 8px 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--t-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-bottom: 1px solid var(--t-line);
}
.t-leaderboard-table th.t-num,
.t-leaderboard-table td.t-num {
  text-align: right;
}
.t-leaderboard-table td {
  padding: 10px 10px;
  border-bottom: 1px solid color-mix(in srgb, var(--t-line) 45%, transparent);
}
.t-leaderboard-table tr:last-child td { border-bottom: none; }

.t-player-name {
  font-weight: 600;
  display: inline-block;
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

.t-score-under { color: var(--t-success); font-weight: 700; }
.t-score-over  { color: var(--t-danger); }
.t-score-even  { color: var(--t-ink); }
.t-score-e     { font-weight: 600; }

/* Overall-board rows are tappable — opens the condensed scorecard. */
.t-player-row-btn {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.t-player-row-btn:hover .t-player-name { text-decoration: underline; }

/* ── Generic modal shell (condensed scorecard) ─────────────────────────
   [style-match] Ported from stylesheet/ui-dialogs.css's .ui-dialog-scrim/
   .ui-dialog-card (the app's real confirm-dialog treatment) — blurred
   translucent scrim instead of a flat dark one, same shadow scale. */
.t-modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(12, 18, 26, 0.28);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
/* An author stylesheet's `display` ALWAYS beats the UA stylesheet's
   `[hidden] { display: none }`, regardless of selector specificity — origin
   wins the cascade tie before specificity is even compared. Without this,
   .t-modal-scrim's own `display: flex` above permanently overrides `hidden`
   and the modal renders visible (though empty) from page load. */
.t-modal-scrim[hidden] { display: none; }
.t-modal-card {
  background: var(--t-surface-alt);
  border: 1px solid var(--t-line);
  border-radius: var(--t-radius);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
  width: 100%;
  max-width: 480px;
  /* dvh accounts for the mobile browser chrome (URL bar) collapsing/expanding
     — a static vh cap can clip the modal or leave a stale gap as chrome
     shows/hides. vh first as a fallback for engines without dvh support. */
  max-height: 85vh;
  max-height: 85dvh;
  overflow-y: auto;
}
.t-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--t-line);
  position: sticky;
  top: 0;
  background: var(--t-surface-alt);
}
.t-modal-header h3 { margin: 0; font-size: 16px; }
.t-modal-body { padding: 14px 16px 18px; }

/* Wider variant — Edit Scores (18 columns) and Scan review need more room
   than the 480px default used by the condensed player-card modal. */
.t-modal-card-wide { max-width: 920px; }
.t-modal-card-wide .t-form-actions {
  position: sticky;
  bottom: 0;
  background: var(--t-surface-alt);
  margin: 0;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--t-line);
}

/* ── Edit Scores table (per-player row, hole 1-18 inputs) ────────────────── */
.t-scores-edit-table th, .t-scores-edit-table td { padding: 4px 3px; }
.t-scores-edit-table td.t-card-label { white-space: nowrap; padding-right: 10px; }
.t-score-cell-input {
  width: 30px;
  text-align: center;
  background: var(--t-surface-alt);
  border: 1px solid var(--t-line);
  border-radius: 6px;
  color: var(--t-ink);
  /* 16px, not 13px — under 16px, iOS Safari auto-zooms the viewport on
     focus; space is clawed back from horizontal padding instead. */
  font: 16px/1 var(--t-font);
  padding: 6px 0;
  -webkit-appearance: none;
}
.t-score-cell-input:focus {
  outline: none;
  border-color: var(--t-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--t-accent) 35%, transparent);
}

/* ── Scan review — one mapping row per OCR-detected player ───────────────── */
.t-scan-map-row {
  padding: 10px 0;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--t-line);
}
.t-scan-map-row:last-child { border-bottom: none; }
.t-scan-map-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.t-scan-detected-name { font-weight: 600; font-size: 14px; }
.t-scan-map-select { flex: 1; min-width: 180px; }

/* ── Condensed scorecard table (Front 9 / Back 9, OUT/IN/TOTAL) ─────────── */
.t-card-meta { margin: 0 0 12px; font-size: 13px; color: var(--t-muted); }
.t-card-table-wrap { overflow-x: auto; margin-bottom: 14px; }
.t-card-table {
  border-collapse: collapse;
  font-size: 13px;
  width: 100%;
}
.t-card-table th, .t-card-table td {
  padding: 5px 6px;
  text-align: center;
  min-width: 24px;
}
.t-card-table th {
  color: var(--t-muted);
  font-weight: 500;
  font-size: 11px;
  border-bottom: 1px solid var(--t-line);
}
.t-card-table td.t-card-label {
  text-align: left;
  color: var(--t-muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.t-card-table td.t-card-total {
  font-weight: 700;
  border-left: 1px solid var(--t-line);
}

/* [stroke indicator] Per-hole handicap-stroke badge on a player-card score
   cell — a small corner dot (1-2 strokes) or "+" cross (plus-handicap,
   gives a stroke) — same data-stroke-symbol/data-stroke-dots convention and
   visual language as the live scorecard's own indicator
   (docs/stylesheet/scorecard.css's [data-stroke-symbol] rules), scaled down
   for this table's more compact cells. Always-on here (no highlight/both
   mode, no user toggle) — this is a read-only admin/spectator view, not the
   player's own device, so the live app's per-device STROKE_INDICATOR_MODE
   preference doesn't apply. */
.t-card-table td.t-stroke-cell { position: relative; }
.t-card-table td.t-stroke-cell[data-stroke-symbol]::after {
  content: '';
  position: absolute;
  top: 3px;
  right: 3px;
  color: var(--t-ink);
  pointer-events: none;
  display: block;
}
.t-card-table td.t-stroke-cell[data-stroke-symbol="dot"]::after {
  width: 6px; height: 6px; border-radius: 50%; background: currentColor;
}
.t-card-table td.t-stroke-cell[data-stroke-symbol="dot"][data-stroke-dots="2"]::after {
  width: 13px; height: 6px; border-radius: 0; background:
    radial-gradient(circle, currentColor 3px, transparent 3.5px) 0 0 / 6px 6px no-repeat,
    radial-gradient(circle, currentColor 3px, transparent 3.5px) 7px 0 / 6px 6px no-repeat;
}
.t-card-table td.t-stroke-cell[data-stroke-symbol="plus"]::after {
  width: 8px; height: 8px; background:
    linear-gradient(currentColor 0 0) center / 1.5px 8px no-repeat,
    linear-gradient(currentColor 0 0) center / 8px 1.5px no-repeat;
}

/* ── Tournament-wide Skins config (shown only while the Skins chip is on) ── */
.t-skins-config {
  background: color-mix(in srgb, var(--t-accent) 6%, transparent);
  border: 1px solid var(--t-line);
  border-radius: 10px;
  padding: 12px 14px;
}
.t-skins-config-hint { margin: 2px 0 10px; font-size: 12px; }
.t-skins-config-row {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: flex-end;
}
.t-skins-config-row .t-field { margin-bottom: 0; min-width: 90px; }

/* ── [Phase 5] Payouts / purse sheet config (create/edit form) ─────────── */
.t-payouts-config {
  background: color-mix(in srgb, var(--t-accent) 6%, transparent);
  border: 1px solid var(--t-line);
  border-radius: 10px;
  padding: 12px 14px;
}
.t-payouts-config-hint { margin: 2px 0 10px; font-size: 12px; }
.t-payouts-config-row {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: flex-end;
}
.t-payouts-config-row .t-field { margin-bottom: 0; min-width: 90px; }
.t-payouts-places-sum { margin: 8px 0 0; font-size: 12px; color: var(--t-muted); }
.t-payouts-places-sum.is-valid { color: var(--t-success); }
.t-payouts-places-sum.is-invalid { color: var(--t-danger); font-weight: 600; }

/* ── [Phase 7] Slope/Rating (create/edit form) ──────────────────────────── */
.t-course-rating-row { display: flex; gap: 10px; flex-wrap: wrap; }

/* Payouts board on the Leaderboard itself — an invalid config (places don't
   sum to 100%) explains itself here rather than guessing at a correction. */
.t-payout-invalid {
  margin: 0 0 10px;
  padding: 8px 12px;
  font-size: 13px;
  color: var(--t-warn);
  background: color-mix(in srgb, var(--t-warn) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--t-warn) 35%, transparent);
  border-radius: 8px;
}
.t-payout-purse-line { margin: 0 0 10px; font-size: 12px; }
.t-payout-subtitle { margin: 14px 0 6px; font-size: 12px; font-weight: 600; color: var(--t-muted); text-transform: uppercase; letter-spacing: 0.05em; }

/* ── Course search (StakeMarkerDB picker) ────────────────────────────── */
.t-course-search-field { position: relative; }
.t-course-search-wrap { position: relative; }
.t-course-search-hint { margin: 4px 0 0; font-size: 12px; }
.t-course-search-dropdown {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 4px);
  z-index: 20;
  background: var(--t-surface-alt);
  border: 1px solid var(--t-line);
  border-radius: 10px;
  box-shadow: 0 8px 24px var(--t-shadow);
  max-height: 280px;
  overflow-y: auto;
  padding: 6px;
}
.t-course-search-options { display: flex; flex-direction: column; gap: 2px; }
.t-course-search-option {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: 6px;
  color: var(--t-ink);
  font: 14px/1.3 var(--t-font);
  padding: 8px 10px;
  cursor: pointer;
}
.t-course-search-option:hover,
.t-course-search-option.is-selected { background: color-mix(in srgb, var(--t-accent) 14%, transparent); }
.t-course-search-status { margin: 4px 4px 2px; font-size: 12px; }

/* ── Course hole grid (par / stroke-index per hole) ─────────────────── */
.t-hole-grid-wrap { overflow-x: auto; }
.t-hole-grid {
  display: grid;
  grid-template-columns: 52px repeat(18, 44px);
  gap: 4px;
  align-items: center;
  width: max-content;
}
.t-hole-grid-label {
  font-size: 11px;
  color: var(--t-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.t-hole-grid-num {
  font-size: 11px;
  color: var(--t-muted);
  text-align: center;
}
.t-hole-grid input.t-input {
  padding: 6px 1px;
  text-align: center;
  /* 16px, not 13px — under 16px, iOS Safari auto-zooms the viewport on
     focus; space is clawed back from horizontal padding instead. */
  font-size: 16px;
  /* Hide spinner buttons for a clean look — mirrors custom-courses.css's create-course-table. */
  -moz-appearance: textfield;
}
.t-hole-grid input.t-input[type="number"]::-webkit-inner-spin-button,
.t-hole-grid input.t-input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* ── Side-games multi-select chips (like .t-mode-btn, independent toggles) ── */
.t-games-group { display: flex; gap: 6px; flex-wrap: wrap; }
.t-game-chip {
  padding: 7px 14px;
  border: 1px solid var(--t-line);
  border-radius: 20px;
  background: transparent;
  color: var(--t-muted);
  font: 13px/1 var(--t-font);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.t-game-chip[aria-pressed="true"] {
  background: var(--t-accent);
  border-color: var(--t-accent);
  color: #07141f;
  font-weight: 600;
}
.t-game-chip:not([aria-pressed="true"]):hover { border-color: var(--t-accent); color: var(--t-ink); }
.t-games-group-hint { margin-top: 8px; font-size: 12px; }

/* ── Admins (owner + co-admins, same permissions) ───────────────────── */
.t-admins-section { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid var(--t-line); }
.t-admins-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.t-admin-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  background: var(--t-surface-alt);
  border: 1px solid var(--t-line);
  border-radius: var(--t-radius);
  font-size: 13px;
}
.t-admin-email { color: var(--t-ink); }
.t-add-admin-row { display: flex; gap: 8px; }
.t-add-admin-row .t-input { flex: 1; }
.t-checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-size: 13px;
  color: var(--t-muted);
  cursor: pointer;
}
.t-checkbox-row input[type="checkbox"] { flex-shrink: 0; width: 16px; height: 16px; accent-color: var(--t-accent); cursor: pointer; }
.t-role-tag {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--t-accent);
  background: color-mix(in srgb, var(--t-accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--t-accent) 30%, transparent);
  border-radius: 8px;
  padding: 2px 6px;
  margin-left: 8px;
  vertical-align: middle;
}

/* [Phase 8 — announcements] Rows reuse .t-admin-item verbatim (same
   flex/justify-between/padding as the Admins list and Flights). */
.t-announcements-section { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid var(--t-line); }
.t-announcements-hint { margin-bottom: 10px; }
.t-add-announcement-row { display: flex; gap: 8px; margin-bottom: 10px; }
.t-add-announcement-row .t-input { flex: 1; }
.t-announcements-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.t-announcement-item { align-items: flex-start; gap: 10px; }
.t-announcement-text { flex: 1; white-space: pre-wrap; word-break: break-word; }
.t-announcement-meta { display: block; margin-top: 2px; font-size: 11px; color: var(--t-muted); font-weight: 400; }

/* [Phase 3] Flights / divisions — rows reuse .t-admin-item verbatim (same
   flex/justify-between/padding as the Admins list and Best Ball team rows). */
.t-flights-section { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid var(--t-line); }
.t-flights-hint { margin-bottom: 10px; }
.t-flights-list, .t-flight-players-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.t-flight-item { gap: 10px; flex-wrap: wrap; }
.t-flight-name-input { max-width: 200px; }
.t-add-flight-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.t-add-flight-row .t-input:not(.t-input-narrow) { flex: 1 1 160px; }
.t-flight-players-hint { margin-bottom: 10px; }
.t-flight-player-row select { min-width: 140px; }

/* ── [Phase 7] Tee Sheet — rows reuse .t-admin-item verbatim ────────────── */
.t-teesheet-section { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid var(--t-line); }
.t-teesheet-hint { margin-bottom: 10px; }
.t-teesheet-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.t-teesheet-row { gap: 10px; flex-wrap: wrap; }
.t-teesheet-row .t-player-name { flex: 1 1 120px; font-weight: 600; }
.t-teesheet-row input[type="time"] { max-width: 130px; }
.t-teesheet-row input[type="number"] { max-width: 80px; }

/* ── [Phase 7] Players registry — status (WD/DQ) + course-handicap helper ── */
.t-players-section { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid var(--t-line); }
.t-chc-field {
  background: color-mix(in srgb, var(--t-accent) 6%, transparent);
  border: 1px solid var(--t-line);
  border-radius: 10px;
  padding: 12px 14px;
  margin-bottom: 14px;
}
.t-chc-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.t-chc-row input { max-width: 100px; }
.t-chc-hint { margin: 8px 0 0; font-size: 12px; }
.t-players-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.t-player-row { gap: 10px; flex-wrap: wrap; }
.t-player-row .t-player-name { flex: 1 1 120px; font-weight: 600; }
.t-player-row input[type="text"] { max-width: 80px; }
.t-player-status-group { display: flex; gap: 4px; }

/* ── Groups (setup + detail) ────────────────────────────────────────── */
.t-subsection-title { margin: 24px 0 12px; font-size: 15px; font-weight: 600; }
.t-groups-section .t-toolbar:first-child .t-subsection-title { margin-top: 0; }
.t-groups-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.t-group-item {
  padding: 14px 16px;
  background: var(--t-surface-alt);
  border: 1px solid var(--t-line);
  border-radius: var(--t-radius);
}
.t-group-item-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 8px;
}
.t-group-item-name { font-weight: 600; font-size: 14px; }
.t-group-locked-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 11px;
  font-weight: 600;
  color: var(--t-warn);
  background: color-mix(in srgb, var(--t-warn) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--t-warn) 35%, transparent);
  border-radius: 8px;
  padding: 2px 8px;
  margin-left: 6px;
  vertical-align: middle;
}
.t-group-codes { display: flex; gap: 8px; flex-wrap: wrap; }
.t-code-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--t-accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--t-accent) 30%, transparent);
  font-size: 12px;
  cursor: pointer;
  color: var(--t-ink);
}
.t-code-badge:hover { background: color-mix(in srgb, var(--t-accent) 20%, transparent); }
.t-code-badge-label { color: var(--t-muted); text-transform: uppercase; font-size: 10px; letter-spacing: 0.04em; }
.t-code-badge-value { font-family: 'SFMono-Regular', Consolas, monospace; font-weight: 600; letter-spacing: 0.05em; }
.t-group-remove-btn {
  display: inline-flex;
  align-items: center;
  background: none;
  border: none;
  color: var(--t-muted);
  font-size: 12px;
  cursor: pointer;
  /* min-height (not just padding) guarantees a safe tap size even for the
     shortest label ("✕" on the Best Ball "Remove team" tile) — this class
     covers every action on a group card: Edit Players/Scores, Scan
     Scorecard, Lock/Unlock, and the destructive Remove group/admin/team. */
  min-height: 40px;
  padding: 10px 12px;
}
.t-group-remove-btn:hover { color: var(--t-danger); }
.t-add-group-form { margin-top: 4px; }

/* [Phase 2.2] Playoffs subsection of the Groups list — same group-item cards,
   just a small heading separating them from the day-tabbed ordinary groups. */
.t-playoffs-subheading { margin: 16px 0 10px; font-size: 13px; font-weight: 600; color: var(--t-muted); }

/* ── Best Ball teams (cross-group partner pairing) ──────────────────── */
/* Roster tiles reuse .t-game-chip verbatim (same pill shape, same
   [aria-pressed="true"] selected state — the tile markup sets aria-pressed
   to the same value as data-selected) — only the paired/disabled look below
   is genuinely new. Team rows reuse .t-admin-item and their remove button
   reuses .t-group-remove-btn as-is (byte-identical layouts already). */
.t-bestball-teams-section { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid var(--t-line); }
.t-bestball-teams-hint { margin-bottom: 10px; }
.t-bestball-roster { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.t-bestball-player-tile[data-paired="true"] { opacity: 0.4; cursor: default; }
.t-bestball-teams-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }

/* ── Day tabs (Groups list + Best Ball pairing day selector) ────────────
   Reuses .t-mode-btn verbatim for each tab pill (same look/feel as every
   other segmented toggle on this page) — only the flex-wrap row is new. */
.t-day-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.t-day-tabs[hidden] { display: none; }
.t-toolbar-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ── Detail-view section tabs ────────────────────────────────────────────
   Same pill strip as the Day tabs above (.t-mode-btn), one level up —
   splits the 10 detail-view sections into 5 grouped tabs (setDetailTab in
   main.js). Slightly larger than the in-section Day pills since this is
   primary navigation, not a data filter. Sticky just under the page's own
   sticky .t-header (z-index 10) so switching tabs never requires scrolling
   back to the top of a long panel. The top offset approximates the
   header's actual height (padding-top max(14px, safe-area) + padding-
   bottom 14px + ~44px button row) rather than measuring it in JS — a few
   px of slop here is cosmetic only. */
.t-detail-tabs {
  position: sticky;
  top: calc(max(14px, env(safe-area-inset-top, 0px)) + 58px);
  z-index: 5;
  background: var(--t-surface);
  margin: 0 -24px 16px;
  padding: 10px 24px;
  border-bottom: 1px solid var(--t-line);
}
.t-detail-tabs .t-mode-btn { padding: 10px 16px; font-size: 14px; }
.t-tabpanel[hidden] { display: none; }

/* ── Start Day N pairing editor ──────────────────────────────────────── */
.t-startday-add-player { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.t-startday-add-player .t-input { flex: 1 1 160px; }
.t-startday-selected-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 12px;
  margin-bottom: 14px;
  background: color-mix(in srgb, var(--t-accent) 15%, transparent);
  border: 1px solid var(--t-accent);
  border-radius: 8px;
  font-size: 13px;
}
.t-startday-cards { display: flex; flex-direction: column; gap: 14px; }
.t-startday-card { padding: 14px; }
.t-startday-card .t-toolbar { margin-bottom: 10px; }
.t-startday-card-name { max-width: 220px; }

/* ── Leaderboard: overall + per-game boards ─────────────────────────── */
.t-leaderboard-section { margin-top: 28px; }
/* overflow-x: a long player/group name can push .t-leaderboard-table wider
   than the card; scroll the table locally instead of bleeding past the
   card edge or forcing page-level horizontal scroll. */
.t-board-card { margin-bottom: 20px; overflow-x: auto; }
.t-board-title { margin: 0 0 8px; font-size: 14px; font-weight: 600; }
.t-group-tag {
  display: inline-block;
  font-size: 11px;
  color: var(--t-muted);
  margin-left: 6px;
  font-weight: 400;
}
.t-money-pos { color: var(--t-success); font-weight: 700; }
.t-money-neg { color: var(--t-danger); }

/* [Phase 2.2] Sudden-death playoff banner (status line + "Tied — Start
   Playoff" button) — sits between a board's title and its table. */
.t-playoff-banner { margin: 0 0 10px; font-size: 13px; }
.t-start-playoff-btn { font-size: 12px; padding: 6px 12px; }
.t-link-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--t-accent);
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
}
.t-money-even { color: var(--t-muted); }

/* ── [Phase 4] Lifecycle: 🏁 FINAL badge + Completed list section ──────── */
.t-final-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--t-warn);
  background: color-mix(in srgb, var(--t-warn) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--t-warn) 35%, transparent);
  border-radius: 8px;
  padding: 3px 9px;
  margin-left: 10px;
  vertical-align: middle;
}
/* [Phase 6 fix — same class of bug Phase 4 fixed for .t-btn] This author-
   origin `display: inline-flex` above always outranks the UA stylesheet's
   `[hidden] { display: none }` regardless of the `hidden` ATTRIBUTE actually
   being set — confirmed live via getComputedStyle while building the Phase 6
   spectator view's own #tSpectatorFinalBadge (same class), which turned out
   to affect the pre-existing #tFinalBadge on the console detail view too
   (always visible; nobody had verified it with getComputedStyle since
   Phase 4 shipped it). Explicit higher-specificity override, same fix shape. */
.t-final-badge[hidden] { display: none; }
/* Same badge, shrunk to fit inline next to a list-row title. */
.t-final-badge-sm { font-size: 10px; padding: 2px 6px; margin-left: 6px; }
.t-completed-section { margin-top: 20px; }
.t-completed-summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--t-muted);
  padding: 8px 0;
  list-style: none;
}
.t-completed-summary::-webkit-details-marker { display: none; }
.t-completed-summary::before { content: '▸ '; }
.t-completed-section[open] .t-completed-summary::before { content: '▾ '; }
.t-completed-section .t-tournament-list { margin-top: 8px; opacity: 0.85; }

/* ── Responsive ──────────────────────────────────────────────────────── */
@media (max-width: 480px) {
  .t-main { padding: 14px 10px 40px; }
  .t-card { padding: 16px; }
  .t-field-group { grid-template-columns: 1fr; }
  .t-player-row { grid-template-columns: 1fr 72px 72px; }
  .t-current-user { display: none; }
  .t-mode-group { gap: 4px; }
  /* Taller than the desktop 7px vertical padding above despite the smaller
     font — phones are exactly where a touch target should NOT shrink. */
  .t-mode-btn { padding: 9px 12px; font-size: 12px; }
  .t-hole-grid { grid-template-columns: 44px repeat(18, 38px); }
  .t-group-item-head { flex-direction: column; align-items: flex-start; }
  .t-add-admin-row { flex-direction: column; }
}

/* ── [Phase 4] Print — hide chrome, let the boards paginate cleanly ─────
   Triggered by the Print button (window.print()) or the browser's own
   print command; also matches a user printing straight from a share link. */
@media print {
  .t-header,
  .t-theme-toggle,
  .t-toolbar,
  .t-detail-tabs,
  .t-admins-section,
  .t-share-section,
  .t-flights-section .t-add-flight-row,
  .t-groups-section .t-add-group-form,
  #tStartDayPanel,
  .t-bestball-teams-section .t-form-actions,
  .t-audit-section,
  .t-leaderboard-section .t-toolbar-actions,
  #tRefreshLeaderboardBtn,
  #tExportCsvBtn,
  #tPrintBtn,
  .t-playoff-banner .t-start-playoff-btn,
  .t-group-remove-btn,
  #tPlayerCardPanel,
  #tEditScoresPanel,
  #tScanPanel,
  #tGroupScorecardPanel {
    display: none !important;
  }
  body { background: #fff; color: #000; }
  .t-main { padding: 0; max-width: none; }
  .t-card { border: none; box-shadow: none; padding: 0; background: #fff; }
  /* One board per printed page where possible — a long tournament's per-
     game/per-day boards otherwise split awkwardly mid-table. */
  .t-board-card { break-inside: avoid; page-break-inside: avoid; margin-bottom: 16px; }
  .t-leaderboard-table { color: #000; }
  .t-money-pos, .t-money-neg, .t-score-under, .t-score-over { color: #000 !important; font-weight: 700; }
  /* [Phase 7] Tee Sheet — flatten the time/hole inputs to plain text so a
     printed tee sheet reads as a real list, not a form. */
  .t-teesheet-row input { border: none; background: transparent; padding: 0; color: #000; }
}

/* ── [Phase 6] Public spectator link — Share card ───────────────────────── */
.t-share-section { margin-top: 24px; }
.t-share-hint { margin: 0 0 12px; font-size: 13px; }
.t-share-row { display: flex; gap: 8px; flex-wrap: wrap; }
.t-share-link-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 12px; }
.t-share-link-row .t-input { flex: 1 1 220px; }
/* Same [hidden]-vs-author-`display` fix as .t-btn[hidden] (Phase 4) and
   .t-final-badge[hidden] (Phase 6, above) — renderShareSection() toggles
   #tShareLinkRow's `hidden` attribute directly. */
.t-share-row[hidden], .t-share-link-row[hidden] { display: none; }
#tShareTvLink { text-decoration: none; }

/* ── [Phase 6] QR share modal (docs/js/cloudsync/qr/qr-code.js, reused
   as-is) — class names below are hardcoded inside that shared module. These
   rules exist purely to skin it with THIS page's tokens; tournament.html
   never loads the main app's stylesheet/forms.css (where the same classes
   are styled for app.html/index.html), so without this the modal would
   render with no positioning/theme at all. */
.qr-modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 80;
  padding: 20px;
}
.qr-modal-card {
  background: var(--t-surface-alt);
  color: var(--t-ink);
  border: 1px solid var(--t-line);
  border-radius: var(--t-radius);
  max-width: 360px;
  width: 100%;
  max-height: calc(100dvh - 40px);
  overflow-y: auto;
  padding: 20px;
  text-align: center;
  box-shadow: 0 12px 32px var(--t-shadow);
}
.qr-modal-title { margin: 0 0 8px; font-size: 17px; }
.qr-modal-subtitle { margin: 0 0 14px; font-size: 13px; color: var(--t-muted); }
.qr-modal-qr-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 10px 0 14px;
  background: #fff;
  border-radius: 10px;
  padding: 10px;
}
.qr-modal-link-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  font-size: 13px;
  border: 1px solid var(--t-line);
  border-radius: 8px;
  background: var(--t-surface);
  color: var(--t-ink);
}
/* The module's Close button is hardcoded to className 'btn' — mirror .t-btn
   scoped to just this modal rather than introducing a bare .btn globally. */
.qr-modal .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 18px;
  border-radius: 8px;
  border: 1px solid var(--t-line);
  background: var(--t-surface);
  color: var(--t-ink);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.qr-modal .btn:hover { background: color-mix(in srgb, var(--t-ink) 12%, transparent); }

/* ── [Phase 6] Public spectator view — booted from ?public=<code> ──────── */
.t-spectator-view { max-width: 720px; margin: 0 auto; padding: 8px 0 40px; }
.t-spectator-header {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}
.t-spectator-header h1 { margin: 0; font-size: 22px; }
#tSpectatorUpdated { width: 100%; margin: 0; }

/* TV mode: hide the page chrome, scale everything up, auto-cycle one board
   card at a time (visibility toggled in JS — see applyTvCycle() in
   docs/js/tournament/main.js). */
body.t-tv-mode .t-header { display: none; }
body.t-tv-mode .t-spectator-view { max-width: 1100px; padding-top: 24px; }
body.t-tv-mode .t-spectator-header h1 { font-size: 40px; }
body.t-tv-mode .t-board-title { font-size: 24px; }
body.t-tv-mode .t-leaderboard-table { font-size: 20px; }
body.t-tv-mode .t-leaderboard-table th,
body.t-tv-mode .t-leaderboard-table td { padding: 12px 10px; }
body.t-tv-mode .t-day-tabs,
body.t-tv-mode #tSpectatorUpdated { font-size: 16px; }
body.t-tv-mode .t-player-row-btn { cursor: default; }

/* ── Group Print Scorecard modal ─────────────────────────────────────────
   #tGroupScorecardPanel — reuses .t-modal-scrim/.t-modal-card-wide/
   .t-card-table (same table styling as the Edit Scores modal) verbatim;
   only the QR wrap and the scorecard header line below are new. */
.t-scorecard-print-header { margin-bottom: 14px; }
.t-scorecard-print-header h4 { margin: 0 0 4px; font-size: 16px; }
.t-qr-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: 18px 0 4px;
  padding: 16px;
  background: #fff;
  border-radius: var(--t-radius);
}
.t-qr-wrap canvas { display: block; width: 180px; height: 180px; }
.t-qr-wrap-link {
  text-align: center;
  word-break: break-all;
  user-select: all;
}

/* [Print Scorecard] Scoped to body.t-printing-group-scorecard, toggled in
   main.js right before window.print() (and removed on window.onafterprint)
   — deliberately separate from the whole-page Leaderboard @media print
   block above, which explicitly hides every modal including this one. The
   two print flows never run at the same time (the class is only ever set
   for the duration of this specific print call), so there's no ordering
   conflict between the two rules. Classic "print only element X" trick:
   hide everything, then re-reveal just the target subtree. */
@media print {
  body.t-printing-group-scorecard > *:not(#tGroupScorecardPanel) { display: none !important; }
  /* [blank-page fix] #tGroupScorecardPanel (like every other modal panel on this
     page) is actually a child of <main id="tMain">, not a direct child of <body> —
     the rule above hides <main> itself (it isn't #tGroupScorecardPanel), which took
     the panel down with it regardless of its own display:block override below,
     producing a blank printed page. #tMain's own id selector outranks the `>
     *:not(...)` universal rule above, so this reveals <main> as a non-boxed
     passthrough (display:contents — no .t-main padding/max-width wrapping the
     print), then the next rule hides <main>'s OTHER children (the list/create/
     detail views + every other modal panel), leaving only the scorecard panel. */
  body.t-printing-group-scorecard #tMain { display: contents !important; }
  body.t-printing-group-scorecard #tMain > *:not(#tGroupScorecardPanel) { display: none !important; }
  body.t-printing-group-scorecard,
  body.t-printing-group-scorecard #tGroupScorecardPanel { background: #fff; color: #000; }
  body.t-printing-group-scorecard #tGroupScorecardPanel {
    position: static;
    display: block !important;
    inset: auto;
    padding: 0;
  }
  body.t-printing-group-scorecard #tGroupScorecardPanel .t-modal-header,
  body.t-printing-group-scorecard #tGroupScorecardPanel .t-form-actions {
    display: none !important;
  }
  body.t-printing-group-scorecard #tGroupScorecardPanel .t-modal-card {
    max-width: none;
    max-height: none;
    overflow: visible;
    box-shadow: none;
    border: none;
    background: #fff;
    color: #000;
  }
  body.t-printing-group-scorecard .t-card-table { color: #000; }
  body.t-printing-group-scorecard .t-score-under,
  body.t-printing-group-scorecard .t-score-over { color: #000 !important; font-weight: 700; }
  /* [write-in grid] A printed scorecard exists to be filled in BY HAND during the
     round (then re-entered via Scan Scorecard) — a tiny '—' character doesn't read
     as "write here". Every cell gets a real border (a proper grid, not just the
     header/total rule lines the on-screen view uses) and enough height for a
     legible handwritten digit; blank cells additionally hide their '—' text
     (color: transparent, not display:none — keeps the cell's own width/alignment
     identical to a filled cell, so hole columns don't jitter between blank and
     scored players) so they read as an empty box, not literal dashes. Cells that
     already carry a real score (t-score-under/-over/-even) are untouched — those
     numbers stay printed and readable, e.g. printing mid-round to hand off a
     partially-played card. */
  body.t-printing-group-scorecard .t-card-table th,
  body.t-printing-group-scorecard .t-card-table td { border: 1px solid #000; }
  body.t-printing-group-scorecard .t-card-table td { min-height: 26px; height: 26px; font-size: 14px; }
  body.t-printing-group-scorecard .t-card-table td.t-card-blank { color: transparent; }
  body.t-printing-group-scorecard .t-qr-wrap { padding: 0; }
}
