/*
 * Fantasy Pressbox: design tokens (brand "Cartridge", the 8-bit evolution of Chyron).
 *
 * Theme mechanism
 *   - Light tokens are custom properties on :root.
 *   - Dark (the brand's default look) follows the OS:
 *     @media (prefers-color-scheme: dark) redefines them on
 *     :root:not([data-theme='light']), so an explicit light choice wins.
 *   - Dark can also be chosen explicitly: :root[data-theme='dark'] carries the
 *     same dark set. The two dark blocks must stay identical.
 *   - Each block sets color-scheme so form controls and scrollbars match.
 *   - Pages apply a saved choice from localStorage (set data-theme on <html>)
 *     before first paint, in an inline script wrapped in try/catch.
 *
 * Fonts: put this on every page's <head> (preconnect to fonts.googleapis.com
 * and fonts.gstatic.com first):
 *   https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Space+Mono:wght@400;700&family=Barlow:wght@400;600;700&display=swap
 *
 * Contrast ratios are against --ground, then --surface, unless stated.
 * All checked 2026-10-12.
 * Tokens come first; the shared shell components follow. Components use only
 * tokens: no colour, font or raw pixel length is written outside the :root
 * blocks (tests/tokens.test.mjs and tests/shell.test.mjs check this).
 */

:root {
  color-scheme: light;

  /* Surfaces */
  --ground: #F2F2E9;
  --surface: #FFFFFF;
  --line: #C9C9BE;

  /* Text */
  --ink: #0F1218;       /* 16.7:1, 18.7:1 */
  --mute: #4D5360;      /* 6.9:1, 7.7:1 */

  /* Accents */
  --brand: #1F7A35;     /* fill only; 4.8:1 on ground, so large text at most */
  --on-brand: #F2F2E9;  /* 4.8:1 on --brand */
  --link: #1B6E31;      /* 5.6:1, 6.3:1 */
  --gold: #F8D030;      /* fill only; --ink on it is 12.6:1 */
  --gold-ink: #7A5F00;  /* 5.4:1, 6.1:1 */
  --on-gold: #0F1218;   /* 12.6:1 on --gold */
  --hot: #B3261E;       /* 5.8:1, 6.5:1, roast and over-limit warnings */
  --frame: #1F7A35;     /* title-screen frame, a fill; same in both themes */

  /* The title screen inside the frame is dark in both themes, like a lit
     cartridge screen. These four never change with the theme. */
  --screen: #0F1218;
  --on-screen: #F2F2E9;     /* 16.7:1 on --screen */
  --screen-mute: #A7ADB8;   /* 8.3:1 on --screen */
  --screen-brand: #3DDC6A;  /* 10.4:1 on --screen */

  /* Pixel art on the title screen and the scroll visuals: fills only, never
     text, and the same in both themes, like the screen itself. */
  --screen-surface: #1A1F28;
  --screen-line: #3A404C;
  --leather: #9A5530;
  --leather-dark: #5A3218;
  --skin: #C68B59;
  --hoop: #D2691E;

  /* Type (docs/brand.md, "Type", has the three rules and tests/type.test.mjs holds the code
     to them). display: Press Start 2P, headings of 20px and up only. ui and num: Space Mono
     for short text only (nav, buttons, tags, stat labels, row headers, step numbers, every
     number), 400 or 700, never under 13px; numbers are 700 with tabular-nums (its zero is
     already dotted). body: Barlow for anything longer than about one line. */
  --f-display: 'Press Start 2P', 'Courier New', ui-monospace, monospace;
  --f-ui: 'Space Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --f-num: 'Space Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --f-body: 'Barlow', system-ui, -apple-system, 'Segoe UI', 'Helvetica Neue', sans-serif;

  /* Pixel units: border and notch size, hash-rule tick, tall tick, gap between ticks */
  --px: 4px;
  --tick: 8px;
  --tick-tall: 24px;
  --tick-gap: 8px;
  --radius: 0;

  /* Layout */
  --gutter: clamp(16px, 3.2vw, 48px);
  --max: 1200px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    color-scheme: dark;

    --ground: #0F1218;
    --surface: #1A1F28;
    --line: #3A404C;

    --ink: #F2F2E9;       /* 16.7:1, 14.7:1 */
    --mute: #A7ADB8;      /* 8.3:1, 7.3:1 */

    --brand: #3DDC6A;     /* 10.4:1, 9.2:1, text or fill */
    --on-brand: #0F1218;  /* 10.4:1 on --brand */
    --link: #3DDC6A;      /* 10.4:1, 9.2:1 */
    --gold: #F8D030;      /* 12.6:1, 11.1:1, text or fill */
    --gold-ink: #F8D030;  /* 12.6:1, 11.1:1 */
    --on-gold: #0F1218;   /* 12.6:1 on --gold */
    --hot: #FF5C5C;       /* 6.2:1, 5.5:1 */
    --frame: #1F7A35;
    --screen: #0F1218;
    --on-screen: #F2F2E9;
    --screen-mute: #A7ADB8;
    --screen-brand: #3DDC6A;
    --screen-surface: #1A1F28;
    --screen-line: #3A404C;
    --leather: #9A5530;
    --leather-dark: #5A3218;
    --skin: #C68B59;
    --hoop: #D2691E;
  }
}

