/* The Classic at Tennessee High School — site styles.
 *
 * Hand-written, no framework, no build step. Every colour is a token on :root and there
 * is no raw hex below those blocks, which is what lets the contrast check see every pair.
 *
 * The brand: maroon, the orange of the basketball and a gold highlight, over a dark gym.
 * The header, hero and footer are always dark, whatever the colour scheme, and the
 * content between them is warm paper (or warm dark under prefers-color-scheme).
 *
 * Mobile-first.
 */

:root {
  /* Brand: Tennessee High maroon. */
  --brand: #6e1a32;          /* 9.4:1 on --paper */
  --brand-deep: #4a1022;     /* 13.5:1 on --paper */
  --brand-ink: #ffffff;      /* 9.4:1 on --brand */
  --orange: #e8792a;         /* the basketball — rules and marks only, never text on light */
  --gold: #f2b134;           /* the highlight — text-safe ONLY on the dark surfaces */
  --accent-ink: #9a4408;     /* 6.3:1 on --paper — the text-safe orange */

  /* Paper: the content sections. */
  --paper: #fbf7f3;
  --paper-alt: #f1e9e3;
  /* The paper of each scheme, fixed in both, for showing a logo on the light page and
   * the dark page side by side. Never redefined below; the contrast check asserts each
   * equals that scheme's --paper. */
  --paper-day: #fbf7f3;
  --paper-night: #1c1216;
  --ink: #1a1114;            /* 16.4:1 on --paper */
  --ink-soft: #584a4e;       /* 7.4:1 on --paper */
  --heading: #4a1022;
  --link: #6e1a32;
  --line: #e0d5cd;
  --line-strong: #7d6a66;    /* 4.3:1 on --paper — a visible rule needs 3:1 */
  --accent-line: #6e1a32;    /* card and panel edges */
  --btn-outline: #6e1a32;    /* secondary button border and label; gold under dark */
  --focus: #0b5cd5;          /* 5.4:1 on --paper, and not a brand colour */
  --ok: #1c6b3c;
  --soon: #9a4408;

  /* The dark surfaces: header, hero, footer. Same in both schemes. */
  --dark: #120c0e;
  --dark-rgb: 18, 12, 14;    /* --dark as a triple, for rgba() overlays */
  --dark-2: #1f1417;
  --dark-3: #2c1d22;
  --dark-line: #3b2a30;
  --dark-text: #f6efe9;      /* 17.5:1 on --dark */
  --dark-text-soft: #d6c9c2; /* 12.6:1 on --dark */
  --focus-on-dark: #f2b134;  /* 9.6:1 on --dark */
  --live: #ff6b6b;           /* the "live" dot only — decorative beside the words "Live now" */

  /* A TEAM'S COLOURS (D-28) are not defined here. A themed page gets --team, --team-ink
   * and --team-stripe from a nonce'd <style>, and only the rules under `.has-team` read
   * them. --team-ink is pure white or pure black, chosen to clear 4.5:1 on --team. A team
   * colour is never text on the paper and never carries meaning alone. */

  /* The ground a school logo sits on, and the one colour deliberately identical in both
   * schemes. Most logos are drawn for white, and a dark crest with no outline disappears
   * on the dark paper, so the pad stays white in both schemes and on any team colour. It
   * is a token so the contrast check can see it, not an excuse for a raw hex. */
  --logo-pad: #ffffff;

  --measure: 68ch;
  --gutter: 1rem;
  --radius: 6px;
  --shadow-rgb: 0, 0, 0;
  --shadow: 0 10px 30px -12px rgba(var(--shadow-rgb), 0.35);

  /* Body stays on the system face: it is the bulk of the text, it costs nothing to
   * download, and it is already the face the reader's phone renders everything else in. */
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Display type is bold condensed uppercase, like the printed bracket. Self-hosted,
   * because a system stack resolved to a different face on every platform (Arial Narrow,
   * Impact, plain system-ui). One file, one weight, 15KB, from our own origin. The
   * fallbacks still matter: `font-display: swap` paints them first on a slow connection. */
  --font-display: "Barlow Condensed", "Roboto Condensed", "Arial Narrow", system-ui, sans-serif;
  /* The script on the hero's "Presented by". Its fallback is deliberately a real
   * alternative rather than the body face: if the 2.5KB subset never arrives, cursive
   * still reads as the same kind of lettering. */
  --font-script: "Dancing Script", "Snell Roundhand", "Segoe Script", cursive;
}

/* ONLY EVER THE WORDS "Presented by". This file is subset to the ten glyphs those two
 * words need (2.5KB instead of 25KB), so any other string set in it renders as blanks or
 * falls back mid-word. The `unicode-range` is the guard: a character outside it never
 * triggers the download and never uses this face. Real type rather than an image of
 * text (WCAG 1.4.5). */
@font-face {
  font-family: "Dancing Script";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/dancing-script-700-subset.woff2") format("woff2");
  unicode-range: U+20, U+50, U+62, U+64-65, U+6e, U+72-74, U+79;
}

/* The only weight the site uses is 700; a second weight is a second download. Latin
 * subset only. */
@font-face {
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/barlow-condensed-700.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
                 U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Dark scheme: the paper turns to warm dark, and maroon — which is unreadable on dark —
 * hands its text roles to the gold. Buttons keep maroon behind white, which works on
 * anything. Nothing here is pink. */
@media (prefers-color-scheme: dark) {
  :root {
    --paper: #1c1216;
    --paper-alt: #26191e;
    --ink: #f6efe9;
    --ink-soft: #cdbfb8;
    --heading: #f6efe9;
    --link: #f2b134;
    --accent-ink: #f2b134;
    --line: #3b2a30;
    --line-strong: #8a777d;
    --accent-line: #f2b134;
    --btn-outline: #f2b134;
    --focus: #f2b134;
    --ok: #7fd6a0;
    --soon: #f2b134;
  }
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
}

img { max-width: 100%; height: auto; display: block; }

.wrap {
  width: 100%;
  max-width: 76rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}
@media (min-width: 40rem) { .wrap { padding-inline: 1.5rem; } }

/* ---- focus -------------------------------------------------------------- */
/* Visible on everything focusable, never removed (2.4.7). On the dark surfaces the
 * ring turns gold, because blue on near-black does not reach 3:1. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}
.on-dark :where(a, button, [tabindex]):focus-visible { outline-color: var(--focus-on-dark); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 20;
  background: var(--paper); color: var(--ink);
  padding: 0.75rem 1rem; border: 3px solid var(--focus);
  font-weight: 700;
}
.skip-link:focus { left: 0.5rem; top: 0.5rem; }

/* ---- type --------------------------------------------------------------- */
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.05;
  margin: 0 0 0.5em;
  color: var(--heading);
  text-wrap: balance;
}
h1 { font-size: clamp(2.2rem, 7vw, 4.25rem); text-transform: uppercase; }
h2 { font-size: clamp(1.6rem, 4vw, 2.4rem); text-transform: uppercase; }
h3 { font-size: 1.25rem; }

p { margin: 0 0 1rem; }
a { color: var(--link); text-underline-offset: 3px; text-decoration-thickness: 2px; }
a:hover { color: var(--heading); }

.muted { color: var(--ink-soft); }
.kicker {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--accent-ink);
  margin: 0 0 0.4rem;
}
.on-dark .kicker { color: var(--gold); }
.on-dark, .on-dark h1, .on-dark h2, .on-dark h3 { color: var(--dark-text); }
.on-dark .muted { color: var(--dark-text-soft); }
.on-dark a { color: var(--dark-text); }
.on-dark a:hover { color: var(--gold); }

/* ---- the announcement bar (D-68) ---------------------------------------- */
/* A full-bleed maroon band above the header on every page: the one thing that speaks for
 * the tournament rather than reporting a result, so it wears the brand colour. White on
 * maroon is the button pair (9.4:1), and --brand is not redefined in the dark scheme, so
 * the band is identical in both. No orange rule under it: the header carries one, and two
 * stacked rules read as a mistake. Nothing renders when there is no announcement. Capped
 * at the reading measure, because an announcement is a sentence. */
.announcement-bar {
  background: var(--brand);
  color: var(--brand-ink);
  padding-block: 0.8rem;
}
.announcement {
  margin: 0;
  max-width: var(--measure);
  font-weight: 500;
}
/* The link is underlined, not just a different shade: on a single-colour band there is
 * no other colour to give it, and colour alone never carries meaning. The offset keeps
 * the rule off the descenders. */
.announcement-bar a {
  color: var(--brand-ink);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}
.announcement-bar a:hover { text-decoration-thickness: 2px; }
/* The page's blue focus ring is nearly invisible on maroon; the gold one the dark
 * surfaces use clears 3:1 on it. */
.announcement-bar :where(a, button, [tabindex]):focus-visible {
  outline-color: var(--focus-on-dark);
}

/* ---- header ------------------------------------------------------------- */
.site-header {
  background: var(--dark);
  border-bottom: 4px solid var(--orange);
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.6rem;
  flex-wrap: wrap;
}
.brand-link {
  display: flex; align-items: center; gap: 0.75rem;
  text-decoration: none;
  min-height: 44px;
}
.brand-logo { width: 56px; height: 56px; }
.brand-words { display: flex; flex-direction: column; line-height: 1.05; }
.brand-name {
  font-family: var(--font-display); text-transform: uppercase;
  font-weight: 700; font-size: 1.25rem; letter-spacing: 0.04em;
  color: var(--dark-text);
}
.brand-sub { font-size: 0.75rem; color: var(--dark-text-soft); letter-spacing: 0.06em; text-transform: uppercase; }

/* ---- the main nav (D-101) ------------------------------------------------- */
/* NARROW FIRST, and on any touch screen: a MENU button beside the logo, a native
 * <details>, opening a vertical list over the page with each section's pages indented
 * under it. No JavaScript, and the browser announces it expanded or collapsed. Every
 * target is 44px (2.5.5). */
.site-header { position: relative; }
/* Logo and Menu share one row on a phone: the name gives way (its second line may wrap)
 * rather than pushing the button onto a row of its own. */
.header-inner { flex-wrap: nowrap; }
.brand-link { min-width: 0; }
.brand-words { min-width: 0; }
.site-nav { flex: 0 0 auto; }
.nav-toggle {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: 0.55rem;
  min-height: 44px; padding: 0 0.9rem;
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; font-size: 0.95rem; color: var(--dark-text);
  border: 1px solid var(--dark-line); border-radius: var(--radius);
}
.nav-toggle::-webkit-details-marker { display: none; }
/* Three bars, drawn in CSS and decorative: the word "Menu" is the name. */
.nav-toggle::before {
  content: ""; width: 1.1rem; height: 2px; background: currentColor;
  box-shadow: 0 -6px 0 currentColor, 0 6px 0 currentColor;
}
.nav-toggle:hover, .nav-menu[open] > .nav-toggle { color: var(--gold); border-color: var(--gold); }

.nav { list-style: none; margin: 0; padding: 0; }
.nav-menu[open] > .nav {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 30;
  display: grid; gap: 0;
  padding: 0.25rem var(--gutter) 0.75rem;
  background: var(--dark); border-bottom: 4px solid var(--orange); box-shadow: var(--shadow);
  max-height: calc(100vh - 6rem); overflow-y: auto;
}
.nav a {
  display: flex; align-items: center;
  min-height: 44px; padding: 0 0.7rem;
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; font-size: 0.95rem; text-decoration: none;
  color: var(--dark-text);
  border-left: 3px solid transparent;
}
.nav a:hover { color: var(--gold); }
/* Current page: the colour AND a rule, never colour alone. */
.nav a[aria-current="page"] { color: var(--gold); border-left-color: var(--gold); }
.nav-sub { list-style: none; margin: 0 0 0.35rem 0.7rem; padding: 0; border-left: 2px solid var(--dark-line); }
.nav-sub a { font-size: 0.82rem; color: var(--dark-text-soft); padding-left: 1rem; }

/* WIDE, WITH A MOUSE: the bar. The list shows whatever the <details> says, the Menu button
 * goes, and a parent's pages are a dropdown on :hover and on :focus-within, so Tab
 * reaches every child with nothing to load. Only where the browser can style the inside
 * of a <details> (::details-content): an older one keeps the Menu button, which works.
 * `hover: hover`, because a touch screen fires neither hover nor focus before the tap
 * navigates, so a dropdown is the one thing a tablet cannot open. */
@media (min-width: 46rem) and (hover: hover) {
  @supports selector(::details-content) {
    /* The bar is long, so it takes the row under the logo. */
    .header-inner { flex-wrap: wrap; }
    .site-nav { flex: 1 1 auto; }
    .nav-toggle { display: none; }
    .nav-menu::details-content { content-visibility: visible; display: contents; }
    .nav-menu > .nav, .nav-menu[open] > .nav {
      position: static; display: flex; flex-wrap: wrap; gap: 0 0.25rem;
      padding: 0; background: none; border: 0; box-shadow: none; max-height: none; overflow: visible;
    }
    .nav > li > a { border-left: 0; border-bottom: 3px solid transparent; }
    .nav > li > a[aria-current="page"] { border-bottom-color: var(--gold); }
    .nav-parent { position: relative; }
    .nav-sub {
      position: absolute; top: 100%; left: 0; z-index: 20;
      display: none; min-width: max-content; margin: 0; padding: 0.25rem 0;
      background: var(--dark); border: 1px solid var(--dark-line); box-shadow: var(--shadow);
    }
    .nav-parent:hover > .nav-sub,
    .nav-parent:focus-within > .nav-sub { display: block; }
    .nav-sub a { padding: 0 1rem; border-left: 0; }
    .nav-sub a[aria-current="page"] { text-decoration: underline; text-underline-offset: 0.3em; }
  }
}

/* ---- hero --------------------------------------------------------------- */
.hero {
  position: relative;
  isolation: isolate;
  background: var(--dark);
  color: var(--dark-text);
  overflow: hidden;
}
.hero-media {
  position: absolute; inset: 0; z-index: -2;
}
.hero-media img {
  width: 100%; height: 100%; object-fit: cover; object-position: center 35%;
}
/* The photo alone fails contrast for any text; the gradient is what makes the copy
 * legible, so it is not decoration and it is not optional. The top stop is 0.7, not
 * lighter: measured behind the words on a wide screen, the hall's lights at 0.55 took the
 * small gold kicker to 2.7:1 at its worst pixel. A checker cannot measure text over a
 * photo, so this number is the only thing holding it at AA. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(180deg, rgba(var(--dark-rgb), 0.7) 0%, rgba(var(--dark-rgb), 0.8) 55%, rgba(var(--dark-rgb), 0.96) 100%);
}
.hero-inner {
  display: grid;
  gap: 1.25rem;
  padding-block: 2.25rem 2.5rem;
}
@media (min-width: 52rem) {
  .hero-inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
    align-items: center;
    gap: 2.5rem;
    padding-block: 3.5rem 4rem;
  }
}
@media (min-width: 52rem) { .hero-logo { margin: 0; width: 100%; max-width: 340px; } }

/* THE HERO LOCKUP, composed in HTML rather than baked into one image: the tournament
 * logo, the words "Presented by" as real type, and the sponsor's mark as a link. That is
 * what makes the sponsor clickable and lets the words scale and take a token colour.
 *
 * No white card behind any of it: the sponsor mark is the white-text variant, drawn for
 * a dark ground, and the script takes --dark-text. */
.hero-lockup-col { display: grid; justify-items: center; gap: 0.75rem; }
.hero-logo-main { width: clamp(200px, 52vw, 320px); height: auto; }
.hero-presented { display: grid; justify-items: center; gap: 0.35rem; margin: 0; }
.presented-script {
  font-family: var(--font-script);
  /* The script's x-height runs small, so it is set larger than a label of this weight
   * would normally be and still reads as secondary next to the logo above it. */
  font-size: clamp(1.4rem, 3.4vw, 1.9rem);
  font-weight: 700;
  line-height: 1;
  color: var(--dark-text);
}
/* The sponsor's mark is the link, and the only thing in this block that is one. The
 * target sits well over 44px (2.5.5) because the mark itself is that big. */
.presented-mark { display: block; line-height: 0; border-radius: 4px; }
.presented-mark img { width: clamp(190px, 46vw, 290px); height: auto; }
@media (min-width: 52rem) {
  .hero-lockup-col { justify-items: start; }
  .hero-presented { justify-items: start; }
}

.hero h1 { color: var(--dark-text); margin-bottom: 0.25em; }
.hero-tagline {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-size: clamp(1rem, 2.4vw, 1.3rem); font-weight: 700;
  color: var(--gold); margin-bottom: 0.9rem;
}
.hero-dates {
  font-size: clamp(1.15rem, 2.8vw, 1.5rem); font-weight: 700; margin-bottom: 0.25rem;
}
.hero-venue { display: block; font-weight: 400; font-size: 1rem; color: var(--dark-text-soft); }
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.25rem; }

/* Right now: the live game, or the next one (D-27). Meaning is in the words — "Live
 * now", "In progress · 3rd quarter" — and the dot and rule are decoration beside them. */
.now {
  margin-top: 1.25rem; padding: 0.9rem 1rem 1rem;
  background: rgba(var(--dark-rgb), 0.55);
  border: 1px solid var(--dark-line); border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  max-width: 34rem;
}
.now-live { border-left-color: var(--live); }
.now p { margin: 0 0 0.3rem; }
.now p:last-child { margin-bottom: 0; }
.now-kicker { display: flex; align-items: center; gap: 0.5rem; }
.live-dot { width: 0.65rem; height: 0.65rem; border-radius: 50%; background: var(--live); flex: 0 0 auto; }
.now-head {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.04em;
  font-weight: 700; font-size: 1.15rem; color: var(--dark-text);
}
.now-state { font-size: 0.95rem; letter-spacing: 0.06em; color: var(--gold); margin-left: 0.5rem; }
.now-matchup { font-weight: 700; font-size: 1.1rem; }
.now-score {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.4rem, 4vw, 2rem); line-height: 1.1; color: var(--dark-text);
  font-variant-numeric: tabular-nums;
}
.now-where { color: var(--dark-text-soft); font-size: 0.95rem; }
.now .countdown-wrap { margin-top: 0.75rem; }
/* NOW beside NEXT (D-100): side by side where there is room, stacked on a phone. Each card
 * is two rows, logo / short name / score, so the pair stays short. */
