
/* Magic Number's own faces, served same-origin from this site's assets.
   The no-third-party-request rule holds: nothing here reaches a font host.
   Served rather than inlined for the reason the stylesheet itself is - at
   ~700 generated pages an inlined face is the payload duplicated per page
   (reports/decision-magic-number-brand.md). Instrument Sans is one variable
   file covering 400-700, so the whole brand costs three requests, ~50 KB,
   once per visitor. */
@font-face{font-family:'Instrument Sans';font-style:normal;font-weight:400 700;
  font-display:swap;src:url('fonts/instrument-sans-var.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;
  font-display:swap;src:url('fonts/ibm-plex-mono-400.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;
  font-display:swap;src:url('fonts/ibm-plex-mono-600.woff2') format('woff2');}

:root {
  --paper:#F8FAFD; --surface:#FFFFFF; --ink:#141923; --muted:#5E6875;
  --faint:#8B94A1; --line:#E4E9F1; --line-strong:#D0D8E4;
  --accent:#2D50D2; --mark-track:#AFBEE7;
  --qualify:#1F9D6B; --qualify-strong:#0E7A52;
  --out:#C4626A; --out-strong:#A8201A; --bubble:#D98A2B;
  --heat-lo:#E7B84D; --heat-hi:#D64828;
  --series-elo:#2D50D2; --series-base:#D98A2B;
  --radius:8px; --maxw:1000px;
  --sans:'Instrument Sans',ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
}
@media (prefers-color-scheme:dark){
  :root{ --paper:#0C111A; --surface:#151C28; --ink:#EDF1F7; --muted:#A0AAB8;
    --faint:#6B7482; --line:#232B37; --line-strong:#33405A; --accent:#7894FF;
    --mark-track:#455989;
    --qualify:#37B583; --qualify-strong:#5CD3A2;
    --out:#D98A8F; --out-strong:#FF7A6B; --bubble:#E7A24C;
    --heat-lo:#C9922E; --heat-hi:#E0583A;
    --series-elo:#7894FF; --series-base:#C4862F; }
}
:root[data-theme="light"]{ --paper:#F8FAFD; --surface:#FFFFFF; --ink:#141923; --muted:#5E6875;
  --faint:#8B94A1; --line:#E4E9F1; --line-strong:#D0D8E4; --accent:#2D50D2;
  --mark-track:#AFBEE7;
  --qualify:#1F9D6B; --qualify-strong:#0E7A52; --out:#C4626A; --out-strong:#A8201A;
  --bubble:#D98A2B; --heat-lo:#E7B84D; --heat-hi:#D64828;
  --series-elo:#2D50D2; --series-base:#D98A2B; }
:root[data-theme="dark"]{ --paper:#0C111A; --surface:#151C28; --ink:#EDF1F7; --muted:#A0AAB8;
  --faint:#6B7482; --line:#232B37; --line-strong:#33405A; --accent:#7894FF;
  --mark-track:#455989;
  --qualify:#37B583; --qualify-strong:#5CD3A2; --out:#D98A8F; --out-strong:#FF7A6B;
  --bubble:#E7A24C; --heat-lo:#C9922E; --heat-hi:#E0583A;
    --series-elo:#7894FF; --series-base:#C4862F; }

*{box-sizing:border-box}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--sans);
  font-size:16px;line-height:1.5;-webkit-font-smoothing:antialiased;
  font-variant-numeric:tabular-nums;}
/* `--wrap-pad` is the side gutter every page's content is inset by, named so a
   full-bleed child can cancel it exactly instead of restating the value. The
   sticky section rail (AUG-732) restated it, agreed with this line, and
   disagreed with the phone override below by 1px - which is a horizontally
   scrollable document at 390px, from a bar whose whole job is to sit still. */
.wrap{--wrap-pad:clamp(16px,4vw,32px);max-width:var(--maxw);margin:0 auto;
  padding:clamp(20px,4vw,44px) var(--wrap-pad);}
h2{font-size:1.32rem;letter-spacing:0;margin:0;text-wrap:balance;}
h3{margin:0;font-size:1rem;letter-spacing:0;}
p{margin:0;color:var(--muted);}
/* The site's default anchor colour, decided here rather than inherited from the
   user agent (AUG-511). Most anchors on this site are a whole surface - a game
   card, a race row, a crest, a club button - and each means to carry its
   container's ink, so `inherit` is the default that suits them; a link meant to
   *read* as a link says so where it lives (`.site-utility a`, `.notice-prose a`,
   `.empty-panel a`, `.home-foot-prov a`). With no rule here at all those anchors
   fell back to the user agent's bright blue underline, which on a dark surface
   is what every reader saw in the public trust footer. Bare-element
   specificity, so every component rule below still wins. */
a{color:inherit;}

/* Taken out of the picture, never out of the page (AUG-467). A probability
   whose only visible label is the column header above it or the heading two
   blocks up is fully labelled for a sighted reader and bare for anyone hearing
   the number read on its own, so the race's name rides along here. Clipped
   rather than `display:none` or `hidden`, both of which take it out of the
   accessibility tree as well - which would defeat the whole point. */
.vh{position:absolute;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;}

.mast{display:flex;flex-wrap:wrap;gap:14px 24px;justify-content:space-between;
  align-items:flex-end;padding-bottom:18px;border-bottom:1px solid var(--line);}

/* Masthead search (AUG-300; compacted to an icon disclosure AUG-337). The
   wide field is collapsed behind a magnifier so the masthead stays compact
   site-wide; opening the native <details> reveals it as a floating panel, so
   the popover overlays rather than reflowing the header. Sits last on desktop
   (order:2, right of the nav) and beside the wordmark on a phone (order:0).
   Text floors at 0.75rem = 12px, the smallest anything a reader reads may
   render. */
.mast-search{position:relative;flex:none;order:2;}
.mast-search>summary{display:inline-flex;align-items:center;justify-content:center;
  min-height:40px;min-width:40px;padding:0 10px;border:1px solid var(--line-strong);
  border-radius:8px;background:var(--surface);color:var(--ink);cursor:pointer;
  list-style:none;user-select:none;}
.mast-search>summary::-webkit-details-marker{display:none;}
.mast-search>summary:hover{border-color:var(--accent);}
.mast-search>summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.mast-search[open]>summary{border-color:var(--accent);
  background:color-mix(in srgb,var(--accent) 8%,var(--surface));}
.mk-search{width:18px;height:18px;}
.mast-search-panel{position:absolute;z-index:40;top:calc(100% + 8px);right:0;
  width:min(340px,calc(100vw - 32px));}
.mast-search input{width:100%;font:inherit;font-size:0.95rem;color:var(--ink);
  background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--radius);padding:8px 12px;outline:none;}
.mast-search input::placeholder{color:var(--muted);opacity:1;}
.mast-search input:focus{border-color:var(--accent);
  box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 22%,transparent);}
.search-pop{position:absolute;z-index:40;top:calc(100% + 6px);left:0;right:0;
  background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--radius);box-shadow:0 12px 32px rgba(8,14,28,.16);
  max-height:min(60vh,360px);overflow-y:auto;}
.search-pop[hidden]{display:none;}
.sp-label{font-family:var(--mono);font-size:0.75rem;text-transform:uppercase;
  color:var(--muted);padding:8px 12px 2px;}
.sp-label~.sp-label{border-top:1px solid var(--line);margin-top:4px;}
.sp-item{display:flex;align-items:center;gap:9px;padding:8px 12px;
  color:var(--ink);text-decoration:none;font-size:0.92rem;min-height:36px;}
.sp-item .sp-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.sp-item .sp-ctx{margin-left:auto;font-family:var(--mono);font-size:0.75rem;
  color:var(--muted);white-space:nowrap;flex:none;}
.sp-item.sel,.sp-item:hover{background:color-mix(in srgb,var(--accent) 10%,transparent);}
.sp-item:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;}
.sp-crest{width:18px;height:18px;flex:none;}
.sp-badge{min-width:20px;height:20px;flex:none;box-sizing:border-box;
  display:inline-flex;align-items:center;justify-content:center;padding:0 3px;
  border:1.5px solid;border-radius:5px;font-weight:700;font-size:0.75rem;
  line-height:1;}
.sp-code{font-family:var(--mono);font-size:0.75rem;color:var(--muted);
  border:1px solid var(--line-strong);border-radius:4px;padding:0 4px;flex:none;}
.sp-empty{padding:10px 12px;color:var(--muted);font-size:0.9rem;}
@media (max-width:560px){
  .mast-search{order:0;}
  /* A floating panel is concise beside desktop navigation, but it covers the
     reader's page on a phone. Once opened, give the disclosure its own row
     and let its input and results extend the masthead instead. */
  .mast-search[open]{flex-basis:100%;}
  .mast-search[open] .mast-search-panel{position:static;width:100%;margin-top:8px;}
  .mast-search[open] .search-pop{position:static;margin-top:6px;}
  .sp-item{min-height:44px;}
}

/* Tap-to-reveal definition affordance (AUG-731): a small `?` beside any term
   that opens its short definition in place, native `<details>` so it opens
   and closes with no script at all. `.def` itself carries no position: an
   absolutely-positioned bottom sheet pinned to the viewport shipped first and
   was contained and never pushed layout, but Ford reviewed it at 390px and
   called it wrong - the answer landed ~370px below the question, under the
   next card down, instead of beside it. `.def-pop` now resolves to the
   nearest ancestor the CALLER marks `position:relative` (`.tp-cd-card` below
   is the reference caller) and spans exactly that box, flush with its edges -
   "beneath the term's own card", his words. That box is already guaranteed to
   fit inside `.wrap`'s gutter through ordinary block layout, so the popover
   inherits containment for free and needs no gutter math of its own (the
   `--wrap-pad` bug AUG-732 hit doesn't apply here: nothing here restates a
   gutter, because nothing here needs one). A caller with no positioned
   ancestor gets a popover relative to the page instead of the card, which is
   a placement bug, not a containment one - `left:0;right:0` still cannot
   exceed 100vw. Wire a new term inside a `position:relative` block sized to
   what the popover should sit under.

   `.def-pop` resets every inherited text property rather than relying on the
   defaults: the affordance composes into whatever label already carries the
   term (AUG-731's team-page caller nests it inside `.tp-kicker`, an uppercase
   mono eyebrow), and without the reset the popover rendered the definition in
   that ancestor's small caps mono instead of a readable sentence - caught in
   this issue's own browser-check screenshot, not a hypothetical. */
.def{display:inline-block;margin-left:3px;vertical-align:middle;}
.def>summary{display:inline-flex;align-items:center;justify-content:center;
  width:17px;height:17px;border-radius:50%;border:1px solid var(--line-strong);
  background:var(--surface);color:var(--muted);font:700 .68rem/1 var(--mono);
  cursor:pointer;list-style:none;user-select:none;}
.def>summary::-webkit-details-marker{display:none;}
.def>summary:hover{border-color:var(--accent);color:var(--accent);}
.def>summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.def[open]>summary{border-color:var(--accent);color:var(--accent);
  background:color-mix(in srgb,var(--accent) 8%,var(--surface));}
.def-pop{position:absolute;z-index:40;top:calc(100% + 8px);left:0;right:0;
  max-height:50vh;overflow-y:auto;
  background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--radius);box-shadow:0 12px 32px rgba(8,14,28,.16);
  padding:9px 11px;font-size:.85rem;line-height:1.4;color:var(--ink);
  font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-weight:400;
  text-transform:none;letter-spacing:normal;text-align:left;}
.brand{display:flex;flex-direction:column;gap:2px;}
.page-context{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:6px 20px;margin-top:12px;color:var(--muted);}
.page-context>.fresh{margin-left:auto;}
/* Wordmark: the mark beside the name, and the way home from every page
   (AUG-150). The mark is the number sign drawn in the product's own axis
   grammar - two probability tracks crossed by the two now-notches - so it is
   emblematic rather than a plotted chart, and is the one visual exempt from
   the to-scale rule (reports/decision-magic-number-brand.md). */
.mark{display:inline-flex;align-items:center;gap:9px;font-weight:650;font-size:1.5rem;
  letter-spacing:-0.015em;color:var(--ink);text-decoration:none;line-height:1;}
.mark svg{width:1.5em;height:1.5em;flex:none;}
.mark .mk-notch{fill:var(--ink);}
/* The unfilled track has its own token, not a line colour: at this size a
   hairline grey drops out and the "#" loses two of its four strokes. */
.mark .mk-track{fill:var(--mark-track);}
.mark .mk-fill,.mark .mk-bar{fill:var(--accent);}
/* The settled full stop. Cobalt on paper; a cobalt-ground surface overrides it
   to white, where the accent would vanish into its own background. */
.mark .mk-stop{color:var(--accent);}
.mark:hover .mk-fill,.mark:hover .mk-bar{
  fill:color-mix(in srgb,var(--accent) 82%,var(--ink));}
.mark:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px;}
/* The arrival (AUG-530). Once per visit the mark draws itself: both fills run
   to their values, then the clinch spark flares and is gone, leaving the
   resting mark every other surface uses. The spark exists only here - never in
   the favicon, a share card, or the preview - which is what keeps a sparkle out
   of the identity while still spending one on the moment the count lands.

   Styled only under the root flag `search.js` sets, so a scriptless reader gets
   the resting mark rather than a half-drawn one; the global
   prefers-reduced-motion rule above disables the whole thing. */
.mark .mk-spark{fill:var(--accent);opacity:0;
  transform-box:fill-box;transform-origin:center;}
[data-mark-arrive] .mark .mk-fill{
  animation:mk-draw 640ms cubic-bezier(.22,.9,.3,1) 120ms both;}
[data-mark-arrive] .mark .mk-spark{
  animation:mk-flare 740ms cubic-bezier(.3,.7,.3,1) 800ms both;}
@keyframes mk-draw{
  from{clip-path:inset(0 100% 0 0 round 6.5px);}
  to{clip-path:inset(0 0 0 0 round 6.5px);}}
@keyframes mk-flare{
  0%{opacity:0;transform:scale(.35);}
  25%{opacity:1;transform:scale(1.15);}
  45%{opacity:1;transform:scale(1);}
  100%{opacity:0;transform:scale(.7);}}
/* The competition-and-season line: machine-recorded metadata, so mono. */
.league{font-family:var(--mono);font-size:0.8rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:0;}
/* The brand promise. Deliberately NOT mono: mono means "recorded fact" in
   this system, and the tagline is the least machine-recorded sentence on the
   page. Sharing the metadata slot made the promise read as a snapshot id
   (AUG-189). */
.tagline{font-family:var(--sans);font-size:0.95rem;color:var(--muted);
  text-transform:none;letter-spacing:0;max-width:52ch;}
.fresh{display:grid;grid-template-columns:auto auto;justify-content:end;align-items:center;
  gap:1px 6px;text-align:right;font-size:0.82rem;color:var(--muted);}
.fresh .dot{width:8px;height:8px;border-radius:50%;display:inline-block;}
.fresh.fresh .dot{background:var(--qualify);} .fresh.recent .dot{background:var(--bubble);}
.fresh.stale .dot{background:var(--heat-hi);}
.fresh .sub{grid-column:2;font-family:var(--mono);font-size:0.75rem;color:var(--muted);letter-spacing:0;}

.stale{margin-top:16px;display:flex;align-items:center;gap:9px;padding:9px 14px;
  font-size:0.85rem;color:var(--ink);border-radius:var(--radius);
  background:color-mix(in srgb,var(--bubble) 14%,var(--surface));
  border:1px solid color-mix(in srgb,var(--bubble) 40%,transparent);}
.stale-dot{width:8px;height:8px;border-radius:50%;background:var(--bubble);flex:none;}

section{margin-top:clamp(30px,5vw,52px);}
.sec-head{margin-bottom:16px;} .sec-head p{max-width:62ch;margin-top:5px;font-size:0.95rem;}

.scroll{overflow-x:auto;}
table{width:100%;border-collapse:collapse;font-size:0.95rem;min-width:560px;}
thead th{text-align:left;font-size:0.75rem;text-transform:uppercase;letter-spacing:0;
  color:var(--muted);font-weight:600;padding:0 12px 8px;border-bottom:1px solid var(--line);}
th.num,td.num{text-align:right;} th.unc,td.unc{width:150px;}
tbody td{padding:11px 12px;border-bottom:1px solid var(--line);vertical-align:middle;}
td.rk{color:var(--muted);font-family:var(--mono);font-size:0.82rem;width:28px;}
td.club{font-weight:560;letter-spacing:0;}
td.num{font-weight:600;white-space:nowrap;}
td.title{color:var(--muted);font-weight:500;}
tbody tr:hover td{background:color-mix(in srgb,var(--accent) 5%,transparent);}
.badge{display:inline-block;margin-left:7px;padding:1px 6px;border-radius:99px;
  font-family:var(--mono);font-size:0.75rem;font-weight:600;letter-spacing:0;
  text-transform:uppercase;vertical-align:middle;}
.badge.clinched{background:color-mix(in srgb,var(--qualify-strong) 18%,transparent);color:var(--qualify-strong);}
.badge.out{background:color-mix(in srgb,var(--out-strong) 16%,transparent);color:var(--out-strong);}

.bar{display:inline-block;vertical-align:middle;width:64px;height:6px;margin-left:9px;
  background:var(--line-strong);border-radius:99px;overflow:hidden;}
.bar .fill{display:block;height:100%;background:var(--accent);border-radius:99px;}
.bar.sm{width:44px;height:5px;margin:0 6px;}

td.unc{padding-top:8px;padding-bottom:8px;}

/* Position state (docs/design-system.md): hue is direction, weight is
   certainty. The bold pair means mathematically settled, never a rounded
   probability, so it is always paired with a badge. */
.pos-clinched{color:var(--qualify-strong);font-weight:640;}
.pos-trending-in{color:var(--qualify);}
.pos-trending-out{color:var(--out);}
.pos-eliminated{color:var(--out-strong);font-weight:640;}
td.club a.pos-clinched,td.club a.pos-eliminated{text-decoration:underline;
  text-underline-offset:3px;text-decoration-thickness:2px;}
.range{display:block;font-family:var(--mono);font-size:0.75rem;color:var(--muted);
  margin-top:2px;letter-spacing:0;}

tr.cutline td{padding:0;border:0;}
/* The rule is the cutline; the words are its caption. Keeping the accent on
   the line and taking it off the text stops the caption reading as a link. */
tr.cutline span{display:block;margin:2px 0;padding:4px 12px;font-size:0.75rem;
  text-transform:uppercase;letter-spacing:0;color:var(--muted);font-weight:640;
  border-top:2px solid var(--accent);}

.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px;}
.game{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  padding:15px 16px;position:relative;overflow:hidden;}
.game::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:color-mix(in srgb,var(--heat-hi) calc(var(--heat)*100%),var(--heat-lo));}
.game header{display:flex;justify-content:space-between;align-items:baseline;gap:10px;}
.game .v{color:var(--muted);font-weight:400;}
.lev{font-family:var(--mono);font-size:0.75rem;font-weight:600;color:var(--ink);
  background:color-mix(in srgb,var(--heat-hi) calc(var(--heat)*22%),transparent);
  padding:2px 7px;border-radius:99px;white-space:nowrap;}
.ko{font-family:var(--mono);font-size:0.75rem;color:var(--muted);margin:3px 0 11px;
  letter-spacing:0;}
.scen{display:grid;grid-template-columns:1fr auto;gap:2px 10px;padding:7px 0;
  border-top:1px solid var(--line);align-items:center;font-size:0.82rem;}
.scen .res{font-weight:600;} .scen .p{font-family:var(--mono);font-size:0.75rem;color:var(--muted);text-align:right;}
.scen .eff{grid-column:1 / -1;display:flex;align-items:center;color:var(--muted);
  font-size:0.78rem;white-space:nowrap;}
.scen .eff:first-of-type{margin-top:3px;}

.tl-empty{background:var(--surface);border:1px dashed var(--line-strong);border-radius:var(--radius);
  padding:20px 18px;color:var(--muted);font-size:0.9rem;display:flex;align-items:center;gap:10px;}
.tl-empty code{font-family:var(--mono);font-size:0.85em;background:var(--paper);
  padding:1px 5px;border-radius:5px;border:1px solid var(--line);}
.empty-panel{grid-column:1 / -1;background:var(--surface);border:1px dashed var(--line-strong);
  border-radius:var(--radius);padding:18px;color:var(--muted);font-size:0.9rem;
  display:flex;align-items:flex-start;gap:11px;min-height:92px;}
.empty-panel strong{display:block;color:var(--ink);margin-bottom:2px;}
.empty-panel p{max-width:62ch;font-size:0.88rem;}
/* Prose links: the trust notices, and the one way out an empty panel offers a
   reader who landed somewhere with nothing on it - the 404's way home most of
   all (AUG-511). These are the anchors that must look clickable inside a
   paragraph, so they take the accent and keep the underline. */
.notice-prose a,.empty-panel a{color:var(--accent);text-underline-offset:3px;}
.notice-prose a:hover,.empty-panel a:hover{color:var(--ink);}
/* The four notice pages are the site's only run-of-paragraphs surface, and they
   were the one that never set its own rhythm: they inherited the global
   `p{margin:0}`, so About rendered as a single unbroken block (AUG-553). Every
   other prose surface here spaces itself (`.ex-states` gap, `.ex-note`
   margin) - these just never got the rule.
   The measure sits on the wrapper rather than on each paragraph because
   `.lede` capped itself at 74ch while the body paragraphs under it ran the
   full ~110ch column, and two line lengths in one column is the ragged edge a
   reader sees before they see anything else. One column, one measure. */
