/* ============================================================
   yapbt — Yet Another Pickleball Tool
   Shared design layer (main.css)
   Palette: court teal / kitchen green / optic ball yellow
   Type:    Archivo Black (display) · Archivo (body) · Martian Mono (data)
   Fonts are loaded via <link> tags in base.html.
   ============================================================ */

/* ---------- Tokens ---------- */
:root {
  color-scheme: light;

  /* chevron for <select>: a data URI cannot read currentColor and <select>
     cannot host a masked pseudo-element, so the whole image is a token and
     each theme supplies its own stroke. */
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235F7079' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");

  /* surfaces */
  --surface-0: #F4F7F6;   /* page background */
  --surface-1: #FFFFFF;   /* cards / panels */
  --surface-2: #EAF0EE;   /* subtle fills, hovers, wells */
  --surface-band: #F0F5F4; /* identity band behind page headers */

  /* ink.
     --ink-3 was #7A8A94, which is ~3.2:1 on white — it failed AA for the
     11px uppercase labels and small print that use it most, and was the
     reason those strings read as washed out. --ink-4 is the replacement
     for genuinely decorative marks (separators, chevrons); never body copy. */
  --ink:   #0E2233;
  --ink-2: #435665;
  --ink-3: #5F7079;
  --ink-4: #8B999F;

  /* --ink-1 is referenced by the footer link hover; it was never defined,
     so that hover silently did nothing. Aliased to the primary ink. */
  --ink-1: var(--ink);

  /* lines */
  --border:      #E1E8E6;
  --border-soft: #EDF1F0;  /* internal dividers inside a bordered surface */

  /* shape — one scale. --radius stays as the legacy alias for --r-md. */
  --r-sm:   8px;
  --r-md:   12px;
  --r-lg:   16px;
  --r-pill: 999px;
  --radius: var(--r-md);

  /* elevation — border *plus* soft shadow. Border-only reads flat. */
  --e-1: 0 1px 2px rgba(14, 34, 51, 0.04);
  --e-2: 0 1px 2px rgba(14, 34, 51, 0.05), 0 6px 16px -6px rgba(14, 34, 51, 0.10);
  --e-3: 0 2px 4px rgba(14, 34, 51, 0.06), 0 16px 40px -12px rgba(14, 34, 51, 0.18);
  --shadow: var(--e-2);   /* legacy alias */

  /* spacing scale */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px;

  /* semantic */
  --good: #3E8E5A;        /* kitchen green */
  --bad:  #D64550;
  --warn: #B4761E;

  /* brand accent — flat court teal (kept as legacy token names) */
  --grad-start: #19647E;
  --grad-end:   #124B60;
  --gradient:   #19647E;
  --gradient-h: #19647E;
  --teal-wash:  color-mix(in srgb, var(--grad-start) 7%, transparent);

  /* signature */
  --ball:      #D6F227;   /* optic ball yellow */
  --ball-deep: #A8C40B;

  --font-display: "Archivo Black", sans-serif;
  --font-body:    "Archivo", sans-serif;
  --font-data:    "Martian Mono", monospace;
}

/* ---------- Dark theme ----------
   Every colour the pages use resolves through the tokens above, so dark mode
   is a token swap rather than a second stylesheet. It follows the OS by
   default; `data-theme` on <html> lets a toggle win in either direction.

   The teal lightens rather than darkens: #19647E on a dark surface has too
   little contrast to carry links and active states. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    /* chevron for <select>: a data URI cannot read currentColor and <select>
       cannot host a masked pseudo-element, so the whole image is a token and
       each theme supplies its own stroke. */
    --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238399A4' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");

    --surface-0: #0B1620;
    --surface-1: #111F2B;
    --surface-2: #172938;
    --surface-band: #0F1C27;

    --ink:   #EAF1F4;
    --ink-2: #A9BCC6;
    --ink-3: #8399A4;
    --ink-4: #5E7482;

    --border:      #21323F;
    --border-soft: #1A2833;

    --good: #5FBF83;
    --bad:  #F0737E;
    --warn: #D9A046;

    /* --grad-start is the *text* teal (links, active tabs) and has to be
       light enough to carry on a dark surface. --gradient / --grad-end are
       the *fill* teal for solid buttons, and have to stay dark enough for
       white label text — so on dark they diverge rather than tracking one
       hue as they do on light. */
    --grad-start: #4FA8C4;
    --gradient:   #1F6E8A;
    --grad-end:   #2A87A6;
    --gradient-h: #4FA8C4;
    --teal-wash:  rgba(79, 168, 196, 0.10);

    --e-1: 0 1px 2px rgba(0, 0, 0, 0.30);
    --e-2: 0 1px 2px rgba(0, 0, 0, 0.30), 0 6px 16px -6px rgba(0, 0, 0, 0.45);
    --e-3: 0 2px 4px rgba(0, 0, 0, 0.35), 0 16px 40px -12px rgba(0, 0, 0, 0.60);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  /* chevron for <select>: a data URI cannot read currentColor and <select>
     cannot host a masked pseudo-element, so the whole image is a token and
     each theme supplies its own stroke. */
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238399A4' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");

  --surface-0: #0B1620;
  --surface-1: #111F2B;
  --surface-2: #172938;
  --surface-band: #0F1C27;

  --ink:   #EAF1F4;
  --ink-2: #A9BCC6;
  --ink-3: #8399A4;
  --ink-4: #5E7482;

  --border:      #21323F;
  --border-soft: #1A2833;

  --good: #5FBF83;
  --bad:  #F0737E;
  --warn: #D9A046;

  --grad-start: #4FA8C4;
  --gradient:   #1F6E8A;
  --grad-end:   #2A87A6;
  --gradient-h: #4FA8C4;
  --teal-wash:  rgba(79, 168, 196, 0.10);

  --e-1: 0 1px 2px rgba(0, 0, 0, 0.30);
  --e-2: 0 1px 2px rgba(0, 0, 0, 0.30), 0 6px 16px -6px rgba(0, 0, 0, 0.45);
  --e-3: 0 2px 4px rgba(0, 0, 0, 0.35), 0 16px 40px -12px rgba(0, 0, 0, 0.60);
}

/* ---------- Base ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

/* 15px rather than 14: at 14 the whole app read as a control panel. The
   line-height is unchanged. */