.now-games {
  display: grid; gap: 0.75rem; margin-top: 1.25rem; max-width: 46rem;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
}
.now-games .now { margin-top: 0; max-width: none; }
.now-head a { color: inherit; text-decoration-color: var(--gold); }
.now-teams { list-style: none; margin: 0.5rem 0; padding: 0; display: grid; gap: 0.4rem; }
.now-team { display: flex; align-items: center; gap: 0.6rem; min-width: 0; }
.now-team-name { flex: 1 1 auto; min-width: 0; font-weight: 600; color: var(--dark-text); overflow-wrap: anywhere; }
.now-team.is-winner .now-team-name, .now-team.is-winner .now-team-score { font-weight: 800; }
.now-team-score {
  font-family: var(--font-display); font-weight: 700; font-size: 1.5rem; line-height: 1;
  color: var(--dark-text); font-variant-numeric: tabular-nums;
}

/* Countdown. The sentence is server-rendered; the ticking cells are added by JS and are
 * aria-hidden, because the sentence is the accessible version. */
.countdown-wrap { margin-top: 1.25rem; }
.countdown-sentence {
  display: inline-block; margin: 0 0 0.6rem;
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.08em;
  font-weight: 700; color: var(--gold);
}
.countdown {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.5rem;
  max-width: 26rem;
}
.countdown-cell {
  background: rgba(var(--dark-rgb), 0.55);
  border: 1px solid var(--dark-line);
  border-top: 3px solid var(--orange);
  border-radius: var(--radius);
  padding: 0.55rem 0.25rem 0.5rem;
  text-align: center;
}
.countdown-num {
  display: block; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.6rem, 6vw, 2.6rem); line-height: 1; color: var(--dark-text);
  font-variant-numeric: tabular-nums;
}
.countdown-label {
  display: block; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--dark-text-soft); margin-top: 0.25rem;
}
/* The pause control WCAG 2.2.2 requires for anything that auto-updates for more than
 * five seconds. Kept deliberately quiet: a text button, 44px tall for the touch target
 * (2.5.5), styled like a footnote rather than a call to action. */
.countdown-toggle {
  margin-top: 0.15rem; min-height: 44px; padding: 0 0.25rem;
  background: transparent; color: var(--dark-text-soft);
  border: 0; border-radius: 2px;
  font: inherit; font-size: 0.8rem; text-decoration: underline; text-underline-offset: 3px;
  cursor: pointer;
}
.countdown-toggle:hover { color: var(--dark-text); }

/* Present for assistive tech and no-JS, gone from the screen. Used for the countdown
 * sentence once the ticking cells say the same thing visually. */
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}


/* ---- buttons ------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: 0.65rem 1.4rem;
  border: 2px solid var(--btn-outline); border-radius: var(--radius);
  background: var(--paper); color: var(--btn-outline);
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; font-size: 1.05rem; text-decoration: none; cursor: pointer;
}
.btn:hover { background: var(--paper-alt); color: var(--heading); }
.btn-primary { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.btn-primary:hover { background: var(--brand-deep); border-color: var(--brand-deep); color: var(--brand-ink); }
/* On the dark hero the secondary button is an outline in the light text colour. */
.on-dark .btn:not(.btn-primary) { background: transparent; color: var(--dark-text); border-color: var(--dark-text-soft); }
.on-dark .btn:not(.btn-primary):hover { background: var(--dark-2); color: var(--gold); border-color: var(--gold); }
.on-dark .btn-primary { border-color: var(--orange); background: var(--brand); }
.on-dark .btn-primary:hover { background: var(--brand-deep); }

/* ---- main --------------------------------------------------------------- */
main > .wrap { padding-block: 2.5rem 3rem; }
section + section { margin-top: 2.75rem; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.section-head h2 { margin: 0; }

/* ---- panels ------------------------------------------------------------- */
.panel-grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 46rem) { .panel-grid { grid-template-columns: repeat(3, 1fr); } }
.panel {
  background: var(--paper-alt);
  border: 1px solid var(--line);
  border-top: 4px solid var(--accent-line);
  border-radius: var(--radius);
  padding: 1.15rem 1.2rem;
}
.panel h3 { margin-bottom: 0.4rem; }
.panel p:last-child { margin-bottom: 0; }

/* A status never relies on colour alone: it carries a word and a shape too. */
.status { display: flex; align-items: baseline; gap: 0.5rem; font-weight: 700; }
.status-dot { flex: 0 0 auto; width: 0.6rem; height: 0.6rem; border-radius: 50%; background: currentColor; transform: translateY(-0.1rem); }
.status-soon { color: var(--soon); }
.status-open { color: var(--ok); }

/* ---- prose and facts ---------------------------------------------------- */
.prose { max-width: var(--measure); }
.two-col { display: grid; gap: 2rem; }
@media (min-width: 52rem) { .two-col { grid-template-columns: 1.3fr 1fr; align-items: start; } }

/* ---- the game video (D-82) ------------------------------------------------- */
/* 16:9 before and after the player loads, so the page never moves. Dark, because a
 * video player is, and the panel is a link that turns into one. */
.game-video {
  position: relative; width: 100%; max-width: 48rem; aspect-ratio: 16 / 9;
  margin: 1.25rem 0; background: var(--dark); border-radius: var(--radius); overflow: hidden;
}
.game-video iframe { width: 100%; height: 100%; border: 0; display: block; }
.video-facade {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.75rem;
  text-decoration: none; color: var(--dark-text);
}
/* The play mark: an orange disc with a triangle, drawn in CSS. Decorative; the words
 * beside it carry the meaning. */
.video-play {
  width: 4.5rem; height: 4.5rem; border-radius: 50%; background: var(--orange);
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.15s ease;
}
.video-play::after {
  content: ""; margin-left: 0.35rem;
  border-style: solid; border-width: 0.9rem 0 0.9rem 1.5rem;
  border-color: transparent transparent transparent var(--dark);
}
.video-facade:hover .video-play, .video-facade:focus-visible .video-play { transform: scale(1.08); }
@media (prefers-reduced-motion: reduce) { .video-play { transition: none; } }
.video-label { font-family: var(--font-display); font-weight: 700; font-size: 1.25rem; letter-spacing: 0.03em; }
.video-facade:hover .video-label { color: var(--gold); }

/* ---- Getting here (D-81) ---------------------------------------------------- */
/* A card in the right-hand column beside About; under it on a phone. */
.about-row { align-items: start; }
.getting-here {
  background: var(--paper-alt); border: 1px solid var(--line);
  border-top: 4px solid var(--accent-line); border-radius: var(--radius);
  padding: 1rem 1.25rem 1.25rem;
}
.getting-here h2 { margin-top: 0; }
/* Address on the left, Directions on the right, wrapping under it when narrow. */
.getting-here-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 0.75rem 1rem; margin-bottom: 1rem;
}
.getting-here-name { font-weight: 700; font-size: 1.15rem; margin: 0 0 0.15rem; color: var(--heading); }
.getting-here address { font-style: normal; }
/* The map fills the card's width at 4:3, sized before it loads so nothing moves. */
.map-embed {
  width: 100%; aspect-ratio: 4 / 3;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden;
}
.map-embed iframe { width: 100%; height: 100%; border: 0; display: block; }

/* ---- the Holiday Hoops front page (D-80) ------------------------------------ */
.hoops-lede { font-size: 1.15rem; color: var(--ink); }
/* Dark, like the hero and the numbers band, because the countdown's cells are drawn for
 * a dark surface — the same block the homepage hero carries. */
.hoops-aside {
  background: var(--dark); border-top: 4px solid var(--orange);
  border-radius: var(--radius); padding: 1.1rem 1.25rem 1.25rem;
}
.hoops-aside .kicker { margin: 0; }
.hoops-aside .countdown-wrap { margin-top: 0.5rem; }
.hoops-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 1rem 0 0; }
/* Newest first, in columns: two dozen one-line entries read as a list, not a scroll. */
.champion-list {
  list-style: none; margin: 1rem 0 2.5rem; padding: 0;
  columns: 2 18rem; column-gap: 2rem;
}
.champion-list li {
  break-inside: avoid; display: grid; grid-template-columns: 3.25rem 1fr; gap: 0.5rem;
  padding: 0.35rem 0; border-bottom: 1px solid var(--line);
}
.champion-year { font-family: var(--font-display); font-weight: 700; color: var(--heading); }

/* ---- "By the numbers" (D-79) ---------------------------------------------- */
/* A dark panel of boxed figures, on the homepage (the Classic) and the Holiday Hoops page.
 * Gold on the dark surfaces, the hero's own pairing, so the band reads as part of the
 * brand rather than a widget dropped in. */
.stats-band {
  background: var(--dark); color: var(--dark-text);
  border-top: 4px solid var(--orange); border-radius: var(--radius);
  padding: 2rem var(--gutter) 2.25rem; margin-block: 2.5rem;
}
.stats-band h2 { margin: 0; text-align: center; }
/* FLEX, NOT GRID, so a short last row CENTRES instead of hugging the left: nine boxes go
 * five and four on a wide screen, and Holiday Hoops' two sit in the middle. Every box
 * is the same width, which is what makes the rows read as one balanced block. */
.stats-grid {
  list-style: none; margin: 1.5rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.75rem;
}
.stats-grid > .stat { flex: 0 1 13rem; }
/* Two across on a phone, the odd one out centred beneath. */
@media (max-width: 30rem) {
  .stats-grid > .stat { flex-basis: calc(50% - 0.375rem); }
}
.stat {
  /* A query container, so the figure below sizes to its BOX, not to the window: at a
   * fixed size "30,000+" and "Hundreds" ran out of their boxes on a laptop. */
  container-type: inline-size;
  display: flex; flex-direction: column; align-items: center; gap: 0.5rem;
  text-align: center; padding: 1.4rem 0.9rem 1.2rem;
  background: var(--dark-2); border: 1px solid var(--dark-line);
  border-top: 3px solid var(--orange); border-radius: var(--radius);
}
.stat-figure {
  font-family: var(--font-display); font-weight: 700; line-height: 1;
  /* The first line is for a browser without container units; it fits the narrowest box.
   * 21% of the box's width keeps eight condensed characters inside it at any size. */
  font-size: 2rem;
  font-size: clamp(1.75rem, 21cqi, 3.25rem); color: var(--gold);
  font-variant-numeric: tabular-nums;
}
/* Decorative and orange, like the box's top rule: a 3:1 graphic on --dark-2 (1.4.11). */
.stat-icon {
  width: 2.25rem; height: 2.25rem; color: var(--orange);
  fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.stat-label { color: var(--dark-text-soft); font-size: 0.95rem; line-height: 1.35; }

/* The odometer (odometer.js): each digit is a one-digit window over a column of 0-9s
 * that slides up. A window is as wide as its widest digit, so nothing moves sideways. */
.odo { display: inline-flex; align-items: flex-start; }
.odo-wheel { display: inline-block; height: 1em; overflow: hidden; }
.odo-column {
  display: flex; flex-direction: column;
  transition-property: transform;
  /* Fast off the mark, then a long settle onto the digit, like a wheel losing speed. */
  transition-timing-function: cubic-bezier(0.12, 0.65, 0.18, 1);
}
.odo-column > span { display: block; height: 1em; line-height: 1; text-align: center; }
.odo-still { display: inline-block; height: 1em; line-height: 1; }
@media (prefers-reduced-motion: reduce) { .odo-column { transition: none; } }

/* ---- sponsors (D-99) ------------------------------------------------------- */
/* Logos sit on the white logo pad for the same reason school marks do: they are drawn
 * for white, and a dark wordmark vanishes on the dark paper. A tier sets two sizes, the
 * tile's width and the logo's height, so going down the sponsors page the logos get
 * smaller and more fit on a row. Rows are centred, so one presenting sponsor sits in the
 * middle rather than in a column of empty tracks. Every tile is a link, and at its
 * smallest still a 44px target. */
.sponsors-section { margin-top: 2.5rem; }
.sponsors-section .sponsor-list + .sponsor-list { margin-top: 0.75rem; }
.sponsors-all { margin-top: 1rem; text-align: center; }
.sponsor-list {
  --tile: 9rem; --logo-h: 4rem;
  list-style: none; margin: 1rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.75rem;
}
.sponsor { flex: 0 1 var(--tile); min-width: 0; text-align: center; }
.sponsor-mark {
  display: flex; align-items: center; justify-content: center;
  min-height: max(44px, calc(var(--logo-h) + 1.5rem)); padding: 0.75rem; box-sizing: border-box;
  background: var(--logo-pad); border: 1px solid var(--line); border-radius: var(--radius);
}
.sponsor-mark img { max-height: var(--logo-h); max-width: 100%; width: auto; object-fit: contain; }
.sponsor-mark .sponsor-name { font-weight: 700; color: var(--brand); }
.sponsor-tier-presenting { --tile: 20rem; --logo-h: 7rem; }
.sponsor-tier-co_sponsor { --tile: 16rem; --logo-h: 5.5rem; }
.sponsor-tier-event      { --tile: 13rem; --logo-h: 4.5rem; }
.sponsor-tier-platinum   { --tile: 10rem; --logo-h: 4rem; }
.sponsor-tier-gold       { --tile: 8.5rem; --logo-h: 3.25rem; }
.sponsor-tier-silver     { --tile: 7rem; --logo-h: 2.75rem; }
.sponsor-tier-bronze, .sponsor-tier-food_court { --tile: 5.75rem; --logo-h: 2.25rem; }
.sponsor-tier { margin-top: 2.5rem; }
.sponsor-tier h2 { text-align: center; }
.sponsor-event { margin: 0.35rem 0 0; font-size: 0.9375rem; }
.contest-sponsors { margin-top: 1rem; }
.contest-sponsors-label { margin: 0; color: var(--ink-soft); text-align: center; }
.contest-sponsors .sponsor-list { margin-top: 0.5rem; }
.food-vendor { margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--line); max-width: var(--measure); }
.food-vendor-head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; }
.food-vendor-head h2 { margin: 0; }
.food-vendor-logo {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 5.5rem; height: 4rem; padding: 0.5rem; box-sizing: border-box;
  background: var(--logo-pad); border: 1px solid var(--line); border-radius: var(--radius);
}
.food-vendor-logo img { max-height: 100%; max-width: 100%; object-fit: contain; }

/* ---- teams page --------------------------------------------------------- */
.crumbs { font-size: 0.875rem; margin-bottom: 1rem; }
.crumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; }
.crumbs li + li::before { content: "/"; margin-right: 0.5rem; color: var(--line-strong); }

.page-head { padding-bottom: 1.25rem; border-bottom: 1px solid var(--line); margin-bottom: 1.5rem; }
.page-dates { font-weight: 700; margin: 0; }
.field-summary { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; color: var(--heading); margin-bottom: 1.25rem; }

.team-grid { list-style: none; margin: 0 0 2rem; padding: 0; display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 38rem) { .team-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 62rem) { .team-grid { grid-template-columns: repeat(3, 1fr); } }
.team-card {
  border: 1px solid var(--line); border-left: 5px solid var(--accent-line);
  border-radius: var(--radius); padding: 1rem; background: var(--paper-alt);
}
.team-name { font-size: 1.2rem; margin-bottom: 0.5rem; text-transform: none; }
.team-meta { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.6rem; font-size: 0.95rem; }
.team-meta dt { color: var(--ink-soft); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.06em; align-self: baseline; }
.team-meta dd { margin: 0; }

.callout {
  background: var(--paper-alt); border: 1px solid var(--line); border-left: 5px solid var(--orange);
  border-radius: var(--radius); padding: 1.25rem; margin-bottom: 2rem; max-width: var(--measure);
}
.callout h1, .callout h2 { margin-top: 0; }
.callout p:last-child { margin-bottom: 0; }
.callout-inline { padding: 0.85rem 1.1rem; }

/* ---- editorial pages and history ------------------------------------------ */
.page-summary { color: var(--ink-soft); margin: 0; max-width: var(--measure); }
.page-body h2 { font-size: clamp(1.3rem, 3vw, 1.7rem); margin-top: 1.75rem; }
.page-body h2:first-child { margin-top: 0; }
.page-body ul, .page-body ol { padding-left: 1.4rem; }
.page-body li { margin-bottom: 0.3rem; }
.page-body li strong { color: var(--heading); }
/* What the page editor can produce (D-97). Size and alignment are classes because the
 * CSP drops inline styles. */
.page-body .text-small { font-size: 0.875em; }
.page-body .text-large { font-size: 1.25em; }
.page-body .text-huge { font-size: 1.6em; line-height: 1.3; }
.page-body .align-center { text-align: center; }
.page-body .align-right { text-align: right; }
.page-body img { margin: 1rem 0; border-radius: var(--radius); }
.page-body .align-center img { margin-inline: auto; }
.page-body .align-right img { margin-left: auto; }
.page-body blockquote, .ql-editor.page-body blockquote {
  margin: 0 0 1rem; padding: 0.25rem 0 0.25rem 1rem;
  border-left: 4px solid var(--accent-line); color: var(--ink-soft);
}
.page-body hr, .ql-editor.page-body hr { border: 0; border-top: 1px solid var(--line-strong); margin: 1.75rem 0; }

.index-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; max-width: var(--measure); }
.index-list li { background: var(--paper-alt); border: 1px solid var(--line); border-left: 5px solid var(--accent-line); border-radius: var(--radius); padding: 1rem 1.15rem; }
.index-list a { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; font-size: 1.15rem; text-decoration: none; display: inline-block; min-height: 44px; line-height: 44px; }
.index-list a:hover { text-decoration: underline; }
.index-list p { margin: 0; color: var(--ink-soft); }

/* One card per year. A heading per year is what a fan looking for 1996 wants, and what
 * a screen reader can jump between; one 41-year table is one object to both. */