.notice-prose{max-width:74ch;}
.notice-prose p{line-height:1.62;}
.notice-prose p+p{margin-top:14px;}
.empty-mark{width:9px;height:9px;border-radius:50%;background:var(--bubble);flex:none;margin-top:6px;
  box-shadow:0 0 0 4px color-mix(in srgb,var(--bubble) 18%,transparent);}
.tl-dot{width:9px;height:9px;border-radius:50%;background:var(--accent);flex:none;
  box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 18%,transparent);}
.tl-chart{width:100%;min-width:600px;height:auto;display:block;}
.tl-chart .gl{stroke:var(--line);stroke-width:1;}
.tl-chart .yl,.tl-chart .xl,.tl-chart .el{fill:var(--muted);font-family:var(--mono);
  font-size:12px;}
.tl-chart .yl{text-anchor:end;} .tl-chart .yl.unit{fill:var(--muted);}
.tl-chart .xl{text-anchor:middle;} .tl-chart .el{fill:var(--muted);font-size:12px;}
.tl-chart .tl-line{fill:none;stroke-width:1;stroke-linejoin:round;stroke-linecap:round;}
.tl-chart .tl-line.q{stroke:var(--qualify);stroke-width:1.5;opacity:0.9;}
.tl-chart .tl-line.o{stroke:var(--out);opacity:0.55;}
.tl-chart .tl-line.qs{stroke:var(--qualify-strong);stroke-width:2.2;opacity:1;}
.tl-chart .tl-line.os{stroke:var(--out-strong);stroke-width:2.2;opacity:0.85;}
.tl-chart .tl-end{fill:var(--qualify);}
/* Leader line from a club's true end point out to its crest in the
   gutter: the label moves to stay readable, the datum does not. */
.tl-chart .tl-lead{stroke:var(--line-strong);stroke-width:1;}
/* Single-club (team-page) view: a thicker, confident line, still coloured by
   where the club actually sits (qualify vs out), plus a soft fill and a dot on
   every published cutoff. The crowded league chart keeps its layered widths. */
.tl-chart.tl-solo .tl-line{stroke-width:3;opacity:1;}
.tl-chart .tl-area{stroke:none;pointer-events:none;}
.tl-chart .tl-area.q,.tl-chart .tl-area.qs{fill:var(--qualify);opacity:0.12;}
.tl-chart .tl-area.o,.tl-chart .tl-area.os{fill:var(--out);opacity:0.12;}
.tl-chart .tl-pt{pointer-events:none;stroke:var(--surface);stroke-width:1.5;}
.tl-chart .tl-pt.q,.tl-chart .tl-pt.qs{fill:var(--qualify);}
.tl-chart .tl-pt.o,.tl-chart .tl-pt.os{fill:var(--out);}
.tl-chart .tl-pt.tl-live{stroke:var(--accent);stroke-width:2;}
/* Transparent per-point target: catches hover/focus/tap and, when the reader
   is on it, shows a soft accent halo under the tooltip the board script opens. */
.tl-chart .tl-hit{fill:transparent;cursor:pointer;outline:none;}
.tl-chart .tl-hit:focus-visible,.tl-chart .tl-hit.is-active{
  fill:var(--accent);fill-opacity:0.16;}
/* The tooltip itself is opened by board.js (progressive enhancement) and lives
   on the body so the chart's horizontal scroll never clips it. */
.tl-tip{position:fixed;z-index:60;pointer-events:none;
  background:var(--surface);border:1px solid var(--line-strong);border-radius:8px;
  padding:8px 11px;white-space:nowrap;
  box-shadow:0 8px 24px color-mix(in srgb,#000 26%,transparent);
  transform:translate(-50%,calc(-100% - 12px));}
.tl-tip[hidden]{display:none;}
.tl-tip .tl-tip-d{font-family:var(--mono);font-size:12px;color:var(--muted);margin-bottom:2px;}
.tl-tip .tl-tip-v{font-family:var(--sans);font-size:14px;color:var(--ink);}
.tl-tip .tl-tip-v b{font-weight:700;}
.tl-tip::after{content:"";position:absolute;left:50%;top:100%;transform:translateX(-50%);
  border:6px solid transparent;border-top-color:var(--surface);}
.tl-cap{margin-top:10px;font-size:0.8rem;color:var(--muted);}
.tl-key{display:inline-block;width:16px;height:3px;border-radius:2px;vertical-align:middle;
  margin-right:4px;}
.tl-key.q{background:var(--qualify);} .tl-key.o{background:var(--out);}

.method{margin-top:clamp(34px,6vw,60px);padding-top:22px;border-top:1px solid var(--line);}
.method h2{font-size:0.82rem;text-transform:uppercase;letter-spacing:0;color:var(--muted);}
.method dl{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));
  gap:10px 24px;margin:14px 0 0;}
.method dt{font-size:0.75rem;text-transform:uppercase;letter-spacing:0;color:var(--muted);}
.method dd{margin:1px 0 0;font-family:var(--mono);font-size:0.8rem;word-break:break-all;}
.limits{margin-top:16px;font-size:0.82rem;max-width:72ch;line-height:1.55;}

.unit{margin-top:20px;} .unit:first-child{margin-top:0;}
/* Section labels take ink, not accent. Bold blue text sitting in a table of
   club links reads as a link that does nothing; the accent stays spent on the
   probability bars and the cutline rule, which are the marks that mean
   something (docs/design-system.md). */
.unit-name{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 12px;
  font-size:0.82rem;text-transform:uppercase;letter-spacing:0;color:var(--ink);
  font-weight:680;margin-bottom:8px;padding-bottom:6px;border-bottom:1px solid var(--line);}
.unit-n{font-family:var(--mono);font-size:0.75rem;letter-spacing:0;color:var(--muted);
  text-transform:none;font-weight:500;}
/* Standings view switcher (AUG-260, AUG-425). A nested competition groups the
   same clubs three ways - by division, by the league a berth is decided across,
   and all at once - and only the reader knows which question they are asking.

   `hidden` in the markup, revealed by the script that makes it work
   (`BOARD_JS` `wireViews`): every view is server-rendered and visible, so a page
   with no JavaScript stacks them under their own headings rather than offering
   buttons that do nothing.

   8px radius through the shared control convention; focus ring on the accent,
   matching every other control. */
.vsw{display:flex;flex-wrap:wrap;gap:4px;margin:0 0 14px;padding:3px;
  background:var(--panel);border:1px solid var(--line);border-radius:8px;
  width:max-content;max-width:100%;}
.vsw[hidden]{display:none;}
.vsw-tab{font:inherit;font-size:0.82rem;font-weight:600;letter-spacing:0;
  color:var(--muted);background:none;border:0;border-radius:6px;
  padding:6px 14px;cursor:pointer;white-space:nowrap;}
.vsw-tab:hover{color:var(--ink);}
.vsw-tab[aria-selected="true"]{background:var(--bg);color:var(--ink);
  box-shadow:0 1px 2px rgba(0,0,0,0.08);}
.vsw-tab:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.race-view[hidden]{display:none;}
/* Only ever seen without the script, where it is the one thing telling the
   stacked tables apart. */
.race-view-name{font-size:0.9rem;font-weight:700;letter-spacing:0;
  margin:0 0 10px;color:var(--ink);}
.race-view-name[hidden]{display:none;}
.race-view + .race-view{margin-top:26px;}
/* The same switcher, on panels that are not standings: the league page's season
   section (AUG-734). The first panel sits directly under the tablist rather
   than a section-gap below it, because the tablist is its heading. */
.vsw-panel{margin-top:0;}
.vsw-panel[hidden]{display:none;}
.vsw-panel-name{font-size:0.9rem;font-weight:700;letter-spacing:0;
  margin:0 0 10px;color:var(--ink);}
.vsw-panel-name[hidden]{display:none;}
.vsw-panel-lede{max-width:62ch;margin:0 0 4px;font-size:0.95rem;}
.vsw-panel + .vsw-panel{margin-top:26px;}

a:focus-visible,tr:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important;}}
@media (max-width:560px){.wrap{--wrap-pad:15px;padding:20px var(--wrap-pad);}.mast{align-items:flex-start;gap:12px;}
  /* Brand shrinks to share the top line with the search icon so the icon sits
     inline top-right, not on its own row - the home page's tagline otherwise
     widened the brand and bumped the icon down (AUG-337). The tagline wraps
     under the wordmark instead. */
  .brand{flex:1 1 0;min-width:0;}.mark{font-size:1.45rem;}
  .league{font-size:0.75rem;overflow-wrap:anywhere;}
  .tagline{font-size:0.9rem;}
  .fresh{justify-content:start;text-align:left;width:100%;}.grid{grid-template-columns:1fr;}
  .game{padding:15px;}.game header{align-items:flex-start;flex-direction:column;}
  .scen .eff{white-space:normal;flex-wrap:wrap;min-width:0;}
  .bar{max-width:44vw;}.empty-panel,.tl-empty{align-items:flex-start;}}

/* Site nav and breadcrumbs (shared chrome). */
.local-nav{position:relative;margin-left:auto;order:1;display:flex;flex-wrap:wrap;align-items:center;
  justify-content:flex-end;gap:8px;font-family:var(--sans);font-size:.9rem;letter-spacing:0;}
.local-nav a{color:var(--ink);}
.local-nav>a,.nav-internal a,.nav-leagues>summary{display:inline-flex;align-items:center;
  justify-content:center;min-height:40px;padding:0 13px;border:1px solid var(--line-strong);
  border-radius:8px;color:var(--ink);text-decoration:none;background:var(--surface);}
.local-nav a[aria-current="page"]{border-color:var(--accent);
  background:color-mix(in srgb,var(--accent) 10%,var(--surface));font-weight:620;}
.local-nav a:focus-visible,.nav-leagues>summary:focus-visible{outline:2px solid var(--accent);
  outline-offset:2px;}
.account-nav{display:inline-flex;position:relative;}
.account-signin,.account-menu>summary{display:inline-flex;align-items:center;justify-content:center;
  min-height:40px;padding:0 13px;border:1px solid var(--line-strong);border-radius:8px;
  color:var(--ink);text-decoration:none;background:var(--surface);cursor:pointer;list-style:none;}
.account-menu>summary::-webkit-details-marker{display:none;}
.account-menu>summary:focus-visible,.account-panel a:focus-visible,.account-panel button:focus-visible{
  outline:2px solid var(--accent);outline-offset:2px;}
.account-avatar{display:grid;place-items:center;width:24px;height:24px;border-radius:50%;
  background:var(--accent);color:var(--surface);font-size:.8rem;font-weight:700;}
.account-panel{position:absolute;z-index:40;top:calc(100% + 8px);right:0;display:grid;
  min-width:180px;padding:10px;border:1px solid var(--line-strong);border-radius:10px;
  background:var(--surface);box-shadow:0 14px 40px color-mix(in srgb,var(--ink) 18%,transparent);}
.account-name{padding:5px 8px 9px;border-bottom:1px solid var(--line);font-size:.84rem;
  overflow-wrap:anywhere;}
.account-panel a,.account-panel button{min-height:38px;padding:7px 8px;border:0;border-radius:6px;
  color:var(--ink);background:transparent;text-align:left;text-decoration:none;font:inherit;font-size:.9rem;cursor:pointer;}
.account-panel a:hover,.account-panel button:hover{background:color-mix(in srgb,var(--accent) 7%,var(--surface));}
.nav-leagues{position:relative;}
.nav-leagues>summary{gap:9px;cursor:pointer;list-style:none;user-select:none;}
.nav-leagues>summary::-webkit-details-marker{display:none;}
.nav-leagues>summary::after{content:"";width:7px;height:7px;border-right:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor;transform:rotate(45deg) translateY(-2px);
  transform-origin:center;transition:transform .12s ease;}
.nav-leagues[open]>summary{border-color:var(--accent);
  background:color-mix(in srgb,var(--accent) 8%,var(--surface));}
.nav-leagues[open]>summary::after{transform:rotate(225deg) translate(-1px,-1px);}
.nav-panel{position:absolute;z-index:40;top:calc(100% + 8px);right:0;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:18px;
  width:min(580px,calc(100vw - 32px));max-height:min(70vh,540px);overflow:auto;
  padding:16px;border:1px solid var(--line-strong);border-radius:10px;background:var(--surface);
  box-shadow:0 14px 40px color-mix(in srgb,var(--ink) 18%,transparent);}
.nav-panel a{display:flex;align-items:center;justify-content:flex-start;min-height:42px;
  padding:8px 10px;border:1px solid transparent;border-radius:7px;color:var(--ink);
  text-decoration:none;background:transparent;font-family:var(--sans);font-size:.9rem;
  text-transform:none;line-height:1.25;}
.nav-panel a:hover{background:color-mix(in srgb,var(--accent) 7%,var(--surface));}
.nav-panel a[aria-current="page"]{border-color:color-mix(in srgb,var(--accent) 60%,var(--line));
  background:color-mix(in srgb,var(--accent) 11%,var(--surface));font-weight:620;}
.nav-internal{display:inline-flex;flex-wrap:wrap;align-items:center;gap:6px;
  padding:3px 8px 3px 6px;border:1px dashed var(--line-strong);border-radius:10px;
  background:color-mix(in srgb,var(--faint) 7%,transparent);}
.nav-internal-label{font-family:var(--mono);font-size:0.75rem;text-transform:uppercase;
  letter-spacing:.04em;color:var(--muted);padding:0 2px;}
.nav-sport{display:grid;align-content:start;gap:4px;min-width:0;}
.nav-sport-label{font-family:var(--sans);font-size:.95rem;font-weight:680;text-transform:uppercase;
  letter-spacing:.025em;color:var(--ink);padding:1px 10px 4px;}
.nav-panel a.nav-future{color:var(--muted);}
.nav-future-state{margin-left:auto;font-family:var(--mono);font-size:.75rem;
  color:var(--muted);text-transform:uppercase;letter-spacing:.04em;}
@media (max-width:560px){
  .local-nav{width:100%;margin-left:0;display:grid;
    grid-template-columns:repeat(2,minmax(0,1fr));gap:7px;}
  .local-nav>a,.nav-leagues>summary{width:100%;min-height:44px;}
  .account-nav{width:100%;}.account-signin,.account-menu{width:100%;}
  .account-menu>summary{min-height:44px;justify-content:flex-start;}
  .local-nav>a:only-child{grid-column:1/-1;}
  .nav-leagues{min-width:0;}
  .nav-leagues[open],.nav-leagues[open]+a,.nav-internal{grid-column:1/-1;}
  .nav-panel{position:static;width:100%;max-height:none;margin-top:7px;padding:10px;
    grid-template-columns:1fr;gap:13px;box-shadow:none;}
  .nav-panel a{min-height:44px;}
  .nav-internal{width:100%;justify-content:flex-start;}
  .nav-internal-label{flex:1 0 100%;}
  .page-context{align-items:flex-start;margin-top:11px;padding-top:1px;}
  .page-context>.fresh{width:auto;margin-left:0;justify-content:start;text-align:left;}
}
.account-page{max-width:52ch;}.account-page section+section{margin-top:28px;}
.account-page .account-danger{border-color:color-mix(in srgb,var(--heat-hi) 45%,var(--line));}
.account-page .account-continue{display:inline-flex;align-items:center;justify-content:center;
  min-height:42px;padding:0 13px;border:1px solid var(--accent);border-radius:8px;
  color:var(--surface);background:var(--accent);font:inherit;font-weight:620;text-decoration:none;}
.account-page .account-continue:hover{background:color-mix(in srgb,var(--accent) 86%,var(--ink));}
.account-page .account-continue:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.account-page button{min-height:42px;padding:0 13px;border:1px solid var(--line-strong);border-radius:8px;
  color:var(--ink);background:var(--surface);font:inherit;cursor:pointer;}
.account-page button:hover{border-color:var(--accent);}.account-page button:disabled{opacity:.6;cursor:wait;}
.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:14px;
  font-family:var(--sans);font-size:.82rem;letter-spacing:0;color:var(--muted);}
.crumbs a{color:var(--muted);text-decoration:none;border-bottom:1px solid var(--line-strong);}
.crumbs a:hover{color:var(--ink);}
.crumbs a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.crumbs [aria-current="page"]{color:var(--ink);}
.crumb-sep{color:var(--muted);}
/* One treatment for every anchor in the utility footer, whether the build put
   the four public trust links there or the private operator route (AUG-511).
   The operator link had this rule to itself and the public four had none, and
   the public four are the footer every reader on the live site sees.
   `a:focus-visible` above already rings them.
   Label separation is the row's `column-gap`, not padding on each link. It used
   to be `padding:0 4px`, which spaces labels from each other just as well but
   also indents the first one 4px past the prose above it and holds the last one
   4px short of the rule's end - visible at both alignments, since the row is
   flush right here and flush left under 560px. The gap is 8px so that the `·`
   still sits centred in the 16px between two labels (AUG-553). */
.site-utility{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;
  column-gap:8px;
  margin-top:clamp(36px,7vw,72px);padding-top:14px;border-top:1px solid var(--line);}
.site-utility a{display:inline-flex;align-items:center;min-height:36px;
  color:var(--muted);font-family:var(--sans);font-size:.82rem;text-decoration:none;
  white-space:nowrap;}
.site-utility a:hover{color:var(--ink);text-decoration:underline;
  text-underline-offset:3px;}
/* The separator is drawn inside the label's own box and reads as nothing, so a
   wrap can strand neither a `·` nor half of "Terms & disclaimer". */
.site-utility a:not(:last-child)::after{content:"·" / "";margin-left:8px;}
/* Phone width is the one place this row wraps - four labels do not fit on a
   390px line - and a separator is only legible while the labels it separates
   sit on the same line. So the dots go and column spacing does the separating;
   a wrapped list needs no punctuation between its rows. Above 560px the row is
   ~300px of text in a >=529px column and cannot wrap, so the dots are safe. */
@media (max-width:560px){.site-utility{justify-content:flex-start;column-gap:22px;}
  .site-utility a{min-height:44px;}
  .site-utility a:not(:last-child)::after{content:none;}}

/* Provenance line (replaces the per-page methodology block). */
.prov{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 14px;font-size:0.85rem;}
.prov a{color:var(--ink);}
.prov-meta{font-family:var(--mono);font-size:0.75rem;color:var(--muted);
  display:flex;flex-wrap:wrap;gap:4px 8px;}

/* Explainer. */
.ex-states{display:grid;gap:12px;margin-top:6px;}
.ex-state{display:grid;grid-template-columns:auto 1fr;gap:6px 14px;align-items:baseline;
  padding:12px 14px;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface);}
.ex-state .fresh{flex-direction:row;align-items:center;gap:7px;font-family:var(--mono);
  font-size:0.76rem;text-transform:uppercase;}
.ex-state p{font-size:0.92rem;max-width:62ch;}
.ex-list{display:grid;gap:10px;margin-top:6px;padding-left:20px;max-width:74ch;font-size:0.95rem;}
.ex-note{margin-top:12px;font-size:0.86rem;color:var(--muted);max-width:74ch;}
.ex-intro .lede,.ex-precision .lede{max-width:70ch;margin-top:10px;font-size:0.98rem;}

/* Crests: identity only. Semantic colour never rides on club art, so a
   position state is always carried by the text or a mark beside it (AUG-144). */
.crest,.crest-sm,.crest-lg{vertical-align:middle;object-fit:contain;}
.crest-sm{width:18px;height:18px;margin-right:7px;}
.crest{width:24px;height:24px;margin-right:8px;}
.crest-lg{width:44px;height:44px;}
.lg-logo{width:22px;height:22px;object-fit:contain;vertical-align:middle;}
.nav-logo{width:16px;height:16px;object-fit:contain;vertical-align:-3px;
  margin-right:6px;}
.crumb-logo{width:15px;height:15px;object-fit:contain;vertical-align:-2px;
  margin-right:5px;}
.lg-hero{width:40px;height:40px;object-fit:contain;}

/* The page identity block both reader types open with (AUG-733,
   `components.identity_header`): crest, `<h1>`, whatever compact facts the
   page states about itself, then the favourite/share controls. `.lg-id`/
   `.tp-id` stay as separate classes because existing tests and the
   sticky-rail-ordering assertions key off them, and because the two page
   types measured differently once real content was on the row (below) -
   `identity_header` is still the one component; only the CSS forks.
   The club side is the two-line block this replaces - the club's `<h1>`
   plus `.tp-subline` used to stack in their own wrapper `<div>`, which is
   what cost the second line; here every child is a direct sibling in the
   same flex row that wraps onto a second line only when a genuinely long
   name needs it (never an ellipsis - `render.short_club_name`'s contract)
   instead of by default. The league side was already a single row and
   stays exactly as tight: `flex-wrap` is club-only, because a league name
   long enough to wrap on its own (e.g. "USL Championship") wrapped that
   text across two lines even before this - `flex-wrap` on the row does not
   fix that, it only relocates the favourite/share controls to a line of
   their own, which measured taller than today rather than shorter
   (verified with `browser_qa` at 390px) - the opposite of this issue's
   outcome. Not worth trading a real height regression on the page type
   that did not have a problem for a cosmetic rewrap of a rare long league
   name; filed as its own discovery (AUG-763) rather than fixed here. */
.lg-id,.tp-id{display:flex;align-items:center;gap:5px 12px;margin:4px 0 18px;}
.tp-id{flex-wrap:wrap;}
.lg-id h1,.tp-id h1{margin:0;font-size:1.5rem;}
.tp-id h1{min-width:0;}
.lg-id .season{color:var(--muted);font-family:var(--mono);font-size:0.9rem;}
.page-actions{display:flex;align-items:center;gap:8px;margin-left:auto;flex:none;}

