/*
 * The whole stylesheet. No bundler, no preprocessor — this file is copied to
 * the output as-is, which keeps Node out of the build and the deploy image.
 *
 * Design rules, so later edits don't drift back:
 *   - solid fills only. no gradients, no drop-shadows, no filters. the trophy
 *     illustration is the one exception: it is flat-coloured but it is a raster
 *     drawing of a real object, not something built in CSS. that's a carve-out
 *     for the mark alone, not a loosening of the rule anywhere else.
 *   - square corners. the only radius on the site is the circular logo.
 *   - contrast comes from color blocks and type scale, never from depth.
 *   - tight tracking on display type, wide tracking only on small caps labels.
 */

/* Weight axis only (100-900). The two-axis build adds a width axis we don't
   use and costs 88KB instead of 34KB. */
@font-face {
  font-family: "Archivo";
  src: url("/fonts/archivo-latin-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* The second face, and it earns its place in exactly three spots — see
   --font-year below.
   A condensed industrial face drawn for civic signage. It differs from Archivo
   by width and proportion rather than by stroke contrast, which is the durable
   kind of difference: there are no hairlines to thin out, so it holds at every
   size on the site instead of only at display sizes. */
@font-face {
  font-family: "Big Shoulders Display";
  src: url("/fonts/big-shoulders-latin-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #0a0a0a;
  --surface: #141414;
  --line: #262626;
  /* The logo ring is #FFA200 (hue 38). This is that pushed well into yellow —
     hue 48, full saturation — which holds up better as type on near-black than
     the ring colour itself does. */
  --gold: #ffd324;
  --red: #d62828;
  --text: #f5f1ea;
  --text-mute: #8c8781;

  --font: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;

  /* Display years: the reigning champion's year, a season page's year, and the
     standings title. Held to those three deliberately — the contrast works
     because the face is rare on the page, and the wall cards, pager, standings
     counts and tick strip read better in Archivo's tabular figures anyway.
     Unlike a high-contrast face this one would survive being used smaller, so
     the limit here is editorial rather than technical.
     The fallback is a condensed stack, not the body stack: if the file never
     arrives the year should still read as a different voice from the team name
     under it, which is the entire point of having a second face. */
  --font-year: "Big Shoulders Display", "Arial Narrow", "Helvetica Neue",
    sans-serif;

  --shell: 76rem;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--gold);
}

/* Two type roles used all over the site. Everything else is body text. */

.u-display {
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 0.95;
}

.u-label {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-mute);
}

/* ---------- masthead ---------- */

.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 1.5rem;
  border-bottom: 1px solid var(--line);
}

.masthead__wordmark {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  text-decoration: none;
  color: inherit;
}

/* The logo art is already a gold ring on black, so it needs no framing here —
   the radius is belt-and-braces in case the asset is ever swapped for a square. */
.masthead__logo {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  flex-shrink: 0;
}

.masthead__names {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

/* word-spacing, not looser letter-spacing: the tight tracking is wanted on the
   caps themselves, it's only the gaps around "OF" that close up too far. The
   tagline below needs none of this — its 0.16em tracking already opens its word
   gaps, which is why the title is the only line here that reads cramped. */
.masthead__title {
  font-size: 1.25rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  word-spacing: 0.08em;
  line-height: 1;
  color: var(--text);
}

.masthead__tagline {
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--gold);
}

/* ---------- spotify badge ---------- */

/* The 0.75rem gap and the padding are Spotify's exclusion zone (half the icon
   height) — they are a brand requirement, not spacing taste. */
.spotify-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.8rem 1rem;
  border: 1px solid var(--line);
  color: var(--text);
  text-decoration: none;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  white-space: nowrap;
  transition: border-color 150ms ease, background-color 150ms ease;
}

.spotify-badge:hover,
.spotify-badge:focus-visible {
  border-color: var(--text-mute);
  background: var(--surface);
}

.spotify-badge:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

.spotify-badge__mark {
  display: block;
  flex-shrink: 0;
}

/* ---------- shell ---------- */

.vault {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 3rem 1.5rem 5rem;
}

/* ---------- trophy mark + reigning champion ---------- */

.trophy-stage {
  text-align: center;
  padding: 1rem 0 3.5rem;
  border-bottom: 1px solid var(--line);
}

/* Sized off width, and the width is chosen from the artwork's aspect rather than
   picked. The drawing is 1:1.44 once trimmed to its ink, and it is tall enough
   that the 21rem which suited a squarer mark pushed the reigning champion off
   the first screen. The trophy introduces the champion; it should not outrank
   him. */