.year-grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 40rem) { .year-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .year-grid { grid-template-columns: repeat(3, 1fr); } .year-grid-wide { grid-template-columns: repeat(2, 1fr); } }
.year-card { background: var(--paper-alt); border: 1px solid var(--line); border-top: 4px solid var(--accent-line); border-radius: var(--radius); padding: 1rem 1.15rem; }
/* margin-top: 0 because each card is a <section>, and the site's `section + section`
 * spacing (2.75rem) gave every card but the first a top margin. In a grid that pushed the
 * rest of the first row down, so the first card stood taller than its neighbours. The
 * grid's own gap spaces the cards. */
.year-grid > .year-card { margin-top: 0; }
.year-card h2 { font-size: 1.35rem; margin-bottom: 0.6rem; }
.placing-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.3rem; }
.placing { display: grid; grid-template-columns: 6.5rem 1fr; gap: 0.5rem; align-items: baseline; }
.placing-label, .mvp .placing-label {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.08em;
  font-size: 0.75rem; font-weight: 700; color: var(--accent-ink);
}
.placing-1 .placing-label { color: var(--heading); }
.mvp { margin: 0 0 0.6rem; }
.mvp .placing-label { margin-right: 0.4rem; }
.name-list { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 1.5rem; font-size: 0.95rem; }
.name-list li { break-inside: avoid; margin-bottom: 0.2rem; }
@media (max-width: 30rem) { .name-list { columns: 1; } }

/* The city and state after a school name: present, but never competing with the name
   itself, which is the thing a reader is scanning for. */
.placing-place { color: var(--ink-soft); font-size: 0.9em; }
/* A contest entrant's score: after the school, in the muted ink, figures lined up. */
.placing-score { color: var(--ink-soft); font-size: 0.9em; font-variant-numeric: tabular-nums; white-space: nowrap; }
.placing-score::before { content: "\00b7\00a0"; }
.contest-scoring { margin: 0.6rem 0 0; font-size: 0.9rem; }
/* On the champions page the place takes its own line under the school, so every row
   reads the same way: name, then where it is. */
.placing-place-below { display: block; font-size: 0.85em; line-height: 1.3; }

/* The record book (D-87). One card per section, a record per row: its name, the mark
   large, and whoever holds it under that. */
.record-section { margin-bottom: 2rem; }
.record-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; max-width: var(--measure); }
.record { background: var(--paper-alt); border: 1px solid var(--line); border-left: 5px solid var(--accent-line); border-radius: var(--radius); padding: 0.8rem 1.1rem; }
.record-name {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.04em;
  font-weight: 700; font-size: 1rem; text-decoration: none;
  display: inline-block; min-height: 44px; line-height: 44px;
}
.record-name:hover { text-decoration: underline; }
.record-mark { margin: 0; }
.record-value { font-family: var(--font-display); font-weight: 700; font-size: 1.6rem; font-variant-numeric: tabular-nums; }
.record-unit { color: var(--ink-soft); font-size: 0.9rem; }
.record-shared {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-size: 0.7rem; font-weight: 700; color: var(--accent-ink);
  border: 1px solid var(--rule); border-radius: 0.2rem; padding: 0 0.3rem; margin-left: 0.3rem;
}
.record-holders { list-style: none; margin: 0.2rem 0 0; padding: 0; }
.record-who { font-weight: 700; }
.record-vs, .record-year { color: var(--ink-soft); }
.record-table { max-width: var(--measure); }
.record-rank, .record-cell-value { white-space: nowrap; font-variant-numeric: tabular-nums; }
.record-cell-value { font-weight: 700; }
/* The holder is the row's header, but only the name is bold, as in the lists. */
.record-table tbody th { font-weight: 400; }

/* A short placing list (a game page's): a narrower label column than the champions list,
   because it holds a short place, not the word "Runner-up". */
.placing-list-inline .placing { grid-template-columns: 4rem 1fr; }

/* A team page's other years (D-125): each year's finish and all-tournament picks on one
 * entry, flowing into columns where there is room, so a long history is a short block.
 * An entry never splits across a column. */
.other-years {
  list-style: none; margin: 0; padding: 0;
  columns: 3 18rem; column-gap: 2rem;
}
.other-years > li {
  display: grid; grid-template-columns: 4rem 1fr; gap: 0.5rem; align-items: baseline;
  break-inside: avoid; margin-bottom: 0.45rem;
}
.other-year-finish { color: var(--heading); }
/* AWARDS, at the top of a decided year's panel (D-126): the finish as a badge on the team's
 * colour, the one surface it may be (D-28), with the derived ink that always clears AA; the
 * site's brand pair on a page with no team colour. Then the all-tournament team. */
.year-awards {
  margin: 0 0 1.25rem; padding: 0.9rem 1rem;
  background: var(--paper-alt); border: 1px solid var(--line);
  border-left: 4px solid var(--accent-line); border-radius: var(--radius);
}
.has-team .year-awards { border-left-color: var(--team); }
.year-awards-head {
  font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-soft); margin: 0 0 0.5rem;
}
.year-finish {
  display: inline-block; margin: 0 0 0.5rem; padding: 0.35rem 0.85rem;
  font-family: var(--font-display); font-weight: 700; font-size: 1.2rem;
  text-transform: uppercase; letter-spacing: 0.05em;
  background: var(--brand); color: var(--brand-ink); border-radius: var(--radius);
}
.has-team .year-finish { background: var(--team); color: var(--team-ink); }
/* The selections as chips (D-126). The label is capitalised here, not in the text, so a screen
 * reader says the words rather than spelling the capitals. A linked chip keeps the link's
 * colour and underline on its name; a plain one is the heading's colour with none, so the two
 * differ by more than colour. The MVP's chip is filled like the finish badge. */