body {
  font-family: var(--font-body);
  background: var(--surface-0);
  color: var(--ink);
  line-height: 1.55;
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

::selection { background: color-mix(in srgb, var(--grad-start) 18%, transparent); }

:focus-visible {
  outline: 3px solid var(--ball-deep);
  outline-offset: 2px;
  border-radius: 6px;
}

a { color: inherit; }
button { font-family: inherit; cursor: pointer; }
img { max-width: 100%; display: block; }

/* The `hidden` attribute has to win.
   `[hidden]` is only `display: none` in the UA stylesheet, so any author rule
   that sets `display` on the same element beats it and the element stays
   visible — which is how a hidden invite link kept rendering. The templates use
   `hidden` in a dozen places (claim sections, conditional fields, footer
   actions), so this is enforced once here rather than remembered every time. */
[hidden] { display: none !important; }

/* Type scale. Page titles previously sat at 24px while stat figures were
   28px/800, so the numbers out-shouted the thing they described. h1 now
   dominates, and carries the display face — which until now appeared only
   in the logo. */
h1, h2, h3, h4 { line-height: 1.15; letter-spacing: -0.02em; }
h1 { font-family: var(--font-display); font-size: 30px; }
h2 { font-size: 20px; font-weight: 700; }
h3 { font-size: 17px; font-weight: 700; }
h4 { font-size: 15px; font-weight: 700; }

@media (max-width: 640px) {
  h1 { font-size: 24px; }
  h2 { font-size: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

.wrap {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--s-5);
}

/* `min-height: calc(100vh - 130px)` used to pin the footer to the bottom of
   the viewport, so a page with one short card sat above half a screen of
   empty background. body is a flex column now: main takes the slack, the
   footer sits under the content, and a short page is allowed to be short. */
main { flex: 1 0 auto; padding: var(--s-5) 0 var(--s-7); }

/* ---------- Nav ---------- */
/* Scoped to the site header by class, not to the `nav` element: breadcrumbs and the
   footer's legal links are navs too, and a bare `nav` selector made each of them a
   second sticky bar at top: 0 that landed on top of the header. */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  /* hardcoded rgba(255,255,255,.92) would stay white in dark mode */
  background: color-mix(in srgb, var(--surface-1) 88%, transparent);
  backdrop-filter: blur(12px) saturate(140%);
  border-bottom: 1px solid var(--border);
}
.site-nav .wrap {
  display: flex;
  align-items: center;
  gap: var(--s-5);
  height: 62px;
}

.logo {
  font-family: var(--font-display);
  font-size: 18px;
  letter-spacing: 0.01em;
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}
.logo::before {
  /* signature: the optic ball */
  content: "";
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--ball);
  box-shadow: inset -3px -3px 0 rgba(14, 34, 51, 0.14);
  flex: none;
}
/* The 9px mono tagline next to the mark is hidden: it ran at a size where it
   read as texture rather than words, and the footer already carries the same
   string. Kept in the markup for the document outline / accessibility. */
.logo span {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

#nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
  list-style: none;
  margin-left: auto;
}
#nav-links a {
  position: relative;
  display: block;
  padding: 8px 14px;
  border-radius: var(--r-pill);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-3);
  text-decoration: none;
  transition: background 0.15s, color 0.15s;
}
#nav-links a:hover {
  background: var(--surface-2);
  color: var(--ink);
}

/* You-are-here. The nav previously gave no indication of the current
   section. `aria-current="page"` carries it for assistive tech; the ball
   underline is the visual half — and the second of four deliberate uses of
   the signature colour. */
#nav-links a[aria-current="page"] { color: var(--ink); }
#nav-links a[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -2px;
  transform: translateX(-50%);
  width: 18px;
  height: 3px;
  border-radius: 3px;
  background: var(--ball-deep);
}

.nav-toggle {
  display: none;
  border: 1px solid var(--border);
  background: var(--surface-1);
  border-radius: 8px;
  padding: 6px 8px;
  color: var(--ink-2);
  line-height: 0;
}

/* Account area — previously built by inline styles in the header partial. */
.nav-account {
  display: flex;
  align-items: center;
  gap: 12px;
}

.nav-signin {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
}
.nav-signin:hover { color: var(--ink); }

.nav-signup {
  font-size: 13px;
  font-weight: 600;
  padding: 6px 16px;
  border-radius: var(--radius);
  background: var(--gradient);
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
}
.nav-signup:hover { background: var(--grad-end); }

.nav-user {
  display: flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  padding: 4px 10px 4px 4px;
  border-radius: var(--r-pill);
  transition: background 0.15s;
}
.nav-user:hover { background: var(--surface-2); }

.nav-name {
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}

/* Signals that the account area opens something, rather than looking like a
   label that happens to be clickable. */
.nav-caret { color: var(--ink-4); font-size: 10px; line-height: 1; }

.avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--gradient);
  color: #fff;
  font-weight: 700;
  font-size: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

@media (max-width: 768px) {
  .site-nav .wrap { gap: var(--s-3); }
  .nav-toggle { display: inline-flex; order: 10; }
  #nav-links {
    display: none;
    position: absolute;
    top: 62px;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    background: var(--surface-1);
    border-bottom: 1px solid var(--border);
    padding: 10px var(--s-4) 14px;
    box-shadow: var(--e-2);
  }
  #nav-links.open { display: flex; }
  #nav-links a { border-radius: var(--r-sm); padding: 10px var(--s-3); }

  /* In the stacked menu a centred underline would float mid-row, so the
     current-page mark becomes a rail on the leading edge instead. */
  #nav-links a[aria-current="page"] {
    background: var(--surface-2);
  }
  #nav-links a[aria-current="page"]::after {
    left: 0;
    top: 6px;
    bottom: 6px;
    transform: none;
    width: 3px;
    height: auto;
  }
}

/* ---------- Footer ---------- */
footer {
  flex: none;
  border-top: 1px solid var(--border);
  padding: var(--s-5) 0;
  color: var(--ink-3);
  font-family: var(--font-data);
  font-size: 11px;
  letter-spacing: 0.05em;
}

/* Brand left, legal links centred, copyright right — collapsing to a stacked,
   centred column on narrow screens rather than letting the three groups
   squeeze together. */
.footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 20px;
}

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

.footer-links a {
  color: var(--ink-3);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
}

