/* LunoSport — design tokens lifted from the Sports Alerts iOS app.
   Colors are the values in SportsAlerts/Colors.xcassets. The app sets Mont as
   its primary face and Avenir as its secondary (UIFont+Additions.swift); the
   site is on Avenir for now, with a web stand-in — see the body rule below.

   Every color used anywhere below is a token, so the dark block near the end
   only has to restate the palette rather than re-style components. If you find
   yourself writing a literal color in a rule, add a token instead — otherwise
   it silently stays light-mode-only. */

:root {
  /* text */
  --ink: #1A1A1A;
  --ink-muted: #404040;         /* TopViewText */
  --ink-soft: #6B7A90;
  --ink-strong: #0A1628;

  /* surfaces */
  --page: #E0E0E0;              /* MainBackground */
  --surface: #FFFFFF;
  --surface-hover: #F7F9FC;
  --surface-sunken: #F2F5F9;
  --section: #E2E7ED;           /* SectionHeaderBackground */
  --border: #DCE3ED;
  --hairline: rgba(0, 0, 0, 0.07);
  --hairline-soft: rgba(0, 0, 0, 0.05);
  --chip: rgba(0, 0, 0, 0.07);
  --shadow: 0 1px 3px rgba(142, 142, 142, 0.10), 0 0 0 1px rgba(142, 142, 142, 0.10);

  /* brand */
  --header: #016FFF;            /* HeaderBackground */
  --header-ink: #F2F2F2;        /* HeaderTitle */
  --header-ink-active: #FFFFFF;
  --tint: #004BFF;              /* PrimaryTint */
  --tint-hover: #0038C2;
  --tint-wash: rgba(1, 111, 255, 0.10);
  --flash: rgba(1, 111, 255, 0.18);

  /* status */
  --green: #007F00;             /* GreenText */
  --red: #CD000A;               /* RedText */
  --gold: #F3C53A;
  --gold-ink: #8A6D0B;
  --on-accent: #FFFFFF;

  /* football field */
  --field: #E8ECF1;
  --field-line: rgba(0, 0, 0, 0.10);
  --field-50: rgba(0, 0, 0, 0.26);

  /* basketball court: a pale hardwood, its boards, white lines */
  --court: #EEDCC0;
  --court-plank: rgba(120, 80, 30, 0.06);
  --court-line: rgba(255, 255, 255, 0.95);
  --court-label: rgba(255, 255, 255, 0.90);
  --court-ring: #FFFFFF;
  --court-shadow: rgba(60, 40, 10, 0.30);

  /* incidental */
  --chev: #C6D0DC;
  --score-lost: #7E8591;
  --logo-fallback: #8A9298;
  --base-empty: #FFFFFF;
  --base-stroke: #B4BDC4;

  /* footer — already dark in both themes, so it only shifts a little */
  --foot-bg: #1F2124;
  --foot-ink: #C2CDD5;
  --foot-ink-body: #A8B0B8;
  --foot-ink-dim: #8A9298;
  --foot-border: #2E3439;
  --foot-inset: #16181A;

  color-scheme: light dark;
}

* { box-sizing: border-box; }

/* Open Sans, served from this origin (fonts/faces.css, fetched by
   tools/get-font.ps1), and the same on every device: it comes first so a Mac
   does not get Avenir while Windows gets the webfont, which is how it was until
   2026-09-17. Jeff chose it from design/font-tryout.html for the Segoe look —
   it is by Segoe UI's designer — after Nunito Sans went soft in the small caps
   and the digits. The system faces after it are only for a visitor with the
   webfont blocked, so they land somewhere sane rather than on Arial. */
body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: "Open Sans", "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased;
}

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

.wrap { max-width: 1160px; margin: 0 auto; padding: 0 20px; }
/* A page with no league has no nav row under the league pills, and its heading
   would otherwise start immediately below the blue band. */
.wrap.nobar { padding-top: 20px; }
.card { background: var(--surface); border-radius: 10px; box-shadow: var(--shadow); }
.sh { background: var(--section); color: var(--ink-muted); font-size: 16px; font-weight: 600; padding: 7px 16px; }
.sub { color: var(--ink-muted); font-size: 12px; }
.grn { color: var(--green); }
.red { color: var(--red); }
.scroller { overflow-x: auto; }

/* ---- band 1: site-wide ---- */
.band1 { background: var(--surface); border-bottom: 1px solid var(--border); }
.b1row { height: 58px; display: flex; align-items: center; gap: 22px; }
.b1row .mark { height: 40px; width: auto; display: block; }
.hspacer { flex-grow: 1; }
.hsep { width: 1px; height: 20px; background: var(--border); flex-shrink: 0; }
.corp { color: var(--ink-soft); font-size: 12.5px; }
.ghost { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--chev); color: var(--ink-strong);
         font-size: 12.5px; font-weight: 600; padding: 6px 12px; border-radius: 7px; }
.ghost:hover { border-color: var(--tint); color: var(--tint); text-decoration: none; }

/* ---- band 2: league selector, scrolls inside the content column ---- */
.band2 { background: var(--header); }
.band2 .inner { max-width: 1160px; margin: 0 auto; padding: 0 12px; }
/* Padded at each end as wide as the fade, so the first and last pills sit
   clear of it with the strip scrolled to that end; the fade is for a pill
   scrolled part way out of view, not for one at the end of the row. At 8px
   the end pills always lost their outer edge to it. */
.leaguescroll { display: flex; align-items: center; gap: 4px; overflow-x: auto; scrollbar-width: none; padding: 6px 18px;
                -webkit-mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
                mask-image: linear-gradient(to right, transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%); }
.leaguescroll::-webkit-scrollbar { display: none; }
.pill { height: 38px; border-radius: 12px; display: flex; align-items: center; justify-content: center;
        font-size: 18px; font-weight: 400; flex-shrink: 0; color: var(--header-ink); gap: 6px; padding: 0 8px; min-width: 80px; }
.pill:hover { text-decoration: none; color: var(--header-ink-active); }
/* A college league is two rows - "NCAA" over the sport, as the apps draw it -
   and a little wider, "football" being longer than "NFL". */
.pill .pl { display: flex; flex-direction: column; align-items: center; line-height: 1.05; }
.pill.two { min-width: 98px; }
.pill.two .pt { font-size: 15px; }
.pill .ps { font-size: 12px; }
/* The sport beside the name, from the symbols at the top of the band. Outlines
   in the pill's colour; on the chosen pill's light chip they take the header blue. */
.picon { width: 16px; height: 16px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 1.4;
         stroke-linecap: round; stroke-linejoin: round; }
.pill.on .picon { color: var(--header); }
/* A flag is drawn in its own colours, a little wider than tall, and dimmed on a
   pill that is not the current one, as the apps dim theirs. */
.picon.flag { stroke: none; width: 17px; height: 12px; border-radius: 2px; opacity: .72; }
.pill.on .picon.flag { opacity: 1; }
/* These three sit on the header blue, which is blue in both themes — so they are
   defined against that surface rather than the palette. The dark block nudges the
   chip's opacity because the header goes darker there. */
.pill.on { background: rgba(242, 242, 242, 0.80); color: #000000; font-weight: 600; }
.pill.on:hover { color: #000000; }
.sep { width: 1px; height: 20px; background: rgba(255, 255, 255, 0.35); flex-shrink: 0; }

/* ---- band 3: league-scoped ---- */
.band3 { background: var(--surface); border-bottom: 1px solid var(--border); }
/* Scrolls rather than overflowing the page. Six links need about 437px and a
   phone gives 375, and unlike the corporate links in band 1 these have nowhere
   else to go — so the strip scrolls and every section stays reachable. The rule
   is unconditional because overflow-x: auto does nothing until it has to. */
.b3row { display: flex; align-items: stretch; gap: 26px; height: 44px;
         overflow-x: auto; scrollbar-width: none; }
.b3row::-webkit-scrollbar { display: none; }
.b3row a { display: flex; align-items: center; font-size: 13.5px; font-weight: 600; color: var(--ink-muted);
           border-bottom: 2px solid transparent; flex-shrink: 0; white-space: nowrap; }
.b3row a:hover { color: var(--ink-strong); text-decoration: none; }
.b3row a.on { color: var(--tint); border-bottom-color: var(--tint); }
/* Football's five — Scores, Schedule, Standings, Teams, Leaders — are 261px of
   words; at the full gap they overrun a 375px phone and Leaders is cut at the
   edge. At this one they fit a 360px phone outright. */
@media (max-width: 420px) {
  .b3row { gap: 16px; }
}

/* ---- page heading + date / week rail ---- */
.h1 { font-size: 30px; font-weight: 700; letter-spacing: -0.02em; margin: 0 0 3px 2px; }
/* A second block on the same page — the playoff race under the divisions. */
.h2 { font-size: 21px; font-weight: 700; letter-spacing: -0.015em; margin: 28px 0 10px 2px; }
.railstrip { display: flex; align-items: stretch; overflow: hidden; }
.railarrow { display: flex; align-items: center; padding: 0 15px; color: var(--ink-muted); flex-shrink: 0; }
.railarrow:hover { background: var(--surface-sunken); text-decoration: none; color: var(--tint); }
.railarrow.l { border-right: 1px solid var(--hairline); }
.railarrow.r { border-left: 1px solid var(--hairline); }
.railtrack { display: flex; flex-grow: 1; overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
             scroll-behavior: smooth; outline: none; }
.railtrack::-webkit-scrollbar { display: none; }
.railcell { flex: 0 0 10%; text-align: center; padding: 8px 6px; line-height: 1.35; white-space: nowrap;
            font-size: 11.5px; color: var(--ink-soft); border-right: 1px solid var(--hairline-soft); }
.railcell:last-child { border-right: none; }
.railcell b { display: block; font-size: 14px; font-weight: 600; color: var(--ink); }
.railcell:hover { background: var(--surface-hover); text-decoration: none; }
.railcell.on { background: var(--tint-wash); color: var(--tint); }
.railcell.on b { color: var(--tint); }

/* Today's slate. Selection is already blue, so this needs to read as something
   else entirely — a gold cap catches the eye while scrolling, and being a
   box-shadow it costs no layout. Both marks can sit on one cell at once. */
.railcell.today { box-shadow: inset 0 3px 0 var(--gold); }
.railcell.today span { color: var(--gold-ink); font-weight: 700; letter-spacing: 0.03em; }
.railcell.on.today span { color: var(--tint); }

/* ---- section headings ---- */
.sect { font-size: 12px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-soft);
        margin: 22px 0 9px 4px; display: flex; align-items: center; gap: 8px; }
/* Green, matching the live status in the game strip: red is reserved for a game
   that has been called off. */
.livedot { width: 7px; height: 7px; border-radius: 4px; background: var(--green); }

/* ---- game rows ----
   The hairlines are cell borders, not a grid background showing through 1px gaps.
   With the gap technique an incomplete trailing row paints its empty cells in the
   grid's background color, so a section with four games in a three-wide grid
   ends in two gray boxes. Borders leave those cells as card. */
.glist { display: grid; grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); }
.grow { background: var(--surface); display: block; padding: 12px 14px; color: inherit;
        border-right: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); }

/* A section holding fewer games than there are columns shrinks to fit them rather
   than stretching one game across the page — three-up is the width the row was
   designed at. The card clips its own outer edges, so the trailing borders land on
   the boundary and are not seen. */
.card.fit { max-width: calc(100% * var(--cols) / 3); }
.grow:hover { text-decoration: none; background: var(--surface-hover); }
.gmain { display: flex; align-items: center; gap: 9px; }
.gteams { flex-grow: 1; min-width: 0; }
/* 6px rather than 8: with a bat between the name and the score there are three
   gaps competing with the team name, which is the part that must not clip. */
.gteam { display: flex; align-items: center; gap: 6px; height: 27px; }
/* The abbreviation is the fallback layer, sitting under the logo on a coloured
   chip. When a logo is actually present it is a transparent PNG that belongs
   directly on the card, so the chip and the letters underneath are both dropped —
   otherwise the plate shows through the artwork. If the image 404s its <picture>
   removes itself and the chip and letters come back. */
.glogo { position: relative; width: 22px; height: 22px; border-radius: 5px; flex-shrink: 0;
         display: flex; align-items: center; justify-content: center;
         font-size: 8.5px; font-weight: 700; color: var(--on-accent); background: var(--logo-fallback); }
.glogo:has(picture) { background: transparent; color: transparent; }
.glogo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
/* The wrapper takes the free space, not the name — so the markers sit against the
   end of the name instead of being pushed across to the score. */
.gnamewrap { display: flex; align-items: center; gap: 6px; flex-grow: 1; min-width: 0; }
.gname { font-size: 15px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gname.won { font-weight: 600; }
.grec { font-size: 12px; color: var(--ink-soft); flex-shrink: 0; }
.gscore { font-size: 17px; font-variant-numeric: tabular-nums; width: 26px; text-align: right; flex-shrink: 0;
          transition: background-color .12s ease-out; }
.gscore.lost { color: var(--score-lost); font-weight: 300; }
.gscore.won { font-weight: 700; }
/* Set by the live poll when a score changes. A class rather than an inline style
   so the highlight color comes from the palette and follows the theme. */
.gscore.flash { background-color: var(--flash); }
/* Column, so the runners stack under the inning and count rather than sitting
   beside them. */
.gstat { display: flex; flex-direction: column; align-items: flex-end; text-align: right;
         font-size: 12.5px; color: var(--ink-muted); flex-shrink: 0; min-width: 54px; line-height: 1.4; }
.gchev { color: var(--chev); flex-shrink: 0; }
/* Inset from the row's edges rather than flush to them — the rule spans the cell
   but the numbers read better pulled in. */
.gline { font-size: 11.5px; color: var(--ink-soft); border-top: 1px solid var(--hairline); margin-top: 9px;
         padding: 7px 12px 0; display: flex; justify-content: space-between; }
/* A soccer row's three-way price reads left to right the way the apps print it,
   the label, then home, away and the draw, rather than pushed to the row's ends. */
.gline.godds { justify-content: flex-start; gap: 16px; }
.gline .golabel { font-style: italic; }

.empty { padding: 26px 18px; text-align: center; color: var(--ink-soft); font-size: 14px; }

/* ---- game page ---- */
/* The h1 carries the matchup for search engines and screen readers; the card
   below says the same thing visually, so the heading itself is not painted. */
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0);
      clip-path: inset(50%); white-space: nowrap; }
.crumbs { font-size: 12.5px; color: var(--ink-soft); margin: 14px 0 10px 2px; }
.crumbs a { color: var(--ink-soft); }
.crumbs a:hover { color: var(--tint); }

.gdhead { padding: 20px 18px 0; }
.gdteams { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 16px; }
.gdteam { display: flex; align-items: center; gap: 12px; min-width: 0; color: inherit; }
/* A link, but the card is not a list of links — it tints only when pointed at. */
.gdteam:hover { text-decoration: none; }
.gdteam:hover .gdteamname { color: var(--tint); }
/* Placed explicitly: the markup renders both teams together and then the middle,
   so left to right would otherwise come out away, home, time. The row is pinned
   as well — naming only the column lets auto-placement push the later item onto
   a second row. */
.gdteam.a { grid-column: 1; grid-row: 1; }
.gdmid { grid-column: 2; grid-row: 1; }
.gdteam.h { grid-column: 3; grid-row: 1; }
/* The home side mirrors so both teams read outward from the score. */
.gdteam.h { flex-direction: row-reverse; text-align: right; }
.gdlogo { position: relative; width: 46px; height: 46px; border-radius: 8px; flex-shrink: 0;
          display: flex; align-items: center; justify-content: center;
          font-size: 13px; font-weight: 700; color: var(--on-accent); background: var(--logo-fallback); }
.gdlogo:has(picture) { background: transparent; color: transparent; }
.gdlogo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.gdname { min-width: 0; }
.gdteamname { font-size: 19px; font-weight: 600; line-height: 1.25; }
.gdrec { font-size: 12.5px; color: var(--ink-soft); margin-top: 2px; }
.gdmid { text-align: center; flex-shrink: 0; }
.gdtime { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; }
.gdscore { font-size: 26px; font-weight: 700; font-variant-numeric: tabular-nums; }
.gdsub { font-size: 12px; color: var(--ink-soft); margin-top: 2px; }
.gdvenue { margin: 16px -18px 0; padding: 11px 18px; border-top: 1px solid var(--hairline);
           text-align: center; font-size: 12.5px; color: var(--ink-soft); }

