/* Single fixed dark theme — deliberately no prefers-color-scheme switching.
   TEST PALETTE: "Black and Gold Elegance" — near-black page, navy cards,
   white text, one warm gold accent for everything that needs to pop
   (primary buttons, the "brand", success/positive states). */
:root {
  /* Hints native widgets (select popups, scrollbars, date pickers) to
     render with a dark palette — this is a fixed hint, not a toggle;
     the app has exactly one theme. */
  color-scheme: dark;
  --bg: #0a0e1a;
  --surface: #14213d;
  /* A flat "shell" tone for panels that hold a grid of cards — sits
     between --bg and --surface so cards read as a distinct layer on top
     of it without needing a border. */
  --surface-muted: color-mix(in srgb, #0a0e1a 60%, #14213d 40%);
  --text: #ffffff;
  --text-muted: #9aa3ba;
  --border: #253253;
  --brand: #fca311;
  --brand-contrast: #14213d;
  --accent: #fca311;
  --danger: #e5484d;
  --danger-contrast: #ffffff;
  --success: #fca311;
  --radius: 22px;

  /* Brand palette. Only one true accent hue exists (the gold), so the
     three "accent-*" tokens differentiate by temperature/brightness
     instead of by hue: warm gold (primary), white (bright neutral),
     soft light gray (tertiary / "team B" in any two-team comparison). */
  --ink: #14213d;
  --pale: #fca311;
  --accent-blue: #fca311;
  --accent-coral: #e5e5e5;
  --accent-green: #ffffff;
  --rail-bg: #000000;
}

* {
  box-sizing: border-box;
}

/* Thinner, classier type — browsers default headings to bold (700); this
   keeps them a lighter, more refined weight everywhere unless a more
   specific rule says otherwise. */
h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: 500;
  letter-spacing: -0.01em;
}

/* Guarantees the `hidden` attribute always wins, regardless of what other
   `display` a more specific rule sets on the same element (e.g. a flex
   toggle) — an unqualified author rule otherwise beats the UA stylesheet's
   own `[hidden] { display: none }`, no matter its specificity. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  font-family:
    'Albert Sans',
    -apple-system,
    BlinkMacSystemFont,
    'Segoe UI',
    Roboto,
    Helvetica,
    Arial,
    sans-serif;
  line-height: 1.5;
}

a {
  color: var(--brand);
  text-decoration: none;
}

.icon {
  display: inline-block;
  vertical-align: middle;
  flex-shrink: 0;
}

/* The ball glyph is the "logo" — always brand blue, regardless of the
   surrounding text color (dark on light topbars, white on the dark rail). */
.brand .icon,
.rail-logo .icon {
  color: var(--brand);
}

.site-header {
  position: sticky;
  top: 14px;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 14px 20px;
  padding: 10px 16px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

/* Floats over the hero photo instead of sitting in its own strip above it —
   fixed (not sticky) so it stays put over the photo the moment the page
   loads, with a frosted-glass treatment (translucent + blur) since a solid
   background would just look like a second bar sitting on the image. */
.site-header-overlay {
  position: fixed;
  top: 14px;
  left: 20px;
  right: 20px;
  margin: 0;
  background: rgba(28, 32, 46, 0.45);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-color: rgba(255, 255, 255, 0.12);
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 500;
  font-size: 1.15rem;
  color: var(--text);
}

.brand-logo-crop {
  display: inline-block;
  vertical-align: middle;
}

.brand-logo-icon {
  width: 26px;
  height: 26px;
  object-fit: contain;
  vertical-align: middle;
}

.brand-name {
  text-transform: uppercase;
}

.brand-pro {
  font-weight: 700;
  color: var(--brand);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 10px;
}

.nav-pill {
  display: inline-flex;
  align-items: center;
  padding: 9px 18px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--text-muted);
}

.nav-pill:hover {
  background: var(--bg);
  color: var(--text);
}

.nav-pill-active {
  background: var(--brand);
  color: var(--brand-contrast);
}

.nav-pill-active:hover {
  background: var(--brand);
  color: var(--brand-contrast);
  filter: brightness(1.15);
}

.nav-pill-primary {
  background: var(--accent-blue);
  color: var(--ink);
}

.nav-pill-primary:hover {
  background: var(--accent-blue);
  filter: brightness(1.05);
  color: var(--ink);
}

.nav-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--accent-coral);
  color: var(--ink);
  font-weight: 500;
  font-size: 0.85rem;
  flex-shrink: 0;
}

/* Mobile nav: hidden on desktop, where the full .nav-links pill row already
   fits. Below the breakpoint, one "main" pill (Pricing, or Dashboard once
   logged in — see nav.js) stays exposed and everything else moves into the
   hamburger's dropdown, instead of every pill trying to squeeze into the
   header and overflowing it. */
.nav-mobile {
  display: none;
  align-items: center;
  gap: 8px;
  position: relative;
}

.nav-menu-panel {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 160px;
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
  z-index: 30;
}

.nav-menu-link {
  display: block;
  width: 100%;
  padding: 10px 14px;
  border-radius: 10px;
  border: none;
  background: transparent;
  color: var(--text);
  font-weight: 600;
  font-size: 0.9rem;
  text-align: left;
  cursor: pointer;
}

.nav-menu-link:hover {
  background: var(--bg);
}

.nav-menu-user {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px 10px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 0.85rem;
}

@media (max-width: 600px) {
  .nav-links {
    display: none;
  }

  .nav-mobile {
    display: flex;
  }
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  cursor: pointer;
  flex-shrink: 0;
}

.icon-btn:hover {
  background: var(--bg);
  color: var(--text);
}

.nav-greeting {
  color: var(--text-muted);
  font-size: 0.9rem;
}

.btn {
  display: inline-block;
  padding: 8px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 0.95rem;
  cursor: pointer;
  transition: filter 0.15s ease, background-color 0.15s ease, transform 0.05s ease;
}

.btn:hover {
  filter: brightness(0.94);
}

.btn:active {
  filter: brightness(0.88);
  transform: translateY(1px);
}

.btn-primary {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-contrast);
  font-weight: 600;
}

.btn-secondary {
  background: transparent;
}

.btn-secondary:hover {
  background: var(--border);
  filter: none;
}

.btn-danger {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--danger-contrast);
  font-weight: 600;
}

main {
  max-width: 720px;
  margin: 0 auto;
  padding: 48px 24px;
}

/* Full-bleed arena photo behind the hero copy — a gradient scrim keeps the
   text readable over the busy crowd/court photo and fades to the plain
   page --bg at the bottom so it blends into the .features section below
   with no hard edge. */
/* Completed-match screens (organizer tracker summary and public watch page
   alike) get the same arena-photo backdrop as the landing hero — fixed
   attachment so `cover` sizes against the viewport (not the page's full
   scroll height) and the photo stays put as a backdrop while the stats
   scroll over it. An extra bottom-black layer on top of the hero's own
   gradient gives the last stretch of the page a proper fade to black. */
.match-done-bg {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, 0.9) 100%),
    linear-gradient(180deg, rgba(10, 14, 26, 0.55) 0%, rgba(10, 14, 26, 0.88) 60%, var(--bg) 100%),
    url('/public/images/volleypro-hero.jpg') center 35% / cover no-repeat;
  background-attachment: fixed, fixed, fixed;
}

.hero-photo {
  background:
    linear-gradient(180deg, rgba(10, 14, 26, 0.55) 0%, rgba(10, 14, 26, 0.88) 60%, var(--bg) 100%),
    url('/public/images/volleypro-hero.jpg') center 35% / cover no-repeat;
}

.hero {
  max-width: 960px;
  margin: 0 auto;
  padding: 96px 24px 64px;
  text-align: center;
}

.brand-logo {
  display: block;
  width: 180px;
  margin: 0 auto 24px;
}

.hero h1 {
  font-size: clamp(2rem, 4vw, 3rem);
  margin: 0 0 16px;
}

.hero p.lede {
  color: var(--text-muted);
  font-size: 1.15rem;
  max-width: 620px;
  margin: 0 auto 32px;
}

.hero-ctas {
  display: flex;
  justify-content: center;
  gap: 12px;
}

.court-demo {
  max-width: 820px;
  margin: 0 auto;
  padding: 24px 24px 72px;
  text-align: center;
}

.court-demo h2 {
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  margin: 0 0 10px;
}

.court-demo-lede {
  color: var(--text-muted);
  margin: 0 0 32px;
}

