/* The Dynasty League stylesheet.
 *
 * Order is fixed by docs/UI_DESIGN.md section 11:
 *   1. @font-face
 *   2. Tokens, light theme
 *   3. Dark theme overrides
 *   4. Reset and base elements
 *   5. Layout primitives
 *   6. Components, alphabetical
 *   7. Utilities, last
 *
 * Rules: light BEM, logical properties, no literal colours or magic pixel
 * values outside the token block, no ID selectors, nesting at most two deep,
 * min-width media queries only, and no !important except in the
 * prefers-reduced-motion block.
 */

/* ===== 1. Font ========================================================== */

/* Archivo Variable, latin. Built from the Google Fonts latin subset with:
 *
 *   fonttools varLib.instancer  --> wght 400:800, wdth 88:112
 *   pyftsubset                  --> U+0020-007E plus typographic punctuation
 *
 * Both axes survive because the design gets its contrast from width as well as
 * weight. Trimming to the ranges actually used takes the file from 90 KB to
 * 38 KB, inside the 45 KB budget. Licensed under the SIL Open Font License;
 * see fonts/OFL.txt.
 */
@font-face {
  font-family: "Archivo Variable";
  src: url("../fonts/archivo-var-latin.woff2") format("woff2-variations");
  font-weight: 400 800;
  font-stretch: 88% 112%;
  font-style: normal;
  font-display: swap;
}

/* ===== 2. Tokens, light theme ("day game") ============================== */

:root {
  --page:            #eff1f4;
  --surface:         #ffffff;
  --surface-sunken:  #f7f8fa;
  --surface-raised:  #ffffff;

  --ink:             #0e1726;
  --ink-muted:       #55616f;
  --ink-faint:       #626c79;
  --ink-inverse:     #ffffff;

  --border:          #dde1e7;
  --border-strong:   #7f8998;

  --accent:          #8a5a0f;
  --accent-strong:   #6e4709;
  --accent-wash:     #fbf3e2;

  --win:             #177245;
  --loss:            #b02a20;
  --tie:             #5b6470;
  --champion:        #b8873a;
  --playoff:         #7f8998;

  --win-wash:        #e8f3ec;
  --loss-wash:       #fbeceb;
  --tie-wash:        #eef0f3;

  /* The head-to-head matrix tints a cell by win rate and needs a second, firmer
   * step than the record pill's wash. Both are picked so --ink still clears AA
   * on them, for the same reason the washes above are.
   */
  --win-wash-strong:  #c8e2d3;
  --loss-wash-strong: #f6d4d1;

  --focus:           #1c5fd9;
  --shadow-tint:     14 23 38;

  /* Type */
  --font: "Archivo Variable", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --text-2xs:  0.6875rem;
  --text-xs:   0.75rem;
  --text-sm:   0.875rem;
  --text-base: 1rem;
  --text-lg:   1.125rem;
  --text-xl:   clamp(1.25rem, 1.1rem + 0.7vw, 1.5rem);
  --text-2xl:  clamp(1.5rem, 1.2rem + 1.4vw, 2.25rem);
  --text-3xl:  clamp(2rem, 1.4rem + 2.8vw, 3.5rem);
  --text-4xl:  clamp(2.5rem, 1.6rem + 4.2vw, 5rem);

  --leading-body: 1.6;
  --leading-heading: 1.25;
  --leading-figure: 1.1;
  --tracking-label: 0.08em;
  --tracking-figure: -0.01em;

  --weight-body: 400;
  --weight-data: 500;
  --weight-heading: 600;
  --weight-title: 700;
  --weight-figure: 800;

  --width-label: 88%;
  --width-normal: 100%;
  --width-title: 112%;

  /* Spacing */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;

  /* Radius, borders, elevation */
  --radius-sm: 4px;
  --radius:    6px;
  --radius-lg: 10px;
  --border-width: 1px;
  --focus-width: 2px;
  --focus-offset: 2px;

  --shadow-sm: 0 1px 2px rgb(var(--shadow-tint) / 0.06);
  --shadow:    0 4px 12px rgb(var(--shadow-tint) / 0.08);
  --shadow-lg: 0 12px 32px rgb(var(--shadow-tint) / 0.14);

  /* Motion */
  --ease: cubic-bezier(0.2, 0, 0.2, 1);
  --duration-fast: 120ms;
  --duration: 200ms;

  /* Layout */
  --container:       68rem;
  --container-wide:  84rem;
  --container-prose: 42rem;
  --gutter: var(--space-4);

  --header-height: 3.5rem;
  --rail-height: 2.75rem;
  --tap-target: 2.75rem;

  /* The rank column is given a fixed width so the manager column beside it
   * knows exactly how far in to stick when the table scrolls sideways.
   */
  --col-rank: 2.75rem;

  /* The head-to-head matrix. The sticky name column holds a display name and
   * each opponent column holds a record, so together they decide the width at
   * which the matrix stops needing to scroll sideways.
   */
  --h2h-name-col: 9rem;
  --h2h-cell-col: 5.5rem;

  /* A table taller than this scrolls inside itself, which is what lets its
   * header row stick. Applied from the md breakpoint up only: on a phone a
   * nested scroll area traps the page scroll, and the header is three short
   * words there anyway.
   */
  --table-max-height: 80vh;

  /* Season strip block sizes, one per size in UI_DESIGN section 2.2 */
  --strip-tiny:  8px;
  --strip-small: 14px;
  --strip-large: 40px;
  --strip-spine: 20px;
  --strip-gap: 3px;

  --avatar-sm: 24px;
  --avatar: 32px;
  --avatar-lg: 48px;

  color-scheme: light;
}