/* Favorite is a local-first control. The rendered default is disabled because
   static Pages has no account state; BOARD_JS removes that state only after it
   can read the browser cache. Its visual grammar intentionally matches Share:
   a compact page-level action, with a 44px phone target and an explicit
   pressed state that does not rely on the star fill alone. */
.favorite{position:relative;flex:none;}
.favorite-btn{display:inline-flex;align-items:center;gap:7px;min-height:40px;
  padding:0 13px;font:inherit;font-size:0.9rem;color:var(--ink);
  background:var(--surface);border:1px solid var(--line-strong);border-radius:8px;cursor:pointer;}
.favorite-btn:disabled{cursor:default;color:var(--muted);}
.favorite-btn:not(:disabled):hover{border-color:var(--accent);}
.favorite-btn:focus-visible,.favorite-prompt a:focus-visible,.favorite-prompt button:focus-visible{
  outline:2px solid var(--accent);outline-offset:2px;}
.favorite-icon{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linejoin:round;}
.favorite-btn.is-favorite{color:var(--accent);border-color:var(--accent);
  background:color-mix(in srgb,var(--accent) 8%,var(--surface));}
.favorite-btn.is-favorite .favorite-icon{fill:currentColor;}
/* The first-star prompt. It names the club and what the reader just got, so it
   needs room to say it - and it stays anchored to the star rather than taking
   the screen, because interrupting the forecast-reading task is the pressure
   the accounts plan's own risk list names (AUG-776). */
.favorite-prompt{position:absolute;z-index:40;top:calc(100% + 8px);right:0;
  width:min(330px,calc(100vw - 32px));padding:14px 15px;
  display:flex;flex-direction:column;gap:9px;text-align:left;
  background:var(--surface);border:1px solid var(--line-strong);border-radius:var(--radius);
  box-shadow:0 12px 32px rgba(8,14,28,.16);font-size:0.82rem;line-height:1.45;}
.favorite-prompt[hidden]{display:none;}
.favorite-prompt b{font-size:0.92rem;}
.favorite-prompt>span{color:var(--muted);}
.favorite-prompt-acts{display:flex;align-items:center;gap:14px;}
.favorite-prompt-cta{display:inline-flex;align-items:center;min-height:38px;
  padding:0 14px;background:var(--accent);color:#fff;border-radius:var(--radius);
  font-size:0.86rem;font-weight:650;text-decoration:none;}
.favorite-prompt-cta:hover{filter:brightness(1.06);}
.favorite-prompt button{padding:0;border:0;background:none;color:var(--muted);
  font:inherit;font-size:0.82rem;text-decoration:underline;
  text-underline-offset:3px;cursor:pointer;}
.favorite-status{display:block;position:absolute;right:0;top:calc(100% + 5px);width:min(300px,calc(100vw - 32px));
  color:var(--muted);font-size:0.75rem;line-height:1.35;text-align:right;}
.favorite-status:empty{display:none;}

/* The share affordance (AUG-490, reports/decision-share-affordance-ux.md).
   The button borrows the masthead search's control shape - same border, same
   radius, same 40px minimum - because it is the same kind of thing: one
   compact page-level control that opens a floating panel rather than
   reflowing the page under it. */
.sharebar{position:relative;flex:none;margin-left:auto;}
.page-actions .sharebar{margin-left:0;}
.sharebar[hidden]{display:none;}
.share-btn{display:inline-flex;align-items:center;gap:7px;min-height:40px;
  padding:0 13px;font:inherit;font-size:0.9rem;color:var(--ink);
  background:var(--surface);border:1px solid var(--line-strong);
  border-radius:8px;cursor:pointer;}
.share-btn:hover{border-color:var(--accent);}
.share-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.share-btn[aria-expanded="true"]{border-color:var(--accent);
  background:color-mix(in srgb,var(--accent) 8%,var(--surface));}
.mk-share{width:17px;height:17px;}
.share-pop{position:absolute;z-index:40;top:calc(100% + 8px);right:0;
  width:min(320px,calc(100vw - 32px));padding:12px;
  background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--radius);box-shadow:0 12px 32px rgba(8,14,28,.16);}
.share-pop[hidden]{display:none;}
/* The card the reader would actually send, at the shape they would send it.
   A share panel that names an image without showing it asks the reader to
   trust it; the thumbnail is also how the fallback tier stays honest - a game
   page below the leverage cut visibly offers its league's race card. */
.share-thumb{display:block;width:100%;height:auto;border-radius:6px;
  border:1px solid var(--line);background:var(--paper);}
.share-cap{margin:8px 0 10px;font-family:var(--mono);font-size:0.75rem;
  line-height:1.45;letter-spacing:0.02em;text-transform:uppercase;
  color:var(--muted);}
.share-act{display:flex;align-items:center;width:100%;min-height:44px;
  padding:0 12px;margin-top:6px;font:inherit;font-size:0.92rem;color:var(--ink);
  text-align:left;text-decoration:none;background:var(--surface);
  border:1px solid var(--line);border-radius:6px;cursor:pointer;}
.share-act[hidden]{display:none;}
.share-act:hover{background:color-mix(in srgb,var(--accent) 10%,transparent);
  border-color:var(--line-strong);}
.share-act:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;}
.share-act.is-done{color:var(--qualify-strong);border-color:var(--qualify);}
/* A game page has no identity row - its subject is the hero - so the control
   takes its own right-aligned line in the same place on the page. */
.gp-idrow{display:flex;justify-content:flex-end;margin:2px 0 -14px;}
@media (max-width:560px){
  .share-pop{width:min(320px,calc(100vw - 30px));}
  /* A phone has no room for a labelled control beside a club's name: the
     label costs about 60px and the h1 is what wraps to pay for it. The icon
     keeps its 44px target and the accessible name carries the word. */
  .share-btn span{position:absolute;width:1px;height:1px;overflow:hidden;
    clip-path:inset(50%);white-space:nowrap;}
  .share-btn{min-width:44px;min-height:44px;padding:0;justify-content:center;}
  .favorite-label{position:absolute;width:1px;height:1px;overflow:hidden;
    clip-path:inset(50%);white-space:nowrap;}
  .favorite-btn{min-width:44px;min-height:44px;padding:0;justify-content:center;}
  .page-actions{gap:5px;}
  .favorite-prompt,.favorite-status{right:-48px;width:min(300px,calc(100vw - 30px));}
}

/* What a game puts in play. Every bar runs a fixed 0-100 axis so two clubs,
   two leagues and two tiles compare directly; a bar fitted to its own span
   would make a 5-point swing look like a 30-point one. Left of the marker is
   what a loss costs, right of it what a win is worth (AUG-165). */
.imp{display:grid;gap:10px;margin-top:9px;}
.rng-key{font-size:0.75rem;color:var(--muted);letter-spacing:0.02em;}
.rng-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 10px;
  align-items:baseline;}
.rng-name{font-size:0.85rem;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;}
/* What this row's number measures, shown only when it is not the playoff race
   the legend already names. A division number reading as a playoff number is a
   wrong claim, so the label is part of the honesty rule, not decoration. */
.rng-of{font-family:var(--mono);font-size:0.75rem;letter-spacing:0.02em;
  text-transform:uppercase;color:var(--muted);margin-left:6px;}
/* A club in a consequence row is a link like any other club name on the site.
   Without this it fell through to the browser's default blue underline, which
   is the one link treatment the design system never chose (AUG-180). */
.rng-name a{color:inherit;text-decoration:none;
  border-bottom:1px solid transparent;}
.rng-name a:hover{border-bottom-color:var(--line-strong);}
.rng-name a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.rng-meta{display:inline-flex;gap:9px;align-items:baseline;}
.rng-end,.rng-nowv{font-family:var(--mono);font-size:0.75rem;color:var(--muted);}
.rng-nowv{color:var(--muted);}
/* Full tile width: the bar is the thing being read, and its ends are 0% and
   100%, so nothing may sit flush against them and pose as an axis label. */
.rng-wrap{grid-column:1 / -1;position:relative;display:block;
  padding-bottom:6px;}
.rng{position:relative;display:block;height:11px;border-radius:6px;
  background:var(--line);overflow:hidden;}
.rng-down,.rng-up,.rng-now{position:absolute;top:0;bottom:0;}
.rng-down{background:var(--out);}
.rng-up{background:var(--qualify);}
.rng-now{width:3px;margin-left:-1.5px;background:var(--ink);
  box-shadow:0 0 0 1px var(--surface);}
/* The draw sits *below* the bar, not on it. The two land within a few points
   of each other on most fixtures, so distinguishing them by colour alone
   failed - vertical separation is what makes them tell apart. */
.rng-draw{position:absolute;bottom:0;top:auto;width:5px;height:5px;
  margin-left:-2.5px;background:var(--muted);
  clip-path:polygon(50% 0,100% 100%,0 100%);}
.rng-key-now,.rng-key-draw{margin-left:12px;padding-left:11px;position:relative;}
.rng-key-now::before{content:"";position:absolute;left:2px;top:50%;
  margin-top:-4px;width:3px;height:9px;background:var(--ink);}
.rng-key-draw::before{content:"";position:absolute;left:0;top:50%;
  margin-top:-2px;width:5px;height:5px;background:var(--muted);
  clip-path:polygon(50% 0,100% 100%,0 100%);}


/* Home footer (AUG-304): the one freshness line, consolidating the per-card
   badges the races section used to carry. Names laggards, never averages. */
.home-foot{display:flex;flex-wrap:wrap;gap:8px 20px;align-items:center;
  justify-content:space-between;margin-top:36px;padding-top:14px;
  border-top:1px solid var(--line);}
.home-foot .fresh{display:inline-flex;flex-direction:row;align-items:center;
  gap:7px;font-size:0.85rem;}
.home-foot-prov{font-family:var(--mono);font-size:0.75rem;}
.home-foot-prov a{color:var(--accent);}

/* Team page v2: one clear chance, the published finish spread, then the
   season line.  The schedule below is deliberately separate (AUG-407).
   `.tp-subline` sits in the identity row now (AUG-733), a flex sibling of
   the crest and `<h1>` rather than a block below them - `margin` stays 0 so
   it lines up on the row instead of dropping to its own line. */
.tp-subline{margin:0;font-family:var(--mono);font-size:.78rem;
  color:var(--muted);text-transform:uppercase;letter-spacing:.03em;}
.tp-chances{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:12px;align-items:stretch;}
.tp-races,.tp-finish{padding:16px;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--surface);}
.tp-kicker{display:block;font-family:var(--mono);font-size:.75rem;color:var(--muted);
  letter-spacing:.05em;text-transform:uppercase;}
/* The league-wide finish spread (schema 1.17), stacked under the division one
   inside the same `.tp-finish` column rather than a second grid cell - a
   nested competition's two histograms are the same kind of claim at two
   grains, not two different sections. */
.tp-finish-2{margin-top:18px;padding-top:16px;border-top:1px solid var(--line);}
/* Every race the competition publishes, at one size (AUG-465). Two columns
   rather than one tall stack: four tile bars in a column are taller than the
   distribution panel beside them, and the pairing is what makes "these are the
   same kind of claim" visible at a glance. The minimum is the width a wrapped
   label plus its value needs before the two collide - below it the grid drops
   to one column on its own, without a query. */
.tp-bars{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));
  gap:18px 20px;}
.tp-races p,.tph-note{margin:14px 0 0;font-size:.84rem;color:var(--muted);}
.tph-wrap{margin-top:14px;}
.tph-bars{position:relative;display:flex;gap:3px;align-items:end;height:105px;
  padding-bottom:2px;border-bottom:1px solid var(--line-strong);}
.tph-bar{display:block;flex:1;min-width:3px;height:var(--height);border-radius:3px 3px 0 0;}
.tph-bar.q{background:var(--qualify-strong);}.tph-bar.o{background:var(--out-strong);}
/* The bars under a line that crowns a winner without settling anyone else (an
   MLB division: first is in, everyone else chases a league-wide wild card).
   Green still marks the winner's place above them, because that berth is
   automatic; rose down here would read "these four finishes are out", which is
   the opposite of true for a club sitting second in its division. */
.tph-bar.n{background:var(--accent);}
.tph-line{position:absolute;top:-6px;bottom:-4px;width:2px;background:var(--ink);opacity:.7;}
/* A small division labels every bar. The ticks are their own row on the same
   flex geometry as the bars, so each ordinal sits under its own bar without
   riding inside a rectangle that may have almost no height. */
.tph-ticks{display:flex;gap:3px;margin-top:5px;font-family:var(--mono);
  font-size:.75rem;color:var(--muted);}
.tph-ticks span{flex:1;min-width:3px;text-align:center;}
.tph-labels{display:flex;justify-content:space-between;gap:8px;margin-top:6px;
  font-family:var(--mono);font-size:.75rem;color:var(--muted);}
.tph-labels span:nth-child(2){text-align:center;}.tph-labels span:last-child{text-align:right;}
/* With ticks under every bar the caption is no longer an axis label, so it
   carries a swatch of the rule it describes and reads as the marker's key
   rather than as a label for whatever bar it happens to sit above. */
.tph-labels.one{justify-content:center;margin-top:3px;}
.tph-labels.one span{text-align:center;}
.tph-labels.one span::before{content:"";display:inline-block;width:2px;
  height:.85em;margin-right:7px;vertical-align:-1px;background:var(--ink);
  opacity:.7;}
.tp-season-chart{display:block;width:100%;min-width:620px;height:auto;margin-top:4px;}
.tp-season-chart text{font-family:var(--mono);font-size:12px;fill:var(--muted);}
.tpc-grid{stroke:var(--line);stroke-width:1;}.tpc-line{fill:none;stroke:var(--accent);
  stroke-width:3;stroke-linejoin:round;stroke-linecap:round;}
.tp-moment circle{fill:var(--surface);stroke:var(--ink);stroke-width:3;}
.tp-moment:hover circle{fill:var(--accent);}.tp-moment:focus-visible{outline:none;}
.tp-moment:focus-visible circle{stroke:var(--accent);stroke-width:6;}
/* Status strip (AUG-738): the club page's whole answer in one block above every
   section - playoff odds, the published countdown, the next game, the last
   result. It replaces the standalone Countdown section (AUG-462), whose two
   cards are now two of these cells; the palette note that section carried still
   holds, so it is restated once here rather than lost with it: green and rose
   are the position palette meaning exactly what they always mean, qualification
   side and elimination side, which is why the tinted rule sits on a cell's edge
   rather than behind its copy. The energy bar deliberately refuses that reading
   (a probability is a magnitude, not a position), which is why the odds cell has
   no tinted edge at all.

   Every cell is optional and the grid is `auto-fit`, so a club with no published
   magic number, or one whose season is over, closes the gap instead of leaving a
   hole - the absence rule this surface exists to keep honest
   (reports/decision-countdown-numbers.md). At 390px the figure row is two
   columns and the game row one, which is what makes an odds-plus-magic-number
   club fit in the first screen.

   `.tp-st-fig` carries `position:relative` for the definition affordance, whose
   popover spans the box of the nearest positioned ancestor: a term defined in a
   cell's kicker opens its answer over that cell, not over the page. */
.tp-status{margin-top:16px;}
.tp-st-figs{display:grid;gap:10px;
  grid-template-columns:repeat(auto-fit,minmax(155px,1fr));}
.tp-st-fig{position:relative;padding:14px 15px 15px;
  border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);}
.tp-st-fig.clinch{border-left:3px solid var(--qualify-strong);}
.tp-st-fig.elim{border-left:3px solid var(--out-strong);}
.tp-st-num{display:block;margin:8px 0 0;font-family:var(--mono);font-size:2rem;
  font-weight:650;line-height:1;color:var(--ink);}
.tp-st-sub{margin:8px 0 0;font-size:.8rem;color:var(--muted);max-width:46ch;}
.tp-st-sub strong{color:var(--ink);font-weight:650;}
/* A club publishing both countdown directions makes three figure cells, and at
   phone width three cells in a two-column grid leave the last one alone beside
   a hole. It takes the row instead - which is also the widest the elimination
   kicker needs to stay on one line with its `?`. Guarded to the width where the
   grid is two columns, so a desktop row of three stays three. */
@media (max-width:560px){
  .tp-st-fig:last-child:nth-child(odd){grid-column:1 / -1;}
}
.tp-st-games{display:grid;gap:10px;margin-top:10px;
  grid-template-columns:repeat(auto-fit,minmax(280px,1fr));}
.tp-st-game .tp-kicker{margin-bottom:6px;}
@media (max-width:760px){.tp-chances{grid-template-columns:1fr;}.tp-season-chart{min-width:560px;}}

/* Paths (AUG-580, re-presented AUG-739): what has to happen for each finish the
   season can end on, read from the published block
   (reports/decision-paths-to-the-playoffs-contract.md).
   The contract says the finishes are a complete split of the season, so the
   section leads with that drawn - one 100% bar - and puts each finish's detail
   behind its own row. The order inside a row is the order the questions come
   in: what this club has to do, what the finish means as a bracket position,
   then what separates the ways it happens. The table the drawing comes from
   sits behind the same <details> chip the schedule uses, because a reader who
   wants eleven rows of a simulated table is asking a second question. */
.tp-paths{margin-top:clamp(24px,4vw,38px);}
/* The split. A segment's `flex` is the route's own published share, so the bar
   tiles without renormalising, and the floor is what keeps a sub-1% finish
   visible and tappable rather than a fifth of a pixel. Two hues and only two:
   which one a segment takes is the snapshot's published `qualifies`, never its
   rank or its label, and the step inside a side is opacity on one token rather
   than a mix toward the page ground - the ground inverts between the themes
   and a written mix percentage does not, so a mixed ramp reads backwards in
   one of them (reports/decision-magic-number-brand.md). */
.pth-bar{display:flex;gap:2px;height:38px;margin-top:12px;border-radius:8px;
  overflow:hidden;}
/* The floor a sub-1% finish is drawn at. MLB's seed-4 finish sits under 1% for
   22 of 30 clubs and at its true share draws a fifth of a pixel: too thin to
   see and far too thin to tap. Same floor `prediction_bar` puts under an NFL
   tie, and it changes nothing the section says - the published share is still
   what the segment's `flex` carries and still what the row prints. */
.pth-seg{min-width:6px;text-decoration:none;}
.pth-seg:focus-visible{outline:2px solid var(--ink);outline-offset:-3px;}
.pth-seg[aria-current="true"]{box-shadow:inset 0 0 0 2px var(--ink);}
.pth-seg.is-q,.pth-dot.is-q{background:var(--qualify-strong);}
.pth-seg.is-o,.pth-dot.is-o{background:var(--out);}
/* The legend names what the two hues mean, because a hue that needs a caption
   and does not carry one is decoration. */
.pth-key{display:flex;flex-wrap:wrap;gap:4px 14px;margin-top:9px;
  font-size:.78rem;line-height:1.45;color:var(--muted);}
.pth-key span{display:inline-flex;align-items:baseline;gap:6px;}
.pth-dot{flex:0 0 auto;width:9px;height:9px;border-radius:3px;
  transform:translateY(1px);}
/* One closed row per finish, in the disclosure grammar the schedule and the
   middle-season table already use. Native <details>, so with no script at all
   every route's detail is one tap away - which is also why the segments above
   are anchors into these rows rather than buttons that would do nothing. */
.pth-routes{display:grid;gap:6px;margin-top:12px;}
.pth-route{border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);}
.pth-route > summary{list-style:none;cursor:pointer;display:flex;
  align-items:center;gap:9px;padding:11px 13px;min-height:44px;}
.pth-route > summary::-webkit-details-marker{display:none;}
.pth-route > summary:focus-visible{outline:2px solid var(--accent);
  outline-offset:-2px;}
.pth-route > summary .pth-dot{transform:none;}
.pth-route-nm{flex:1 1 auto;font-size:.9rem;font-weight:600;}
.pth-route-v{font-family:var(--mono);font-size:.85rem;font-weight:650;}
.pth-route[open]{border-color:var(--line-strong);}
.pth-route-body{padding:0 13px 13px;}
/* A deterministic answer prints the word, never a percentage: nothing sampled
   proves it, and nothing proved was sampled. A proof takes the board's own
   settled badge (`.scen-badge`, green or grey); a route nothing was sampled on
   is neither, so it gets this third, quieter chip and cannot be mistaken for
   either of them. */
.pth-state{font-family:var(--mono);font-size:.75rem;font-weight:600;
  text-transform:uppercase;padding:3px 9px;border-radius:99px;white-space:nowrap;
  color:var(--muted);background:color-mix(in srgb,var(--line-strong) 45%,transparent);}
.pth-note{margin-top:7px;font-size:.85rem;color:var(--muted);max-width:58ch;}
.pth-split{margin-top:6px;font-family:var(--mono);font-size:.75rem;color:var(--muted);}
/* The club's own requirement, once per finish rather than once per path: the
   floors inside one finish come out 45+, 45+, 46+, which is one requirement
   measured on three subsets rather than three requirements. */
.pth-need{margin-top:8px;font-size:.95rem;font-weight:620;}
/* The bracket position the finish means, from the middle season's own seeds.
   The club's own tie is drawn; the rest of its round follows in one muted
   line, because a seat in a bracket is a place in a field. */
.pth-tie{margin-top:10px;padding:10px 12px;border:1px solid var(--line);
  border-radius:8px;background:var(--paper);}