.court-demo-scoreboard {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 24px;
  margin-bottom: 20px;
}

.court-demo-team {
  display: flex;
  align-items: center;
  gap: 10px;
}

.court-demo-team-name {
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.95rem;
}

.court-demo-score {
  font-size: 2rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  min-width: 1.1em;
  text-align: center;
}

.court-demo-set {
  color: var(--text-muted);
  font-size: 0.8rem;
  padding: 5px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
}

.court-demo-surface {
  position: relative;
  aspect-ratio: 2.2 / 1;
  background: var(--brand);
  border-radius: 16px;
  border: 3px solid #fff;
  display: flex;
  overflow: hidden;
}

/* 3m attack lines — same 33%/67% convention as the real tracker/watch court. */
.court-demo-surface::before,
.court-demo-surface::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: rgba(255, 255, 255, 0.75);
}

.court-demo-surface::before {
  left: 33.333%;
}

.court-demo-surface::after {
  left: 66.667%;
}

.court-demo-side {
  flex: 1;
  position: relative;
}

.court-demo-dot {
  position: absolute;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: rgba(20, 33, 61, 0.55);
  border: 2px solid rgba(255, 255, 255, 0.65);
  transform: translate(-50%, -50%);
}

.features {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 24px 96px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 28px;
  position: relative;
  background: radial-gradient(ellipse 800px 400px at 50% 0%, color-mix(in srgb, var(--brand) 10%, transparent), transparent 70%);
}

/* Big, pronounced "glass" panels — a translucent tint + blur + soft border,
   same frosted-glass language as the floating nav over the hero photo. */
.feature-card {
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 28px;
  padding: 36px;
}

.feature-card h3 {
  margin: 0 0 12px;
  font-size: 1.4rem;
}

.feature-card p {
  margin: 0;
  color: var(--text-muted);
  font-size: 1.05rem;
  line-height: 1.55;
}

.live-preview {
  max-width: 1100px;
  margin: 0 auto;
  padding: 40px 24px 96px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: center;
}

.live-preview-copy h2 {
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  margin: 0 0 16px;
}

.live-preview-copy p {
  color: var(--text-muted);
  font-size: 1.05rem;
  line-height: 1.65;
  margin: 0;
}

.live-preview-scoreboard {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 28px;
}

.how-it-works {
  max-width: 1100px;
  margin: 0 auto;
  padding: 40px 24px 96px;
  text-align: center;
}

.how-it-works h2 {
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  margin: 0 0 40px;
}

.how-steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 28px;
  text-align: left;
}

.how-step {
  background: var(--surface);
  border-radius: 24px;
  padding: 32px;
}

.how-step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--brand);
  color: var(--brand-contrast);
  font-weight: 700;
  margin-bottom: 16px;
}

.how-step h3 {
  margin: 0 0 8px;
  font-size: 1.15rem;
}

.how-step p {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.95rem;
  line-height: 1.5;
}

.final-cta {
  width: 100%;
  background: linear-gradient(135deg, var(--brand), color-mix(in srgb, var(--brand) 65%, #ff7a00 35%));
  padding: 96px 24px;
  text-align: center;
}

.final-cta-inner {
  max-width: 680px;
  margin: 0 auto;
}

.final-cta h2 {
  color: var(--brand-contrast);
  font-size: clamp(2rem, 4.5vw, 3.2rem);
  font-weight: 800;
  line-height: 1.15;
  margin: 0 0 16px;
}

.final-cta p {
  color: color-mix(in srgb, var(--brand-contrast) 75%, transparent);
  font-size: 1.1rem;
  margin: 0 0 36px;
}

.final-cta .btn {
  background: var(--brand-contrast);
  border-color: var(--brand-contrast);
  color: var(--brand);
  font-weight: 700;
  font-size: 1.1rem;
  padding: 18px 44px;
}

.final-cta .btn:hover {
  filter: brightness(1.25);
}

.pricing-page {
  max-width: 640px;
  margin: 0 auto;
  padding: 64px 24px 120px;
  text-align: center;
}

.pricing-header h1 {
  font-size: clamp(2rem, 4vw, 2.8rem);
  margin: 0 0 16px;
}

.pricing-header .lede {
  color: var(--text-muted);
  font-size: 1.1rem;
  margin: 0 0 48px;
}

.pricing-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 28px;
  padding: 48px 40px;
}

.pricing-price {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 4px;
  margin-bottom: 12px;
}

.pricing-currency {
  font-size: 1.8rem;
  font-weight: 700;
  margin-top: 10px;
}

.pricing-amount {
  font-size: 5rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.pricing-period {
  align-self: center;
  color: var(--text-muted);
  font-size: 1.1rem;
  margin-left: 4px;
}

.pricing-sub {
  color: var(--text-muted);
  font-size: 0.95rem;
  max-width: 420px;
  margin: 0 auto 32px;
  line-height: 1.5;
}

.pricing-features {
  list-style: none;
  margin: 0 0 36px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
  text-align: left;
  max-width: 340px;
  margin-left: auto;
  margin-right: auto;
}

.pricing-features li {
  position: relative;
  padding-left: 28px;
  font-size: 1rem;
}

.pricing-features li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--brand);
}

.pricing-features li::after {
  content: '';
  position: absolute;
  left: 6px;
  top: 8px;
  width: 6px;
  height: 10px;
  border-right: 2px solid var(--brand-contrast);
  border-bottom: 2px solid var(--brand-contrast);
  transform: rotate(45deg);
}

.pricing-cta {
  width: 100%;
  padding: 14px;
  font-size: 1.05rem;
  font-weight: 600;
}

.site-footer {
  max-width: 1100px;
  margin: 0 auto;
  padding: 32px 24px 48px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 0.85rem;
}

.site-footer-links {
  display: flex;
  gap: 20px;
}

.site-footer-links a {
  color: var(--text-muted);
}

.site-footer-links a:hover {
  color: var(--text);
}

.legal-page {
  max-width: 760px;
  margin: 0 auto;
  padding: 56px 24px 96px;
}

.legal-page h1 {
  font-size: clamp(1.8rem, 3.5vw, 2.4rem);
  margin: 0 0 8px;
}

.legal-page .legal-updated {
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 0 0 40px;
}

.legal-page h2 {
  font-size: 1.25rem;
  margin: 40px 0 12px;
}

.legal-page h2:first-of-type {
  margin-top: 0;
}

.legal-page p,
.legal-page li {
  color: var(--text-muted);
  line-height: 1.65;
  font-size: 0.98rem;
}