:root[data-theme='dark'] {
  color-scheme: dark;

  --ground: #0F1218;
  --surface: #1A1F28;
  --line: #3A404C;

  --ink: #F2F2E9;       /* 16.7:1, 14.7:1 */
  --mute: #A7ADB8;      /* 8.3:1, 7.3:1 */

  --brand: #3DDC6A;     /* 10.4:1, 9.2:1, text or fill */
  --on-brand: #0F1218;  /* 10.4:1 on --brand */
  --link: #3DDC6A;      /* 10.4:1, 9.2:1 */
  --gold: #F8D030;      /* 12.6:1, 11.1:1, text or fill */
  --gold-ink: #F8D030;  /* 12.6:1, 11.1:1 */
  --on-gold: #0F1218;   /* 12.6:1 on --gold */
  --hot: #FF5C5C;       /* 6.2:1, 5.5:1 */
  --frame: #1F7A35;
  --screen: #0F1218;
  --on-screen: #F2F2E9;
  --screen-mute: #A7ADB8;
  --screen-brand: #3DDC6A;
  --screen-surface: #1A1F28;
  --screen-line: #3A404C;
  --leather: #9A5530;
  --leather-dark: #5A3218;
  --skin: #C68B59;
  --hoop: #D2691E;
}

/* ---------------------------------------------------------------------------
 * Base
 * ------------------------------------------------------------------------- */

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--f-body);
  line-height: 1.5;
  overflow-wrap: break-word;
}

img,
svg {
  max-width: 100%;
}

img {
  height: auto;
}

/* Press Start 2P for headings of 20px and up only; smaller headings use the UI face. */
h1,
h2 {
  font-family: var(--f-display);
  font-weight: 400;
  line-height: 1.5;
}

h1 {
  font-size: clamp(1.25rem, 1rem + 2vw, 2rem);
}

h2 {
  font-size: clamp(1.25rem, 1.1rem + 0.8vw, 1.375rem);
}

h3,
h4 {
  font-family: var(--f-ui);
  font-weight: 400;
  line-height: 1.3;
}

a {
  color: var(--link);
  text-underline-offset: 0.2em;
}

:focus-visible {
  outline: var(--px) solid var(--link);
  outline-offset: calc(var(--px) * 2);
}

/* The skip link's target: focus lands here without drawing a ring round the page. */
#main:focus {
  outline: none;
}

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

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: var(--px);
  left: var(--gutter);
  z-index: 10;
  padding: calc(var(--px) * 2) calc(var(--px) * 4);
  background: var(--gold);
  color: var(--on-gold);
  font-family: var(--f-ui);
}