.year-honors-label {
  margin: 0.75rem 0 0.4rem;
  font-family: var(--font-body); font-size: 0.8rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-soft);
}
.honor-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }
.honor-chip {
  display: inline-flex; align-items: center; gap: 0.45rem;
  min-height: 40px; padding: 0.25rem 0.9rem; box-sizing: border-box;
  font-weight: 700; color: var(--heading);
  background: var(--paper); border: 1px solid var(--line-strong); border-radius: 999px;
}
a.honor-chip { color: var(--link); }
a.honor-chip:hover { border-color: var(--heading); color: var(--heading); }
a.honor-chip .honor-chip-name { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
span.honor-chip .honor-chip-name { text-decoration: none; }
.honor-chip.is-mvp { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.has-team .honor-chip.is-mvp { background: var(--team); border-color: var(--team); color: var(--team-ink); }
.honor-chip-tag {
  font-family: var(--font-display); font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase;
}
/* A contest title (D-129): a paper chip with its tag in the accent ink, apart from the MVP's fill. */
.honor-chip.is-contest .honor-chip-tag { color: var(--accent-ink); }
.honor-names { font-size: 0.95rem; }
/* MVP is marked with a word, never colour alone. */
.honor-mvp {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.08em;
  font-size: 0.68rem; font-weight: 700; color: var(--accent-ink);
  border: 1px solid var(--rule); border-radius: 0.2rem; padding: 0 0.25rem;
}
@media (max-width: 30rem) {
  .placing-list-inline .placing, .other-years > li { grid-template-columns: 3.2rem 1fr; }
}

/* ---- a team's page wears its colours (D-28) ------------------------------ */
/* Only under .has-team, which the layout sets when the server wrote a theme. Everything
 * else on the page is untouched: the dark header, hero and footer are the tournament's,
 * the paper and its text are the site's. The team colour is a band, a button and the
 * accent rules — surfaces with derived ink, never text on paper. */
.has-team .page-head {
  background: var(--team); color: var(--team-ink);
  border: 1px solid var(--line); border-left: 8px solid var(--team-stripe);
  border-radius: var(--radius); padding: 1.25rem 1.25rem 1.1rem;
}
.has-team .page-head h1, .has-team .page-head .kicker, .has-team .page-head .page-dates,
.has-team .page-head .hero-venue, .has-team .page-head a { color: var(--team-ink); }
.has-team .btn-primary { background: var(--team); border-color: var(--team); color: var(--team-ink); }
.has-team .btn-primary:hover { background: var(--team); box-shadow: inset 0 0 0 2px var(--team-ink); }
.has-team .panel { border-top-color: var(--team); }
.has-team .team-card, .has-team .callout, .has-team .index-list li { border-left-color: var(--team); }
.has-team .year-card { border-top-color: var(--team); }
/* A two-colour swatch beside a team's name. Decorative: the name carries the meaning. */
.swatch {
  display: inline-block; width: 1.1em; height: 1.1em; vertical-align: -0.15em;
  background: linear-gradient(135deg, var(--team) 0 62%, var(--team-stripe) 62%);
  border: 1px solid var(--line-strong); border-radius: 2px;
}

/* ---- footer ------------------------------------------------------------- */
.site-footer {
  background: var(--dark); color: var(--dark-text);
  border-top: 4px solid var(--orange);
  padding-block: 2rem 2.5rem; font-size: 0.95rem;
}
/* Three parts: who and where, the accessibility statement, and a legal strip under a rule.
 * On a wide screen the statement takes the larger track because it is the longest text
 * here — at its widest that is still a readable measure — and the copyright and logo notice
 * span the full width beneath both, so neither is squeezed into a column it doesn't need. */
.footer-inner { display: grid; gap: 1.5rem; }
.footer-title { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; margin-bottom: 0.25rem; }
.footer-id p:last-child { margin-bottom: 0; }
/* Smaller than the body, but not dimmed below --dark-text-soft: fine print still has to
 * be readable. */
.fine-print { font-size: 0.85rem; }
/* Left-aligned even on the right, because a ragged left edge on a paragraph this long is
 * hard to read. The heading is the footer title's style at a body size. */
.footer-a11y h2 { font-size: 0.95rem; margin: 0 0 0.25rem; }
.footer-a11y .fine-print { font-size: 0.9rem; color: var(--dark-text-soft); margin: 0; }
.footer-legal { border-top: 1px solid var(--dark-line); padding-top: 1.25rem; }
.footer-legal p { margin: 0; }
.footer-legal .fine-print { margin-top: 0.35rem; }
@media (min-width: 46rem) {
  .footer-inner { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); column-gap: 3rem; }
  .footer-legal { grid-column: 1 / -1; }
}

/* ---- reduced motion ----------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---- print -------------------------------------------------------------- */
/* Schedules and brackets get printed and pinned up in the gym. Ink is black on white
 * whatever the screen tokens say. */
@media print {
  .site-header nav, .site-footer nav, .skip-link, .announcement, .hero-actions, .hero-media,
  .countdown, .countdown-toggle, .live-dot { display: none; }
  .now, .has-team .page-head { background: #fff; color: #000; border-color: #999; }
  .hero, .site-header, .site-footer { background: #fff; color: #000; }
  .hero::before { display: none; }
  body { font-size: 11pt; color: #000; background: #fff; }
  a { text-decoration: none; color: #000; }
  .panel, .team-card, .callout { border: 1px solid #999; break-inside: avoid; }
}

/* ---- seasonal takeover (D-35) -------------------------------------------- */
/* The block directly under the hero that carries the news of the moment: the field is
 * announced, the bracket is drawn. It sits on the alternate paper so it reads as a
 * distinct band between the dark hero and the panels below, without another card
 * border — the hero's edge is already doing that work. */
.takeover {
  background: var(--paper-alt);
  border-bottom: 1px solid var(--line);
  padding-block: 2.25rem 2.5rem;
}
.takeover h2 { margin-bottom: 0.5rem; }
.takeover-lede { color: var(--ink-soft); max-width: var(--measure); margin-bottom: 1.5rem; }
/* auto-fit rather than a fixed column count, so the grid holds a field of any size
 * without a media query per size.
 *
 * EVERY ROW IS THE TALLEST ROW'S HEIGHT, so the dividing rules sit at even intervals:
 * `1fr` rows in a grid of no set height all take the tallest row's size, and each row
 * centres its contents (below). One long name therefore sets every row's height, so the
 * columns are at least 18rem: at 15rem a long school name took three lines and every row
 * grew to match. */
.takeover-teams {
  list-style: none; margin: 0 0 1.5rem; padding: 0;
  display: grid; gap: 0.6rem 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  grid-auto-rows: 1fr;
}
/* CENTRED, NOT BASELINE-ALIGNED. A logo chip has no baseline of its own (a flex
 * container falls back to the box's bottom edge), so baseline alignment drops the name
 * level with the bottom of the crest. Centring puts the mark beside the name.
 *
 * THE CHIP NEVER WRAPS AWAY FROM ITS NAME. The name and the town are one item that takes
 * the rest of the row and wraps inside it, so a long name never leaves the logo alone on
 * a line. */
.takeover-teams li {
  display: flex; align-items: center; gap: 0.5rem;
  padding-block: 0.35rem;
  border-bottom: 1px solid var(--line);
}
/* The town is always the second line, so every row reads the same way, name then place,
 * rather than some putting the town beside the name and some under it. */
.takeover-text {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column;
}
/* THE ROW IS THE LOGO'S HEIGHT WHETHER OR NOT THERE IS A LOGO, so a team with no logo,
 * or no town for a second line, never rules a shorter row than its neighbour. The logo's
 * own size is not touched: the logo sizes are set in one place. */
.takeover-teams li { min-height: 44px; }
.takeover-teams .logo-box { margin-right: 0.15rem; }
.takeover-team { font-weight: 700; color: var(--heading); }
.takeover-home { color: var(--ink-soft); font-size: 0.9rem; white-space: nowrap; }

/* ---- team page ----------------------------------------------------------- */
/* The one surface that wears the program's colours (D-28). --team-ink is pure white or
 * pure black, chosen so it always clears AA against --team, which is why no rule here
 * sets a text colour conditionally. Without a theme the band falls back to the site's
 * own dark. */
.team-banner {
  background: var(--dark);
  color: var(--dark-text);
  border-radius: var(--radius);
  margin-bottom: 1.5rem;
  padding: 1.5rem;
  border-left: 8px solid var(--gold);
  display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap;
}
/* FIXED HEIGHT, FLEXIBLE WIDTH. A square box was wrong: logos run from 0.8:1 crests to
 * 3.3:1 wordmarks, and a 2.5:1 wordmark forced into a square renders at a third of the
 * available height. Matching on height makes every logo optically the same size.
 *
 * The white pad is deliberate: many logos are dark with no outline and would vanish into
 * their own team colour. */
/* ONE BOX, FOUR SIZES, AND THE LOGO SCALES TO FILL IT.
 *
 * The space a logo occupies is fixed and the artwork scales into it, rather than the
 * artwork setting the size, so a wide wordmark and a tall crest end up the same optical
 * weight. `object-fit: contain` does the scaling and keeps the aspect ratio.
 *
 * THE PADDING IS BUILT IN, because the logo files are cropped tight to the artwork with
 * no margin. --logo-inset is that margin, as real padding on a border-box, so the content
 * area is always exactly (size - 2 × inset).
 *
 * The box has a definite width AND height, so the image's 100%/100% resolve against it.
 * Bound by width alone, a portrait logo keeps its intrinsic height and hangs out of the
 * bottom. */
.logo-box {
  --logo-size: 72px;
  --logo-inset: 7px;
  flex: 0 0 auto;
  box-sizing: border-box;
  width: var(--logo-size);
  height: var(--logo-size);
  padding: var(--logo-inset);
  display: block;
  background: var(--logo-pad);
  border-radius: var(--radius);
  /* Hard stop, in case anything above ever lets a logo escape. */
  overflow: hidden;
}
/* A <picture> is INLINE, so where one sits between the box and the image it lands on the
 * text baseline and leaves descender space underneath — a logo with more room below it
 * than above, which is exactly the uneven padding. Block-ify whatever is in there. */
.logo-box > * { display: block; line-height: 0; }
.logo-box img {
  display: block;
  width: 100%; height: 100%;
  /* Scale into the space, never crop and never distort. */
  object-fit: contain;
}

/* The spaces a logo is given. Nothing else may set these. */
.logo-team  { --logo-size: 104px; --logo-inset: 10px; }
.logo-card  { --logo-size: 64px;  --logo-inset: 6px; }
.logo-score { --logo-size: 76px;  --logo-inset: 8px; }
.logo-chip  { --logo-size: 28px;  --logo-inset: 3px; }
.logo-now   { --logo-size: 44px;  --logo-inset: 4px; }
/* As tall as the row it sits in, where the row is already two lines of text or a 44px
 * control: a team's games, the homepage's field. A logo sized to one line of text beside
 * them was a third of the room it had. On a phone a game card's opponent drops to a line
 * of its own, so there it is a step smaller rather than making every card taller. */
.logo-row   { --logo-size: 44px;  --logo-inset: 4px; }
@media (max-width: 34rem) { .game-card .logo-row { --logo-size: 36px; } }
/* On the corner of a player's headshot on their trading card. */
.logo-badge { --logo-size: 40px;  --logo-inset: 4px; }

/* THE GROUND A LOGO SITS ON is set per program, because the right answer is in the
 * artwork: a flat mark floats, a dark crest needs a light card, a white wordmark needs a
 * dark one. All three keep the same box and inset, so switching between them moves
 * nothing else on the page. */
.logo-bg-light { background: var(--logo-pad); }
.logo-bg-dark  { background: var(--dark); }
/* No card at all: the logo sits directly on whatever is behind it. The inset STAYS, so a
 * floating mark still lines up with a carded one beside it in the same row. */
.logo-bg-none  { background: none; }

/* A logo preview: the banner's logo box on the team colour, then the same box on the light
 * page's paper and the dark page's. */
.admin-logo-previews { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; margin: 0.25rem 0 0; }
.admin-logo-sample { margin: 0; }
.admin-logo-sample figcaption { margin-top: 0.3rem; font-size: 0.8rem; color: var(--ink-soft); }
.admin-logo-paper { padding: 12px; border: 1px solid var(--line-strong); }
.admin-logo-paper-day { background: var(--paper-day); }
.admin-logo-paper-night { background: var(--paper-night); }
.admin-logo-preview {
  position: relative; display: inline-block; overflow: hidden;
  border-radius: var(--radius); padding: 12px 12px 12px 20px; margin: 0;
  line-height: 0;
}
.admin-logo-ground { position: absolute; inset: 0; width: 100%; height: 100%; }
.admin-logo-ground-team:not([fill])   { fill: var(--dark); }
.admin-logo-ground-stripe:not([fill]) { fill: var(--gold); }
.admin-logo-preview .logo-box { position: relative; }

.team-banner-inner { flex: 1 1 16rem; min-width: 0; }
.has-team .team-banner {
  background: var(--team);
  color: var(--team-ink);
  border-left-color: var(--team-stripe);
}
/* The rest of the page picks the colour up in small doses: the rules beside the
 * headline numbers and the section edges. Deliberately restrained — the band is the
 * statement and everything else is an accent, so a bright team colour does not turn the
 * results table into a highlighter. --team is never TEXT on the paper (D-28): it is
 * only ever a surface or a rule, which is what makes it safe without knowing the hue. */
.has-team .team-facts div { border-left-color: var(--team); }
.has-team .game-card { border-left-color: var(--team); }
.has-team .takeover-team { color: var(--heading); }
.team-banner h1 { color: inherit; margin-bottom: 0.35rem; }
.team-banner .kicker { color: inherit; opacity: 0.85; }
.team-banner-meta {
  display: flex; flex-wrap: wrap; gap: 0.35rem 1rem;
  margin: 0; color: inherit; opacity: 0.9; font-weight: 700;
}

/* The headline numbers. auto-fit so three facts sit in a row on a laptop and stack on a
 * phone without a breakpoint per count — a first-time program has one, a 40-year one
 * has three. */
.team-facts {
  display: grid; gap: 0.75rem 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  margin: 0 0 1.25rem; padding: 0;
}
.team-facts div {
  border-left: 3px solid var(--accent-line);
  padding-left: 0.85rem;
}
.team-facts dt {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: 0.06em; font-size: 0.8rem; color: var(--ink-soft);
}
.team-facts dd {
  margin: 0; font-family: var(--font-display); font-weight: 700;
  font-size: 1.6rem; line-height: 1.1; color: var(--heading);
  font-variant-numeric: tabular-nums;
}
.team-fact-note {
  display: block; font-family: var(--font-body); font-weight: 400;
  font-size: 0.9rem; color: var(--ink-soft);
}

/* Icon-only links to the program's own places. 44x44 (2.5.5) — an icon is a small
 * target and this row is the one place on the page a thumb is likely to miss. */
.team-links {
  display: flex; flex-wrap: wrap; gap: 0.4rem; list-style: none;
  margin: 0 0 1.5rem; padding: 0;
}
.team-links a {
  width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.team-links a:hover, .team-links a:focus-visible {
  color: var(--brand-ink); background: var(--accent-line); border-color: var(--accent-line);
}
.has-team .team-links a:hover, .has-team .team-links a:focus-visible {
  color: var(--team-ink); background: var(--team); border-color: var(--team);
}
/* currentColor throughout the SVG, so one icon works on paper, on dark and on a team
 * colour without a second copy. */
.team-links .icon { width: 20px; height: 20px; display: block; }

.team-bio { margin-bottom: 1.5rem; }

/* ---- the year selector ---------------------------------------------------- */
/* Newest year first, so the current one leads on every screen (D-124). Bracket and field
 * pages list most of the archive, so they get one row per decade, each row wrapping on a
 * phone. A team page may hold one year or forty, so it gets the newest few in a row and
 * every year behind "All years", a native disclosure that needs no script. */
.team-season { margin-bottom: 2.5rem; }
.year-strip {
  display: grid; gap: 0.25rem;
  border-bottom: 2px solid var(--line);
  padding-bottom: 0.5rem;
  margin-bottom: 1.25rem;
}
.year-strip ul {
  display: flex; flex-wrap: wrap; gap: 0.25rem;
  list-style: none; margin: 0; padding: 0;
}
.year-decade { display: flex; align-items: flex-start; gap: 0.5rem; }
.year-decade ul { flex: 1; min-width: 0; }
.year-decade-label {
  flex: 0 0 3.25rem;
  /* Centred on the first line of years, which are 44px high. */
  line-height: 44px;
  font-size: 0.85rem; font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}
/* A team page: the recent row, then "All years" beside it. Opened, the disclosure takes the
 * full width and lists every year in decade rows, and the recent row hides so no year shows
 * twice. :has() does it with no script; team-years.js sets the class for a browser without
 * :has(), which otherwise just shows both. */
.year-strip-recent { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.25rem; }
.year-all[open] { flex-basis: 100%; display: grid; gap: 0.25rem; }
.year-strip-recent.is-all-open > .year-row,
.year-strip-recent:has(> .year-all[open]) > .year-row { display: none; }
.year-all summary {
  display: inline-flex; align-items: center; gap: 0.35rem;
  min-height: 44px; padding: 0 0.75rem;
  font-family: var(--font-display); font-weight: 700; font-size: 1rem;
  color: var(--ink-soft); cursor: pointer;
  border-radius: var(--radius);
  list-style: none;
}
.year-all summary::-webkit-details-marker { display: none; }
.year-all summary::after { content: "\25BE"; }
.year-all[open] summary::after { content: "\25B4"; }
.year-all summary:hover { color: var(--heading); background: var(--paper-alt); }
.year-all summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.year-strip a {
  display: flex; align-items: center; justify-content: center;
  /* 44px high (2.5.5): this is the main control on the page and a thumb has to hit it. */
  min-height: 44px; padding: 0 0.6rem;
  font-family: var(--font-display); font-weight: 700; font-size: 1rem;
  letter-spacing: 0.02em; font-variant-numeric: tabular-nums;
  color: var(--ink-soft); text-decoration: none;
  border-radius: var(--radius);
  white-space: nowrap;
}
.year-strip a:hover { color: var(--heading); background: var(--paper-alt); }
/* The selected year is marked by weight, colour AND a 3px underline, never by colour
 * alone. The underline is what survives a greyscale print. */
.year-strip a.is-selected {
  color: var(--heading);
  background: var(--paper-alt);
  box-shadow: inset 0 -3px 0 var(--accent-line);
}
.has-team .year-strip a.is-selected { box-shadow: inset 0 -3px 0 var(--team); }

/* "Skip past the years": off screen until it has focus, then in place above the strip, so
 * a keyboard user does not Tab through every year to reach the page. Its target is an
 * empty span, with nothing to draw a ring around. */
.skip-years { position: absolute; left: -9999px; }
.skip-years:focus {
  position: static; display: inline-block; margin-bottom: 0.5rem;
  background: var(--paper); color: var(--ink);
  padding: 0.75rem 1rem; border: 3px solid var(--focus);
  font-weight: 700;
}
.skip-target:focus { outline: none; }

.year-panel:focus { outline: none; }
.year-panel:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; }
.year-meta {
  display: flex; flex-wrap: wrap; gap: 0.5rem 2rem; margin: 0 0 1rem;
}
.year-meta dt {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: 0.06em; font-size: 0.75rem; color: var(--ink-soft);
}
.year-meta dd { margin: 0; font-weight: 700; color: var(--heading); }
/* A team photo is capped well short of the panel: at full width it would push the
 * year's games off a laptop screen, and 48rem is what the 1280 copy covers at 1.6x. */
.year-photo { display: block; max-width: 48rem; margin: 0 0 1.25rem; }
.year-photo img { width: 100%; border-radius: var(--radius); background: var(--paper-alt); }
.year-bio { margin-bottom: 1.25rem; }
.year-empty { margin-bottom: 1.25rem; }
.roster-head { margin-top: 1.5rem; }
.team-year-alias { font-family: var(--font-body); font-weight: 400; font-size: 0.95rem; color: var(--ink-soft); }

/* The swap. 140ms out, 200ms in — short enough to read as one motion rather than a
 * transition to sit through. `is-loading` only shows if a fetch is genuinely slow. */
@media (prefers-reduced-motion: no-preference) {
  .year-panel { transition: opacity 140ms ease, transform 140ms ease; }
  .year-panel.slide-out-left  { opacity: 0; transform: translateX(-1.5rem); }
  .year-panel.slide-out-right { opacity: 0; transform: translateX(1.5rem); }
  .year-panel.slide-in-left   { animation: year-in-left 200ms ease both; }
  .year-panel.slide-in-right  { animation: year-in-right 200ms ease both; }
  @keyframes year-in-left  { from { opacity: 0; transform: translateX(1.5rem); } }
  @keyframes year-in-right { from { opacity: 0; transform: translateX(-1.5rem); } }
}
.team-season.is-loading .year-panel { opacity: 0.6; }

/* ---- where this program stands -------------------------------------------- */
.team-standing {
  margin: 0 0 1.25rem; padding: 0.7rem 1rem;
  border-left: 4px solid var(--line-strong);
  background: var(--paper-alt); border-radius: 0 var(--radius) var(--radius) 0;
}
.team-standing-now { border-left-color: var(--ok); }
.team-standing-archive { border-left-color: var(--soon); color: var(--ink-soft); }
.team-standing-archive strong { color: var(--heading); }

/* ---- a game as a card: a team's games, and the session schedule ---------
 * Three parts in a row on a wide screen (the game, who is in it, the result), and on a
 * phone the game and the result share the top line while the teams take the width
 * beneath, so nothing scrolls sideways (1.4.10). The left edge is the team's colour on a
 * themed page. */
.game-cards { list-style: none; margin: 0.25rem 0 1.25rem; padding: 0; display: grid; gap: 0.5rem; }
.game-card {
  display: grid; align-items: center; gap: 0.35rem 0.9rem;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "no main result";
  padding: 0.6rem 0.5rem 0.6rem 0.75rem;
  background: var(--paper-alt);
  border: 1px solid var(--line); border-left: 4px solid var(--accent-line);
  border-radius: var(--radius);
  font-size: 0.95rem;
}
.game-card-no { grid-area: no; min-width: 6.5rem; }
.game-card-main { grid-area: main; min-width: 0; display: grid; gap: 0.3rem; }
.game-card-result { grid-area: result; justify-self: end; white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (max-width: 34rem) {
  .game-card {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "no result" "main main";
  }
}

/* The game: its number (a link to it), what it decides, and when. */
.game-card-num {
  display: block; font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: 0.06em; font-weight: 700; color: var(--heading); text-decoration: none;
}
a.game-card-num:hover, a.game-card-num:focus-visible { text-decoration: underline; }
/* What the game decides — "Championship", "3rd/4th place". A word, not a colour. */
.game-card-label {
  display: block; white-space: normal;
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.05em;
  font-size: 0.7rem; font-weight: 700; color: var(--accent-line);
}
.game-card-meta { display: block; font-size: 0.82rem; color: var(--ink-soft); }

/* A team in it: the logo chip, the name (a link to the team), and on the schedule its
 * score. A slot nobody has filled keeps the chip's space, so names line up. */
.game-card-team { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
/* The gap takes the size class of the logo it stands in for, or the chip's 28px. */
.game-chip-gap { flex: 0 0 auto; width: var(--logo-size, 28px); height: var(--logo-size, 28px); }
.game-card-name { font-weight: 700; color: var(--heading); min-width: 0; overflow-wrap: anywhere; }
a.game-card-name { text-decoration: none; }
a.game-card-name:hover, a.game-card-name:focus-visible { text-decoration: underline; }
.game-card-team-score { margin-left: auto; padding-left: 0.75rem; font-weight: 700; color: var(--heading); }
/* On a decided game the winner's line keeps its weight and the loser's drops, and the
 * result box beside them says "Final" in words, so weight is never the only carrier. */
.game-card-main.is-decided .game-card-team:not(.is-winner) .game-card-name { font-weight: 400; color: var(--ink); }
.game-card-main.is-decided .game-card-team:not(.is-winner) .game-card-team-score { font-weight: 400; color: var(--ink-soft); }
/* A contest is on the floor between games and reads as its own kind of card. */
.game-card-contest { font-style: italic; color: var(--ink-soft); display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
/* A contest's sponsor beside its title, where a game shows its teams' logos (D-133). Wider
 * than a team's chip, because a sponsor's mark is a wordmark more often than a crest; it sits
 * on the sponsor's own ground (.logo-bg-*), the white card unless the logo needs dark. */
.sponsor-chip {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  height: 2.25rem; max-width: 6.5rem; padding: 3px 5px; box-sizing: border-box; border-radius: 4px;
}
.sponsor-chip img { display: block; max-height: 100%; max-width: 100%; width: auto; height: auto; }

/* The result is the way into the game: one obvious, 44px target rather than a bare link. */
.game-result-box {
  display: inline-flex; align-items: center; justify-content: flex-end; gap: 0.4rem;
  min-height: 44px; padding: 0.25rem 0.6rem; border-radius: var(--radius);
  color: var(--ink); text-decoration: none;
}
a.game-result-box { border: 1px solid var(--line-strong); background: var(--paper); }
a.game-result-box:hover { border-color: var(--heading); }
.game-card-score { font-weight: 700; color: var(--heading); }
.game-card-state { font-weight: 700; color: var(--ink-soft); text-align: right; }
.game-card-time { display: block; font-weight: 400; font-size: 0.82rem; }
.game-card-go { font-size: 1.25rem; line-height: 1; color: var(--ink-soft); }

/* The player page's game table still uses these two. */
.team-game-no { white-space: nowrap; font-weight: 700; }
.team-game-round { display: block; font-weight: 400; font-size: 0.85rem; color: var(--ink-soft); }
/* The word carries the outcome; the colour only reinforces it. */
.result-won { color: var(--ok); font-weight: 700; }
.result-lost { color: var(--ink-soft); font-weight: 700; }

/* THE ROSTER, a card per player: the number on a tile, the name, and the line under it.
 * The tile is the team's colour with its derived ink (D-28) on a themed page, and the
 * site's dark otherwise — the one surface here that wears the team. */
.roster {
  list-style: none; margin: 0 0 0.75rem; padding: 0;
  display: grid; gap: 0.5rem 0.75rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.roster-card {
  display: flex; align-items: center; gap: 0.75rem; min-width: 0;
  padding: 0.5rem 0.75rem 0.5rem 0.5rem;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
}
.roster-no {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 2.75rem; height: 2.75rem; border-radius: var(--radius);
  background: var(--dark); color: var(--dark-text);
  font-family: var(--font-display); font-weight: 700; font-size: 1.35rem; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.has-team .roster-no { background: var(--team); color: var(--team-ink); }
.roster-id { display: grid; gap: 0.1rem; min-width: 0; }
/* A linked name looks like every other link on the page, colour and underline (D-126): in
 * the heading's colour with no underline it read as bold text, not a way to the player. */
.roster-name { font-weight: 700; overflow-wrap: anywhere; }
.roster-meta { font-size: 0.85rem; color: var(--ink-soft); }

/* A linked team name in the field grid and the homepage takeover keeps the heading's
 * weight and colour: the whole card already reads as one object, so an underline only
 * on hover keeps the grid from looking like a list of links. */
.team-name a, .takeover-team {
  color: var(--heading); text-decoration: none;
}
.team-name a:hover, .team-name a:focus-visible,
.takeover-team:hover, .takeover-team:focus-visible { text-decoration: underline; }

/* The field grid's logo. Smaller than the team page's and lazy-loaded: there are 18 of
 * them on one page, over a gym connection, and none is above the fold on a phone. The
 * white pad is the same reasoning as the banner — dark logos need a ground. */
/* Only what the field grid adds to .logo-box: it floats beside the write-up. */
.team-card-logo { float: right; margin: 0 0 0.5rem 0.75rem; }


/* ---- players to watch ----------------------------------------------------- */
/* Above the write-up, as the printed program sets it. A bordered block rather than a
 * card: it belongs to the year panel around it, and another card edge inside a card
 * would read as a separate object (it is not one). */
.watch {
  border-left: 3px solid var(--accent-line);
  padding: 0.1rem 0 0.1rem 0.9rem;
  margin: 0 0 1.25rem;
}
.has-team .watch { border-left-color: var(--team); }
.watch-head {
  font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-soft); margin: 0 0 0.4rem;
}
.watch ul {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0.3rem 1.75rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
.watch li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 0.5rem; }
/* An unlinked pick stays the heading's colour; a linked one takes the link's, underline and all,
 * so the two differ by more than colour. */
.watch-name { font-weight: 700; }
span.watch-name { color: var(--heading); }
.watch-line { font-size: 0.9rem; color: var(--ink-soft); }

/* ---- player page ---------------------------------------------------------- */
/* ---- a player's page (D-127) ----------------------------------------------
 * THE TRADING CARD. The top in the player's team colour with its derived ink (D-28), or the
 * site's dark where the team has none; the vitals in a paper strip along the bottom, like the
 * info bar on the back of a basketball card. The stripe is the team's second colour. Links on
 * the colour take its ink, and their focus ring follows it, because the site's blue ring is
 * not guaranteed against an arbitrary team colour. */
.player-card {
  margin: 0 0 1rem; overflow: hidden;
  border-radius: calc(var(--radius) * 2);
  border: 3px solid var(--gold);
  box-shadow: var(--shadow);
}
.has-team .player-card { border-color: var(--team-stripe); }
.player-card-top {
  display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap;
  padding: 1.25rem 1.25rem 1.1rem;
  background: var(--dark); color: var(--dark-text);
}
.has-team .player-card-top { background: var(--team); color: var(--team-ink); }
.player-card-top a { color: inherit; }
.player-card-top a:focus-visible { outline-color: currentColor; }
.player-card-top .kicker { color: inherit; opacity: 0.85; margin-bottom: 0.15rem; }
.player-card-top h1 { color: inherit; margin: 0 0 0.25rem; }
/* A HEADSHOT: portrait, the photo filling the frame from the top down so a face is never
 * the part cropped away, and the team's logo on its corner. The badge always has a ground,
 * because a logo set to sit on nothing would sit on a face. The frame is sized by its width
 * alone; the 4:5 ratio gives the height. */
.headshot { position: relative; flex: 0 0 auto; display: block; line-height: 0; }
.headshot > img {
  display: block; width: var(--headshot-w); height: auto; aspect-ratio: 4 / 5;
  object-fit: cover; object-position: 50% 15%;
  border-radius: var(--radius); background: var(--paper-alt);
}
.headshot > .logo-box {
  position: absolute; right: -0.4rem; bottom: -0.4rem;
  box-shadow: 0 2px 6px rgba(var(--shadow-rgb), 0.35);
}
.headshot > .logo-bg-none { background: var(--logo-pad); }
.headshot-card { --headshot-w: 128px; }
.headshot-card > img { box-shadow: 0 4px 14px rgba(var(--shadow-rgb), 0.35); }
.headshot-season { --headshot-w: 64px; margin-right: 0.35rem; }
@media (max-width: 34rem) { .headshot-card { --headshot-w: 104px; } }
.player-card-id { flex: 1 1 14rem; min-width: 0; }
.player-card-team { margin: 0; font-weight: 700; }
.player-card-team span { font-weight: 400; }
.player-card-home { margin: 0.15rem 0 0; opacity: 0.85; }
.player-card-number {
  margin: 0 0 0 auto; align-self: flex-start;
  font-family: var(--font-display); font-weight: 700; line-height: 1;
  font-size: clamp(2.5rem, 8vw, 4rem); font-variant-numeric: tabular-nums;
}
.player-card-vitals {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr));
  margin: 0; background: var(--paper);
}
.player-card-vitals > div { padding: 0.6rem 1rem; border-left: 1px solid var(--line); }
.player-card-vitals > div:first-child { border-left: 0; }
.player-card-vitals dt {
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-soft);
}
.player-card-vitals dd {
  margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 1.15rem; color: var(--heading);
}
.player-aliases { font-size: 0.9rem; margin: 0 0 1.25rem; }

/* AWARDS. A record held outright is a dark tile with a gold star and tag, apart from the
 * team's colours so it reads as the tournament's honour, not the school's. Selections reuse
 * the team page's chips. Places on other lists are a plain ranked list. */
.player-awards { margin: 0 0 1.5rem; }
.player-awards-head {
  font-size: 0.8rem; font-family: var(--font-body); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-soft); margin: 0 0 0.5rem;
}
.record-badges { list-style: none; margin: 0 0 0.25rem; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.record-badge {
  display: grid; gap: 0.1rem; min-height: 44px; padding: 0.6rem 0.9rem;
  background: var(--dark); color: var(--dark-text); text-decoration: none;
  border-radius: var(--radius); border-left: 4px solid var(--gold);
}
.record-badge:hover .record-badge-name { text-decoration: underline; }
.record-badge:focus-visible { outline-color: var(--focus-on-dark); }
.record-badge-tag {
  font-family: var(--font-display); font-size: 0.75rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--gold);
}
.record-badge-name { font-weight: 700; }
.record-badge-value { font-size: 0.9rem; color: var(--dark-text-soft); }
.record-ranks { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.3rem; max-width: var(--measure); }
.record-ranks li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.6rem; }
.record-rank-place {
  min-width: 2.5rem; font-family: var(--font-display); font-weight: 700; color: var(--heading);
  font-variant-numeric: tabular-nums;
}
.record-rank-value { color: var(--ink-soft); font-size: 0.9rem; }

/* EACH YEAR A CARD: that year's logo by the year, the team's colour on the edge. */
.player-season {
  margin: 0 0 1.5rem; padding: 1rem 1.1rem;
  background: var(--paper); border: 1px solid var(--line);
  border-left: 4px solid var(--accent-line); border-radius: var(--radius);
}
.has-team .player-season { border-left-color: var(--team); }
.player-season-head { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; }
.player-season-head h2 { margin: 0; }
.player-season-team { font-family: var(--font-body); font-size: 1rem; font-weight: 400; text-transform: none; }
.player-season-detail { margin: 0.1rem 0 0; }
.player-season-awards { margin: 0 0 0.9rem; }
.honor-chip.is-record { background: var(--dark); border-color: var(--dark); color: var(--dark-text); }
.honor-chip.is-record .honor-chip-tag { color: var(--gold); }
a.honor-chip.is-record:hover { color: var(--dark-text); border-color: var(--gold); }

/* The game log. Tabular figures so the columns line up, which is most of what makes a
 * box score readable, and a fixed narrow width for the number columns so a dash and a
 * two-digit total occupy the same space. */
.box-log { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.box-log th, .box-log td {
  text-align: left; padding: 0.5rem 0.6rem 0.5rem 0;
  border-bottom: 1px solid var(--line);
}
.box-log thead th {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: 0.06em; font-size: 0.75rem; color: var(--ink-soft);
  border-bottom: 2px solid var(--line-strong);
}
.box-log .num { text-align: right; font-variant-numeric: tabular-nums; min-width: 3.2rem; }
.box-log tbody th { font-weight: 700; white-space: nowrap; }
/* A PLAYER'S GAME LOG (D-128): banded rows, the round under the game number, the opponent's
 * logo, the result as a pill that says the word and the score, points set larger than the rest,
 * and the year's best scoring game edged and marked "High" in words. Every colour is a checked
 * pair on paper and on the panel, so a banded row never weakens it. */
/* The edge goes on the row's first cell, not the row: box-shadow on a <tr> is not
 * painted everywhere, and the cell has the left padding the edge needs to sit in
 * without covering "Game 29". */
.game-log th:first-child, .game-log td:first-child { padding-left: 0.75rem; }
.game-log tbody tr.is-best > :first-child { box-shadow: inset 4px 0 0 var(--accent-line), var(--sticky-edge); }
.has-team .game-log tbody tr.is-best > :first-child { box-shadow: inset 4px 0 0 var(--team), var(--sticky-edge); }
.game-log td, .game-log th { vertical-align: middle; }
/* The opponent column starts clear of the pinned column's edge. Table cells have no left
 * padding, so a logo on a white card otherwise sits on that rule. */
.game-log td.game-log-opp-cell, .game-log thead th:nth-child(2) { padding-left: 0.75rem; }
.game-log-round { display: block; font-weight: 400; font-size: 0.8rem; color: var(--ink-soft); }
.game-log-opp { display: inline-flex; align-items: center; gap: 0.5rem; }
.result-pill {
  display: inline-block; padding: 0.15rem 0.6rem; white-space: nowrap;
  font-weight: 700; font-size: 0.85rem; font-variant-numeric: tabular-nums;
  background: var(--paper); border: 1px solid var(--line-strong); border-radius: 999px;
  color: var(--ink-soft);
}
.result-pill-won { color: var(--ok); border-color: var(--ok); }
.game-log-pts { font-family: var(--font-display); font-weight: 700; font-size: 1.1rem; color: var(--heading); white-space: nowrap; }
.game-log-high {
  font-family: var(--font-body); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--accent-ink);
  border: 1px solid var(--rule); border-radius: 0.2rem; padding: 0 0.25rem; vertical-align: middle;
}

/* ---- a game page --------------------------------------------------------- */
/* The scoreline is the page. Two teams with the score or the tip-off time between
 * them, and it has to stay readable at 360px where the two names are long. So: a
 * three-column grid on a wide screen, and on a phone the middle column moves to its
 * own row between the teams rather than squeezing to nothing. */
.game-head { margin-bottom: 1.5rem; }
.scoreline {
  display: grid; grid-template-columns: 1fr auto 1fr;
  gap: 0.75rem 1rem; align-items: center; margin: 0.75rem 0 0.5rem;
}
.scoreline-team { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 0.4rem; }

.scoreline-name { margin: 0; font-family: var(--font-display); font-size: 1.15rem; font-weight: 700; line-height: 1.15; }
.scoreline-side {
  margin: 0; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-soft);
}
/* "Won" in words, because colour and weight never carry meaning alone. */
.scoreline-result { color: var(--accent-ink); font-weight: 700; }
.scoreline-middle { text-align: center; }
.scoreline-score {
  margin: 0; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(2rem, 9vw, 3rem); line-height: 1; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.score-sep { color: var(--ink-soft); margin: 0 0.15em; }
/* The loser's number is dimmed, but only as reinforcement — "Won" already said it. */
.score-loser { color: var(--ink-soft); }
.scoreline-state, .scoreline-when {
  margin: 0.3rem 0 0; font-size: 0.78rem; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--ink-soft);
}
.scoreline-when { text-transform: none; letter-spacing: 0; font-size: 0.95rem; }
.game-meta { margin: 0; color: var(--ink-soft); font-size: 0.95rem; text-align: center; }
.game-stream { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin: 1.25rem 0; }
.game-section { margin: 2rem 0; }
.game-nobox { margin: 1.5rem 0; }

/* The two stat tables reuse the game log's typography — same tabular figures, same
 * narrow number columns, so a dash and a two-digit total occupy the same space. */
.period-table, .box-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.period-table th, .period-table td, .box-table th, .box-table td {
  text-align: left; padding: 0.45rem 0.6rem 0.45rem 0; border-bottom: 1px solid var(--line);
}
.period-table thead th, .box-table thead th {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-size: 0.72rem; color: var(--ink-soft); border-bottom: 2px solid var(--line-strong);
}
.period-table .num, .box-table .num {
  text-align: right; font-variant-numeric: tabular-nums; min-width: 2.8rem;
}
.period-table tbody th, .box-table tbody th { font-weight: 700; white-space: nowrap; }
.box-table tfoot th, .box-table tfoot td {
  border-top: 2px solid var(--line-strong); border-bottom: none; font-weight: 700;
}
.box-jersey {
  display: inline-block; min-width: 1.6rem; color: var(--ink-soft);
  font-variant-numeric: tabular-nums; font-weight: 400;
}
.box-starter { color: var(--ink-soft); }
/* The unattributed line (D-42): a stat with no player to credit. Set apart so it does
 * not read as a person, which is exactly what it is not. */
.box-team-line th, .box-team-line td { color: var(--ink-soft); font-style: italic; }
.box-team-line th { font-weight: 400; }
/* Points are the column a reader scans, so they are set like the game log's. */
.box-table .box-pts { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: var(--heading); }
.box-table .box-team-line .box-pts { color: var(--ink-soft); font-weight: 400; }

/* ---- the stat tables, together: box score, periods, a player's game log ----
 * Banded rows; a header row on the panel colour; figures that never wrap ("24-47" broke
 * in two at phone width); and the first column — the player, the team, the game —
 * pinned while the numbers scroll under it, so a reader on a phone never loses whose
 * line they are reading. A pinned cell needs its own ground or the figures show through
 * it, which is why the band is painted on the cells and not on the row. */
.box-table, .period-table, .game-log { --sticky-edge: inset -1px 0 0 var(--line); }
.box-table .num, .period-table .num, .game-log .num { white-space: nowrap; }
.box-table thead th, .period-table thead th, .game-log thead th { background: var(--paper-alt); }
.box-table tbody tr > *, .period-table tbody tr > *, .game-log tbody tr > * { background: var(--paper); }
.box-table tbody tr:nth-child(even) > *, .period-table tbody tr:nth-child(even) > *,
.game-log tbody tr:nth-child(even) > * { background: var(--paper-alt); }
.box-table tfoot > tr > * { background: var(--paper); }
.box-table tr > :first-child, .period-table tr > :first-child, .game-log tr > :first-child {
  position: sticky; left: 0; z-index: 1; box-shadow: var(--sticky-edge);
}
.box-table th, .box-table td, .period-table th, .period-table td { padding-right: 0.75rem; }
.box-table tr > :first-child, .period-table tr > :first-child { padding-left: 1rem; }
.period-table tbody th { font-weight: 700; }
.period-team { display: inline-flex; align-items: center; gap: 0.5rem; }

/* The card a stat table sits in on a game page. The scroller inside does the clipping,
 * so the card never cuts off a focus ring. */
.stat-card { background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); }
.stat-card .scroll-x { border-radius: 0 0 var(--radius) var(--radius); }
.scroll-x.stat-card { margin-top: 0.75rem; }

/* THE TEAM BAND over each box score: that team's colour and ink (D-28), or the site's dark
 * for a team with none. --team-ink is pure black or white and clears AA on --team, so the
 * band uses it at full strength and never at reduced opacity. */
.box-head {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  padding: 0.75rem 1rem; border-radius: var(--radius) var(--radius) 0 0;
  background: var(--dark); color: var(--dark-text); border-bottom: 4px solid var(--orange);
}
.box-head.is-themed { background: var(--team); color: var(--team-ink); border-bottom-color: var(--team-stripe); }
.box-head-id { flex: 1 1 12rem; min-width: 0; }
.box-head h2 { margin: 0; font-size: clamp(1.15rem, 4.5vw, 1.6rem); line-height: 1.1; color: inherit; }
.box-head a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
.box-head a:focus-visible { outline-color: var(--focus-on-dark); }
.box-head.is-themed a:focus-visible { outline-color: currentColor; }
.box-head-side {
  margin: 0.15rem 0 0; font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em;
}
.box-head-result { margin: 0 0 0 auto; display: flex; align-items: baseline; gap: 0.5rem; }
.box-head-word { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; }
.box-head-score {
  font-family: var(--font-display); font-weight: 700; line-height: 1;
  font-size: clamp(1.75rem, 7vw, 2.4rem); font-variant-numeric: tabular-nums;
}

.name-short { display: none; }
@media (max-width: 34rem) {
  /* On a phone a player is an initial and a last name, as a printed box score has it: a
   * full name took half the screen from the figures. */
  .name-full { display: none; }
  .name-short { display: inline; }
  /* A school's name has no short form to fall back on, so it wraps instead. */
  .period-table tbody th { white-space: normal; min-width: 8rem; }
  .period-table .num { min-width: 2.2rem; }
  .box-table tr > :first-child, .period-table tr > :first-child { padding-left: 0.75rem; }
  .box-head-id { flex-basis: 0; }
}

/* A PLAYER'S GAME LOG ON A PHONE (D-138): no sideways scroll and no pinned column. Each game
 * is a block: its number in a gutter on the left, the opponent and the result on the first
 * line, and the figures wrapping under them, each after its own small label. The round moves
 * under the opponent's name, where the gutter has no room for it. The header row stays in
 * the page for a screen reader and leaves the screen; the template's roles keep the rows
 * read as a table. The band and the year's-best edge move from the cells to the row. */
.game-log-label, .game-log-round-phone { display: none; }
@media (max-width: 34rem) {
  .game-log, .game-log tbody { display: block; }
  .game-log thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .game-log tbody tr {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.9rem;
    position: relative; padding: 0.6rem 0.6rem 0.6rem 3.5rem;
    background: var(--paper); border-bottom: 1px solid var(--line);
  }
  .game-log tbody tr:nth-child(even) { background: var(--paper-alt); }
  .game-log tbody tr > *, .game-log tbody tr:nth-child(even) > * {
    position: static; padding: 0; border: 0; background: none; box-shadow: none;
    min-width: 0; text-align: left;
  }
  .game-log tbody tr.is-best { box-shadow: inset 4px 0 0 var(--accent-line); }
  .has-team .game-log tbody tr.is-best { box-shadow: inset 4px 0 0 var(--team); }
  .game-log tbody tr.is-best > :first-child, .has-team .game-log tbody tr.is-best > :first-child { box-shadow: none; }
  .game-log tbody tr > th.game-log-no { position: absolute; left: 0.75rem; top: 0.95rem; padding: 0; }
  .game-log-round-wide { display: none; }
  .game-log-round-phone { display: block; }
  .game-log-opp-cell { flex: 1 1 0; }
  .game-log tbody td.game-log-opp-cell { padding-left: 0; }
  .game-log-opp { align-items: flex-start; }
  .game-log-result { flex: 0 0 auto; }
  /* Two breaks: the counting figures start a line after the result, and the shooting
   * figures one after them, so every game's numbers sit in the same places. */
  .game-log tbody tr::before, .game-log tbody tr::after { content: ""; flex-basis: 100%; height: 0; }
  .game-log tbody tr::before { order: 1; }
  .game-log tbody tr::after { order: 3; }
  .game-log tbody td.num { order: 2; min-width: 0; font-size: 0.95rem; }
  .game-log tbody td.num:nth-child(n+7) { order: 4; }
  .game-log tbody td.game-log-pts { display: inline-flex; align-items: baseline; gap: 0.3rem; font-size: 1.1rem; }
  .game-log-pts .game-log-high { order: 1; }
  .game-log-label {
    display: inline; margin-right: 0.3rem; font-family: var(--font-body); font-weight: 700;
    font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft);
  }
}

@media (max-width: 34rem) {
  .scoreline { grid-template-columns: 1fr 1fr; }
  /* The score drops to its own full-width row between the two teams. */
  .scoreline-middle { grid-column: 1 / -1; grid-row: 2; }
  .scoreline-team:last-of-type { grid-row: 1; grid-column: 2; }
  .scoreline-logo { --logo-size: 58px; --logo-inset: 6px; }
  .scoreline-name { font-size: 1rem; }
}

/* ---- the bracket ---------------------------------------------------------- */
/* A REAL BRACKET, laid out the way the printed one is: teams enter down the middle,
 * winners advance right toward the championship, losers go left into consolation and the
 * placement games. Every position arrives as grid coordinates in a nonce'd <style>: the
 * CSP blocks inline style attributes.
 *
 * SEMANTIC DOM, SPATIAL CSS. The markup is a list of columns, each a list of games, in
 * left-to-right order. Grid alone does the placing, so reading order is DOM order and a
 * screen reader walks the chart column by column. The elbows are aria-hidden: they
 * repeat what every game's accessible name already says. */

/* The one place on the site that scrolls sideways. Focusable so a keyboard user can
 * scroll it (2.1.1) and NAMED so a screen reader says what it is; it traps nothing,
 * because everything inside is a link in source order. */
.bracket-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 0.75rem;
  margin-bottom: 2.5rem;
}
/* BREAKS OUT OF THE READING COLUMN, but only where there is a reading column to break
 * out of. `.wrap` caps the page at 76rem; past that the window has room the chart can use.
 *
 * ONLY ABOVE 78rem. Below it the wrapper is already the full window, so the breakout buys
 * nothing, and `50vw` counts the scrollbar while the wrapper does not, which pushes the
 * whole page sideways on a phone. */
@media (min-width: 78rem) {
  .bracket-scroll-sheet {
    margin-left: calc(50% - 50vw + var(--gutter));
    margin-right: calc(50% - 50vw + var(--gutter));
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }
  /* The em that goes with this breakout is set BELOW, after the base `.bracket-grid`
   * rule — not here. See the comment on that block: at equal specificity the later rule
   * wins, and a media query buys none, so an override written here is dead. */
}
.bracket-scroll:focus { outline: none; }
.bracket-scroll:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }

/* Each chart FITS ITS CONTAINER. Columns are 1fr, so four columns share the width
 * rather than forcing a metre of sideways scroll — which is what one seven-column chart
 * did. The scroll container stays, because a narrow phone still cannot fit four. */
.bracket-chart-wrap { margin-bottom: 2.5rem; }
/* h1 and h2 are uppercase site-wide but h3 is not, and a corner's heading is an h3, so
 * this is spelled out rather than inherited and every chart heading reads the same. */
.bracket-chart-head { margin: 0 0 0.6rem; text-transform: uppercase; }
.bracket-grid {
  /* THE WHOLE SHEET SCALES. Seven columns of readable boxes is about 1,500px, the width of
   * the printed bracket, so instead of squeezing the columns and breaking school names
   * mid-word, the chart takes its own font size from its container and every dimension
   * below is in `em`. Between its floor and its ceiling it fits; below the floor it stops
   * shrinking and the container scrolls, because past that point it is not readable. */
  /* DIVIDED BY THE CONTAINER, NOT BY THE VIEWPORT. The sheet is 109.85em wide (six columns
   * at 14.25em, one centre at 14.75em and six gaps at 1.6em), so the em is at most the
   * container's width over that. The container is 100vw minus the wrapper's gutters, and
   * 100vw counts the scrollbar while the wrapper does not, so subtract both, and divide by
   * 111 rather than 109.85 so a rounded em never creeps over the edge into a sliver of
   * sideways scroll. Below 40rem the gutter is 1rem, not 1.5rem; the em is at its floor
   * there anyway. */
  /* THE FLOOR AND THE CEILING ARE CLOSE TOGETHER, AND BOTH ARE IN rem, which is what lets
   * browser zoom work on this chart. Zoom shrinks the viewport in CSS pixels, so a size that
   * tracks the window shrinks to cancel the zoom out; with two bounds a pixel apart, every
   * zoom step makes the chart bigger and the scroller takes the rest. rem, not px, so a
   * reader's default text size moves the chart too.
   *
   * THE FLOOR is set so a 1440px window still fits (the sheet is 1,371px in 1,408px of
   * scroller), which covers a 1080p laptop at 125% scaling. Narrower windows scroll rather
   * than shrink, because below this a team name is under 11px.
   *
   * THE CEILING is one step up: every pixel above it is a zoom step that does nothing. */
  --bracket-em-min: 0.78rem;
  --bracket-em-max: 0.875rem;
  font-size: clamp(var(--bracket-em-min), calc((min(100vw - 1rem, 76rem) - 3rem) / 111), var(--bracket-em-max));

  --bracket-track: 1.15em;    /* a game box is 8 tracks tall */
  --bracket-gap: 1.6em;       /* the gap columns, where the elbows are drawn */
  /* WIDE ENOUGH FOR THE NAME BUDGET. A slot is the column minus its padding, its logo
   * and its score: about 10.5em of text at 0.86em, roughly 18 characters, which is what a
   * team's short name is capped at. Narrower and a 17-character name wraps. */
  --bracket-centre: 14.75em;  /* the first round — the longest team names live here */
  --bracket-col: 14.25em;
  --bracket-line: 2px;

  display: grid;
  /* grid-template-columns and -rows are emitted per edition in the nonce'd <style>: how
   * many columns a chart has is a property of that year's bracket, not of this file. */
  list-style: none; margin: 0; padding: 0.4em 0 0;
  width: max-content;
}