.legal-page ul,
.legal-page ol {
  padding-left: 22px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.legal-page a {
  color: var(--brand);
}

.legal-page strong {
  color: var(--text);
}

.tracking-preview {
  max-width: 1100px;
  margin: 0 auto;
  padding: 40px 24px 96px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: center;
}

.tracking-preview-copy h2 {
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  margin: 0 0 16px;
}

.tracking-preview-copy p {
  color: var(--text-muted);
  font-size: 1.05rem;
  line-height: 1.65;
  margin: 0;
}

/* Gold "court" backdrop behind the card mockup — same color the real
   court surface uses, so this reads as "a slot on the court" at a glance. */
.tracking-preview-card-wrap {
  background: var(--brand);
  border-radius: var(--radius);
  padding: 28px;
}

.tracking-preview-card {
  width: 100%;
  height: 190px;
  cursor: default;
}

.tracking-preview-taxonomy {
  margin: 20px 0 0;
  color: var(--brand-contrast);
  font-size: 0.85rem;
  font-weight: 600;
  text-align: center;
  line-height: 1.5;
}

@media (max-width: 760px) {
  .live-preview,
  .tracking-preview {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .features {
    grid-template-columns: 1fr;
  }
}

form.auth-form {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 32px;
  max-width: 380px;
  margin: 0 auto;
}

form.auth-form h1 {
  font-size: 1.4rem;
  margin: 0 0 24px;
}

.field {
  margin-bottom: 16px;
}

.field label {
  display: block;
  font-size: 0.9rem;
  margin-bottom: 6px;
  color: var(--text-muted);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 1rem;
}

.form-error {
  color: var(--danger);
  font-size: 0.9rem;
  margin-bottom: 16px;
  display: none;
}

.form-error.visible {
  display: block;
}

.form-submit {
  width: 100%;
  padding: 12px;
  font-size: 1rem;
}

.form-footer {
  margin-top: 20px;
  font-size: 0.9rem;
  color: var(--text-muted);
  text-align: center;
}

/* Dashboard */

/* App shell v2 — a slim icon-only rail (site-wide primary nav) plus a
   light top bar, replacing the earlier photo sidebar which didn't scale to
   every screen (esp. the live tracker's own full-bleed layout). */
.app-shell {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
}

.rail {
  position: sticky;
  top: 0;
  align-self: flex-start;
  flex: 0 0 76px;
  height: 100vh;
  background: var(--rail-bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 0 24px;
  gap: 28px;
}

.rail-logo {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  position: relative;
}

.rail-logo-img {
  width: 26px;
  height: 26px;
  object-fit: contain;
}

.rail-nav {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rail-icon {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: rgba(255, 255, 255, 0.55);
  font-size: 1.2rem;
  background: transparent;
  border: none;
  cursor: pointer;
  position: relative;
}

.rail-icon:hover {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
}

.rail-icon.active {
  background: #fff;
  color: var(--ink);
}

/* Rail is icon-only, so a hover tooltip is the only way to know what each
   button does — a plain floating label to the right of the icon, shown on
   hover/focus only (the rail itself never scrolls/clips, so no need to
   worry about the tooltip being cut off). */
.rail-logo[data-tooltip]::after,
.rail-icon[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  left: calc(100% + 14px);
  top: 50%;
  transform: translateY(-50%);
  padding: 6px 12px;
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
  z-index: 60;
}

.rail-logo[data-tooltip]:hover::after,
.rail-logo[data-tooltip]:focus-visible::after,
.rail-icon[data-tooltip]:hover::after,
.rail-icon[data-tooltip]:focus-visible::after {
  opacity: 1;
}

.rail-bottom {
  margin-top: auto;
}

.shell-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.topbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  padding: 24px 40px 4px;
}

.topbar-brand {
  grid-column: 1;
  justify-self: start;
  display: flex;
  align-items: center;
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--text);
}

.topbar-links {
  grid-column: 2;
  display: flex;
  gap: 28px;
}

.topbar-link {
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.95rem;
}

.topbar-link:hover {
  color: var(--text);
}

.topbar-link.active {
  color: var(--text);
}

.topbar-account {
  grid-column: 3;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 10px;
}

.dashboard {
  max-width: 1320px;
  width: 100%;
  margin: 0 auto;
  padding: 24px 40px 64px;
}

.dashboard-header {
  margin-bottom: 24px;
}

.dashboard-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 16px;
  margin-bottom: 32px;
}

.stat-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--surface);
  border-radius: var(--radius);
  padding: 20px 22px;
}

.stat-card-value {
  font-size: 2rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.stat-card-accent .stat-card-value {
  color: var(--brand);
}

.stat-card-label {
  color: var(--text-muted);
  font-size: 0.85rem;
  font-weight: 500;
}

.dashboard-header h1 {
  margin: 0 0 4px;
}

.meta {
  margin: 2px 0;
  color: var(--text-muted);
  font-size: 0.9rem;
}

.empty-state {
  color: var(--text-muted);
  padding: 24px 0;
}

.empty-state-small {
  color: var(--text-muted);
  font-size: 0.9rem;
  padding: 8px 0;
}

/* Tournament "club" header — crest + name on the left, one headline stat
   on the right, modeled after the club-manager reference. */
.club-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 8px;
}

.club-header-identity {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.club-crest {
  font-size: 2rem;
  flex-shrink: 0;
}

.club-header-identity h1 {
  margin: 0;
  font-size: 1.8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.club-header-stat {
  display: flex;
  align-items: baseline;
  gap: 6px;
  flex-shrink: 0;
}

.club-header-stat-value {
  font-size: 1.6rem;
  font-weight: 600;
}

.club-header-stat-label {
  color: var(--text-muted);
  font-size: 0.9rem;
}

.club-header-right {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-shrink: 0;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.header-actions .btn-danger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Dates read as a plain muted line — everything else about the tournament
   (format, scoring rules) is a fact, not a date range, so it gets its own
   row below instead of being lumped in with the rest. */
.tournament-dates {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 4px 0 14px;
}

/* A row of small facts about the tournament (format, scoring rules) — each
   one gets its own icon and a color that groups it with what it's related
   to, so the row reads at a glance instead of as one flat list. Flat
   icon+text (no pill background), matching .stat-pill below it. */
.tournament-info-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px;
  margin: 0 0 20px;
}

.info-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-muted);
  font-size: 0.85rem;
  font-weight: 500;
}

.info-chip-blue {
  color: var(--accent-blue);
}

.info-chip-green {
  color: var(--accent-green);
}

.info-chip-coral {
  color: var(--accent-coral);
}

.stat-pills {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 20px 0 28px;
  flex-wrap: wrap;
}

.stat-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 500;
}

.stat-pill-blue {
  color: var(--accent-blue);
}

.stat-pill-green {
  color: var(--accent-green);
}

.stat-pill-icon {
  font-size: 1rem;
}

.stat-pills .btn {
  margin-left: auto;
}

#teams-panel .field {
  margin-bottom: 12px;
}

.teams-table {
  width: 100%;
  table-layout: auto;
  border-collapse: collapse;
}

.teams-table th {
  text-align: left;
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: 0 10px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.teams-table th:first-child,
.teams-table td:first-child {
  padding-left: 0;
}

.teams-table th:last-child,
.teams-table td:last-child {
  padding-right: 0;
  text-align: right;
}

.teams-table td {
  padding: 12px 10px;
  border-bottom: 1px solid var(--border);
}

.teams-table tbody tr:last-child td {
  border-bottom: none;
}

.teams-col-center {
  text-align: center;
}

.teams-name-cell a {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  color: var(--text);
  min-width: 0;
}

.teams-name-cell .team-logo {
  width: 32px;
  height: 32px;
  margin: 0;
  font-size: 0.7rem;
  flex-shrink: 0;
}

.teams-name-cell span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
  margin-bottom: 32px;
}

.card {
  display: block;
  background: var(--surface);
  border-radius: var(--radius);
  padding: 16px;
  color: var(--text);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.1);
}

/* Just a spacing wrapper now — cards float directly on --bg, each with
   its own surface tone, no intermediate shell. */
.glass-panel {
  margin-bottom: 32px;
}

.glass-panel .card-grid,
.glass-panel .match-table,
.glass-panel > .panel {
  margin-bottom: 0;
}

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 32px 0 16px;
}

.section-header h2 {
  margin: 0;
}

.card h3 {
  margin: 0 0 4px;
  font-size: 1rem;
}

/* Tabs — a plain underline tab bar for switching panels within a page
   (e.g. Roster/Stats on the team page). */
.tab-bar {
  display: flex;
  gap: 20px;
  margin: 24px 0 0;
  border-bottom: 1px solid var(--border);
}

.tab-btn {
  background: none;
  border: none;
  font: inherit;
  font-weight: 500;
  font-size: 0.95rem;
  color: var(--text-muted);
  cursor: pointer;
  padding: 10px 2px;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
}

.tab-btn.active {
  color: var(--text);
  border-bottom-color: var(--brand);
}

.tab-panel h2 {
  margin: 24px 0 16px;
}

/* Player stats table */

.player-stats-table {
  width: 100%;
  border-collapse: collapse;
}

.player-stats-table th {
  text-align: center;
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: 0 12px 10px;
  border-bottom: 1px solid var(--border);
}

.player-stats-table th:first-child {
  text-align: left;
  padding-left: 0;
}

.player-stats-table th:last-child {
  padding-right: 0;
}

.player-stats-table td {
  padding: 14px 12px;
  text-align: center;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--border);
}

.player-stats-table tbody tr:last-child td {
  border-bottom: none;
}

.player-stats-table td:first-child {
  padding-left: 0;
}

.player-stats-table td:last-child {
  padding-right: 0;
}

.player-stats-name {
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
}

.player-stats-photo {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.player-stats-photo-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--brand);
  color: var(--brand-contrast);
  font-size: 0.6rem;
}

/* Team logo */

.team-logo {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  object-fit: cover;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 8px;
  font-weight: 600;
  font-size: 1.1rem;
}

.team-logo-placeholder {
  background: var(--brand);
  color: var(--brand-contrast);
}

.team-card {
  text-align: center;
}