.pth-pair{display:grid;justify-items:start;gap:4px;margin-top:6px;}
.pth-side{display:flex;align-items:center;gap:7px;font-size:.9rem;min-width:0;}
.pth-side .crest{flex:none;}
.pth-side.is-club{background:color-mix(in srgb,var(--accent) 8%,transparent);
  border-radius:99px;padding:3px 10px 3px 4px;}
.pth-side.is-club .pth-club{font-weight:650;}
.pth-seed{font-family:var(--mono);font-size:.78rem;color:var(--muted);font-weight:600;}
.pth-club{overflow-wrap:break-word;}
/* A side an earlier round still has to fill: the round names itself on one
   line and the clubs that can arrive follow underneath, rather than a label
   and a list competing for the same 390px row. */
.pth-side.is-open{flex-direction:column;align-items:flex-start;gap:2px;}
.pth-open{font-family:var(--mono);font-size:.75rem;color:var(--muted);}
.pth-at{font-family:var(--mono);font-size:.75rem;text-transform:uppercase;
  color:var(--muted);border:1px solid var(--line-strong);border-radius:99px;
  padding:1px 6px;}
.pth-v{font-family:var(--mono);font-size:.75rem;color:var(--muted);padding-left:6px;}
.pth-field{margin-top:7px;font-size:.78rem;color:var(--muted);}
/* One row per path: the share it holds, then the relations that separate it,
   each with the numbers backing it underneath. */
.pth-paths{list-style:none;margin:12px 0 0;padding:0;display:grid;gap:10px;}
.pth-path{border-left:2px solid var(--line-strong);padding-left:11px;}
.pth-share{font-family:var(--mono);font-size:.75rem;color:var(--muted);}
.pth-share b{color:var(--ink);font-weight:650;}
.pth-cond{list-style:none;margin:4px 0 0;padding:0;display:grid;gap:5px;font-size:.9rem;}
.pth-own{font-weight:600;}
.pth-rel{font-weight:600;text-decoration:underline;
  text-decoration-color:var(--line-strong);text-underline-offset:3px;}
.pth-rel:hover{text-decoration-color:var(--accent);}
.pth-back{display:block;font-size:.78rem;color:var(--muted);}
/* The middle season's table. Narrow by construction - a seed, a club and the
   record its competition ranks on - so it drops the shared 560px floor rather
   than scrolling a phone sideways for three columns. */
.pth-table{margin-top:12px;}
.pth-table .scroll{margin-top:10px;}
.pth-table table{min-width:0;font-size:.85rem;}
.pth-table caption{caption-side:bottom;text-align:left;color:var(--muted);
  font-size:.75rem;padding-top:8px;}
.pth-table thead th{padding:0 8px 6px;}
.pth-table tbody td,.pth-table tbody th{padding:7px 8px;font-weight:500;}
.pth-table tbody th{text-align:left;}
.pth-table tr.is-club{background:color-mix(in srgb,var(--accent) 8%,transparent);}
.pth-table tr.is-club th{font-weight:650;}
/* The berth line, in the one hue a drawn cutline takes anywhere on the board. */
.pth-table tr.is-cut td,.pth-table tr.is-cut th{border-bottom:2px solid var(--accent);}
.pth-seed-cell{font-family:var(--mono);color:var(--muted);width:38px;}

/* Schedule (AUG-407): NEXT UP as the featured card, then UPCOMING, then
   HISTORY - each group bounded, the rest behind a native <details>, same
   disclosure grammar the home spotlight grid uses (AUG-326: dashed chip,
   rotating chevron, no new JS pattern, openable with JS off). Every card is
   the shared .gc component in its .gc-row-3 grid; nothing here restyles it. */
.tp-schedule{margin-top:clamp(24px,4vw,38px);}
.sched-group{margin-top:18px;}
.sched-group h3{font-size:0.8rem;text-transform:uppercase;letter-spacing:.03em;
  color:var(--muted);margin-bottom:8px;}
/* The chip was shared with home's club grid until AUG-464 made every club
   visible; the schedule is its one caller now. */
.sched-all{margin-top:12px;}
.sched-all .gc-row-3{margin-top:10px;}
.sched-more{list-style:none;cursor:pointer;display:inline-flex;align-items:baseline;
  gap:6px;font-family:var(--mono);font-size:0.8rem;font-weight:600;color:var(--muted);
  border:1px dashed var(--line-strong);border-radius:8px;padding:6px 12px;}
.sched-more::-webkit-details-marker{display:none;}
.sched-more::before{content:"\25b8";display:inline-block;margin-right:2px;
  transition:transform .15s;}
.sched-all[open] .sched-more::before{transform:rotate(90deg);}
.sched-more:hover{color:var(--ink);border-color:var(--accent);}
.sched-more span{font-family:var(--sans);font-weight:500;font-size:0.75rem;color:var(--muted);}

/* The race table keeps every column at every width and scrolls inside its own
   container, so a phone reaches the standings by scrolling rather than losing
   them. A right-edge fade makes that scrollability visible. */
.scroll{position:relative;}
.scroll::after{content:"";position:sticky;right:0;top:0;float:right;width:22px;height:100%;
  pointer-events:none;background:linear-gradient(to right,transparent,var(--paper));}
/* Opt-in modifier: this container opens at its end, not its start (AUG-461).
   `direction:rtl` is the CSS-only way to park a scroll box at its far edge, so
   there is no script to fail and no left-anchored flash before one runs; the
   child restores `ltr` because only the scroll origin is being reversed, never
   the content's own writing direction. The fade moves to the left edge with it
   - it points at the content still hidden, which is now history. */
.scroll-end{direction:rtl;}
.scroll-end>*{direction:ltr;}
.scroll-end::after{right:auto;left:0;float:left;
  background:linear-gradient(to left,transparent,var(--paper));}

/* League page links out (the linking rule, docs/design-system.md). */
td.club a{color:var(--ink);text-decoration:none;display:inline-block;padding:6px 0;
  border-bottom:1px solid transparent;}
td.club a:hover{border-bottom-color:var(--line-strong);}
td.club a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.game h3 a{color:inherit;text-decoration:none;display:block;}
.game h3 a:hover{color:var(--accent);}
.game h3 a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.scen .eff a{color:inherit;text-decoration:none;border-bottom:1px solid transparent;}
.scen .eff a:hover{border-bottom-color:var(--line-strong);}
.scen .eff a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}

/* The internal contact sheet: cards shown at a readable fraction of their
   real pixel size, so a reviewer can see all of them at once.

   `--sc-w` and `--sc-h` are the card's own canvas, and the figure has to be
   told them: `transform` does not change layout, so a figure that sized
   itself would be a full 1080px box holding a 367px picture, and the sheet
   would stack one card per screen with a page of dead space between them -
   the opposite of seeing all of them at once. The card's own element sets
   those properties on itself, which is inside this figure rather than on it,
   so `cards` stamps them here too (AUG-483).

   The page also takes the wrap off its reading measure. 1000px is right for
   prose and wrong for a grid of canvases up to 1920px wide, which is why the
   one page that is nothing but canvases opts out. */
.sheet-page .wrap{--maxw:2000px;}
.sheet{display:flex;flex-wrap:wrap;gap:28px;align-items:flex-start;}
.sheet-item{margin:0;display:flex;flex-direction:column;gap:6px;}
.sheet-item figcaption{font-family:var(--mono);font-size:12px;color:var(--muted);}
.sheet-item .sc{transform:scale(0.34);transform-origin:top left;}
.sheet-item{width:calc(var(--sc-w) * 0.34);height:calc(var(--sc-h) * 0.34 + 22px);}

/* Share cards. A card is read at feed size and travels without the page
   around it, so nothing here shrinks below the readable floor at any ratio
   and the context block is never dropped to make room (AUG-186, AUG-187). */
.sc{width:var(--sc-w);height:var(--sc-h);box-sizing:border-box;
  display:flex;flex-direction:column;gap:18px;padding:56px 56px 44px;
  background:var(--surface);color:var(--ink);font-family:var(--sans);
  border:1px solid var(--line);}
.sc-top{display:flex;justify-content:space-between;gap:18px;
  font-family:var(--mono);font-size:20px;text-transform:uppercase;
  letter-spacing:.03em;color:var(--muted);}
/* A game already underway (AUG-895), on the one card family whose subject can
   be one. The badge is the club page's own `.gc-live`, borrowed rather than
   restyled; all a card decides is where it sits and how big the one mark sized
   in px comes out. The dot is 7px against 0.75rem type on a page and would read
   as a speck against the card's 20px kicker, which is the same reason
   `.sc .rrow` sets a row height rather than inheriting one. Its two gaps go up
   with it and for the same reason: at the page's 0.75rem the badge's 5px and
   the lead's 8px are proportionate, and at 20px uppercase mono they close up
   until the dot reads as part of the `I` and the league name runs on from
   `PROGRESS`. Caught on the rendered card, which is the only place a gap this
   small is visible at all. The other half of the state - that the forecast
   predates kickoff - is a caption clause, because a line inside the device
   costs the prediction bar (`social._branch_fit`). */
.sc .sc-top .gc-live{gap:9px;}
.sc .sc-top .gc-live::before{width:11px;height:11px;}
.sc .sc-top .gc-lead{gap:6px 16px;}
.sc-subject{font-size:58px;line-height:1.05;margin:0;letter-spacing:-0.015em;
  text-wrap:balance;}
.sc-value{display:flex;align-items:baseline;gap:20px;font-size:104px;
  font-weight:700;line-height:1;letter-spacing:-0.02em;}
.sc-move{font-family:var(--mono);font-size:32px;font-weight:600;
  color:var(--muted);}
/* `overflow:hidden` is the guard, not the layout: a device is drawn from live
   data and a crowded table stacks taller than a quiet one, so the row clips
   rather than letting the table paint over the footer. It is the card's rule
   rather than one canvas's - the square canvas overran its own footer for as
   long as this lived under `.sc-og` alone (AUG-575). */
.sc-device{flex:1;display:flex;flex-direction:column;justify-content:start;
  gap:14px;min-height:0;overflow:hidden;}
/* Only a device that *is* an SVG fills the slot. A direct-child selector, not
   a descendant one: every device on the og canvas is now a page component, and
   those carry marks of their own - a crest badge, a delta arrow - which a
   blanket rule would blow up to the card's full width. */
.sc-device>svg{width:100%;height:auto;}
.sc-range{font-family:var(--mono);font-size:24px;color:var(--muted);margin:0;}
.sc-impact{display:grid;grid-template-columns:1fr auto;gap:4px 16px;
  font-size:26px;}
.sc-impact .rng-wrap{grid-column:1 / -1;}
.sc-impact .rng{height:20px;border-radius:10px;}
.sc-impact-now{font-family:var(--mono);font-size:24px;color:var(--muted);}
/* Which race the number beside it belongs to. A card is read where the page's
   legend is not, so it states the quantity rather than inheriting it. */
.sc-impact-of{font-family:var(--mono);font-size:20px;letter-spacing:0.02em;
  text-transform:uppercase;color:var(--muted);margin-left:10px;}
.sc-foot{display:flex;justify-content:space-between;align-items:center;
  gap:18px;padding-top:22px;border-top:1px solid var(--line);}
.sc-brand{display:flex;align-items:center;gap:12px;font-size:28px;
  font-weight:650;}
.sc-brand svg{width:34px;height:34px;}
/* The watermark carries the small master, and it has to be painted here: the
   mark's own rules are scoped to `.mark`, so a card outside that scope was
   stamping a black glyph rather than the brand's cobalt. */
.sc-brand .mk-bar{fill:var(--accent);}
.sc-brand .mk-notch{fill:var(--ink);}
.sc-brand .mk-stop{color:var(--accent);}
.sc-ts{font-family:var(--mono);font-size:22px;color:var(--muted);
  text-transform:uppercase;}
/* **What is a card's rule, and what is a canvas's.** Everything from here to
   the canvas blocks turns a *page* device into a *card* device: no scroll
   gutter, no column header, no legend, no explanatory prose, and one `zoom`
   in place of the reader's own root size. That is a statement about cards
   against pages, and it does not change with the shape of the canvas - so it
   is scoped to `.sc`, and each canvas block below carries only what its own
   geometry decides: the composition, the type scale, how many columns the
   race table takes, and the scale its device row is magnified to.

   It lived under `.sc-og` until AUG-575, which is why the other three
   canvases drew page-sized components in a card-sized box: the square
   overran its footer, the portrait rendered the league table at reading size
   in a 1080px box, and the landscape spent two thirds of its canvas on
   nothing. None of that was a layout decision - it was the absence of one. */

/* **How a page device gets to card size.** Every shared component sizes its
   type in `rem`, because on a page the reader's own root size is the one
   authority on how big text should be. A card is not a page: it is a fixed
   canvas that a feed shows at about half, and a reviewer reads at a third. So
   the card magnifies the device rather than restyling it - one `zoom` on the
   wrapper, no per-component px override anywhere. That keeps the guarantee the
   whole redesign rests on: what a card draws is the page's own object, not a
   copy of it that can drift.

   `--sc-zoom` is resolved per canvas from `--z-<ratio>`, which the card writes
   on itself where a family's device is variable in height (a race table's row
   count is the competition's, `social.Canvas`). The fallback in each canvas
   block is what the fixed-height devices take. */
.sc .sc-cols,.sc .sc-branches,.sc .sc-clinch,.sc .sc-swing-set,
.sc .sc-races,.sc .sc-finish{zoom:var(--sc-zoom,1);}

/* Race update: the league table, in one column per grouping the competition
   cuts at. Two is the cap on a wide canvas - a third column takes the club
   cell under the width a name needs, and an unreadable name is not a table -
   and a portrait canvas takes one, because 1080px split two ways is that same
   unreadable name with the height to have avoided it. */
.sc .sc-cols{display:grid;gap:0 18px;align-items:start;}
.sc .sc-cols-2{grid-template-columns:repeat(2,minmax(0,1fr));}
.sc .sc-col{min-width:0;}
.sc .sc-colname{margin:0 0 3px;font-size:0.9rem;font-weight:650;}
.sc .sc-unit{margin:5px 0 1px;font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.06em;text-transform:uppercase;color:var(--muted);}
/* The page's table sits in its own surface with a scroll gutter; a card has
   neither, so the slice is the plain rows and the card is the surface. */
.sc .race-slice{padding:0;background:none;border:none;overflow:visible;
  min-width:0;}
.sc .race-slice::after{display:none;}
/* **The row's columns are the card's, not the page's.** The page's template
   (`components.race_columns`) is written for a scrolling table on a phone, so
   it floors the track at 80px and reserves a cell per published standings
   figure; on this canvas that width is what a club's name needs, and a name a
   reader cannot read is not a table. The card spends it on the name instead
   and states the ordering in its caption. Written with `!important` because
   the slice carries the page's own template inline - which is right, since the
   page computes it per competition. */
.sc .race-slice{--rr-cols:minmax(0,420px) minmax(72px,1fr) 56px!important;
  --rr-min:0!important;}
/* AUG-999: the race-update card's own row, which carries what Ford asked this
   card for - the playoff line and its raw percentage as they already were
   (nothing below this comment touches `.rr-track`/`.rr-pc`), the standings
   figures the full league table already carries (Played, the record, the
   sorting figure: `social._card_figures`), and a bare percentage per further
   published race beside them, no bar and no track
   (`social._card_secondary_outcomes`). `.sc-has-cols` (`social._card_slice`)
   marks a slice carrying either kind of extra column, and it is the only
   marker: the race columns are sized as implicit tracks below, so no rule
   needs to know how many there are.

   **Every width here is `social`'s own budget constant for that column, and
   the two are held equal by `test_the_row_budget_matches_the_stylesheet_it_
   mirrors`.** In order: `ROW_NAME_PX`, `ROW_TRACK_PX`, `ROW_PCT_PX`, then
   `ROW_FIG_PX`/`ROW_REC_PX`/`ROW_FIG_PX` - Played and the sorting figure are
   one short mono token ("14", "82"), the record is the wider composed cell
   ("12-8-3") - and `ROW_OUTCOME_PX` per race, in the implicit track below.
   `social.ROW_NAME_PX`'s comment carries what each was measured against.

   Two of them are capped and pinned where an earlier template left them
   elastic, and that is deliberate rather than tidying. Ford: *"sometimes the
   spacing can be off to the right of the playoff probability bars ... keep it
   tight and concise. Spread out the other columns to fit and take the whole
   thing up."* What he was looking at was this template disagreeing with
   `social._row_across_px`: the budget priced the club cell and the track at
   fixed widths while the template let the first grow to 420px and the second
   take `1fr`. Those agree only when the zoom `_fit` picks is the width-filling
   one; when it is height-bound or ceiling-bound instead - which varies per
   competition and per canvas, hence *sometimes* - the row is laid out in more
   unzoomed pixels than the budget spends, and CSS grid hands every surplus
   pixel to the two elastic tracks and none to the fixed ones. The club cell
   grew to 420px and left a void beside a short name; the track then stretched
   to 184px (uslc og) and 245px (mlb og) and drew a long empty rail past the
   dot. So both are held at what they are budgeted, and the surplus goes to the
   gutters instead (`justify-content` below), which spreads the columns across
   the card rather than crowding them into its right-hand third. */
.sc .sc-has-cols .race-slice{
  --rr-cols:minmax(0,176px) 44px 38px 32px 46px 32px!important;}
/* **The race columns are implicit tracks, not enumerated ones.** Their count
   is the competition's (`social._card_secondary_outcomes` reads
   `render.league_table_outcome_columns`, the league page's own set) and it runs
   from two (the USLs) through three (MLS, the Premier League) to four (MLB, the
   NFL), with nothing in the config capping it. `repeat()` cannot take a custom
   property for its count, so enumerating a template per count is the one shape
   this cannot have: the competition that publishes one more race than the last
   rule anticipated would fall through every one of them and wrap its extra
   cells onto a second row. Instead the explicit template above stops at the six
   columns every competition shares (the club cell, the track, the lead
   percentage and the three standings figures), and each race beyond them lands
   in an implicit track that `grid-auto-columns` sizes at
   `social.ROW_OUTCOME_PX`. `grid-auto-flow:column` is what makes those tracks
   columns rather than a second row, and it is a no-op for the six explicit
   ones. One declaration for any count, so `_row_across_px`'s arithmetic and
   this template cannot disagree the way an enumerated list eventually would.
   Set on `.rrow`, which is both the body row and the header
   (`components.race_head` writes `class="rrow rr-head"`) - they have to resolve
   the same tracks. */
.sc .sc-has-cols .rrow{grid-auto-flow:column;grid-auto-columns:44px;}
/* `social.ROW_GAP_PX`, the gutter the budget charges once per boundary between
   two columns. Scoped to the card's wide row rather than written over
   `.sc .rrow{gap:7px}` below, which every other card family's three-column
   race row still reads. */
.sc .sc-has-cols .rrow{gap:5px;}
/* Where the surplus goes now that no track will absorb it: one equal share
   per boundary between two columns, so a row that is wider than its budget
   spreads rather than pools. `space-between` rather than an `fr` share of the
   cells themselves - growing the cells would put the surplus back inside the
   club cell (the void again) and back into the track (the empty rail again),
   since those are the only two cells whose content does not sit against their
   trailing edge. It reaches both rows that read `--rr-cols`: the body row and
   the header (`components.race_head` writes `class="rrow rr-head"`), which
   have to distribute identically or every label would sit off its column.
   Every track is content-independent, so every row resolves the same shares.
   A row narrower than its budget (the crowded competition `_fit_canvas` clamps
   to `CARD_ZOOM_MIN`) has no surplus to place and behaves exactly as before:
   `minmax(0,...)` still lets the club cell give way first. */
.sc .sc-has-cols .rrow{justify-content:space-between;}
/* The bare-percentage treatment Ford asked for: the same
   `components.energy_bar` the league page's own outcome columns draw -
   unchanged, so the number is never a second implementation of `fmt_pct` or
   the clinched/eliminated badge - with its track hidden and its value
   pushed to the column's trailing edge. The label stays in the DOM either
   way (`header_labelled=True`'s `.vh` span): a screen reader and the
   unlabelled-probability audit both still find it, even though nothing
   visible repeats it - the header row below states it once per column
   instead, the same way `.rr-o`'s always has. */
.sc .rr-o .eb-track{display:none;}
.sc .rr-o .eb-top{justify-content:flex-end;}
/* Mirrored by `social.CARD_ROW_PX`, which has to know how tall the table comes
   out before it can pick a scale that fits it. A test asserts the two agree.
   One declaration for all four canvases, and that is the answer to AUG-575's
   geometry question: a row's height is the *component's* in unzoomed pixels,
   so it cannot vary by canvas while the rules above do not. What varies is the
   box those rows are fitted into, and that is `social.CANVASES`. A race-
   carrying row is this same 22px (AUG-999): its extra columns are bare
   percentages with no fill under them, so none of them is taller than the club
   name beside it and height enters the budget only through the header. */
.sc .rrow{height:22px;gap:7px;}
/* The card hides the page's column header by default: the club, the track and
   the lead percentage need none of it, since the card's own rail above the
   table already names that race (`.rr-nm`/`.rr-track`/`.rr-pc` stay zeroed
   below). The cell keeps every race in its DOM either way
   (`components.race_label`). */
.sc .rr-head{display:none;}
/* AUG-999: a standings figure or a further race has no rail of its own to be
   named by - a number with no visible label anywhere is exactly what
   `components.energy_bar` refuses to render on a page (its own docstring:
   "always names its race"), and `.rr-fig`/`.rr-rec` carry no label at all
   outside this header row. So a slice with either kind of column brings the
   header back. Higher specificity than `.sc .rr-head{display:none}` above wins
   outright, no `!important` needed. */