/* ===== 3. Dark theme ("night game") ==================================== */

/* Written once as a custom property block and applied from two selectors: the
 * system preference when nothing has been chosen, and the explicit override
 * the header toggle sets on <html>.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --page:            #0b1220;
    --surface:         #121b2c;
    --surface-sunken:  #0e1626;
    --surface-raised:  #18243a;

    --ink:             #e6eaf0;
    --ink-muted:       #9aa6b8;
    --ink-faint:       #8996a8;
    --ink-inverse:     #0b1220;

    --border:          #22304a;
    --border-strong:   #5470ae;

    --accent:          #e3b341;
    --accent-strong:   #f2c75c;
    --accent-wash:     #1c2436;

    --win:             #3fb950;
    --loss:            #f0685a;
    --tie:             #98a3b4;
    --champion:        #e3b341;
    --playoff:         #5470ae;

    --win-wash:        #10231a;
    --loss-wash:       #2a1614;
    --tie-wash:        #182135;
    --win-wash-strong:  #17402c;
    --loss-wash-strong: #4b211c;

    --focus:           #5b9dff;
    --shadow-tint:     0 0 0;

    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --page:            #0b1220;
  --surface:         #121b2c;
  --surface-sunken:  #0e1626;
  --surface-raised:  #18243a;

  --ink:             #e6eaf0;
  --ink-muted:       #9aa6b8;
  --ink-faint:       #8996a8;
  --ink-inverse:     #0b1220;

  --border:          #22304a;
  --border-strong:   #5470ae;

  --accent:          #e3b341;
  --accent-strong:   #f2c75c;
  --accent-wash:     #1c2436;

  --win:             #3fb950;
  --loss:            #f0685a;
  --tie:             #98a3b4;
  --champion:        #e3b341;
  --playoff:         #5470ae;

  --win-wash:        #10231a;
  --loss-wash:       #2a1614;
  --tie-wash:        #182135;
  --win-wash-strong:  #17402c;
  --loss-wash-strong: #4b211c;

  --focus:           #5b9dff;
  --shadow-tint:     0 0 0;

  color-scheme: dark;
}

/* ===== 4. Reset and base =============================================== */

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

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-block-start: calc(var(--header-height) + var(--rail-height) + var(--space-4));
}

body {
  background-color: var(--page);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--text-base);
  font-weight: var(--weight-body);
  line-height: var(--leading-body);
  min-block-size: 100vh;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
  font-weight: var(--weight-heading);
  line-height: var(--leading-heading);
  text-wrap: balance;
}

h1 {
  font-size: var(--text-2xl);
  font-stretch: var(--width-title);
  font-weight: var(--weight-title);
}

h2 {
  font-size: var(--text-xl);
}

h3 {
  font-size: var(--text-lg);
}

p {
  max-inline-size: var(--container-prose);
  text-wrap: pretty;
}

a {
  color: var(--accent);
  text-decoration-thickness: from-font;
  text-underline-offset: 0.15em;
}

a:hover {
  color: var(--accent-strong);
}

img {
  block-size: auto;
  max-inline-size: 100%;
}

button {
  color: inherit;
  font: inherit;
}

ul,
ol {
  padding-inline-start: 0;
}