.team-header {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.team-header .team-logo {
  width: 80px;
  height: 80px;
  font-size: 1.5rem;
  margin: 0;
  flex-shrink: 0;
}

.team-header-info {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
}

.team-header-info h1 {
  margin: 0;
}

.logo-upload-label {
  cursor: pointer;
}

/* Player card — flips on click to show stats on the back */

.player-card {
  aspect-ratio: 3 / 4;
  perspective: 1200px;
  cursor: pointer;
  transition: transform 0.15s ease;
}

.player-card:hover {
  transform: scale(1.05);
}

.player-card-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 0.6s cubic-bezier(0.4, 0.2, 0.2, 1);
  transform-style: preserve-3d;
}

.player-card.flipped .player-card-inner {
  transform: rotateY(180deg);
}

.pcard-front,
.pcard-back {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: 16px;
  overflow: hidden;
  border: 4px solid var(--surface);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
}

.pcard-front {
  background: var(--brand);
}

.pcard-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.pcard-photo-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
}

.pcard-placeholder-number {
  font-size: 2.6rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.55);
}

.pcard-scrim {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 62%;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.88), rgba(0, 0, 0, 0));
  pointer-events: none;
}

.pcard-name {
  position: absolute;
  left: 14px;
  right: 56px;
  bottom: 56px;
  color: #fff;
  font-weight: 600;
  text-transform: uppercase;
  font-size: 1.05rem;
  line-height: 1.12;
  letter-spacing: 0.01em;
}

.pcard-banner {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 46px;
  background: #fff;
  clip-path: polygon(0 45%, 100% 0, 100% 100%, 0 100%);
  display: flex;
  align-items: center;
  padding-left: 64px;
}

.pcard-position {
  color: #14161b;
  font-weight: 500;
  text-transform: uppercase;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
}

.pcard-number-badge {
  position: absolute;
  left: 10px;
  bottom: 8px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: #14161b;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 0.8rem;
  border: 3px solid #fff;
}

.pcard-back {
  transform: rotateY(180deg);
  background: #14161b;
  color: #fff;
  display: flex;
  flex-direction: column;
  padding: 14px;
  text-align: left;
}

.pcard-back-view {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.pcard-back-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 10px;
  gap: 8px;
}

.pcard-back-name {
  font-weight: 600;
  text-transform: uppercase;
  font-size: 0.85rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pcard-back-number {
  font-weight: 600;
  color: #8aa8ff;
  flex-shrink: 0;
}

.pcard-stats-label {
  text-transform: uppercase;
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.5);
  margin-bottom: 8px;
}

.pcard-stats-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px 8px;
  margin-bottom: 10px;
}

.pcard-stat {
  display: flex;
  flex-direction: column;
}

.pcard-stat-value {
  font-size: 1.3rem;
  font-weight: 600;
  line-height: 1;
}

.pcard-stat-caption {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: rgba(255, 255, 255, 0.55);
  margin-top: 2px;
}

.pcard-stats-note {
  font-size: 0.68rem;
  color: rgba(255, 255, 255, 0.45);
  margin: 0;
  flex: 1;
}

.pcard-back-actions {
  margin-top: auto;
  margin-left: -14px;
  margin-right: -14px;
  margin-bottom: -14px;
  padding: 10px 14px;
  display: flex;
  gap: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(0, 0, 0, 0.28);
}

.pcard-back-actions .btn {
  flex: 1;
  text-align: center;
}

.pcard-back .player-edit-form {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.pcard-edit-fields {
  flex: 1;
  min-height: 0; /* lets this shrink and actually scroll instead of pushing the actions bar out of view */
  overflow-y: auto;
}

.pcard-edit-fields .field-row {
  display: flex;
  gap: 8px;
}

.pcard-edit-fields .field-row .field {
  min-width: 0;
}

.pcard-edit-fields .field-jersey {
  flex: 1;
}

.pcard-edit-fields .field-position {
  flex: 2;
}

.pcard-back .player-edit-form .field {
  margin-bottom: 10px;
}

.pcard-back .player-edit-form .field label {
  font-size: 0.7rem;
  margin-bottom: 3px;
}

.pcard-back .player-edit-form input {
  padding: 6px 8px;
  font-size: 0.85rem;
  width: 100%;
}

.player-edit-form {
  text-align: left;
}

/* Match list — a real table, one row per match, living inside a single
   .panel card (not one card per row — with rows this close in tone to
   their shell, a per-row "floating card" reads as barely-there gaps
   instead of clearly separate rows). Rows are divided by a plain
   border-bottom instead, same as a simple list. */
.match-table {
  width: 100%;
  table-layout: auto;
  border-collapse: collapse;
}

.match-table tbody tr:last-child td {
  border-bottom: none;
}

/* Fixed widths on every column except Match, which is the only one left to
   take the remaining space — this is what keeps team names from getting
   truncated down to nothing next to the action buttons, even when this
   table is squeezed into one half of a two-column layout. */
.match-table th:nth-child(1) {
  width: 92px;
}

.match-table th:nth-child(3) {
  width: 100px;
}

.match-table th:nth-child(4) {
  width: 140px;
}

.match-table th {
  text-align: left;
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: 0 10px 10px;
  border-bottom: 1px solid var(--border);
}

.match-table th.match-col-center {
  text-align: center;
}

.match-table th:first-child {
  padding-left: 0;
}

.match-table th:last-child {
  padding-right: 0;
}

.match-table td {
  padding: 16px 10px;
  border-bottom: 1px solid var(--border);
}

.match-table td:first-child {
  padding-left: 0;
}

.match-table td:last-child {
  padding-right: 0;
}

.match-when {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.match-col-date {
  color: var(--text-muted);
  font-size: 0.8rem;
  white-space: nowrap;
}

.match-col-time {
  font-weight: 600;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.match-teams {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.match-vs {
  color: var(--text-muted);
  font-weight: 400;
  margin: 0 4px;
}

.match-court-sub {
  color: var(--text-muted);
  font-size: 0.78rem;
  font-weight: 400;
  margin-top: 2px;
}

.match-result {
  text-align: center;
  white-space: nowrap;
}

.match-score {
  font-weight: 600;
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
}

.match-status {
  display: inline-block;
  text-transform: uppercase;
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.03em;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--border);
  color: var(--text-muted);
  white-space: nowrap;
}

.match-status-in_progress {
  background: var(--accent);
  color: var(--brand-contrast);
}

.match-status-completed {
  background: var(--brand);
  color: var(--brand-contrast);
}

.match-actions {
  white-space: nowrap;
  text-align: right;
}

.match-actions .btn {
  margin-left: 8px;
}

/* Wraps any wide table so it scrolls horizontally inside itself on a
   narrow viewport, rather than the whole page gaining horizontal scroll. */
.table-scroll {
  overflow-x: auto;
}

.standings-table {
  width: 100%;
  table-layout: auto;
  border-collapse: collapse;
}

.standings-table tbody tr:last-child td {
  border-bottom: none;
}

.standings-table th {
  text-align: left;
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: 0 10px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.standings-table th:first-child,
.standings-table td:first-child {
  padding-left: 0;
}

.standings-table th:last-child,
.standings-table td:last-child {
  padding-right: 0;
}

.standings-table td {
  padding: 12px 10px;
  border-bottom: 1px solid var(--border);
  font-variant-numeric: tabular-nums;
}

.standings-col-center {
  text-align: center;
}

.standings-rank {
  color: var(--text-muted);
  font-weight: 600;
  text-align: center;
  width: 28px;
}

/* Deliberately not display:flex — this is a <td>, and overriding a table
   cell's display away from table-cell is what caused the logo/name to
   collapse onto their own line instead of sitting in the table row. Plain
   inline-block + vertical-align keeps it a normal table cell. */
.standings-team {
  font-weight: 600;
  white-space: nowrap;
}

.standings-team .team-logo {
  display: inline-flex;
  vertical-align: middle;
  width: 28px;
  height: 28px;
  margin: 0 10px 0 0;
  font-size: 0.65rem;
}

.standings-team span {
  vertical-align: middle;
}

.standings-points {
  font-weight: 700;
  color: var(--brand);
  font-size: 1.05rem;
}

.standings-toggle {
  margin-top: 16px;
}

/* One per stage ("Group A", "Semifinal", ...) — a plain free-text label,
   not a real bracket/tree structure, so this is just a heading + stacked
   panels, no connecting lines or seeding logic. */
.stage-block + .stage-block {
  margin-top: 24px;
}

.stage-heading {
  margin: 0 0 10px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-muted);
}

.panel {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 24px;
  margin-bottom: 24px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
}

.panel h2 {
  margin-top: 0;
}

.section-count {
  font-weight: 400;
  color: var(--text-muted);
  font-size: 0.85em;
}

/* Tournament list — same "panel with divided rows" pattern as the match
   and player-stats tables, just a different column set. */
.tournament-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

.tournament-table th {
  text-align: left;
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: 0 10px 10px;
  border-bottom: 1px solid var(--border);
}

.tournament-table th:first-child {
  padding-left: 0;
}

.tournament-table th:last-child {
  padding-right: 0;
}

.tournament-table th:nth-child(2) {
  width: 220px;
}

.tournament-table th:nth-child(3) {
  width: 120px;
}

.tournament-table th:nth-child(4) {
  width: 100px;
}

.tournament-table th:nth-child(5) {
  width: 100px;
}

.tournament-table td {
  padding: 16px 10px;
  border-bottom: 1px solid var(--border);
}

.tournament-table tbody tr:last-child td {
  border-bottom: none;
}

.tournament-table td:first-child {
  padding-left: 0;
}

.tournament-table td:last-child {
  padding-right: 0;
  text-align: right;
}

.tournament-name {
  font-weight: 500;
}

.stacked-form .field {
  margin-bottom: 16px;
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 16px 20px;
}

.scoring-details {
  margin: 8px 0 20px;
}

.scoring-details summary {
  cursor: pointer;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin-bottom: 12px;
}

.btn-small {
  padding: 6px 12px;
  font-size: 0.85rem;
}

/* Live tracker */

.tracker-prestart {
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  padding: 64px 24px;
  text-align: center;
  align-self: center;
}

.tracker-summary {
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 40px 24px 64px;
  align-self: center;
}

.tracker-prestart h1,
.tracker-summary h1 {
  margin-bottom: 16px;
}

/* Set-by-set box score — a team-colored row per team, one score cell per
   set, styled after a broadcast-graphic final-score card: each box is a
   tilted parallelogram (clip-path, not transform, so the number/logo/text
   inside stays upright), semi-transparent so the page's own background
   glow reads through, with a diagonal gradient for a little dimension. The
   team box and score cells all share the same rightward-leaning cut so
   they read as one continuous strip of interlocking shards. */
.stats-scoreline {
  margin-bottom: 8px;
}

.stats-scoreline-header,
.stats-scoreline-row {
  display: flex;
  align-items: stretch;
  gap: 6px;
}

.stats-scoreline-header {
  padding: 0 0 6px;
}

.stats-scoreline-row + .stats-scoreline-row {
  margin-top: 6px;
}

.stats-scoreline-team-spacer {
  flex: 0 0 160px;
}

.stats-scoreline-setlabel {
  flex: 1;
  text-align: center;
  font-size: 0.65rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  align-self: flex-end;
}

.stats-scoreline-team {
  flex: 0 0 160px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 500;
  color: var(--ink);
  overflow: hidden;
  padding: 14px 30px 14px 18px;
  clip-path: polygon(0 0, 100% 0, calc(100% - 18px) 100%, 0 100%);
}

.stats-scoreline-team .team-logo {
  width: 28px;
  height: 28px;
  font-size: 0.65rem;
  margin: 0;
  flex-shrink: 0;
}

.stats-scoreline-team span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stats-scoreline-cell {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 1.3rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  padding: 14px 20px;
  clip-path: polygon(18px 0, 100% 0, calc(100% - 18px) 100%, 0 100%);
}

.stats-scoreline-row-a .stats-scoreline-team,
.stats-scoreline-row-a .stats-scoreline-cell {
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--brand) 85%, transparent) 0%,
    color-mix(in srgb, var(--brand) 100%, transparent) 100%
  );
}