/* Odds is two short strings and a link, the forecast a paragraph — so they get
   different widths, and align-items keeps each card its own height instead of
   stretching the small one to match the tall one. */
.gdcols { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
          align-items: start; gap: 14px; margin-top: 14px; }
.gdpanel { overflow: hidden; }
.gdbody { padding: 12px 16px; font-size: 13px; }
.gdodds { display: flex; gap: 26px; }
.gdlabel { color: var(--ink-soft); margin-right: 7px; }
.gdmore { display: inline-block; margin-top: 9px; font-size: 12.5px; }
.gdwx { display: flex; align-items: flex-start; gap: 12px; }
.gdcond { font-size: 12px; text-transform: capitalize; color: var(--ink-muted); margin-bottom: 4px; }
.gdprose { margin: 0; line-height: 1.55; color: var(--ink-muted); }

/* Weather glyphs, coloured by what they mean rather than all one tint. */
.wx { width: 30px; height: 30px; flex-shrink: 0; }
.wx.sun { color: #E8A317; }
.wx.cloud { color: #8B95A3; }
.wx.rain { color: #3B8EDE; }
.wx.wind { color: #8B95A3; }
.wx.snow { color: #6FA8D6; }

.card + .card, .gdcols + .card { margin-top: 14px; }
/* Not where cards sit side by side, though: there the second is a neighbour rather
   than the one below, and the stacking margin drops it out of line with the first.
   Each of these grids has its own gap, which spaces the pair — including when they
   stack on a phone, so this comes off at every width. */
.gdcols .card + .card, .boxcols .card + .card { margin-top: 0; }
.gdsplit { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.gdhalf { padding: 12px 16px 14px; }
.gdhalf + .gdhalf { border-left: 1px solid var(--hairline); }
.gdteamlabel { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
               color: var(--ink-soft); margin-bottom: 7px; }
.gdperson { display: flex; align-items: center; gap: 11px; }
.gdpinfo { min-width: 0; }
/* The circle stays when the headshot is missing — the image removes itself, so
   what is left reads as an empty avatar rather than a broken picture. */
.shot { width: 44px; height: 44px; border-radius: 22px; flex-shrink: 0; overflow: hidden;
        background: var(--chip); display: block; }
.shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gdplayer { font-size: 15px; font-weight: 600; display: inline-block; }
a.gdplayer { color: var(--ink); }
a.gdplayer:hover { color: var(--tint); }
.gdstats { font-size: 12.5px; color: var(--ink-soft); margin-top: 3px; }
.gdmuted { color: var(--ink-soft); font-size: 13px; }

/* table-layout: fixed so the colgroup governs. Auto layout hands the name column
   every spare pixel and crushes the six stat columns together on the right. */
.lineup { width: 100%; border-collapse: collapse; font-size: 12.5px; table-layout: fixed; }
.lineup .c-ord { width: 24px; }
.lineup .c-name { width: auto; }
.lineup .c-pos { width: 42px; }
.lineup .c-b { width: 30px; }
.lineup .c-avg { width: 52px; }
.lineup .c-hr { width: 38px; }
.lineup .c-rbi { width: 42px; }
.lineup th { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
             color: var(--ink-soft); text-align: center; padding: 0 4px 6px; white-space: nowrap; }
.lineup td { padding: 5px 4px; text-align: center; border-top: 1px solid var(--hairline);
             white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lineup th.l, .lineup td.l { text-align: left; padding-left: 2px; }
.lineup th.n, .lineup td.n { text-align: right; font-variant-numeric: tabular-nums; }
.lineup td.ord { color: var(--ink-soft); }

/* ---- live baseball ----
   Two columns of roughly half the page. Everything here is narrow by nature — a
   line score is twelve small columns, the at-bat is a plot and a few lines — and
   run full width they are mostly whitespace. */
/* The margin is on the row itself: it follows the score card, and the card-to-card
   rule below only matches a .card, which this grid is not. */
.livecols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
            gap: 14px; align-items: start; margin-top: 14px; }
.livecol { min-width: 0; }
/* Every section after the first, whether it is a bare card or one inside the
   wrapper the live poll swaps by key — an adjacency rule on cards alone left
   the wrapped ones touching. */
.livecol > * + * { margin-top: 14px; }

/* Line score. Fixed layout with the club column taking a quarter and the innings
   splitting the rest evenly — left to itself the club column absorbs all the slack
   and the innings end up huddled against the right edge of a wide card. The
   min-width lets it scroll rather than crush on a narrow phone. */
.linescore { width: 100%; min-width: 340px; table-layout: fixed; border-collapse: collapse;
             font-size: 13px; font-variant-numeric: tabular-nums; }
.linescore th, .linescore td { text-align: center; padding: 7px 0; }
.linescore th { font-size: 11px; font-weight: 600; color: var(--ink-soft);
                border-bottom: 1px solid var(--hairline); }
.linescore th.l, .linescore td.l { width: 25%; text-align: left; padding-left: 16px;
                                   white-space: nowrap; }
.linescore td.l { display: flex; align-items: center; gap: 7px; font-weight: 600; }
.lslogo { width: 20px; height: 20px; flex-shrink: 0; }
.lslogo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.linescore td { border-bottom: 1px solid var(--hairline-soft); }
.linescore tbody tr:last-child td { border-bottom: none; }
/* The inning being played, tinted as the app tints it. */
.linescore td.now { color: var(--tint); font-weight: 700; }
/* R, H and E are the summary, so they are set apart from the innings and the R
   is the one that gets the weight. */
.linescore .tot { font-weight: 700; }
.linescore th.tot { color: var(--ink-muted); }
.linescore .tot:first-of-type { border-left: 1px solid var(--hairline); }

/* Live odds to win: one bar, each club in its own colour.

   The gap between the two is the point. Painted edge to edge, two clubs with
   similar colours — and there are plenty, the Reds against the Cardinals say —
   read as one continuous bar with no telling where the split falls. The card
   shows through the gap, so the boundary is always visible whatever the colours. */
.winpct { padding: 14px 16px 16px; border-top: 1px solid var(--hairline); }
.wpbar { display: flex; gap: 3px; height: 12px; background: var(--surface); }
.wpfill { display: block; height: 100%; background: var(--c, var(--ink-soft)); }
.wpfill:first-child { border-radius: 6px 0 0 6px; }
/* The home share is whatever is left, so it is not given a width of its own —
   the two would otherwise have to add up across a rounding boundary. */
.wpfill.h { flex: 1; border-radius: 0 6px 6px 0; }
.wprow { display: flex; justify-content: space-between; align-items: baseline;
         margin-top: 8px; font-size: 12px; color: var(--ink-muted); }
.wplabel { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase;
           color: var(--ink-soft); }

/* The at-bat, stacked the way the app stacks it: the plot at the top with the
   batter beside it, then the count, then who is throwing and who is due up. In a
   half-width column there is no room to put the two side by side, and stacking is
   what the app does anyway. */
.atbat { padding: 14px 18px 16px; }
.zonewrap { display: flex; align-items: center; justify-content: center; gap: 12px; }
/* The batter's card goes on his own side of the plate: a right-handed batter
   stands to the catcher's left, which is the left of the plot as drawn. */
.zonewrap.rhb .bcard { order: -1; }
.zone { width: 132px; height: auto; display: block; flex-shrink: 0; }
.zplot { fill: var(--tint-wash); stroke: var(--tint); stroke-opacity: 0.45; stroke-width: 0.75; }
.zgrid { stroke: var(--tint); stroke-opacity: 0.45; stroke-width: 0.6; stroke-dasharray: 3 3; }
.zbox { fill: var(--red); fill-opacity: 0.08; stroke: var(--ink-soft); stroke-width: 1.3; }
.zthird { stroke: var(--ink-soft); stroke-opacity: 0.5; stroke-width: 0.7; stroke-dasharray: 3 3; }
.zplate { fill: var(--ink-soft); fill-opacity: 0.35; }
.pitch text { fill: #FFFFFF; font-size: 7px; font-weight: 700; text-anchor: middle; }
.pitch.ball circle { fill: var(--green); }
.pitch.strike circle { fill: var(--red); }
.pitch.inplay circle { fill: var(--tint); }
.pitch.other circle { fill: var(--ink-soft); }

.bcard { width: 92px; flex-shrink: 0; text-align: center; padding: 10px 8px 11px;
         background: var(--surface-sunken); border-radius: 10px; }
.bcard .shot { margin: 0 auto 6px; }
.bname { display: block; font-size: 13px; font-weight: 600; line-height: 1.3; }
.bline { font-size: 12px; color: var(--ink-soft); margin-top: 2px; }

/* The count sits above the plot, centred on it, with the diamond alongside. The
   space below separates the state of the inning from the at-bat within it. */
.countrow { display: flex; align-items: center; justify-content: center; gap: 14px;
            margin-bottom: 22px; }
.cnt { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums;
       display: inline-flex; align-items: center; }
.cnt i { font-style: normal; font-size: 12px; font-weight: 600; color: var(--ink-soft);
         margin-right: 4px; }
/* The pips repeat the outs count as a glance. Set well clear of the numbers, so
   they read as their own thing rather than as a fourth digit in the count. */
.outs { display: inline-flex; align-items: center; gap: 6px; margin-left: 26px; }
.outs b { width: 9px; height: 9px; border-radius: 50%; display: block;
          border: 1.5px solid var(--ink-soft); box-sizing: border-box; }
.outs b.on { background: var(--ink); border-color: var(--ink); }
.bases.lg { margin-left: 8px; }

.pitching { display: flex; align-items: center; gap: 10px; margin-top: 14px;
            padding-top: 13px; border-top: 1px solid var(--hairline); }
.plabel { font-size: 11px; font-weight: 700; letter-spacing: 0.08em;
          text-transform: uppercase; color: var(--ink-soft); margin-bottom: 1px; }
.pname { font-size: 14px; font-weight: 600; }
.phand { font-size: 12px; color: var(--ink-soft); margin-left: 6px; }

/* Two label/value pairs, aligned — a definition list is what this actually is. */
.upnext { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 3px 10px;
          margin: 12px 0 0; font-size: 13px; }
.upnext dt { color: var(--ink-soft); white-space: nowrap; }
.upnext dd { margin: 0; min-width: 0; }

.lastplay { font-size: 13px; color: var(--ink-muted); line-height: 1.5;
            padding: 11px 18px 13px; border-top: 1px solid var(--hairline); }
.lastplay span { font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
                 font-size: 11px; color: var(--ink-soft); margin-right: 8px; }

/* ---- odds ---- */
/* Clubs down the side, markets across, the way the odds app lays them out. */
.ingame { width: 100%; border-collapse: collapse; font-size: 14px;
          font-variant-numeric: tabular-nums; }
.ingame th { font-size: 12px; font-weight: 400; color: var(--ink-muted); padding: 6px 8px 8px; }
.ingame td { padding: 7px 8px; text-align: center; white-space: nowrap; }
.ingame th.l, .ingame td.l { width: 46px; padding-left: 16px; text-align: left; }
.iglogo { width: 26px; height: 26px; display: inline-block; }
.iglogo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.ingame tbody tr:last-child td { padding-bottom: 12px; }

/* Up is not good and down is not bad — the arrow says only which way the number
   went, so both directions take the same colour. Green, not the app's red-to-
   yellow ramp: red on this site means a game called off. Age is carried in the
   opacity instead, so a move still reads as fresh or stale. */
.omove { font-size: 10px; margin-left: 6px; color: var(--green); vertical-align: 1px; }
.omove.recent { opacity: 0.75; }
.omove.fading { opacity: 0.45; }
/* A move in the last few seconds pulses, as the app's symbol effect does. */
.omove.fresh { animation: omovepulse 1.1s ease-in-out infinite; }
@keyframes omovepulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(1.25); }
}

/* Opening beside closing, two rows per market — away above home. */
.oclose { width: 100%; border-collapse: collapse; font-size: 13px;
          font-variant-numeric: tabular-nums; }
.oclose th { font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
             color: var(--ink-soft); padding: 8px 10px; text-align: right; }
.oclose td { padding: 5px 10px; text-align: right; }
.oclose th.l, .oclose td.l { text-align: left; padding-left: 16px; color: var(--ink-muted);
                             font-weight: 600; white-space: nowrap; }
.oclose th.t, .oclose td.t { text-align: left; color: var(--ink-soft); font-weight: 600; }
.oclose td:last-child { padding-right: 16px; }
.oclose thead th { border-bottom: 1px solid var(--hairline); }
/* The rule goes between markets, not between a market's own two rows. */
.oclose td.l { border-top: 1px solid var(--hairline-soft); }
.oclose tbody tr:first-child td { border-top: none; }
.oclose td.l + td, .oclose td.l ~ td { border-top: 1px solid var(--hairline-soft); }
.oclose tbody tr:first-child td.l ~ td { border-top: none; }

/* ---- scoring plays ----
   A column per club with the abbreviations in the header, so the score reads down
   the page rather than as a running "6–3" beside every play. */
.sphead { display: flex; align-items: baseline; justify-content: space-between; }
.sphead a { font-size: 12.5px; font-weight: 400; }
.sptable { width: 100%; border-collapse: collapse; font-size: 13px; }
.sptable th { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
              color: var(--ink-soft); padding: 7px 8px 6px; text-align: center; width: 40px;
              border-bottom: 1px solid var(--hairline); }
.sptable th.l { text-align: left; padding-left: 16px; width: auto; }
.sptable td { padding: 9px 8px; text-align: center; font-weight: 700;
              font-variant-numeric: tabular-nums; color: var(--ink-soft); vertical-align: top; }
.sptable td.l { text-align: left; padding-left: 16px; font-weight: 400;
                display: flex; gap: 10px; align-items: start; }
.sptable tr + tr td { border-top: 1px solid var(--hairline-soft); }
/* The side that just scored is the number that changed. */
.sptable td.scored { color: var(--ink); }
.sptable tbody tr:last-child td { padding-bottom: 12px; }
.splogo { width: 22px; height: 22px; flex-shrink: 0; }
.splogo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.spbody { min-width: 0; color: var(--ink-muted); line-height: 1.45; }
.spmeta { display: block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em;
          text-transform: uppercase; color: var(--ink-soft); }
.pbplink { margin: 0 0 12px 16px; }

/* ---- soccer: the match timeline ----
   The home side's events on the left, the away side's mirrored on the right
   with the minute at the outer edge, and a marker with the score at the break,
   the way the apps draw a match's facts. */
.mtl { list-style: none; margin: 0; padding: 6px 0 8px; }
.mtlev { display: flex; align-items: flex-start; gap: 12px; padding: 10px 16px; }
.mtlev.a { flex-direction: row-reverse; text-align: right; }
.mtlmin { flex-shrink: 0; width: 44px; font-weight: 700; font-size: 15px; color: var(--ink); }
.mtlev.a .mtlmin { text-align: right; }
.mtlev.h .mtlmin { text-align: left; }
.mtlicon { flex-shrink: 0; width: 26px; display: flex; justify-content: center; padding-top: 1px; }
.mtlball { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.4;
           stroke-linecap: round; stroke-linejoin: round; color: var(--ink); }
.mtlball.missed { color: var(--red); }
.mtlcard { display: inline-block; width: 15px; height: 20px; border-radius: 3px; }
.mtlcard.y { background: #f2c53d; }
.mtlcard.r { background: #d9342b; }
.mtlbody { min-width: 0; display: flex; flex-direction: column; gap: 2px; font-size: 15px; line-height: 1.3; }
.mtlwho { color: var(--ink); }
.mtlscore { color: var(--ink-muted); margin-left: 4px; }
.mtlscore b { font-weight: 400; }
.mtlscore b.g { color: var(--green); font-weight: 700; }
.mtlsub { font-size: 13px; color: var(--ink-soft); }
/* A substitution: the player coming on above the one going off, the arrows
   beside them in the same colours. */
.subin { color: var(--green); }
.subout { color: var(--red); }
.subdots { display: flex; flex-direction: column; gap: 4px; }
.subdots i { display: inline-flex; width: 18px; height: 18px; border-radius: 50%; align-items: center;
             justify-content: center; font-style: normal; font-size: 12px; font-weight: 700; color: #fff; }
.subdots i.in { background: var(--green); }
.subdots i.out { background: var(--red); }
/* The break: a ring with HT on a line across the timeline, the score beside it. */
.mtlhalf { display: flex; align-items: center; gap: 10px; margin: 4px 0; padding: 0 16px; font-size: 14px;
           color: var(--ink); }
.mtlhalf::before, .mtlhalf::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }
.mtlhalf::before { margin-right: 6px; }
.mtlhalf::after { margin-left: 6px; }
.mtlht { display: inline-flex; width: 34px; height: 34px; border: 2px solid var(--ink); border-radius: 50%;
         align-items: center; justify-content: center; font-size: 11px; font-weight: 700; flex-shrink: 0; }
.mtlhts { font-weight: 600; white-space: nowrap; }

/* ---- final: decisions and box score ---- */
.decs { padding: 4px 0 8px; }
.dec { display: flex; align-items: flex-start; gap: 11px; padding: 9px 16px; }
.dec + .dec { border-top: 1px solid var(--hairline-soft); }
.decbody { min-width: 0; }
/* The label leads the line: a box score is read for who won and lost first. */
.declbl { font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
          color: var(--ink-soft); margin-right: 7px; }
.declbl.w { color: var(--green); }
.declbl.l { color: var(--red); }
.decname { font-size: 15px; font-weight: 600; }
.decteam { font-size: 12px; color: var(--ink-soft); margin-left: 6px; }
.decline { font-size: 12.5px; color: var(--ink-muted); margin-top: 2px;
           font-variant-numeric: tabular-nums; }

/* ---- recap and highlights ----
   Both link off the site, so both are laid out the same: thumbnail, headline, and
   a line saying where it goes. */
.media { display: flex; gap: 13px; align-items: center; padding: 12px 16px 14px;
         color: inherit; }
.media:hover { text-decoration: none; background: var(--surface-hover); }
.mthumb { position: relative; width: 108px; flex-shrink: 0; aspect-ratio: 16 / 9;
          border-radius: 8px; overflow: hidden; background: var(--surface-sunken);
          display: block; }
.mthumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The play badge marks the reel as a video without loading a player. */
.mthumb.play svg { position: absolute; left: 50%; top: 50%; width: 26px; height: 26px;
                   transform: translate(-50%, -50%); fill: #FFFFFF;
                   filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6)); }
.mbody { min-width: 0; flex-grow: 1; display: flex; flex-direction: column; gap: 3px; }

/* The video stands in for itself until it is asked for. Sized by aspect ratio so
   the swap to the real player does not move anything below it. */
.vwrap { position: relative; aspect-ratio: 16 / 9; background: var(--surface-sunken); }
.vwrap iframe { width: 100%; height: 100%; display: block; border: 0; }
.vfacade { display: block; width: 100%; height: 100%; padding: 0; border: 0; cursor: pointer;
           background: none; position: relative; }
.vfacade img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vplay { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
         width: 56px; height: 38px; border-radius: 9px; background: rgba(18, 18, 18, 0.78);
         display: flex; align-items: center; justify-content: center;
         transition: background-color .12s ease-out; }
.vfacade:hover .vplay { background: var(--red); }
.vplay svg { width: 18px; height: 18px; fill: #FFFFFF; margin-left: 2px; }
/* The player loads in a layer over the thumbnail, unseen and out of the way of
   a press, and shows when it is ready (video.js). */
.vlayer { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.vready .vlayer { opacity: 1; pointer-events: auto; }
/* Pressed, and the player on its way: the button stays red and breathes. */
.vloading .vfacade { cursor: progress; }
.vloading .vplay { background: var(--red); animation: vbreathe 1s ease-in-out infinite alternate; }
@keyframes vbreathe { to { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) { .vloading .vplay { animation: none; } }
.vfallback { position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
             background: rgba(18, 18, 18, 0.78); color: #FFFFFF; padding: 6px 12px;
             border-radius: 7px; font-size: 13px; }
.vmeta { padding: 12px 16px 14px; }
.vmeta .mdesc { margin: 3px 0 0; }
.vmeta .gdmore { margin-top: 8px; }
.mtitle { font-size: 14.5px; font-weight: 600; line-height: 1.35; }
.mdesc { font-size: 12.5px; color: var(--ink-muted); line-height: 1.45; }
.msub { font-size: 11.5px; color: var(--ink-soft); }

/* ---- football final: leaders, team stats, video strip ---- */
.lead + .lead { border-top: 1px solid var(--hairline); }
.leadcat { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
           color: var(--ink-soft); padding: 10px 16px 0; }
.leadhalf { padding: 6px 16px 12px; }
.leadhalf + .leadhalf { border-left: 1px solid var(--hairline); }
.leadname { font-size: 14px; font-weight: 600; }
.leadline { font-size: 12.5px; color: var(--ink-muted); margin-top: 1px;
            font-variant-numeric: tabular-nums; }

/* ---- basketball in progress: who is on the floor ---- */
/* Two columns at every width: five short rows a side fit a phone, and the two
   sides facing each other is how the app draws the card. */
.oncourt { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ochalf { padding: 4px 16px 10px; min-width: 0; }
.ochalf + .ochalf { border-left: 1px solid var(--hairline); }
.octeam { display: flex; align-items: center; gap: 7px; padding: 8px 0 4px;
          font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
          color: var(--ink-soft); }
.ocplayers { list-style: none; margin: 0; padding: 0; }
.ocplayers li { display: flex; gap: 8px; padding: 6px 0; }
.ocplayers li + li { border-top: 1px solid var(--hairline); }
.ocnum { width: 28px; flex-shrink: 0; padding-top: 1px; font-size: 12.5px; color: var(--ink-soft);
         font-variant-numeric: tabular-nums; }
.ocwho { display: flex; flex-direction: column; min-width: 0; }
.ocname { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ocline { font-size: 12.5px; color: var(--ink-muted); margin-top: 1px; font-variant-numeric: tabular-nums; }
.ocempty .ocname { font-weight: 400; color: var(--ink-soft); }

/* The court above the list. The floor and its lines are drawn to scale at any
   width; the players go over them at fixed sizes, so a name stays readable on a
   phone however small the court gets. */
.court { --face: 44px; position: relative; aspect-ratio: 94 / 50; margin: 14px 16px 6px;
         border-radius: 6px;
         background: repeating-linear-gradient(to bottom, transparent 0 11px, var(--court-plank) 11px 12px),
                     var(--court); }
.court svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.court .cl { fill: none; stroke: var(--court-line); }
.court .cl * { stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.court .cl .dash { stroke-dasharray: 4 4; }
.court .lane { opacity: 0.26; }
.court .clogo { opacity: 0.3; }
.court .clogo.dk { display: none; }
@media (prefers-color-scheme: dark) {
  .court .clogo.lt { display: none; }
  .court .clogo.dk { display: inline; }
}
/* Placed by the centre of the face, with the name hanging below it. */
.cp { position: absolute; display: flex; flex-direction: column; align-items: center; gap: 3px;
      transform: translate(-50%, calc(var(--face) / -2)); color: var(--ink); }
a.cp:hover { text-decoration: none; }
a.cp:hover .cpname { color: var(--tint); }
.cpface { position: relative; width: var(--face); height: var(--face); }
.cpimg { position: absolute; inset: 0; border-radius: 50%; overflow: hidden;
         background: var(--c, var(--logo-fallback));
         box-shadow: 0 0 0 2px var(--court-ring), 0 2px 5px var(--court-shadow); }
.cpimg img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
             object-position: 50% 10%; }
/* Under the headshot, so it shows only where there is none. */
.cpno { position: absolute; inset: 0; display: grid; place-items: center;
        font-size: 15px; font-weight: 700; color: var(--on-accent); }
.cpnum { position: absolute; right: -7px; bottom: -4px; min-width: 19px; height: 17px; padding: 0 4px;
         border-radius: 9px; display: grid; place-items: center;
         background: var(--c, var(--logo-fallback)); box-shadow: 0 0 0 1.5px var(--court-ring);
         font-size: 10.5px; font-weight: 700; line-height: 1; color: var(--on-accent);
         font-variant-numeric: tabular-nums; }
/* No headshot, so the number is already filling the circle. */
.cpface:not(:has(img)) .cpnum { display: none; }
.cpname { padding: 1px 6px; border-radius: 8px; background: var(--court-label);
          font-size: 11.5px; font-weight: 700; line-height: 1.3; white-space: nowrap; }
@media (max-width: 600px) {
  .court { --face: 34px; margin: 12px 12px 4px; }
  .cpno { font-size: 13px; }
  .cpnum { min-width: 16px; height: 15px; padding: 0 3px; right: -6px; font-size: 9.5px; }
  .cpname { padding: 0 4px; font-size: 10.5px; }
}

/* Two numbers either side of what they measure. No bold on the larger one: yards
   read one way and penalties the other, so a mark meaning "more" on one row and
   "worse" on the next would say less than nothing. */
.tstats { width: 100%; border-collapse: collapse; font-size: 13px;
          font-variant-numeric: tabular-nums; }
.tstats th { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
             color: var(--ink-soft); padding: 8px 16px; }
.tstats td { padding: 6px 16px; text-align: center; width: 24%; white-space: nowrap; }
.tstats th:first-child, .tstats td:first-child { text-align: left; }
.tstats th:last-child, .tstats td:last-child { text-align: right; }
.tstats .mid { width: 52%; color: var(--ink-muted); text-align: center; }
.tstats th.mid { text-align: center; }
/* The larger of the two figures on each row. */
.tstats td.more { font-weight: 700; color: var(--ink); }

/* Shooting and rebounding as paired bars, the way the app charts them: the
   comparison is the shape, and the figure sits at the end of it. */
.sbar { padding: 12px 16px 4px; }
.sbar + .sbar { padding-top: 8px; }
.sbarlbl { font-size: 13px; font-weight: 600; margin-bottom: 7px; }
.sbarrow { display: flex; align-items: center; gap: 10px; margin-bottom: 5px; }
.sbarteam { flex: 0 0 34px; font-size: 12px; font-weight: 600; color: var(--ink-soft); }
.sbartrack { flex: 1 1 auto; height: 12px; border-radius: 6px; background: var(--surface-sunken); }
.sbarfill { display: block; height: 100%; border-radius: 6px;
            background: var(--c, var(--tint)); }
.sbarval { flex: 0 0 46px; text-align: right; font-size: 13.5px; font-weight: 600;
           font-variant-numeric: tabular-nums; }
.tstats tbody tr + tr td { border-top: 1px solid var(--hairline-soft); }
.tstats tbody tr:last-child td { padding-bottom: 12px; }

/* A clip per scoring play means ten or so; they scroll rather than stack. */
.vstrip { display: flex; gap: 12px; padding: 14px 16px 16px; }
.vcard { flex: 0 0 auto; width: 172px; color: inherit; }
.vcard:hover { text-decoration: none; }
.vcthumb { position: relative; display: block; aspect-ratio: 16 / 9; border-radius: 8px;
           overflow: hidden; background: var(--surface-sunken); }
.vcthumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vcard .vplay { width: 40px; height: 27px; border-radius: 7px; }
.vcard .vplay svg { width: 13px; height: 13px; }
.vcard:hover .vplay { background: var(--red); }
/* A reel embedded with others under it: the player fills the top of the card,
   so it takes the card's corners, and the strip under it is its playlist, with
   the clip in the player marked. */
[data-highlights] { overflow: hidden; }
[data-highlights] .vstrip { border-top: 1px solid var(--hairline); }
.vcard[aria-current="true"] .vcthumb::after { content: ""; position: absolute; inset: 0;
                                               border: 3px solid var(--tint); border-radius: inherit; }
.vcard[aria-current="true"] .vctitle { color: var(--tint); }
.vctitle { display: block; margin-top: 7px; font-size: 12.5px; line-height: 1.35;
           /* Two lines, so titles of wildly different lengths still line the cards
              up with each other. */
           display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
           overflow: hidden; }

/* Box score: one card per club, side by side where there is room. */
.boxcols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px;
           align-items: start; }
.box { width: 100%; min-width: 300px; border-collapse: collapse; font-size: 13px;
       font-variant-numeric: tabular-nums; }
.box th { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
          color: var(--ink-soft); padding: 8px 4px 6px; text-align: center; }
.box td { padding: 5px 4px; text-align: center; border-top: 1px solid var(--hairline-soft); }
.box th.l, .box td.l { text-align: left; padding-left: 16px; width: 40%; white-space: nowrap;
                       overflow: hidden; text-overflow: ellipsis; }
.box th.avg, .box td.avg { padding-right: 16px; }
.box thead th { border-bottom: 1px solid var(--hairline); }
/* A substitute is indented under the man he came in for, as a box score prints
   him. */
.box tr.sub td.l { padding-left: 28px; }
.box tr.btot td { font-weight: 700; border-top: 1px solid var(--hairline); }
.bpos { color: var(--ink-soft); margin-left: 6px; font-size: 11.5px; }
.bdec { margin-left: 6px; font-size: 11px; font-weight: 700; color: var(--ink-soft); }
.bdec.w { color: var(--green); }
.bdec.l { color: var(--red); }

/* Basketball: a card a side with up to fourteen columns, so the table keeps a
   width it reads at and scrolls on a phone, under a name column that stays
   put. The starters and the bench each open with their own row of headings. */
.bboxcard + .bboxcard { margin-top: 14px; }
.bbox { min-width: 680px; }
.bbox th, .bbox td { white-space: nowrap; }
.bbox th.l, .bbox td.l { width: 150px; max-width: 170px; position: sticky; left: 0; z-index: 1;
                         background: var(--surface); }
.bbox th:last-child, .bbox td:last-child { padding-right: 16px; }
.bbox tr.bhead th { border-bottom: 1px solid var(--hairline); }
/* A table of five, hockey's goaltenders, fits a phone as it is. */
.bbox.slim { min-width: 0; }
.scroller + .scroller .bbox { border-top: 1px solid var(--hairline); }
.bbox tbody + tbody tr.bhead th { padding-top: 16px; }
/* The five on the floor, in the app's green. */
.bbox tr.bcourt td.l, .bbox tr.bcourt td.l a, .bnow { color: var(--green); }
.bnow { font-weight: 600; }
@media (max-width: 760px) {
  /* Scrolling now, so the name column gets an edge to scroll under. */
  .bbox th.l, .bbox td.l { box-shadow: 1px 0 0 var(--hairline); }
}

/* ---- line movement ----
   Three lists across, each scrolling inside its own card. A line polled all
   afternoon runs to hundreds of rows, and three of those at full height would bury
   everything below them. */
/* The margin is on the row itself: it follows the score card, and the card-to-card
   rule only matches a .card, which this grid is not. */
.lmcols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px;
          align-items: start; margin-top: 14px; }
.lmcols .card + .card { margin-top: 0; }
.lmcard { display: flex; flex-direction: column; }
.lmcount { float: right; font-size: 11.5px; font-weight: 400; color: var(--ink-soft); }
.lmscroll { overflow-y: auto; max-height: 420px; }
.lmtable { width: 100%; border-collapse: collapse; font-size: 12.5px;
           font-variant-numeric: tabular-nums; }
.lmtable th { position: sticky; top: 0; background: var(--surface); z-index: 1;
              font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
              text-transform: uppercase; color: var(--ink-soft); padding: 8px 8px 6px;
              text-align: right; border-bottom: 1px solid var(--hairline); }
.lmtable td { padding: 5px 8px; text-align: right; }
.lmtable th.l, .lmtable td.l { text-align: left; padding-left: 16px; white-space: nowrap;
                               color: var(--ink-soft); }
.lmtable td:last-child { padding-right: 16px; }
.lmtable tr + tr td { border-top: 1px solid var(--hairline-soft); }
/* The numbers carry the weight; the timestamps are the index down the side. */
.lmtable td { font-weight: 600; }
.lmtable td.l { font-weight: 400; }
/* The line as it stands now — the row everything else is read against. */
.lmtable tbody tr:first-child td { color: var(--tint); }

/* ---- standings ----
   Club down the left taking the slack, fixed narrow columns for the figures so
   several divisions stacked in a card line up with each other. */
/* min-width with a scroller around it: left to shrink, a long club name — "Chi
   White Sox" — squeezes the figure columns until "6-4" wraps onto two lines. */
.stand { width: 100%; min-width: 340px; border-collapse: collapse; font-size: 13px;
         font-variant-numeric: tabular-nums; }
.stand td, .stand th { white-space: nowrap; }
.stand th { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
            color: var(--ink-soft); padding: 10px 6px 6px; text-align: right; width: 40px;
            border-bottom: 1px solid var(--hairline); }
.stand th.l { text-align: left; padding-left: 16px; width: auto; white-space: nowrap; }
.stand td { padding: 6px; text-align: right; border-top: 1px solid var(--hairline-soft); }
.stand td.l { text-align: left; padding-left: 16px; display: flex; align-items: center; gap: 8px; }
.stand th:last-child, .stand td:last-child { padding-right: 16px; }
/* A second division follows in the same card, so its heading needs the rule the
   card edge would otherwise give it. Matched through the scroller each table sits
   in — the tables themselves are no longer siblings. */
.scroller + .scroller .stand thead th { border-top: 1px solid var(--hairline);
                                        padding-top: 16px; }
.stlogo { width: 20px; height: 20px; flex-shrink: 0; }
.stlogo img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* The club's name is its page's link, but the row is read as a table rather than
   as a list of links, so it takes the row's colour until it is pointed at. */
.stname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
          color: inherit; }
.stname:hover { color: var(--tint); }
/* The clinched/eliminated letter, set apart from the club's name. No negative
   margin: the link clips its overflow for long names, so a mark pulled past
   the link's left edge loses the side of its letter — it did with Open Sans. */
.stmark { color: var(--ink-soft); }
/* The qualifying line drawn through a race table: everything above it is in.
   Dashed and tinted rather than red, which is spoken for by postponed games. */
.stand tr.stcut td { padding: 4px 16px; text-align: right;
                     border-top: 2px dashed var(--tint); }
.stand tr.stcut + tr td { border-top: 0; }
.stand tr.stcut span { font-size: 10px; font-weight: 700; letter-spacing: 0.05em;
                       text-transform: uppercase; color: var(--tint); }
.stkey { margin: 14px 0 4px 4px; font-size: 12px; color: var(--ink-soft); }
.stkey b { color: var(--ink-muted); }

/* ---- prose pages: about, contact, the legal ones ---- */
/* Held to a readable measure rather than run to the full width of a card that was
   built for tables. */
.prose { padding: 22px 26px; max-width: 68ch; font-size: 15px; line-height: 1.62;
         color: var(--ink-muted); }
.prose p { margin: 0 0 14px; }
.prose p:last-child { margin-bottom: 0; }
.prose .intro { font-size: 17px; line-height: 1.55; color: var(--ink); }
.prose h2 { font-size: 16px; font-weight: 700; color: var(--ink); margin: 24px 0 8px; }
.prose a { font-weight: 600; }

@media (max-width: 700px) { .prose { padding: 18px 16px; font-size: 14.5px; } }

/* ---- leaders ---- */
/* Players / Teams. */
.ldtabs { display: flex; gap: 4px; margin: 10px 0 14px 2px; }
.ldtab { padding: 6px 15px; border-radius: 999px; font-size: 13.5px; font-weight: 600;
         color: var(--ink-soft); background: var(--chip); }
.ldtab:hover { text-decoration: none; color: var(--ink); }
.ldtab.on { background: var(--tint); color: #fff; }
.ldtab.on:hover { color: #fff; }
/* The same pills under a game's header, leading to its box score and plays.
   Spaced below as well: the box score grid brings no margin of its own, and on
   the pages that do the two collapse into one. */
.gdtabs { margin: 12px 0 14px 2px; }

/* The category list beside the board it chooses. A column of links rather than a
   dropdown: every category is its own page, so these are real destinations. */
.ldcols { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 16px;
          align-items: start; }
.ldnav { background: var(--surface); border-radius: 10px; box-shadow: var(--shadow);
         overflow: hidden; padding-bottom: 4px; }
.ldnavhead { font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em;
             text-transform: uppercase; color: var(--ink-soft);
             padding: 12px 14px 5px; }
.ldpick { display: block; padding: 6px 14px; font-size: 13.5px; color: var(--ink-muted); }
.ldpick:hover { background: var(--surface-hover); color: var(--tint); text-decoration: none; }
.ldpick.on { background: var(--tint-wash); color: var(--tint); font-weight: 700;
             box-shadow: inset 3px 0 0 var(--tint); }
.ldmain { min-width: 0; }
/* Beside the list, the board is held to a reading width rather than the whole
   column: at full width the figure sat most of a screen away from the name it
   belongs to (Jeff, 2026-10-07). Stacked on a phone it takes the width there is. */
@media (min-width: 801px) {
  .ldmain { max-width: 520px; }
}
/* Rows tall enough for a headshot you can make out. */
.tlead { width: 100%; border-collapse: collapse; font-size: 14px;
         font-variant-numeric: tabular-nums; }
.tlead td { padding: 9px 6px; border-top: 1px solid var(--hairline-soft); }
.tlead tr:first-child td { border-top: 0; }
.tlead td.k { width: 22px; padding-left: 14px; color: var(--ink-soft); font-size: 12.5px; }
.tlead td.p { width: auto; display: flex; align-items: center; gap: 10px; min-width: 0; }
.tlead td.p a { color: inherit; min-width: 0; overflow: hidden;
                text-overflow: ellipsis; white-space: nowrap; }
.tlead td.p a:hover { color: var(--tint); }
.tlead td.v { width: 64px; text-align: right; padding-right: 14px; font-weight: 700;
              font-size: 14.5px; white-space: nowrap; }
.psub { color: var(--ink-soft); font-size: 12px; flex-shrink: 0; }
.pfacewrap { width: 36px; height: 36px; flex-shrink: 0; border-radius: 50%;
             background: var(--surface-sunken); overflow: hidden; display: block; }
.pface { width: 100%; height: 100%; object-fit: cover; display: block; }
.tlead .tlogo { width: 30px; height: 30px; }

/* On a phone the list goes above the board and scrolls sideways, so a league
   with two dozen categories does not push the leaderboard off the screen. */
@media (max-width: 800px) {
  .ldcols { grid-template-columns: 1fr; }
  .ldnav { display: flex; gap: 4px; overflow-x: auto; padding: 8px; scrollbar-width: none; }
  .ldnav::-webkit-scrollbar { display: none; }
  .ldnavhead { display: flex; align-items: center; padding: 0 4px 0 8px; flex: 0 0 auto; }
  .ldnavhead:first-child { padding-left: 2px; }
  .ldpick { flex: 0 0 auto; padding: 5px 11px; border-radius: 999px; white-space: nowrap; }
  .ldpick.on { box-shadow: none; }
}

/* ---- player page ---- */
.plface { position: relative; width: 66px; height: 66px; flex-shrink: 0; border-radius: 50%;
          background: var(--surface-sunken); display: block; }
.plface > img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
/* The club crest sits on the corner of the portrait, as it does in the app. */
.plbadge { position: absolute; right: -3px; bottom: -3px; width: 26px; height: 26px;
           border-radius: 50%; background: var(--surface); padding: 3px;
           box-shadow: var(--shadow); }
.plbadge img { width: 100%; height: 100%; object-fit: contain; display: block; }
.plteam { color: var(--ink-muted); font-weight: 600; }
.plteam:hover { color: var(--tint); }

/* A season line reads across, so it is a header row and a single row of figures. */
.tpstat { width: 100%; min-width: 300px; border-collapse: collapse; font-size: 14px;
          font-variant-numeric: tabular-nums; }
.tpstat th { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
             text-transform: uppercase; color: var(--ink-soft);
             padding: 10px 10px 6px; text-align: right; white-space: nowrap;
             border-bottom: 1px solid var(--hairline); }
.tpstat td { padding: 9px 10px; text-align: right; font-weight: 600; white-space: nowrap; }
.tpstat th:first-child, .tpstat td:first-child { padding-left: 16px; }
.tpstat th:last-child, .tpstat td:last-child { padding-right: 16px; }

/* The bio as a row of facts under the header. Each block sizes to its own
   content, so a long birthplace takes the room it needs and the short ones do
   not stretch to match. */
.plbio { display: flex; flex-wrap: wrap; gap: 4px 32px; padding: 14px 16px; }
.plfact { min-width: 0; }
.plfactlabel { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
               text-transform: uppercase; color: var(--ink-soft); margin-bottom: 2px; }
.plfactvalue { font-size: 13.5px; font-weight: 600; }
.tglog td.l a { color: inherit; }
.tglog td.l a:hover { color: var(--tint); }

/* A game log is many rows of the same line, so the header repeats down the page
   rather than reading as one wide figure. */
.tglog td { font-weight: 500; border-top: 1px solid var(--hairline-soft); }
.tglog tbody tr:first-child td { border-top: 0; }
.tglog th.l, .tglog td.l { text-align: left; white-space: nowrap; }
.tglog td.l { color: var(--ink-soft); }
.cardlink { display: block; padding: 10px 16px; font-size: 13px; font-weight: 600;
            color: var(--tint); border-top: 1px solid var(--hairline); }
.cardlink:hover { background: var(--surface-hover); text-decoration: none; }

@media (max-width: 700px) { .plface { width: 54px; height: 54px; } }

/* ---- teams index ---- */
.tline { display: flex; align-items: center; gap: 11px; padding: 10px 16px; color: inherit;
         border-top: 1px solid var(--hairline-soft); }
.card .sh + .tline { border-top: 0; }
.tline:hover { background: var(--surface-hover); text-decoration: none; color: var(--tint); }
.tlname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
          white-space: nowrap; font-size: 14.5px; font-weight: 600; }
.tchev { color: var(--ink-soft); font-size: 15px; flex-shrink: 0; }
/* The crest carries the row here, rather than sitting beside a score. */
.tline .tlogo { width: 24px; height: 24px; }

/* ---- team page ---- */
.teamhead { display: flex; align-items: center; gap: 16px; margin-bottom: 6px; }
.teamlogo { width: 58px; height: 58px; flex-shrink: 0; }
.teamlogo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.teamsub { font-size: 14px; color: var(--ink-soft); margin: 2px 0 0 2px;
           font-variant-numeric: tabular-nums; }
.teamsub b { color: var(--ink); font-weight: 700; }

/* Season stats: label, figure, league rank. The rank column is the reason the
   table exists, so it gets its own weight rather than trailing the value. */
.tstat { width: 100%; border-collapse: collapse; font-size: 13.5px;
         font-variant-numeric: tabular-nums; }
.tstat td { padding: 7px 6px; border-top: 1px solid var(--hairline-soft); }
.tstat tr:first-child td { border-top: 0; }
.tstat td.l { padding-left: 16px; color: var(--ink-muted); width: auto; white-space: nowrap; }
/* The rank drawn rather than read: the bar runs full width for the league leader
   and down to one club's width for last. Hidden on a narrow screen, where the
   label and the figures need the room more than the picture does. */
.tstat td.bar { width: 34%; padding-right: 12px; }
.bartrack { display: block; height: 6px; border-radius: 999px; background: var(--chip);
            overflow: hidden; }
.barfill { display: block; height: 100%; border-radius: 999px; background: var(--ink-soft); }
.barfill.lead { background: var(--tint); }
.tstat td.v { text-align: right; font-weight: 700; width: 74px; white-space: nowrap; }
.tstat td.r { text-align: right; padding-right: 16px; width: 52px; white-space: nowrap;
              color: var(--ink-soft); font-size: 12px; }
.tstat td.r.lead { color: var(--tint); font-weight: 700; }

/* Month picker over the schedule. Scrolls sideways rather than wrapping — a
   baseball season is seven months and the card is half the page. */
.months { display: flex; gap: 2px; padding: 8px 10px; overflow-x: auto;
          border-bottom: 1px solid var(--hairline); scrollbar-width: none; }
.months::-webkit-scrollbar { display: none; }
.month { flex: 0 0 auto; appearance: none; border: 0; background: transparent;
         font: inherit; font-size: 12.5px; font-weight: 600; color: var(--ink-soft);
         padding: 5px 11px; border-radius: 999px; cursor: pointer; white-space: nowrap; }
.month:hover { background: var(--surface-hover); color: var(--ink); }
.month.on { background: var(--tint-wash); color: var(--tint); }
.monthpane.off { display: none; }
/* A season-type heading following a table needs the rule the card edge would
   otherwise give it. */
.monthpane .scroller + .sh { border-top: 1px solid var(--hairline); }

/* Schedule: date, opponent, result, score or start time. */
.tsched { width: 100%; min-width: 380px; border-collapse: collapse; font-size: 13.5px;
          font-variant-numeric: tabular-nums; }
.tsched td { padding: 7px 6px; border-top: 1px solid var(--hairline-soft);
             white-space: nowrap; }
.tsched tr:first-child td { border-top: 0; }
.tsched td.d { padding-left: 16px; color: var(--ink-soft); width: 92px; }
.tsched td.o { width: auto; display: flex; align-items: center; gap: 7px; }
.tsched td.w { width: 26px; text-align: center; }
.tsched td.s { width: 76px; text-align: right; font-weight: 600; }
.tsched td.n { width: 58px; padding-right: 16px; color: var(--ink-soft); font-size: 11.5px; }
/* "@" and "vs" sit in a fixed column so the club names line up under each other. */
.tsched .at { width: 20px; color: var(--ink-soft); font-size: 12px; flex-shrink: 0; }
.tlogo { width: 19px; height: 19px; flex-shrink: 0; }
.tlogo img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* min-width:0 so the club's name can actually shrink — a flex item refuses to go
   below its content width without it, and the ellipsis never gets a chance. */
.tsched td.o a, .tsched td.s a, .trost td.l a { color: inherit; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tsched td.o a:hover, .tsched td.s a:hover, .trost td.l a:hover { color: var(--tint); }
.res { font-weight: 700; }
.res.w { color: var(--green); }
.res.l { color: var(--ink-soft); }
/* A draw, or a tie: neither colour. */
.res.d, .res.t { color: var(--ink-muted); }

/* A league's whole season (Schedule.cshtml): the date on a day's first game,
   then each game as the sport reads it — away @ home in football, home first in
   soccer — with the score, or the start time, between the two sides. Fixed
   layout so the sides split what the date and the middle leave evenly, and the
   middle column lines up down the page whatever the names. */
/* Clipped so a week heading at the top of the card keeps its rounded corners. */
.schedcard { max-width: 680px; overflow: hidden; }
/* Football's season is one list: a rule over each week heading after the
   first, and a little room above one the page scrolls to. */
.lsched + .sh { border-top: 1px solid var(--hairline); }
.schedcard .wk { scroll-margin-top: 12px; }
.lsched { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: 13.5px;
          font-variant-numeric: tabular-nums; }
.lsched td { padding: 7px 6px; border-top: 1px solid var(--hairline-soft); white-space: nowrap; }
.lsched tr.nd td { border-top-color: var(--hairline); }
.lsched tr:first-child td { border-top: 0; }
.lsched td.d { width: 100px; padding-left: 16px; color: var(--ink-soft); font-size: 12.5px; }
.lsched td.m { width: 96px; text-align: center; }
.lsched td.t2 { padding-right: 16px; }
.lsched td.m a { color: var(--ink-soft); }
.lsched td.m.fin a { color: var(--ink); font-weight: 700; }
.lsched td.m.live a { color: var(--green); font-weight: 700; }
.lsched td.m.ppd a { font-size: 12px; }
.lsched td.m a:hover { color: var(--tint); }
.lsched td.m .x { margin-left: 4px; font-size: 11px; color: var(--ink-soft); }
/* The name gives way to an ellipsis before the logo does; see .tsched above. */
.lsched .side { display: flex; align-items: center; gap: 7px; min-width: 0; }
.lsched td.t1 .side { justify-content: flex-end; }
.lsched .side a { color: inherit; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.lsched .side a:hover { color: var(--tint); }
.lsched .won a { font-weight: 700; }
.lsched .at { color: var(--ink-soft); font-size: 12px; flex-shrink: 0; }
/* The phone layout's halves, below. */
.lsched .narrow, .lsched .sc { display: none; }

/* Roster. */
.trost { width: 100%; min-width: 340px; border-collapse: collapse; font-size: 13.5px;
         font-variant-numeric: tabular-nums; }
.trost th { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
            text-transform: uppercase; color: var(--ink-soft);
            padding: 10px 6px 6px; text-align: right; width: 52px;
            border-bottom: 1px solid var(--hairline); }
.trost th.l, .trost th.num { text-align: left; }
.trost th.l { width: auto; padding-left: 4px; }
.trost th.num, .trost td.num { width: 42px; padding-left: 16px; }
.trost td { padding: 7px 6px; text-align: right; white-space: nowrap;
            border-top: 1px solid var(--hairline-soft); }
.trost td.num { text-align: left; color: var(--ink-soft); }
.trost td.l { text-align: left; width: auto; padding-left: 4px; }
.trost th:last-child, .trost td:last-child { padding-right: 16px; }
.card .sh + .scroller .trost thead th { border-top: 0; }
.scroller + .sh { border-top: 1px solid var(--hairline); }

@media (max-width: 700px) {
  .teamlogo { width: 46px; height: 46px; }
  .teamhead { gap: 12px; }
  /* Tighten the fixed columns so the schedule fits a phone outright rather than
     scrolling sideways inside its card. */
  .tsched { min-width: 300px; }
  .tsched td.d { width: 62px; font-size: 12.5px; }
  /* The weekday is the first thing to go — the date still says which day it is. */
  .tsched .dow { display: none; }
  .tsched td.n { width: 44px; font-size: 11px; }
  .tsched td.s { width: 66px; }
  .tstat td.bar { display: none; }
  /* Side by side, a phone leaves each club nine letters — "Manchest…" for both
     Manchester clubs. So the sides stack, as on the scoreboard: the day over its
     first game, each side on its own line with its own score, and the start
     time or "Final" to the right of both. The cells become grid areas; an empty
     date cell (every game but a day's first) drops out. */
  .lsched, .lsched tbody { display: block; }
  .lsched tr { display: grid; grid-template-columns: minmax(0, 1fr) auto;
               grid-template-areas: "d d" "t1 m" "t2 m";
               border-top: 1px solid var(--hairline-soft); }
  .lsched tr:first-child { border-top: 0; }
  .lsched tr.nd { border-top-color: var(--hairline); }
  .lsched td { border-top: 0; padding: 0 14px; }
  .lsched td.d { grid-area: d; width: auto; padding: 10px 14px 0; font-size: 12px; font-weight: 600; }
  .lsched td.d:empty { display: none; }
  .lsched td.t1 { grid-area: t1; padding-top: 8px; padding-bottom: 2px; }
  .lsched td.t2 { grid-area: t2; padding-top: 2px; padding-bottom: 8px; }
  .lsched td.m { grid-area: m; width: auto; align-self: center; text-align: right; }
  .lsched td.m.fin a { font-weight: 600; color: var(--ink-soft); }
  .lsched td.t1 .side { justify-content: flex-start; }
  .lsched td.t1 .tlogo { order: -1; }
  .lsched .at, .lsched .wide, .lsched .x { display: none; }
  .lsched .narrow { display: inline; }
  .lsched .sc { display: inline; margin-left: auto; padding-left: 8px; color: var(--ink-soft); }
  .lsched .won .sc { color: var(--ink); font-weight: 700; }
}

/* ---- team news ---- */
/* A headline to a row, the whole row the link. Capped so a title stays near its
   picture on a wide screen; the picture sits right, so the titles line up
   whether a story has one or not. */
.newscard { max-width: 760px; overflow: hidden; }
.newsrow { display: flex; align-items: center; gap: 14px; padding: 11px 16px; color: inherit;
           border-top: 1px solid var(--hairline-soft); }
.newsrow:first-child { border-top: 0; }
.newsrow:hover { background: var(--surface-hover); text-decoration: none; }
.newsrow:hover .newstitle { color: var(--tint); }
.newstext { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.newstitle { font-size: 15px; font-weight: 600; line-height: 1.35; color: var(--ink);
             display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.newsmeta { font-size: 12.5px; color: var(--ink-soft); }
/* The last twelve hours, as news.js counts them: "3 hours ago" in bold. */
.newsmeta time.fresh { font-weight: 700; color: var(--tint); }
.newsimg { flex-shrink: 0; width: 104px; height: 58px; border-radius: 6px; object-fit: cover;
           background: var(--chip); }
@media (max-width: 700px) {
  .newsrow { gap: 12px; padding: 10px 14px; }
  .newstitle { font-size: 14.5px; }
  .newsimg { width: 84px; height: 48px; }
}

/* ---- hockey final ---- */
/* The shootout column sits apart from the periods actually played. */
.linescore td.so { color: var(--ink-soft); }
.sog { display: flex; justify-content: space-between; align-items: baseline;
       padding: 10px 16px 12px; border-top: 1px solid var(--hairline);
       font-size: 12.5px; font-variant-numeric: tabular-nums; }
.soglabel { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase;
            color: var(--ink-soft); }

/* Three across, each with the mark of what he did. */
.stars { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
         padding: 6px 8px 14px; text-align: center; }
.star { padding: 8px 6px; min-width: 0; }
.star + .star { border-left: 1px solid var(--hairline); }
.starno { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase;
          color: var(--ink-soft); margin-bottom: 8px; }
.star .shot { margin: 0 auto 7px; }
.starname { display: block; font-size: 14px; font-weight: 600; line-height: 1.3; }
.starline { font-size: 12.5px; color: var(--ink-muted); margin-top: 2px; }

/* The period heading above each run of penalties, which have no score columns to
   line up with. Scoring uses the table's own header row instead. */
.hper { padding: 10px 16px 4px; font-size: 11.5px; font-weight: 700;
        letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); }
/* A second period's table follows the first directly, so its header needs the
   separation the card's own top edge would otherwise give it. */
.sptable + .hper, .sptable ~ .sptable thead th { border-top: 1px solid var(--hairline); }
.sptable ~ .sptable thead th { padding-top: 14px; }
.hclock { font-size: 12px; color: var(--ink-soft); white-space: nowrap;
          font-variant-numeric: tabular-nums; }
.sptable td.hclock { text-align: left; padding-left: 16px; font-weight: 400; width: 46px; }
/* The scoring table's own left cell loses its padding when a clock precedes it. */
.sptable td.hclock + td.l { padding-left: 0; }

.shootout, .pens { list-style: none; margin: 0; padding: 2px 0 8px; }
.shootout li, .pens li { display: flex; gap: 10px; align-items: center;
                         padding: 8px 16px; font-size: 13px; line-height: 1.45; }
.shootout li + li, .pens li + li { border-top: 1px solid var(--hairline-soft); }
.pens li { align-items: flex-start; }
.pens .spbody, .shootout .spbody { flex-grow: 1; }
/* Scored or stopped. */
.somark { font-size: 15px; font-weight: 700; flex-shrink: 0; }
.somark.in { color: var(--green); }
.somark.out { color: var(--red); }

/* ---- play by play ---- */
/* A compact score header — whoever is here came from the game card and wants the
   plays, but still needs to know which score they belong to. */
.pbphead { padding: 14px 18px; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.pbpteam { display: flex; align-items: center; gap: 9px; }
.pbpname { font-size: 15px; }
.pbpname.won { font-weight: 700; }
.pbpscore { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.pbpstatus { margin-left: auto; font-size: 13px; color: var(--ink-muted); }
/* The half-inning heading carries the batting club's mark. */
.pbplogo { width: 18px; height: 18px; display: inline-block; }
.pbplogo img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* When the drive began, beside the club that had it. */
.pbpwhen { font-weight: 400; letter-spacing: 0; text-transform: none; font-size: 12.5px;
           color: var(--ink-soft); margin-left: 2px; }

.plays { list-style: none; margin: 0; padding: 2px 0; }
.plays li { display: flex; gap: 14px; align-items: baseline; padding: 9px 16px;
            font-size: 13.5px; line-height: 1.5; color: var(--ink-muted); }
.plays li + li { border-top: 1px solid var(--hairline-soft); }
/* A play that scored is the one being looked for, so it is the one that is not
   muted, with the score it made underneath. */
.plays li.scored { color: var(--ink); font-weight: 500; }
.pbpbody { flex-grow: 1; min-width: 0; }
/* The situation the snap came from, stacked in its own column as the app has it:
   clock, then down and distance, then where the ball was. */
.plays.drive li { align-items: flex-start; }
.pbpsit { flex: 0 0 76px; display: flex; flex-direction: column; gap: 1px;
          font-size: 12px; color: var(--ink-soft); font-variant-numeric: tabular-nums;
          text-align: right; }
.pbpclock { font-weight: 700; color: var(--ink-muted); }
/* The score after a scoring play, named rather than left as a bare pair. */
.pbpnow { display: block; margin-top: 3px; font-weight: 700; color: var(--tint);
          font-variant-numeric: tabular-nums; }
/* End of a quarter: a band across the list rather than a row pretending to be a
   play, since it has no clock, no down and no score of its own. */
.plays li.pbpbreak { display: block; text-align: center; background: var(--section);
                     color: var(--ink-muted); font-size: 12px; font-weight: 700;
                     letter-spacing: 0.06em; text-transform: uppercase; padding: 7px 16px; }
.pbpback { margin: 18px 0 4px 2px; font-size: 13px; }

/* ---- more from: the rest of the slate as a strip ----
   Cards rather than rows, scrolling sideways: this is a way back out to the other
   games, not a second scoreboard. Fixed-width so a sixteen-game NFL week and a
   three-game weeknight both read the same, and the last card is deliberately cut
   off at the right edge on a full slate — a clipped card is what says there is
   more to scroll to. Padding on the strip, not the page, so the first card lines
   up with the cards above it and the row can still run to both edges. */
.strip { display: flex; gap: 9px; padding-bottom: 6px; }
.scard { flex: 0 0 auto; width: 126px; background: var(--surface); border-radius: 10px;
         box-shadow: var(--shadow); padding: 10px 11px 9px; color: inherit; }
.scard:hover { text-decoration: none; background: var(--surface-hover); }
.steam { display: flex; align-items: center; gap: 7px; height: 23px; }
/* The shared logo chip, sized down for the card. */
.scard .glogo { width: 18px; height: 18px; border-radius: 4px; font-size: 7.5px; }
.sabbr { font-size: 12.5px; flex-grow: 1; min-width: 0; white-space: nowrap;
         overflow: hidden; text-overflow: ellipsis; }
.sabbr.won { font-weight: 700; }
.sscore { font-size: 14px; font-variant-numeric: tabular-nums; flex-shrink: 0; }
.sscore.won { font-weight: 700; }
.sscore.lost { color: var(--score-lost); font-weight: 300; }
.sstat { font-size: 11px; color: var(--ink-soft); white-space: nowrap; overflow: hidden;
         text-overflow: ellipsis; margin-top: 6px; padding-top: 6px;
         border-top: 1px solid var(--hairline); }
/* Green for a game under way, not red: red means postponed or cancelled here, the
   same as it does in the apps. Green rather than the tint blue because every card
   in the strip is already a link, and blue text inside one reads as a second one. */
.sstat.live { color: var(--green); font-weight: 600; }
.sstat.stopped { color: var(--red); font-weight: 600; }

/* ---- get the apps ----
   Two halves side by side, split by the same hairline the game page uses between
   its lineup cards, and stacking at the same width it does — two store buttons
   and a sentence do not fit in half of a phone. */
.apps { display: grid; grid-template-columns: repeat(var(--appcols, 2), minmax(0, 1fr)); }
.apphalf { display: flex; gap: 14px; padding: 18px 18px 20px; min-width: 0; }
/* Two across, and the list is configuration, so it can be any length. Keyed off
   position in the row rather than the previous sibling: a third app wraps onto a
   second row, where a plain + rule would draw a divider down its left edge and
   none above it. */
.apps .apphalf:not(:nth-child(2n + 1)) { border-left: 1px solid var(--hairline); }
.apps .apphalf:nth-child(n + 3) { border-top: 1px solid var(--hairline); }
/* The artwork is a full-bleed gradient square, so it wants the same rounding iOS
   gives it rather than a plate behind it. */
.appicon { width: 56px; height: 56px; border-radius: 13px; flex-shrink: 0; display: block; }
.appbody { min-width: 0; }
.appname { font-size: 17px; font-weight: 600; }
.appdesc { margin: 4px 0 0; font-size: 13px; line-height: 1.5; color: var(--ink-muted); }
.appbtns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

/* A sentence under a page heading, outside any card. */
.pagelead { margin: 2px 2px 16px; font-size: 15.5px; line-height: 1.55;
            color: var(--ink-muted); max-width: 68ch; }

/* The apps page: one card per app, the full pitch rather than the one-liner the
   promo block has room for. */
.appfull { padding: 22px 26px 24px; }
.appfull + .appfull { margin-top: 14px; }
.apphead { display: flex; align-items: center; gap: 16px; }
.appbigicon { width: 72px; height: 72px; border-radius: 16px; flex-shrink: 0; display: block; }
.appbigname { margin: 0; font-size: 21px; font-weight: 700; letter-spacing: -0.01em; }
.apprating { display: flex; align-items: center; gap: 7px; margin-top: 3px;
             font-size: 13px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
/* Five solid stars rather than a part-filled row: the figure beside them is the
   precise claim, and a half star drawn at 13px reads as a rendering fault. */
.stars { color: var(--gold); letter-spacing: 1px; font-size: 12px; }
.appbigblurb { margin: 6px 0 0; font-size: 14.5px; line-height: 1.5; color: var(--ink-muted); }
.appfeatures { margin: 18px 0 0; padding: 0 0 0 20px; font-size: 14.5px; line-height: 1.6;
               color: var(--ink-muted); max-width: 68ch; }
.appfeatures li { margin-bottom: 7px; }
.appfeatures li::marker { color: var(--ink-soft); }
.appcovers { margin: 16px 0 0; font-size: 13.5px; color: var(--ink-soft); max-width: 68ch; }
.appcovers b { color: var(--ink-muted); }
.appfull .appbtns { margin-top: 18px; }

@media (max-width: 700px) {
  .appfull { padding: 18px 16px 20px; }
  .appbigicon { width: 60px; height: 60px; }
  .apphead { gap: 13px; }
}
/* Near-black in both themes, as store badges are everywhere. In dark mode it sits
   darker than the card it is on, which still reads as a raised chip because the
   navy is off-black rather than matching the page. */
.store { display: inline-flex; align-items: center; gap: 9px; background: #0A1628;
         border-radius: 8px; padding: 7px 13px 7px 11px; color: #FFFFFF; }
.store:hover { text-decoration: none; background: #16233A; }
.store svg { width: 17px; height: 17px; fill: #FFFFFF; flex-shrink: 0; }
.store .b1 { font-size: 8px; letter-spacing: 0.06em; color: #B4BDC4; display: block; }
.store .b2 { font-size: 13px; font-weight: 700; color: #FFFFFF; line-height: 1.15; display: block;
             white-space: nowrap; }

/* ---- footer ---- */
/* In light mode the footer is a near-black band on a gray page and the boundary
   is obvious. In dark both are near-black, so it needs an explicit edge — the
   hairline is invisible against the light-mode footer and does no harm there. */
.foot { background: var(--foot-bg); margin-top: 30px; border-top: 1px solid var(--hairline); }
.foot a { color: var(--foot-ink); font-size: 13px; }
.foot a:hover { color: var(--header-ink-active); }
.fcol { display: flex; flex-direction: column; gap: 9px; }
.fh { font-size: 11px; font-weight: 700; letter-spacing: 0.1em; color: var(--foot-ink-dim); margin-bottom: 2px; }
.legal { border-top: 1px solid var(--foot-border); font-size: 11px; line-height: 1.65; color: var(--foot-ink-dim);
         padding: 18px 20px 30px; }
.legal a { font-size: 11px; color: var(--foot-ink-dim); }
.gamble { display: flex; align-items: center; gap: 9px; background: var(--foot-inset); border: 1px solid var(--foot-border);
          border-radius: 7px; padding: 9px 13px; font-size: 11px; color: var(--foot-ink-body); line-height: 1.5;
          margin-bottom: 14px; }
.plus21 { display: flex; align-items: center; justify-content: center; width: 26px; height: 26px;
          border-radius: 13px; border: 1.5px solid var(--foot-ink-dim); font-size: 10px; font-weight: 700;
          color: var(--foot-ink); flex-shrink: 0; }
/* The odds provider's credit, at the end of the odds notice; on a phone, on a
   line of its own under the notice's text. */
.gamble .gtext { flex: 1; min-width: 0; }
.oddsby { flex-shrink: 0; padding-left: 12px; opacity: 0.85; line-height: 0; }
.oddsby:hover { opacity: 1; }
.oddsby img { display: block; width: 132px; height: auto; }
@media (max-width: 700px) {
  .gamble { flex-wrap: wrap; }
  .oddsby { flex-basis: 100%; padding-left: 35px; }
}

/* ---- consent bar ---- */
/* Across the bottom, over the page, until a choice is made (consent.js). Two
   identical buttons: refusing has to be exactly as easy as agreeing. */
.consentbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
              background: var(--surface); border-top: 1px solid var(--border);
              box-shadow: 0 -4px 18px rgba(0, 0, 0, 0.12);
              padding-bottom: env(safe-area-inset-bottom); }
.consentbar[hidden] { display: none; }
.consentrow { display: flex; align-items: center; gap: 18px; padding-top: 14px; padding-bottom: 14px; }
.consentrow p { flex: 1; margin: 0; font-size: 14px; line-height: 1.45; color: var(--ink); }
.consentbtns { display: flex; gap: 10px; flex-shrink: 0; }
.consentbtn { appearance: none; font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
              min-width: 104px; padding: 9px 18px; border-radius: 8px;
              border: 1px solid var(--chev); background: var(--surface); color: var(--ink-strong); }
.consentbtn:hover { border-color: var(--tint); color: var(--tint); }
@media (max-width: 700px) {
  .consentrow { flex-direction: column; align-items: stretch; gap: 12px; }
  .consentbtn { flex: 1; }
}

/* ---- per-sport row detail ---- */
/* A team's probable starter, indented to line up under the team name rather than
   the logo (22px badge + 6px gap). */
.gsub { font-size: 11.5px; color: var(--ink-soft); line-height: 1.3;
        padding-left: 28px; margin: -2px 0 3px; }

.mlines { font-size: 11.5px; color: var(--ink-soft); line-height: 1.55; margin-top: 4px; }
.mlines span { color: var(--ink-muted); }
/* The count is the widest thing in the status column, and every pixel it takes
   comes off the team name beside it. */
.bso { font-size: 10.5px; letter-spacing: -0.01em; color: var(--ink-muted); white-space: nowrap; }
/* Bat beside whichever team is batting, as the app draws it. */
.bat { width: 12px; height: 12px; flex-shrink: 0; fill: var(--ink-muted); }
.bat.off { display: none; }
/* Occupied bases are driven by data-on rather than fill attributes, so the same
   markup themes itself and the live poll only has to flip the attribute. */
.bases { flex-shrink: 0; margin-top: 4px; }
.bases polygon { fill: var(--base-empty); stroke: var(--base-stroke); stroke-width: 1.3; }
/* --tint, not --header: the header token is a background blue and goes darker in
   dark mode, which would leave an occupied base almost invisible on the card. */
.bases polygon[data-on="1"] { fill: var(--tint); stroke: var(--tint); }
/* A football beside whichever team has the ball, as the app marks it. */
.poss { width: 15px; height: 9px; flex-shrink: 0; color: var(--ink-muted); }
/* TD / FG / RZ, shown in place of the football. A tinted chip with coloured text
   rather than a solid fill, matching GameCell.m: scores read green, the red-zone
   warning red. */
.fstat { font-size: 9px; font-weight: 700; letter-spacing: 0.02em; padding: 1px 4px;
         border-radius: 2px; background: var(--chip); flex-shrink: 0; line-height: 1.5; }
.fstat.score { color: var(--green); }
.fstat.warn { color: var(--red); }
.poss.off, .fstat.off { display: none; }
.fcap { font-size: 11.5px; color: var(--ink-muted); margin-top: 8px; padding-top: 7px;
        border-top: 1px solid var(--hairline); }
/* Following the field graphic it reads as that graphic's caption, so it loses the
   rule above it and centers under the strip. */
.field + .fcap { border-top: none; margin-top: 0; padding-top: 5px; text-align: center; }
.fcap.off { display: none; }

/* ---- football field position ----
   120 yards wide: a 10-yard end zone in each club's color with the 100-yard
   field between them, so a yard line is a fixed percentage. Everything inside is
   positioned by percentage, which keeps it correct at any column width and lets
   the live poll move the markers by writing `left` alone. */
/* Inset rather than full-bleed, and centered — the app draws the strip at roughly
   two thirds of the cell so it reads as a diagram rather than a divider. */
.field { position: relative; height: 22px; max-width: 70%; margin: 9px auto 0;
         border-radius: 3px; overflow: hidden; background: var(--field); }
/* --c is the club's primary color and --cl its lighter alternate, both written
   inline by the view. Dark mode prefers the alternate because a primary like
   Carolina's black would vanish against the field. */
.fez { position: absolute; top: 0; bottom: 0; width: 8.3333%;
       background: var(--c, var(--logo-fallback)); }
.fez.a { left: 0; }
.fez.h { right: 0; }
/* A line every ten yards, drawn only across the field of play. */
.fgrid { position: absolute; top: 0; bottom: 0; left: 8.3333%; right: 8.3333%;
         background: repeating-linear-gradient(to right,
           transparent 0, transparent calc(10% - 1px),
           var(--field-line) calc(10% - 1px), var(--field-line) 10%); }
.f50 { position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--field-50); }
/* The CFL's field: 20-yard end zones either side of 110 yards, so on a strip of
   150 each end zone is 13.333% and the field of play has eleven ten-yard bands.
   Midfield is the 55, still the centre. */
.field.f110 .fez { width: 13.3333%; }
.field.f110 .fgrid { left: 13.3333%; right: 13.3333%;
                     background: repeating-linear-gradient(to right,
                       transparent 0, transparent calc(9.0909% - 1px),
                       var(--field-line) calc(9.0909% - 1px), var(--field-line) 9.0909%); }
/* Ground still to gain, and the line that ends it. */
.ftogo { position: absolute; top: 0; bottom: 0; background: rgba(243, 197, 58, 0.45); }
.fgain { position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px; background: var(--gold); }
.fscrim { position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px; background: var(--tint); }

/* The play, animated. Everything on the strip is placed in percentages, so
   transitioning left and width is enough for the ball to travel when the live
   poll moves it — no per-frame work, and it lands exactly where a static render
   would have put it. */
.fscrim, .fgain, .fball { transition: left .45s cubic-bezier(.4, 0, .2, 1); }
.ftogo { transition: left .45s cubic-bezier(.4, 0, .2, 1),
                     width .45s cubic-bezier(.4, 0, .2, 1),
                     opacity .2s linear; }
.fgain { transition: left .45s cubic-bezier(.4, 0, .2, 1), opacity .2s linear; }
/* Faded rather than removed, so there is something to animate back in. */
.ftogo.off, .fgain.off { opacity: 0; }
/* Set for one frame when a move should not read as the ball traveling. */
.field.nofx .fscrim, .field.nofx .fgain, .field.nofx .fball, .field.nofx .ftogo { transition: none; }

/* A ring at the ball, fired on every snap. It marks a play having happened, which
   is not the same as the ball having moved — 2nd & 10 becoming 3rd & 10 leaves the
   spot untouched and still deserves the beat. Rides on the same left transition so
   it travels with the arrow. */
.fpulse { position: absolute; top: 50%; width: 10px; height: 10px; margin: -5px 0 0 -5px;
          border-radius: 50%; background: currentColor; color: var(--c, var(--tint));
          opacity: 0; pointer-events: none;
          transition: left .45s cubic-bezier(.4, 0, .2, 1); }
.fpulse.go { animation: fpulseOut .65s ease-out; }
@keyframes fpulseOut {
  from { transform: scale(.35); opacity: .6; }
  to { transform: scale(3.4); opacity: 0; }
}

@keyframes fcapIn { from { opacity: 0; } to { opacity: 1; } }
.fcap.upd { animation: fcapIn .25s ease-out; }
/* The ball, pointing the way the offence is moving. Color comes from the club
   via an inline `color`, so the arrow inherits it through currentColor. */
.fball { position: absolute; top: 50%; transform: translateY(-50%); width: 0; height: 0;
         color: var(--c, var(--ink-muted));
         border-top: 7px solid transparent; border-bottom: 7px solid transparent; }
.fball.r { border-left: 9px solid currentColor; margin-left: -9px; }
.fball.l { border-right: 9px solid currentColor; }

/* ---- dark ----
   Values are the dark appearances in Colors.xcassets, so the site tracks the app
   rather than a palette invented here. Two exceptions, noted inline: the app
   leaves cell backgrounds to UIKit's system colors, and it has no dark Gold. */
@media (prefers-color-scheme: dark) {
  :root {
    --ink: #FFFFFF;
    --ink-muted: #B2B2B2;       /* TopViewText dark */
    --ink-soft: #8A94A6;
    --ink-strong: #FFFFFF;

    --page: #000000;            /* MainBackground dark */
    /* The app does not define a cell color — grouped table cells fall through to
       UIKit. This is secondarySystemGroupedBackground, which is what those cells
       actually paint on a black grouped background. */
    --surface: #1C1C1E;
    --surface-hover: #26292E;
    --surface-sunken: #26292E;
    --section: #24282C;         /* SectionHeaderBackground dark */
    --border: #2A2F35;
    --hairline: rgba(255, 255, 255, 0.10);
    --hairline-soft: rgba(255, 255, 255, 0.07);
    --chip: rgba(255, 255, 255, 0.12);
    --shadow: 0 1px 3px rgba(16, 16, 16, 0.60), 0 0 0 1px rgba(255, 255, 255, 0.06);

    --header: #0030A6;          /* HeaderBackground dark */
    --header-ink: #CCCCCC;      /* HeaderTitle dark */
    --tint: #0191FF;            /* PrimaryTint dark */
    --tint-hover: #66B8FF;
    --tint-wash: rgba(1, 145, 255, 0.16);
    --flash: rgba(1, 145, 255, 0.28);

    --green: #00BF00;           /* GreenText dark */
    --red: #FF0000;             /* RedText dark */
    /* Gold has no dark appearance in the asset catalog; it is already bright
       enough on black, and the caption beside it needs to be legible there. */
    --gold-ink: #F3C53A;

    --field: #2A2E34;
    --field-line: rgba(255, 255, 255, 0.10);
    --field-50: rgba(255, 255, 255, 0.30);

    --court: #2D2924;
    --court-plank: rgba(0, 0, 0, 0.16);
    --court-line: rgba(255, 255, 255, 0.24);
    --court-label: rgba(28, 28, 30, 0.88);
    --court-ring: rgba(255, 255, 255, 0.85);
    --court-shadow: rgba(0, 0, 0, 0.50);

    --chev: #4A525C;
    --score-lost: #6E7681;
    --logo-fallback: #3A4048;
    --base-empty: transparent;
    --base-stroke: #6E7681;

    --foot-bg: #131517;
    --foot-inset: #0C0E0F;
    --foot-border: #23282D;
  }

  /* The selected pill is a light chip on blue in light mode; on the darker header
     it needs less weight or it glares. */
  .pill.on { background: rgba(242, 242, 242, 0.90); }

  /* Nothing needed for logos here: the light-artwork variant is selected in the
     markup via <picture>, and the chip behind it is dropped in both themes. */

  /* Club colors switch to the lighter alternate the feed supplies, so an end
     zone in a near-black primary still reads against the field. */
  .fez { background: var(--cl, var(--c, var(--logo-fallback))); }
  /* Near-black club colours disappear against a dark card, so these bars take the
     lighter alternate here as the field does. */
  .wpfill { background: var(--cl, var(--c, var(--ink-soft))); }
  .sbarfill { background: var(--cl, var(--c, var(--tint))); }
  .fball { color: var(--cl, var(--c, var(--ink-muted))); }
  .fpulse { color: var(--cl, var(--c, var(--tint))); }
}

@media (prefers-reduced-motion: reduce) {
  .railtrack { scroll-behavior: auto; }
  .gscore { transition: none; }
  .fscrim, .fgain, .fball, .ftogo, .fpulse { transition: none; }
  .fcap.upd, .fpulse.go { animation: none; }
  /* The arrow still shows, and still says which way and how recently — it just
     does not pulse to say it. */
  .omove.fresh { animation: none; }
}

/* The top row needs about 600px laid out in full: a 130px wordmark, three
   corporate links, sign-in, the app button, and 154px of gaps between them.
   Below that it pushed the document wider than the viewport, which is why the
   coloured bands stopped short of the content on a phone. The corporate links
   are the ones to drop — all three are in the footer — and the rest tightens up. */
@media (max-width: 700px) {
  .b1row { gap: 14px; }
  .b1row .mark { height: 34px; }
  .corp, .hsep { display: none; }
  .ghost { white-space: nowrap; flex-shrink: 0; }
}

@media (max-width: 900px) {
  .glist { grid-template-columns: 1fr; }
  /* One column, so there is never a partial row to collapse. */
  .card.fit { max-width: 100%; }
  .railcell { flex: 0 0 88px; }
  .wrap { padding: 0 14px; }
  .h1 { font-size: 23px; }
  /* The game page stacks: two lineup cards side by side do not fit a phone. */
  .gdcols, .gdsplit, .apps, .livecols, .boxcols, .lmcols { grid-template-columns: 1fr; }
  /* Stacked, the second column's first card needs the gap the grid was giving it. */
  .livecol + .livecol { margin-top: 14px; }
  .gdhalf + .gdhalf { border-left: none; border-top: 1px solid var(--hairline); }
  /* One column: every card after the first is a new row, none of them a new column. */
  .apps .apphalf { border-left: none; }
  .apps .apphalf + .apphalf { border-top: 1px solid var(--hairline); }
  /* Stacked, the half is a phone wide and the two store buttons miss sharing a
     line by a couple of pixels. A smaller icon gives the text column those back,
     so the pair stays on one row instead of doubling the block's height. Below
     about 340px they wrap anyway, which is a fine place to land. */
  .appicon { width: 44px; height: 44px; border-radius: 10px; }
  .apphalf { gap: 12px; }
  .gdteams { grid-template-columns: 1fr; gap: 12px; }
  /* Stacked, so the explicit columns above have to be released. */
  .gdteam.a, .gdteam.h, .gdmid { grid-column: auto; grid-row: auto; }
  .gdteam.h { flex-direction: row; text-align: left; }
  .gdmid { text-align: left; }
}

/* ---- home page ---- */
/* The league rows are built on .sect so a heading here lines up with every other
   section heading on the site. These rules only undo the parts of it that a date
   or a sentence should not inherit: the uppercasing and the wide tracking. */
.homerow + .homerow { margin-top: 4px; }
.homehead { flex-wrap: wrap; }
.homename { color: var(--ink-strong); font-size: 14px; }
.homename:hover { color: var(--tint); text-decoration: none; }
.homewhen { text-transform: none; letter-spacing: 0; font-weight: 600; }
/* Pushed to the far end of the row, which is where a way out belongs. */
.homemore { margin-left: auto; text-transform: none; letter-spacing: 0;
            font-size: 12.5px; font-weight: 600; }

@media (max-width: 700px) {
  /* The date goes first when the row runs out of room. The league name and the
     way through to its scoreboard are what the row is for. */
  .homewhen { display: none; }
}

/* ---- error pages ---- */
/* The request id, which matters to whoever reads the log and to nobody else. */
.errref { font-size: 13px; color: var(--ink-soft); }

/* The 404 sets the art beside the words, and stacks it on a phone. */
.e404 { display: flex; align-items: center; gap: 28px; padding: 24px 26px 26px; }
.e404 .zone { width: 232px; }
.e404body { min-width: 0; }
.e404body p { margin: 0 0 10px; font-size: 15px; line-height: 1.55; color: var(--ink-muted); }
.e404body p:last-child { margin-bottom: 0; }
.e404body .e404quote { margin: 0 0 15px; }
.e404body .e404quote p { margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -0.02em;
                         line-height: 1.25; color: var(--ink-strong); }
.e404body .e404quote cite { display: block; margin-top: 7px; font-style: normal;
                            font-size: 12.5px; color: var(--ink-soft); }
/* The path the pitch took. Not data, unlike everything else on the plot, so it is
   dashed and faint enough to read as an illustration. */
.ztrail { fill: none; stroke: var(--ink-soft); stroke-opacity: 0.45; stroke-width: 1.4;
          stroke-dasharray: 4 5; stroke-linecap: round; }
/* The hero ball carries a three-digit number where a pitch carries one. */
.pitch.big text { font-size: 12px; }

@media (max-width: 700px) {
  .e404 { flex-direction: column; text-align: center; gap: 16px; padding: 20px 18px 22px; }
  .e404 .zone { width: 176px; }
}

/* ---- season disclosure ---- */
/* The scoreboard's season, openable. A row of every year would say "archive" to
   everyone arriving for today's scores; one word with a caret says it only to
   the few who go looking. A native details element, so it costs no script, and
   the links are in the HTML for a crawler whether it is open or not.

   Blue and semibold, like every other link on the site, so it reads as something
   to click - the first cut inherited the subline's grey and looked like plain
   text. Still small and still in the subline, so it is plainly secondary to the
   rail beneath it. */
.seasonpick { display: inline-block; position: relative; }
.seasonpick summary { list-style: none; cursor: pointer; color: var(--tint); font-weight: 600; }
.seasonpick summary::-webkit-details-marker { display: none; }
.seasonpick summary::after { content: " ▾"; font-size: 11px; }
.seasonpick[open] summary::after { content: " ▴"; }
.seasonpick summary:hover { text-decoration: underline; }
/* Drops over whatever is below rather than pushing it down. */
.seasonpick .years { position: absolute; top: 100%; left: 0; z-index: 5; margin-top: 8px;
                     display: grid; grid-template-columns: repeat(4, auto); gap: 2px;
                     padding: 8px; background: var(--surface); border-radius: 10px;
                     box-shadow: var(--shadow); }
.year { font-size: 12.5px; font-weight: 600; color: var(--ink-soft); padding: 5px 11px;
        border-radius: 999px; white-space: nowrap; text-align: center; }
.year:hover { background: var(--surface-hover); color: var(--ink); text-decoration: none; }
.year.on { background: var(--tint-wash); color: var(--tint); }

/* ---- popovers ---- */
/* Any open details.popover dims and softens the page behind it, so the panel
   is the one thing in focus. The scrim is the element's own fixed pseudo -
   which is what lets popover.js tell a click on it from a click inside the
   panel - and the summary is lifted above it so the thing you opened stays
   readable and stays clickable to close. Escape and outside clicks: popover.js. */
.popover[open]::before { content: ""; position: fixed; inset: 0; z-index: 4;
                         background: rgba(10, 22, 40, 0.28);
                         -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.popover[open] > summary { position: relative; z-index: 5; }
/* ---- college football: the conference disclosure and poll ranks ---- */
/* The same panel as the season picker, in two columns: the FBS conferences
   down one, the FCS down the other, each under its own small heading. */
.seasonpick .years.conf { grid-template-columns: repeat(2, auto); gap: 4px 14px; align-items: start; }
.confcol { display: grid; gap: 2px; }
.confhead { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
            color: var(--ink-soft); padding: 4px 11px 2px; }
.conf .year { text-align: left; }
/* A ranked club's poll position, small and grey before the name: #3 Ohio State. */
.grank { font-size: 11px; font-weight: 700; color: var(--ink-soft); margin-right: 4px; }

/* ---- college: the slate picker above the rail, and poll ranks in tables ---- */
/* A button, not a word in the subline: on a college page the slate is the
   control most readers reach for, where the season is a footnote. The same
   two-column panel as before opens beneath it. */
.slaterow { margin-top: 12px; }
.slatepick { display: inline-block; position: relative; }
.slatepick summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
                     height: 34px; padding: 0 14px; border-radius: 999px;
                     border: 1px solid var(--hairline-soft); background: var(--surface);
                     color: var(--ink); font-weight: 600; font-size: 14px; }
.slatepick summary::-webkit-details-marker { display: none; }
.slatepick summary::after { content: "▾"; font-size: 11px; color: var(--ink-soft); }
.slatepick[open] summary::after { content: "▴"; }
.slatepick summary:hover { color: var(--tint); border-color: var(--tint); }
.slatepick .years { position: absolute; top: 100%; left: 0; z-index: 5; margin-top: 8px;
                    display: grid; padding: 8px; background: var(--surface); border-radius: 10px;
                    box-shadow: var(--shadow); }
.years.conf { grid-template-columns: repeat(2, auto); gap: 4px 14px; align-items: start; }
/* The rank column of a poll or a college standings table. */
.strank { width: 22px; flex-shrink: 0; text-align: right; font-weight: 700; color: var(--ink-soft);
          font-variant-numeric: tabular-nums; }

/* ---- live football: the situation card ---- */
/* The scoreboard row's field strip, drawn at full width and a little taller so
   the ball, the line to gain and the caption read at a glance. */
.sit { padding: 12px 16px 14px; }
.fieldbig .field { height: 40px; max-width: 100%; margin: 4px 0 0; }
.fieldbig .fcap { text-align: center; }
.sitline { display: flex; align-items: center; gap: 8px; margin-top: 12px; font-size: 14px; }
.sitline .glogo { width: 24px; height: 24px; flex-shrink: 0; }
.sitline .fstat { position: static; margin-left: 4px; }
.sitplay { margin-top: 10px; font-size: 13px; }
.sitplay .gdlabel { margin-right: 6px; }
.sitmeta { margin-top: 8px; font-size: 12px; color: var(--ink-soft); }

/* ---- in-game football situation, laid out as the app's ----
   The side with the ball as a badge in its colour, the down as the headline
   with the spot beneath, and the timeouts as three dashes a side. */
.sh.split { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.sh .shsub { font-size: 12.5px; font-weight: 400; color: var(--ink-soft); white-space: nowrap; }
.sithead { display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.sitdown { display: flex; align-items: center; gap: 12px; min-width: 0; }
.sitbadge { display: inline-block; min-width: 44px; padding: 7px 8px; border-radius: 8px; text-align: center;
            font-size: 15px; font-weight: 700; color: #fff; background: var(--c, var(--logo-fallback)); }
.sitdd1 { display: flex; align-items: center; gap: 8px; font-size: 22px; font-weight: 700; line-height: 1.15;
          color: var(--ink); }
.sitdd1 .fstat { position: static; }
.sitdd2 { font-size: 13.5px; color: var(--ink-muted); margin-top: 2px; min-height: 1em; }
.sittos { display: flex; flex-direction: column; gap: 5px; align-items: flex-end; flex-shrink: 0; }
.sitto { display: flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700; letter-spacing: .04em;
         color: var(--ink-soft); }
.sitto span { margin-right: 4px; }
/* A dash per timeout, lit in the club's colour while it is still there. */
.sitto i { display: inline-block; width: 16px; height: 5px; border-radius: 3px; background: var(--hairline); }
.sitto i.on { background: var(--c, var(--ink-muted)); }
/* In the dark theme the badge and the dashes take the club's lighter alternate,
   as the field and the bars do. Here rather than in the dark block above,
   which these light rules would otherwise outrank by coming later. */
@media (prefers-color-scheme: dark) {
  .sitbadge { background: var(--cl, var(--c, var(--logo-fallback))); }
  .sitto i.on { background: var(--cl, var(--c, var(--ink-muted))); }
}
.sitlabel { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
            color: var(--ink-soft); margin-bottom: 3px; }
.sitplay { margin-top: 12px; font-size: 14px; line-height: 1.4; }
/* The big field: a bigger arrow, the app's tick marks at the edges instead of
   lines across, thicker lines, and no caption — the headline says it. */
.fieldbig .field { height: 48px; margin: 12px 0 0; border-radius: 6px; }
.fieldbig .fball { border-top-width: 12px; border-bottom-width: 12px; }
.fieldbig .fball.r { border-left-width: 18px; margin-left: -18px; }
.fieldbig .fball.l { border-right-width: 18px; }
.fieldbig .fpulse { width: 16px; height: 16px; margin: -8px 0 0 -8px; }
.fieldbig .fscrim, .fieldbig .fgain { width: 3px; margin-left: -1.5px; }
.fieldbig .fgrid { background:
  repeating-linear-gradient(to right, transparent 0, transparent calc(10% - 1px),
    var(--field-50) calc(10% - 1px), var(--field-50) 10%) top / 100% 8px no-repeat,
  repeating-linear-gradient(to right, transparent 0, transparent calc(10% - 1px),
    var(--field-50) calc(10% - 1px), var(--field-50) 10%) bottom / 100% 8px no-repeat; }
.fieldbig .field.f110 .fgrid { background:
  repeating-linear-gradient(to right, transparent 0, transparent calc(9.0909% - 1px),
    var(--field-50) calc(9.0909% - 1px), var(--field-50) 9.0909%) top / 100% 8px no-repeat,
  repeating-linear-gradient(to right, transparent 0, transparent calc(9.0909% - 1px),
    var(--field-50) calc(9.0909% - 1px), var(--field-50) 9.0909%) bottom / 100% 8px no-repeat; }
.fieldbig .fcap { display: none; }
/* The down, the spot, the badge and the last play fade in when they change, as
   the caption does on a scoreboard row. */
[data-sit].upd { animation: fcapIn .35s ease-out; }
/* The football beside the side with the ball in the line score. */
.linescore .lsposs { width: 18px; height: 11px; margin-left: 6px; vertical-align: -1px; display: inline-block; }

/* ---- your leagues: the edit pill and the picker ----
   The pill is a button dressed as the others, pinned beside the scrolling strip
   so it stays on screen, and hidden until leagues.js is running, since without
   it there is nothing for it to open. The dialog takes the palette variables, so
   it follows the theme like every card. */
.leaguebar { display: flex; align-items: center; }
.leaguebar .leaguescroll { flex: 1; min-width: 0; }
/* A control, not another league: small capitals in an outlined capsule, so it
   reads as the thing that edits the row rather than a member of it. */
.editleagues { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px 0 10px;
               margin: 0 4px 0 8px; flex-shrink: 0; border: 1px solid rgba(255, 255, 255, 0.55); border-radius: 999px;
               background: rgba(255, 255, 255, 0.14); color: var(--header-ink-active); font: inherit; font-size: 12.5px;
               font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; line-height: 1; cursor: pointer; }
.editleagues:hover { background: rgba(255, 255, 255, 0.26); }
.editleagues[hidden] { display: none; }
/* The star at the front of the bar: the reader's own games across every league
   (/your-games), as the apps keep a Favorites entry among their leagues. On the
   header blue in both themes like the pills beside it, and chipped like the
   chosen pill when its page is the one open. */
.yourstar { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
            width: 40px; height: 38px; margin-left: 4px; border-radius: 12px; color: var(--header-ink); }
.yourstar:hover { color: var(--header-ink-active); background: rgba(255, 255, 255, 0.12); text-decoration: none; }
.yourstar svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linejoin: round; }
.yourstar.on { background: rgba(242, 242, 242, 0.80); color: var(--header); }
.yourstar.on svg { fill: currentColor; }
/* The Your games page's teams, each a link to its page, where its star is. */
.yourteams { display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 14px; }
.teamchip { display: inline-flex; align-items: baseline; gap: 6px; padding: 6px 12px; border-radius: 999px;
            border: 1px solid var(--border); color: var(--ink); font-weight: 600; font-size: 14px; }
.teamchip small { color: var(--ink-soft); font-weight: 600; font-size: 11.5px; }
.teamchip:hover { background: var(--surface-hover); text-decoration: none; }
.editleagues .eicon { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.6;
                      stroke-linecap: round; stroke-linejoin: round; }
.lpick { border: 0; border-radius: 14px; padding: 0; width: min(440px, calc(100vw - 32px));
         background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }
.lpick::backdrop { background: rgba(0, 0, 0, 0.45); }
.lpickform { display: flex; flex-direction: column; max-height: min(80vh, 640px); }
.lpickhead { display: flex; align-items: center; gap: 8px; padding: 14px 16px 4px; }
.lpickhead h2 { margin: 0; font-size: 16px; }
.lpickx { margin-left: auto; width: 32px; height: 32px; border-radius: 8px; border: 0; background: var(--chip);
          color: var(--ink); font-size: 20px; line-height: 1; cursor: pointer; }
.lpickhint { margin: 0 16px 8px; font-size: 12.5px; color: var(--ink-soft); }
.lpickbody { overflow-y: auto; padding: 0 8px 8px; }
.lpgroup { padding: 10px 8px 4px; font-size: 11px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
           color: var(--ink-soft); }
.lpnone { padding: 4px 8px 8px; font-size: 13px; color: var(--ink-soft); }
/* A row can be taken hold of anywhere. pan-y leaves a finger free to scroll the
   list; a press and hold lifts the row instead, and leagues.js then keeps the
   browser from scrolling for the rest of the drag. No text selection or press
   callout, since either would fight the drag. */
.lprow { display: flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: 8px; touch-action: pan-y;
         user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.lprow:hover { background: var(--surface-hover); }
.lprow label { display: flex; align-items: center; gap: 10px; flex: 1; min-height: 34px; cursor: pointer; font-size: 14.5px; }
.lprow input { width: 18px; height: 18px; accent-color: var(--tint); margin: 0; flex-shrink: 0; }
[data-showing] .lprow { cursor: grab; }
/* The grip: three lines, as the phones draw their reorder control, and the
   keyboard's way to move a row. */
.lphandle { width: 28px; height: 34px; display: flex; align-items: center; justify-content: center; flex-shrink: 0;
            color: var(--ink-soft); border-radius: 6px; }
.lphandle:focus-visible { outline: 2px solid var(--tint); outline-offset: -2px; }
.lpgrip { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }
.lpflag { width: 18px; height: 13px; border-radius: 2px; flex-shrink: 0; }
/* The lifted row rides with the pointer (leagues.js sets its transform) above
   the others, tinted and shadowed so there is no doubt which one is moving. */
.lprow.dragging { background: var(--tint-wash); outline: 1px solid var(--tint); box-shadow: 0 8px 22px rgba(0, 0, 0, 0.22);
                  position: relative; z-index: 2; cursor: grabbing; }
.lprow.dragging:hover { background: var(--tint-wash); }
.lpickfoot { display: flex; gap: 8px; padding: 10px 16px 14px; border-top: 1px solid var(--hairline); }
.lpickgap { flex: 1; }
.lpickbtn { padding: 8px 14px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface);
            color: var(--ink); font: inherit; font-size: 14px; cursor: pointer; }
.lpickbtn:hover { background: var(--surface-hover); }
.lpickbtn.primary { background: var(--tint); border-color: var(--tint); color: #fff; font-weight: 600; }
.lpickbtn.primary:hover { background: var(--tint-hover); border-color: var(--tint-hover); }

/* A league with nothing on today is one line on the front page, not an empty card. */
.homerow.homeoff + .homerow.homeoff { margin-top: 0; }
.homerow.homeoff .homehead { margin: 12px 0 0 4px; }
.homeoffnote { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--ink-soft); }

/* /leagues: every league by sport, a row each — its mark, its name, then the
   pages it has. On a narrow screen the pages drop under the name. */
.lgx { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-top: 1px solid var(--hairline); font-size: 14.5px; }
.lgx:first-child { border-top: 0; }
.lgxmark { display: flex; width: 20px; justify-content: center; color: var(--ink-muted); flex-shrink: 0; }
.lgxname { font-weight: 600; flex: 1 1 auto; min-width: 0; }
.lgxlinks { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px 14px; font-size: 12.5px; color: var(--ink-soft); }
@media (max-width: 520px) {
  .lgx { flex-wrap: wrap; }
  .lgxlinks { flex-basis: 100%; justify-content: flex-start; padding-left: 32px; }
}

/* A tournament's match days: a chip a day, wrapping, with the day's match count. */
.days { display: flex; flex-wrap: wrap; gap: 6px; }
.day { font-size: 12.5px; font-weight: 600; color: var(--ink-soft); padding: 5px 10px; border-radius: 8px;
  background: var(--surface); border: 1px solid var(--hairline); }
.day span { color: var(--ink-muted); font-weight: 400; margin-left: 4px; }
.day:hover { background: var(--surface-hover); color: var(--ink); text-decoration: none; }
/* A strip card's score before the game starts: rendered, so the poll only has
   to unhide it when the first one is scored. */
.sscore.off { display: none; }

/* A tennis row: the same row as a game, with the sets in columns where a game
   has its score, and the flag where a club has its logo. Flags come in their
   own proportions, 200 wide and anywhere from about 100 to 200 high; a box of
   the common 3:2 crops the few that differ, so every name starts at the same
   place. The hairline keeps a white field from melting into the card. */
.tflag { width: 21px; height: 14px; flex-shrink: 0; display: flex; }
.tflag img { width: 100%; height: 100%; object-fit: cover; border-radius: 2px; display: block;
  box-shadow: 0 0 0 1px var(--hairline); }
.trank { font-size: 11.5px; color: var(--ink-soft); flex-shrink: 0; }
.tserve { width: 6px; height: 6px; border-radius: 3px; background: var(--green); flex-shrink: 0; }
.tserve.off { visibility: hidden; }
.tsets { display: flex; gap: 2px; flex-shrink: 0; }
.tset { width: 18px; text-align: center; font-size: 15px; font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.tset.won { color: var(--ink); font-weight: 700; }
.tset sup { font-size: 9px; margin-left: 1px; font-weight: 400; }
/* A set a tiebreak decided carries both players' points, so it is as wide on
   both lines as a game count and its superscript, and the columns stay in line. */
.tset.tb { width: 25px; }
.tgame { width: 24px; text-align: right; font-size: 13px; font-weight: 600; color: var(--green);
  font-variant-numeric: tabular-nums; flex-shrink: 0; }
/* Under the name: the flag's width and the row's gap. */
.tround { font-size: 11.5px; color: var(--ink-soft); margin-top: 4px; padding-left: 27px; }

/* A tennis match page: the flag where a club has its logo, and the sets as a
   line score in the half of the page beside the book's line. */
.tflaghead { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; flex-shrink: 0; }
.tflaghead img { width: 42px; height: 28px; object-fit: cover; border-radius: 4px; display: block;
  box-shadow: 0 0 0 1px var(--hairline); }
/* A tennis score that just changed, marked as a game's score is: the games of
   a set on a row or in the grid, the point score, and the sets won in a match
   page's head. The same palette color and the same quick fade. */
.tset, .tgame, .tsetgrid td, [data-sets-score], [data-points] { transition: background-color .12s ease-out; }
.tset.flash, .tgame.flash, .tsetgrid td.flash, [data-sets-score].flash, [data-points].flash {
  background-color: var(--flash); border-radius: 3px; }
@media (prefers-reduced-motion: reduce) {
  .tset, .tgame, .tsetgrid td, [data-sets-score], [data-points] { transition: none; }
}
.tsetgrid { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.tsetgrid th, .tsetgrid td { padding: 8px 10px; text-align: center; border-top: 1px solid var(--hairline); }
.tsetgrid thead th { border-top: 0; font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.tsetgrid .l { text-align: left; }
.tsetgrid td { font-size: 16px; color: var(--ink-soft); }
.tsetgrid td.won, .tsetgrid td a.won { color: var(--ink); font-weight: 700; }
.tsetgrid sup { font-size: 10px; margin-left: 1px; font-weight: 400; }
/* Both names in the set grid in ink, the winner in bold: link blue on the
   loser alone read as though only one of them were a link. */
.tsetgrid td.l a { color: var(--ink); }

/* A tennis tournament: a section heading that leads to the tournament, the
   champion under the title once the final is over, and qualifying folded. */
.sect a { color: inherit; }
.sect a:hover { text-decoration: underline; }
.tchamp { margin: 12px 2px 0; font-size: 15px; }
.tchamp a { font-weight: 700; }
.tqual > summary { cursor: pointer; list-style: revert; }
/* A tennis card on the front page: the flag where a game card has a crest. */
.scard .tflag { width: 18px; height: 12px; }

/* A tennis player's page: the flag on the corner of the portrait, where a
   club player's carries the crest, square-cornered and a little wider. */
.plbadge.plflag { width: 30px; height: 21px; padding: 2px; border-radius: 4px; }
.plbadge.plflag img { object-fit: cover; border-radius: 2px; }

/* The tennis rankings: two thirds of the page, the columns spread across it,
   the name taking the most room and the country read left to right. On a
   narrow screen, the whole width. */
.card.rankcard { max-width: 66.667%; }
.stand.fitw th.l { width: 44%; }
.stand.fitw th.c { width: 26%; }
.stand.fitw .c { text-align: left; padding-left: 12px; }
@media (max-width: 900px) { .card.rankcard { max-width: none; } }

/* A match page's way to the two players' head-to-head. */
.tlink { margin: 14px 2px; font-size: 14px; }

/* A tennis match's stats on a phone. Its ratios carry their fractions, as in
   85% (68/80), and with the team tables' fixed shares and padding the three
   columns ran wider than the screen. Here the columns size to what they hold,
   sit closer together, and wrap on the narrowest phones rather than push the
   page sideways. */
@media (max-width: 900px) {
  .tstats.tmstats td, .tstats.tmstats th { width: auto; white-space: normal; }
  .tstats.tmstats td:first-child, .tstats.tmstats th:first-child { padding-right: 6px; }
  .tstats.tmstats td:last-child, .tstats.tmstats th:last-child { padding-left: 6px; }
  .tstats.tmstats .mid { padding-left: 6px; padding-right: 6px; }
}

/* A head-to-head: each player's portrait where a match page has the flag, the
   flag on its corner as on the player's own page. */
.plface.h2hface { width: 48px; height: 48px; }
.h2hface .plbadge.plflag { width: 24px; height: 17px; right: -6px; bottom: -3px; padding: 1px; }

/* A golf leaderboard: position, the player, score to par, the current round
   and holes played, then the strokes of each round and the total. On a phone
   it is wider than the screen: position and player stay put and the rest
   slides under them, so the rounds and the total are a swipe away. The
   borders are the cells' own (separate, not collapsed) so that they move with
   the cells that stay put. */
.glb { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 14px; font-variant-numeric: tabular-nums; }
.glb th { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-soft); padding: 9px 6px 7px; text-align: center; white-space: nowrap; }
.glb td { padding: 7px 6px; text-align: center; border-top: 1px solid var(--hairline-soft); white-space: nowrap;
  transition: background-color .12s ease-out; }
.glb .l { text-align: left; }
.glb th:first-child, .glb td:first-child { padding-left: 16px; }
.glb th:last-child, .glb td:last-child { padding-right: 16px; }
.glb .gpos { width: 44px; text-align: left; color: var(--ink-muted); font-weight: 600; }
.glb td[data-par] { font-weight: 700; }
.glb td.grd { color: var(--ink-soft); }
.glb tr.gout td { color: var(--ink-soft); font-weight: 400; }
.glb tr.gcut td { padding: 6px 16px; font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-soft); background: var(--section); text-align: left; }
/* The line's words stay in view however far the table is swiped. */
.glb tr.gcut span { position: sticky; left: 12px; }
/* A score the poll has just changed, marked as a game's is. */
.glb td.flash { background-color: var(--flash); }
@media (prefers-reduced-motion: reduce) { .glb td { transition: none; } }
/* Rounded like its card, so the cells that stay put never square its corners. */
.glbcard .scroller { border-radius: inherit; }
.glb .gsn { display: none; }
@media (max-width: 600px) {
  .glb .gfn { display: none; }
  .glb .gsn { display: inline; }
  .glb th, .glb td { padding-left: 5px; padding-right: 5px; }
  .glb th:first-child, .glb td:first-child { padding-left: 12px; }
  .glb th:last-child, .glb td:last-child { padding-right: 12px; }
  /* Position and player stay put. The position column is fixed wide enough
     for "T145", so the player's always starts where it ends. */
  .glb .gpos, .glb .l { position: sticky; z-index: 1; background: var(--surface); }
  .glb .gpos { left: 0; width: 52px; }
  .glb .l { left: 52px; box-shadow: 1px 0 0 var(--hairline); }
}
.glbcard { margin-top: 16px; }
.gsub { font-size: 14px; color: var(--ink-soft); margin: 2px 0 0 2px; }
.gstatus { display: flex; align-items: center; gap: 6px; margin: 10px 2px 0; font-size: 14px; font-weight: 600; }
.gsoft { color: var(--ink-soft); font-weight: 400; }
.gnext { margin: 8px 2px 0; font-size: 14px; }

/* An MMA card: each fight is its two fighters on their own lines, the flag
   where a club would have its logo and the record beside the name, with how it
   ended under them — or, before the night, the money line on each.

   Two thirds of the width, as the tennis rankings are: a name, a record and a
   price drift apart with nothing between them across a whole desktop. */
.card.fcard { max-width: 66.667%; }
@media (max-width: 900px) { .card.fcard { max-width: none; } }
.fight { padding: 11px 16px; border-top: 1px solid var(--hairline-soft); }
.fight:first-child { border-top: 0; }
.fclass { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: 5px; }
.fside { display: flex; align-items: center; gap: 9px; padding: 3px 0; font-size: 15px; }
.fname { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The winner, which is also why no W or L is printed beside them. */
.fside.won .fname { font-weight: 700; }
.frec { color: var(--ink-soft); font-size: 13px; font-variant-numeric: tabular-nums; }
.fml { min-width: 54px; text-align: right; font-variant-numeric: tabular-nums; }
.fres { margin-top: 6px; font-size: 13px; color: var(--ink-soft); }
.fcards { margin-top: 2px; font-size: 12px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
/* The cards list, like a golf schedule: the venue goes on a phone, where the
   event's own name is what a reader is looking for. */
.csched td.c, .csched th.c { text-align: left; }
@media (max-width: 600px) {
  .fight { padding-left: 12px; padding-right: 12px; }
  .csched td.c, .csched th.c { display: none; }
  .csched td.l { white-space: normal; }
  .csched .stname { white-space: normal; overflow: visible; }
}

/* Boxing's odds board: a night's fights in one table, so each book's column
   lines up down the night; the time over each pair, and the best price on each
   fighter in green (.grn), as the apps mark it. The name column takes the slack,
   so the prices sit together at the right. */
.bxodds { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 15px;
  font-variant-numeric: tabular-nums; }
.bxodds th { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-soft); padding: 9px 10px 7px; text-align: right; white-space: nowrap; }
.bxodds td { padding: 3px 10px; text-align: right; white-space: nowrap; }
.bxodds .l { text-align: left; }
.bxodds td.l { width: 100%; }
.bxodds th:first-child, .bxodds td:first-child { padding-left: 16px; }
.bxodds th:last-child, .bxodds td:last-child { padding-right: 16px; }
.bxodds tr.bxwhen td { padding-top: 10px; padding-bottom: 2px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; color: var(--ink-soft); text-align: left;
  border-top: 1px solid var(--hairline-soft); }
.bxodds tbody tr:last-child td { padding-bottom: 9px; }
.bxodds .gsn { display: none; }
@media (max-width: 600px) {
  .bxodds .gfn { display: none; }
  .bxodds .gsn { display: inline; }
  .bxodds th, .bxodds td { padding-left: 6px; padding-right: 6px; }
  .bxodds th:first-child, .bxodds td:first-child { padding-left: 12px; }
  .bxodds th:last-child, .bxodds td:last-child { padding-right: 12px; }
}

/* Racing: the schedule is a list like MMA's (.rsched), with the series in a
   column where a league runs more than one; a race's result is its running
   order (.rres), the position first, padded as the name column would be. */
.rsched td.c, .rsched th.c { text-align: left; }
.rsched .rser { text-align: left; }
.rsched td.rser { color: var(--ink-soft); }
.rres th.pos, .rres td.pos { text-align: left; padding-left: 16px; width: 44px;
  font-weight: 600; color: var(--ink-muted); }
.rres .gsn { display: none; }
@media (max-width: 600px) {
  .rsched td.c, .rsched th.c { display: none; }
  .rsched td.l { white-space: normal; }
  .rsched .stname { white-space: normal; overflow: visible; }
  .rres .gfn { display: none; }
  .rres .gsn { display: inline; }
  .rres th.pos, .rres td.pos { padding-left: 12px; }
}
/* The schedule's locations read left to right, as the rankings' countries do.
   On a phone they go: a course and a town is the widest cell in the table, and
   the tournament, its dates and its par are what fit. */
.gsched td.c, .gsched th.c { text-align: left; }
@media (max-width: 600px) {
  .gsched td.c, .gsched th.c { display: none; }
  /* A long name wraps rather than push the dates off the card. */
  .gsched td.l { white-space: normal; }
  .gsched .stname { white-space: normal; overflow: visible; }
}

/* ---- sign-in and the account page (ACCOUNTS-PLAN.md, "The site") ---- */
/* The card is narrow and centred: a form, not a page of data. */
.card.signin { max-width: 440px; margin: 0 auto; padding: 22px 26px; }
.signerr { background: rgba(205, 0, 10, 0.08); color: var(--red); border-radius: 8px; padding: 10px 12px;
           font-size: 14px; line-height: 1.45; margin-bottom: 14px; }
.provbtns { display: flex; flex-direction: column; gap: 10px; align-items: center; }
#appleid-signin { width: 300px; height: 40px; cursor: pointer; }
#google-signin { min-height: 40px; }
.or { text-align: center; color: var(--ink-soft); font-size: 11px; font-weight: 700; margin: 16px 0;
      text-transform: uppercase; letter-spacing: 0.08em; }
.sform { display: block; }
.sform + .sform { margin-top: 10px; }
.sfield { display: flex; flex-direction: column; gap: 6px; }
.sfield label { font-size: 12.5px; font-weight: 600; color: var(--ink-muted); }
.sfield input { font: inherit; font-size: 16px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;
                background: var(--surface); color: var(--ink); }
.sfield input:focus { outline: 2px solid var(--tint); outline-offset: 1px; border-color: var(--tint); }
.sfield input.code { font-size: 26px; letter-spacing: 0.3em; text-align: center; font-variant-numeric: tabular-nums; }
.sbtn { display: block; width: 100%; margin-top: 12px; padding: 11px 14px; border-radius: 10px; border: 1px solid var(--tint);
        background: var(--tint); color: var(--on-accent); font: inherit; font-weight: 600; cursor: pointer; }
.sbtn:hover { background: var(--tint-hover); border-color: var(--tint-hover); }
.sbtn.quiet { background: var(--surface); color: var(--tint); }
.sbtn.quiet:hover { background: var(--surface-hover); }
.snote { font-size: 12.5px; color: var(--ink-soft); margin: 12px 0 0; line-height: 1.5; }
.snote a { font-weight: 600; }
/* While the site checks a provider's token or sends the code: a status line
   with a spinner in place of a page that looks stuck, and nothing else on the
   card takes a press until the answer is back. */
.signwait { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 10px 12px; margin-bottom: 14px;
            border-radius: 8px; background: var(--tint-wash); color: var(--ink); font-size: 14px; font-weight: 600; }
.signwait[hidden] { display: none; }
.spin { flex: none; width: 16px; height: 16px; border: 2px solid var(--tint); border-right-color: transparent;
        border-radius: 50%; animation: signspin 0.8s linear infinite; }
@keyframes signspin { to { transform: rotate(360deg); } }
.signin.busy .provbtns, .signin.busy .or, .signin.busy .sform { opacity: 0.45; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .spin { animation: none; border-right-color: var(--tint); opacity: 0.6; } }
/* Deleting: the red button on its own page, and the link to that page. A link
   styled as a button is centred and loses its underline. */
a.sbtn { text-align: center; text-decoration: none; }
.sbtn.danger { background: var(--red); border-color: var(--red); color: #fff; }
.sbtn.danger:hover { background: #a80008; border-color: #a80008; }
a.lbtn { text-decoration: none; }
.lbtn.danger { color: var(--red); }
/* Favorite teams and tracked games (lists.js): a pill with an outline icon,
   filled and tinted while it is on. Shipped hidden and revealed by the script,
   so the display rules below must not override [hidden]. */
.favbtn { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px 6px 10px; border-radius: 999px;
          border: 1px solid var(--border); background: var(--surface); color: var(--ink); font: inherit;
          font-size: 13px; font-weight: 600; line-height: 1.2; cursor: pointer; white-space: nowrap; }
.favbtn svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8;
              stroke-linejoin: round; }
.favbtn:hover { background: var(--surface-hover); }
.favbtn[aria-pressed="true"] { border-color: var(--tint); color: var(--tint); background: var(--tint-wash); }
.favbtn[aria-pressed="true"] svg { fill: currentColor; }
.favbtn:disabled { opacity: 0.65; cursor: progress; }
.favbtn:focus-visible { outline: 2px solid var(--tint); outline-offset: 2px; }
.favbtn[hidden], .gdacts[hidden] { display: none; }
/* Your games (lists.js) hides a scoreboard section it has emptied, heading and
   card; the heading is a flex box, which would otherwise outrank [hidden]. */
.sect[hidden], .card[hidden] { display: none; }
.gdacts { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 2px; }
.teamhead .teamfav { margin-left: auto; }
/* The account page: sections under one card, forms inline with the text. */
.card.acct { max-width: 640px; }
.acct .sform.inline { max-width: 340px; }
.acct .sform.inline .sbtn { width: auto; margin-top: 8px; padding: 7px 14px; }
.acct ul.plain { list-style: none; padding: 0; margin: 0 0 14px; }
.acct ul.plain li { padding: 6px 0; border-top: 1px solid var(--hairline-soft); }
.acct table.sessions { width: 100%; border-collapse: collapse; font-size: 14px; margin: 0 0 8px; }
.acct .sessions th { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
                     color: var(--ink-soft); text-align: left; padding: 6px 6px 6px 0; }
.acct .sessions td { padding: 8px 6px 8px 0; border-top: 1px solid var(--hairline-soft); vertical-align: top; }
.acct .sessions td:last-child { text-align: right; }
.acct .sessions .sub { display: block; }
.acct .tag { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 9px; background: var(--tint-wash);
             color: var(--tint); font-size: 11px; font-weight: 600; }
.acct form.inline { display: inline; }
.acct .lbtn { background: none; border: 0; padding: 0; color: var(--tint); font: inherit; font-weight: 600; cursor: pointer; }
.acct .lbtn:hover { text-decoration: underline; }