.chest {
  position: relative;
  margin: 0 auto 2.25rem;
  max-width: 15.5rem;
}

/* Behind the artwork. The image is position:relative so it paints over this
   despite coming later in the DOM — an absolutely-positioned sibling would
   otherwise sit on top and cover the chest entirely. */
.chest__fills {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.chest__fills rect {
  fill: var(--gold);
}

.chest__art {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: auto;
}

/* Pointer-only, and gated positively so it fails closed. A vial renders 11.5px
   wide at 390px and the targets cannot exceed the ~24px pitch without
   overlapping each other — about half the 44px minimum tap size, and a tap
   would navigate before you could see which season you had hit. On touch the
   champion wall below reaches every season with full-size cards. */
.chest__hits {
  display: none;
}

@media (hover: hover) and (pointer: fine) {
  .chest__hits {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 2;
  }
}

.chest__hit {
  position: absolute;
  display: block;
}

.chest__hit:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

/* Every label occupies the same spot inside the open lid, and only the hovered
   one is shown — so nothing moves and the page needs no extra height for it. */
.chest__vial-label {
  position: absolute;
  left: 0;
  right: 0;
  top: 26%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}

.chest__hit:hover + .chest__vial-label,
.chest__hit:focus-visible + .chest__vial-label {
  opacity: 1;
}

.chest__vial-year {
  font-family: var(--font-year);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1;
  color: var(--gold);
}

.chest__vial-name {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text);
}

/* The h1 exists for the document outline; every visual decision still lives on
   the elements inside it, so it resets the browser's heading defaults. */
.champion-hero__heading,
.season__heading {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
}

.champion-hero {
  display: inline-block;
  text-decoration: none;
  color: inherit;
  max-width: 100%;
}

.champion-hero__marker {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1.25rem;
}

/* One of only two places --red is used. The trophy artwork sits directly above
   this bar and carries a third, slightly hotter red (#ed310a, the logo's) that
   this token deliberately doesn't match — the marker is a 3px sliver of UI
   chrome, not part of the mark, and matching them made it read as a stray
   fragment of the pepper. */
.champion-hero__marker::before {
  content: "";
  width: 3px;
  height: 0.85rem;
  background: var(--red);
}

/* Tracking sits at 0 rather than the -0.035em this carried in Archivo. A
   condensed face has already closed the space between letters by design;
   pulling it tighter on top of that jams the flat-sided digits together. */
.champion-hero__year {
  display: block;
  font-family: var(--font-year);
  font-size: clamp(4rem, 12vw, 8rem);
  font-weight: 800;
  letter-spacing: 0;
  line-height: 0.85;
  color: var(--gold);
  transition: color 150ms ease;
}

/* This now carries the team name rather than a single first name, so it's
   sized down a step and allowed to wrap. `balance` keeps a two-line name from
   breaking as one long line plus one orphan word.

   Set in the name's own case, not uppercased. Team names carry meaning in their
   capitalisation — "aChane aChane" and "TurfTonio&theBadJuju" are only legible
   as written, and flattening them to caps costs the reader the word boundaries.
   Weight and size carry the emphasis instead. The stat cells below were already
   mixed case, so this also makes the two agree. */
.champion-hero__name {
  display: block;
  margin-top: 0.5rem;
  font-size: clamp(2.25rem, 6.5vw, 4rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.05;
  color: var(--text);
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.champion-hero:hover .champion-hero__year,
.champion-hero:focus-visible .champion-hero__year {
  color: var(--text);
}

.champion-hero:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 6px;
}

.champion-hero__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.65rem;
  margin-top: 1.25rem;
  color: var(--text-mute);
  font-size: 1.05rem;
}

.champion-hero__sep {
  opacity: 0.45;
}

.champion-hero__score {
  font-variant-numeric: tabular-nums;
}

/* ---------- wall of past champions ---------- */

.champion-wall {
  padding-top: 3.5rem;
}

.champion-wall__heading {
  margin: 0 0 2rem;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-mute);
}

/* Hairlines come from each card's own right/bottom border rather than from a
   1px grid gap over a lighter container. The gap trick leaves any unfilled
   cell in the last row showing as a visibly lighter block, and the column
   count is responsive so those cells can't be filled in markup. */
.champion-wall__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
}

.champion-wall__item {
  display: flex;
  min-width: 0;
}