.stats-scoreline-row-b .stats-scoreline-team,
.stats-scoreline-row-b .stats-scoreline-cell {
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--accent-coral) 85%, transparent) 0%,
    color-mix(in srgb, var(--accent-coral) 100%, transparent) 100%
  );
}

.stats-scoreline-cell-win {
  text-decoration: underline;
  text-decoration-thickness: 3px;
  text-underline-offset: 6px;
}

.stats-scoreline-summary {
  text-align: center;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  margin: 0 0 28px;
}

/* Post-match stats screen */

.stats-hero {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 8px 0 28px;
}

/* Horizontal, not stacked — logo/emblem innermost (nearest the score),
   name outermost, per team. Team A's markup order is [name, logo] and team
   B's is [logo, name], so each side just needs its content packed toward
   its own outer edge for that to fall out correctly. */
.stats-hero-team {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
}

.stats-hero-team:first-child {
  justify-content: flex-start;
}

.stats-hero-team:last-child {
  justify-content: flex-end;
}

.stats-hero-team .team-logo {
  width: 56px;
  height: 56px;
  font-size: 1.1rem;
  margin: 0;
  flex-shrink: 0;
}

.stats-hero-name {
  font-weight: 700;
  font-size: 1.3rem;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stats-hero-score {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex: 0 0 auto;
  padding: 0 8px;
}

.stats-hero-points {
  font-size: 2.75rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}

.stats-hero-points.stats-hero-winner {
  color: var(--brand);
}

.stats-hero-sep {
  font-size: 1.5rem;
  color: var(--text-muted);
}

.stats-compare {
  margin-top: 8px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}

.stats-compare h2,
.stats-top h2 {
  margin: 0 0 20px;
  font-size: 1rem;
  text-align: center;
}

.stats-compare-row {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  margin-bottom: 16px;
}

.stats-compare-value {
  flex: 0 0 32px;
  text-align: center;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.stats-compare-mid {
  flex: 1;
  min-width: 0;
}

.stats-compare-label {
  display: block;
  text-align: center;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin-bottom: 5px;
}

.stats-compare-track {
  display: flex;
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--border);
}

.stats-compare-fill-a {
  background: var(--brand);
}

.stats-compare-fill-b {
  background: var(--accent-coral);
}

.stats-top {
  margin-top: 32px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}

.stats-top-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 20px;
  max-width: 460px;
  margin: 0 auto;
}

.stats-top-card.pcard-front {
  position: relative;
  inset: auto;
  aspect-ratio: 3 / 4;
  cursor: default;
}

.stats-top-team-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
}

.stats-top-team-badge .team-logo {
  width: 30px;
  height: 30px;
  margin: 0;
  font-size: 0.65rem;
  border: 2px solid rgba(255, 255, 255, 0.85);
}

.stats-top-card .pcard-position {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
  font-size: 0.62rem;
  letter-spacing: 0;
}

/* The whole screen, never taller than the viewport — this is meant for an
   iPad held courtside, so there is no page scroll at all: every piece
   (scoreboard/court/bench) gets a fixed or flex-derived share of exactly
   the available height, recalculated live as that height changes. */
.tracker {
  height: 100dvh;
  max-height: 100dvh;
  padding: 8px 16px;
  max-width: 1400px;
  margin: 0 auto;
  touch-action: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow: hidden;
}

#scoreboard {
  flex: 0 0 auto;
  display: flex;
  align-items: stretch;
  gap: 10px;
  position: relative;
}

/* A compact horizontal strip, not a tall stack — this whole bar is meant to
   stay well under 100px tall so the court below gets the real estate.
   Order is deliberately mirrored left/right (see markup) so both teams'
   +/- buttons land closest to the center, reachable from a scorer sitting
   at the middle of the court rather than out at either sideline. */
.team-panel {
  flex: 1;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 12px;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  min-width: 0;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

#panel-right {
  flex-direction: row-reverse;
}

/* "Attacking side" indicator — a soft highlight, not tied to score, purely a
   live presentation signal (see live-common.js::computeAttackingSide). */
.team-panel.attacking {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent);
}

.team-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}

#panel-right .team-info {
  align-items: flex-end;
  text-align: right;
}

.team-panel.attacking .team-name::before {
  content: '\25B6 ';
  color: var(--accent);
}

/* Watch page: no score buttons to make room for, so instead of the
   tracker's [name+sets] / [score] split, sets-won moves in next to the
   score — both cluster toward the middle next to the set label, with just
   the team name left out on its own toward the edge. */
.spectator .team-panel {
  justify-content: space-between;
}

.score-block {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
}

/* #panel-right's own row-reverse (above) only flips which *side* the whole
   score-block sits on — left to itself, sets-won still comes before
   point-score, which would land sets-won next to the set label and points
   out next to the team name. Mirroring the block's own internal order here
   keeps points innermost (next to the set label) on both sides. */
#panel-right .score-block {
  flex-direction: row-reverse;
}