.sc .sc-has-cols .rr-head{display:grid;grid-template-columns:var(--rr-cols);
  gap:5px;height:auto;margin:0 0 3px;}
/* One header per *column*, not per slice (`social._card_slice`'s `head`,
   charged once in `social._column_height`). Two slices stacked in one column
   are the same competition's table cut at two grains and carry the identical
   column set by construction - `--rr-cols` is one declaration for the whole
   card - so the second copy of the header, four rows under the first, states
   nothing the first did not and costs a whole `social._head_px` to say it. On
   MLB that repeat was two of the four slices its square canvas could not
   fit. */
.sc .sc-nohead .rr-head{display:none;}
/* `height:0` rather than `visibility:hidden` alone: the page's own `.rr-pc`
   carries its full reading-size type even hidden, and a row sized to its
   tallest cell took that height whether the cell was visible or not - the
   first render of this measured 34.7px unzoomed for one caption line, read off
   the rendered card. Zeroing each hidden cell's own box means
   the row's auto height is the one real line left: the figures' and `.rr-o`'s. */
.sc .sc-has-cols .rr-head .rr-nm,.sc .sc-has-cols .rr-head .rr-track,
.sc .sc-has-cols .rr-head .rr-pc{height:0;overflow:hidden;}
/* 0.75rem, not smaller: that is the floor every other race-row label already
   sits at (`.sc-unit`), and `test_no_label_renders_below_the_readable_floor`
   holds every declared size in this file to 12px so a tightening pass can't
   quietly shrink one back under it - a caption this small would clear 12px
   only at `CARD_ZOOM_MAX`, never at the `CARD_ZOOM_MIN` floor a crowded
   competition actually renders at. `white-space:normal` rather than nowrap: a
   long one-word race name ("Championship") gets `components.race_head`'s own
   soft hyphens (`_hyphenate_rr_o`), and letting it wrap is what turns those
   into lines instead of an overflow. `line-height:1.15` at this size is what
   `social.CARD_RR_HEAD_LINE_PX` is derived from - 0.75 x 16 x 1.15 = 13.8 -
   so the height budget counts the same line box the browser draws. */
.sc .sc-has-cols .rr-head .rr-fig,.sc .sc-has-cols .rr-head .rr-rec,
.sc .sc-has-cols .rr-head .rr-o{font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.05em;text-transform:uppercase;color:var(--muted);
  text-align:right;white-space:normal;line-height:1.15;overflow-wrap:break-word;}
.sc .rr-cutwrap{min-width:0;}

/* Team forecast: every published race at one weight, beside the finish
   spread. The bars are the card's picture; the histogram is one element among
   them rather than the whole canvas, which was Ford's other note. */
/* Two races stack and four pair up: the grid follows the competition's own
   outcome set rather than a fixed shape, because USL League One publishes two
   and MLB four and neither should get the other's layout. */
.sc .sc-races{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:14px 22px;grid-auto-rows:minmax(0,1fr);}
/* A label that wraps to two lines - "Supporters' Shield" - would drop its own
   bar below its neighbour's and break the row the four races read across, so
   the bar sits at the bottom of an equal-height cell instead. */
.sc .sc-races .eb{display:flex;flex-direction:column;
  justify-content:space-between;}
.sc .sc-races-1,.sc .sc-races-2{grid-template-columns:minmax(0,1fr);}
.sc .sc-kicker{display:block;font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.06em;text-transform:uppercase;color:var(--muted);}
.sc .tph-bars{height:112px;}
/* Ford's round-three note: *"take out the explanatory text, that information
   already conveyed visually."* The note is the histogram's prose sentence
   ("Most likely 1st. The division winner takes an automatic berth...") and on a
   630px canvas it was also the element that ran out of room and stopped
   mid-word. Hidden rather than truncated, and rather than editing the page's
   own device: the sentence is right where a reader has the page around it. The
   marker caption above it stays - that is what the line means, not an
   explanation of it - and the bars take the height back. */
.sc .tph-note{display:none;}

/* Game consequence and clinch both draw branch sets, so they share the
   treatment: the page's grid, given the whole width, one branch per result.
   The branch badge hangs above its card's top edge, so the row it sits in
   needs the room or `overflow:hidden` shears it off.

   How many columns is the canvas's, written on the card as `--bc-<ratio>` by
   the fit that priced it (`social._branch_layout`) and read by each canvas
   block below. One row is still the default and what a card with no count
   takes: `auto-fit` with a `1fr` track never wraps, so a canvas that says
   nothing draws exactly what it drew before. The row gap only matters where a
   canvas does wrap, and it is 18 rather than 14 because the lower row's badges
   sit 9px above their own branches (AUG-624). */
.sc .branch-set{grid-template-columns:repeat(auto-fit,minmax(0,1fr));
  gap:18px 14px;align-items:start;}
.sc .branch{padding:10px 13px 12px;}
/* A club name that wraps costs the branch a whole row of height the card
   budgeted for a race, so on a card the cell ellipsizes the way the race
   row's club cell already does rather than growing. */
.sc .branch-club{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
/* The clinch card is two halves of one question: where the club stands, and
   what would settle it. The first is the team page's race bar, the second the
   game page's branches - neither is new, which is the point. */
.sc .sc-branches{display:grid;gap:12px;align-content:start;}
.sc .sc-pred{padding:0 2px;}
/* Game consequence, round three. Ford: *"let's also make it a tad more visual,
   it's a lot of text as is today."* The branch card is unchanged; what is
   inside it is now the explorer's bar rather than its text line, so each result
   reads as a picture of where the clubs land. Same numbers, and the same
   component the page animates to when a reader picks that result
   (`social._result_branches`).

   A branch is a third of the canvas at most, so the row takes **the shape the
   page gives this same row under 720px**: the race and its from-to-delta on one
   line, the track full width underneath. Three columns inside a third of a card
   leaves a 50px track, which is a bar in name only - and the two-line form puts
   every track at the same x by construction, which is the alignment fault Ford
   found on the retrospective. */
.sc .branch .scen-row{grid-template-columns:minmax(0,1fr) auto;
  gap:3px 10px;padding:3px 0;}
.sc .branch .scen-label{grid-column:1;grid-row:1;}
.sc .branch .scen-val{grid-column:2;grid-row:1;}
.sc .branch .scen-bar{grid-column:1 / -1;grid-row:2;}
.sc .branch .scen-row.is-swung .scen-label{font-size:0.8rem;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.sc .branch .scen-val{font-size:0.76rem;}
.sc .branch .scen-val b{font-size:0.86rem;}
.sc .branch .scen-delta{font-size:0.75rem;}
.sc .branch .scen-club{font-size:0.85rem;margin-top:8px;padding-top:6px;}
.sc .branch .scen-club:first-child{margin-top:2px;}
.sc .branch .sc-swings{gap:0;}
.sc .sc-clinch{display:grid;gap:14px;justify-items:stretch;}
.sc .sc-clinch .sc-now{max-width:360px;}
.sc .sc-now .eb{margin-top:6px;}
.sc .sc-magic{font-family:var(--mono);font-weight:700;}
/* The clinch card's destinations (AUG-577, re-seated on the section's own
   shape in AUG-742). Inside the branch box is the team page's own Paths
   section, in its own `.pth-` grammar: the route row's shade, name and
   reading, the club's requirement stated once, and each path's rival relation
   with its numbers under it. Nothing here is a card-only mark - what a reader
   recognises on the club's page is what travels.

   Two things the card takes off the page device, both for the reason every
   card device drops them. A relation on the page is an anchor to the rival's
   own page and a raster has nowhere to go, so the relation keeps its weight
   and loses the underline; the split bar's segments are anchors into the route
   rows for the same reason and become spans. And the bar's legend is prose,
   which the canvas has no room for. */
.sc .branch.is-path{gap:0;}
.sc .branch-set.is-paths{align-items:stretch;}
/* The section's complete split, at card size. Shorter than the page's 38px
   because a card magnifies its device rather than restyling it, so the bar
   arrives already scaled by `--sc-zoom` and the page's own height would take
   a destination's worth of the row. */
.sc .pth-bar{height:26px;margin:0;}
/* The route row, minus its disclosure: a raster has nothing to open, so the
   summary's three parts sit on one line and the detail follows underneath. */
.sc .branch.is-path .pth-route-head{display:flex;align-items:center;gap:8px;
  margin:0 0 2px;}
.sc .branch.is-path .pth-route-head .pth-dot{transform:none;}
.sc .branch.is-path .pth-route-v{font-size:0.85rem;}
.sc .branch.is-path .scen-badge{font-size:0.75rem;white-space:nowrap;}
/* The destination's own name, in the weight the section's row gives it rather
   than the mono eyebrow a result branch takes. A result branch is titled with a
   condition ("Chicago win") and reads as a label over the rows; a destination
   branch is titled with the thing itself, and uppercase mono at 0.75rem turns a
   seven-word label into three lines of shouting. */
.sc .branch.is-path .branch-title{font-family:inherit;font-size:0.92rem;
  font-weight:620;letter-spacing:0;text-transform:none;color:var(--ink);
  margin-bottom:0;line-height:1.35;flex:1 1 auto;min-width:0;
  min-height:calc(var(--pth-title,1) * 1.35em);}
.sc .branch .pth-cond{gap:4px;font-size:0.8rem;}
.sc .branch .pth-note{margin:4px 0 2px;font-size:0.8rem;max-width:none;}
.sc .branch .pth-rel{text-decoration:none;}
.sc .branch .pth-share{margin-right:5px;}
/* 0.75rem, not the section's 0.78: this is the smallest type on the card and
   the board's floor is 12px, which 0.75rem is exactly - and `PATH_ZOOM_MIN`
   never draws it under 1.0. */
.sc .branch .pth-back{font-size:0.75rem;margin-top:1px;}

/* Retrospective: the played page's own stacked swing bars, one club per block.

   **Stacked, not side by side.** Round two put the two clubs in two columns and
   Ford found the two faults that construction causes: the bars did not line up
   and the lower half of the card was empty. Both are the same cause - half a
   canvas is not enough width for a label, a readable track and a from-to-delta
   column, so the track was squeezed to about 60px and the row's own grid
   resolved differently on every row (a `1fr` track absorbing the difference
   between `33% -> 58% +25` and `<1% -> <1% +/-0`). Stacking is also what the
   played page itself does: `.scen-rows` is one club's head over its stack, then
   the next club's. So the card now gets the page's arrangement, a full-width
   track, and rows that cannot drift out of line - every track in the fixed
   template below starts at the same x. */
.sc .sc-swing-set{display:grid;gap:14px;align-content:stretch;}
.sc .sc-swing-club{min-width:0;}
.sc .scen-club{margin-top:0;padding-top:0;border-top:none;font-size:1rem;}
.sc .sc-swings{display:grid;gap:6px;}
/* Definite widths on the outer two tracks, and the bar takes the rest. The
   page's template flexes them to content, which is right on a page where each
   club's stack is read on its own; a card puts two stacks under one eye, and
   two bars that start 40px apart read as a rendering fault.

   The label track is wide enough for the longest race any competition
   publishes ("Supporters' Shield"), because a race name that wraps costs its
   row a second line and the card budgets its scale on the row count - the
   round-two MLS retrospective lost its last bar to exactly that. */
.sc .sc-swing-set .scen-row{
  grid-template-columns:158px minmax(0,1fr) 132px;gap:11px;padding:2px 0;}
.sc .sc-swing-set .scen-label{white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;}
.sc .sc-swing-set .sc-swings{gap:3px;}
.sc .sc-score{font-family:var(--mono);font-weight:700;color:var(--accent);}

/* The two charts are SVGs with their own aspect ratio, so they fit rather than
   zoom: `meet` scales the whole drawing, including its labels, into the row.
   The chip row is a control - it ships `hidden` for a script to reveal, and a
   card has no script and nothing to reveal it for.

   Centred, because a chart is only ever as tall as its own viewBox says and
   the row can be taller. AUG-575 left that slack on every canvas and called it
   the honest cost of a season line on a portrait one; the first real export
   showed what it costs - the square spent half its canvas on nothing and the
   portrait nearly two thirds, which reads as a rendering fault rather than as
   a chart with room around it (AUG-628). Stretching to fill would still mean
   `preserveAspectRatio="none"` and still distorts the strokes and the type, so
   the answer is neither of the two this comment used to weigh: the card asks
   for a viewBox the row's shape, per canvas, and `social._graph_devices`
   argues that where it is decided. What is left here is the slack on the
   canvases whose shape the page's chart already suits, and centring it. */
.sc[data-family="season-story"] .sc-device,
.sc[data-family="team-season"] .sc-device{justify-content:safe center;}
.sc .hg,.sc .hg .scroll{min-width:0;overflow:visible;}
.sc .hg-chart{width:100%;height:auto;max-height:100%;min-width:0;}
.sc .hg-keys{display:none;}

/* --- The canvases ----------------------------------------------------- */

/* The square (1080x1080). The canonical composition every other canvas is
   re-laid out from: the rail, the subject, the value, then the device across
   the whole width at the card's remaining height. It keeps the stacked
   composition rather than `og`'s two-column head because it has the height
   for it - what it does not have is `og`'s 1122px, so the type comes down
   from the base sizes and the table's ceiling comes down with the width. */
.sc-1x1{padding:44px 48px 34px;gap:14px;}
.sc-1x1 .sc-top{font-size:22px;}
.sc-1x1 .sc-subject{font-size:52px;}
.sc-1x1 .sc-subject img,.sc-1x1 .sc-subject svg{width:56px;height:56px;
  flex:none;}
.sc-1x1 .sc-value{font-size:64px;gap:16px;}
.sc-1x1 .sc-move{font-size:26px;}
.sc-1x1 .sc-range{font-size:22px;line-height:1.35;}
.sc-1x1 .sc-foot{padding-top:16px;}
.sc-1x1 .sc-brand{font-size:26px;}
.sc-1x1 .sc-brand svg{width:30px;height:30px;}
.sc-1x1 .sc-ts{font-size:20px;}
/* `safe center` rather than `center`: a square is constrained across rather
   than down - the club name decides the table's scale, not the height - so a
   device that has taken all the width it can still leaves slack under it, and
   a picture hanging from the top edge with a band of nothing beneath reads as
   a rendering fault. `safe` is what makes centring survive the other case: a
   device taller than its row falls back to `start`, so it clips at the bottom
   where the caption explains the loss rather than at the top where the reader
   would never know a row had gone. */
.sc-1x1 .sc-device{--sc-zoom:var(--z-1x1,1.45);justify-content:safe center;}
.sc-1x1 .branch-set{grid-template-columns:var(--bc-1x1,repeat(auto-fit,minmax(0,1fr)));}
.sc-1x1 .branch-set.is-paths{--pth-title:var(--tl-1x1,1);}
/* AUG-999, `social.Card.stack_cols`: a race table this canvas
   cannot draw two column groups across any more (`social._stacked_canvases`)
   collapses to one, the same way `.sc-4x5 .sc-cols-2` already does
   unconditionally. Unset, `--rc-1x1` falls back to the ordinary two-across
   grid, so every card that never sets it - every family, and a race-update
   card whose row still fits two across - is untouched. */
.sc-1x1 .sc-cols-2{grid-template-columns:var(--rc-1x1,repeat(2,minmax(0,1fr)));}
/* Stacked rather than side by side: a square has the height the `og` grid
   does not, and stacking gives each half the full 982px instead of 60% of it.
   The spread takes the height that buys - `og` gives its histogram 112px
   because that is what is left over there, not because that is the right
   size for one. */
.sc-1x1[data-family="team-forecast"] .sc-device{--sc-zoom:2.0;}
.sc-1x1 .tph-bars{height:158px;}

/* The portrait (1080x1350). Same width as the square and 270px more height,
   so the one thing it changes is the race table: one column at the full 968px
   rather than two at 475px each. That is the AUG-187 rule applied to the axis
   that actually differs - a portrait canvas is short of width and long on
   height, and splitting it into columns spends the scarce one to save the
   plentiful one. */
.sc-4x5{padding:48px 48px 38px;gap:16px;}
.sc-4x5 .sc-top{font-size:22px;}
.sc-4x5 .sc-subject{font-size:56px;}
.sc-4x5 .sc-subject img,.sc-4x5 .sc-subject svg{width:60px;height:60px;
  flex:none;}
.sc-4x5 .sc-value{font-size:72px;gap:18px;}
.sc-4x5 .sc-move{font-size:28px;}
.sc-4x5 .sc-range{font-size:23px;line-height:1.35;}
.sc-4x5 .sc-foot{padding-top:18px;}
.sc-4x5 .sc-brand{font-size:28px;}
.sc-4x5 .sc-brand svg{width:32px;height:32px;}
.sc-4x5 .sc-ts{font-size:21px;}
.sc-4x5 .sc-device{--sc-zoom:var(--z-4x5,1.35);justify-content:safe center;}
.sc-4x5 .branch-set{grid-template-columns:var(--bc-4x5,repeat(auto-fit,minmax(0,1fr)));}
.sc-4x5 .branch-set.is-paths{--pth-title:var(--tl-4x5,1);}
.sc-4x5 .sc-cols-2{grid-template-columns:minmax(0,1fr);}
.sc-4x5 .sc-cols{gap:16px 0;}
.sc-4x5[data-family="team-forecast"] .sc-device{--sc-zoom:2.2;}
.sc-4x5 .tph-bars{height:225px;}

/* The landscape (1920x1080). The widest canvas and the only one with more
   room than it needs, so nothing here fits by shrinking: the type goes up
   from the square's, the table's ceiling goes up with the width, and the
   device row still ends up the tallest of the four. */
.sc-16x9{padding:52px 64px 42px;gap:16px;}
.sc-16x9 .sc-top{font-size:24px;}
.sc-16x9 .sc-subject{font-size:58px;}
.sc-16x9 .sc-subject img,.sc-16x9 .sc-subject svg{width:62px;height:62px;
  flex:none;}
.sc-16x9 .sc-value{font-size:76px;gap:20px;}
.sc-16x9 .sc-move{font-size:30px;}
.sc-16x9 .sc-range{font-size:24px;line-height:1.35;}
.sc-16x9 .sc-foot{padding-top:20px;}
.sc-16x9 .sc-brand{font-size:30px;}
.sc-16x9 .sc-brand svg{width:34px;height:34px;}
.sc-16x9 .sc-ts{font-size:22px;}
.sc-16x9 .sc-device{--sc-zoom:var(--z-16x9,2.3);justify-content:safe center;}
.sc-16x9 .branch-set{grid-template-columns:var(--bc-16x9,repeat(auto-fit,minmax(0,1fr)));}
.sc-16x9 .branch-set.is-paths{--pth-title:var(--tl-16x9,1);}
.sc-16x9 .sc-cols{gap:0 28px;}
/* AUG-999: see `.sc-1x1 .sc-cols-2` above - the same per-canvas
   stacking fallback, unset on every card today since 16x9's own 1790px row
   has held two columns across at every width this issue has priced. */
.sc-16x9 .sc-cols-2{grid-template-columns:var(--rc-16x9,repeat(2,minmax(0,1fr)));}
.sc-16x9[data-family="team-forecast"] .sc-device{display:grid;
  grid-template-columns:minmax(0,1.45fr) minmax(0,1fr);gap:0 44px;
  align-items:center;--sc-zoom:3.1;}

/* The Open Graph canvas (AUG-483). 1200x630 is barely wider than the square
   and a little over half its height, so the square composition cannot stack
   into it: laid out in one column with the square's 104px headline the device
   gets ~150px, which is the squash AUG-187 forbids. The headline comes down
   and moves onto one line with the value instead, and the device takes the
   whole width underneath at the card's remaining height.

   **Every device on this canvas is a page device drawn wide** - the race
   table, the branch set, the swing bars, the history graph. That is the
   redirection of Ford's 2026-07-31 review: the two devices that wanted a
   narrow column here were the two the league page had already deleted. So
   there is one og layout rather than a wide variant and a narrow one. */
.sc-og{display:grid;padding:28px 38px 16px;gap:7px 24px;
  grid-template-columns:auto minmax(0,1fr);
  grid-template-rows:auto auto 1fr auto auto;
  grid-template-areas:"top top" "subject value" "device device"
    "range range" "foot foot";}
.sc-og .sc-top{grid-area:top;font-size:18px;}
.sc-og .sc-subject{grid-area:subject;align-self:baseline;font-size:38px;
  display:flex;align-items:center;gap:12px;white-space:nowrap;}
.sc-og .sc-subject img,.sc-og .sc-subject svg{width:42px;height:42px;flex:none;}
.sc-og .sc-value{grid-area:value;align-self:baseline;font-size:40px;
  gap:12px;}
.sc-og .sc-move{font-size:21px;}
.sc-og .sc-range{grid-area:range;align-self:end;font-size:18px;
  line-height:1.3;}
.sc-og .sc-device{grid-area:device;}
.sc-og .sc-foot{grid-area:foot;padding-top:10px;}
.sc-og .sc-foot .sc-brand{font-size:24px;}
.sc-og .sc-brand svg{width:28px;height:28px;}
.sc-og .sc-ts{font-size:18px;}

/* The scale this canvas's device row takes. `--z-og` is what the card writes
   on itself for a family whose device is variable in height; the fallback is
   what the fixed-height devices take. */
.sc-og .sc-device{--sc-zoom:var(--z-og,1.6);}
.sc-og .branch-set{grid-template-columns:var(--bc-og,repeat(auto-fit,minmax(0,1fr)));}
.sc-og .branch-set.is-paths{--pth-title:var(--tl-og,1);}
/* AUG-999: see `.sc-1x1 .sc-cols-2` above - `og`'s 1122px row is
   the one landscape canvas narrow enough to need this in practice, once a
   race-update row carries figures and two outcome columns. */
.sc-og .sc-cols-2{grid-template-columns:var(--rc-og,repeat(2,minmax(0,1fr)));}
/* Centred rather than hanging from the top: with the histogram's prose sentence
   gone (`.tph-note`) the round-two composition left a band of empty canvas
   under it, and a bar whose label has been pushed away from it to fill height
   is not a fix. The slack is split above and below instead. */
.sc-og[data-family="team-forecast"] .sc-device{display:grid;
  grid-template-columns:minmax(0,1.45fr) minmax(0,1fr);gap:0 30px;
  align-items:center;--sc-zoom:2.0;}
.sc-og[data-family="game-consequence"] .sc-device,
.sc-og[data-family="clinch"] .sc-device{padding-top:8px;}
/* One branch each way is a short device on a tall row, and a club that has
   settled neither direction has none at all - so this one centres rather than
   hanging from the top with the slack under it. */
.sc-og[data-family="clinch"] .sc-device{justify-content:center;}

/* Game page. */
/* Matchup header (AUG-404): both clubs side by side with their record and rank
   beneath, the kickoff between them, and the win/draw/win forecast as one
   full-width segmented bar underneath. The played hero keeps the same three-
   column shape with a score where the forecast would be, so the two states read
   as one design system. Composed from the shared tokens - mono for the recorded
   numbers, --muted for the quiet labels - so both schemes come for free. */
.gp-matchup{margin-top:clamp(20px,4vw,32px);}
.gp-face{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  gap:10px;margin:0;font-size:1rem;font-weight:400;text-wrap:balance;}
.gp-side{display:flex;flex-direction:column;align-items:center;gap:6px;
  text-align:center;min-width:0;overflow-wrap:break-word;}
/* `align-items:center` sizes each child to its own max-content rather than to
   the column, so a long name's line box was as wide as the word and
   `overflow-wrap` had nothing to break against (AUG-449). Cap the children at
   the column and the break rule above starts working. */
.gp-side>*{max-width:100%;}
.gp-side .crest-lg{width:clamp(48px,12vw,64px);height:clamp(48px,12vw,64px);
  margin:0;}
.gp-face a{font-size:1rem;font-weight:600;line-height:1.15;color:var(--ink);
  text-decoration:none;border-bottom:2px solid transparent;}
.gp-face a:hover{border-color:var(--accent);}
.gp-face a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.gp-sub{font-family:var(--mono);font-size:0.75rem;text-transform:uppercase;
  letter-spacing:0.06em;color:var(--muted);}
.gp-at{font-size:0.85rem;color:var(--muted);text-align:center;}
.gp-when{display:block;margin-top:4px;font-family:var(--mono);font-size:0.75rem;
  color:var(--muted);}
/* `.gc-score` is a compact card's row-aligned number (`text-align:right`,
   `min-width:1.2em` to sit flush against a name); the hero's side is a
   centered column, not a row, so the same class needs its own shape here
   rather than inheriting one built for the other layout (AUG-892 follow-up). */
.gp-side .gc-score{display:block;font-size:1.5rem;font-weight:600;
  text-align:center;min-width:0;margin-top:2px;}
/* AUG-449: the middle column is `auto`, so it sizes to the kickoff line's
   max-content - "Wed 29 Jul, 7:00 PM EDT" is ~158px of a 360px row - and
   leaves each club ~91px. That is narrower than a single long name
   ("Independence", "Diamondbacks"), which then paints across the kickoff line
   and the opposite crest. Cap the column at phone width so the kickoff wraps
   into two short lines and the names get the width instead; the side columns'
   `overflow-wrap` is the backstop for a name no column can hold. */
@media (max-width:560px){.gp-face{grid-template-columns:1fr 88px 1fr;}}
/* The header's prediction bar is the shared component at hero scale: same
   markup and same neutral segment colours, taller and with its key spread the
   full width. A page never gets a second bar component. */
.gp-pred{margin-top:clamp(14px,3vw,20px);}
.gp-pred .pred-bar{height:12px;border-radius:7px;}
.gp-pred .pred-key{font-size:0.75rem;color:var(--muted);margin-top:6px;}
/* The hero's live state (AUG-894, reports/decision-live-game-state.md): the
   "v" between the clubs becomes this badge, so a game already underway reads
   as in progress rather than a matchup still to come - same accent-dot
   treatment as the card's `.gc-live` (AUG-835), no new colour token. */
.gp-live{display:inline-flex;align-items:center;gap:5px;color:var(--accent);
  font-weight:600;}
.gp-live::before{content:"";width:7px;height:7px;border-radius:50%;
  background:var(--accent);flex:none;}
/* What the forecast beside a running game is: the last one published before
   kickoff, stated in words next to the bar it labels, not only in the hidden
   `data-race` claim the audit reads. */
.gp-live-note{margin:6px 0 0;font-size:0.78rem;color:var(--muted);
  text-align:center;}
/* Played-game hero (AUG-376): the final score in place of the forward-looking
   win%. Existing tokens only - no new colour, a score is a fact not a signal. */
.score-line{font-family:var(--mono);font-size:1.7rem;font-weight:600;line-height:1;
  letter-spacing:0;}
.score-line .sl-sep{color:var(--muted);padding:0 6px;}
.sl-note{display:block;margin-top:6px;font-size:0.75rem;text-transform:uppercase;
  letter-spacing:0.08em;color:var(--muted);border-top:1px solid var(--line);
  padding-top:4px;}
/* The played page's closing line (AUG-405): where both clubs stand today, each
   name linking on to that club's own season. It ends the page by putting one
   result back in the season it belongs to. */
.gp-closing{margin-top:14px;font-size:0.85rem;color:var(--muted);}
.gp-closing a{color:var(--accent);text-decoration:none;
  border-bottom:1px solid transparent;}
.gp-closing a:hover{border-color:var(--accent);}
.gp-closing a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}

/* Local status dashboard. */
.status-hero{display:flex;gap:16px;align-items:flex-start;justify-content:space-between;margin:28px 0 18px;padding:20px;border:1px solid var(--line);border-radius:8px;background:var(--surface);}
.status-hero h1{margin:0 0 6px;font-size:1.55rem;letter-spacing:0;text-wrap:balance;}
.status-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin:18px 0 22px;}
.status-card{border:1px solid var(--line);border-radius:8px;background:var(--surface);padding:14px;min-height:126px;}
.status-card strong{display:block;font-size:1.3rem;margin:8px 0 4px;}
.status-label,.status-muted{font-family:var(--mono);font-size:0.75rem;text-transform:uppercase;letter-spacing:0;color:var(--muted);}
.status-section{margin-top:24px;padding-top:18px;border-top:1px solid var(--line);}
.status-meta{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:14px 0 0;}
.status-meta div{border:1px solid var(--line);border-radius:8px;background:var(--surface);padding:12px;min-width:0;}
.status-meta dt{font-family:var(--mono);font-size:0.75rem;text-transform:uppercase;letter-spacing:0;color:var(--muted);}
.status-meta dd{margin:5px 0 0;overflow-wrap:anywhere;}
.status-list{display:grid;gap:8px;list-style:none;margin:14px 0 0;padding:0;}
.status-list li{display:flex;justify-content:space-between;gap:12px;align-items:center;border:1px solid var(--line);border-radius:8px;background:var(--surface);padding:11px 12px;}
.status-list span:first-child{display:grid;gap:3px;min-width:0;}
.status-list code,.status-urls code,.status-warning code{font-family:var(--mono);font-size:.8rem;color:var(--muted);overflow-wrap:anywhere;}
.status-actions{margin:12px 0 0;padding-left:22px;color:var(--muted);}
.status-actions li+li{margin-top:6px;}
.status-empty{margin-top:12px;}
.status-urls{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px;}
.status-urls code{padding:7px 9px;border:1px solid var(--line);border-radius:8px;background:var(--surface);}
.status-warning{display:grid;gap:5px;margin-top:12px;padding:12px;border:1px solid var(--line-strong);border-radius:8px;background:var(--surface);}
.status-warning span{font-family:var(--mono);font-size:0.75rem;text-transform:uppercase;letter-spacing:0;color:var(--muted);}
.status-current{border-color:color-mix(in srgb,var(--qualify) 32%,var(--line));}
.status-stale,.status-incomplete{border-color:color-mix(in srgb,var(--bubble) 40%,var(--line));}
.status-blocked,.status-failed{border-color:color-mix(in srgb,var(--heat-hi) 45%,var(--line));}
@media (max-width:760px){
  .status-grid,.status-meta{grid-template-columns:1fr;}
  .status-hero,.status-list li{align-items:flex-start;flex-direction:column;}
  .status-list li{gap:8px;}.status-urls{display:grid;grid-template-columns:1fr;}
}