.footer-links a:hover,
.footer-links a:focus-visible {
  color: var(--ink-1);
  border-bottom-color: currentColor;
}

@media (max-width: 640px) {
  .footer-inner {
    flex-direction: column;
    justify-content: center;
    text-align: center;
    gap: 12px;
  }
}

/* ---------- Hero band ----------
   A full-bleed surface behind the page header, so "who or what you are
   looking at" reads separately from "what it contains". This is the single
   largest change to how finished the pages look; it needs the header markup
   to sit outside main's .wrap, which base.html's `page_header` block does.

   The band is one surface step plus a teal wash rather than a strong tint —
   at higher saturation it competes with the content below it. */
.hero-band {
  background-color: var(--surface-band);
  background-image: linear-gradient(var(--teal-wash), var(--teal-wash));
  border-bottom: 1px solid var(--border);
}
.hero-band > .wrap {
  padding-top: var(--s-4);
  padding-bottom: var(--s-5);
}

/* Breadcrumbs — the detail pages had a single "back" link at best, so there
   was no way to tell how deep you were. */
.crumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-2);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-3);
  margin-bottom: var(--s-4);
}
.crumbs a { text-decoration: none; }
.crumbs a:hover { color: var(--grad-start); }
.crumbs .sep { color: var(--ink-4); }
.crumbs [aria-current="page"] { color: var(--ink-2); }

.page-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s-4);
}
.page-emblem {
  width: 60px;
  height: 60px;
  border-radius: var(--r-lg);
  background: var(--gradient);
  color: #fff;
  font-family: var(--font-display);
  font-size: 22px;
  letter-spacing: -0.02em;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  box-shadow: var(--e-2);
}
.page-head-text { min-width: 0; }
.page-head-text h1 { margin-bottom: 6px; }

/* The meta line carries facts (type, place, dates, counts). Anything that is
   a *state* belongs in a .badge instead — see the note there. */
.page-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-3);
  font-size: 13.5px;
  color: var(--ink-3);
}
.page-meta .dot { color: var(--ink-4); }

.page-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex: none;
}

@media (max-width: 640px) {
  .page-head { flex-wrap: wrap; }
  .page-emblem { width: 48px; height: 48px; font-size: 18px; }
  .page-actions { margin-left: 0; width: 100%; }
}

/* ---------- Badges ----------
   A badge is for state — Active, Archived, Registration open. Type and
   category are facts, and belong in .page-meta as plain text; the org page
   used to put "LEAGUE" in a badge, which read ambiguously as a status. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--surface-2);
  color: var(--ink-2);
  white-space: nowrap;
}
.badge-dot::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.badge-good { background: color-mix(in srgb, var(--good) 14%, transparent); color: var(--good); }
.badge-bad  { background: color-mix(in srgb, var(--bad) 14%, transparent);  color: var(--bad); }
.badge-warn { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.badge-info { background: var(--teal-wash); color: var(--grad-start); }

/* ---------- Card / panel ----------
   .card is the legacy name and keeps working. .panel is the same surface with
   a defined header/body structure, which is what most pages were building by
   hand out of inline padding. */
.card,
.panel {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--e-2);
}
.panel { overflow: hidden; }

.panel-head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--border-soft);
}
.panel-head .spacer { margin-left: auto; }
.panel-title { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.panel-sub { font-size: 13px; color: var(--ink-3); font-weight: 400; }
.panel-body { padding: var(--s-5); }

@media (max-width: 560px) {
  .panel-head { flex-wrap: wrap; }
  .panel-head .spacer { margin-left: 0; width: 100%; height: 0; }
}

/* ---------- Detail layout ----------
   Content plus a sidebar. Detail pages used to be one full-width card, so a
   page with little data looked broken rather than new; the sidebar gives the
   layout structure that does not depend on how much data exists. */
.detail-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 304px;
  gap: var(--s-5);
  align-items: start;
}
@media (max-width: 900px) {
  .detail-layout { grid-template-columns: minmax(0, 1fr); }
}

.side { display: grid; gap: var(--s-4); }
.side-card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--e-1);
  padding: var(--s-4) var(--s-5) var(--s-5);
}
.side-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: var(--s-3);
}

/* key/value list for the facts that used to be crammed into the meta line */
.kv { display: grid; gap: 10px; }
.kv-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  font-size: 13.5px;
}
.kv-k { color: var(--ink-3); }
.kv-v { font-weight: 600; text-align: right; min-width: 0; }
.kv-v.mono { font-family: var(--font-data); font-size: 12px; font-variant-numeric: tabular-nums; }

.side-actions { display: grid; gap: var(--s-2); }
.side-action {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-2);
  text-decoration: none;
  border: 1px solid transparent;
  text-align: left;
  width: 100%;
  background: none;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.side-action:hover {
  background: var(--surface-0);
  border-color: var(--border);
  color: var(--ink);
}
.side-action svg { color: var(--ink-3); flex: none; }
.side-action:hover svg { color: var(--grad-start); }

/* A shareable URL.
   Distinct from .joincode: that is for a short token like FD-4K2Q, where centring
   and wide tracking help someone read characters aloud. A 70-character URL in
   that style centres, stretches and breaks mid-token. This one is left-aligned,
   tight, and breaks anywhere so it wraps without overflowing its card. */
.invite-link {
  width: 100%;
  font-family: var(--font-data);
  font-size: 11.5px;
  line-height: 1.5;
  letter-spacing: 0;
  padding: 10px var(--s-3);
  background: var(--surface-0);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--ink-2);
  overflow-wrap: anywhere;
  word-break: break-all;
  user-select: all;
}

/* Join code / share token — reads as something to copy rather than as body
   text that happens to be uppercase. */
.joincode {
  font-family: var(--font-data);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.12em;
  padding: 11px var(--s-4);
  text-align: center;
  background: var(--surface-0);
  border: 1px dashed var(--border);
  border-radius: var(--r-md);
  color: var(--ink);
}

/* ---------- Tables ---------- */
.table-wrap {
  overflow-x: auto;
  border-radius: var(--r-lg);
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
thead th {
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-3);
  background: var(--surface-0);
  padding: 10px var(--s-5);
  border-bottom: 1px solid var(--border-soft);
  white-space: nowrap;
}
tbody td {
  padding: 13px var(--s-5);
  border-bottom: 1px solid var(--border-soft);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background 0.12s; }