.team-panel .team-name {
  font-weight: 500;
  font-size: 0.95rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.team-panel .sets-won {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-muted);
  background: var(--bg);
  border-radius: 999px;
  padding: 1px 10px;
  width: fit-content;
}

.team-panel .point-score {
  font-size: clamp(1.8rem, 4vw, 2.6rem);
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

.score-buttons {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}

.score-buttons .btn {
  width: 40px;
  height: 40px;
  padding: 0;
  font-size: 1.2rem;
  font-weight: 500;
  line-height: 1;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.scoreboard-center {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

#set-number-label {
  font-weight: 500;
  color: var(--text-muted);
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  white-space: nowrap;
  padding: 0 4px;
}

.watch-link-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 38px;
  padding: 0 14px;
  border-radius: 999px;
  background: var(--brand);
  color: var(--brand-contrast);
  font-weight: 600;
  font-size: 0.85rem;
  white-space: nowrap;
  flex-shrink: 0;
}

.watch-link-btn:hover {
  filter: brightness(1.08);
}

/* A real volleyball court is 18m x 9m — two 9m x 9m (square) halves either
   side of the net. Fixing the whole area to a 2:1 ratio and splitting it
   evenly means each team's half comes out square automatically, no matter
   the viewport width.

   Visual style follows a classic top-down court diagram: a slim dashed-edge
   "free zone" surround (kept deliberately thin — the court itself should
   dominate) around a solid-bordered coral playing surface. */
/* #tracker-body holds court + bench and takes whatever height #scoreboard
   didn't use; #court-area then takes whatever height #bench-area didn't
   use. The court's own box now derives its *width* from that leftover
   height via aspect-ratio (below), rather than the other way around —
   on a wide-but-short iPad landscape viewport, sizing from width first
   (the old approach) could ask for more height than actually exists. */
#tracker-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

#court-area {
  position: relative;
  background: #363b8a;
  border-radius: var(--radius);
  padding: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex: 1 1 auto;
  min-height: 0;
}

.court-surface {
  width: auto;
  max-width: 100%;
  aspect-ratio: 2 / 1;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: stretch;
  gap: 0;
  position: relative;
  background: var(--brand);
  border-radius: 6px;
  border: 3px solid #fff;
}

/* Rotate buttons live in the outer free-zone strip (not on the brand-colored
   playing surface) — controls, not court markings. */
.court-controls {
  flex: 0 0 auto;
  width: 100%;
  max-width: 100%;
  display: flex;
  gap: 8px;
}

.court-controls .rotate-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.25);
  color: #fff;
  border-radius: 999px;
  font-weight: 600;
}

.court-controls .rotate-btn:hover {
  background: rgba(255, 255, 255, 0.18);
}

/* The net: a solid vertical bar between the two teams' halves, with a
   "post" dot top and bottom for a bit of realism. Courts are side-by-side
   (not stacked), matching the left/right scoreboard layout above them. */
.net-line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 6px;
  transform: translateX(-50%);
  background: #fff;
  z-index: 2;
}

.net-line::before,
.net-line::after {
  content: '';
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #fff;
}

.net-line::before {
  top: -5px;
}

.net-line::after {
  bottom: -5px;
}

/* Attack (3m) lines: real courts mark these 3m from the net on each side —
   3m out of each 9m half is 1/3 of the way in, so they sit at the true
   33%/67% mark of the whole surface. Kept as a background court marking
   (lower stacking than .court-slots) rather than tied to any format's slot
   grid, so it never needs to line up with grid columns that vary by
   playersPerSide. Beach (2-a-side) has no attack line in real volleyball. */
.court-surface::before,
.court-surface::after {
  content: '';
  position: absolute;
  top: -24px;
  bottom: -24px;
  width: 2px;
  background: rgba(255, 255, 255, 0.75);
}

.court-surface::before {
  left: 33.333%;
}

.court-surface::after {
  left: 66.667%;
}

.court-surface:has(.court-slots[data-count='2'])::before,
.court-surface:has(.court-slots[data-count='2'])::after {
  display: none;
}

.court {
  flex: 1;
  display: flex;
  flex-direction: column;
  position: relative;
  padding: 10px;
  min-width: 0;
}

/* Base layout: 2 columns (near-net / far-net), rows sized to fill whatever
   height the square half actually has. This is also the fallback for any
   playersPerSide other than the three explicitly laid out below. */
.court-slots {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: minmax(0, 1fr);
  gap: 8px;
  min-height: 0;
  position: relative;
  z-index: 1;
}

/* 6-a-side: the standard 3 rows of 2 (matches real rotational positions). */
.court-slots[data-count='6'] {
  grid-template-rows: repeat(3, 1fr);
}

/* 4-a-side: 1 spanning the width up front, 2 side-by-side in the middle, 1
   spanning the width at the back. */
.court-slots[data-count='4'] {
  grid-template-rows: repeat(3, 1fr);
}
.court-slots[data-count='4'] .slot:nth-child(1) {
  grid-column: 1 / -1;
  grid-row: 1;
}
.court-slots[data-count='4'] .slot:nth-child(2) {
  grid-column: 1;
  grid-row: 2;
}
.court-slots[data-count='4'] .slot:nth-child(3) {
  grid-column: 2;
  grid-row: 2;
}
.court-slots[data-count='4'] .slot:nth-child(4) {
  grid-column: 1 / -1;
  grid-row: 3;
}

/* 2-a-side (beach): a single column, one player front, one back. */
.court-slots[data-count='2'] {
  grid-template-columns: 1fr;
  grid-template-rows: repeat(2, 1fr);
}

.slot {
  background: rgba(255, 255, 255, 0.1);
  border: 2px dashed rgba(255, 255, 255, 0.45);
  border-radius: 10px;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
}

/* The dashed border is a drag-drop affordance for the organizer's tracker —
   the read-only spectator view has no drag targets, so it'd just be clutter. */
.spectator .slot {
  border: none;
}

.slot .player-chip .chip-name {
  color: #fff;
}

.slot .chip-photo-placeholder {
  background: var(--surface);
  color: var(--text);
}

/* A fixed, short strip rather than a wrapping grid — a live scoring iPad
   screen has no room to spare, so bench players scroll horizontally
   instead of pushing the court off-screen. */
#bench-area {
  flex: 0 0 auto;
  height: 104px;
  display: flex;
  gap: 10px;
}

.bench {
  flex: 1;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 10px;
  overflow-x: auto;
  min-width: 0;
  padding: 6px 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  touch-action: pan-x;
}

/* Compact horizontal pill instead of the court slot's bigger vertical chip
   — bench is just a roster strip, not a drop target that needs to read
   clearly from a few feet away the way an on-court chip does. */
.bench .player-chip {
  flex-direction: row;
  width: auto;
  flex-shrink: 0;
  gap: 6px;
}

.bench .chip-photo {
  width: 56px;
  height: 56px;
}

.bench .chip-photo-placeholder {
  font-size: 0.85rem;
}

.bench .chip-name {
  font-size: 0.8rem;
  white-space: nowrap;
}

.bench-empty {
  color: var(--text-muted);
  font-size: 0.85rem;
  margin: auto;
}

.player-chip {
  touch-action: none;
  cursor: grab;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 78px;
  user-select: none;
}

.chip-photo {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--brand);
  -webkit-user-drag: none;
  user-drag: none;
}

.chip-photo-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--brand-contrast);
  font-weight: 500;
  font-size: 0.85rem;
}

.chip-name {
  font-size: 0.7rem;
  text-align: center;
  line-height: 1.15;
  color: var(--text);
}

.chip-photo-wrap {
  position: relative;
  display: inline-block;
}

/* Hidden by default (the name label below already carries the number) —
   only shown on narrow phones, where the name gets dropped for space and
   this becomes the one way to tell players apart at a glance. */
.chip-number-badge {
  display: none;
  position: absolute;
  bottom: -2px;
  right: -2px;
  background: var(--ink);
  color: #fff;
  font-size: 0.6rem;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 999px;
  border: 2px solid var(--bg);
  white-space: nowrap;
}

/* Watch page on a phone in portrait — a 6-a-side match puts 12 chips on
   screen at once (6 per side), so the circle+name treatment that works on
   a wide screen gets cramped fast. Numbers-only: shrink the circle, drop
   the name label, and surface the jersey number as a small badge over a
   real photo (the placeholder circle already just *is* the number). */