/* Track record page. Scoped where it diverges from the shared base. */
code{font-family:var(--mono);font-size:0.85em;background:var(--surface); padding:1px 5px;border-radius:5px;border:1px solid var(--line);}
body.page-track .fresh{display:flex;flex-direction:column;align-items:flex-end;gap:1px; font-size:0.82rem;color:var(--muted);font-family:var(--mono);}
body.page-track .fresh .sub{font-size:0.75rem;color:var(--muted);letter-spacing:0;}
.lede{margin-top:18px;max-width:74ch;font-size:0.98rem;}
body.page-track .sec-head p{max-width:74ch;margin-top:5px;font-size:0.95rem;}
.sub-h{margin:26px 0 4px;font-size:0.92rem;text-transform:uppercase; letter-spacing:0;color:var(--muted);}
.hint{max-width:74ch;font-size:0.86rem;margin-bottom:12px;}
.sub{font-family:var(--mono);font-size:0.75rem;color:var(--muted);letter-spacing:0; text-transform:none;font-weight:500;}
body.page-track table{width:100%;border-collapse:collapse;font-size:0.92rem;min-width:460px;margin-top:6px;}
body.page-track thead th{text-align:left;font-size:0.75rem;text-transform:uppercase;letter-spacing:0; color:var(--muted);font-weight:600;padding:0 12px 8px;border-bottom:1px solid var(--line);}
body.page-track tbody td{padding:9px 12px;border-bottom:1px solid var(--line);vertical-align:middle;}
td.mono,td.num{font-family:var(--mono);font-size:0.86rem;white-space:nowrap;}
td.good,.kv.good{color:var(--qualify);}
.empty{display:flex;gap:12px;align-items:flex-start;padding:18px 18px; background:var(--surface);border:1px dashed var(--line-strong);border-radius:var(--radius); color:var(--muted);font-size:0.92rem;max-width:80ch;}
.empty strong{color:var(--ink);}
.empty-dot{width:10px;height:10px;border-radius:50%;background:var(--bubble);flex:none;margin-top:5px; box-shadow:0 0 0 4px color-mix(in srgb,var(--bubble) 18%,transparent);}
.live-comp{margin-top:16px;padding:16px 18px;background:var(--surface); border:1px solid var(--line);border-radius:var(--radius);}
.live-comp h3{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 12px;}
/* A competition that publishes and has nothing scored yet, named under
   the blocks rather than dropped from the page (AUG-653). */
.live-waiting{margin-top:14px;font-size:0.875rem;color:var(--muted);}
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px; margin:14px 0 4px;}
.kpi{display:flex;flex-direction:column;gap:2px;padding:11px 13px;border:1px solid var(--line); border-radius:8px;background:var(--paper);}
.kv{font-family:var(--mono);font-size:1.15rem;font-weight:600;}
.kl{font-size:0.75rem;color:var(--muted);text-transform:uppercase;letter-spacing:0;}
.kl em{font-style:normal;color:var(--muted);font-family:var(--mono);text-transform:none;}
.legend{display:flex;flex-wrap:wrap;gap:8px 20px;margin:4px 0 6px;font-size:0.85rem; color:var(--muted);}
.lg{display:inline-flex;align-items:center;gap:7px;}
.sw{width:16px;height:4px;border-radius:2px;display:inline-block;}
.sw.elo{background:var(--series-elo);}
.sw.base{background:var(--series-base);}
.sw.diag-key{background:none;border-top:2px dashed var(--faint);height:0;width:18px;}
.rel-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;}
.rel-fig{margin:0;padding:12px 12px 6px;background:var(--surface);border:1px solid var(--line); border-radius:var(--radius);}
.rel-fig figcaption{font-size:0.86rem;font-weight:560;margin-bottom:4px; display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 8px;}
.rel{width:100%;height:auto;display:block;}
.curve{width:100%;min-width:520px;height:auto;display:block;}
.gl{stroke:var(--line);stroke-width:1;}
.diag{stroke:var(--faint);stroke-width:1.2;stroke-dasharray:3 3;opacity:0.8;}
.ax{fill:var(--muted);font-family:var(--mono);font-size:12px;text-anchor:middle;}
.ax.yl{text-anchor:end;}
.ax.unit{text-anchor:start;fill:var(--muted);font-size:12px;}
.ax.cap{text-anchor:start;font-size:12px;fill:var(--muted);}
.ax.cap.end{text-anchor:end;}
.rl{fill:none;stroke-width:2;stroke-linejoin:round;stroke-linecap:round;}
.rl.elo{stroke:var(--series-elo);}
.rl.base{stroke:var(--series-base);}
.rd.elo{fill:var(--series-elo);}
.rd.base{fill:var(--series-base);}
.rd{stroke:var(--surface);stroke-width:1.2;}
.end{font-family:var(--sans);font-size:12px;font-weight:600;}
.end.elo{fill:var(--series-elo);}
.end.base{fill:var(--series-base);}
.cal-table{min-width:560px;}
body.page-track .method dl{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); gap:10px 24px;margin:14px 0 0;}
body.page-track .limits{margin-top:16px;font-size:0.82rem;max-width:78ch;line-height:1.55;}
@media (max-width:560px){
  body.page-track .fresh{align-items:flex-start;width:100%;}
  .empty{align-items:flex-start;}
  .live-comp, .rel-fig, .kpi{padding:12px;}
  .legend, .live-comp h3{gap:7px 12px;}
  .rel-grid, .kpis{grid-template-columns:1fr;}
}

/* ==========================================================================
   Shared board components (AUG-324). One implementation each, reused across
   the reader surfaces (reports/decision-surface-refresh.md). Colour rides on
   the tokens above, so both schemes are handled; nothing here hard-codes a hue.
   ========================================================================== */

/* Sparkline cell: a tiny trend-coloured glance. Colour is carried by the trend
   class through `currentColor`, so the same SVG reads correctly in both
   schemes and the markup holds no hex literal. */
.spark{display:block;overflow:visible;}
.spark polyline{fill:none;stroke-width:1.6;stroke-linecap:round;
  stroke-linejoin:round;}
.spark-rising polyline{stroke:var(--qualify-strong);}
.spark-rising circle{fill:var(--qualify-strong);}
.spark-falling polyline{stroke:var(--out-strong);}
.spark-falling circle{fill:var(--out-strong);}
/* Flat is a faint hairline, not text: --faint is the structure colour. */
.spark-flat polyline{stroke:var(--faint);}
.spark-flat circle{fill:var(--faint);}

/* Delta chip: arrow + signed number, so direction survives greyscale and colour
   only reinforces it. It arrived with the stat tile and outlived it - a played
   game card's odds swing is its one caller now (AUG-408). */
.st-delta{font-family:var(--mono);font-size:0.9rem;font-weight:600;}
.st-delta.up{color:var(--qualify-strong);}
.st-delta.down{color:var(--out-strong);}
.st-delta.flat{color:var(--muted);}

/* Scenario control: the segmented Now / result toggle over conditional bars.
   The Now bars are server-rendered; the control ships `hidden` and the script
   reveals it, so a scriptless reader sees the truthful Now state. Segment
   styling mirrors the standings view switcher `.vsw` above. */
.scenario-control{display:grid;gap:12px;}
.scen-ctl{display:flex;flex-wrap:wrap;gap:4px;padding:3px;background:var(--paper);
  border:1px solid var(--line);border-radius:8px;width:max-content;max-width:100%;}
.scen-ctl[hidden]{display:none;}
.scen-seg-btn{font:inherit;font-size:0.82rem;font-weight:600;color:var(--muted);
  background:none;border:0;border-radius:6px;padding:7px 14px;cursor:pointer;
  white-space:nowrap;}
.scen-seg-btn:hover{color:var(--ink);}
.scen-seg-btn.is-on{background:var(--surface);color:var(--ink);
  box-shadow:0 1px 2px rgba(0,0,0,0.08);}
.scen-seg-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
/* The rows sit in one card, a club at a time: club name, then a bar per race
   that club is in. Consecutive club headings are ruled apart so a long stack
   still reads as two clubs rather than one list (AUG-404). */
.scen-rows{padding:2px 16px 12px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);}
.scen-club{display:flex;align-items:center;gap:9px;font-size:0.92rem;
  font-weight:650;margin-top:12px;padding-top:10px;}
.scen-club img,.scen-club svg{flex:none;}
.scen-club:first-child{margin-top:4px;padding-top:0;}
.scen-club~.scen-club{border-top:1px solid var(--line);}
.scen-row{display:grid;grid-template-columns:minmax(96px,150px) 1fr minmax(150px,auto);
  gap:14px;align-items:center;padding:5px 0;}
.scen-label{font-size:0.82rem;color:var(--muted);}
/* The sub-label is a magic number or a `season odds` note - a qualifier on the
   bar above it, so it takes the mono voice and sits under the race name. */
.scen-label small{display:block;font-family:var(--mono);font-size:0.75rem;
  color:var(--muted);}
.scen-bar{position:relative;height:10px;background:var(--paper);
  border:1px solid var(--line);border-radius:5px;}
.scen-fill.is-in{background:var(--qualify);}
.scen-fill.is-out{background:var(--out);}
/* A race with no published per-result landings: real current odds, greyed
   because the chips cannot move them. --line-strong is structure, so it never
   reads as a qualify/out claim (AUG-404; AUG-416 makes these live). */
.scen-fill.is-static{background:var(--line-strong);}
/* A race with no qualifying line takes the energy bar's flat cobalt: length
   carries the magnitude and no hue claims a side (`swing_bar`, AUG-463). */
.scen-fill.is-mag{background:var(--accent);}
.scen-fill.is-dead{background:var(--muted);}
.scen-row.is-fixed .scen-tick{display:none;}
/* A played game's swing bar (AUG-405): the same row with both ends served. The
   row's subject is the club rather than the race it is in, so its label carries
   a club name at reading weight instead of a race name at caption weight. */
.scen-row.is-swung .scen-label{font-size:0.9rem;font-weight:650;color:var(--ink);}
.scen-static[hidden]{display:none;}
.scen-static{margin-top:14px;}
.scen-fill{position:absolute;top:1px;bottom:1px;left:1px;background:var(--accent);
  border-radius:4px;min-width:2px;transition:width .5s cubic-bezier(.2,.7,.2,1);}
.scen-tick{position:absolute;top:-3px;bottom:-3px;width:2px;margin-left:-1px;
  background:var(--faint);border-radius:1px;opacity:0;
  transition:opacity .3s,left .5s cubic-bezier(.2,.7,.2,1);}
.scen-tick.is-shown{opacity:0.9;}
.scen-val{display:flex;align-items:baseline;gap:5px;justify-content:flex-end;
  font-family:var(--mono);font-size:0.8rem;font-weight:600;color:var(--muted);
  white-space:nowrap;}
.scen-val b{font-size:0.9rem;color:var(--ink);}
/* `now 35% ->` in front of the landing, written by the script when a result is
   picked. Empty at Now, where the bar is simply where the club stands. */
.scen-from:empty{display:none;}
.scen-delta{font-size:0.78rem;font-weight:600;}
.scen-delta.up{color:var(--qualify-strong);}
.scen-delta.down{color:var(--out-strong);}
.scen-delta.flat{color:var(--muted);}
/* A badge is a fact and a number is a forecast (AUG-404): a settled race says
   Clinched or Out in the same green/grey the rest of the board reserves for
   those two states, and carries no percentage beside it. */
.scen-badge{padding:1px 8px;border-radius:5px;font-size:0.75rem;
  text-transform:uppercase;letter-spacing:0.04em;color:var(--paper);}
.scen-badge.is-clinched{background:var(--qualify);}
.scen-badge.is-eliminated{background:var(--faint);}
/* On a phone the bar drops to its own line under the race name and its value,
   rather than squeezing three columns into 390px. Placed explicitly: the row's
   DOM order is label / bar / value, and auto-placement would put the value
   below the bar instead of beside its own label. */
@media (max-width:560px){
  .scen-row{grid-template-columns:minmax(0,1fr) auto;gap:2px 10px;}
  .scen-label{grid-column:1;grid-row:1;}
  .scen-val{grid-column:2;grid-row:1;}
  .scen-bar{grid-column:1 / -1;grid-row:2;}
}

/* ---- Reader Experience v2 grammar (AUG-396) --------------------------------
   The game card, the club nav card and the scenario branch, plus the
   prediction bar they share. Layout follows the approved mocks; type sizes are
   rebased onto this system's 12px floor (0.75rem), which several mock labels
   sat under. Nothing here spends --qualify/--out on anything but
   qualification-side meaning, and no label takes --faint as a text colour. */