tbody tr:hover { background: var(--surface-0); }

/* Two-line table cells: a title with a quiet qualifier underneath, rather
   than a second column for something that is really a subtitle. */
.row-title { font-weight: 600; }
.row-sub { font-size: 12.5px; color: var(--ink-3); }

/* Inline status pill for table rows — lighter than a .badge, since a column
   of uppercase badges turns into visual noise. */
.chip {
  display: inline-block;
  padding: 2px 9px;
  border-radius: var(--r-pill);
  font-size: 11.5px;
  font-weight: 600;
  background: var(--surface-2);
  color: var(--ink-2);
  white-space: nowrap;
}
.chip-good { background: color-mix(in srgb, var(--good) 14%, transparent); color: var(--good); }
.chip-bad  { background: color-mix(in srgb, var(--bad) 14%, transparent);  color: var(--bad); }
.chip-warn { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.chip-info { background: var(--teal-wash); color: var(--grad-start); }

@media (max-width: 560px) {
  thead th, tbody td { padding-inline: var(--s-4); }
}

th.num, td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
td.rank {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

/* ---------- Empty states ----------
   .empty-state is the terse one, for a cell inside a table where a table
   already has a header explaining what is missing. It stays, because dozens
   of `<td colspan>` rows use it. */
.empty-state {
  text-align: center;
  padding: var(--s-6) var(--s-4);
  color: var(--ink-3);
  font-size: 13.5px;
}

/* .onboard is the one that matters: for a *page* with no data yet. A new
   organization's first screen is nothing but zeros and grey "No leagues yet"
   text, which reads as a broken app rather than an empty one. This gives it
   an icon, a headline, a sentence of what the thing is for, and the primary
   action in the empty area itself instead of only as a small button in a
   panel header. */
.onboard {
  padding: var(--s-7) var(--s-5);
  text-align: center;
}
.onboard-icon {
  width: 56px;
  height: 56px;
  margin: 0 auto var(--s-4);
  border-radius: var(--r-lg);
  background: var(--teal-wash);
  color: var(--grad-start);
  display: flex;
  align-items: center;
  justify-content: center;
}
.onboard h3 { font-size: 20px; margin-bottom: var(--s-2); }
.onboard p {
  font-size: 14px;
  color: var(--ink-3);
  max-width: 46ch;
  margin: 0 auto var(--s-5);
}

/* First-run checklist. The cheapest way to make a sparse app feel alive, and
   it doubles as onboarding. The left rail is the fourth deliberate use of the
   signature colour. */
.checklist {
  max-width: 420px;
  margin: 0 auto var(--s-5);
  text-align: left;
  padding: var(--s-4) var(--s-4) var(--s-4) var(--s-5);
  background: var(--surface-0);
  border: 1px solid var(--border-soft);
  border-left: 3px solid var(--ball);
  border-radius: var(--r-md);
  display: grid;
  gap: var(--s-3);
}
.step { display: flex; align-items: flex-start; gap: var(--s-3); font-size: 14px; }
.step-num {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex: none;
  margin-top: 1px;
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--font-data);
  font-size: 10px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}
.step-label { font-weight: 600; }
.step-hint { display: block; font-size: 13px; color: var(--ink-3); font-weight: 400; }
.step.done .step-num { background: var(--good); color: #fff; }
.step.done .step-label {
  color: var(--ink-3);
  text-decoration: line-through;
  text-decoration-color: var(--ink-4);
}

/* ---------- Skeletons ----------
   Shown while a fetch is in flight, instead of rendering the empty state and
   then replacing it — which made every load look briefly like an empty
   account. */
.sk {
  background: var(--surface-2);
  border-radius: 6px;
  height: 12px;
  animation: sk-pulse 1.4s ease-in-out infinite;
}
@keyframes sk-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.sk-row {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: 15px var(--s-5);
  border-bottom: 1px solid var(--border-soft);
}
.sk-row:last-child { border-bottom: 0; }
.sk-avatar { width: 38px; height: 38px; border-radius: 10px; flex: none; }
.sk-lines { flex: 1; min-width: 0; }
.sk-lines .sk + .sk { margin-top: 7px; height: 9px; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 9px 16px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s, color 0.15s, opacity 0.15s,
              box-shadow 0.15s, transform 0.1s;
}
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn:active:not(:disabled) { transform: translateY(1px); }

.btn-sm { padding: 6px 12px; font-size: 13px; border-radius: var(--r-sm); }
.btn-lg { padding: 11px 22px; font-size: 15px; }
.btn-icon { width: 38px; height: 38px; padding: 0; }

.btn-primary {
  background: var(--gradient);
  color: #fff;
  box-shadow: var(--e-1);
}
/* Third deliberate use of the signature colour: a hairline that appears on
   intent, tucked just under the button's lower edge so the CTA reads as
   sitting on the accent. The negative spread keeps it from running the full
   width as a slab. */
.btn-primary:hover:not(:disabled) {
  background: var(--grad-end);
  box-shadow: 0 3px 0 -1px var(--ball), var(--e-2);
}

.btn-secondary {
  background: var(--surface-1);
  color: var(--ink);
  border-color: var(--border);
  box-shadow: var(--e-1);
}
.btn-secondary:hover:not(:disabled) { border-color: var(--ink-3); }

.btn-danger { background: var(--bad); color: #fff; }
.btn-danger:hover:not(:disabled) { opacity: 0.9; }

.btn-good { background: var(--good); color: #fff; }
.btn-good:hover:not(:disabled) { opacity: 0.9; }

/* Tinted variants — for inline row actions (division cards, match rows)
   where a solid fill would dominate the surrounding content. */
.btn-soft-secondary {
  background: var(--surface-2);
  color: var(--ink-2);
  border-color: var(--border);
}
.btn-soft-secondary:hover:not(:disabled) { border-color: var(--ink-3); color: var(--ink); }

.btn-soft-danger {
  background: color-mix(in srgb, var(--bad) 12%, transparent);
  color: var(--bad);
  border-color: color-mix(in srgb, var(--bad) 25%, transparent);
}
.btn-soft-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--bad) 20%, transparent); }

.btn-soft-good {
  background: color-mix(in srgb, var(--good) 12%, transparent);
  color: var(--good);
  border-color: color-mix(in srgb, var(--good) 25%, transparent);
}
.btn-soft-good:hover:not(:disabled) { background: color-mix(in srgb, var(--good) 20%, transparent); }

/* Outline variant — destructive actions that need to read as "are you sure". */
.btn-outline-danger {
  background: transparent;
  color: var(--bad);
  border-color: var(--bad);
}
.btn-outline-danger:hover:not(:disabled) { background: var(--bad); color: #fff; }

.page-btn {
  padding: 6px 14px;
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  color: var(--ink-2);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: border-color 0.15s, color 0.15s;
}
.page-btn:hover:not(:disabled) { border-color: var(--ink); color: var(--ink); }
.page-btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* ---------- Pillbox (segmented control) ---------- */
.pillbox {
  display: inline-flex;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px;
  gap: 2px;
}
.pill {
  padding: 7px 16px;
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-2);
  transition: background 0.15s, color 0.15s;
}
.pill:hover:not(.active) { color: var(--ink); }
.pill.active { background: var(--ink); color: #fff; }
.pill-count {
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
  margin-left: 5px;
  font-size: 11px;
}

/* ---------- Underline tabs ---------- */
.tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--s-5);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

/* A full-bleed tab bar, for pages where the tabs sit directly under the hero
   band and should read as part of the page chrome rather than as a control
   floating in the content area. */
.tabbar {
  background: var(--surface-0);
  border-bottom: 1px solid var(--border);
}
.tabbar .tabs { border-bottom: 0; margin-bottom: 0; }
.tabbar .tab { margin-bottom: -1px; }

.tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 13px 16px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  color: var(--ink-3);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
  white-space: nowrap;
  flex: none;
}
.tab:hover { color: var(--ink); }
/* Both the legacy class and the ARIA state, so a tab list can be driven by
   aria-selected alone rather than keeping a class in sync with it. */