/* THE SHEET'S EM ONCE IT HAS BROKEN OUT. THIS BLOCK MUST STAY BELOW THE BASE RULE
 * ABOVE: the two selectors have the same specificity and a media query adds none, so
 * placed above it this override loses at every width, and the sheet renders at the base
 * em inside a scroller the full window wide.
 *
 * Once the sheet is out of the reading column its container is the window, not the
 * 76rem wrapper, so the em grows against that: the base rule's arithmetic with the 76rem
 * cap removed. The corners scale with it. This is the only width where the chart fits
 * rather than scrolls; below 78rem the sheet sits at its floor. */
@media (min-width: 78rem) {
  .bracket-grid { font-size: clamp(var(--bracket-em-min), calc((100vw - 1rem - 3rem) / 111), var(--bracket-em-max)); }
  /* Past the ceiling the window outgrows the sheet, so it is centred on the page rather
   * than left against the window's edge. Auto margins resolve to zero once the sheet
   * overflows, so this never costs a pixel of scroll. The corners follow it. */
  .bracket-scroll-sheet .bracket-grid { margin-inline: auto; }
  .bracket-corners { justify-content: center; }
}
.bracket-game { min-width: 0; }
.bracket-box {
  display: grid; gap: 0.15em;
  /* SPACE-BETWEEN, NOT START, and that is geometry rather than taste. The box is sized
   * for its worst case (8 tracks), so a box whose names do not wrap has slack. Packed to
   * the start, the slack all falls below the away row, the middle line sits above the
   * box's centre while every branch arm is drawn at it, and the two slots sit at
   * different distances from it. Space-between anchors home to the top and away to the
   * bottom, and centres the middle line exactly where the arm points. */
  align-content: space-between;
  /* One track short of its slot, so stacked boxes always have a track of air between
   * them and an elbow's arm never grazes the box beneath the one it points at. */
  height: calc(100% - var(--bracket-track)); min-width: 0; overflow: hidden;
  padding: 0.4em 0.55em;
  background: var(--paper-alt);
  border: 1px solid var(--line);
  /* The one strong edge, so a box reads as a unit at a glance and in greyscale. */
  border-left: 4px solid var(--accent-line);
  border-radius: var(--radius);
  color: var(--ink); text-decoration: none;
}
.bracket-box:hover { border-color: var(--line-strong); }
.bracket-box:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* THE MIDDLE LINE — where the printed sheet writes "(16) 4:00 P.M. DEC. 29" between the
 * two slots. Number, trophy, time and state on one ruled line; it is what separates the
 * top team from the bottom one. */
/* FIXED AT TWO LINES — "Game N | Award" over "Date, time … Status" — so every box in a
 * column is the same height whether or not that game carries an award. 0.8em is the
 * smallest it goes: the game's state is on this line, and at 0.7em it was under 9px at
 * every laptop width. */
.bracket-mid {
  display: grid; gap: 0.1em;
  padding: 0.15em 0; margin: 0.05em 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-size: 0.8em; color: var(--ink-soft); line-height: 1.3;
}
.bracket-mid-row {
  display: flex; align-items: baseline; gap: 0.15em 0.5em; min-width: 0;
}
.bracket-no {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: 0.06em; font-weight: 700; color: var(--ink-soft);
  flex: 0 0 auto;
}
/* What the game decides. Marked by weight and a rule, not by colour alone — and it is
 * words, so it survives greyscale and a screen reader reads it as part of the link's
 * name rather than as decoration. */
.bracket-award {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--heading); font-weight: 700;
  padding-left: 0.5em; border-left: 2px solid var(--accent-line);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Date and time, left; the state is pushed to the far right of the same line, like the
 * printed sheet's "(16) 4:00 P.M. DEC. 29" against its result. */
.bracket-when {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The state is the one thing on the line that is never quiet. */
.bracket-state { font-weight: 700; color: var(--ink); flex: 0 0 auto; margin-left: auto; }
.bracket-tba { font-style: italic; }
.bracket-side {
  display: flex; align-items: center; gap: 0.35em; font-size: 0.86em;
  min-width: 0; padding: 0.05em 0;
}
.bracket-side .logo-box { --logo-size: 1.8em; --logo-inset: 0.2em; border-radius: 3px; }
/* A school with no logo still lines its name up with the ones that have one. */
.bracket-chip-gap { flex: 0 0 auto; width: 1.8em; }
.bracket-team {
  flex: 1 1 auto; min-width: 0;
  /* ONE LINE, STRUCTURALLY. A team's short name is capped so it fits the column, but the
   * cap is a character count and the column is ems, so a name of unusually wide letters
   * can still overrun, and a wrapped name grows the box past its slot and pushes the
   * tip-off time out of the bottom. Ellipsis is the better failure, and the full name is
   * one click away. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.15;
}
.bracket-score { font-variant-numeric: tabular-nums; font-weight: 700; }

/* WIN IS NEVER COLOUR ALONE. The winner's row is bold AND carries the word "Won": the
 * weight is the quick read, the word is the one that survives greyscale, a colour-blind
 * reader and a screen reader landing mid-box. */
.bracket-side.is-winner .bracket-team,
.bracket-side.is-winner .bracket-score { font-weight: 700; color: var(--heading); }
.bracket-won {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: 0.06em; font-size: 0.78em; color: var(--ok);
}
/* A slot still reading "Winner of Game 24" is not a team yet, and says so in words
 * rather than by being greyer. The italic is reinforcement, not the message. */
.bracket-side.is-pending .bracket-team { color: var(--ink-soft); font-style: italic; }

/* ---- the branches --------------------------------------------------------- */
/* ONE LINE PER FEEDER, never a merged pair converging on a box's centre. A game with two
 * feeders gets two `.bracket-link` elements — the printed sheet routes each feeder to
 * where its team actually lands: game 5 into the TOP of every game it feeds, game 7 into
 * the BOTTOM of the same games, never both meeting at one centre point. Two independent
 * lines is also what keeps them apart: they bend at different heights instead of both
 * reaching for the same one.
 *
 * Each line is an arm (at the FEEDER's own row), a spine, and a stub (into the target's
 * home or away slot — `bracket-link-slot-top`/`-bottom`, matching which one it actually
 * is). The bend sits at a QUARTER of the gap, close to the feeder, not the half a
 * midpoint-converging line would use: every game in a column is a feeder for something,
 * so the gap is full of these end to end, and bending early keeps each line's spine
 * inside the quarter nearest its own feeder — a zone no OTHER line's spine ever reaches
 * into. Only the long stub crosses into the far three-quarters, and only to land on the
 * box it's actually feeding, which is the one place a touch is correct rather than a
 * crossing. */
.bracket-link { position: relative; pointer-events: none; --slot-off: 0; }
.bracket-link::before,
.bracket-link::after { content: ''; position: absolute; }
.bracket-link::before {
  border: var(--bracket-line) solid var(--line-strong);
}
.bracket-link::after {
  border-top: var(--bracket-line) solid var(--line-strong);
}
/* HOW FAR A SLOT SITS FROM THE BOX'S CENTRE, in tracks. Derived, not tuned: with the
 * rows spaced apart (see .bracket-box) a team row is 1.9em tall at 0.86em — 1.63em —
 * so its centre is 0.4em of padding plus half of that from the box's edge, and the
 * box's own centre is 3.5 tracks in. 3.5 − (0.4 + 0.82) / 1.15 = 2.44. */
.bracket-link-slot-top { --slot-off: 2.44; }
.bracket-link-slot-bottom { --slot-off: -2.44; }

.bracket-link-straight::before {
  left: 0; right: 0; bottom: auto; border: 0;
  border-top: var(--bracket-line) solid var(--line-strong);
  /* 3.5 TRACKS, NOT 50%. The grid area is the box's full 8 tracks but the box itself is
   * 7 of them (one track of air below it), so its centre is 3.5 tracks down — half the
   * area is half a track too low. The slot offset then moves that up toward the home
   * row or down toward the away row. */
  top: calc(var(--bracket-track) * (3.5 - var(--slot-off)));
}
.bracket-link-straight::after { display: none; }

/* THE CHART IS MIRRORED, so `right` (winners' side: feeder left of the gap, target
 * right) and `left` (the sixth-place wing: feeder right of the gap, target left) are
 * mirror images of each other, not just relabelled. */
.bracket-link-right.bracket-link-down::before {
  /* The arm stays at the FEEDER's own centre (3.5 tracks — fixed, whatever the slot);
   * only the spine's far end and the stub below move with the slot. */
  left: 0; right: 75%; border-left: 0; border-bottom: 0;
  top: calc(var(--bracket-track) * 3.5);
  bottom: calc(var(--bracket-track) * (4.5 + var(--slot-off)));
}
.bracket-link-right.bracket-link-down::after {
  left: 25%; right: 0; top: auto;
  bottom: calc(var(--bracket-track) * (4.5 + var(--slot-off)));
}
.bracket-link-right.bracket-link-up::before {
  left: 0; right: 75%; border-left: 0; border-top: 0;
  /* 4.5, not 3.5: on an `up` line the FEEDER is the box at the BOTTOM of the grid area,
   * and a box is 7 tracks of the area's 8, so its centre is 4.5 tracks up from the
   * bottom edge. At 3.5 the arm left the feeder a full track below its own centre —
   * the visible symptom was every upward line starting off-box. */
  bottom: calc(var(--bracket-track) * 4.5);
  top: calc(var(--bracket-track) * (3.5 - var(--slot-off)));
}
.bracket-link-right.bracket-link-up::after {
  left: 25%; right: 0;
  top: calc(var(--bracket-track) * (3.5 - var(--slot-off)));
}
.bracket-link-left.bracket-link-down::before {
  right: 0; left: 75%; border-right: 0; border-bottom: 0;
  top: calc(var(--bracket-track) * 3.5);
  bottom: calc(var(--bracket-track) * (4.5 + var(--slot-off)));
}
.bracket-link-left.bracket-link-down::after {
  right: 25%; left: 0; top: auto;
  bottom: calc(var(--bracket-track) * (4.5 + var(--slot-off)));
}
.bracket-link-left.bracket-link-up::before {
  right: 0; left: 75%; border-right: 0; border-top: 0;
  /* 4.5, not 3.5: on an `up` line the FEEDER is the box at the BOTTOM of the grid area,
   * and a box is 7 tracks of the area's 8, so its centre is 4.5 tracks up from the
   * bottom edge. At 3.5 the arm left the feeder a full track below its own centre —
   * the visible symptom was every upward line starting off-box. */
  bottom: calc(var(--bracket-track) * 4.5);
  top: calc(var(--bracket-track) * (3.5 - var(--slot-off)));
}
.bracket-link-left.bracket-link-up::after {
  right: 25%; left: 0;
  top: calc(var(--bracket-track) * (3.5 - var(--slot-off)));
}

/* THE STRAIGHT PART OF A LINE ACROSS AN EMPTY COLUMN: level with the feeder's centre, the
 * full width from the feeder to the gap where the line bends, with no arrowhead of its own.
 * It meets its elbow's arm exactly, so it is drawn by the arm's own rule: on a `down` line a
 * top border 3.5 tracks from the top, on an `up` line a bottom border 4.5 tracks from the
 * bottom. The same value from the other edge rounds to a different pixel, and the join steps. */
.bracket-link.bracket-link-run::before {
  left: 0; right: 0; bottom: auto; border: 0;
  border-top: var(--bracket-line) solid var(--line-strong);
  top: calc(var(--bracket-track) * 3.5);
}
.bracket-link.bracket-link-run.bracket-link-up::before {
  top: auto; border-top: 0;
  border-bottom: var(--bracket-line) solid var(--line-strong);
  bottom: calc(var(--bracket-track) * 4.5);
}
.bracket-link.bracket-link-run::after,
.bracket-link-run .bracket-link-arrow { display: none; }

/* THE ARROWHEAD. A real child element, not a third pseudo-element (::before and ::after
 * are both spoken for): a small triangle where the stub above already reaches, pointing
 * the way the line flows. It is what tells a play-in's line (its winner walks INTO the
 * main row) from a lower bracket's line sharing the same gap (a loser moving OUT).
 *
 * Each rule's vertical position is the SAME formula as that shape's ::after above, then
 * `+ half the line's thickness − half the arrowhead's height`: the line is a border
 * hanging below the coordinate. Change the two together.
 *
 * Sized in em, like every other dimension on the sheet. */
.bracket-link-arrow {
  --arrow-half: 0.32em;       /* half its height; the tip is this far from either edge */
  --arrow-nudge: calc(var(--bracket-line) / 2 - var(--arrow-half));
  position: absolute;
  width: 0; height: 0;
  border-top: var(--arrow-half) solid transparent;
  border-bottom: var(--arrow-half) solid transparent;
}
.bracket-link-right .bracket-link-arrow { right: 0; border-left: 0.5em solid var(--line-strong); }
.bracket-link-left .bracket-link-arrow { left: 0; border-right: 0.5em solid var(--line-strong); }

.bracket-link-straight .bracket-link-arrow {
  top: calc(var(--bracket-track) * (3.5 - var(--slot-off)) + var(--arrow-nudge));
}
.bracket-link-down .bracket-link-arrow {
  top: auto;
  bottom: calc(var(--bracket-track) * (4.5 + var(--slot-off)) + var(--arrow-nudge));
}
.bracket-link-up .bracket-link-arrow {
  top: calc(var(--bracket-track) * (3.5 - var(--slot-off)) + var(--arrow-nudge));
}

/* ---- the corner brackets -------------------------------------------------- */
/* The small brackets the printed sheet tucks into its margins — one per round whose
 * losers keep playing — drawn beneath the main sheet, side by side where they fit. Same
 * scale as the sheet so a box is a box wherever it appears. */
.bracket-corners {
  display: flex; flex-wrap: wrap; gap: 1.5rem 3rem;
  align-items: flex-start;
  margin-bottom: 2.5rem;
}
/* THE CORNERS BREAK OUT WITH THE SHEET. Above 78rem the sheet's em, which the corners
 * share, grows with the window, so left in the 76rem column the corners outgrow it and
 * one drops onto a row of its own. Same margins as `.bracket-scroll-sheet`, so they line
 * up under its left edge. */
@media (min-width: 78rem) {
  .bracket-corners {
    margin-left: calc(50% - 50vw + var(--gutter));
    margin-right: calc(50% - 50vw + var(--gutter));
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }
}
/* margin-top: 0 because the site's `section + section` spacing would push every corner
 * after the first down. Side by side, they share a top edge. */
.bracket-chart-corner { margin-top: 0; margin-bottom: 0; flex: 0 1 auto; max-width: 100%; }
.bracket-chart-corner .bracket-chart-head { font-size: 1.15rem; }
.bracket-chart-corner .bracket-scroll { margin-bottom: 0; }

/* ---- the horizontal scroller every wide table sits in ------------------- */
/* Wide tables (box scores, the period table, game logs) scroll sideways inside this
 * rather than overrunning a narrow screen (1.4.10). Mirrors .bracket-scroll. */
.scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* A scrollable region has to be reachable by keyboard (2.1.1), so each wrapper that can
 * scroll carries tabindex="0" and is named by its heading. */
.scroll-x[tabindex] { padding-bottom: 0.4rem; }

/* ---- the session schedule, under the bracket ---------------------------- */
/* The schedule reflows instead: the chart has to scroll sideways on a phone, this never
 * does. */
.schedule { margin-top: 1rem; }
.schedule-head { margin-bottom: 0.25rem; }
.schedule-session { margin-bottom: 2rem; }
.schedule-session-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem;
  margin-bottom: 0.5rem; padding-bottom: 0.3rem;
  border-bottom: 2px solid var(--line);
}
.schedule-session-name { font-family: var(--font-body); font-weight: 400; color: var(--ink-soft); }
/* The day is the thing people actually navigate by, so it is not dimmed into furniture. */
.schedule-day {
  font-family: var(--font-body); font-size: 0.85rem; font-weight: 400;
  color: var(--ink-soft); margin-left: auto;
}

.schedule-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.schedule-table th, .schedule-table td {
  text-align: left; padding: 0.5rem 0.75rem 0.5rem 0;
  border-bottom: 1px solid var(--line); vertical-align: baseline;
}
.schedule-table thead th {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-size: 0.72rem; color: var(--ink-soft); border-bottom: 2px solid var(--line-strong);
}
.schedule-no { white-space: nowrap; font-weight: 700; }
/* What the game decides, under its number. Not colour-coded and not a pill: it is a
 * label, and has to read as one in greyscale too. */
.schedule-award {
  display: block; white-space: normal;
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.05em;
  font-size: 0.7rem; font-weight: 700; color: var(--accent-line);
}
/* "vs" is furniture between two names, not a word being read for meaning. */
.schedule-vs { color: var(--ink-soft); padding: 0 0.15rem; }

/* The bracket on paper. The scroll container is not one on paper, so the whole grid lays
 * out rather than being clipped to the screen's width. */
@media print {
  .bracket-scroll { overflow: visible; padding: 0; }
  .bracket-rounds { width: auto; gap: 0.4rem; }
  .bracket-round { min-width: 0; max-width: none; }
  .bracket-box { break-inside: avoid; }
  .year-strip { display: none; }
  /* The schedule prints whole: nothing clipped and no row split across a page. */
  .scroll-x { overflow: visible; }
  .schedule-session { break-inside: avoid; }
  .schedule-table tr, .game-card { break-inside: avoid; }
}

/* ---- the press page ------------------------------------------------------- */
.press-days ul { list-style: none; padding: 0; margin: 0 0 1.5rem; display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; }
.press-days a { display: inline-flex; align-items: center; min-height: 44px; }
.press-days a[aria-current="page"] { font-weight: 700; color: var(--heading); text-decoration: none; border-bottom: 3px solid var(--accent-line); }
.press-results, .press-schedule { list-style: none; padding: 0; margin: 0 0 1.5rem; max-width: var(--measure); }
.press-results li { padding: 0.5rem 0; border-bottom: 1px solid var(--line); }
.press-line, .press-scorers { margin: 0; }
.press-scorers { color: var(--ink-soft); font-size: 0.95rem; }
.press-box-link { display: inline-block; margin-left: 0.5rem; padding: 0.6rem 0; white-space: nowrap; }
.press-schedule li { padding: 0.3rem 0; }
.press-time { display: inline-block; min-width: 6.5rem; font-variant-numeric: tabular-nums; font-weight: 700; }
.press-text {
  display: block; width: 100%; max-width: var(--measure); box-sizing: border-box;
  padding: 0.75rem; font: inherit; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem; color: var(--ink); background: var(--paper-alt);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}

/* ---- where a team plays next ----------------------------------------------
 * A card under the team's banner: the next game, then where a win and a loss each lead.
 * Wins and losses are the words "Win" and "Lose", never colour. */
.team-next {
  margin: 1rem 0 1.5rem; padding: 1rem 1.25rem; max-width: var(--measure);
  background: var(--paper-alt); border: 1px solid var(--line);
  border-left: 5px solid var(--accent-line); border-radius: var(--radius);
}
.team-next h2 { margin: 0 0 0.35rem; font-size: 1.2rem; }
.team-next-game { margin: 0; font-size: 1.15rem; }
.team-next-when { margin: 0.15rem 0 0.75rem; color: var(--ink-soft); }
.team-next-paths { margin: 0; display: grid; gap: 0.35rem; }
.team-next-paths div { display: grid; grid-template-columns: 3.5rem 1fr; gap: 0.5rem; align-items: baseline; }
.team-next-paths dt {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; color: var(--heading);
}
.team-next-paths dd { margin: 0; }
/* ---- add to a calendar ----------------------------------------------------
 * Four equal buttons: one row where they fit, two by two on a phone, never one long
 * button and three short ones. The display face is a size down from .btn's because
 * "Google Calendar" is the longest label on any button on the site. */
.calendar-links { margin: 1rem 0 0; }
.calendar-links-lede { font-weight: 700; color: var(--heading); margin: 0 0 0.5rem; }
.calendar-buttons {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0.5rem; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  max-width: 48rem;
}
.calendar-buttons .btn { width: 100%; box-sizing: border-box; padding-inline: 0.75rem; font-size: 0.95rem; }
.calendar-other { margin-top: 0.5rem; }
.calendar-other > summary {
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--link); text-decoration: underline; cursor: pointer;
}
.calendar-links-note { margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--ink-soft); max-width: var(--measure); }
/* On a team page it is a small box after the season, not part of "Next up": the box keeps it
 * apart from the game, and the buttons are a step smaller than the schedule page's. */