/* Prediction bar. The three fills are deliberately NOT the qualify/out pair:
   green and rose mean in-a-place and out-of-one, and a card can carry this bar
   and a playoff number at once. Each segment is named in the key below it. */
.pred{display:block;margin-top:2px;}
.pred-bar{display:flex;height:6px;border-radius:4px;overflow:hidden;
  background:var(--line);}
.pred-seg{height:100%;}
.pred-seg.is-home_win{background:var(--accent);}
.pred-seg.is-draw{background:var(--line-strong);}
.pred-seg.is-away_win{background:var(--muted);}
/* The key wraps inside its own slot rather than holding the row at its content
   width. A three-key soccer bar labelled with club names ("Portland Hearts of
   Pine 24%") is wider than a phone, and a nowrap key made that width the whole
   document's - every upcoming soccer game page scrolled sideways at 390px and
   430px (AUG-417). Alignment carries the mapping instead: first key left, draw
   centre, last key right, the same idiom .tph-labels uses. */
.pred-key{display:flex;justify-content:space-between;gap:8px;margin-top:4px;
  font-family:var(--mono);font-size:0.75rem;color:var(--muted);
  letter-spacing:0.02em;}
.pred-k{min-width:0;overflow-wrap:break-word;}
.pred-k:nth-child(2){text-align:center;}
.pred-k:last-child{text-align:right;}

/* Game card. The whole card is the anchor, so it sets its own colour and
   nothing inside it is a link (nested anchors are invalid and the audit's link
   check would not see them). */
.gc{display:flex;flex-direction:column;gap:8px;padding:12px 14px;
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);color:var(--ink);text-decoration:none;}
.gc:hover{border-color:var(--line-strong);}
.gc:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.gc.featured{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent);}
/* The card's third state (AUG-835). The accent border says "this one", the
   badge says which state, and the note under the bar says what the forecast
   beside a running game is. No new colour token: green and rose already mean
   qualification-side and elimination-side in this system
   (reports/decision-magic-number-brand.md), so a live badge borrowing either
   would collide with a meaning the same card can carry. */
.gc.live{border-color:var(--accent);}
.gc-lead{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;min-width:0;}
.gc-live{display:inline-flex;align-items:center;gap:5px;white-space:nowrap;
  color:var(--accent);font-weight:600;}
.gc-live::before{content:"";width:7px;height:7px;border-radius:50%;
  background:var(--accent);flex:none;}
.gc-pre{font-size:0.78rem;color:var(--muted);}
.gc-top{display:flex;justify-content:space-between;gap:10px;
  font-family:var(--mono);font-size:0.75rem;letter-spacing:0.05em;
  color:var(--muted);text-transform:uppercase;}
.gc-when{white-space:nowrap;}
/* The odds cell never squeezes the club name: the name keeps a floor width and
   the row wraps, so a long cell (a club's own race is labelled when it is not
   the playoff race) drops to its own line instead of breaking "Vancouver
   Whitecaps" mid-word down three lines (AUG-403). */
.gc-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap;}
.gc-row img,.gc-row svg{flex:none;}
.gc-club{flex:1;min-width:6.5em;font-size:0.88rem;font-weight:650;
  overflow-wrap:anywhere;}
.gc-odds{font-family:var(--mono);font-size:0.8rem;color:var(--muted);
  white-space:nowrap;margin-left:auto;}
.gc-score{font-family:var(--mono);font-size:1rem;font-weight:600;
  min-width:1.2em;text-align:right;}
.gc-foot{padding-top:7px;border-top:1px solid var(--line);
  font-size:0.78rem;color:var(--muted);}
.gc-row-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;}
@media (max-width:720px){.gc-row-3{grid-template-columns:1fr;}}

/* Club nav card: the same component on home and on the league page. A long
   club name wraps rather than ellipsizing - a truncated name defeats the one
   job this card has. */
.clubgrid{display:grid;gap:8px;margin-top:12px;
  grid-template-columns:repeat(auto-fill,minmax(182px,1fr));}
.clubgrid-lbl{margin:14px 0 0;font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.08em;color:var(--muted);text-transform:uppercase;}
.clubbtn{display:flex;flex-direction:column;gap:8px;padding:9px 10px;
  background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--radius);color:var(--ink);text-decoration:none;}
.clubbtn:hover{border-color:var(--accent);}
.clubbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.clubbtn img,.clubbtn svg{flex:none;}
.cb-hd{display:flex;align-items:center;gap:9px;min-width:0;}
.cb-tx{flex:1;min-width:0;}
.cb-name{display:block;font-size:0.82rem;font-weight:650;overflow-wrap:anywhere;}
.cb-sub{display:block;font-family:var(--mono);font-size:0.75rem;
  color:var(--muted);}
.cb-ch{flex:none;color:var(--accent);font-size:1rem;line-height:1;}
/* Home's league at a glance (AUG-464): every club's published races stacked
   under its mark, playoff first, as compact energy bars. The card grows from
   ~84px to 254px at 390px, and playoff-only was still rejected: it would keep
   the equal-citizenship promise everywhere except the surface most readers see
   first. What paid for the height is the club count, not the meters - AUG-475
   caps a section at eight clubs in two labelled groups, so the same card costs
   a section ~1,050px instead of the 3,818px thirty of them cost. Trim the
   count with a rule a reader can see, never the meters. */
.cb-races{display:flex;flex-direction:column;gap:6px;}
/* The way out, under the grid rather than as a card inside it: a cell reading
   "all N clubs" would be the "+N more" disclosure decision A deleted, and the
   fold head above already states how many clubs the league has. */
.club-full{margin-top:12px;font-family:var(--mono);font-size:0.78rem;}
.club-full a{color:var(--accent);}

/* ---- Home v5 (AUG-408): the sport -> league brief and router ---------------
   Sport header, then a fold per league that opens into a block; the sport's
   flagship is the one that starts open (AUG-475). Everything inside a block is
   the shared v2 grammar above - the game card and the club nav card - so this
   section is layout only and restyles neither. */
.home-league-group{margin-top:clamp(26px,4vw,40px);}
/* A reader's own clubs, first on the page (AUG-776). Nothing here introduces a
   hue: ownership is carried by position, by the accent hairline the site
   already spends on a followed league fold, and by the club's own badge. The
   card itself is the club-nav card, the tile energy bar and the prediction bar
   - the same three components the league blocks below it draw. */
/* No top spacing of its own: the block is inserted as the wrap's first child,
   where the wrap's own padding is the page's top margin. The first sport group
   below keeps its `margin-top`, which is what separates the two. */
.home-personalized{padding-top:0;}
.hp-hd{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding-bottom:8px;border-bottom:1px solid var(--line);}
.hp-hd h2{margin:0;font-family:var(--mono);font-size:0.85rem;font-weight:600;
  letter-spacing:0.1em;text-transform:uppercase;color:var(--ink);}
.hp-manage{flex:none;font-family:var(--mono);font-size:0.78rem;color:var(--accent);}
.hp-grid{display:grid;gap:10px;margin-top:12px;
  grid-template-columns:repeat(auto-fit,minmax(272px,1fr));}
.hp-card{gap:11px;padding:12px 13px;border-color:var(--accent);}
.hp-card .cb-name{font-size:0.95rem;}
.hp-card .eb-tile .eb-v{font-size:1.6rem;}
.hp-card .eb-label{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 8px;}
/* The sub-line is standings, not the club's identity: it may lose its tail
   rather than take a second line and leave one card in a row taller than its
   neighbours. The club's name above it still wraps in full. */
.hp-card .cb-sub{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
/* Movement. The tick marks where the club stood at the earlier cutoff and the
   number says how far it came; both are neutral, because green and rose mean
   qualification side and spending them on direction would make the palette
   ambiguous on a page that also draws the qualification tracks. */
.eb-tick{position:absolute;top:-2px;bottom:-2px;width:2px;background:var(--ink);}
.eb-note{font-family:var(--mono);font-size:0.75rem;letter-spacing:0.02em;
  color:var(--muted);white-space:nowrap;}
.hp-next{display:flex;flex-direction:column;gap:6px;padding-top:9px;
  border-top:1px solid var(--line);}
.hp-next-top{display:flex;justify-content:space-between;gap:10px;
  font-family:var(--mono);font-size:0.75rem;letter-spacing:0.05em;
  text-transform:uppercase;color:var(--muted);}
.hp-next.is-soon .hp-when,.hp-next.is-live .hp-when{color:var(--accent);font-weight:600;}
.hp-vs{display:flex;align-items:center;gap:8px;font-size:0.88rem;font-weight:650;}
/* Native <details>: the rest of a reader's clubs are one tap away with no
   script at all, the same disclosure home's club grids already use. */
.hp-more{margin-top:10px;}
.hp-more summary{list-style:none;cursor:pointer;display:inline-flex;
  align-items:center;min-height:40px;padding:0 14px;font-family:var(--mono);
  font-size:0.78rem;color:var(--accent);background:var(--surface);
  border:1px solid var(--line-strong);border-radius:var(--radius);}
.hp-more summary::-webkit-details-marker{display:none;}
.hp-more summary:hover{border-color:var(--accent);}
.hp-more summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.hp-more .hp-more-open,.hp-more[open] .hp-more-shut{display:none;}
.hp-more[open] .hp-more-open{display:inline;}
.hp-foot{margin:10px 0 0;font-size:0.8rem;color:var(--muted);}
.hp-foot a{color:var(--accent);}
.hp-empty{margin:12px 0 0;padding:14px 16px;border:1px dashed var(--line-strong);
  border-radius:var(--radius);background:var(--surface);color:var(--muted);
  font-size:.9rem;}
/* The client-drawn badge at the two sizes this block needs. `.sp-badge` is the
   masthead search's own mark, so how a badge drawn from JSON looks has one
   home in this stylesheet rather than one per script. */
.sp-badge-lg{min-width:34px;height:34px;border-radius:8px;border-width:2.5px;
  font-size:0.82rem;}
.sp-badge-sm{min-width:20px;height:20px;border-radius:5px;font-size:0.75rem;}
/* A followed league keeps its accent hairline and says so in a word, because a
   border alone does not tell a reader why this fold is first. */
.lg-following{flex:none;font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.08em;text-transform:uppercase;color:var(--accent);
  border:1px solid var(--accent);border-radius:999px;padding:1px 8px;
  white-space:nowrap;}
/* A reader with saved clubs reserves the block's slot before the payload
   resolves, sized by how many clubs they follow (`--hp-reserve`, capped at the
   three the block shows). A reader with none never gets the class, so the
   static home keeps its exact default geometry. */
html.home-personalizing .home-league-group:first-of-type{margin-top:268px;}
/* The signed-out invitation, first on the page: the claim on one side and one
   real club card on the other, so the offer is shown rather than described. It
   sits on the surface rather than the paper, and the card inside it drops to
   the paper and to the plain hairline - the accent border means "yours", and
   this club is not. */
.home-benefits{display:grid;align-items:center;gap:22px;
  grid-template-columns:minmax(0,1.2fr) minmax(272px,0.8fr);
  padding:18px;background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--radius);}
.home-benefits strong{display:block;margin-bottom:8px;font-size:1.2rem;}
.hb-copy{min-width:0;}
.home-benefits p{margin:0 0 12px;font-size:0.92rem;color:var(--muted);max-width:40ch;}
.hb-acts{display:flex;flex-wrap:wrap;align-items:center;gap:14px;margin:0;}
.hb-cta{display:inline-flex;align-items:center;min-height:44px;padding:0 18px;
  background:var(--accent);color:#fff;border-radius:var(--radius);
  font-weight:650;font-size:0.92rem;text-decoration:none;}
.hb-cta:hover{filter:brightness(1.06);}
/* `hidden` has to beat the CTA's own `display`, or hiding the signed-out half
   of the panel leaves the Google button sitting there (AUG-796). Written
   against the attribute rather than the class so a second anon part inherits
   it. */
[data-home-benefits-anon][hidden]{display:none;}
.hb-cta:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
.hb-preview{position:relative;min-width:0;}
.hb-tag{position:absolute;top:-9px;left:10px;z-index:1;padding:1px 8px;
  background:var(--surface);font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);}
.hb-preview .hp-card{background:var(--paper);border-color:var(--line);}
.home-benefits-dismiss{flex:none;padding:5px 0;border:0;background:transparent;color:var(--muted);
  font:inherit;font-size:.82rem;text-decoration:underline;text-underline-offset:3px;cursor:pointer;}
.home-benefits-dismiss:hover{color:var(--ink);}.home-benefits-dismiss[hidden]{display:none;}
.home-benefits-dismiss:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
.home-benefits[hidden]{display:none;}
.home-coming{margin-top:clamp(26px,4vw,40px);padding:13px 18px;
  border:1px solid var(--line-strong);border-radius:var(--radius);background:var(--surface);
  font-size:.9rem;color:var(--muted);}
.home-coming p{margin:0;}.home-coming a{color:var(--accent);}
.home-sport{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px;
  margin:0;padding-bottom:8px;border-bottom:1px solid var(--line);
  font-family:var(--mono);font-size:0.85rem;font-weight:600;
  letter-spacing:0.1em;text-transform:uppercase;color:var(--ink);}
.home-sport span{min-width:0;font-weight:400;letter-spacing:0.07em;
  color:var(--muted);overflow-wrap:anywhere;}
.lg-name{font-weight:700;font-size:1rem;}
.lg-meta{min-width:0;font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.04em;color:var(--muted);text-transform:uppercase;
  overflow-wrap:anywhere;}
/* A card inside a block sits on the page colour, so the block reads as the
   surface and the cards as objects on it. */
.lg-fold .gc{background:var(--paper);}
.lg-games{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;
  margin-top:12px;}
@media (max-width:720px){
  .lg-games{grid-template-columns:1fr;}
  /* The card goes under the claim rather than beside it, and stays: a reader
     who has not seen what the page becomes is exactly the reader this panel
     is for. */
  .home-benefits{grid-template-columns:1fr;gap:18px;}
}
.lg-quiet{margin-top:12px;font-size:0.85rem;color:var(--muted);}
/* Every league is a native <details>, so its games and clubs are one tap away
   with no script at all, and the flagship's `open` is the only thing that makes
   it the one a reader lands inside (AUG-475). */
.lg-fold{margin-top:12px;background:var(--surface);
  border:1px solid var(--line-strong);border-radius:var(--radius);}
.lg-fold-head{display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  padding:13px 18px;list-style:none;cursor:pointer;}
.lg-fold-head::-webkit-details-marker{display:none;}
.lg-fold-head:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.lg-fold-head:hover .lg-name{color:var(--accent);}
.lg-hint{margin-left:auto;font-family:var(--mono);font-size:0.78rem;
  color:var(--accent);white-space:nowrap;}
.lg-hint::after{content:"\25be";display:inline-block;margin-left:6px;
  transition:transform .15s;}
.lg-fold[open] .lg-hint::after{transform:rotate(180deg);}
/* Which word the affordance shows is the <details> state, not a script. */
.lg-fold .lg-shut,.lg-fold[open] .lg-open{display:none;}
.lg-fold[open] .lg-shut{display:inline;}
.lg-fold[data-personalized-league]{border-color:var(--accent);}
.lg-fold-body{padding:0 18px 16px;}
/* A publishing league with nothing published yet keeps its place in its sport
   rather than disappearing from the page. */
a.lg-fold.lg-none{display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  padding:13px 18px;color:var(--ink);text-decoration:none;}
.lg-none .lg-meta{font-style:italic;}
@media (max-width:560px){
  /* One card per row here, so the reservation is per card rather than one
     blanket height: a reader who follows one club is not shown three cards'
     worth of blank before the payload lands. */
  html.home-personalizing .home-league-group:first-of-type{
    margin-top:calc(248px * var(--hp-reserve,3) + 46px);}
  .hp-grid{grid-template-columns:1fr;}
  .lg-fold-head,a.lg-fold.lg-none{padding:12px 14px;}
  .lg-fold-body{padding:0 14px 14px;}
  /* The club grid goes 2-up on a phone rather than to one column: finding your
     club is scanning marks, and one per row turns two rows into ten. */
  .clubgrid{grid-template-columns:repeat(2,minmax(0,1fr));}
}

/* Scenario branch. A counterfactual is dimmed, not faded out of legibility:
   an opacity step on the card, never --faint on its text. */
.branch-set{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;}
@media (max-width:720px){.branch-set{grid-template-columns:1fr;}}
.branch{position:relative;display:flex;flex-direction:column;gap:4px;
  padding:13px 15px;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);}
.branch.is-actual{border-color:var(--qualify);}
.branch.is-dim{opacity:0.72;}
.branch-title{font-family:var(--mono);font-size:0.75rem;letter-spacing:0.06em;
  color:var(--muted);text-transform:uppercase;margin-bottom:4px;}
.branch.is-actual .branch-title{color:var(--qualify-strong);}
/* The race a run of rows is about, drawn once above them (AUG-463). Same mono
   eyebrow voice as the slice's unit line, at the 12px floor every label on the
   board stops at, and tighter because this one lives inside a card rather than
   over a block. The first run sits directly under the title, so only later ones
   are spaced away from the rows above them. */
.branch-race{font-family:var(--mono);font-size:0.75rem;letter-spacing:0.06em;
  color:var(--muted);text-transform:uppercase;}
.branch-row+.branch-race{margin-top:7px;}
/* A badge that names a side of the race takes that side's token, because hue
   here means qualification-side or elimination-side and nothing else: a
   guarantee of elimination in green read as its own opposite (AUG-633). A
   badge that names no side - "What happened" on a played game - is neither,
   and keeps the default. */
.branch-badge{position:absolute;top:-9px;right:10px;padding:2px 8px;
  border-radius:999px;background:var(--qualify);color:var(--paper);
  font-family:var(--mono);font-size:0.75rem;font-weight:600;
  letter-spacing:0.05em;text-transform:uppercase;white-space:nowrap;}
.branch-badge.is-qualify{background:var(--qualify);}
.branch-badge.is-out{background:var(--out);}
.branch-row{display:flex;justify-content:space-between;align-items:baseline;
  gap:10px;font-size:0.85rem;}
.branch-club{min-width:0;overflow-wrap:anywhere;}
.branch-move{display:flex;align-items:baseline;gap:5px;white-space:nowrap;
  font-family:var(--mono);font-size:0.8rem;color:var(--muted);}
.branch-move b{color:var(--ink);}
.branch-arrow{color:var(--muted);}

/* Race row (AUG-404, AUG-471): one row per club carrying the standings, the
   leading race, and every other race the competition publishes. Rank, mark and
   name sit left and stay put; the shared 0-100 track sits beside them, so every
   club's dot is read against the same axis whatever the row order is. Where the
   leading race has a cutline, green at or above 50%, rose below, grey when out
   - the dot is qualification-side, never table-side (decision-reader-experience
   -v2 7).

   Hue is split down the row. The leading column keeps those three colours only
   where a cutline runs through it (`is-mag` otherwise - AUG-975, the Premier
   League's title column); every other race is always a flat cobalt energy bar,
   because that colour rule is a claim about a line and at most one of these
   races has one (decision E, pre-launch-visual-revamp-plan).

   The table is the page's one wide element and scrolls inside this box, so a
   phone reaches the far races by swiping rather than losing them and the page
   body never scrolls sideways (design-system page structure, AUG-417). The
   column template and the row's floor come from the markup, because the column
   set is the competition's: `--rr-cols` and `--rr-min` are written on the slice
   by `components.race_columns`. */
.race-slice{position:relative;padding:14px 18px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);overflow-x:auto;}
/* The affordance: a fade at the right edge that says there is more table. It is
   sticky rather than absolute so it rides the scroll, the same construction the
   wide tables already use. It reaches back over the card's own padding
   (`right`/`width` pay for the 18px) so it washes the edge of the last visible
   column rather than only the gutter beside it - a fade with nothing under it
   is not an affordance. */
.race-slice::after{content:"";position:sticky;right:-18px;top:0;float:right;
  width:30px;height:100%;margin-right:-18px;pointer-events:none;
  background:linear-gradient(to right,transparent,var(--surface));}
.rr-unit{margin:14px 0 6px;font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.08em;color:var(--muted);text-transform:uppercase;}
/* The row is a fixed 31px so a slice reads as a compact block rather than a
   table - the round-2 proportions Ford picked in the 2026-07-25 review. */
.rrow{display:grid;align-items:center;gap:10px;height:31px;
  grid-template-columns:var(--rr-cols);min-width:var(--rr-min);
  color:var(--ink);text-decoration:none;}
/* The head is a single faint line inside the card, and it is what makes the
   races affordable: a cell in a labelled column repeats nothing. */
.rr-head{height:auto;padding-bottom:4px;margin-bottom:4px;
  border-bottom:1px solid var(--line);}
.rr-head span{font-family:var(--mono);font-size:0.75rem;letter-spacing:0.08em;
  color:var(--muted);text-transform:uppercase;text-align:right;}
.rr-head .rr-rec{text-align:center;}
/* A race's own name is the one header that can outrun its column - "World
   Series" and "Supporters' Shield" both do - so this cell wraps where the rest
   do not. The head is the slice's only auto-height row, which is what makes
   two lines cheaper than the truncation that would otherwise read "World S...". */
.rr-head .rr-o{text-align:left;white-space:normal;line-height:1.25;
  overflow-wrap:break-word;}
/* "PLAYOFFS" (uppercase, one word) overflows the 52px `pct` column on its own
   line - a pre-existing gap the AUG-735 header consolidation makes worth
   closing, since the one instance a reader now sees is this column's only
   label rather than one of several repeats. Same wrap treatment as `.rr-o`
   rather than a wider column: the column set is the competition's and stays
   unchanged (AUG-735 non-goal), only how its own label breaks. */