.tab.active,
.tab[aria-selected="true"] {
  color: var(--grad-start);
  border-bottom-color: var(--grad-start);
}

/* ---------- Tab counts ----------
   These replace the row of big stat tiles that used to sit above the tabs.
   The tiles duplicated the tab labels exactly — four counts rendered twice,
   as two controls that selected the same view — and rendered 0 in 28px
   figures, so a brand-new org shouted its emptiness in the largest type on
   the page. On the tab, weight tracks content: a zero goes small and muted,
   a real number gets a filled pill, and the selected tab's count picks up
   the teal. */
.count {
  font-family: var(--font-data);
  font-size: 10.5px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.count.zero {
  background: transparent;
  color: var(--ink-4);
  padding-inline: 2px;
}
.tab[aria-selected="true"] .count:not(.zero),
.tab.active .count:not(.zero) {
  background: color-mix(in srgb, var(--grad-start) 14%, transparent);
  color: var(--grad-start);
}

@media (max-width: 480px) {
  .tab { padding: 11px 12px; font-size: 13px; gap: 5px; }
}

/* ---------- Forms ---------- */
.field { margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }

.field-label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-3);
  margin-bottom: 5px;
}

/* Marks a field as not required, so the absence of the word does not have to be
   read as "required" by inference. Lighter than the label it sits inside. */
.field-optional {
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink-4);
}

/* Guidance under an input: what the field is for, and what a valid value looks
   like. Present before the user gets it wrong rather than as an error afterwards. */
.field-hint {
  margin-top: 5px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-3);
}

.field-hint.is-bad { color: var(--bad); }

.field-input {
  width: 100%;
  padding: 9px 12px;
  font-size: 14px;
  font-family: inherit;
  background: var(--surface-0);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--ink);
  transition: border-color 0.15s;
}
.field-input:focus {
  outline: none;
  border-color: var(--grad-start);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--grad-start) 18%, transparent);
}

/* The chevron used to be a data-URI SVG with stroke='%23656A7D' baked in, so
   it stayed a mid-grey regardless of theme and all but vanished against a
   dark field. A data URI cannot read currentColor, and <select> cannot host
   a pseudo-element to mask one onto — so the whole image becomes a token and
   the dark block overrides it. No markup change, which matters: there are
   selects across nine templates. */
select.field-input {
  appearance: none;
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 32px;
  cursor: pointer;
}

.field-row   { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); }
.field-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--s-3); }

@media (max-width: 560px) {
  .field-row, .field-row-3 { grid-template-columns: 1fr; }
}

/* ---------- Modals ---------- */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 100;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
/* Two open-state names are in circulation; both are honoured. */
.modal-overlay.show,
.modal-overlay.open { display: flex; }

.modal {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--e-3);
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  overflow-y: auto;
  position: relative;
}
.modal::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--gradient-h);
}

.modal-header {
  padding: 20px 20px 0;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}
.modal-header h2 { font-size: 19px; font-weight: 700; letter-spacing: -0.015em; }
.modal-body   { padding: 16px 20px 20px; }
.modal-footer { padding: 0 20px 20px; display: flex; justify-content: flex-end; gap: 8px; }

.modal-close {
  background: none;
  border: none;
  color: var(--ink-3);
  font-size: 20px;
  cursor: pointer;
  padding: 0 4px;
  line-height: 1;
}
.modal-close:hover { color: var(--ink); }

@media (max-width: 480px) {
  .modal-overlay { padding: 12px; }
  .modal { max-width: 100%; }
}

/* ---------- Match detail modal ---------- */
.match-detail-card { pointer-events: none; overflow: hidden; }
.match-detail-card .bracket-match-wrap { cursor: default; margin: 0; width: 100%; padding: 0; min-width: 0; display: block; }
.match-detail-card .match-card { box-shadow: none; border: 1px solid var(--border); width: 100%; }
.match-detail-sections { margin-top: 16px; display: flex; flex-direction: column; gap: 16px; }
.match-detail-section h4 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-3); margin: 0; }
.match-detail-section-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.match-detail-placeholder { font-size: 13px; color: var(--ink-4); margin: 0; }
.match-video-item { margin-bottom: 6px; }
.match-video-item a { font-size: 13px; color: var(--accent); word-break: break-all; }
.match-video-meta { display: block; font-size: 11px; color: var(--ink-4); margin-top: 1px; }
.match-detail-actions { margin-top: 12px; display: flex; gap: 8px; }
.btn-dnf {
  font-size: 13px; font-weight: 600; padding: 6px 14px;
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 10%, var(--surface-1));
  color: var(--warn); border-radius: var(--radius); cursor: pointer;
}
.btn-dnf:hover { background: color-mix(in srgb, var(--warn) 18%, var(--surface-1)); }