.team-calendar { max-width: 40rem; margin: 0 0 2.5rem; padding: 1rem 1.1rem; }
.team-calendar .calendar-links { margin: 0; }
.team-calendar .calendar-buttons { grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); }
.team-calendar .calendar-buttons .btn { font-size: 0.9rem; }

/* ---- the bracket challenge (D-102) ----------------------------------------
 * Each game is a fieldset of two radio buttons, and each choice is a whole-row label at
 * least 48px tall: the target is the team's name, not a 16px circle. Right and wrong are
 * words in the tables, never colours. */
.challenge-intro p { max-width: var(--measure); }
.challenge-count, .challenge-status { font-weight: 700; color: var(--heading); }
.challenge-names { list-style: none; padding: 0; margin: 0 0 1.5rem; display: flex; flex-wrap: wrap; gap: 0.25rem 1.25rem; }
.challenge-names a { display: inline-flex; align-items: center; min-height: 44px; }
.challenge-table caption { text-align: left; font-weight: 700; color: var(--heading); padding-bottom: 0.5rem; }
.challenge-table .num { text-align: right; font-variant-numeric: tabular-nums; padding-right: 1rem; }
/* A PERFECT BRACKET'S ROW stands out: its own ground and a bar in the accent colour down
 * its edge. The word "Perfect" stays in the tag beside the name, so the row's colour is
 * never the only thing saying it. */