.rr-head .rr-pc{white-space:normal;line-height:1.25;overflow-wrap:break-word;}
/* AUG-926: the sort affordance is added by `wireRaceSort`, not shipped in the
   markup - a header with no script to answer a click stays plain text, same
   as the share button and view switcher stay hidden until their scripts run. */
.rr-sort-h{cursor:pointer;}
.rr-sort-h:hover{color:var(--ink);}
.rr-sort-h:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.rr-sort-h.is-sort-asc::after,.rr-sort-h.is-sort-desc::after{margin-left:3px;}
.rr-sort-h.is-sort-desc::after{content:"\25bc";}
.rr-sort-h.is-sort-asc::after{content:"\25b2";}
/* Sorted order is not the competition's own order, so the cutline - which
   marks a rank boundary - no longer means anything and is hidden rather than
   left sitting at whatever row it lands on after the reorder. */
.race-slice.rr-sorted .rr-cutwrap{display:none;}
.rr-gb{font-family:var(--mono);font-size:0.78rem;color:var(--muted);
  text-align:right;}
a.rrow:hover{background:var(--paper);}
a.rrow:hover .rr-nm{background:var(--paper);}
a.rrow:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;}
.rrow.is-dead{opacity:0.62;}
.rrow.is-focus .rr-club{color:var(--accent);}
.rr-rk{flex:none;width:18px;font-family:var(--mono);font-size:0.75rem;
  color:var(--muted);text-align:right;}
/* The same cell holding the club's record, in a table whose rules settled no
   order to number the rows with (AUG-653). It stays in the sticky club column
   so a phone reader sees it without swiping, which is the whole reason it is
   here rather than in the record column further right. Sized for a two-figure
   W-D-L, and it never wraps: at 18px a mid-season 10-2-4 broke onto three
   lines and took the row's fixed height with it. */
.rr-rk.is-rec{width:44px;white-space:nowrap;overflow:hidden;}
/* Rank and club are one cell so they can be one sticky column: a reader swiping
   the races never loses whose row they are reading. The background is the
   card's own, because a transparent sticky cell would have the scrolled table
   sliding underneath it. */
.rr-nm{display:flex;align-items:center;gap:6px;min-width:0;
  position:sticky;left:0;z-index:1;background:var(--surface);
  box-shadow:6px 0 6px -6px rgba(0,0,0,0.20);}
.rr-nm img,.rr-nm svg{flex:none;}
.rr-club{font-size:0.85rem;font-weight:650;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;}
.rr-rec{font-family:var(--mono);font-size:0.75rem;color:var(--muted);
  text-align:center;}
.rr-fig{font-family:var(--mono);font-size:0.8rem;font-weight:600;
  text-align:right;}
/* A race with no cutline, as the shared energy bar: the label is drawn once by
   the column header, so the cell is the value over its track. Tighter than the
   bar's own compact size only in leading - the track stays 11px, which is what
   keeps it a vessel rather than the race row's 2px rail. */
.rr-o{min-width:0;}
.rr-o .eb-v{font-size:0.75rem;line-height:1.1;}
.rr-o .eb-track{margin-top:2px;}
.rr-o .scen-badge{font-size:0.75rem;padding:0 5px;}
.rr-track{position:relative;height:14px;}
.rr-rail{position:absolute;inset:6px 0;height:2px;background:var(--line);
  border-radius:2px;}
.rr-fill{position:absolute;top:6px;left:0;height:2px;border-radius:2px;}
.rr-dot{position:absolute;top:1.5px;width:11px;height:11px;margin-left:-5.5px;
  border-radius:999px;border:2px solid var(--surface);}
.rrow.is-in .rr-fill,.rrow.is-in .rr-dot{background:var(--qualify);}
.rrow.is-out .rr-fill,.rrow.is-out .rr-dot{background:var(--out);}
.rrow.is-dead .rr-fill,.rrow.is-dead .rr-dot{background:var(--muted);}
/* A leading column with no cutline (AUG-975, `race_row`'s `cutline=False`) -
   the Premier League's title race - takes the shared energy bar's flat
   cobalt instead of borrowing qualification-side green or rose. */
.rrow.is-mag .rr-fill,.rrow.is-mag .rr-dot{background:var(--accent);}
.rr-pc{font-family:var(--mono);font-size:0.8rem;color:var(--muted);
  text-align:right;}
/* The caption leads the line rather than trailing it, and sticks to the left
   edge with the club column: a caption a reader has to swipe to find is a line
   of dashes with no explanation on the view they start from (decision E). */
.rr-cutwrap{display:flex;align-items:center;gap:8px;min-width:var(--rr-min);}
.rr-cut{flex:1;border:none;border-top:1.5px dashed var(--accent);margin:7px 0;}
.rr-cut-lbl{position:sticky;left:0;z-index:1;padding:4px 6px 4px 0;
  background:var(--surface);font-family:var(--mono);font-size:0.75rem;
  letter-spacing:0.07em;color:var(--accent);text-transform:uppercase;
  white-space:nowrap;}
/* The trajectory (AUG-459): playoff odds at every published cutoff, with a
   pre-highlighted default set and every other club as a ghost line. Colour here
   is identity, not state - each drawn line takes its own club's `--club` colour
   (solved for both themes by `branding.line_colour`) so six lines can be told
   apart, which the two qualification hues could not do. The cutline semantics
   stay on the race table's track, where a line is actually drawn. Its own
   section wrapper left with AUG-734: it is a panel of the season section now,
   and the panel owns the spacing above it. */
.hg{margin-top:8px;position:relative;}
/* `min-width` grew with the viewBox (AUG-518 widened it 760->806 for the
   right-hand axis column) so the chart renders at the same pixel scale as
   before - 660px was tuned for a 760-unit box, and 660*806/760 rounds to 700. */
.hg-chart{display:block;width:100%;min-width:700px;height:auto;}
/* Scoped to the axis classes, never bare `text`: the club badges in the gutter
   are nested <g> drawings with their own font sizing, and a rule on `text`
   inside the chart overrides the presentation attribute that keeps a four-letter
   code inside its square. */
.hg-chart .hg-yl,.hg-chart .hg-xl,.hg-chart .hg-el,.hg-chart .hg-yt{
  font-family:var(--mono);font-size:12px;fill:var(--muted);}
/* The axis sits right of the plot now (AUG-518), so its tick numbers grow away
   from the lines - the default SVG anchor - rather than ending flush against
   them the way the retired left-side axis needed. */
.hg-chart .hg-yt{letter-spacing:0.04em;}
.hg-grid{stroke:var(--line);stroke-width:1;}
.hg-line polyline{fill:none;stroke-linejoin:round;stroke-linecap:round;}
/* The ghost is structure, not text: --line-strong is the weight the gridlines
   and leader lines carry, so the rest of the field reads as context. */
.hg-line.is-ghost polyline{stroke:var(--line-strong);stroke-width:1.1;opacity:0.55;}
/* The club colour rides on the group; the stylesheet decides when it applies,
   so a chip toggles a club by adding a class and never touches a colour. */
.hg-line.is-on polyline{stroke:var(--club);stroke-width:2.4;opacity:1;}
.hg-line.is-hot polyline{stroke-width:3.8;}
.hg-end{fill:var(--ink);}
.hg-lead{stroke:var(--line-strong);stroke-width:1;}
.hg-col{fill:transparent;}
.hg-scan{stroke:var(--ink);stroke-width:1;opacity:0.35;stroke-dasharray:3 3;}
.hg-keys{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-top:14px;}
.hg-keys[hidden]{display:none;}
.hg-keys-lede{font-family:var(--mono);font-size:0.75rem;letter-spacing:0.05em;
  text-transform:uppercase;color:var(--muted);margin-right:2px;}
.hg-chip{display:inline-flex;align-items:center;padding:3px 9px 3px 6px;
  border:1px solid var(--line);border-radius:999px;background:var(--surface);
  font-family:var(--mono);font-size:0.75rem;color:var(--muted);cursor:pointer;
  opacity:0.65;}
.hg-chip .crest-sm,.hg-chip svg{width:14px;height:14px;margin-right:4px;}
.hg-chip:hover{opacity:1;}
/* A pressed chip wears the same colour its line does, so the two read as one
   club. The swatch is a border rather than a fill: the club colour is solved to
   clear 3:1 as a line, which is the graphic floor, not the 4.5:1 a background
   under text would need. */
.hg-chip[aria-pressed="true"]{opacity:1;color:var(--ink);background:var(--paper);
  border-color:var(--club);border-left:4px solid var(--club);padding-left:6px;}
.hg-chip:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.hg-tip{position:absolute;z-index:5;pointer-events:none;background:var(--surface);
  border:1px solid var(--line-strong);border-radius:7px;padding:7px 9px;
  box-shadow:0 6px 20px rgba(10,16,28,0.14);font-family:var(--mono);
  font-size:0.75rem;color:var(--ink);min-width:120px;}
.hg-tip b{display:block;color:var(--muted);font-weight:600;margin-bottom:4px;}
.hg-tip div{display:flex;justify-content:space-between;gap:12px;line-height:1.55;}
.hg-tip div span:last-child{font-variant-numeric:tabular-nums;}
.hg-empty{display:flex;gap:10px;align-items:center;margin-top:10px;
  color:var(--muted);font-size:0.9rem;}
.hg-dot{width:9px;height:9px;border-radius:50%;background:var(--accent);flex:none;}
@media (max-width:560px){
  .hg-chart{min-width:636px;}
  .hg-keys-lede{width:100%;margin-bottom:2px;}
}

/* Turning points (AUG-402). One story row per game that moved the race: date,
   the club's odds line to that moment, the sentence, and the link affordance.
   The whole row is the anchor, so the class hangs off `.moment` and every part
   inside it is scoped there - `.when` / `.story` / `.go` are ordinary words and
   must never become page-wide selectors. The sparkline colours itself from the
   shared spark- trend classes, so nothing here spends a hex. */
.tp-list{display:flex;flex-direction:column;gap:10px;margin-top:10px;}
.moment{display:flex;align-items:center;gap:14px;padding:12px 16px;
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);color:inherit;text-decoration:none;}
.moment:hover{border-color:var(--line-strong);}
/* The mock drew this date in --faint; --muted is what the contrast floor
   allows for text a reader reads (AUG-177). */
.moment .when{flex:none;width:52px;font-family:var(--mono);font-size:0.75rem;
  color:var(--muted);}
.moment .spark{flex:none;}
.moment .story{flex:1;min-width:0;font-size:0.9rem;}
.moment .story b{font-weight:650;overflow-wrap:anywhere;}
.moment .story small{display:block;color:var(--muted);font-size:0.78rem;
  font-family:var(--mono);}
.moment .go{flex:none;color:var(--accent);font-family:var(--mono);
  font-size:0.75rem;}
/* The one "we have nothing published to show you here" line, shared by the
   turning points section and the played game page's stakes (AUG-405). */
.tp-none,.gp-none{color:var(--muted);font-size:0.9rem;margin-top:10px;}
.tp-record{color:var(--muted);font-size:0.78rem;font-family:var(--mono);
  margin-top:10px;}
@media (max-width:560px){
  .moment{flex-wrap:wrap;gap:8px 12px;}
  .moment .story{flex-basis:100%;order:3;}
}

/* Energy bar (AUG-458): any published outcome as a labelled 0-100 fill. The
   label is beside the number in every size, because a probability that does not
   name its race is an implication rather than a label
   (reports/pre-launch-visual-revamp-plan.md).

   The fill is one flat cobalt and length alone carries magnitude. A ramp was
   built first and dropped on 2026-07-27: it put a second hue rule on a form
   that already reads like the race row's track, so the same club at the same
   37% was rose on a league page and mid-ramp on home. Colour now means one
   thing on both - qualification-side state - and it appears here only where the
   snapshot's own `status` puts it (green clinched, grey eliminated), with the
   badge carrying the fact and the fill agreeing.

   The track is deliberately thicker than the race row's 2px rail and carries no
   dot. That is what keeps the two apart: a race row is a mark on a shared axis
   ("which side of the line?"), this is a filled vessel ("how much?"). Two
   constructions that look alike and mean differently are worse than two that
   look different (design system, chart grammar). */
.eb{display:block;}
.eb-top{display:flex;align-items:baseline;justify-content:space-between;gap:8px;}
.eb-label{min-width:0;overflow-wrap:break-word;color:var(--muted);}
.eb-v{font-family:var(--mono);color:var(--ink);}
/* --paper inside a card and --line's hairline around it: the trough is
   structure, so it never reads as a value of its own. */
.eb-track{position:relative;display:block;background:var(--paper);
  border:1px solid var(--line);border-radius:3px;overflow:hidden;}
/* A settled club's fill sits at the rail its badge names - full at clinched,
   empty at eliminated - and the min-width keeps the empty one visible as a
   coloured nub rather than a track with nothing in it. */
.eb-fill{position:absolute;top:0;bottom:0;left:0;min-width:2px;
  border-radius:2px;}
.eb-fill.is-mag{background:var(--accent);}
.eb-fill.is-clinched{background:var(--qualify);}
.eb-fill.is-eliminated{background:var(--muted);}
/* Compact: the grid cell and the table row. Tile: the hero, where the number is
   the thing a reader came for. Same markup, same grammar, two weights. The
   tracks are 11px and 14px rather than the 6px and 10px built first - at 6px
   the bar and the race row's rail were both "a thin horizontal thing", and
   thickness is what separates a vessel from a mark. */
/* The header-labelled variant (AUG-471): the column header draws the label, so
   the cell does not repeat it. The label stays in the DOM and is only taken out
   of the picture, through the shared `.vh` every hidden race label uses - a
   screen reader landing on the cell still hears which race it is.
   `position:relative` on the row above it is what keeps the clipped label from
   escaping to the nearest positioned ancestor and dragging the page sideways. */
.eb-hdr .eb-top{position:relative;}
.eb-compact .eb-label{font-size:0.75rem;}
.eb-compact .eb-v{font-size:0.8rem;font-weight:600;}
.eb-compact .eb-track{height:11px;margin-top:3px;}
.eb-tile .eb-label{font-size:0.85rem;}
.eb-tile .eb-v{font-size:1.5rem;font-weight:650;letter-spacing:0;}
.eb-tile .eb-track{height:14px;margin-top:6px;}
/* The badge is the same one the scenario rows use; at tile size it takes the
   weight the number it replaced would have had. */
.eb-tile .scen-badge{font-size:0.8rem;padding:2px 10px;}

/* Motion layer: staggered reveal-on-scroll. The hidden-then-in state applies
   ONLY under the root flag the shared script sets, so no-JS and (via the global
   reduced-motion rule above) reduced-motion readers always see the final
   state. Count-up numbers keep their final value as text regardless. */
[data-board-motion] .board-reveal{opacity:0;transform:translateY(10px);
  transition:opacity .5s ease var(--reveal-delay,0s),
             transform .5s ease var(--reveal-delay,0s);}
[data-board-motion] .board-reveal.is-in{opacity:1;transform:none;}

/* Section index (AUG-732): the sticky rail of chips both reader page types
   carry, under the masthead. Every chip is a plain anchor to an id in the same
   document, so the rail is a working table of contents with the script off;
   `wireSectionIndex` only marks which chip the reader is inside.

   The negative inline margin is `.wrap`'s own gutter, read from the property
   that declares it rather than restated, so the bar bleeds to both edges - a
   sticky bar inset by the gutter leaves content sliding past it in the margins
   - and the padding puts the gutter back for the chips. Because the two are
   the same value the document never gains width from it at any breakpoint. */
.sec-rail{position:sticky;top:0;z-index:20;
  margin-inline:calc(var(--wrap-pad) * -1);
  padding:0 var(--wrap-pad);
  background:var(--paper);border-bottom:1px solid var(--line);
  box-shadow:0 6px 14px -10px color-mix(in srgb,var(--ink) 55%,transparent);}
/* The row scrolls inside itself. A phone fits three or four chips of a
   six-chip index, and the alternative - wrapping to a second line - is a
   sticky bar that eats a third of the screen on the page type that can least
   afford it. */
.sec-jump{display:flex;gap:4px;overflow-x:auto;padding:7px 0 6px;
  scrollbar-width:none;}
.sec-jump::-webkit-scrollbar{display:none;}
.sec-chip{flex:0 0 auto;font-size:0.82rem;font-weight:500;text-decoration:none;
  white-space:nowrap;color:var(--muted);padding:5px 11px;border-radius:20px;
  border:1px solid transparent;min-height:32px;display:inline-flex;
  align-items:center;}
/* The section in view. Ink, weight and a bordered surface rather than a colour
   token: which section a reader is in is a position, not a qualification
   claim, and hue on this board means side-of-the-line. */
.sec-chip.is-here{color:var(--ink);border-color:var(--line-strong);
  background:var(--surface);font-weight:600;}
.sec-chip:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
/* The jump lands below the sticky rail rather than under it. Scoped to the
   indexed sections so no other anchor on the site changes where it lands. */
.sec-anchor{scroll-margin-top:64px;}

/* The trophy hero (AUG-741, `src/board/trophy_hero.py`): the league page's
   opening statement, between the identity block and the indexed body. It is
   not an indexed section and carries no chip - the reasoning is in that
   module's docstring, not repeated here.

   The heading block is the popover's positioned ancestor, so the definition
   opens directly under the question rather than under the whole hero. */
.th{margin:0 0 22px;}
.th-head{position:relative;}

/* The split bar. Both halves are solid cobalt: this race has no cutline, so
   nothing here may spend hue on magnitude, and fading the smaller half made
   the larger one read as the weaker side (caught in the mock). Length is the
   whole claim, and the flex weights are the published sums, so the bar is to
   scale (`reports/decision-magic-number-brand.md`).

   A half wraps its own label rather than clipping it: an ellipsis inside the
   bar would hide which side of the bracket a share belongs to, and a
   min-width big enough to prevent it would stretch the axis, which the
   to-scale rule forbids. A lopsided split therefore costs the bar a line of
   height, never a truncated word. */
.th-bar{display:flex;gap:3px;min-height:44px;margin-top:12px;}
.th-half{flex-basis:0;min-width:0;display:flex;flex-direction:column;
  align-items:flex-start;justify-content:center;gap:1px;padding:5px 11px;
  border-radius:8px;background:var(--accent);color:var(--surface);}
.th-half b{font-size:0.82rem;font-weight:600;letter-spacing:0.01em;
  overflow-wrap:anywhere;}
.th-half i{font-family:var(--mono);font-size:0.88rem;font-style:normal;
  font-weight:600;}

/* One column per side of the bracket, or one column and no bar for a
   competition that has no split to draw. */
.th-cols{display:grid;grid-template-columns:1fr 1fr;gap:6px 10px;margin-top:14px;}
.th-cols.is-single{grid-template-columns:1fr;}
.th-col{min-width:0;display:grid;gap:2px;align-content:start;}
.th-col .tp-kicker{margin-bottom:3px;}
.th-row{display:flex;align-items:center;gap:4px;min-width:0;min-height:32px;
  padding:3px 6px;border-radius:6px;text-decoration:none;color:inherit;}
.th-row:hover{background:var(--surface);}
.th-row:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
/* Never an ellipsis: a name no rule shortens wraps (`short_club_name`). */
.th-row b{flex:1 1 auto;min-width:0;font-size:0.82rem;font-weight:500;
  overflow-wrap:anywhere;}
.th-row i{flex:none;font-family:var(--mono);font-size:0.78rem;font-style:normal;
  color:var(--muted);}
/* The leader of each side reads as the leader without a second colour. */
.th-col .th-row:first-of-type b{font-weight:700;}
.th-col .th-row:first-of-type i{color:var(--ink);}
/* A followed club, marked in place. The star ships in the markup and stays
   hidden until the shared script has read this reader's own favourites, so a
   scriptless reader - and a reader with no favourite - sees an ordinary row
   rather than a mark that means nothing. */
.th-fav{display:none;flex:none;color:var(--accent);font-size:0.78rem;line-height:1;}
.th-row.is-fav{background:color-mix(in srgb,var(--accent) 14%,transparent);
  box-shadow:inset 2px 0 0 var(--accent);}
.th-row.is-fav .th-fav{display:inline;}
.th-note{font-size:0.79rem;line-height:1.45;margin:9px 0 0;color:var(--muted);}

/* The two readings the drawing does not already give: how many clubs have a
   realistic chance, and the one game the rest of the page is about to argue
   over. */
.th-tiles{display:grid;gap:8px;margin-top:14px;
  grid-template-columns:repeat(auto-fit,minmax(154px,1fr));}
.th-tile{display:block;border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);padding:11px 13px 12px;min-width:0;
  color:inherit;text-decoration:none;}
a.th-tile:hover{border-color:var(--accent);}
a.th-tile:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
/* No negative tracking: this is a compact panel, not display type, and the
   stylesheet reserves tightened letter-spacing for the wordmark and the share
   card's own display sizes. */
.th-tile-v{display:block;font-size:1.3rem;font-weight:650;line-height:1.15;
  margin-top:4px;text-wrap:balance;}
.th-tile-v span{display:block;margin-top:2px;font-family:var(--mono);
  font-size:0.75rem;font-weight:400;color:var(--muted);
  text-transform:uppercase;letter-spacing:0.05em;}
.th-tile-n{font-size:0.79rem;line-height:1.4;margin:6px 0 0;color:var(--muted);}