@media (max-width: 600px) {
  .spectator .chip-photo {
    width: 34px;
    height: 34px;
  }

  .spectator .chip-photo-placeholder {
    font-size: 0.6rem;
  }

  .spectator .chip-name {
    display: none;
  }

  .spectator .chip-number-badge {
    display: block;
  }
}

/* On-court card: a rectangle instead of the plain circle+name — the photo
   and jersey/name occupy the left side, and the right side is a grid of
   one-tap buttons for the routine, rally-continues actions (serve/attack
   in play, reception, dig, assist). Anything that actually decides the
   point (kills, errors, aces, blocks) stays out of this fast path and
   behind the full taxonomy menu — tapping the ID side still opens that,
   same as before. Bench chips keep the plain circle+name (no .player-chip-card
   modifier there), since bench players aren't mid-play. */
.player-chip-card {
  flex-direction: row;
  align-items: stretch;
  width: 100%;
  height: 100%;
  gap: 0;
  background: var(--surface);
  border-radius: 8px;
  overflow: hidden;
}

/* Shared ID block, default appearance: centered small circle + name below
   — this is what the bench strip actually renders (bench chips are plain
   .player-chip, never .player-chip-card), so it must stay a self-contained,
   non-absolute layout that doesn't depend on a stretched row-flex parent to
   have any height. */
.chip-id {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  flex: 0 0 auto;
  width: 42%;
  min-width: 0;
  padding: 4px;
}

.chip-id .chip-photo {
  width: 34px;
  height: 34px;
}

.chip-id .chip-photo-placeholder {
  font-size: 0.65rem;
}

.chip-id-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

.chip-jersey {
  font-weight: 600;
  font-size: 0.8rem;
  line-height: 1.1;
}

.chip-id .chip-name {
  font-size: 0.6rem;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* On-court card variant only: a full-bleed square photo (no padding, no
   circle) with the jersey number/name overlaid bottom-left on a scrim —
   same "player card" language as the roster's trading-card partial, just
   compact enough to fit inside an on-court slot. Names/numbers sized big,
   matching the roster page's own player cards, since this is read from a
   few feet away on a courtside iPad. */
.player-chip-card .chip-id {
  position: relative;
  overflow: hidden;
  padding: 0;
}

.player-chip-card .chip-id .chip-photo,
.player-chip-card .chip-id .chip-photo-placeholder {
  width: 100%;
  height: 100%;
  border-radius: 0;
}

.player-chip-card .chip-id .chip-photo-placeholder {
  font-size: 1.3rem;
}

.player-chip-card .chip-id-text {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  align-items: flex-start;
  padding: 20px 8px 6px;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0));
}

.player-chip-card .chip-jersey {
  font-size: 1.4rem;
  line-height: 1.05;
  color: #fff;
}

.player-chip-card .chip-id .chip-name {
  font-size: 0.9rem;
  font-weight: 600;
  text-align: left;
  color: #fff;
}

.chip-quick-actions {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 3px;
  padding: 4px;
  min-width: 0;
}

.quick-action-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  border: 1px solid rgba(255, 255, 255, 0.4);
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  border-radius: 5px;
  padding: 1px 2px;
  cursor: pointer;
  touch-action: manipulation;
  min-width: 0;
}

.quick-action-label,
.quick-action-sub {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.quick-action-label {
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.1;
}

/* The plain action word alone doesn't say what it's scoring — "Good" or
   "Perfect" mean nothing without knowing it's a reception. This is the
   category/qualifier that disambiguates it, so it rides along under the
   main label instead of only living in a hover title. */
.quick-action-sub {
  font-size: 0.68rem;
  font-weight: 500;
  line-height: 1;
  opacity: 0.75;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.quick-action-btn:hover {
  background: rgba(255, 255, 255, 0.25);
}

.quick-action-btn:active {
  background: rgba(255, 255, 255, 0.4);
}

.chip-dragging {
  opacity: 0.35;
}

.chip-ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  pointer-events: none;
  opacity: 0.9;
  filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.3));
}

.drop-target-hover {
  border-color: var(--brand) !important;
  background: color-mix(in srgb, var(--brand) 12%, transparent);
}

/* Fixed rather than sticky — the tracker page never scrolls (see .tracker),
   so this floats over the fixed-height layout instead of trying to sit in
   its normal flow, where it'd have to steal height from the court/bench
   budget on the (rare) sets/match-complete moments it's actually visible. */
.tracker-banner {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 500;
  max-width: calc(100vw - 32px);
  background: var(--surface);
  border: 2px solid var(--brand);
  border-radius: var(--radius);
  padding: 16px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

.tracker-banner p {
  margin: 0;
  font-weight: 500;
  font-size: 1.1rem;
}

.site-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.live-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 6px 16px;
  border-radius: 999px;
  background: var(--danger);
  color: var(--danger-contrast);
  font-weight: 500;
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  width: fit-content;
}

.live-badge::before {
  content: '';
  animation: live-pulse 1.4s ease-in-out infinite;
}

@keyframes live-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.live-badge-final {
  background: var(--text-muted);
}

/* Only the live (in_progress) branch of watch/spectator.ejs uses this — an
   iPad-courtside court needs to fit with no page scroll at all. The
   scheduled/completed branches render as normal page content instead (so
   they scroll with the rest of the page, under the sticky .site-header),
   which is why .spectator-page only ever wraps a .tracker main now. Wraps
   the header + main together so the header's real (rendered) height comes
   out of the same 100dvh budget .tracker's own height:100dvh assumes it
   owns entirely — without this, the header sits on top of a
   full-viewport-height main and pushes the bottom of the court out of
   view. */
.spectator-page {
  height: 100dvh;
  max-height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.spectator-page .site-header {
  position: static;
  flex: 0 0 auto;
}

.spectator-page .tracker {
  height: auto;
  max-height: none;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* .tracker's own margin:0 auto is written for when it sits directly in
     <body> (a normal block: width:auto already fills the line, so auto
     margins are inert there). As a flex item here instead, horizontal auto
     margins do something completely different — they shrink the item to
     its content width and center *that*, overriding stretch entirely — so
     width:100% (fill up to the inherited max-width:1400px) does the
     "stretch" job auto-margins can't, and align-self:center does the actual
     centering once that max-width caps it on a wide viewport (align-self
     centers the item's own box within the leftover space directly, rather
     than relying on margins to absorb it). */
  width: 100%;
  margin-left: 0;
  margin-right: 0;
  align-self: center;
}

.tracker.spectator {
  text-align: center;
}

.watch-link-btn {
  text-decoration: none;
}

/* Live action banners — brief animated call-outs for notable plays, pushed
   over SSE (see live-common.js::showActionBanner). */
#action-banner-host {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
  z-index: 5;
}

.action-banner {
  padding: 10px 28px;
  border-radius: 999px;
  font-weight: 600;
  font-size: clamp(1.3rem, 4vw, 2.2rem);
  letter-spacing: 0.03em;
  color: #fff;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  opacity: 0;
  transform: scale(0.7) translateY(8px);
  transition: opacity 0.25s ease, transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.action-banner-in {
  opacity: 1;
  transform: scale(1) translateY(0);
}

.banner-score {
  background: var(--brand);
  color: var(--brand-contrast);
}

.banner-error {
  background: var(--danger);
  color: var(--danger-contrast);
}

.banner-dig {
  background: #16a34a;
}

/* Tap-player action menu */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 2000;
}

.modal {
  background: var(--surface);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  width: 100%;
  max-width: 640px;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
}

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

.modal-header h2 {
  margin: 0;
  font-size: 1.1rem;
}