.skip-link:not(:focus) {
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Controls that need JavaScript; the theme script in <head> adds .js before paint. */
html:not(.js) .js-only {
  display: none;
}

/* Theme-specific artwork, e.g. the two lockups. Mirrors the token blocks above. */
.on-dark {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .on-dark {
    display: revert;
  }

  :root:not([data-theme='light']) .on-light {
    display: none;
  }
}

:root[data-theme='dark'] .on-dark {
  display: revert;
}

:root[data-theme='dark'] .on-light {
  display: none;
}

/* Nothing moves for people who ask for less motion. Nothing blinks for anyone. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------------------
 * Notched pixel frame: square corners notched by --px, drawn with four
 * box-shadows (no images, no border-radius). Leaves --px of margin so the
 * shadows are never clipped. Set --frame-color to recolour it.
 * ------------------------------------------------------------------------- */

.pixel-frame,
.btn,
.nav__toggle,
.dialogue {
  --frame-color: var(--ink);
  margin: var(--px);
  border: 0;
  border-radius: var(--radius);
  box-shadow:
    0 calc(-1 * var(--px)) 0 0 var(--frame-color),
    0 var(--px) 0 0 var(--frame-color),
    calc(-1 * var(--px)) 0 0 0 var(--frame-color),
    var(--px) 0 0 0 var(--frame-color);
}

.pixel-frame {
  background: var(--surface);
  padding: calc(var(--px) * 4);
}

/* The title-screen frame: the same green in both themes. */
.pixel-frame--field {
  --frame-color: var(--frame);
}

/* ---------------------------------------------------------------------------
 * Buttons and tags
 * ------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--px) * 2);
  padding: calc(var(--px) * 2) calc(var(--px) * 4);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--f-ui);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.25;
  text-decoration: none;
  cursor: pointer;
}

.btn:hover {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* Pressed: the button drops one pixel. A state, not an animation. */
.btn:active {
  transform: translateY(var(--px));
}

.btn--primary {
  --frame-color: var(--brand);
  background: var(--brand);
  color: var(--on-brand);
}

.tag {
  display: inline-block;
  padding: 0 var(--px);
  background: var(--ink);
  color: var(--ground);
  font-family: var(--f-ui);
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0.05em;
  text-decoration: none;
  text-transform: uppercase;
  vertical-align: middle;
}

.tag--gold {
  background: var(--gold);
  color: var(--on-gold);
}

.tag--brand {
  background: var(--brand);
  color: var(--on-brand);
}

.tag--hot {
  background: var(--hot);
  color: var(--ground);
}

/* ---------------------------------------------------------------------------
 * Locked: a page that is switched off (content/flags.json). Its links render
 * as plain text in the muted colour with an outlined LOCKED tag, written by
 * scripts/chrome.mjs. Not a link, so it takes no pointer and no menu cursor.
 *   <span class="btn btn--locked">The tool · coming soon
 *     <span class="tag tag--locked">LOCKED</span></span>
 * ------------------------------------------------------------------------- */

.tag--locked {
  background: transparent;
  color: var(--mute);
  box-shadow: inset 0 0 0 calc(var(--px) / 2) var(--mute);
}

.btn--locked {
  --frame-color: var(--line);
  background: transparent;
  color: var(--mute);
  cursor: default;
}

.btn--locked:hover {
  text-decoration: none;
}

.btn--locked:active {
  transform: none;
}

/* ---------------------------------------------------------------------------
 * Stepped block: the lockup's PRESSBOX block for short display headings.
 *   <span class="stepped">
 *     <svg class="stepped__stair" viewBox="0 0 2 3" preserveAspectRatio="none"
 *          aria-hidden="true" focusable="false"><path d="M2 1H1V2H0V3H2Z"/></svg>
 *     <span class="stepped__text">Game over</span>
 *     <svg class="stepped__stair" viewBox="0 0 2 3" preserveAspectRatio="none"
 *          aria-hidden="true" focusable="false"><path d="M0 0H2V1H1V2H0Z"/></svg>
 *   </span>
 * The stairs step out at the bottom left and the top right, as in the lockup.
 * ------------------------------------------------------------------------- */

.stepped {
  display: inline-flex;
  align-items: stretch;
  max-width: 100%;
  color: var(--brand);
}

.stepped__stair {
  flex: none;
  width: calc(var(--tick) * 2);
  height: auto;
  fill: currentColor;
}

.stepped__text {
  min-width: 0;
  padding: calc(var(--px) * 2) calc(var(--px) * 2);
  background: var(--brand);
  color: var(--on-brand);
  font-family: var(--f-display);
  font-weight: 400;
  line-height: 1.5;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------------------
 * Dialogue box: a notched frame holding a post, so a draft reads like
 * in-game dialogue. Text in Barlow.
 * ------------------------------------------------------------------------- */

.dialogue {
  padding: calc(var(--px) * 4);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--f-body);
  /* Scores are body text, so they stay in Barlow, but in tabular figures. */
  font-variant-numeric: tabular-nums;
}

.dialogue__speaker {
  margin: 0 0 calc(var(--px) * 2);
  color: var(--gold-ink);
  font-family: var(--f-ui);
  font-size: 0.875rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.dialogue > :last-child {
  margin-bottom: 0;
}

/* ---------------------------------------------------------------------------
 * Menus: a ▶ cursor marks the current item, and follows hover and focus.
 *   <ul class="menu"><li><a class="menu__item" aria-current="true">…</a></li></ul>
 * In a menu of links where nothing is current (the title screen's), mark the
 * default choice with .menu__item--start instead: it holds the cursor until
 * the pointer or focus is on another item. No ARIA state is claimed for it.
 * ------------------------------------------------------------------------- */

.menu {
  display: grid;
  gap: var(--px);
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--f-ui);
}

.menu__item {
  position: relative;
  display: block;
  width: 100%;
  padding: var(--px) 0 var(--px) 1.5em;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu__item[aria-current],
.menu__item[aria-pressed='true'],
.menu:not(:hover, :focus-within) .menu__item--start,
.menu__item:hover,
.menu__item:focus-visible {
  color: var(--gold-ink);
}

/* A locked item (see Locked, above) is text, not a choice: no cursor, no highlight. */
.menu .menu__item--locked {
  color: var(--mute);
  cursor: default;
}

/* ---------------------------------------------------------------------------
 * Stat strip: a row of numbers, each with its label. A missing number is
 * shown as unavailable (.stat__value--unavailable), never guessed.
 *   <dl class="stat-strip"><div class="stat"><dt class="stat__label">…</dt>
 *   <dd class="stat__value">…</dd></div></dl>
 * ------------------------------------------------------------------------- */

.stat-strip {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--px) * 4) calc(var(--px) * 8);
  margin: 0;
  padding-block: calc(var(--px) * 4);
  border-block: var(--px) solid var(--line);
}