/* ---------- Toast ---------- */
.toast {
  position: fixed;
  top: 16px; right: 16px;
  max-width: 360px;
  min-width: 200px;
  z-index: 200;
  padding: 12px 18px;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.16), 0 2px 8px rgba(0,0,0,0.08);
  transform: translateY(-120%) scale(0.95);
  opacity: 0;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.3s ease;
  pointer-events: none;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.toast-text {
  font-size: 13px;
  font-weight: 500;
  color: #fff;
  line-height: 1.4;
  letter-spacing: 0.01em;
}
.toast.show {
  transform: translateY(0) scale(1);
  opacity: 1;
  pointer-events: auto;
}
.toast.success {
  background: color-mix(in srgb, var(--good) 88%, #000);
  border: 1px solid color-mix(in srgb, var(--good) 50%, #fff);
}
.toast.error {
  background: color-mix(in srgb, var(--bad) 88%, #000);
  border: 1px solid color-mix(in srgb, var(--bad) 50%, #fff);
}
.toast.info {
  background: color-mix(in srgb, var(--ink-2) 90%, #000);
  border: 1px solid color-mix(in srgb, var(--ink-2) 50%, #fff);
}

@media (max-width: 720px) {
  .toast { right: 12px; left: 12px; max-width: none; min-width: 0; top: 12px; }
}

/* ---------- Small shared utilities ---------- */
.req { color: var(--grad-start); }

.section-divider {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  margin: 18px 0 12px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.section-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }

.sep, .divider { color: var(--ink-3); }

.placeholder { color: var(--ink-3); }

.result-badge {
  display: inline-block;
  padding: 3px 10px;
  font-size: 11.5px;
  font-weight: 600;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  white-space: nowrap;
}

/* ---------- Verification banner ---------- */
/*
  Full-bleed strip under the nav, shown while the signed-in account's address is
  unconfirmed. Amber rather than red: nothing is broken and no action failed —
  the account simply has less access than it will have. Red here would read as
  an error on every page the user visits.

  Not dismissible by design, so there is no close control to style.
*/
.verify-banner {
  background: color-mix(in srgb, var(--warn) 12%, var(--surface-1));
  border-bottom: 1px solid color-mix(in srgb, var(--warn) 34%, transparent);
  color: var(--ink-1);
  font-size: 13.5px;
  line-height: 1.5;
}

.verify-banner > .wrap {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding-top: var(--s-3);
  padding-bottom: var(--s-3);
}

.verify-banner-icon {
  flex: 0 0 auto;
  color: var(--warn);
}

.verify-banner-text {
  margin: 0;
  /* Takes the slack so the actions sit hard right on a wide viewport. */
  flex: 1 1 auto;
}

.verify-banner-text strong {
  font-weight: 650;
  /* The instruction and the explanation are one paragraph for screen readers,
     but the instruction should be findable without reading the sentence. */
  margin-right: var(--s-1);
}

.verify-banner-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  white-space: nowrap;
}

.verify-banner-resend {
  font: inherit;
  font-weight: 600;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  background: var(--surface-1);
  color: var(--warn);
  cursor: pointer;
}

.verify-banner-resend:hover:not(:disabled) {
  background: color-mix(in srgb, var(--warn) 10%, var(--surface-1));
}

/* Held disabled through the cooldown, so the state has to look inert rather
   than merely unstyled. */
.verify-banner-resend:disabled {
  cursor: default;
  opacity: 0.6;
}

.verify-banner-actions a {
  color: var(--ink-2);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.verify-banner-actions a:hover { color: var(--ink-1); }

/* The send outcome replaces the actions in place. Announced because the button
   it replaces was the thing under the pointer. */
.verify-banner-note {
  margin: 0;
  flex: 0 0 auto;
  font-weight: 600;
  color: var(--ink-2);
  white-space: nowrap;
}

@media (max-width: 700px) {
  .verify-banner > .wrap {
    flex-wrap: wrap;
    /* The icon holds a column on one line and wastes the row below it. */
    row-gap: var(--s-2);
  }
  .verify-banner-text { flex: 1 1 100%; }
  .verify-banner-icon { display: none; }
  .verify-banner-note { white-space: normal; }
}

/* ---------- Narrow single-purpose pages ---------- */
/*
  /verify, /forgot-password and /reset-password: one panel, one job, no page
  furniture. They each carried an identical page-scoped <style> block, which is
  three copies of the same rules and three places to change a padding.

  Not merged with .auth-card (login and signup) — those have their own gradient
  rule and header treatment, and folding them together here would mean styling
  two different layouts through one set of names.
*/
.narrow-page {
  display: flex;
  justify-content: center;
  padding: var(--s-7) 0;
}

.narrow-panel {
  width: 100%;
  max-width: 460px;
  padding: var(--s-6);
}

.narrow-panel h1 {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: var(--s-3);
}

.narrow-panel p {
  color: var(--ink-2);
  font-size: 14px;
  line-height: 1.6;
}

/* One labelled input. */
.narrow-field { margin-top: var(--s-5); }

.narrow-field label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: var(--s-2);
}

.narrow-actions {
  margin-top: var(--s-5);
  display: flex;
  gap: var(--s-3);
  align-items: center;
}

/* The outcome line. Colour comes from the state class, so a note with neither
   reads as ordinary body text rather than as a silent success. */
.narrow-note {
  margin-top: var(--s-4);
  font-size: 13.5px;
  font-weight: 600;
}
.narrow-note.is-good { color: var(--good); }
.narrow-note.is-bad  { color: var(--bad); }

/* Live guidance under a field: character counts, match/mismatch. */
.narrow-hint {
  margin-top: var(--s-2);
  font-size: 12.5px;
  color: var(--ink-3);
}
.narrow-hint.is-bad { color: var(--bad); }

/* ── Player view toggle ── */
.player-view-toggle {
  position: fixed; bottom: 20px; right: 20px; z-index: 900;
  box-shadow: 0 4px 16px rgba(0,0,0,0.18);
  border-radius: var(--r-pill, 20px);
  padding: 10px 20px; font-size: 13px; font-weight: 600;
  background: var(--surface-1); color: var(--ink-2);
  border: 1px solid var(--border);
  cursor: pointer; transition: all 0.2s ease;
  display: flex; align-items: center; gap: 6px;
}
.player-view-toggle:hover {
  border-color: var(--ink-3); color: var(--ink);
  box-shadow: 0 6px 20px rgba(0,0,0,0.22);
}
.player-view-toggle.active {
  background: var(--gradient); color: #fff; border-color: transparent;
  box-shadow: 0 4px 16px color-mix(in srgb, var(--grad-start) 30%, transparent);
}
@media (max-width: 480px) {
  .player-view-toggle { bottom: 12px; right: 12px; padding: 10px 16px; }
}

/* ---------- Session RSVP ---------- */
/*
  The RSVP control is the whole point of the session modal, so it is the largest
  thing in it and its state is carried by colour and by words — not colour alone,
  which fails for anyone who cannot distinguish the two.

  Red for "not coming yet" rather than for an error: it is the unfilled state of a
  decision, and the button is the action to take.
*/
.rsvp-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  padding: 14px 20px;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}

/* Not yet attending. */
.rsvp-toggle-out {
  background: var(--bad);
  border-color: var(--bad);
  color: #fff;
}
.rsvp-toggle-out:hover:not(:disabled) {
  background: color-mix(in srgb, var(--bad) 88%, black);
}

/*
  Attending. Stays green on hover — the state is "you are in", and flipping to red
  under the cursor read as an error rather than as an offer to withdraw.
*/
.rsvp-toggle-in {
  background: var(--good);
  border-color: var(--good);
  color: #fff;
}
.rsvp-toggle-in:hover:not(:disabled) {
  background: color-mix(in srgb, var(--good) 88%, black);
}

.rsvp-toggle:disabled {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--ink-3);
  cursor: default;
}

.rsvp-toggle-icon { display: flex; }

.session-fee-tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: 6px;
  background: var(--green-bg, color-mix(in srgb, #22c55e 12%, transparent));
  color: var(--green-text, #16a34a);
}

.session-settings { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 8px; }
.session-settings-toggle {
  font-size: 12px; font-weight: 600; color: var(--ink-2);
  cursor: pointer; user-select: none; list-style: none;
}
.session-settings-toggle::-webkit-details-marker { display: none; }
.session-settings-toggle::before { content: '\25B6'; display: inline-block; margin-right: 6px; font-size: 9px; transition: transform .15s; }
details[open] > .session-settings-toggle::before { transform: rotate(90deg); }
.session-settings-body { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 8px; }
.session-settings-body .field-row { display: flex; flex-direction: column; gap: 2px; }
.session-settings-body .field-label { font-size: 11px; font-weight: 600; color: var(--ink-2); }
.session-settings-body .field-input {
  font-size: 13px; padding: 5px 8px; border: 1px solid var(--border);
  border-radius: 6px; background: var(--surface); color: var(--ink);
}

/* The count sits with the heading, so "who is coming" reads as one block. */
.rsvp-head {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  margin: var(--s-5) 0 var(--s-2);
}
.rsvp-head-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-3);
}
.rsvp-head-count {
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
  margin-left: auto;
}

/* Scrolls rather than growing the modal past the viewport on a full session. */
.rsvp-people {
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}

.rsvp-person {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 9px var(--s-3);
  border-bottom: 1px solid var(--border);
  font-size: 13.5px;
}
.rsvp-person:last-child { border-bottom: 0; }

/* The viewer's own row, so they can find themselves in a long list. */
.rsvp-person.is-you { background: var(--teal-wash); font-weight: 600; }

.rsvp-person-when {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--ink-3);
  white-space: nowrap;
}

.rsvp-empty {
  padding: var(--s-5);
  text-align: center;
  font-size: 13.5px;
  color: var(--ink-3);
}

/* Organiser controls, kept below the player action and visually quieter. */
.session-org-actions {
  display: flex;
  gap: var(--s-2);
  flex-wrap: wrap;
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
}

/* ---------- Fixtures ---------- */
/*
  The schedule for a division: who plays whom, in which round, on which court.
  Rendered under the division list, or in place of it when there is only one
  division — a lone card only hides what the page is for.
*/
.fixture-groups {
  display: grid;
  gap: var(--s-4);
}

.fixture-pool { padding-bottom: var(--s-3); }

.fixture-list {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scroll-padding-left: 16px;
  gap: var(--s-4);
  padding: 12px 16px;
  scrollbar-width: thin;
}
.fixture-round {
  min-width: 0;
  scroll-snap-align: start;
}
.round-stepper {
  display: flex;
  gap: 2px;
  padding: 8px 16px 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.round-step {
  flex: 0 0 auto;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-3);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s, border-color 0.15s;
}
.round-step:hover { color: var(--ink); }
.round-step.active {
  color: var(--grad-start);
  border-bottom-color: var(--grad-start);
}
.round-step.done::after {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--good);
  margin-left: 4px;
  vertical-align: middle;
}
.fixture-round-label {
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0 0 8px;
}
.fixture-round-matches {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.fixture-round-matches .bracket-match-wrap { width: 100%; display: block; padding: 0; min-width: 0; }
.fixture-round-matches .match-card { width: 100%; }

/* Each round is a column of fixtures, using the bracket tree markup and CSS in
   templates/tournament.html — the knockout draw and the round robin then look like
   the same application. No connector lines are drawn between the columns: nothing
   advances out of a round robin, and lines would say it does. */

/* The division list when there are several: a fixed-height column that scrolls,
   with the fixtures below it rather than pushed off the screen. */
.div-grid-scroll {
  max-height: 420px;
  overflow-y: auto;
  padding-right: var(--s-2);
  margin-bottom: var(--s-5);
}

.status-ok   { color: var(--good); }
.status-warn { color: var(--bad); }
.onboard-block { padding: var(--s-4) 0; }
.onboard-block p { margin: 0 0 var(--s-4); color: var(--text-2); }
.stripe-body { padding: var(--s-4) var(--s-5); }
.stripe-body p { margin: 0 0 var(--s-4); color: var(--text-2); }
.stripe-details { margin: 0; padding: 0 0 0 var(--s-4); color: var(--text-2); font-size: 14px; }
.stripe-details li { margin: 0 0 var(--s-2); }

/* ── Coming Soon badge ─────────────────────────────────────────── */
/* ── Match cards ──────────────────────────────────────────────── */
.match-card {
  display: grid;
  grid-template-columns: 4px 1fr auto 1fr 4px;
  align-items: center;
  gap: 0;
  padding: 0;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 1px 3px rgba(0,0,0,0.06);
  overflow: hidden;
}
.match-card .accent-bar {
  width: 4px;
  align-self: stretch;
  border-radius: var(--r-lg) 0 0 var(--r-lg);
}
.match-card .accent-bar-r {
  width: 4px;
  align-self: stretch;
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
}
.match-card.win .accent-bar { background: var(--good); }
.match-card.loss .accent-bar { background: var(--bad); }
.match-card.draw .accent-bar { background: var(--ink-3); }
.match-card.completed .accent-bar { background: var(--good); }
.match-card.completed .accent-bar-r { background: var(--bad); }
.bracket-match.completed.away-win .accent-bar,
.match-card.completed.away-win .accent-bar { background: var(--bad); }
.bracket-match.completed.away-win .accent-bar-r,
.match-card.completed.away-win .accent-bar-r { background: var(--good); }
.match-card.pending .accent-bar { background: var(--border); }
.match-card.dnf .accent-bar,
.match-card.dnf .accent-bar-r { background: var(--warn); }
.match-card.dnf { opacity: 0.7; }
.match-card.dnf .score-center .dnf-label {
  font-size: 13px; font-weight: 700; color: var(--warn);
  letter-spacing: 0.04em; text-transform: uppercase;
}
.match-card .team {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 16px;
  min-width: 0;
}
.match-card .team .player {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  min-width: 0;
}
.match-card .team.winner .player { color: var(--ink); font-weight: 600; }
.match-card .team.right { align-items: flex-end; }
.match-card .team.right .player { flex-direction: row-reverse; }
.match-card .player .avatar {
  width: 26px; height: 26px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700;
  text-transform: uppercase;
  background: var(--surface-2);
  color: var(--ink-3);
  flex-shrink: 0;
}
.match-card .team.winner .avatar {
  background: color-mix(in srgb, var(--grad-start) 12%, transparent);
  color: var(--grad-start);
}
.match-card .player .pname {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.match-card .player.is-me .pname { color: var(--grad-start); }
.dupr-sup { font-size: 0.65em; color: var(--ink-3); margin-left: 2px; font-weight: 600; vertical-align: super; }
.match-card .score-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 10px 12px;
  min-width: 80px;
}
.match-card .score-nums {
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  display: inline-flex;
  align-items: center;
  gap: 0;
}
.match-card .score-nums .win-score { color: var(--good); }
.match-card .score-nums .lose-score { color: var(--ink-3); }
.match-card .score-nums .sep { color: var(--border); margin: 0 2px; }
.match-card.draw .score-nums .win-score,
.match-card.draw .score-nums .lose-score { color: var(--ink-3); }
.match-card .match-format { font-size: 11px; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.04em; }
.match-card .score-center .bracket-score {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  padding: 0;
  border-radius: var(--r-md, 6px);
  width: 44px;
  height: 38px;
  text-align: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--border);
  background: var(--surface-1);
  transition: border-color 0.15s, background 0.15s;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  box-sizing: border-box;
}
.match-card .score-center .bracket-score:hover {
  border-color: var(--grad-start);
  background: color-mix(in srgb, var(--grad-start) 6%, var(--surface-1));
}
.match-card .score-center .bracket-score.editing {
  background: var(--surface-0);
  border-color: var(--grad-start);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--grad-start) 20%, transparent);
}
.match-card .score-center .bracket-score input {
  width: 100%; height: 100%; border: none; background: transparent;
  text-align: center; font: inherit; font-size: 16px; font-weight: 700; color: var(--ink);
  outline: none; padding: 0; -moz-appearance: textfield;
}
.match-card .score-center .bracket-score input::-webkit-outer-spin-button,
.match-card .score-center .bracket-score input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.match-card .bracket-court {
  font-size: 9px; font-weight: 700; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.02em;
}
.match-history-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.match-date-group { grid-column: 1 / -1; font-size: 12px; font-weight: 700; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.05em; padding: 12px 0 4px; }
.match-date-group:first-child { padding-top: 0; }
@media (max-width: 640px) { .match-history-list { grid-template-columns: 1fr; } }
@media (max-width: 480px) {
  .match-card { grid-template-columns: 3px 1fr auto 1fr 3px; }
  .match-card .accent-bar { width: 3px; }
  .match-card .accent-bar-r { width: 3px; }
  .match-card .team { padding: 8px 10px; }
  .match-card .team .player { gap: 5px; font-size: 12px; }
  .match-card .player .avatar { width: 22px; height: 22px; font-size: 9px; }
  .match-card .score-center { min-width: 56px; padding: 8px 6px; }
  .match-card .score-nums { font-size: 16px; }
  .match-card .match-format { font-size: 10px; }
  .match-card .score-center .bracket-score { width: 36px; height: 32px; font-size: 14px; }
}

.coming-soon-wrap { position: relative; pointer-events: none; opacity: 0.55; }
.coming-soon-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--accent, #1B9A85); background: color-mix(in srgb, var(--accent, #1B9A85) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, #1B9A85) 25%, transparent);
  padding: 2px 8px; border-radius: var(--r-pill, 100px); white-space: nowrap;
  pointer-events: auto; opacity: 1;
}

/* ── Standings table (shared) ── */
.standings-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.standings-table th {
  text-align: left; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-3);
  background: var(--surface-0); padding: 9px var(--s-4);
  border-bottom: 1px solid var(--border-soft);
}
.standings-table th.num { text-align: right; }
.standings-table td {
  padding: 10px var(--s-4); border-bottom: 1px solid var(--border-soft);
  font-variant-numeric: tabular-nums;
}
.standings-table td.num { text-align: right; font-weight: 600; }
.standings-table tr:last-child td { border-bottom: none; }
.standings-table .rank { width: 34px; text-align: center; font-weight: 700; color: var(--ink-3); }
.standings-table .player-name { font-weight: 600; }