:focus-visible {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* Tabular figures everywhere a number sits in a column. Without this,
 * 1,854.32 and 1,601.08 do not line up and the whole site looks sloppy.
 */
table th,
table td,
.stat,
.stat__figure,
.record-pill,
.season-strip {
  font-variant-numeric: tabular-nums;
}

/* ===== 5. Layout primitives ============================================ */

.container {
  inline-size: 100%;
  margin-inline: auto;
  max-inline-size: var(--container);
  padding-inline: var(--gutter);
}

.container--wide {
  max-inline-size: var(--container-wide);
}

.stack > * + * {
  margin-block-start: var(--space-4);
}

.stack--tight > * + * {
  margin-block-start: var(--space-2);
}

.stack--loose > * + * {
  margin-block-start: var(--space-6);
}

.cluster {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  list-style: none;
}

.section {
  padding-block: var(--space-7);
}

.section + .section {
  border-block-start: var(--border-width) solid var(--border);
}

.grid {
  display: grid;
  gap: var(--space-4);
}

@media (min-width: 30rem) {
  .grid--cards {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 48rem) {
  :root {
    --gutter: var(--space-6);
    --header-height: 4rem;
  }

  .section {
    padding-block: var(--space-8);
  }

  .grid--2,
  .grid--records {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .grid--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 64rem) {
  .grid--cards,
  .grid--records {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* ===== 6. Components =================================================== */

/* --- Card ------------------------------------------------------------- */

.card {
  background-color: var(--surface);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
}

.card__title {
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-stretch: var(--width-label);
  font-weight: var(--weight-heading);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

/* --- Champion line ---------------------------------------------------- */

.champion-line {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

/* --- Data table ------------------------------------------------------- */

/* The shared stat table. Three things it has to get right, all of them here
 * rather than repeated per page:
 *
 *   Column priority. Every cell declares the width at which it earns its
 *   space. Priority 1 shows at 360px, and the rest arrive as the screen grows.
 *
 *   Nothing lost when a column is hidden. Each hidden column is also written
 *   into a detail row underneath, which is open by default. tables.js collapses
 *   it behind a per-row control, so scripting improves this rather than
 *   enabling it.
 *
 *   Sticky axes. The first two columns hold still while the table scrolls
 *   sideways, and the header row holds still while a long table scrolls down.
 */

/* The overflow here is not belt and braces. .table-wrap is the scroll box and
 * clips its own painting, but a table wider than that box still contributes
 * its full width to this element's scrollable overflow, which pushes the whole
 * page sideways. No standings table ever hit it, because the column priority
 * system shrinks those to fit; the head-to-head matrix is the first table on
 * the site that genuinely has to scroll, and it made the page scroll with it.
 */
.table-scroll {
  overflow: hidden;
  position: relative;
}

.table-wrap {
  overflow-x: auto;
}

/* A visible edge fade means "there is more this way". tables.js adds the
 * scrollable class only when the table really does overflow and removes the
 * fade at the end of the scroll, so the hint is never a lie.
 */
.table-scroll::after {
  background-image: linear-gradient(to right, transparent, var(--page));
  content: "";
  inline-size: var(--space-6);
  inset-block: 0;
  inset-inline-end: 0;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  transition: opacity var(--duration) var(--ease);
}

.table-scroll--scrollable::after {
  opacity: 1;
}

.table-scroll--end::after {
  opacity: 0;
}

.data-table {
  border-collapse: collapse;
  font-size: var(--text-sm);
  inline-size: 100%;
}

.data-table caption {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  margin-block-end: var(--space-3);
  text-align: start;
}

.data-table__head,
.data-table__cell {
  background-color: var(--page);
  border-block-end: var(--border-width) solid var(--border);
  padding: var(--space-3) var(--space-2);
  text-align: start;
  vertical-align: top;
}

.data-table__cell--numeric {
  text-align: end;
  white-space: nowrap;
}

.data-table__head {
  border-block-end-color: var(--border-strong);
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-stretch: var(--width-label);
  font-weight: var(--weight-heading);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  vertical-align: bottom;
  white-space: nowrap;
}

.data-table tbody th,
.data-table tbody td {
  font-weight: var(--weight-data);
}

.data-table__row:hover > * {
  background-color: var(--surface-sunken);
}

.data-table__row--open > * {
  background-color: var(--surface-sunken);
}

/* Sticky axes. The rank column has a fixed width so the manager column knows
 * how far in to sit; the header row needs the wrapper's own height limit
 * below to have anything to stick against.
 */
.data-table__head:first-child,
.data-table__row > :first-child {
  inline-size: var(--col-rank);
  inset-inline-start: 0;
  position: sticky;
  z-index: 1;
}

.data-table__head:nth-child(2),
.data-table__row > :nth-child(2) {
  inset-inline-start: var(--col-rank);
  position: sticky;
  z-index: 1;
}

.data-table thead th {
  inset-block-start: 0;
  position: sticky;
  z-index: 2;
}

.data-table thead th:first-child,
.data-table thead th:nth-child(2) {
  z-index: 3;
}

@media (min-width: 48rem) {
  .table-wrap {
    max-block-size: var(--table-max-height);
  }
}

/* The manager cell: name, optionally a team name, optionally a season strip. */

.data-table__name {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.data-table__name-text {
  font-weight: var(--weight-data);
}

.data-table__note {
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

/* Column priority. Priority 1 is visible at every width; everything else
 * arrives as the screen grows.
 */
.data-table [data-priority="2"],
.data-table [data-priority="3"],
.data-table [data-priority="4"] {
  display: none;
}

@media (min-width: 30rem) {
  .data-table [data-priority="2"] {
    display: table-cell;
  }
}

@media (min-width: 48rem) {
  .data-table [data-priority="3"] {
    display: table-cell;
  }
}

@media (min-width: 64rem) {
  .data-table [data-priority="4"] {
    display: table-cell;
  }
}

/* The detail row, holding whatever the width is hiding. It is open until
 * tables.js collapses it, so no figure is ever unreachable.
 */
.data-table__detail {
  display: table-row;
}

.data-table__detail[hidden] {
  display: none;
}

.data-table__detail-cell {
  background-color: var(--surface-sunken);
  border-block-end: var(--border-width) solid var(--border);
  padding: var(--space-3) var(--space-2);
}

.data-table__detail-list {
  display: grid;
  gap: var(--space-2);
}

.data-table__detail-item {
  display: flex;
  gap: var(--space-3);
  justify-content: space-between;
}

.data-table__detail-label {
  color: var(--ink-muted);
}

.data-table__detail-value {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-data);
  margin-inline-start: 0;
}

/* Each detail item is the mirror image of its column: shown exactly while that
 * column is hidden, and gone the moment the column itself appears.
 */
@media (min-width: 30rem) {
  .data-table__detail-item[data-priority="2"] {
    display: none;
  }
}

@media (min-width: 48rem) {
  .data-table__detail-item[data-priority="3"] {
    display: none;
  }
}

@media (min-width: 64rem) {
  .data-table__detail,
  .data-table__detail-item[data-priority="4"] {
    display: none;
  }
}

/* Controls tables.js adds. Neither exists in the served HTML, because a
 * control that cannot do anything is worse than no control at all.
 */

.data-table__sort {
  align-items: center;
  background-color: transparent;
  border: 0;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  gap: var(--space-1);
  letter-spacing: inherit;
  min-block-size: var(--tap-target);
  padding: 0;
  text-transform: inherit;
}

.data-table__sort:hover {
  color: var(--ink);
}

.data-table__sort-icon {
  block-size: var(--space-3);
  color: var(--border-strong);
  flex: none;
  inline-size: var(--space-3);
}

[aria-sort="ascending"] .data-table__sort,
[aria-sort="descending"] .data-table__sort {
  color: var(--ink);
}

[aria-sort="ascending"] .data-table__sort-icon,
[aria-sort="descending"] .data-table__sort-icon {
  color: var(--accent);
}

.data-table__expand {
  align-items: center;
  background-color: transparent;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--ink-muted);
  cursor: pointer;
  display: inline-flex;
  justify-content: center;
  min-block-size: var(--tap-target);
  min-inline-size: var(--tap-target);
  transition: transform var(--duration-fast) var(--ease);
}

.data-table__expand svg {
  block-size: var(--space-4);
  inline-size: var(--space-4);
}

.data-table__expand[aria-expanded="true"] {
  transform: rotate(180deg);
}

@media (min-width: 64rem) {
  .data-table__expand {
    display: none;
  }
}

/* --- Empty state ------------------------------------------------------ */

.empty-state {
  background-color: var(--surface-sunken);
  border: var(--border-width) dashed var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink-muted);
  padding: var(--space-4);
}

/* --- Eyebrow ---------------------------------------------------------- */

.eyebrow {
  color: var(--ink-faint);
  display: block;
  font-size: var(--text-xs);
  font-stretch: var(--width-label);
  font-weight: var(--weight-heading);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

/* --- Filter ----------------------------------------------------------- */

/* Hidden until tables.js confirms it can actually filter something. A search
 * box that filters nothing is worse than no search box.
 */
.filter {
  display: none;
}

.filter--ready {
  display: block;
}

.filter__label {
  color: var(--ink-faint);
  display: block;
  font-size: var(--text-xs);
  font-stretch: var(--width-label);
  font-weight: var(--weight-heading);
  letter-spacing: var(--tracking-label);
  margin-block-end: var(--space-1);
  text-transform: uppercase;
}

.filter__input {
  background-color: var(--surface);
  border: var(--border-width) solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  inline-size: 100%;
  max-inline-size: var(--container-prose);
  min-block-size: var(--tap-target);
  padding-inline: var(--space-3);
}

.filter__count {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  min-block-size: var(--space-5);
}

/* --- Footer ----------------------------------------------------------- */

.site-footer {
  border-block-start: var(--border-width) solid var(--border);
  color: var(--ink-muted);
  font-size: var(--text-sm);
  margin-block-start: var(--space-8);
  padding-block: var(--space-6);
}

.site-footer__line {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.site-footer__updated {
  color: var(--ink);
  font-weight: var(--weight-data);
}

/* Data that has gone stale during the season is the one thing on this site
 * worth colouring red, and the sentence beside it says so in words too.
 */
.site-footer__updated--stale {
  color: var(--loss);
}

.site-footer__warnings {
  margin-block-start: var(--space-4);
}

.site-footer__warning {
  color: var(--ink-muted);
  margin-block-start: var(--space-2);
}

/* --- Game log --------------------------------------------------------- */

/* One pairing's meetings, most recent first. An outcome is a letter before it
 * is a colour, so the log reads correctly in greyscale and to a screen reader.
 */

.game-log {
  font-size: var(--text-sm);
  list-style: none;
  margin-block-start: var(--space-3);
}

.game-log__game {
  align-items: baseline;
  border-block-start: var(--border-width) solid var(--border);
  display: grid;
  gap: var(--space-2);
  grid-template-columns: minmax(0, 1fr) auto auto;
  padding-block: var(--space-2);
}

.game-log__when {
  color: var(--ink-muted);
}

.game-log__tag {
  background-color: var(--accent-wash);
  border-radius: var(--radius-sm);
  color: var(--accent-strong);
  font-size: var(--text-xs);
  padding-inline: var(--space-1);
  white-space: nowrap;
}

.game-log__outcome {
  font-weight: var(--weight-heading);
  inline-size: var(--space-4);
  text-align: center;
}

.game-log__outcome--W {
  color: var(--win);
}

.game-log__outcome--L {
  color: var(--loss);
}

.game-log__outcome--T {
  color: var(--tie);
}

.game-log__score {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* --- Head-to-head ----------------------------------------------------- */

/* Two interfaces over one set of numbers, not one interface scaled. The matrix
 * needs width and appears at md; the detail layer works at every width and is
 * the whole thing on a phone. See docs/UI_DESIGN.md section 6.6.
 */

.h2h-grid {
  display: none;
}

@media (min-width: 48rem) {
  .h2h-grid {
    display: block;
  }
}

/* Separate borders rather than collapsed ones: a collapsed border belongs to
 * the table rather than to the cell, and a sticky cell leaves it behind.
 */
.h2h-matrix {
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--text-sm);
  inline-size: 100%;
}

.h2h-matrix caption {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  margin-block-end: var(--space-3);
  max-inline-size: var(--container-prose);
  text-align: start;
}

.h2h-matrix th,
.h2h-matrix td {
  border-block-end: var(--border-width) solid var(--border);
}

.h2h-matrix th {
  padding: var(--space-2);
}

/* A cell's own padding lives on its contents, so the link fills the cell it
 * sits in. That makes the whole cell the tap target and the whole cell the
 * focus ring, rather than a word floating in the middle of one.
 */
.h2h-matrix td {
  padding: 0;
}

/* Both axes hold still: the heading row against the wrapper's height limit,
 * the name column against its horizontal scroll.
 */
.h2h-matrix__corner,
.h2h-matrix__head {
  background-color: var(--page);
  border-block-end-color: var(--border-strong);
  color: var(--ink-muted);
  font-weight: var(--weight-heading);
  inset-block-start: 0;
  position: sticky;
  text-align: center;
  z-index: 2;
}

.h2h-matrix__corner,
.h2h-matrix__row-head {
  background-color: var(--page);
  inline-size: var(--h2h-name-col);
  inset-inline-start: 0;
  position: sticky;
  text-align: start;
  z-index: 1;
}

.h2h-matrix__corner {
  z-index: 3;
}

.h2h-matrix__head-name {
  display: block;
  max-inline-size: var(--h2h-cell-col);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.h2h-matrix__row-head .h2h-matrix__head-name {
  font-weight: var(--weight-data);
  max-inline-size: var(--h2h-name-col);
}

.h2h-matrix__cell {
  font-variant-numeric: tabular-nums;
  min-inline-size: var(--h2h-cell-col);
  text-align: center;
}

.h2h-matrix__link,
.h2h-matrix__mark {
  display: block;
  padding: var(--space-3) var(--space-2);
}

.h2h-matrix__mark {
  color: var(--ink-faint);
}

.h2h-matrix__link {
  color: var(--ink);
  font-weight: var(--weight-data);
  text-decoration: none;
}

.h2h-matrix__link:hover {
  color: var(--ink);
  text-decoration: underline;
}

/* The crosshair, added by tables.js on hover and on focus. It is a reading aid
 * and carries nothing: every cell prints its record and both headers stay on
 * screen whether it is there or not.
 */
.h2h-matrix__marked {
  box-shadow: inset 0 0 0 var(--border-width) var(--border-strong);
}

th.h2h-matrix__marked {
  background-color: var(--accent-wash);
  color: var(--accent-strong);
}

/* Win rate tints. The record text is always printed as well, so the colour is
 * reinforcement and never the message. The steps are symmetric about .500,
 * because a matrix says the same thing twice and both halves have to agree.
 */
.h2h-tint--win-strong {
  background-color: var(--win-wash-strong);
}

.h2h-tint--win {
  background-color: var(--win-wash);
}

.h2h-tint--even {
  background-color: var(--tie-wash);
}

.h2h-tint--loss {
  background-color: var(--loss-wash);
}

.h2h-tint--loss-strong {
  background-color: var(--loss-wash-strong);
}

.h2h-tint--self,
.h2h-tint--absent {
  color: var(--ink-faint);
}

.h2h-key {
  align-items: center;
  color: var(--ink-muted);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  gap: var(--space-2);
  margin-block-start: var(--space-3);
  max-inline-size: none;
}

.h2h-key__swatch {
  block-size: var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-sm);
  display: inline-block;
  inline-size: var(--space-4);
}

/* The detail layer. Every panel ships visible, so the page is complete with
 * scripting off; tables.js narrows it to one at a time.
 */
.h2h-panels > * + * {
  margin-block-start: var(--space-6);
}

.h2h-panel[hidden] {
  display: none;
}

.h2h-panel__header {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: space-between;
}

.h2h-panel__title {
  font-size: var(--text-lg);
}

.h2h-panel__career {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  gap: var(--space-2);
}

.h2h-list {
  display: grid;
  gap: var(--space-2);
  list-style: none;
}

.h2h-pair {
  background-color: var(--surface);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
}

/* The summary keeps its default display, because setting display to grid on it
 * drops the disclosure marker in Chrome and Safari. The grid lives on a span
 * inside instead, and the marker is replaced by a caret in that grid: the
 * native one cannot be positioned inside the row, and this row has to line its
 * columns up with every other row in the list.
 */
.h2h-pair__summary {
  cursor: pointer;
  list-style: none;
  padding-block: var(--space-2);
  padding-inline: var(--space-3);
}

.h2h-pair__summary::-webkit-details-marker {
  display: none;
}

.h2h-pair__row {
  align-items: center;
  display: grid;
  gap: var(--space-3);
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  min-block-size: var(--tap-target);
}

@media (min-width: 30rem) {
  .h2h-pair__row {
    grid-template-columns: auto minmax(0, 1fr) auto var(--space-8) auto;
  }
}

.h2h-pair__caret {
  block-size: var(--space-3);
  color: var(--ink-faint);
  inline-size: var(--space-3);
  transition: transform var(--duration-fast) var(--ease);
}

.h2h-pair[open] .h2h-pair__caret {
  transform: rotate(90deg);
}

.h2h-pair__name {
  font-weight: var(--weight-data);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.h2h-pair__rate {
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

/* A bar is a second reading of the rate printed beside it, never the only one. */
.h2h-pair__meter {
  background-color: var(--tie-wash);
  block-size: var(--space-2);
  border-radius: var(--radius-sm);
  display: none;
  overflow: hidden;
}

@media (min-width: 30rem) {
  .h2h-pair__meter {
    display: block;
  }
}

.h2h-pair__fill {
  background-color: var(--tie);
  block-size: 100%;
  display: block;
  inline-size: calc(var(--h2h-share) * 100%);
}

.h2h-pair__fill--win {
  background-color: var(--win);
}

.h2h-pair__fill--loss {
  background-color: var(--loss);
}

.h2h-pair--empty {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  max-inline-size: none;
  padding: var(--space-3);
}

.h2h-pair__body {
  padding: 0 var(--space-3) var(--space-3);
}

.h2h-facts {
  display: grid;
  gap: var(--space-1) var(--space-4);
}

@media (min-width: 30rem) {
  .h2h-facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.h2h-facts__item {
  display: flex;
  font-size: var(--text-sm);
  gap: var(--space-2);
  justify-content: space-between;
}

.h2h-facts__item dt {
  color: var(--ink-muted);
}

.h2h-facts__item dd {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-data);
  margin-inline-start: 0;
  text-align: end;
}

/* --- Header ----------------------------------------------------------- */

.site-header {
  background-color: var(--surface);
  border-block-end: var(--border-width) solid var(--border);
  inset-block-start: 0;
  position: sticky;
  z-index: 10;
}

.site-header__bar {
  align-items: center;
  display: flex;
  gap: var(--space-3);
  justify-content: space-between;
  min-block-size: var(--header-height);
}

.site-header__title {
  font-size: var(--text-base);
  font-stretch: var(--width-title);
  font-weight: var(--weight-title);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.site-header__title a {
  color: var(--ink);
  text-decoration: none;
}

/* --- Leaderboard ------------------------------------------------------ */

.leaderboard {
  list-style: none;
}

.leaderboard__row {
  align-items: baseline;
  border-block-end: var(--border-width) solid var(--border);
  display: grid;
  gap: var(--space-2);
  grid-template-columns: var(--space-5) 1fr auto auto;
  padding-block: var(--space-2);
}

.leaderboard__rank {
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.leaderboard__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.leaderboard__record,
.leaderboard__rate {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-data);
}

.leaderboard__rate {
  color: var(--ink-muted);
}

/* --- Manager badge ---------------------------------------------------- */

.manager-badge {
  align-items: center;
  display: flex;
  gap: var(--space-2);
  min-inline-size: 0;
}

.manager-badge__avatar {
  block-size: var(--avatar);
  border-radius: 50%;
  flex: none;
  inline-size: var(--avatar);
  object-fit: cover;
}

.manager-badge__initials {
  align-items: center;
  background-color: hsl(var(--badge-hue, 210) 38% 82%);
  block-size: var(--avatar);
  border-radius: 50%;
  color: hsl(var(--badge-hue, 210) 60% 18%);
  display: flex;
  flex: none;
  font-size: var(--text-xs);
  font-weight: var(--weight-figure);
  inline-size: var(--avatar);
  justify-content: center;
}

.manager-badge__text {
  min-inline-size: 0;
}

.manager-badge__name {
  display: block;
  font-weight: var(--weight-data);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manager-badge__team {
  color: var(--ink-muted);
  display: block;
  font-size: var(--text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manager-badge--sm .manager-badge__avatar,
.manager-badge--sm .manager-badge__initials {
  block-size: var(--avatar-sm);
  inline-size: var(--avatar-sm);
}

.manager-badge--lg .manager-badge__avatar,
.manager-badge--lg .manager-badge__initials {
  block-size: var(--avatar-lg);
  font-size: var(--text-base);
  inline-size: var(--avatar-lg);
}

/* --- Manager card ----------------------------------------------------- */

.manager-card__record {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.manager-card__facts {
  display: grid;
  gap: var(--space-1) var(--space-3);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.manager-card__fact {
  display: flex;
  font-size: var(--text-sm);
  gap: var(--space-2);
  justify-content: space-between;
}

.manager-card__fact dt {
  color: var(--ink-muted);
}

.manager-card__fact dd {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-data);
  margin-inline-start: 0;
}

/* --- Name list -------------------------------------------------------- */

.name-list {
  columns: 2;
  font-weight: var(--weight-data);
  list-style: none;
}

.name-list__item {
  break-inside: avoid;
  padding-block: var(--space-1);
}

/* --- Pill rail -------------------------------------------------------- */

/* One horizontally scrolling row of links. Used by the primary navigation and
 * by the season selector. Plain links in a scroll container, so it works with
 * scripting disabled.
 */
.pill-rail {
  display: flex;
  gap: var(--space-2);
  list-style: none;
  margin-inline: calc(var(--gutter) * -1);
  mask-image: linear-gradient(
    to right,
    transparent 0,
    black var(--space-4),
    black calc(100% - var(--space-6)),
    transparent 100%
  );
  overflow-x: auto;
  padding-inline: var(--gutter);
  scroll-snap-type: x proximity;
  scrollbar-width: none;
}

.pill-rail::-webkit-scrollbar {
  display: none;
}

.pill-rail__item {
  flex: none;
  scroll-snap-align: start;
}

.pill {
  align-items: center;
  border: var(--border-width) solid transparent;
  border-radius: var(--radius);
  color: var(--ink-muted);
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: var(--weight-data);
  min-block-size: var(--tap-target);
  padding-inline: var(--space-3);
  text-decoration: none;
  transition: background-color var(--duration-fast) var(--ease);
  white-space: nowrap;
}

.pill:hover {
  background-color: var(--surface-sunken);
  color: var(--ink);
}

/* Matches the rail's own aria-current="page" and the head-to-head chips, which
 * select a view within this page rather than a page and so carry "true".
 */
.pill[aria-current] {
  background-color: var(--accent-wash);
  border-color: var(--accent);
  color: var(--accent-strong);
}

/* --- Placement note --------------------------------------------------- */

.placement-note {
  background-color: var(--surface-sunken);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--ink-muted);
  font-size: var(--text-sm);
  padding: var(--space-1) var(--space-3);
}

/* --- Record card ------------------------------------------------------ */

.record-card__figure {
  display: block;
  font-size: var(--text-3xl);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-figure);
  letter-spacing: var(--tracking-figure);
  line-height: var(--leading-figure);
  margin-block-start: var(--space-2);
}

.record-card__context {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  margin-block-start: var(--space-1);
}

.record-card__rest {
  border-block-start: var(--border-width) solid var(--border);
  font-size: var(--text-sm);
  list-style: none;
  margin-block-start: var(--space-3);
  padding-block-start: var(--space-3);
}

/* The name wraps inside its own column and the figures hold their line, so a
 * place carrying an era marker never pushes its own value onto a row of its
 * own. Nothing here wraps as a whole: the value belongs beside the record.
 */
.record-card__place {
  align-items: baseline;
  display: flex;
  gap: var(--space-2);
  justify-content: space-between;
}

.record-card__place + .record-card__place {
  margin-block-start: var(--space-1);
}

.record-card__who {
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow-wrap: break-word;
}

.record-card__tail {
  align-items: baseline;
  display: flex;
  flex: none;
  gap: var(--space-2);
}

.record-card__value {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-data);
}

/* The holder line under the figure: who set it and when. */
.record-card__meta {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-start: var(--space-2);
}

.record-card__when {
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

/* Held on one line only where it is one: the holder line says "2025 week 12",
 * while a place row can carry a streak's whole span and has to be free to wrap
 * rather than push the card sideways at 360px.
 */
.record-card__meta .record-card__when {
  white-space: nowrap;
}

/* The era marker. It names the starting slot count in words rather than
 * carrying a glyph, for two reasons: the subset font has no dagger, and an
 * asterisk is exactly the reading DESIGN section 16.3 rejects. The record is
 * annotated, not asterisked out.
 */
.record-card__era {
  background-color: var(--accent-wash);
  border-radius: var(--radius-sm);
  color: var(--accent-strong);
  font-size: var(--text-xs);
  font-weight: var(--weight-data);
  padding-inline: var(--space-2);
  white-space: nowrap;
}

/* --- Record note ------------------------------------------------------ */

.record-note {
  border-block-start: var(--border-width) solid var(--border);
  color: var(--ink-muted);
  font-size: var(--text-sm);
  padding-block-start: var(--space-4);
}

.record-note > * + * {
  margin-block-start: var(--space-2);
}

.record-note__eras {
  list-style: none;
}

.record-note__eras li {
  align-items: center;
  display: flex;
  gap: var(--space-2);
}

/* --- Record pill ------------------------------------------------------ */

.record-pill {
  background-color: var(--tie-wash);
  border-radius: var(--radius-sm);
  color: var(--tie);
  display: inline-block;
  font-weight: var(--weight-data);
  padding-inline: var(--space-2);
  white-space: nowrap;
}

.record-pill--win {
  background-color: var(--win-wash);
  color: var(--win);
}

.record-pill--loss {
  background-color: var(--loss-wash);
  color: var(--loss);
}

/* --- Result ----------------------------------------------------------- */

.result-list {
  display: grid;
  gap: var(--space-3);
  list-style: none;
}

@media (min-width: 48rem) {
  .result-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.result__kind {
  margin-block-end: var(--space-2);
}

.result__side {
  align-items: baseline;
  display: grid;
  gap: var(--space-2);
  grid-template-columns: 1fr auto var(--space-5);
  padding-block: var(--space-1);
}

.result__side--winner {
  font-weight: var(--weight-figure);
}

.result__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.result__score {
  font-variant-numeric: tabular-nums;
}

.result__marker {
  color: var(--ink-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-heading);
  text-align: end;
}

.result__side--winner .result__marker {
  color: var(--win);
}

/* --- Season navigation ------------------------------------------------ */

.season-nav {
  display: flex;
  gap: var(--space-3);
  justify-content: space-between;
}

.season-nav__link {
  align-items: center;
  display: inline-flex;
  font-weight: var(--weight-data);
  min-block-size: var(--tap-target);
}

.season-nav__link--next {
  margin-inline-start: auto;
}

/* --- Season strip ----------------------------------------------------- */

.season-strip {
  display: flex;
  gap: var(--strip-gap);
  list-style: none;
}

.season-strip__year {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.season-strip__label {
  color: var(--ink-faint);
  font-size: var(--text-2xs);
  font-stretch: var(--width-label);
  font-weight: var(--weight-heading);
}

.season-strip__block {
  align-items: center;
  block-size: var(--strip-tiny);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-sm);
  color: var(--ink);
  display: flex;
  font-size: var(--text-2xs);
  font-weight: var(--weight-figure);
  inline-size: var(--strip-tiny);
  justify-content: center;
  line-height: 1;
}

.season-strip__block--champion {
  background-color: var(--champion);
  color: var(--ink-inverse);
}

.season-strip__block--runner_up {
  background-color: var(--accent-wash);
  border-color: var(--champion);
  color: var(--accent-strong);
}

.season-strip__block--playoff {
  background-color: var(--playoff);
}

.season-strip__block--missed {
  border-color: var(--border-strong);
}

.season-strip__block--in_progress {
  border-color: var(--border-strong);
  border-style: dashed;
  color: var(--ink-faint);
}

/* A season the manager did not play keeps its column so strips stay aligned,
 * but shows nothing in it.
 */
.season-strip__block--absent {
  visibility: hidden;
}

.season-strip__star {
  block-size: 62%;
  inline-size: 62%;
}

/* Sizes. Glyphs and year labels only appear once a block is big enough to
 * hold them, which is why the two small sizes hide both.
 */
.season-strip--tiny .season-strip__label,
.season-strip--small .season-strip__label {
  display: none;
}

.season-strip--tiny .season-strip__block {
  font-size: 0;
}

.season-strip--small .season-strip__block {
  block-size: var(--strip-small);
  font-size: var(--text-2xs);
  inline-size: var(--strip-small);
}

.season-strip--large {
  gap: var(--space-2);
  margin-inline: calc(var(--gutter) * -1);
  overflow-x: auto;
  padding-inline: var(--gutter);
  scrollbar-width: none;
}

.season-strip--large::-webkit-scrollbar {
  display: none;
}

.season-strip--large .season-strip__year {
  flex: none;
}

.season-strip--large .season-strip__block {
  block-size: var(--strip-large);
  font-size: var(--text-lg);
  inline-size: var(--strip-large);
}

/* The fourth size, the champions timeline's spine, is not a strip variant. A
 * timeline interleaves a body with every block, so its blocks cannot be
 * siblings in one list; the block itself is reused at --strip-spine under
 * .timeline__mark instead. See section 6, Timeline.
 */

/* --- Skip link -------------------------------------------------------- */

.skip-link {
  background-color: var(--surface);
  border: var(--border-width) solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
  padding: var(--space-2) var(--space-3);
  position: absolute;
  transform: translateY(-200%);
  z-index: 20;
}

.skip-link:focus {
  transform: translateY(0);
}

/* --- Stat block ------------------------------------------------------- */

.stat__label {
  color: var(--ink-faint);
  display: block;
  font-size: var(--text-xs);
  font-stretch: var(--width-label);
  font-weight: var(--weight-heading);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.stat__figure {
  display: block;
  font-size: var(--text-3xl);
  font-weight: var(--weight-figure);
  letter-spacing: var(--tracking-figure);
  line-height: var(--leading-figure);
}

.stat__figure--hero {
  font-size: var(--text-4xl);
}

/* A name is not a figure and does not survive being set at figure size. */
.stat__figure--name {
  font-size: var(--text-xl);
  overflow-wrap: anywhere;
}

.stat__note {
  color: var(--ink-muted);
  display: block;
  font-size: var(--text-sm);
}

/* --- Theme toggle ----------------------------------------------------- */

/* Hidden until the inline head script confirms scripting is available,
 * because a toggle that cannot store a choice is a dead control.
 */
.theme-toggle {
  display: none;
}

.js .theme-toggle {
  align-items: center;
  background-color: transparent;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  color: var(--ink-muted);
  cursor: pointer;
  display: inline-flex;
  flex: none;
  justify-content: center;
  min-block-size: var(--tap-target);
  min-inline-size: var(--tap-target);
  transition: color var(--duration-fast) var(--ease);
}

.theme-toggle:hover {
  color: var(--ink);
}

.theme-toggle__icon {
  block-size: var(--space-5);
  inline-size: var(--space-5);
}

/* --- Timeline --------------------------------------------------------- */

/* The champions page: the season strip rotated ninety degrees, so the league's
 * history reads top to bottom. Each stop is literally a .season-strip__block in
 * the same visual states, which is what makes a title read the same here as it
 * does in a standings row, and costs the spine no colour or glyph of its own.
 */
.timeline {
  list-style: none;
}

.timeline__season {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: auto minmax(0, 1fr);
}

.timeline__rail {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.timeline__year {
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-heading);
}

.timeline__mark {
  block-size: var(--strip-spine);
  flex: none;
  font-size: var(--text-xs);
  inline-size: var(--strip-spine);
}

/* The line between stops is drawn down the rail rather than between two
 * elements, so the oldest season simply has nothing below it.
 *
 * The gap between stops is the body's own bottom margin rather than a margin on
 * the row, because a grid row is as tall as its tallest item's margin box: the
 * rail stretches through that gap and the spine stays one unbroken line.
 */
.timeline__body {
  margin-block-end: var(--space-4);
}

.timeline__rail::after {
  background-color: var(--border);
  content: "";
  flex: 1;
  inline-size: var(--border-width);
  min-block-size: var(--space-4);
}

.timeline__season:last-child .timeline__rail::after {
  content: none;
}

.timeline__final {
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

.timeline__score {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-data);
}

/* ===== 7. Utilities ==================================================== */

.visually-hidden {
  block-size: 1px;
  clip-path: inset(50%);
  inline-size: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
}

.text-muted {
  color: var(--ink-muted);
}

.text-prose {
  max-inline-size: var(--container-prose);
}

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