.stat {
  min-width: 0;
}

.stat__label {
  color: var(--mute);
  font-family: var(--f-ui);
  font-size: 0.875rem;
}

.stat__value {
  margin: 0;
  color: var(--ink);
  font-family: var(--f-num);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
}

/* "Not available" is words, not a figure, so it goes back to the UI face. */
.stat__value--unavailable {
  color: var(--mute);
  font-family: var(--f-ui);
  font-size: 1rem;
  font-weight: 400;
}

/* Small print under a figure: where it came from and the date it was read. The words
   are UI type; the version link (its first link) and the date are numbers. */
.stat__note {
  display: block;
  color: var(--mute);
  font-family: var(--f-ui);
  font-size: 0.875rem;
  font-weight: 400;
}

/* Every number is Space Mono 700 in tabular figures. Dates are <time> elements, so one
   rule covers the stat strip, the patch notes and the studio. */
time,
.stat__note a:first-child,
.num {
  font-family: var(--f-num);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
 * Site header and nav
 * ------------------------------------------------------------------------- */

.site-header {
  border-bottom: var(--px) solid var(--line);
  background: var(--ground);
}

.nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--px) * 3) calc(var(--px) * 6);
  padding-block: calc(var(--px) * 3);
}

.nav__brand {
  display: inline-flex;
  margin-right: auto;
  padding-block: var(--px);
}

/* No display here: .on-light / .on-dark decide which lockup shows, and the
   flex container already makes the visible one a block. */
.nav__brand img {
  width: auto;
  height: calc(var(--px) * 6);
}

.nav__menu {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--px) * 3) calc(var(--px) * 6);
}

.nav__links {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--px) * 2) calc(var(--px) * 6);
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--f-ui);
}

.nav__links a {
  position: relative;
  display: inline-block;
  padding: var(--px) 0 var(--px) 1.25em;
  color: var(--ink);
  text-decoration: none;
}

.nav__links a:hover {
  text-decoration: underline;
}

.nav__links a[aria-current='page'] {
  color: var(--gold-ink);
}

.nav__toggle {
  padding: calc(var(--px) * 2) calc(var(--px) * 3);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--f-ui);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}

.theme-toggle {
  display: inline-flex;
  gap: var(--px);
}

.theme-toggle button {
  padding: calc(var(--px) * 2) calc(var(--px) * 3);
  border: calc(var(--px) / 2) solid var(--line);
  border-radius: var(--radius);
  background: transparent;
  color: var(--mute);
  font-family: var(--f-ui);
  font-size: 0.875rem;
  line-height: 1;
  cursor: pointer;
}

.theme-toggle button[aria-pressed='true'] {
  border-color: var(--gold);
  background: var(--gold);
  color: var(--on-gold);
}

/* The menu cursor. The text after the slash is the (empty) spoken alternative;
   browsers without that syntax use the first declaration. */
.menu__item[aria-current]::before,
.menu__item[aria-pressed='true']::before,
.menu:not(:hover, :focus-within) .menu__item--start::before,
.menu__item:hover::before,
.menu__item:focus-visible::before,
.nav__links a[aria-current='page']::before {
  content: '▶';
  content: '▶' / '';
  position: absolute;
  left: 0;
  font-size: 0.75em;
  line-height: 2;
}