.champion-card {
  flex: 1;
  min-width: 0;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 1.5rem 1.25rem;
  background: var(--surface);
  text-decoration: none;
  transition: background-color 150ms ease, color 150ms ease;
}

/* No `font-variant-numeric: tabular-nums` on anything set in --font-year: that
   face has no tabular figures and no `tnum` feature for CSS to switch on, so the
   declaration would be inert — and an inert declaration reads to the next person
   as a guarantee that alignment is handled. Its digits vary by about 30% across
   year strings ("2011" against "2025"), which is fine here because each card
   left-aligns its own year, and wrong for the standings tick strip, which is why
   that one keeps Archivo. */
.champion-card__year {
  font-family: var(--font-year);
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: 0;
  line-height: 1;
  color: var(--gold);
  transition: color 150ms ease;
}

/* Team names are user-entered and unbounded in practice, so this wraps rather
   than truncates — an archive shouldn't hide part of a name.
   `anywhere`, not `break-word`: only `anywhere` also shrinks the element's
   min-content width. With `break-word` a single unbroken 40-character name
   still forces the grid track wide and pushes the whole page into horizontal
   overflow (measured at 103px past the viewport at 390px). */
/* Mixed case, for the reason given on .champion-hero__name. The tracking goes
   slightly negative with it — the old 0.01em was opening up the caps, and the
   house rule is tight tracking on display type, wide only on small-caps
   labels. */
.champion-card__team {
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--text);
  overflow-wrap: anywhere;
  text-wrap: balance;
  transition: color 150ms ease;
}

.champion-card__manager {
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--text-mute);
  transition: color 150ms ease;
}

.champion-card:hover,
.champion-card:focus-visible {
  background: var(--gold);
}

.champion-card:hover .champion-card__year,
.champion-card:hover .champion-card__team,
.champion-card:focus-visible .champion-card__year,
.champion-card:focus-visible .champion-card__team {
  color: var(--bg);
}

/* The manager stays subordinate on hover too — solid --bg would flatten it
   into the team name. */
.champion-card:hover .champion-card__manager,
.champion-card:focus-visible .champion-card__manager {
  color: rgba(10, 10, 10, 0.72);
}

.champion-card:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

/* ---------- standings ---------- */

.u-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.standings {
  max-width: 54rem;
}

.standings__header {
  margin: 2.5rem 0 3rem;
}

/* The one place --font-year sets words rather than digits. It holds because the
   line is short and set large; it would not hold as a body face. */
.standings__title {
  margin: 0;
  font-family: var(--font-year);
  font-size: clamp(2.25rem, 7vw, 3.75rem);
  font-weight: 800;
  letter-spacing: 0;
  line-height: 0.9;
  color: var(--gold);
}

.standings__dek {
  margin: 0.85rem 0 0;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}

/* The one grid every row shares. Rows and the tick strip use identical column
   definitions, which is what lets a column of marks read as a single season all
   the way down the page — the whole reason this is a timeline and not a list. */
.board__axis,
.board__row {
  display: grid;
  grid-template-columns: 2.25rem 6.5rem 1fr;
  align-items: center;
  gap: 0.75rem;
}

.board__axis {
  margin-bottom: 0.5rem;
}

.board__timeline {
  display: grid;
  grid-template-columns: repeat(var(--season-count), 1fr);
  gap: 3px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.board__tick {
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-mute);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.board {
  margin-bottom: 3.5rem;
}

.board__heading {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--gold);
}

/* Sized and given a flat line-height rather than inheriting the heading's —
   emoji ignore `color` and carry their own leading. Same treatment as
   .season__stat-marker and .teaser__marker. */
.board__marker {
  font-size: 0.85rem;
  line-height: 1;
}

.board--basement .board__heading {
  color: var(--text-mute);
}

.board__summary {
  margin: 0 0 1.5rem;
  font-size: 0.95rem;
  color: var(--text-mute);
}

.board__rows {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}

.board__row {
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--line);
}