.challenge-table tr.is-perfect th, .challenge-table tr.is-perfect td { background: var(--paper-alt); }
.challenge-table tr.is-perfect td:first-child { box-shadow: inset 4px 0 0 var(--accent-line); padding-left: 0.6rem; }
.challenge-perfect-count {
  display: inline-block; margin-left: 0.35rem; padding: 0.1rem 0.6rem;
  border: 2px solid var(--accent-line); border-radius: 999px; color: var(--heading);
}
/* "Perfect" is a word in a tag, beside the name — never only a colour. */
.challenge-perfect {
  display: inline-block; margin-left: 0.4rem; padding: 0.05rem 0.45rem;
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em;
  font-size: 0.72rem; font-weight: 700; vertical-align: middle;
  color: var(--brand-ink); background: var(--brand); border-radius: 999px;
}
/* Find a name above the table; how many to a page below it, beside Previous and Next. */
.challenge-find { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem 1rem; margin: 1rem 0; }
.challenge-find label { display: block; font-weight: 700; margin-bottom: 0.25rem; }
.challenge-per { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; margin-left: auto; }
.challenge-per label { font-weight: 700; }
.challenge-find input, .challenge-per select {
  min-height: 48px; box-sizing: border-box; padding: 0.4rem 0.6rem; font: inherit;
  color: var(--ink); background: var(--paper-alt);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.challenge-find input { width: 16rem; max-width: 100%; }
.challenge-found { font-weight: 700; color: var(--heading); }
.challenge-place { font-size: 1.15rem; color: var(--heading); }
/* This device's brackets: each name a link at full touch-target height. */
.challenge-yours h2 { margin-top: 0; font-size: 1.2rem; }
.challenge-yours ul { margin: 0 0 0.25rem; padding-left: 1.25rem; }
.challenge-yours li a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 700; }
.challenge-yours .challenge-hint { margin: 0; }
.challenge-forget p { margin-top: 0; }
.challenge-pager { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.5rem; margin-top: 1rem; }
.challenge-pager-links { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.challenge-errors {
  border: 2px solid var(--brand); border-radius: var(--radius); padding: 1rem 1.25rem;
  margin-bottom: 1.5rem; max-width: var(--measure); background: var(--paper-alt);
}
.challenge-errors h2 { margin-top: 0; font-size: 1.2rem; }
.challenge-errors ul { margin-bottom: 0; }
.challenge-form { max-width: var(--measure); }
.challenge-game {
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  padding: 0.75rem 1rem 0.5rem; margin: 0 0 1rem;
}
.challenge-game legend { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700; color: var(--heading); padding: 0 0.35rem; }
.challenge-feed { margin: 0 0 0.35rem; font-size: 0.85rem; color: var(--ink-soft); }
.challenge-choice {
  display: flex; align-items: center; gap: 0.75rem; min-height: 48px;
  padding: 0.25rem 0.5rem; border-radius: var(--radius); cursor: pointer;
}
.challenge-choice:hover { background: var(--paper-alt); }
.challenge-choice input { width: 1.25rem; height: 1.25rem; margin: 0; flex: none; accent-color: var(--brand); }
.challenge-choice span { font-weight: 600; }
.challenge-buttons { display: flex; flex-wrap: wrap; gap: 0.75rem; margin: 1.5rem 0; }
.challenge-field { margin: 0 0 1.25rem; }
.challenge-field label { display: block; font-weight: 700; margin-bottom: 0.25rem; }
.challenge-hint { margin: 0 0 0.4rem; font-size: 0.9rem; color: var(--ink-soft); }
.challenge-field input[type="text"] {
  width: 100%; max-width: 24rem; box-sizing: border-box; min-height: 48px;
  padding: 0.4rem 0.6rem; font: inherit;
  color: var(--ink); background: var(--paper-alt);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.challenge-field input#tiebreak { max-width: 8rem; }
.challenge-meta { margin-bottom: 1.25rem; }
.challenge-share { display: inline-block; margin-right: 0.75rem; white-space: nowrap; }
/* The private link, shown so it can be saved. Read-only, selectable, and as wide as the
 * column so the whole address can be seen without scrolling the field. */
.challenge-link, .calendar-address { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin: 0.75rem 0; }
.challenge-link input, .calendar-address input {
  flex: 1 1 22rem; min-width: 0; min-height: 48px; box-sizing: border-box;
  padding: 0.4rem 0.6rem; font: inherit; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem; color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}

/* THE INVITATION, on the bracket page while picks are open (and the leaderboard after).
 * A dark band like the hero rather than a paper callout: it is the one thing on that page
 * asking you to do something, and it runs the full width of the column so the sentence
 * does not wrap at reading width. The button is a real link, styled by `.on-dark`. */
.challenge-banner {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 1rem 2rem;
  margin: 0 0 2rem; padding: 1.25rem 1.5rem;
  background: linear-gradient(120deg, var(--dark) 0%, var(--dark-3) 100%);
  border-left: 6px solid var(--orange); border-radius: var(--radius);
  color: var(--dark-text); box-shadow: var(--shadow);
}
.challenge-banner-text { flex: 1 1 28rem; min-width: 0; }
.challenge-banner-kicker {
  margin: 0 0 0.2rem; font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: 0.08em; font-size: 0.85rem; color: var(--dark-text-soft);
}
.challenge-banner h2 { margin: 0 0 0.25rem; color: var(--gold); font-size: clamp(1.4rem, 3vw, 1.9rem); }
.challenge-banner p:last-child { margin: 0; color: var(--dark-text); }
.challenge-banner .btn { flex: 0 0 auto; }

/* Two ways into the same entry. Game by game is the filled button on a phone — the drawn
 * bracket is a metre of chart there — and the bracket is the filled one on a wide screen.
 * Only the fill swaps; the DOM order (and so the tab order) stays the same everywhere. */
.challenge-ways { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.challenge-way-list { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.challenge-way-list:hover { background: var(--brand-deep); border-color: var(--brand-deep); color: var(--brand-ink); }
@media (min-width: 60rem) {
  .challenge-way-list { background: var(--paper); border-color: var(--btn-outline); color: var(--btn-outline); }
  .challenge-way-list:hover { background: var(--paper-alt); color: var(--heading); }
  .challenge-way-bracket { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
  .challenge-way-bracket:hover { background: var(--brand-deep); border-color: var(--brand-deep); color: var(--brand-ink); }
}
/* A submit button that reads as a link: switching views is navigation, not the main act. */
.challenge-linkish {
  background: none; border: 0; padding: 0.5rem 0; min-height: 44px; cursor: pointer;
  font: inherit; color: var(--link); text-decoration: underline;
}
.challenge-lead { max-width: var(--measure); }
.challenge-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 1.5rem; }
.challenge-tools a { display: inline-flex; align-items: center; min-height: 44px; }

/* THE BRACKET AS A FORM. Each box is a fieldset wearing .bracket-box, so it sits in the
 * chart exactly as the bracket page's links do; these only undo a fieldset's defaults. */
/* position: relative is the containing block for the box's visually-hidden legend. Without
 * it the 1px legend is positioned against the PAGE, escapes the chart's sideways scroller,
 * and stretched a phone's page to the width of the whole sheet. */
.challenge-box { margin: 0; min-inline-size: 0; position: relative; }
.challenge-pick { cursor: pointer; border-radius: 3px; }
.challenge-pick:hover { background: var(--paper); }
.challenge-pick input { width: 1em; height: 1em; margin: 0; flex: none; accent-color: var(--brand); }
/* The pick is the checked button first; bold is the quick read, never the only one. */
.challenge-pick:has(input:checked) .bracket-team { font-weight: 700; color: var(--heading); }
/* THE GAMES STILL TO PICK STAND OUT; the picked and the waiting step back. Done with the
 * border and the ground, never opacity — a faded box would take its text under AA — and
 * never colour alone: the middle line says "Pick a winner", "Picked" or "Waiting" in words. */
.challenge-box.is-to-pick {
  background: var(--paper);
  border: 2px solid var(--accent-line); border-left-width: 6px;
  box-shadow: var(--shadow);
}
.challenge-box.is-to-pick .challenge-box-state { color: var(--accent-ink); }
.challenge-box.is-picked, .challenge-box.is-waiting { border-left-color: var(--line-strong); }
.challenge-box.is-waiting { border-style: dashed; border-left-style: solid; }
.challenge-box-state { font-weight: 700; color: var(--ink-soft); }
/* With the script running the bracket fills as you pick, so the no-script button goes. */
.challenge-bracket-form.is-live .challenge-fill { display: none; }

/* ---- the admin ----------------------------------------------------------- */
/* One column at phone width, and nothing here needs a script. Not held to WCAG (D-74),
 * but controls stay 44px for a thumb. Past phone width it uses the whole window: two
 * columns of fieldsets, and tables of inputs that need the room. */

body.admin { background: var(--paper); }
body.admin .wrap { max-width: 110rem; }

.admin-header-inner {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.25rem;
  /* padding-block, not the shorthand: `padding: 0.75rem 0` would zero the side gutter
   * `.wrap` provides. Same for .admin-main below. */
  padding-block: 0.75rem;
}
.admin-brand { margin: 0; font-weight: 700; }
.admin-brand a { color: var(--dark-text); text-decoration: none; }
.admin-nav {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; align-items: center;
}
.admin-nav a {
  color: var(--dark-text-soft); text-decoration: none;
  display: inline-flex; align-items: center; min-height: 44px;
}
.admin-nav a:hover { color: var(--gold); }
.admin-nav a[aria-current="page"] {
  color: var(--dark-text); font-weight: 700;
  box-shadow: inset 0 -3px 0 var(--gold);
}
.admin-logout button {
  background: none; border: none; color: var(--dark-text-soft);
  font: inherit; cursor: pointer; min-height: 44px; padding: 0;
  text-decoration: underline;
}
.admin-logout button:hover { color: var(--gold); }

.admin-main { padding-block: 1rem 3rem; }
.admin-main h1 { color: var(--heading); }

.admin-notice, .admin-error {
  padding: 0.6rem 0.9rem; border-radius: var(--radius); font-weight: 600;
}
/* Ink text with a coloured edge: the words carry the meaning, and --ink on --paper-alt
 * is a checked pair in both schemes. */
.admin-notice { background: var(--paper-alt); border-left: 4px solid var(--ok); color: var(--ink); }
.admin-error { background: var(--paper-alt); border-left: 4px solid var(--accent-line); color: var(--ink); }

/* One column. Labels above inputs, hints below — the reading order a screen reader
 * and a narrow phone both want. */
.admin-form { max-width: 44rem; }
.admin-form fieldset {
  min-width: 0;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  margin: 0 0 1.25rem; padding: 0.75rem 1rem 1rem;
}
.admin-form legend { font-weight: 700; color: var(--heading); padding: 0 0.35rem; }
.admin-form p { margin: 0.75rem 0; }
.admin-form label { display: block; font-weight: 600; margin-bottom: 0.25rem; }
.admin-form small, .admin-table small, .admin-cards small {
  display: block; color: var(--ink-soft); font-weight: 400; margin-top: 0.25rem;
}
.admin-form input[type="text"], .admin-form input[type="search"],
.admin-form input[type="number"], .admin-form input[type="url"],
.admin-form input[type="date"], .admin-form input[type="time"],
.admin-form input[type="datetime-local"], .admin-form input[type="password"],
.admin-form select, .admin-form textarea,
.admin-searchbar input[type="search"] {
  width: 100%; box-sizing: border-box; min-height: 44px;
  padding: 0.4rem 0.6rem; font: inherit;
  /* --paper-alt, not --logo-pad: the logo pad is deliberately white in BOTH schemes,
   * and dark mode lightens --ink — light text on a white input would be invisible. */
  color: var(--ink); background: var(--paper-alt);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.admin-form input[type="number"] { max-width: 10rem; }
.admin-form input[type="date"], .admin-form input[type="time"] { max-width: 14rem; }
.admin-form input[type="datetime-local"] { max-width: 18rem; }
.admin-form textarea { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.admin-inline-options label { display: inline-flex; align-items: center; gap: 0.35rem; margin-right: 1rem; font-weight: 400; }

.admin-form button, .admin-searchbar button, .admin-table button, .admin-cards button {
  font: inherit; min-height: 44px; padding: 0.4rem 1rem; cursor: pointer;
  border: 1px solid var(--btn-outline); border-radius: var(--radius);
  background: var(--paper); color: var(--btn-outline); font-weight: 600;
}
.admin-form button:hover, .admin-searchbar button:hover,
.admin-table button:hover, .admin-cards button:hover { background: var(--paper-alt); }
.admin-primary { background: var(--brand) !important; color: var(--brand-ink) !important; border-color: var(--brand) !important; }
.admin-primary:hover { background: var(--brand-deep) !important; }
/* Danger is words plus a heavier border, never colour alone. */
.admin-danger { border-width: 2px !important; }

.admin-table { border-collapse: collapse; width: 100%; margin: 1rem 0; }
.admin-table th, .admin-table td {
  text-align: left; vertical-align: top;
  padding: 0.5rem 0.75rem 0.5rem 0; border-bottom: 1px solid var(--line);
}
.admin-table thead th { border-bottom: 2px solid var(--line-strong); color: var(--heading); }
.admin-table tbody th { font-weight: 600; }

.admin-rowlinks { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin: 0.5rem 0; }
.admin-rowlinks a { display: inline-flex; align-items: center; min-height: 32px; }
/* Inside a table the cell must stay a table cell: as a flex box it drops out of the
 * row's layout. The children wrap inline instead. */
.admin-table td.admin-rowlinks { display: table-cell; margin: 0; }
.admin-table td.admin-rowlinks > * { display: inline-block; vertical-align: middle; margin: 0 0.75rem 0.25rem 0; }

/* A link that leaves the admin opens a new tab, and says so with the usual mark. */
.admin-external svg { width: 0.85em; height: 0.85em; margin-left: 0.3em; vertical-align: -0.05em; }

/* ---- two columns ----
 * `.admin-columns` lays its children out side by side once there is room: fieldsets on
 * a form, or sections on a page. A child with `.admin-span` takes the full row. Below
 * 64rem it is the one column a phone needs. */
.admin-wide { max-width: none; }
.admin-columns { display: grid; gap: 0 1.5rem; grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 64rem) {
  .admin-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .admin-columns > .admin-span { grid-column: 1 / -1; }
}
.admin-columns > section > h2:first-child { margin-top: 0.5rem; }

/* ---- tabs ----
 * Plain links styled as tabs: one page per tab, so each works with JavaScript off and
 * has its own address. */
.admin-tabs {
  list-style: none; margin: 1rem 0 1.25rem; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.25rem;
  border-bottom: 2px solid var(--line-strong);
}
.admin-tabs a {
  display: inline-flex; align-items: center; min-height: 40px; padding: 0.25rem 0.9rem;
  border: 1px solid var(--line); border-bottom: none; border-radius: var(--radius) var(--radius) 0 0;
  background: var(--paper-alt); text-decoration: none; color: var(--link); font-weight: 600;
}
.admin-tabs a[aria-current="page"] {
  background: var(--brand); color: var(--brand-ink); border-color: var(--brand);
}
.admin-tabs small { font-weight: 400; margin-left: 0.35rem; }
.admin-tabs a[aria-current="page"] small { color: var(--brand-ink); }
.admin-tab-heading { margin-bottom: 0; }

/* ---- colour fields ----
 * The text box is the field (it can be empty, which a native colour input cannot be);
 * a script adds the picker beside it and keeps the two in step. */
.admin-color { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
/* The logo eyedropper: a large copy of the logo, a crosshair, the colour under it. */
.admin-eyedrop { margin: 0.5rem 0 1rem; padding: 0.75rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper-alt); max-width: 22rem; }
.admin-eyedrop p { margin: 0 0 0.5rem; font-size: 0.9rem; }
.admin-eyedrop canvas { display: block; width: 100%; max-width: 280px; height: auto; cursor: crosshair; touch-action: none;
  background: repeating-conic-gradient(var(--line) 0% 25%, transparent 0% 50%) 50% / 16px 16px; }
.admin-eyedrop-row { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.5rem; }
.admin-eyedrop-swatch { width: 2rem; height: 2rem; border: 1px solid var(--line-strong); border-radius: 4px; }
.admin-color input[type="text"] { max-width: 9rem; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.admin-color input[type="color"] {
  inline-size: 3.25rem; block-size: 44px; padding: 2px; cursor: pointer;
  border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--paper-alt);
}

/* ---- the awards set editor ----
 * One table, one form, every row editable at once. Narrow inputs so a whole
 * all-tournament team fits on a laptop screen without scrolling sideways. */
.admin-awards input[type="text"], .admin-awards select {
  width: 100%; box-sizing: border-box; min-height: 40px; padding: 0.3rem 0.5rem; font: inherit;
  color: var(--ink); background: var(--paper-alt);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.admin-awards td { vertical-align: middle; }
.admin-awards .admin-check { text-align: center; width: 4.5rem; }
.admin-awards .admin-check input { inline-size: 1.25rem; block-size: 1.25rem; }
.admin-awards tr.is-removed td { opacity: 0.45; text-decoration: line-through; }
.admin-scroll { overflow-x: auto; }

.admin-cards { list-style: none; margin: 1rem 0; padding: 0; display: grid; gap: 0.5rem; }
.admin-cards li {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
  gap: 0.5rem; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.5rem 0.75rem; background: var(--paper-alt);
}
.admin-cards a { display: inline-flex; align-items: center; min-height: 32px; }

/* A season's headshot beside its upload and remove, in the public page's 4:5 frame, so a
 * bad crop shows here. */
.admin-headshots { list-style: none; margin: 1rem 0; padding: 0; display: grid; gap: 0.5rem; max-width: 44rem; }
.admin-headshots li {
  display: flex; align-items: flex-start; gap: 0.75rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.5rem 0.75rem; background: var(--paper-alt);
}
.admin-headshots img, .admin-headshot-none {
  flex: 0 0 auto; width: 64px; height: 80px; border-radius: var(--radius);
  object-fit: cover; object-position: 50% 15%;
}
.admin-headshot-none {
  display: grid; place-items: center; font-size: 0.8rem; color: var(--ink-soft);
  border: 1px dashed var(--line-strong);
}
.admin-headshots li > div { display: grid; gap: 0.35rem; min-width: 0; }
.admin-headshots small { color: var(--ink-soft); }
.admin-headshots form { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.admin-headshots input[type="file"] { max-width: 100%; }
#headshots .admin-notice ul { margin: 0.25rem 0 0; font-weight: 400; }

.admin-searchbar { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: end; margin: 1rem 0; max-width: 44rem; }
.admin-searchbar label { font-weight: 600; }
.admin-searchbar input[type="search"] { flex: 1 1 12rem; width: auto; }
.admin-searchbar select {
  flex: 1 1 14rem; min-height: 44px; padding: 0.4rem 0.6rem; font: inherit;
  color: var(--ink); background: var(--paper-alt);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}

/* The courtside page: the two scores are the page, so they are enormous. */
/* ---- the game page (D-113) ----------------------------------------------
 * The score card first, built for a phone: a row per side of five small quarter boxes and
 * one big total, which wrap at phone width rather than scroll. */
.admin-game-line { margin: -0.5rem 0 0.75rem; color: var(--ink-soft); }
.admin-scorecard .admin-side { margin-bottom: 0.75rem; padding-bottom: 0.75rem; }
.admin-scorecard .admin-side legend { font-size: 1.1rem; }
.admin-quarters { display: flex; flex-wrap: wrap; gap: 0.5rem 0.6rem; align-items: end; }
.admin-quarters label { display: block; margin: 0; font-weight: 600; }
.admin-quarters label span { display: block; font-size: 0.8rem; color: var(--ink-soft); margin-bottom: 0.15rem; }
.admin-form .admin-quarters input[type="number"] { width: 3.4rem; max-width: none; padding-inline: 0.3rem; text-align: center; }
.admin-form .admin-quarters .admin-final input[type="number"] { width: 5.5rem; margin-left: 0.5rem; }
.admin-score-input { font-size: 1.5rem !important; }
.admin-score-save { font-size: 1.15rem; }
.admin-inline { display: flex; flex-wrap: wrap; gap: 0 1.5rem; }
/* A year's photos: the picture beside its form, stacked on a phone. */
.admin-photos { list-style: none; margin: 1rem 0; padding: 0; display: grid; gap: 1.5rem; }
.admin-photo { display: grid; gap: 0.75rem 1.25rem; grid-template-columns: minmax(0, 1fr); padding-bottom: 1.5rem; border-bottom: 1px solid var(--line); }
.admin-photo img { display: block; max-width: 16rem; width: 100%; height: auto; border-radius: var(--radius); }
@media (min-width: 48rem) { .admin-photo { grid-template-columns: 16rem minmax(0, 1fr); } .admin-photo > form:last-child { grid-column: 2; } }
.admin-actions { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; align-items: center; }
/* A fold inside a card: no box of its own, just the summary line. */
.admin-details-plain { background: none; border: 0; padding: 0; margin: 0.25rem 0; max-width: none; }
.admin-details-plain > summary { min-height: 36px; }
.admin-details-plain[open] > summary { border-bottom: 0; }
.admin-game-details > .admin-details { max-width: none; }

/* A long form folded away until it is wanted. <details> is native HTML: it opens with a
 * click, with Enter, and with JavaScript off. */
.admin-details {
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--paper-alt); margin: 1rem 0; padding: 0 0.75rem;
  max-width: 44rem;
}
.admin-details > summary {
  font-weight: 600; color: var(--heading); cursor: pointer;
  min-height: 44px; display: flex; align-items: center;
}
.admin-details[open] > summary { border-bottom: 1px solid var(--line); }
.admin-details > *:last-child { margin-bottom: 0.75rem; }

.admin-onsite { padding: 0.25rem 1rem; }
.admin-onsite h2 { margin-top: 0.5rem; }
.admin-setting { border-bottom: 1px solid var(--line); padding-bottom: 0.75rem; }
.admin-setting p { margin: 0.5rem 0; }

/* The inline-markup editor (D-72). The toolbar is added by a script and does not exist
 * without it; the textarea and the Preview button are the whole editor then. Each button
 * is a 44px target. */
.markup-toolbar { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.35rem; }
.admin-form .markup-toolbar button { min-width: 44px; padding: 0.4rem 0.7rem; }
.markup-toolbar .mt-bold { font-weight: 800; }
.markup-toolbar .mt-italic { font-style: italic; }
.markup-toolbar .mt-strike { text-decoration: line-through; }
.markup-preview { margin: 0.75rem 0; }
.markup-preview .announcement-bar { padding-inline: 0.8rem; border-radius: var(--radius); }
.admin-form .markup-preview-label { margin: 0 0 0.35rem; font-weight: 600; font-size: 0.9rem; }

/* The page editor (D-97): Quill over the body's textarea. Quill's own stylesheet is
 * light-only and sized for a mouse, so these give it the site's tokens (dark mode
 * included) and 44px targets. Without the script none of this exists. */
.page-editor-modes { display: flex; gap: 0.35rem; margin-bottom: 0.5rem; }
.admin-form .page-editor-modes button[aria-pressed="true"] {
  background: var(--brand); color: var(--brand-ink); border-color: var(--brand);
}
.page-editor-visual .ql-toolbar.ql-snow {
  position: sticky; top: 0; z-index: 2; padding: 0.25rem;
  background: var(--paper); border: 1px solid var(--line-strong);
  border-radius: var(--radius) var(--radius) 0 0; font-family: var(--font-body);
}
.page-editor-visual .ql-snow.ql-toolbar .ql-formats { margin-right: 0.5rem; }
.page-editor-visual .ql-snow.ql-toolbar button {
  width: 44px; height: 44px; min-height: 44px; padding: 11px; border: 0; border-radius: var(--radius);
  background: transparent;
}
.page-editor-visual .ql-snow.ql-toolbar button.ql-active { background: var(--paper-alt); }
.page-editor-visual .ql-snow .ql-picker { height: 44px; color: var(--ink); }
.page-editor-visual .ql-snow .ql-picker-label { display: flex; align-items: center; }
.page-editor-visual .ql-snow .ql-stroke { stroke: var(--ink); }
.page-editor-visual .ql-snow .ql-fill, .page-editor-visual .ql-snow .ql-stroke.ql-fill { fill: var(--ink); }
.page-editor-visual .ql-snow .ql-active .ql-stroke, .page-editor-visual .ql-snow button:hover .ql-stroke,
.page-editor-visual .ql-snow .ql-picker-label:hover .ql-stroke { stroke: var(--link); }
.page-editor-visual .ql-snow .ql-active .ql-fill, .page-editor-visual .ql-snow button:hover .ql-fill { fill: var(--link); }
.page-editor-visual .ql-snow .ql-picker-label:hover, .page-editor-visual .ql-snow .ql-picker-label.ql-active,
.page-editor-visual .ql-snow .ql-picker-item:hover, .page-editor-visual .ql-snow .ql-picker-item.ql-selected { color: var(--link); }
.page-editor-visual .ql-snow .ql-picker-options { background: var(--paper); border-color: var(--line-strong); }
.page-editor-visual .ql-snow .ql-picker-item { min-height: 44px; display: flex; align-items: center; }
.page-editor-visual .ql-snow .ql-picker.ql-expanded .ql-picker-label { border-color: var(--line-strong); }
.page-editor-visual .ql-snow .ql-tooltip {
  background: var(--paper); color: var(--ink); border-color: var(--line-strong); z-index: 3;
}
.page-editor-visual .ql-snow .ql-tooltip input[type="text"] {
  width: 14rem; height: 44px; font-size: 1rem; color: var(--ink); background: var(--paper-alt);
  border-color: var(--line-strong);
}
.page-editor-visual .ql-snow a { color: var(--link); }
.page-editor-visual .ql-snow .ql-tooltip a.ql-action, .page-editor-visual .ql-snow .ql-tooltip a.ql-remove {
  display: inline-block; min-height: 44px; line-height: 44px;
}
.page-editor-visual .ql-container.ql-snow {
  border: 1px solid var(--line-strong); border-top: 0; border-radius: 0 0 var(--radius) var(--radius);
  background: var(--paper); font: inherit;
}
/* The text area itself reads like the public page: the site's type, then the .page-body
 * rules above, over Quill's resets. */
.ql-editor.page-body {
  min-height: 24rem; padding: 1rem; color: var(--ink);
  font-family: var(--font-body); font-size: 1.0625rem; line-height: 1.6;
}
.ql-editor.page-body p { margin: 0 0 1rem; }
.ql-editor.page-body h1, .ql-editor.page-body h2, .ql-editor.page-body h3 {
  font-family: var(--font-display); font-weight: 700; letter-spacing: 0.01em; line-height: 1.05;
  color: var(--heading); margin: 1.75rem 0 0.5em;
}
.ql-editor.page-body h2 { font-size: clamp(1.3rem, 3vw, 1.7rem); text-transform: uppercase; }
.ql-editor.page-body h3 { font-size: 1.25rem; }
.ql-editor.page-body > :first-child { margin-top: 0; }
.ql-editor.page-body ol { margin: 0 0 1rem; }
.ql-editor.page-body li { margin-bottom: 0.3rem; }
.ql-editor.page-body a { color: var(--link); }
.ql-editor.page-body img { display: inline-block; }
.ql-snow .ql-editor.page-body.ql-blank::before { color: var(--ink-soft); }
.page-editor-status:empty { display: none; }
.page-editor-status { margin: 0.5rem 0 0; font-weight: 600; }

.admin-page-images { max-width: 44rem; margin-top: 2rem; border-top: 1px solid var(--line); }
.admin-uploaded img { max-width: 16rem; border: 1px solid var(--line); border-radius: var(--radius); }

.admin-chip {
  display: inline-block; border: 1px solid var(--line-strong); border-radius: 999px;
  padding: 0.1rem 0.7rem; margin: 0 0.25rem 0.25rem 0; background: var(--paper-alt);
}

/* ---- Photos (D-131): a year's gallery, and the same figures on a game page and in a team's
 * year. A grid of natural-shape pictures; each is a link to the full image, so a tap opens it
 * with no script. Thumbnails load as they scroll in. */
.photo-section { margin: 2rem 0; }
.photo-group-head { margin: 1.25rem 0 0.5rem; font-size: 1rem; }
.photo-grid {
  display: grid; gap: 0.75rem; align-items: start;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr));
}
.photo { margin: 0; }
.photo-link { display: block; border-radius: var(--radius); overflow: hidden; background: var(--paper-alt); }
.photo-link img { display: block; width: 100%; height: auto; }
.photo-link:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.photo figcaption { margin-top: 0.35rem; font-size: 0.875rem; line-height: 1.35; }
.photo-label { display: block; font-weight: 600; }
.photo-caption { display: block; }
.photo-credit { display: block; color: var(--ink-soft); font-size: 0.8rem; }