.menu .menu__item--locked:hover::before {
  content: none;
}

/* Narrow screens: the menu folds under a toggle, but only when JavaScript can
   open it. Without JavaScript the links stay listed under the lockup. */
@media (max-width: 47.99em) {
  .nav__menu {
    flex-basis: 100%;
    flex-direction: column;
    align-items: flex-start;
  }

  .nav__links {
    flex-direction: column;
  }

  .js .nav:not([data-open]) .nav__menu {
    display: none;
  }
}

@media (min-width: 48em) {
  .nav__toggle {
    display: none;
  }

  .nav__brand img {
    height: calc(var(--px) * 7);
  }
}

/* ---------------------------------------------------------------------------
 * Site footer, with the pixel hash strip along its top edge
 * ------------------------------------------------------------------------- */

.site-footer {
  position: relative;
  margin-top: calc(var(--px) * 16);
  padding-top: calc(var(--tick-tall) + var(--px) * 4);
  padding-bottom: calc(var(--px) * 8);
  color: var(--mute);
  font-family: var(--f-ui);
  font-size: 0.875rem;
}

.site-footer::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  height: var(--tick-tall);
  background: url('/assets/brand/hash-rule.svg') repeat-x left top / auto 100%;
  image-rendering: pixelated;
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: calc(var(--px) * 2) calc(var(--px) * 6);
}

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--px) calc(var(--px) * 2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-footer__links li + li::before {
  content: '·';
  content: '·' / '';
  margin-right: calc(var(--px) * 2);
}

.site-footer__links a {
  color: var(--ink);
}

.site-footer__note {
  margin: 0;
}

/* ---------------------------------------------------------------------------
 * Scroll visuals (assets/js/site.js). Decoration only, aria-hidden, and the
 * page is complete without them.
 * ------------------------------------------------------------------------- */

/* Section kickoff: a down marker, with its chain pole, beside each section
   heading. It sits before the <h2>, never inside it, so the heading's text
   stays exactly the heading. */
.down-marker + h2 {
  display: inline-block;
}

.down-marker {
  display: inline-block;
  margin-inline: calc(var(--px) * 2) calc(var(--px) * 3);
  padding: var(--px) calc(var(--px) * 2);
  background: var(--gold);
  color: var(--on-gold);
  box-shadow: calc(var(--px) * -2) 0 0 0 var(--ink);
  font-family: var(--f-ui);
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1.5;
  vertical-align: middle;
  white-space: nowrap;
}

.kickoff__text {
  display: inline-block;
}

@keyframes kickoff-type {
  to { clip-path: inset(0 0 0 0); }
}

@keyframes kickoff-marker {
  to { opacity: 1; transform: none; }
}

/* Sections below the fold wait to kick off (site.js adds .kickoff to the
   section only when motion is welcome, and .is-kicked once it is in view). */
@media (prefers-reduced-motion: no-preference) {
  .kickoff .kickoff__text {
    clip-path: inset(0 100% 0 0);
  }

  .kickoff .down-marker {
    opacity: 0;
    transform: translateX(calc(var(--px) * -8));
  }

  .kickoff.is-kicked .kickoff__text {
    animation: kickoff-type 0.9s steps(14) forwards;
  }

  .kickoff.is-kicked .down-marker {
    animation: kickoff-marker 0.5s steps(4) forwards;
  }
}

/* The yard-line rail: a strip of field at the right edge, from 10 to 10, with
   the ball at the reader's place on the page. Wide screens only, where it sits
   in the margin beside the content; site.js shows it once the page scrolls. */
.yard-rail {
  --progress: 0;
  display: none;
}

@media (min-width: 80em) {
  .yard-rail[data-active] {
    position: fixed;
    z-index: 5;
    top: calc(var(--px) * 24);
    right: 0;
    bottom: calc(var(--px) * 6);
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: calc(var(--px) * 8);
    margin: 0;
    padding: calc(var(--px) * 2) 0;
    background: var(--frame);
    list-style: none;
    pointer-events: none;
  }
}

.yard-rail__line {
  padding-top: var(--px);
  border-top: calc(var(--px) / 2) solid var(--screen-brand);
  color: var(--on-screen);
  font-family: var(--f-num);
  font-size: 0.8125rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  text-align: center;
}

.yard-rail__ball {
  position: absolute;
  top: calc((100% - var(--px) * 4) * var(--progress, 0));
  left: calc(var(--px) / 2);
  width: calc(var(--px) * 7);
  height: calc(var(--px) * 4);
  image-rendering: pixelated;
}
