/* Hand-written, no framework and no build step -- the same rule the scraper
   dashboard follows, and the same tokens, so the two look like one project.
   A copy rather than a shared file: these are separate applications and are
   meant to be able to diverge without breaking each other. */

:root {
  /* The site is a record of games, not a broadcast of them, and the palette
     serves that. The ground is ice -- a fresh sheet under arena lights is
     cold and faintly cyan, never a neutral grey -- and everything structural
     is a deep, desaturated teal that belongs to no club.

     Exactly one colour here is warm, and it means a goal. Nothing the site
     sets *in type* is ever allowed to be red apart from that. Club colours
     (see the slate, below) are a separate register: they are identity rather
     than meaning, and they appear only as an edge and never as text, so the
     two cannot be confused.

     Both palettes live here, one token each, and `light-dark()` chooses
     between them from `color-scheme`. That is the whole theme mechanism --
     the two rules under this block set `color-scheme`, and every token
     follows. The alternative, a second `:root` block per palette (once in a
     media query, once behind an attribute), is the same eleven colours
     written three times and three places for them to drift apart.

     `color-scheme` earns its keep twice: it also tells the browser what to
     paint the parts this stylesheet cannot reach. Scrollbars and the footer's
     `<select>` popups used to render light against the dark palette, because
     a media query changes our colours and nothing else. */
  color-scheme: light dark;

  --bg: light-dark(#edf2f4, #0b1013);
  --panel: light-dark(#ffffff, #131a1e);
  --text: light-dark(#0e1417, #e4ebee);
  --muted: light-dark(#5c6b72, #8b9ba3);
  --border: light-dark(#d8e2e7, #232e34);
  /* A hairline lighter than --border, for a rule that divides two halves
     of one thing rather than separating two things. */
  --rule: light-dark(#e9eff2, #1a2328);
  --accent: light-dark(#15556e, #5fb3d4);
  --accent-soft: light-dark(#e2edf2, #16303c);
  --goal: light-dark(#c8102e, #ff6b6b);

  /* One measure for the whole site, shared by `main` and `footer` so the two
     can never drift apart. The homepage used to widen this so three game
     cards would fit; it bought three columns only past a 1512px viewport and
     ran edge-to-edge on every laptop below that, which is a high price for a
     column. The top bar is full-bleed, so with one rail the content now sits
     at the same place on every page and nothing moves as you navigate. */
  --rail: 1100px;

  /* Shape says what kind of thing something is. A card is a discrete object
     and gets both; a strip set into the page gets the smaller radius and no
     shadow; a control is a pill; a table is not an object at all.

     Dark drops the shadow to transparent rather than darkening it: on a dark
     ground a shadow is invisible at best and a grey halo at worst, and the
     panel already separates itself by being lighter than what it sits on. */
  --radius: 12px;
  --radius-inset: 8px;
  --shadow: 0 1px 2px light-dark(rgb(14 20 23 / 5%), transparent),
    0 4px 14px light-dark(rgb(14 20 23 / 4%), transparent);

  /* A scale on a 1.2 ratio. There were twelve sizes here, most within a hair
     of each other, which is drift rather than a scale -- and it is why
     nothing looked deliberately larger than anything else. */
  --t-xs: 0.7rem;
  --t-sm: 0.833rem;
  --t-base: 1rem;
  --t-md: 1.2rem;
  --t-lg: 1.44rem;
  --t-xl: 1.728rem;
  --t-2xl: 2.074rem;
}

/* The override, and the only thing the toggle does. With neither attribute
   present the root keeps `light dark` and the operating system decides, which
   is exactly what an unset cookie means -- so "Automatic" is the server
   writing no attribute at all rather than writing a third value.

   The attribute is rendered from the cookie, server-side, so the first byte
   of HTML already carries it. A script that reads storage and repaints cannot
   manage that: it runs after the first paint, and a dark-mode visitor sees a
   white flash on every navigation. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  /* `rem` is the browser's root size, not this one, so the scale has to
     anchor here -- and a visitor who enlarged their text keeps it. */
  font-size: var(--t-base);
  line-height: 1.5;
  /* Without this the stack renders noticeably heavier on macOS than the
     weights here were chosen against. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ----- chrome ----------------------------------------------------------- */

.topbar {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 0.9rem 1.5rem;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 650;
  color: var(--text);
  white-space: nowrap;
}

.brand:hover { text-decoration: none; }

.puck {
  width: 18px;
  height: 10px;
  border-radius: 50%;
  background: var(--text);
  display: inline-block;
}

main {
  max-width: var(--rail);
  margin: 0 auto;
  padding: 1.5rem;
}

footer {
  max-width: var(--rail);
  margin: 0 auto;
  padding: 0 1.5rem 3rem;
  color: var(--muted);
  font-size: var(--t-sm);
}

/* The version and the display settings sit on one row and stack when the
   screen is too narrow to hold both. `.prefs` already carries the top margin
   that separates the row from the disclaimer, so `.version` matches it rather
   than the row setting one for both -- that way a wrapped `.prefs` keeps its
   own spacing instead of collapsing against the line above it. */
.settings {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0 1.25rem;
}

/* No font-size of its own: the footer already sets one, and the version is
   the same small print as everything else down here. Tabular figures so the
   number does not shift sideways when a digit changes. */
.version {
  margin: 0.75rem 0 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Both display settings, one form, one submit. They are the same kind of
   thing -- a preference the server remembers in a cookie -- and splitting
   them into two forms would have meant two copies of the hidden inputs that
   carry the rest of the query string. */
.prefs {
  display: flex;
  align-items: center;
  gap: 0.5rem 1.25rem;
  flex-wrap: wrap;
  margin-top: 0.75rem;
}

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

/* The two forms sit side by side and read as one row of display settings,
   which is the whole reason they are wrapped: `.settings` is `space-between`,
   and three children would push the zone into the middle of the footer rather
   than keeping both settings together on the right. */
.prefs-group {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 1.25rem;
}

.prefs select,
.prefs button {
  font: inherit;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.25rem 0.5rem;
}

.prefs button { cursor: pointer; }

/* Choosing one of a few alternatives. The site already says this with the
   pills of `.splits` -- the current one filled and outlined, the rest quiet --
   so the palette picker says it the same way rather than inventing a second
   dialect for the same idea.

   Buttons rather than a `<select>` and a Set: three options do not need a
   menu, and the one shortcut that would drop the Set -- submitting on change
   -- moves the page out from under anyone arrowing through the options. The
   zone beside it keeps its select, and its Set with it, because ten zones do
   want a menu.

   After `.prefs button` on purpose: same specificity, so order is what decides
   which one dresses these. */
.choices { display: flex; gap: 0.25rem; flex-wrap: wrap; }
.choices button {
  padding: 0.3rem 0.7rem;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  font: inherit;
  font-size: var(--t-sm);
  color: var(--muted);
  cursor: pointer;
}
.choices button:hover { background: var(--panel); border-color: transparent; }
.choices button[aria-current] {
  background: var(--panel);
  border-color: var(--border);
  color: var(--text);
  font-weight: 600;
}

/* ----- search ----------------------------------------------------------- */

.search {
  position: relative;
  flex: 1;
  max-width: 420px;
}

.search input {
  width: 100%;
  padding: 0.5rem 0.75rem;
  font: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.search input:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}

.suggestions {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  right: 0;
  margin: 0;
  padding: 0.25rem;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  /* A phone with the keyboard up has perhaps 300px of viewport left; a
     fixed 22rem list is taller than that and the last hits are unreachable. */
  max-height: min(22rem, 60vh);
  overflow-y: auto;
}

.suggestions li a {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.5rem;
  border-radius: 6px;
  color: var(--text);
}

.suggestions li a:hover,
.suggestions li.active a {
  background: var(--accent-soft);
  text-decoration: none;
}

.suggestions .muted { margin-left: auto; }

/* ----- shared bits ------------------------------------------------------ */

.panel {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-inset);
  padding: 1.1rem 1.25rem;
  margin-bottom: 1.5rem;
}

.panel h2 {
  margin: 0 0 0.9rem;
  font-size: var(--t-lg);
  letter-spacing: -0.02em;
}

.panel h2 .muted {
  font-weight: 400;
  margin-left: 0.4rem;
  font-size: var(--t-sm);
}

.muted { color: var(--muted); }
.empty { color: var(--muted); margin: 0; }

.note {
  margin: 0 0 0.9rem;
  padding: 0.6rem 0.8rem;
  background: var(--accent-soft);
  border-radius: var(--radius-inset);
  font-size: var(--t-sm);
}

/* Where a face goes. The initials are always drawn; a photograph, when one
   is switched on and arrives, covers them. So "no photo" and "photo failed"
   look identical and neither looks broken. */
.avatar {
  position: relative;
  display: inline-grid;
  place-items: center;
  flex: none;
  border-radius: 50%;
  overflow: hidden;
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
  letter-spacing: 0.01em;
  user-select: none;
}

.avatar::before { content: attr(data-initials); }

.avatar img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--bg);
}

.avatar.mug { width: 32px; height: 32px; font-size: var(--t-xs); }

/* Tables scroll inside their own box rather than pushing the page sideways:
   a full game log is eighteen columns wide and a phone is not. */
table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
  display: block;
  overflow-x: auto;
  white-space: nowrap;
}

th, td {
  padding: 0.42rem 0.6rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
}

thead th {
  font-size: var(--t-xs);
  letter-spacing: 0.01em;
  color: var(--muted);
  font-weight: 600;
}

tbody tr:last-child td,
tbody tr:last-child th { border-bottom: 0; }

.num { text-align: right; }
.won { color: var(--accent); font-weight: 600; }
.lost { color: var(--muted); }
.date { color: var(--muted); }

/* The line is the new fact on the row; the linemates are context for it, so
   they sit back the way the date does. Neither needs a width: the table is
   already a scroll box, and nowrap up there keeps three surnames on one line. */
.line { font-weight: 600; }
.linemates { color: var(--muted); }
/* The power-play unit is the same kind of fact as the line, so it reads the
   same way. It follows the linemates rather than the line: two columns that
   answer "who with" sit together, and this one answers "yes or no". */
.pp { font-weight: 600; }

/* ----- home ------------------------------------------------------------- */

.hero { padding: 1.5rem 0 2rem; }
.hero h1 {
  margin: 0 0 0.5rem;
  font-size: var(--t-2xl);
  line-height: 1.15;
  letter-spacing: -0.03em;
}
.lede { margin: 0; color: var(--muted); max-width: 46ch; }

/* The slate, as the evening it is.

   One row per hour, with the games at that hour hung off a rail, rather than a
   grid of cards. Two things come of that. The shape of the night -- four games
   at seven, one at half past ten -- is readable before a single tri-code is,
   which no arrangement of cards managed. And it fits: a sixteen-game Saturday
   is the heaviest night the schedule produces, and it ran to five screens of
   cards against two of this.

   Away above home inside every game, the way every scoreboard in the sport is
   laid out -- so position says which club is at home and no word has to. That
   is also why nothing here prints "at" or "vs". */

.evening { list-style: none; margin: 0; padding: 0; }

.slot { display: grid; grid-template-columns: 5.6rem 1fr; gap: 0 1.15rem; }

/* The hour, and the two things that qualify it, right-aligned against the rail.
   Only the clock is at full contrast; the day and the zone are conditions on
   it and are set like conditions.

   The day is not decoration and not redundant with the heading above the
   slate: one Saturday in Toronto is two different days in Berlin -- a matinee
   falls on the Saturday evening there and a late game on the Sunday morning --
   and this column is now the only thing on the page that can say so.

   The padding is the exact stack of boxes above the first line of the first
   game beside it: the rail's own top padding, then the fixture's border and
   padding. With the absolute `line-height` giving the clock the same 1.5rem
   line box the tri-code has, two top-aligned boxes of equal height put the two
   baselines within a pixel -- no estimate of the font's ascent enters into it,
   so the two stay aligned if the type scale is ever retuned. */
.oclock {
  margin: 0;
  padding-top: calc(0.35rem + 0.55rem + 1px);
  text-align: right;
  font-size: var(--t-sm);
  line-height: 1.5rem;
  color: var(--muted);
}

.oclock .puck-drop {
  display: block;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.oclock .when, .oclock .state {
  display: block;
  font-size: var(--t-xs);
  line-height: 1.35;
}

/* The rail itself, and the pip that marks the hour on it -- so the eye can
   find ten o'clock on the way down without reading every time above it. The
   pip takes the same stack as the clock, plus half a line box, less half of
   itself, which centres it on the first game's first line. */
.hung {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0.35rem 0 1.15rem 1.4rem;
  border-left: 1px solid var(--border);
  display: grid;
  gap: 0.5rem;
}

.hung::before {
  content: "";
  position: absolute;
  left: -4.5px;
  top: calc(0.35rem + 0.55rem + 1px + 0.75rem - 4px);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

/* The rail stops at the last hour rather than running past it into nothing. */
.slot:last-child .hung { border-left-color: transparent; padding-bottom: 0; }

/* One game. The border is the site's accent folded most of the way into the
   border it replaces: same blue as the pip, a fraction of its weight, so
   sixteen boxes never compete with the nine pips that order them.

   `grid-auto-rows: 1fr` is what makes the two clubs' colour edges the same
   length -- the rows are equalised to the taller, so a club whose names wrap
   to a second line does not end up with a longer edge than its opponent. The
   `gap` is what keeps those two edges apart rather than letting them meet into
   one continuous line down the box, which would read as a single club's. There
   is deliberately no hairline between the rows: a rule sitting in that gap
   would join exactly what the gap is there to separate. */
.fixture {
  display: grid;
  grid-auto-rows: 1fr;
  gap: 0.5rem;
  padding: 0.55rem 0.8rem;
  background: var(--panel);
  border: 1px solid light-dark(
    color-mix(in oklab, var(--accent) 38%, var(--border)),
    color-mix(in oklab, var(--accent) 32%, var(--border)));
  border-radius: var(--radius-inset);
}

/* One club's row: who they are, and who is worth watching.
 *
 * `--club-soft` is a third of the club's colour and the only place any of it
 * appears. The four-pixel band down the full height of a card was what made
 * thirty-two brand hues read as a rainbow on this page; a two-pixel edge at a
 * third strength, twice per game, is a different quantity of the same idea --
 * enough to tie five names to a club, too little to register as a hue anyone
 * is being shown.
 *
 * Dark reaches that same third by a different route rather than a different
 * ratio. Mixing a navy straight into a near-black border moves almost nothing
 * while a gold moves a lot, so on dark the club is lightened toward white
 * first and only then folded in -- which nudges every club by about the same
 * amount, instead of leaving Vegas legible and Toronto invisible.
 *
 * A club the stylesheet has no colour for keeps `--border` and simply looks
 * like a club with a quiet edge. Nothing to notice, which is why a rename
 * would otherwise go unseen -- `test_every_club_on_the_slate_has_a_colour`
 * is what notices. */
.side {
  position: relative;
  display: grid;
  /* The club's column is sized to the longest name the league can produce,
     not to the longest one on the slate. "Columbus Blue Jackets" measures
     9.01rem at --t-sm in the stack above; the next widest, Vegas, is 8.51rem
     and most of the league is under 8. It was 9rem -- short of Columbus by a
     tenth of a pixel, and so the one club in thirty-two that wrapped.

     A wrapped name is not a cosmetic problem on this row. `grid-auto-rows: 1fr`
     on the fixture equalises the two clubs to the taller, so one second line
     made that whole game a line and a half deeper than the nine beside it, and
     the shape of the evening -- which is the entire argument for the rail --
     was reporting a club's name length as if it were something about the game.
     It went unseen because Columbus was not on a slate until the preseason was
     turned on, which is the same way the colour test earns its keep.

     9.75rem rather than 9.01 because the font is not ours: the stack resolves
     to a different face on every platform and the same name measures 8.70rem
     in Arial and 9.59rem in Verdana, the widest face tried. Wrapping stays the
     failure mode rather than an overflow, so a franchise renamed past even
     that degrades where it can be seen instead of printing over the players. */
  grid-template-columns: 2.7rem 9.75rem 1fr auto;
  align-items: start;
  gap: 0 0.6rem;
  min-width: 0;
  padding-left: 0.8rem;

  --club: var(--border);
  --club-soft: light-dark(
    color-mix(in oklab, var(--club) 33%, var(--border)),
    color-mix(in oklab, color-mix(in oklab, var(--club) 70%, #fff) 33%, var(--border)));
}

/* The edge, as a pseudo-element rather than a `border-left`. A border takes
   the height of whatever padding separates the rows as well as the row itself,
   which is what makes the two of them different lengths and leaves them
   touching; this is pinned to its own row's box and nothing else. */
.side::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  border-radius: 1px;
  background: var(--club-soft);
}

.side[data-club="ANA"] { --club: #f47a38; }
.side[data-club="BOS"] { --club: #ffb81c; }
.side[data-club="BUF"] { --club: #003087; }
.side[data-club="CAR"] { --club: #ce1126; }
.side[data-club="CBJ"] { --club: #002654; }
.side[data-club="CGY"] { --club: #d2001c; }
.side[data-club="CHI"] { --club: #cf0a2c; }
.side[data-club="COL"] { --club: #6f263d; }
.side[data-club="DAL"] { --club: #006847; }
.side[data-club="DET"] { --club: #ce1126; }
.side[data-club="EDM"] { --club: #ff4c00; }
.side[data-club="FLA"] { --club: #c8102e; }
.side[data-club="LAK"] { --club: #a2aaad; }
.side[data-club="MIN"] { --club: #154734; }
.side[data-club="MTL"] { --club: #af1e2d; }
.side[data-club="NJD"] { --club: #ce1126; }
.side[data-club="NSH"] { --club: #ffb81c; }
.side[data-club="NYI"] { --club: #00539b; }
.side[data-club="NYR"] { --club: #0038a8; }
.side[data-club="OTT"] { --club: #c52032; }
.side[data-club="PHI"] { --club: #f74902; }
.side[data-club="PIT"] { --club: #fcb514; }
.side[data-club="SEA"] { --club: #355464; }
.side[data-club="SJS"] { --club: #006d75; }
.side[data-club="STL"] { --club: #002f87; }
.side[data-club="TBL"] { --club: #002868; }
.side[data-club="TOR"] { --club: #00205b; }
.side[data-club="UTA"] { --club: #71afe5; }
.side[data-club="VAN"] { --club: #00843d; }
.side[data-club="VGK"] { --club: #b4975a; }
.side[data-club="WPG"] { --club: #004c97; }
.side[data-club="WSH"] { --club: #c8102e; }

.tri {
  font-weight: 650;
  letter-spacing: -0.03em;
  white-space: nowrap;
}

/* Everything on the row that is not the tri-code takes the tri-code's 1.5rem
   line box rather than its own 1.25rem one. Top-aligned boxes of equal height
   put the baselines within about a pixel, so the club's name, its players and
   its score all sit on the tri-code's line -- and the row sits on the clock's,
   out on the rail. */
.full, .names, .side .score { line-height: 1.5rem; }

.full {
  margin: 0;
  font-size: var(--t-sm);
  color: var(--muted);
  min-width: 0;
}

/* Five names, set as text. Twelve outlined capsules per game was most of what
   made this page feel busy, and a name reads as a name rather than as a
   control without one. The row gap is zero on purpose: the line box already
   carries the leading, so a wrapped second line of names keeps the same
   rhythm as the first rather than drifting away from it. */
.names {
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.95rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--t-sm);
  min-width: 0;
}

.names a { color: var(--text); }
.names a:hover { color: var(--accent); }
.names em { font-style: normal; color: var(--muted); font-variant-numeric: tabular-nums; }

/* The loser's score stays muted. Which side won is the first thing anybody
   wants from a finished game, and colour says it faster than reading two
   numbers and comparing them. */
.side .score {
  font-size: var(--t-md);
  font-weight: 650;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.side .score.won { color: var(--goal); }

.none {
  margin: 0;
  font-size: var(--t-sm);
  color: var(--muted);
  font-style: italic;
}

.leaders, .results {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: rank;
}

.leaders li a, .results li a {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.25rem;
  border-bottom: 1px solid var(--border);
  color: var(--text);
}

.leaders li:last-child a, .results li:last-child a { border-bottom: 0; }
.leaders li a:hover, .results li a:hover {
  background: var(--accent-soft);
  text-decoration: none;
}

.who { display: flex; flex-direction: column; line-height: 1.25; }
.who .muted { font-size: var(--t-sm); }

.tally {
  margin-left: auto;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.tally .muted { font-size: var(--t-sm); }

/* ----- player ----------------------------------------------------------- */

.player-head {
  display: flex;
  gap: 1.25rem;
  align-items: flex-start;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.25rem;
  margin-bottom: 1rem;
}

.avatar.headshot { width: 96px; height: 96px; font-size: var(--t-xl); }

.identity h1 {
  margin: 0;
  font-size: var(--t-xl);
  letter-spacing: -0.03em;
}

.sweater { color: var(--muted); font-weight: 400; }
.subtitle { margin: 0.15rem 0 0.9rem; color: var(--muted); }
.subtitle .club-name { color: var(--text); }

.vitals {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 2rem;
  margin: 0;
}

.vitals dt {
  font-size: var(--t-xs);
  color: var(--muted);
}

.vitals dd { margin: 0; }

/* The page used to offer three choices in three rows of near-identical
   links -- the season type, the split, and the tab that did not exist yet --
   and an unaskable fourth, the season, which was inferred and had no control
   at all. Nothing looked like a different kind of decision from anything else,
   which is most of what made it tiring to move around.

   One control shape per kind of choice now: a menu for a value out of a list
   that grows by one every year, a tab bar for which view, pills for narrowing
   a table. Three shapes, and none of them can be mistaken for another. */
.scope {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
  margin: 0 0 1rem;
}

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

/* Dressed like the footer's settings on purpose. Both are "a menu that
   decides what you are looking at", and a second dialect for the same idea is
   how a site ends up with two kinds of select. */
.scope select,
.scope button {
  font: inherit;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-inset);
  padding: 0.35rem 0.55rem;
}

.scope button { cursor: pointer; }

.field label {
  font-size: var(--t-xs);
  color: var(--muted);
}

/* ----- the admin section ------------------------------------------------ */

/* Deliberately not dressed like the footer's settings or like `.scope`. Both
   of those change what one visitor is looking at; this changes the site for
   everybody, and looking like a preference is how somebody flips it the way
   they flip a theme. Stacked checkboxes in a panel, with room around them. */
.settings-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.5rem;
}

.tick {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  cursor: pointer;
}

.tick input {
  width: 1rem;
  height: 1rem;
  accent-color: var(--accent);
  cursor: pointer;
}

.settings-form button {
  font: inherit;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-inset);
  padding: 0.35rem 0.9rem;
  cursor: pointer;
  /* Pushed to the end of the row so the boxes read as one group and the
     commit is not the fourth thing in a list of four. */
  margin-left: auto;
}

/* The one message on this site that is a refusal rather than a note.
   Deliberately not red: `--goal` belongs to a puck crossing a line and to
   nothing else, which is a rule a test enforces because it is the kind that
   erodes one well-meaning exception at a time. An outline and the weight are
   enough -- it is read by somebody who has just pressed Save and is looking
   straight at it. */
.note.refused {
  background: transparent;
  border: 1px solid var(--accent);
  font-weight: 600;
}

/* The one line on the site carrying a clock time, so it is allowed to look
   like a thing rather than like a row of a table. */
.next-game {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 1rem;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-inset);
  background: var(--bg);
}

.next-game .label {
  font-size: var(--t-xs);
  color: var(--muted);
}

.next-game .when { margin-left: auto; color: var(--muted); }

/* Tabs and their content are one card, so the panel below a tab is visibly
   the thing that tab opened rather than the next section of the page. The
   alternative -- a free-floating row of links above a separate box -- is what
   the page already had three of. */
.tabbed {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 1.5rem;
}

/* Scrolls rather than wraps. Two tabs today and more expected; tabs on a
   second row stop reading as a bar and start reading as a bug, and a phone
   would find that out first. The scrollbar itself is hidden because the
   overflowing tab is its own affordance. */
.tabs {
  display: flex;
  gap: 1.5rem;
  padding: 0 1.25rem;
  border-bottom: 1px solid var(--rule);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}

.tabs::-webkit-scrollbar { display: none; }

/* An inset shadow rather than a border: it is drawn inside the padding box,
   so it sits on the strip's hairline without a negative margin that the
   scroll container would clip. */
.tabs a {
  padding: 0.8rem 0 0.7rem;
  color: var(--muted);
  white-space: nowrap;
  box-shadow: inset 0 -2px 0 transparent;
}

.tabs a:hover { color: var(--text); text-decoration: none; }

/* Colour and a rule, and deliberately not weight. Bolding the current tab
   changes its width, and every tab after it moves a few pixels each time you
   switch -- on a bar whose whole job is to be switched. */
.tabs a.here {
  color: var(--text);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.tab-body { padding: 1.25rem; }

/* Narrowing a table, which is a smaller decision than the two above it and
   is dressed as one. */
.splits {
  display: flex;
  gap: 0.25rem;
  flex-wrap: wrap;
  /* Tighter than the space under the totals below, because the pills and the
     totals are one group -- the control and the answer it produced -- and the
     log is what comes after it. */
  margin: 0 0 0.6rem;
}

.splits a {
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  font-size: var(--t-sm);
  color: var(--muted);
  border: 1px solid transparent;
}

/* Filled with the page ground, not with `--panel` -- these sit inside the
   card now, and a pill that fills with the colour it is already sitting on is
   an outline and nothing else. Sunk into the card rather than raised out of
   it, which also keeps them clear of the tab above: that one is marked with
   the accent, and two accented selections on top of each other would compete
   for which choice you had just made. */
.splits a:hover { color: var(--text); text-decoration: none; }

.splits a.here {
  background: var(--bg);
  border-color: var(--border);
  color: var(--text);
  font-weight: 600;
}

/* What the log adds up to. This replaced a thirteen-column table of three
   seasons, which was the second scroll box on a page that only needed one --
   and which answered a question the menus above now answer better, since the
   menus reach every season we hold rather than three of them.

   Space is the separator. The facts are separate elements with a real gap
   between them, rather than one string stitched together with a glyph.

   Sized as a caption for the table, which is what it is. It first shipped
   with its four leading numbers at --t-md, which made a season line the second
   loudest thing on the page after the player's own name -- a lot of voice for
   a strip whose job is to give the table underneath it some context. Nothing
   here now exceeds the body size, and the whole strip fits on one line at the
   rail width where it used to take two.

   The hierarchy survived the shrink because it stopped being made of size.
   Four numbers lead at the body size in semibold and the other nine sit one
   step down; that is a fifth of the difference there was, and it still reads,
   because weight and size are two separate signals and only one of them costs
   room. A stat line with thirteen equal numbers in it has no shape.

   The labels are --t-xs muted, which is not a coincidence: that is exactly
   what `thead th` is, so the strip's labels and the column headings a few
   pixels below them are the same type saying the same words. That is also
   what the hairline underneath is for. Two adjacent runs of small muted
   labels is one run too many, and on a page where the numbers a visitor is
   reading came from two different scopes -- the whole split up here, ten rows
   of it down there -- the one thing worth drawing is where one ends.

   `--rule` and not `--border`: the token exists for a line that divides two
   halves of one thing rather than separating two things, which is the summary
   and the log exactly. And a line under it rather than a box around it,
   because the totals do not belong to the table below them -- they answer the
   split above them, and a box would have fenced them off from it.

   Space is the separator between the facts themselves. They are separate
   elements with a real gap between them, rather than one string stitched
   together with a glyph. */
.totals {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 1rem;
  margin: 0 0 0.8rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--rule);
  font-size: var(--t-sm);
}

.totals > div {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
}

.totals dt { color: var(--muted); font-size: var(--t-xs); }
.totals dd { margin: 0; font-variant-numeric: tabular-nums; }

.totals .lead dd {
  font-size: var(--t-base);
  font-weight: 600;
}

.more { margin: 0.9rem 0 0; color: var(--muted); font-size: var(--t-sm); }

/* ----- distribution ----------------------------------------------------- */

/* Four small multiples of the same season: a horizontal bar per bucket, the
   count at the tip and the share of the season beside it. No axis, because
   with six rows the direct labels *are* the axis, and no legend, because one
   series needs no key -- the heading already names what is plotted.

   The colour job here is magnitude, not identity, so it is one hue rather
   than four: giving Goals, Assists, Points and Shots a colour each would
   invite a reader to compare across charts by hue when the only thing worth
   comparing is length. That also keeps the whole tab inside the two tokens
   the rest of the site already uses, and out of reach of every
   colour-blindness question a categorical palette would raise. */

.spread-note {
  margin: 0 0 1rem;
  color: var(--muted);
  font-size: var(--t-sm);
}

/* The grid counts its own columns, like the slate does -- three across the
   rail, two on a tablet, one on a phone, and no breakpoint to keep in step
   with the rest of the stylesheet. 17rem is the narrowest a track can get
   before an eleven-row shots chart stops reading as a shape.

   Four charts across three columns leaves Shots alone on the second row with
   two empty columns beside it, and that is on purpose rather than unfinished:
   letting it span the row would give it a wider track than the other three,
   and the four are drawn on one shared scale precisely so that a bar's length
   means the same thing in each of them. Equal tracks are the point; a tidy
   rectangle is not. */
.spreads {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: 1.4rem 1.8rem;
  align-items: start;
}

.spread h3 {
  margin: 0 0 0.5rem;
  font-size: var(--t-sm);
  font-weight: 600;
}

.spread dl { margin: 0; display: flex; flex-direction: column; gap: 0.25rem; }

/* Bucket label, then everything the bar row is made of. */
.spread dl > div {
  display: grid;
  grid-template-columns: 1.4rem 1fr;
  align-items: center;
  gap: 0.5rem;
}

.spread dt {
  color: var(--muted);
  font-size: var(--t-xs);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Fixed columns for the two numbers rather than flex, so the counts line up
   down the chart and across all four of them -- a column of right-aligned
   tabular figures is read at a glance, and a ragged one is read row by row. */
.spread dd {
  display: grid;
  grid-template-columns: 1fr 1.5rem 2rem;
  align-items: center;
  gap: 0.45rem;
  margin: 0;
}

/* Drawn under every bar including the empty ones: it is what makes the scale
   shared across the four charts visible. Without it a short bar in a flat
   chart and a short bar in a peaked one look alike. */
.spread .track {
  height: 0.6rem;
  border-radius: 2px;
  background: var(--rule);
}

/* Square where it leaves the baseline, rounded at the end that carries the
   value. A bar rounded at both ends reads as a pill -- a thing with a length
   of its own -- rather than as a measurement from zero. */
.spread .fill {
  display: block;
  height: 100%;
  border-radius: 0 4px 4px 0;
  background: var(--accent);
}

.spread .nights {
  text-align: right;
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
}

.spread .share {
  text-align: right;
  color: var(--muted);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}

/* A bucket nobody landed in is still a row -- the gap in a distribution is
   part of its shape, and dropping the row would silently renumber the axis.
   It recedes instead. */
.spread .none dt,
.spread .none .nights { color: var(--muted); opacity: 0.55; }

/* ----- small screens ---------------------------------------------------- */

/* Most of the site needs no breakpoint at all, which is deliberate: every
   table scrolls inside its own box and the flexed rows wrap. What is left is
   the handful of places where a desktop measurement is simply wrong on a
   phone, plus one for the slate, which is the only layout here holding four
   columns on a row.

   Two breakpoints. At 900px the club's name gives up its column -- it is the
   widest thing on a game's row and the first that cannot be afforded -- and
   drops under the tri-code with the players following it. At 640px the rest
   of the site narrows. */
@media (max-width: 900px) {
  .side { grid-template-columns: 2.7rem 1fr auto; }
}
@media (max-width: 640px) {
  /* 1.5rem each side is 48px of a 360px screen, an eighth of it, spent on
     margin. The card is the thing worth showing. */
  main { padding: 1rem; }
  footer { padding: 0 1rem 2rem; }

  .topbar {
    /* The search drops to its own line under the brand and takes the width. */
    flex-wrap: wrap;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
  }

  .search { max-width: none; }

  .hero { padding: 1rem 0 1.5rem; }
  .hero h1 { font-size: var(--t-xl); }

  /* 5.6rem of clock beside a 360px screen is a sixth of it spent on the rail. */
  .slot { grid-template-columns: 4.4rem 1fr; gap: 0 0.9rem; }
  .hung { padding-left: 1.1rem; }

  .player-head {
    flex-direction: column;
    padding: 1rem;
  }

  /* 96px of face beside nothing, on a column layout, is a lot of the screen
     gone before the name. */
  .avatar.headshot { width: 72px; height: 72px; font-size: var(--t-lg); }

  /* A 2rem column gap puts two vitals per row on a phone with a ragged gutter
     down the middle; at 1.25rem three fit and the rows line up. */
  .vitals { gap: 0.4rem 1.25rem; }

  .next-game .when { margin-left: 0; width: 100%; }

  /* The card keeps its edge; what it spends on margin is what a phone has
     least of. */
  .tabs { padding: 0 1rem; }
  .tab-body { padding: 1rem; }

  /* Stacked, because a label and a menu side by side puts two of these on one
     row and neither of them wide enough to read its own value. */
  .scope { gap: 0.6rem 1rem; }
  .field { flex: 1 1 100%; }
  .field select { flex: 1; min-width: 0; }
}

/* A finger is not a mouse pointer, and these are all links sized for one.
   Keyed on the pointer rather than the width, because a 1024px tablet is
   touched and a 600px window on a laptop is not. */
@media (pointer: coarse) {
  .names a { padding: 0.3rem 0; }
  .tabs a { padding: 0.95rem 0 0.85rem; }
  .splits a { padding: 0.45rem 0.8rem; }
  .scope select,
  .scope button { padding: 0.45rem 0.6rem; }
  .prefs select,
  .prefs button { padding: 0.45rem 0.6rem; }
  .suggestions li a { padding: 0.6rem 0.5rem; }
}