.board__count {
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.board--basement .board__count {
  color: var(--text);
}

.board__name {
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

/* Square, not round: the site has no radius anywhere except the logo, and a row
   of circles would be the one place it broke. */
.board__cell {
  height: 1.5rem;
  background: var(--surface);
}

/* Seasons whose records were lost drop to the page colour, so the strip has a
   literal hole in it. This has to read as "no data" rather than as "didn't
   win" — an unfilled --surface cell already means the latter. A hatch would say
   it more explicitly, but hatching means a repeating gradient and the house rule
   at the top of this file is solid fills only; a hole is honest and costs
   nothing. The hairline keeps it from reading as a gap in the grid itself. */
.board__cell--unrecorded {
  background: var(--bg);
  border: 1px solid var(--line);
}

.board__cell--marked,
.board__cell--marked.board__cell--unrecorded {
  background: var(--gold);
}

.board--basement .board__cell--marked,
.board--basement .board__cell--marked.board__cell--unrecorded,
.board__cell--basement {
  background: var(--text-mute);
}

.board__mark {
  display: block;
  height: 100%;
}

.board__mark:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

.board__cell--marked:hover {
  background: var(--text);
}

.standings__notes {
  font-size: 0.85rem;
  color: var(--text-mute);
  padding-top: 2rem;
  border-top: 1px solid var(--line);
}

.standings__notes p {
  margin: 0 0 0.85rem;
}

.standings__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
}

.standings__key {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

/* Legend swatches are samples of the real thing, so they inherit the cell rules
   above and only need a width — the grid that normally sizes them isn't here. */
.standings__key .board__cell {
  width: 1.5rem;
  flex-shrink: 0;
}

/* ---------- homepage standings teaser ---------- */

/* Positioned so the link below can stretch across the whole block. */
.teaser {
  position: relative;
  padding-top: 3.5rem;
  margin-top: 3.5rem;
  border-top: 1px solid var(--line);
}

.teaser__heading {
  margin: 0 0 1.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-mute);
}

.teaser__boards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1px;
  background: var(--bg);
  border: 1px solid var(--line);
  transition: border-color 150ms ease;
}

.teaser__board {
  outline: 1px solid var(--line);
  background: var(--bg);
  padding: 1.25rem;
}

.teaser__label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.85rem;
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--gold);
}

/* Emoji ignore `color` and carry their own leading, so this is sized and set to
   a flat line-height rather than inheriting the label's. Matches the treatment
   on .season__stat-marker. */
.teaser__marker {
  font-size: 0.85rem;
  line-height: 1;
}

.teaser__board--basement .teaser__label {
  color: var(--text-mute);
}