.modal-body {
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.action-menu-category h3 {
  margin: 0 0 8px;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.action-menu-actions {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
}

.action-btn {
  padding: 14px 10px;
  border-radius: var(--radius);
  border: 2px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;
  text-align: center;
}

.action-btn:hover {
  filter: brightness(0.95);
}

.action-btn:active {
  transform: translateY(1px);
}

.action-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.action-btn-self {
  border-color: var(--success);
  color: var(--success);
}

.action-btn-opponent {
  border-color: var(--danger);
  color: var(--danger);
}

.action-btn-none {
  border-color: var(--border);
  color: var(--text-muted);
}

/* Live action animations: a ball crossing the net for serve/attack, a
   court-wide flash for dig/set (actions that stay on one side, no crossing
   to animate). Positioned within .court-surface, which is already the
   percentage-based coordinate space the court markings use. */
.ball-flight {
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.9), 0 2px 6px rgba(0, 0, 0, 0.4);
  transform: translate(-50%, -50%);
  z-index: 5;
  pointer-events: none;
  transition: left 0.65s cubic-bezier(0.3, 0, 0.7, 1), top 0.65s cubic-bezier(0.3, 0, 0.7, 1);
}

/* Landing-page court demo only: a scale pulse peaking mid-flight, faking the
   ball rising off the court and coming back down since left/top alone read
   as flat. Re-triggered per hit in court-demo.js via a reflow + class toggle
   (CSS animations don't restart on their own when re-added without one). */
@keyframes ball-flight-arc-pulse {
  0%,
  100% {
    transform: translate(-50%, -50%) scale(1);
  }
  50% {
    transform: translate(-50%, -50%) scale(1.8);
  }
}

.ball-flight-arc {
  animation: ball-flight-arc-pulse 0.65s ease-in-out;
}

@keyframes court-blink-pulse {
  0%,
  100% {
    background-color: transparent;
  }
  25%,
  75% {
    background-color: rgba(255, 255, 255, 0.4);
  }
}

.court-blink {
  animation: court-blink-pulse 1.1s ease-in-out;
}

/* Highlights the specific chip that performed a dig/reception/assist —
   more useful than only lighting up the whole side, when the player is
   still findable on court. Same duration as .court-blink so both read as
   one coherent moment. */
@keyframes chip-blink-pulse {
  0%,
  100% {
    box-shadow: none;
  }
  25%,
  75% {
    box-shadow: 0 0 0 3px var(--brand), 0 0 16px 2px color-mix(in srgb, var(--brand) 70%, transparent);
  }
}

.chip-blink {
  animation: chip-blink-pulse 1.1s ease-in-out;
  border-radius: 8px;
}

/* Radial "impact" ring where a kill/ace lands — a beat more decisive than
   the routine ball-in-play flight, since these actually end the rally. */
@keyframes impact-burst-pulse {
  0% {
    width: 10px;
    height: 10px;
    border-width: 5px;
    opacity: 1;
  }
  100% {
    width: 100px;
    height: 100px;
    border-width: 1px;
    opacity: 0;
  }
}

.impact-burst {
  position: absolute;
  border-radius: 50%;
  border: 5px solid #fff;
  box-shadow: 0 0 20px 4px rgba(255, 255, 255, 0.6);
  transform: translate(-50%, -50%);
  z-index: 6;
  pointer-events: none;
  animation: impact-burst-pulse 0.65s ease-out forwards;
}

/* Flatpickr theme — restyled to use this site's own tokens instead of the
   library's default fixed light theme, so it matches light/dark mode. */
.flatpickr-calendar {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
}

.flatpickr-calendar.arrowTop::before,
.flatpickr-calendar.arrowTop::after {
  border-bottom-color: var(--border);
}

.flatpickr-months .flatpickr-month {
  color: var(--text);
  fill: var(--text);
}

.flatpickr-current-month .flatpickr-monthDropdown-months,
.flatpickr-current-month input.cur-year {
  color: var(--text);
  font-weight: 600;
}

.flatpickr-current-month .flatpickr-monthDropdown-months option {
  background: var(--surface);
  color: var(--text);
}

.flatpickr-prev-month,
.flatpickr-next-month {
  color: var(--text-muted);
  fill: var(--text-muted);
}

.flatpickr-prev-month:hover,
.flatpickr-next-month:hover {
  color: var(--brand);
  fill: var(--brand);
}

span.flatpickr-weekday {
  color: var(--text-muted);
  font-weight: 600;
}

.flatpickr-day {
  color: var(--text);
  border-radius: 999px;
}

.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover {
  color: var(--text-muted);
  opacity: 0.4;
}

.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay {
  color: var(--text-muted);
  opacity: 0.5;
}

.flatpickr-day:hover,
.flatpickr-day:focus {
  background: var(--bg);
  border-color: var(--bg);
}

.flatpickr-day.today {
  border-color: var(--brand);
}

.flatpickr-day.selected,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange,
.flatpickr-day.endRange:hover {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-contrast);
}

.flatpickr-day.inRange {
  background: color-mix(in srgb, var(--brand) 15%, transparent);
  box-shadow: -5px 0 0 color-mix(in srgb, var(--brand) 15%, transparent), 5px 0 0 color-mix(in srgb, var(--brand) 15%, transparent);
  border-color: transparent;
}

.flatpickr-time {
  border-top: 1px solid var(--border);
}

.flatpickr-time input,
.flatpickr-time .flatpickr-time-separator,
.flatpickr-time .flatpickr-am-pm {
  color: var(--text);
}

.flatpickr-time input:hover,
.flatpickr-time .flatpickr-am-pm:hover,
.flatpickr-time input:focus,
.flatpickr-time .flatpickr-am-pm:focus {
  background: var(--bg);
}

.numInputWrapper span.arrowUp:after {
  border-bottom-color: var(--text-muted);
}

.numInputWrapper span.arrowDown:after {
  border-top-color: var(--text-muted);
}

.numInputWrapper:hover {
  background: var(--bg);
}

/* "Scoreboard" mode on the public watch page — hides the court entirely and
   blows up the existing #scoreboard bar (same elements .render() already
   keeps live-updated, just restyled/repositioned) into a big edge-to-edge
   single-screen scoreboard: team name floats free above its own "points
   container" card (not boxed in with it), sets tally sits under the SET
   label in the middle. ID-qualified selectors out-specificity #panel-right's
   own row-reverse/mirroring rules from the compact bar layout. */
.spectator.scoreboard-only.tracker {
  max-width: none;
  padding: 0;
}

.spectator.scoreboard-only #court-area {
  display: none;
}

.spectator.scoreboard-only #scoreboard {
  flex: 1;
  align-items: stretch;
  gap: 0;
}

.spectator.scoreboard-only #panel-left,
.spectator.scoreboard-only #panel-right {
  flex: 1;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 24px;
  padding: 24px;
  background: none;
  border: none;
  border-radius: 0;
}

.spectator.scoreboard-only #panel-left {
  border-right: 1px solid var(--border);
}

.spectator.scoreboard-only #panel-right {
  border-left: 1px solid var(--border);
}

/* Team name is a plain floating heading — not part of the points card. */
.spectator.scoreboard-only .team-panel .team-name {
  font-size: clamp(2rem, 5.5vw, 3.8rem);
  white-space: normal;
  text-align: center;
}

/* The actual "points container" — its own distinct card, holding only the
   score, sized big (fills up to the panel's width) but kept roughly square
   rather than a short wide rectangle — width:min(100%, 65vh) bounds it by
   the viewport's available height too, so aspect-ratio:1 doesn't just blow
   it out sideways on a wide screen. Sets-won moves out of it entirely in
   this mode (see below). */
.spectator.scoreboard-only #panel-left .score-block,
.spectator.scoreboard-only #panel-right .score-block {
  flex-direction: column;
  align-self: center;
  align-items: center;
  justify-content: center;
  gap: 0;
  width: min(100%, 65vh);
  aspect-ratio: 1;
  box-sizing: border-box;
  background: var(--surface);
  border-radius: var(--radius);
  padding: 32px 24px;
}

.spectator.scoreboard-only .team-panel .sets-won {
  display: none;
}

.spectator.scoreboard-only .team-panel .point-score {
  font-size: clamp(6rem, 22vw, 16rem);
  line-height: 1;
}

.spectator.scoreboard-only .scoreboard-center {
  flex-direction: column;
  gap: 10px;
  padding: 0 24px;
}

.spectator.scoreboard-only #set-number-label {
  font-size: clamp(1rem, 3vw, 1.6rem);
}

/* Sets tally, hidden outside scoreboard mode — lives under the SET label. */
.scoreboard-sets-line {
  display: none;
}

.spectator.scoreboard-only .scoreboard-sets-line {
  display: flex;
  align-items: baseline;
  gap: 14px;
  font-size: clamp(2.1rem, 5.5vw, 3.6rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.scoreboard-sets-sep {
  color: var(--text-muted);
  font-weight: 500;
}

/* Score-change flash — any watch-mode point-score gets this on a real
   change (not the initial render), win or lose the rally. */
@keyframes score-flash-pulse {
  0% {
    transform: scale(1);
  }
  35% {
    transform: scale(1.18);
    color: var(--brand);
  }
  100% {
    transform: scale(1);
  }
}

.score-flash {
  display: inline-block;
  animation: score-flash-pulse 0.55s ease-out;
}