.teaser__rows {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.teaser__row {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.teaser__count {
  min-width: 1.5rem;
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.teaser__board--basement .teaser__count {
  color: var(--text);
}

.teaser__name {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

.teaser__more {
  margin: 1.5rem 0 0;
}

.teaser__link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  text-decoration: none;
  color: var(--gold);
}

/* The boards are part of the link's hit area, not decoration beside it. Done as
   a stretched pseudo-element rather than by wrapping the section in an <a>:
   there is already an anchor in here, anchors cannot nest, and this way the
   accessible name stays the words "All-time standings" instead of becoming the
   whole block's text content read aloud.
   The trade is that text over the covered area can't be selected. Acceptable
   here — the same names are selectable on the page this links to. */
.teaser__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* Hover anywhere in the block, not just on the link text, so the affordance
   matches the hit area. */
.teaser:hover .teaser__arrow,
.teaser__link:hover .teaser__arrow {
  transform: translateX(3px);
}

.teaser:hover .teaser__boards {
  border-color: var(--text-mute);
}

.teaser__arrow {
  transition: transform 150ms ease;
}

.teaser__link:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

/* ---------- season detail ---------- */

.season {
  max-width: 48rem;
}

/* One rule for both back-links. They were two identical copies under different
   class names, which is exactly how the standings page kept its 13px-tall link
   when the season page's was raised to a 24px tap target (WCAG 2.2 target size,
   minimum). A second copy is a second thing to forget.

   The paragraph gives up the margin the link takes on, so the extra height
   doesn't push the heading below it down the page. */
.season__back,
.standings__back {
  margin: 0 0 0.35rem;
}

.season__back a,
.standings__back a {
  display: inline-flex;
  align-items: center;
  min-height: 1.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  text-decoration: none;
  color: var(--text-mute);
}

.season__back a:hover,
.standings__back a:hover {
  color: var(--gold);
}

.season__header {
  margin: 2.5rem 0 3rem;
}

/* Display year, so it takes --font-year on the same terms as the hero. */
.season__year {
  display: block;
  font-family: var(--font-year);
  font-size: clamp(3.5rem, 11vw, 7rem);
  font-weight: 800;
  letter-spacing: 0;
  line-height: 0.85;
  color: var(--gold);
}

/* Mixed case, for the reason given on .champion-hero__name. */
.season__champion {
  display: block;
  margin-top: 0.5rem;
  font-size: clamp(1.5rem, 4.5vw, 2.5rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.05;
  color: var(--text);
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.season__subtitle {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 1.5rem 0 0;
}

.season__eyebrow {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-mute);
}

.season__team {
  font-size: 1.05rem;
  color: var(--text);
}

/* Sized off the year's own clamp so the asterisk stays proportional as the
   display type scales, and pulled up to sit as a true superscript. */
.season__year-mark {
  font-size: clamp(1.2rem, 3.5vw, 2.2rem);
  vertical-align: super;
  line-height: 0;
  margin-left: 0.1em;
}

.season__lost {
  display: flex;
  gap: 0.75rem;
  margin: 0 0 2.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: 0.95rem;
  color: var(--text-mute);
}

.season__lost-mark {
  color: var(--gold);
}

.season__footnote {
  display: flex;
  gap: 0.5rem;
  margin: 0 0 2rem;
  padding-top: 2rem;
  border-top: 1px solid var(--line);
  font-size: 0.85rem;
  color: var(--text-mute);
}

.season__footnote-mark {
  color: var(--gold);
}

/* The footnote already draws the closing rule, so the pager doesn't draw a
   second one two lines below it. */
.season__footnote + .season__pager {
  padding-top: 0;
  border-top: none;
}

.season__stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 1px;
  margin: 0 0 3.5rem;
  background: var(--bg);
  border: 1px solid var(--line);
}

/* The dividing lines are drawn by each cell's outline rather than by letting a
   --line container background show through the 1px gaps. Same result when the
   grid is full, but an odd cell count leaves an empty track — and under the
   background technique that track fills solid --line and reads as a broken
   panel. Outlines are drawn outward, so neighbours share the gap and the
   outermost ones land on the container border. */
.season__stat {
  background: var(--bg);
  padding: 1.25rem;
  outline: 1px solid var(--line);
}

.season__stat-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-mute);
}

/* Emoji ignore `color` and carry their own leading, so this is sized and
   nudged rather than inherited. */
.season__stat-marker {
  font-size: 0.85rem;
  line-height: 1;
}

.season__stat-value {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin: 0.5rem 0 0;
}

/* Clamped rather than fixed at 1.5rem: these cells hold real team names now
   ("It's Not Delivery it's Bijorno"), not the short scores the grid was first
   built for, and the columns bottom out around 10rem. */
.season__stat-headline {
  font-size: clamp(1.15rem, 3.2vw, 1.5rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.season__stat-subhead {
  font-size: 0.9rem;
  color: var(--text-mute);
  overflow-wrap: anywhere;
}

.season__photos {
  margin-bottom: 3.5rem;
}

.season__photos h2 {
  margin: 0 0 0.85rem;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--gold);
}

.season__photos ul {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 1px;
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--line);
}

.season__photos img {
  display: block;
  width: 100%;
  height: auto;
}

.season__pager {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 2rem;
  border-top: 1px solid var(--line);
}

.season__pager-link {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  text-decoration: none;
  color: var(--text-mute);
}

.season__pager-link--next {
  text-align: right;
  margin-left: auto;
}

.season__pager-dir {
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

.season__pager-year {
  font-family: var(--font-year);
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: 0;
  color: var(--gold);
  transition: color 150ms ease;
}

/* The second and last use of --red. */
.season__pager-link:hover .season__pager-year {
  color: var(--red);
}

/* ---------- footer ---------- */

.site-footer {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 2rem 1.5rem 3rem;
  border-top: 1px solid var(--line);
  color: var(--text-mute);
  font-size: 0.82rem;
}

/* Podcast badge left, required Yahoo credit right, centred on each other.
   Stacked below the breakpoint.

   Deliberately a breakpoint rather than flex-wrap. Letting it wrap on its own
   sounds tidier but the wrap point and the styling then disagree: the two need
   860px to sit side by side (451 + 329 + gap + padding, measured), so an
   auto-wrapping row spends every width between the media query and 860px
   wrapped but still dressed as two columns — no separator, credit adrift at the
   left. One switch, at a width with headroom over 860, keeps them in step. */
@media (min-width: 56rem) {
  .site-footer__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
  }
}

.site-footer__brand {
  margin: 0 0 0.4rem;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text);
}

.site-footer__podcast {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin: 1.25rem 0;
}

.site-footer__show {
  color: var(--text-mute);
}

/* Yahoo attribution. Required by their API agreement, so this block is a
   compliance surface, not a styling opportunity — see _yahoo_attribution.slim
   before changing anything here.

   Stacked, a rule separates it from the podcast row above, since vertical space
   alone doesn't read as a division. Side by side, the row centres the two and
   neither is needed. */
/* flex, not block: the credit's <a> is inline-level, so in a block container it
   sits in a line box and picks up that box's leading — enough to land it 2.5px
   above the Spotify badge's centre even with the row centring both. As a flex
   item there's no line box and the two centres match exactly. */
.site-footer__credit {
  display: flex;
  min-width: 0;
  margin-top: 1.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}

@media (min-width: 56rem) {
  .site-footer__credit {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }
}

/* Wraps rather than overflows: at 390px this lands within a few pixels of the
   content width, so anything narrower puts the phrase on its own line. */
.yahoo-credit {
  display: inline-flex;
  align-items: center;
  min-height: 1.5rem;
  flex-wrap: wrap;
  gap: 0.6rem 0.75rem;
  color: var(--text-mute);
  text-decoration: none;
  font-size: 0.72rem;
}

.yahoo-credit:hover .yahoo-credit__text,
.yahoo-credit:focus-visible .yahoo-credit__text {
  color: var(--text);
}

.yahoo-credit:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

/* Sized off the mark's real aspect (168.5 x 22.9). Height drives it so the
   wordmark keeps a consistent cap height next to the text. The asset carries
   its own colours (Yahoo purple + white) — don't add fill/filter rules here.

   The nudge is a baseline correction, not a fudge. Both "yahoo" and "fantasy"
   have descenders, so the mark's box runs ~20% below its baseline while the
   adjacent text's descender is proportionally shallower. align-items: center
   lines up the two boxes, which leaves the wordmark sitting a pixel low against
   the text. Measured: mark baseline 675.70 vs text baseline 674.72.

   Percentage translateY resolves against the element's own height, so this
   holds if the mark is ever resized. */
.yahoo-credit__mark {
  display: block;
  flex-shrink: 0;
  height: 0.95rem;
  width: auto;
  transform: translateY(-6.4%);
}

/* ---------- misc ---------- */

.empty-state {
  padding: 5rem 0;
}

.empty-state h1 {
  margin: 0 0 0.75rem;
  font-size: clamp(4rem, 14vw, 8rem);
  font-weight: 900;
  letter-spacing: -0.04em;
  line-height: 0.85;
  color: var(--gold);
}

.empty-state p {
  margin: 0 0 0.5rem;
  color: var(--text-mute);
}

/* The only way off a 404, so it has to be reachable — 19px tall was under the
   24px minimum. Sentence case here rather than the back-links' eyebrow style:
   this is a sentence's worth of instruction, not a label. */
.empty-state p a {
  display: inline-flex;
  align-items: center;
  min-height: 1.5rem;
}

@media (max-width: 40rem) {
  .masthead {
    justify-content: center;
    text-align: center;
  }

  .masthead__names {
    align-items: center;
  }

  /* 10rem, not 11rem: at 390px the shell leaves 342px, and two 11rem tracks
     need 352px, so the grid silently collapsed to a single column. */
  .champion-wall__grid {
    grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  }

  .champion-card {
    padding: 1.25rem 1rem;
  }

  .champion-card__year {
    font-size: 1.75rem;
  }

  .champion-card__team {
    font-size: 0.95rem;
  }

  /* The timeline takes its own line rather than sharing one with the count and
     the name. Kept inline, fourteen cells would have about 10px each once the
     labels take their share — too small to read a run off, which is the only
     reason the timeline exists. Full width they get roughly 24px. */
  .board__axis,
  .board__row {
    grid-template-columns: 2rem 1fr;
    row-gap: 0.6rem;
  }

  .board__timeline {
    grid-column: 1 / -1;
  }
}

/* The badge leaves the masthead at exactly the width where it would wrap onto a
   second line. Above this it sits beside the wordmark and costs nothing; below
   it, it doubles the header's height to repeat a link the footer already
   carries on every page — so on a phone it's pure overhead above the fold.
   Scoped to .masthead deliberately: the footer badge is where it goes instead.
   31.25rem is measured, not rounded — 500px is the first width at which the
   badge's top clears the wordmark's bottom. Re-measure if the league name or
   the badge's label ever changes length; the wrap point moves with them. Note
   this is well below the 40rem block above, which centres the masthead while it
   is still comfortably on one line. */
@media (max-width: 31.25rem) {
  .masthead .spotify-badge {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .champion-card,
  .champion-card__year,
  .champion-card__team,
  .champion-card__manager,
  .champion-hero__year,
  .season__pager-year {
    transition: none;
  }
}
