/* ==========================================================================
   CartaTM Wedding Cup
   Dark, neon-purple, motorsport-flavoured. Hand-written CSS, no framework.
   ========================================================================== */

/* --- Fonts ---------------------------------------------------------------- */

/* Rock Salt is used only for the event wordmark. */
@font-face {
  font-family: 'Rock Salt';
  src: url('/fonts/rock-salt-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Chakra Petch carries every heading and all the numerics. Latin and Latin
   Extended are split so browsers only download the range a page actually uses. */
@font-face {
  font-family: 'Chakra Petch';
  src: url('/fonts/chakra-petch-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Chakra Petch';
  src: url('/fonts/chakra-petch-600-latin-ext.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Chakra Petch';
  src: url('/fonts/chakra-petch-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Chakra Petch';
  src: url('/fonts/chakra-petch-700-latin-ext.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Azeret Mono carries every number: times, points, ranks and countdown digits.
   Chakra Petch has no tabular figures - its "1" is roughly 40% narrower than its
   "0" - so columns of numbers would not line up and the countdown would twitch on
   every tick. Azeret was picked over the usual code monos (JetBrains, Roboto)
   because its zero is plain rather than slashed, which suits a scoreboard. */
@font-face {
  font-family: 'Azeret Mono';
  src: url('/fonts/azeret-mono-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Azeret Mono';
  src: url('/fonts/azeret-mono-500-latin-ext.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Azeret Mono';
  src: url('/fonts/azeret-mono-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Azeret Mono';
  src: url('/fonts/azeret-mono-700-latin-ext.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- Tokens --------------------------------------------------------------- */

:root {
  --accent: #680ef9;
  --accent-bright: #8b4dff;
  --accent-soft: #a87bff;
  --accent-ink: #d7c4ff;
  --accent-glow: rgba(104, 14, 249, 0.55);

  /*
   * Every one of these sits a step above where it started: the theme is still
   * near-black, but the old values were dark enough that the surfaces barely
   * separated from the page. The ladder bg -> chrome -> surface -> surface-2 ->
   * surface-3 is what gives the page its depth, so they move together.
   *
   * NOTE: .hero__grade hard-codes --bg as rgba() literals, because a gradient
   * stop cannot take an alpha of a var(). If --bg changes, change those too or
   * the hero stops meeting the page cleanly.
   */
  --bg: #0a0913;
  --bg-2: #0e0b1a;
  /* Header and footer. A step above --bg, roughly two thirds of the way to
     --surface, so the chrome reads as a frame around the page rather than more
     page - but not so far that it competes with the panels sitting on it. */
  --chrome: #110e1f;
  --surface: #151027;
  --surface-2: #1c1434;
  --surface-3: #241a41;

  /* Edges do a lot of the lifting on a dark theme - a border you can actually
     see reads as depth, where one you cannot reads as flat. */
  --line: rgba(255, 255, 255, 0.11);
  --line-strong: rgba(168, 123, 255, 0.36);

  --text: #edeaf6;
  --muted: #a49dbd;
  /* Both muted tones were lifted alongside the surfaces, by more than the
     surfaces moved, so contrast went up rather than down: --muted-dim is now
     4.65:1 at worst (on --surface-3), where it used to fall to 4.25:1 and miss
     WCAG AA for the small text it carries. */
  --muted-dim: #8b86a3;

  --gold: #ffce4a;
  --silver: #cdd5e5;
  --bronze: #e08a46;
  --up: #3ed598;
  --down: #ff5c7a;

  --font-display: 'Chakra Petch', 'Segoe UI', system-ui, sans-serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
    Arial, sans-serif;
  --font-wordmark: 'Rock Salt', 'Brush Script MT', cursive;
  /* Every number on the site. The fallbacks are all monospaced too, so digits
     stay aligned even before the webfont lands. */
  --font-mono: 'Azeret Mono', ui-monospace, 'SF Mono', 'Cascadia Mono',
    'Segoe UI Mono', Consolas, 'Liberation Mono', monospace;

  --radius: 14px;
  --radius-sm: 9px;
  --container: 1240px;
  --header-h: 68px;

  --shadow-lift: 0 18px 40px -22px rgba(0, 0, 0, 0.95);
  --shadow-glow: 0 0 32px -8px var(--accent-glow);
}

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

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  /* `clip`, not `hidden`: `overflow-x: hidden` turns <body> into a scroll
     container, which breaks the sticky header and the sticky table head. */
  overflow-x: clip;
}

/* A faint purple haze so the near-black never reads as flat.
   Painted on a fixed layer rather than with background-attachment: fixed, which
   forces the whole page to repaint on every scroll frame. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(80rem 40rem at 50% -10rem, rgba(104, 14, 249, 0.2), transparent 65%),
    radial-gradient(50rem 30rem at 100% 40%, rgba(104, 14, 249, 0.1), transparent 70%);
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.15;
  margin: 0;
  letter-spacing: -0.01em;
}

p {
  margin: 0 0 1em;
}

a {
  color: var(--accent-ink);
  text-decoration-color: rgba(168, 123, 255, 0.4);
  text-underline-offset: 3px;
}

a:hover {
  color: #fff;
}

img {
  max-width: 100%;
  display: block;
}

button {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 3px;
  border-radius: 4px;
}

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(16px, 4vw, 32px);
}

.stack > * + * {
  margin-top: clamp(28px, 4vw, 48px);
}

/*
 * The homepage's two-column band. Equal columns, and minmax(0, 1fr) rather than
 * a bare 1fr so a long word or a wide card cannot push a column past its share.
 */
.home-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 3.5vw, 40px);
  align-items: start;
}

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

.skip-link {
  position: absolute;
  left: 12px;
  top: -60px;
  z-index: 100;
  background: var(--accent);
  color: #fff;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  transition: top 0.15s ease;
}

.skip-link:focus {
  top: 12px;
}

.accent {
  color: var(--accent-soft);
}

.eyebrow {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 0.75rem;
  color: var(--accent-soft);
  margin-bottom: 0.6em;
}

.section-title {
  font-size: clamp(1.2rem, 2.4vw, 1.55rem);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.9rem;
  display: flex;
  align-items: center;
  gap: 14px;
}

.section-title::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--line-strong), transparent);
}

/* --- Buttons -------------------------------------------------------------- */

.btn {
  --btn-bg: var(--accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 11px 20px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--btn-bg);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.02em;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease,
    border-color 0.2s ease;
}

.btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: var(--shadow-glow);
  color: #fff;
}

.btn:active:not(:disabled) {
  transform: translateY(0);
}

.btn:disabled {
  opacity: 0.55;
  cursor: default;
}

.btn--discord {
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-bright) 100%);
  box-shadow: 0 0 0 1px rgba(168, 123, 255, 0.35) inset;
}

/* The header CTA is a secondary action next to the nav, not the hero of the
   page - keep it compact. Below 560px it drops its label entirely: the nav plus
   a full-width button does not fit, and the nav links were wrapping to two
   lines to make room. */
@media (max-width: 560px) {
  .site-header .btn--discord {
    padding: 8px;
    aspect-ratio: 1;
  }

  .site-header .btn--discord [data-discord-label] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .site-header .btn--discord .icon {
    width: 18px;
    height: 18px;
  }
}

.site-header .btn--discord {
  padding: 7px 13px;
  font-size: 0.8rem;
  border-radius: 8px;
}

.site-header .btn--discord .icon {
  width: 15px;
  height: 15px;
}

.btn--ghost {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--line);
  color: var(--text);
}

.btn--ghost:hover {
  border-color: var(--line-strong);
  background: rgba(104, 14, 249, 0.14);
}

.btn--sm {
  padding: 7px 14px;
  font-size: 0.82rem;
}

.btn--block {
  width: 100%;
  margin-top: 12px;
}

.btn .icon {
  width: 18px;
  height: 18px;
  fill: currentColor;
  flex: none;
}

.btn.is-busy {
  pointer-events: none;
  opacity: 0.75;
}

.linklike {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--accent-ink);
  font: inherit;
}

.linklike:hover {
  color: #fff;
}

/* --- Header --------------------------------------------------------------- */

/* Scrolls away with the page rather than following it down. Nothing passes
   underneath it any more, so the translucent background and its blur are gone
   too - they only ever existed to soften what was sliding past. */
.site-header {
  position: relative;
  z-index: 50;
  height: var(--header-h);
  background: var(--chrome);
  border-bottom: 1px solid var(--line);
}

.site-header__inner {
  height: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(16px, 4vw, 32px);
  display: flex;
  align-items: center;
  gap: clamp(16px, 4vw, 40px);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.28rem;
  letter-spacing: -0.01em;
}

/* The logo is generated as a transparent cutout by scripts/build-assets.mjs, so
   it needs no blend-mode trickery to sit on the dark page. */
.brand__mark,
.hero__logo,
.site-footer__brand img {
  border-radius: 50%;
}

.brand__tm {
  color: var(--accent-soft);
}

.site-nav {
  display: flex;
  gap: clamp(14px, 3vw, 28px);
}

/* Holds the right edge on its own, so the header still lays out correctly with
   no nav at all - which is how it currently renders (see NAV_LINKS in
   views/layout.tsx). */
.site-header__cta {
  margin-left: auto;
}

.site-nav a {
  position: relative;
  color: var(--muted);
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.92rem;
  padding: 6px 0;
  white-space: nowrap;
  transition: color 0.18s ease;
}

.site-nav a:hover {
  color: var(--text);
}

.site-nav a.is-active {
  color: var(--text);
}

.site-nav a.is-active::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  background: var(--accent);
  box-shadow: 0 0 12px var(--accent-glow);
}

/* --- Hero ----------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: min(78vh, 720px);
  display: grid;
  place-items: center;
  padding: clamp(20px, 4vw, 44px) 0 clamp(56px, 8vw, 88px);
  overflow: hidden;
  isolation: isolate;
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 50%;
  /* Lifted well past a gentle nudge so the scene actually reads as a place
     rather than a dark texture. Contrast is pushed with it: brightness alone at
     this strength washes the blacks out, and the purple screen layer above is
     already raising them. Everything that sits over this carries its own shade
     - .hero__grade, and the text shadows on the brand, lede and strip - so the
     copy stays legible against the brighter parts of the image. */
  filter: brightness(1.55) contrast(1.12) saturate(1.12);
}

/*
 * Much lighter than a daytime photo would need: the scene is already nearly
 * black, so this only has to settle the headline and hand the bottom edge over
 * to the page.
 *
 * The bottom of the source measures RGB 0,7,11 against a page background of
 * 7,6,13, so the fade can start late and stay short - the last stop is literally
 * var(--bg) at 100%, which makes the join to the page exact by construction.
 */
.hero__grade {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(48% 46% at 50% 46%, rgba(10, 9, 19, 0.38), transparent 76%),
    linear-gradient(180deg, rgba(10, 9, 19, 0.5) 0%, rgba(10, 9, 19, 0) 30%, rgba(10, 9, 19, 0) 68%, rgba(10, 9, 19, 0.55) 88%, var(--bg) 100%);
}

/* Pushes the scene toward the brand purple without darkening it.
   The source leans teal (mean green 16, blue 23 against red 3), so this carries
   more of the brand purple than it did over the old daytime shot. */
.hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(78% 62% at 50% 42%, rgba(139, 77, 255, 0.2), transparent 74%);
  mix-blend-mode: screen;
}

.hero__grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(168, 123, 255, 0.08) 1px, transparent 1px),
    linear-gradient(90deg, rgba(168, 123, 255, 0.08) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(70% 60% at 50% 45%, #000 0%, transparent 75%);
  -webkit-mask-image: radial-gradient(70% 60% at 50% 45%, #000 0%, transparent 75%);
  opacity: 0.55;
}

.hero__content {
  position: relative;
  text-align: center;
  padding-inline: clamp(16px, 4vw, 32px);
  max-width: 900px;
}

/*
 * A pool of shade behind the hero copy.
 *
 * The scene has genuinely bright patches - the car's lights, the signage - and
 * white text crossing one of them stops being readable however heavy its text
 * shadow is. Darkening the photo itself would undo the whole point of lifting
 * its brightness, so this only shades where the words actually are.
 *
 * A pseudo-element rather than a background on .hero__content, because the
 * shade has to spill past the text to fade out, and that box hugs the text
 * exactly. Negative inset percentages resolve against .hero__content's own box,
 * so the spill stays proportional as the hero grows and shrinks, and being
 * absolutely positioned it adds nothing to the layout.
 *
 * z-index -1 sits above .hero__media (-2) and below the copy, which is in
 * normal flow. .hero's isolation: isolate is what keeps both out of the page's
 * stacking context.
 */
.hero__content::before {
  content: '';
  position: absolute;
  inset: -14% -10%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    ellipse at 50% 50%,
    rgba(7, 6, 13, 0.72) 0%,
    rgba(7, 6, 13, 0.55) 45%,
    rgba(7, 6, 13, 0.24) 70%,
    transparent 86%
  );
}

.hero__brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.9rem, 5.5vw, 3.2rem);
  letter-spacing: -0.02em;
  margin-bottom: 0.1em;
  text-shadow: 0 4px 24px rgba(0, 0, 0, 0.85), 0 2px 8px rgba(7, 6, 13, 0.7);
}

.hero__logo {
  width: clamp(48px, 8vw, 72px);
  height: auto;
}

.hero__title {
  position: relative;
  margin: 0.1em 0 0.55em;
}

.hero__crown {
  display: block;
  width: clamp(34px, 5vw, 48px);
  /* No bottom margin: the wordmark's own negative top margin sets the spacing,
     so the two stay in step as the wordmark scales. */
  margin: 0 auto;
  fill: var(--accent-soft);
  filter: drop-shadow(0 0 14px var(--accent-glow));
}

/* Rock Salt is a light handwritten face, so it gets tightened up and given a
   layered glow to hold its own at display size. */
/* Rock Salt has unusually tall ascenders and deep descenders, and the skew
   throws glyphs past the edges of the box. Because the gradient is clipped to
   the text, any glyph outside that box gets cut off - hence the generous
   line-height and padding rather than the usual tight display sizing. */
.wordmark {
  display: block;
  font-family: var(--font-wordmark);
  font-weight: 400;
  font-size: clamp(1.85rem, 6.8vw, 4.6rem);
  line-height: 2.25;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  /* Applied right to left: skew first for the italic lean, then tilt the whole
     lockup. */
  transform: rotate(-3deg) skewX(-6deg);
  background: linear-gradient(180deg, #ffffff 0%, #e6d9ff 45%, var(--accent-soft) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter:
    drop-shadow(0 0 18px rgba(139, 77, 255, 0.55))
    drop-shadow(0 4px 18px rgba(0, 0, 0, 0.7));
  padding: 0.02em 0.16em;
  /*
   * The tall line box exists for the glyphs, not for spacing: measured at this
   * face, roughly 0.38em above the line box is dead air before the ink starts.
   * The top margin claws most of that back so the crown tucks in close. The
   * bottom claws back nothing at all, on purpose: the -3deg tilt drops the left
   * end of the wordmark by around 18px at full size, and that swallows the
   * clearance the old negative bottom margin used to be able to afford.
   * None of this touches the padding box the text-clipped gradient depends on.
   */
  margin: -0.28em -0.16em 0;
}

.hero__strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 2.5vw, 20px);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(0.78rem, 1.9vw, 0.98rem);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: #cfc7e4;
  margin-bottom: clamp(28px, 4vw, 44px);
  text-shadow: 0 2px 10px rgba(7, 6, 13, 0.9), 0 0 22px rgba(7, 6, 13, 0.7);
  /*
   * A soft pool of shade so the line holds up over the brighter parts of the
   * scene. Painted as the element's own background rather than a positioned
   * pseudo-element: the background box sits behind the text automatically, with
   * no stacking-order games, and vertical-only padding means it cannot push the
   * items into wrapping on a narrow screen.
   */
  padding-block: 0.7em;
  /* Lighter than it was: .hero__content::before now lays a scrim under the
     whole lockup, and at the old strength the two stacked into a dark band. */
  background: radial-gradient(
    ellipse 55% 140% at 50% 50%,
    rgba(7, 6, 13, 0.38) 0%,
    rgba(7, 6, 13, 0.2) 52%,
    transparent 78%
  );
}

.hero__strip strong {
  color: #fff;
  font-weight: 700;
}

.hero__strip-sep {
  color: var(--accent);
  opacity: 0.7;
}

/*
 * The homepage hero.
 *
 * Same scene and the same treatment, but shorter and with the brand lockup
 * promoted to the h1: the landing page has nothing to count down to, so the
 * hero only has to introduce Carta and then get out of the way of the page.
 */
.hero--home {
  min-height: min(46vh, 420px);
  padding-bottom: clamp(32px, 5vw, 52px);
}

.hero__brand--title {
  margin: 0 0 0.2em;
  font-size: clamp(2.1rem, 6.2vw, 3.6rem);
}

.hero--home .hero__logo {
  width: clamp(56px, 10vw, 96px);
}

/* Wide enough to hold the tagline on one line at desktop, and letter-spaced
   less than the cup page's strip because it is carrying a longer string. */
.hero__lede {
  max-width: 56ch;
  margin-inline: auto;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(0.85rem, 2.1vw, 1.05rem);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: #e4defa;
  text-shadow: 0 2px 10px rgba(7, 6, 13, 0.95), 0 0 24px rgba(7, 6, 13, 0.85);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: clamp(20px, 3vw, 30px);
}

/* --- Countdown ------------------------------------------------------------ */

.countdown {
  position: relative;
  display: inline-block;
  padding: clamp(18px, 3vw, 26px) clamp(20px, 4vw, 40px) clamp(16px, 2.5vw, 22px);
  background: linear-gradient(180deg, rgba(23, 16, 41, 0.92), rgba(11, 8, 19, 0.94));
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift), inset 0 1px 0 rgba(255, 255, 255, 0.05);
  overflow: hidden;
  /* Motorsport-livery corner cuts. Shared with the ::after outline below, so
     the two shapes can never drift apart. */
  --cut: 18px;
  /* Where the outline's inner edge meets the top and left sides. A 45 degree
     edge pushed 1px along its own normal travels sqrt(2)px on each axis, so the
     inner corner starts (sqrt(2) - 1)px further out than the outer one - that is
     what keeps the diagonal exactly as thick as the straight sides. */
  --cut-inner: calc(var(--cut) + 0.4142px);
  clip-path: polygon(
    var(--cut) 0,
    100% 0,
    100% calc(100% - var(--cut)),
    calc(100% - var(--cut)) 100%,
    0 100%,
    0 var(--cut)
  );
}

/*
 * The border, carried around the cut corners.
 *
 * `border` alone only ever reaches the four straight sides: the box is painted
 * first and clip-path slices it afterwards, taking the border off with the
 * corners and leaving the two diagonals bare. This traces the same hexagon as a
 * 1px ring - the outer path, then the same path 1px in, with the even-odd fill
 * rule keeping only the band between them.
 *
 * The two clips on the parent then divide the work, which is worth knowing
 * before changing either:
 *
 *   - `overflow: hidden` clips to the PADDING box, so the ring's straight sides
 *     fall outside it and are trimmed. The real `border` draws those, and this
 *     ring only ever shows on the diagonals - which is all it is needed for.
 *   - that same clip is rounded by `border-radius`, which is what stops this
 *     polygon's square top-right and bottom-left corners poking out past the
 *     curve. Do not drop `overflow: hidden` on the strength of clip-path
 *     already containing the sweep: those corners reappear if you do.
 *
 * inset -1px lines the ring up with the border box rather than the padding box,
 * so its diagonals sit flush against the cut edge instead of 1px inside it.
 */
.countdown::after {
  content: '';
  position: absolute;
  inset: -1px;
  pointer-events: none;
  background: var(--line-strong);
  clip-path: polygon(
    evenodd,
    var(--cut) 0,
    100% 0,
    100% calc(100% - var(--cut)),
    calc(100% - var(--cut)) 100%,
    0 100%,
    0 var(--cut),
    var(--cut-inner) 1px,
    calc(100% - 1px) 1px,
    calc(100% - 1px) calc(100% - var(--cut-inner)),
    calc(100% - var(--cut-inner)) calc(100% - 1px),
    1px calc(100% - 1px),
    1px var(--cut-inner)
  );
}

/* Slow accent sweep behind the digits. */
.countdown::before {
  content: '';
  position: absolute;
  inset: -40% -10%;
  background: linear-gradient(115deg, transparent 42%, rgba(139, 77, 255, 0.16) 50%, transparent 58%);
  animation: sweep 7s linear infinite;
  pointer-events: none;
}

@keyframes sweep {
  from {
    transform: translateX(-60%);
  }
  to {
    transform: translateX(60%);
  }
}

/*
 * "Qualifications Started/Finished" - the hour-long banner over the clock.
 *
 * Deliberately the loudest thing in the box: it is the only element here that
 * is news, and it is gone an hour later. Sized in the display face against the
 * digits below it, which are 2.4rem at the same breakpoints.
 */
.countdown__announcement {
  position: relative;
  margin: 0 0 0.7em;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.35rem, 4.2vw, 2rem);
  line-height: 1.05;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 0 24px rgba(139, 77, 255, 0.55);
}

/* --- Twitch stream, in the countdown's place ------------------------------ */

.hero-stream {
  position: relative;
  width: min(880px, 100%);
  margin-inline: auto;
}

.hero-stream__label {
  margin: 0 0 0.7em;
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.24em;
  font-size: 0.72rem;
  color: var(--muted);
}

/* Fixed 16:9 so the box does not jump about while the player loads. */
.hero-stream__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  overflow: hidden;
  background: #0b0813;
  box-shadow: var(--shadow-lift);
}

.hero-stream__frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.hero-stream__fallback {
  margin: 10px 0 0;
  font-size: 0.8rem;
  color: var(--muted-dim);
}

.hero-stream__fallback a {
  color: var(--accent-soft);
}

/* --- Final podium --------------------------------------------------------- */

.podium {
  position: relative;
  width: min(620px, 100%);
  margin-inline: auto;
}

/*
 * The podium's heading, and the largest thing the hero ever shows. Deliberately
 * a step above the hour-long announcement banner: that one is news mid-event,
 * this one closes the tournament out, and it has the slot to itself.
 */
.podium__title {
  margin: 0 0 0.8em;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.7rem, 5.4vw, 2.6rem);
  line-height: 1.02;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 0 26px rgba(139, 77, 255, 0.55);
}

.podium__label {
  margin: 0 0 0.35em;
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.24em;
  font-size: 0.72rem;
  color: var(--muted);
}

.podium__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}

.podium__place {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  background: linear-gradient(180deg, rgba(23, 16, 41, 0.92), rgba(11, 8, 19, 0.94));
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--radius-sm);
}

.podium__place--1 {
  border-left-color: var(--gold);
}

.podium__place--2 {
  border-left-color: var(--silver);
}

.podium__place--3 {
  border-left-color: var(--bronze);
}

.podium__rank {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  min-width: 3ch;
}

.podium__place--1 .podium__rank {
  color: var(--gold);
}

.podium__place--2 .podium__rank {
  color: var(--silver);
}

.podium__place--3 .podium__rank {
  color: var(--bronze);
}

/*
 * The prize, pushed to the far end of the row. Mono so the amounts line up
 * down the column the way the leaderboard's numbers do, and in the medal colour
 * of the place it belongs to (set below) so it reads as part of that row.
 */
.podium__prize {
  margin-left: auto;
  padding-left: 12px;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: -0.02em;
  color: var(--muted);
  white-space: nowrap;
}

.podium__place--1 .podium__prize {
  color: var(--gold);
  font-size: 1.1rem;
}

.podium__place--2 .podium__prize {
  color: var(--silver);
}

.podium__place--3 .podium__prize {
  color: var(--bronze);
}

/* The winner is the headline; the other two sit a size down. */
.podium__name {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text);
  min-width: 0;
}

.podium__place--1 .podium__name {
  font-size: clamp(1.2rem, 3.2vw, 1.6rem);
  color: #fff;
}

.countdown__label {
  position: relative;
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.24em;
  font-size: 0.72rem;
  color: var(--muted);
  margin: 0 0 0.9em;
}

.countdown__units {
  position: relative;
  display: flex;
  gap: clamp(8px, 1.8vw, 14px);
  justify-content: center;
}

.countdown__unit {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-width: clamp(62px, 12vw, 86px);
}

.countdown__value {
  display: block;
  width: 100%;
  padding: clamp(8px, 1.6vw, 14px) 4px;
  background: linear-gradient(180deg, var(--surface-3), var(--surface));
  border: 1px solid var(--line);
  border-top: 2px solid var(--accent);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(1.55rem, 4.5vw, 2.45rem);
  line-height: 1;
  color: #fff;
  text-shadow: 0 0 20px rgba(139, 77, 255, 0.5);
  box-shadow: inset 0 -12px 24px -18px var(--accent), 0 6px 18px -12px #000;
}

/* Pulse on each tick, driven by countdown.js toggling the class. */
.countdown__value.is-tick {
  animation: tick 0.42s ease-out;
}

@keyframes tick {
  0% {
    transform: translateY(-2px);
    box-shadow: inset 0 -12px 24px -14px var(--accent-bright), 0 0 22px -4px var(--accent-glow);
  }
  100% {
    transform: translateY(0);
  }
}

.countdown__unit-label {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted-dim);
}

.countdown__date {
  position: relative;
  margin: 1.1em 0 0;
  font-size: 0.82rem;
  color: var(--muted);
}

/* --- Tabs ----------------------------------------------------------------- */

.tabs {
  margin-top: clamp(-28px, -3vw, -12px);
  position: relative;
  z-index: 2;
  padding-bottom: clamp(56px, 9vw, 100px);
}

.tabs__list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px;
  box-shadow: var(--shadow-lift);
}

.tabs__tab {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: clamp(12px, 2vw, 17px) 10px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(0.72rem, 1.7vw, 0.9rem);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  cursor: pointer;
  position: relative;
  transition: color 0.18s ease, background 0.18s ease;
}

.tabs__tab:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.03);
}

.tabs__tab.is-active {
  color: #fff;
  background: linear-gradient(180deg, rgba(104, 14, 249, 0.22), rgba(104, 14, 249, 0.06));
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

.tabs__tab.is-active::after {
  content: '';
  position: absolute;
  left: 18%;
  right: 18%;
  bottom: 4px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  box-shadow: 0 0 14px var(--accent-glow);
}

.tabs__icon svg {
  width: 19px;
  height: 19px;
  fill: currentColor;
  display: block;
}

.tabs__panel {
  margin-top: 22px;
}

/* Without JS every panel stays visible, so nothing is trapped behind a script. */
.tabs[data-enhanced] .tabs__panel {
  display: none;
}

.tabs[data-enhanced] .tabs__panel.is-active {
  display: block;
  animation: panel-in 0.3s ease both;
}

@keyframes panel-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* --- Panels --------------------------------------------------------------- */

.panel {
  background: linear-gradient(180deg, var(--surface) 0%, rgba(11, 8, 19, 0.92) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(20px, 3.5vw, 34px);
  box-shadow: var(--shadow-lift);
}

.panel__head {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 22px;
}

.panel__title {
  display: flex;
  align-items: center;
  gap: 13px;
  font-size: clamp(1.4rem, 3.4vw, 2rem);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.panel__icon {
  width: clamp(28px, 4vw, 36px);
  height: auto;
  fill: var(--accent-soft);
  filter: drop-shadow(0 0 12px var(--accent-glow));
  flex: none;
}

.panel__subtitle {
  color: var(--muted);
  margin: 0.5em 0 0;
  /* No measure cap: these are single-sentence intros and a cap was forcing them
     onto a second line well before the container ran out of room. The container
     width is the only limit they need. */
  max-width: none;
}


.notice {
  padding: 13px 16px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.9rem;
  margin-bottom: 18px;
}


.empty {
  padding: 40px 20px;
  text-align: center;
  color: var(--muted);
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
}

.empty--sm {
  padding: 18px;
  font-size: 0.88rem;
}

/* --- Leaderboard controls ------------------------------------------------- */

.lb-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.lb-search {
  position: relative;
  flex: 1 1 240px;
  max-width: 340px;
}

.lb-search svg {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 17px;
  height: 17px;
  fill: var(--muted-dim);
  pointer-events: none;
}

.lb-search input {
  width: 100%;
  padding: 10px 14px 10px 38px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: inherit;
  font-size: 0.92rem;
}

.lb-search input::placeholder {
  color: var(--muted-dim);
}

.lb-search input:focus {
  border-color: var(--line-strong);
  outline: none;
  box-shadow: 0 0 0 3px rgba(104, 14, 249, 0.18);
}

.segmented {
  display: inline-flex;
  padding: 4px;
  gap: 4px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.segmented button {
  padding: 7px 15px;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--muted);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.82rem;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}

.segmented button:hover {
  color: var(--text);
}

.segmented button.is-active {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 0 18px -6px var(--accent-glow);
}

/* Sits opposite the heading in .panel__head, so it reads as a status stamp on
   the box rather than a caption above the table. On narrow screens .panel__head
   turns into a column and this simply wraps underneath. */
.lb-status {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  /* This is a flex row of text runs, so the literal spaces in the markup are
     stripped at each item boundary and `gap` is what separates the words. Keep
     it at a word space (~3.6px for Inter at this size) or the line reads as if
     it has been double-spaced. The dot buys its own wider gap below.

     Row gap is set separately: when this wraps on a narrow screen the lines
     want more air between them than the words want between each other. */
  gap: 6px 4px;
  flex-wrap: wrap;
  font-size: 0.82rem;
  color: var(--muted-dim);
  margin: 0;
  /* Nudged down to sit level with the heading's cap height. */
  padding-top: 8px;
  text-align: right;
}

/* The status line reads as a single state - "Live", "Paused", "Final" - with
   "Updated 5 minutes ago / Next update in 3 minutes" behind a hover. The dotted
   underline and the help cursor are the whole affordance, so keep both. */
.lb-live {
  position: relative;
  border-bottom: 1px dotted currentColor;
  cursor: help;
}

/* Focusable so the detail is reachable without a mouse: tab to it on a
   keyboard, tap it on a touch screen, where :hover never fires. */
.lb-live:focus-visible {
  outline: 2px solid var(--up);
  outline-offset: 3px;
  border-radius: 2px;
}

.lb-live::after {
  content: attr(data-tip);
  position: absolute;
  /* Below rather than above: the line sits at the top of the panel, level with
     the heading, and there is nothing but border up there to open into. */
  top: calc(100% + 9px);
  right: 0;
  z-index: 5;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  box-shadow: var(--shadow-lift);
  color: var(--muted);
  font-size: 0.78rem;
  white-space: nowrap;
  /* visibility, not just opacity, so the hidden tooltip cannot be hovered or
     selected. It snaps while the opacity fades, which is what we want. */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.12s ease;
}

.lb-live:hover::after,
.lb-live:focus-visible::after {
  opacity: 1;
  visibility: visible;
}

.pulse {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--up);
  box-shadow: 0 0 0 0 rgba(62, 213, 152, 0.6);
  animation: pulse 2.4s ease-out infinite;
  flex: none;
  /* On top of .lb-status's word-space gap: the dot is an indicator rather than
     a word, so it wants more air than the gap between "Updated" and the time. */
  margin-right: 3px;
}

/* A board that is not refreshing should not have a heartbeat. Must come AFTER
   .pulse: same specificity, so source order is what decides the animation. */
.pulse--idle {
  background: var(--muted-dim);
  box-shadow: none;
  animation: none;
}

@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(62, 213, 152, 0.5);
  }
  70% {
    box-shadow: 0 0 0 8px rgba(62, 213, 152, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(62, 213, 152, 0);
  }
}

/* --- Leaderboard table ---------------------------------------------------- */

.lb-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-2);
}

.lb {
  width: 100%;
  border-collapse: collapse;
  min-width: 560px;
}

.lb thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface-2);
  padding: 13px 16px;
  text-align: left;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.lb__th-rank {
  width: 118px;
}

/* `.lb thead th` sets text-align: left and outspecifies a bare class, so these
   have to be qualified or the headings sit over the wrong edge of the numbers. */
.lb thead th.lb__th-time,
.lb thead th.lb__th-points {
  text-align: right;
}

.lb__th-points {
  width: 116px;
}

.lb tbody td {
  padding: 0 16px;
  height: 54px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.045);
  vertical-align: middle;
}

.lb__row {
  position: relative;
  /* The whole row toggles the per-track splits, not just the chevron. */
  cursor: pointer;
  transition: background 0.15s ease;
}

.lb__row::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  background: var(--accent);
  transition: width 0.18s ease;
}

.lb__row:hover {
  background: rgba(104, 14, 249, 0.08);
}

.lb__row:hover::after,
.lb__row.is-open::after {
  width: 3px;
}

.lb__row.is-highlighted {
  background: rgba(104, 14, 249, 0.16);
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

.lb__rank {
  display: flex;
  align-items: center;
  gap: 9px;
  height: 54px;
}

.rank {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 30px;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--muted);
}

/* Podium places are marked by colour alone - see RankCell for why there are no
   crowns on a qualifying board. */
.rank--podium {
  font-size: 1.05rem;
}

.rank--1 {
  color: var(--gold);
  text-shadow: 0 0 12px rgba(255, 206, 74, 0.4);
}

.rank--2 {
  color: var(--silver);
}

.rank--3 {
  color: var(--bronze);
}

.lb__row.is-podium-1 {
  background: linear-gradient(90deg, rgba(255, 206, 74, 0.09), transparent 45%);
}

.lb__row.is-podium-2 {
  background: linear-gradient(90deg, rgba(205, 213, 229, 0.07), transparent 45%);
}

.lb__row.is-podium-3 {
  background: linear-gradient(90deg, rgba(224, 138, 70, 0.07), transparent 45%);
}

.move {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.65rem;
  letter-spacing: 0.02em;
  min-width: 30px;
}

.move--up {
  color: var(--up);
}

.move--down {
  color: var(--down);
}

.move--new {
  color: var(--accent-soft);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
}

.lb__expand {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  color: var(--text);
}

.lb__name {
  font-weight: 600;
  font-size: 0.98rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lb__chevron {
  width: 14px;
  height: 14px;
  color: var(--muted-dim);
  flex: none;
  transition: transform 0.2s ease, color 0.2s ease;
}

.lb__row.is-open .lb__chevron {
  transform: rotate(180deg);
  color: var(--accent-soft);
}

.flag {
  width: 21px;
  height: 15px;
  border-radius: 2px;
  flex: none;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14);
  object-fit: cover;
}

.lb__time,
.lb__points {
  text-align: right;
  font-family: var(--font-mono);
  font-weight: 500;
  white-space: nowrap;
}

/* Points decide the standings, so they carry the emphasis; the total time is
   supporting context and is toned down to match. */
.lb__points {
  font-size: 0.98rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: #fff;
}

.lb__time {
  color: var(--muted-dim);
  font-size: 0.82rem;
  letter-spacing: -0.02em;
}

/* Shown when a player has not run every track, so there is no meaningful total. */
/*
 * The em-dash standing in for a total that would mislead. Quieter than a real
 * time, but it still has to be readable: this was --muted-dim at opacity 0.5,
 * which worked out at 1.78:1 against the row behind it.
 */
.lb__time--partial {
  color: var(--muted);
  cursor: help;
}

/*
 * "Secret" stands in for a time Nadeo withhold on the secret qualifier maps.
 * Set in the body face rather than the mono clock face - it is a word, not a
 * number, and letting it keep the tabular figures made it read as a broken
 * time. Muted, because it is the absence of a value, but not as faint as the
 * incomplete dash above: this row did run the track.
 */
.lb__time--secret,
.track-board__time--secret {
  font-family: var(--font-body);
  font-style: italic;
  letter-spacing: 0;
  color: var(--muted);
  cursor: help;
}

.lb__row.is-podium-1 .lb__points {
  color: var(--gold);
}

/* Expanded per-track splits.
   `.lb tbody td` is more specific than a bare `.lb__splits td`, so the padding
   here has to be qualified - otherwise the shorthand there wins and the last
   split ends up flush against the next player's row. Only the block padding is
   set, so the mobile rules keep control of the horizontal padding. */
.lb tbody tr.lb__splits td {
  padding-block: 0 18px;
  height: auto;
  background: rgba(0, 0, 0, 0.28);
}

/* The minimum track has to fit the whole row on one line: index chip + the
   longest "0:00.000 . #100" + the points figure + padding. Too narrow and the
   nowrap time/position text overflows its flex item and paints over the points
   (its own bounding box stays inside, so nothing visibly clips). */
.splits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 14px 0 0;
}

.splits__item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.015));
  border: 1px solid var(--line);
  border-radius: 8px;
}

/* A numbered chip reads as "track 1 of 5" far better than an accent stripe. */
.splits__index {
  flex: none;
  padding: 3px 6px;
  border-radius: 5px;
  background: rgba(104, 14, 249, 0.2);
  color: var(--accent-ink);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.64rem;
  line-height: 1.35;
}

.splits__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  /* Belt and braces: if this ever does get squeezed below the text width, clip
     rather than letting it run over the points. */
  overflow: hidden;
}

.splits__track {
  font-size: 0.74rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.splits__meta {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.8rem;
  letter-spacing: -0.02em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.splits__points {
  flex: none;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.84rem;
  letter-spacing: -0.02em;
  color: var(--accent-soft);
  white-space: nowrap;
}

.splits__points small {
  font-size: 0.62rem;
  font-weight: 600;
  color: var(--muted-dim);
  margin-left: 3px;
}

/* A track the player never ran. Dimmed rather than hidden, so it is obvious
   which one is missing and what it cost them. */
.splits__item--missing {
  background: none;
  border-style: dashed;
  opacity: 0.55;
}

.splits__item--missing .splits__index {
  background: rgba(255, 255, 255, 0.06);
  color: var(--muted-dim);
}

.splits__item--missing .splits__meta {
  font-style: italic;
  font-weight: 500;
  color: var(--muted-dim);
}

.splits__item--missing .splits__points {
  color: var(--muted-dim);
}

/* Qualification cutoff divider */
.lb__cutoff td {
  height: auto;
  padding: 0;
  border-bottom: 0;
}

.lb__cutoff span {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 16px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.24em;
  color: var(--accent-soft);
  background: linear-gradient(90deg, rgba(104, 14, 249, 0.22), transparent);
  border-top: 1px dashed var(--accent);
  border-bottom: 1px dashed rgba(104, 14, 249, 0.35);
}

.lb__cutoff span::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--line-strong), transparent);
}

/*
 * Note under the final row explaining the display limit. Deliberately quieter
 * than .lb__cutoff above: that divider marks a result, this one is an aside
 * about the table, and it should not compete with the qualification line.
 */
.lb__limit td {
  padding: 14px 16px;
  border-bottom: 0;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.5;
  text-align: center;
  white-space: normal;
}

/* --- Pagination ----------------------------------------------------------- */

.lb-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding-top: 18px;
}

.pager {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 5px;
}

.pager button {
  min-width: 36px;
  height: 36px;
  padding: 0 9px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 7px;
  color: var(--muted);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.84rem;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.pager button:hover:not(:disabled):not(.is-current) {
  color: var(--text);
  border-color: var(--line-strong);
}

.pager button:disabled {
  opacity: 0.35;
  cursor: default;
}

.pager__page.is-current {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: 0 0 18px -6px var(--accent-glow);
}

.pager__gap {
  align-self: center;
  color: var(--muted-dim);
  padding: 0 3px;
}

.lb-meta {
  margin: 0;
  font-size: 0.82rem;
  color: var(--muted-dim);
}

/* --- Tracks --------------------------------------------------------------- */

.tracks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  gap: 18px;
}

.track {
  display: flex;
  flex-direction: column;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  transition: border-color 0.2s ease, transform 0.2s ease;
}

.track:hover {
  border-color: var(--line-strong);
  transform: translateY(-2px);
}

.track__media {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--surface-2);
  overflow: hidden;
}

.track__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.track__media::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, transparent 45%, rgba(10, 8, 19, 0.92) 100%);
}

.track__index {
  position: absolute;
  top: 10px;
  left: 12px;
  z-index: 1;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.8);
}

.track__body {
  padding: 15px 16px 17px;
  display: flex;
  flex-direction: column;
  flex: 1;
}

.track__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.track__name {
  font-size: 1.12rem;
  text-transform: uppercase;
  letter-spacing: 0.01em;
}

.track__author {
  margin: 2px 0 0;
  font-size: 0.8rem;
  color: var(--muted-dim);
}

.track-board {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.track-board__row {
  display: grid;
  grid-template-columns: 26px 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 7px 8px;
  border-radius: 5px;
  font-size: 0.88rem;
}

.track-board__row:nth-child(odd) {
  background: rgba(255, 255, 255, 0.025);
}

.track-board__pos {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.82rem;
  color: var(--muted-dim);
  text-align: right;
}

.track-board__row.is-podium-1 .track-board__pos {
  color: var(--gold);
}

.track-board__row.is-podium-2 .track-board__pos {
  color: var(--silver);
}

.track-board__row.is-podium-3 .track-board__pos {
  color: var(--bronze);
}

.track-board__player {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.track-board__player span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.track-board__time {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.8rem;
  letter-spacing: -0.02em;
  color: var(--muted);
}

.track-board__points {
  min-width: 52px;
  text-align: right;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: -0.02em;
  color: var(--accent-soft);
}

.track-board.is-expanded {
  max-height: 460px;
  overflow-y: auto;
  scrollbar-width: thin;
}

/* --- FAQ ------------------------------------------------------------------ */

.faq {
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.faq__item {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  transition: border-color 0.2s ease;
}

.faq__item[open] {
  border-color: var(--line-strong);
}

.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 15px 18px;
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  list-style: none;
}

.faq__q::-webkit-details-marker {
  display: none;
}

.faq__q:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.02);
}

.faq__chevron {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--accent-soft);
  transition: transform 0.2s ease;
}

.faq__item[open] .faq__chevron {
  transform: rotate(180deg);
}

.faq__a {
  padding: 0 18px 18px;
  color: var(--muted);
  max-width: 72ch;
}

.faq__a p {
  margin-bottom: 0;
}

/* Answers can run to several paragraphs, so they need separating. */
.faq__a p + p {
  margin-top: 0.85em;
}

.faq__a .btn {
  margin-top: 14px;
}

/* --- Homepage ------------------------------------------------------------- */

.lede {
  max-width: 62ch;
  color: var(--muted);
  font-size: clamp(0.98rem, 2vw, 1.08rem);
}

/* The Wedding Cup panel.
   The one loud thing on an otherwise quiet page, so it gets the hero's own
   vocabulary - crown, wordmark - at panel size. Two columns: the billing on the
   left, the numbers and the call to action stacked in a stub on the right. */
.cup-panel {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  /* Stretch, so the stub's rule runs the full height of the panel and reads as
     a divider between the columns rather than a stray line beside the chip. */
  align-items: stretch;
  gap: clamp(22px, 3.5vw, 48px);
  padding: clamp(24px, 3vw, 34px) clamp(20px, 3vw, 38px);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background:
    radial-gradient(85% 130% at 18% 0%, rgba(104, 14, 249, 0.34), transparent 64%),
    linear-gradient(160deg, var(--surface-2), var(--bg-2));
  box-shadow: var(--shadow-lift);
}

/* Quieter while there is nowhere to go, so that flipping the switch on
   config.weddingCup.href visibly lights the panel up. */
.cup-panel--soon {
  background:
    radial-gradient(85% 130% at 18% 0%, rgba(104, 14, 249, 0.2), transparent 64%),
    linear-gradient(160deg, var(--surface), var(--bg-2));
}

/*
 * Decorative only. Unmasked, unlike the hero's: this one runs corner to corner,
 * and the panel's own overflow: hidden trims it to the rounded border.
 *
 * Do NOT give this a grid-area. The panel is a grid container, and an
 * absolutely positioned child with a definite grid placement has its containing
 * block set to that GRID AREA - which lives inside the content box, so inset: 0
 * would then stop a whole panel's padding short of the edge. Left with auto
 * placement, the containing block is the padding box and the grid reaches the
 * border, which is the point of it.
 *
 * pointer-events: none keeps the layer from swallowing clicks on the CTA.
 */
.cup-panel__grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(168, 123, 255, 0.085) 1px, transparent 1px),
    linear-gradient(90deg, rgba(168, 123, 255, 0.085) 1px, transparent 1px);
  background-size: 56px 56px;
}

/* Centred as a block of its own, which is what puts the crown over the middle
   of the wordmark - the crown is centred in this column, not against the panel.
   An explicit stacking peer too, so it sits above the grid rather than relying
   on paint order. */
.cup-panel__body {
  position: relative;
  max-width: 54ch;
  margin-inline: auto;
  text-align: center;
}

.cup-panel__crown {
  display: block;
  width: clamp(26px, 3.4vw, 34px);
  /* No bottom margin: the wordmark's negative top margin sets the spacing. */
  margin: 0 auto;
  fill: var(--accent-soft);
  filter: drop-shadow(0 0 14px var(--accent-glow));
}

.cup-panel__title {
  margin: 0;
}

/* Around half the hero's size. The wordmark's own margins and padding are tuned
   to its glyph overhang, so only the size is touched here - except at the
   bottom: that clearance is sized for the tilt at hero scale, and at this size
   the left end drops far less, so most of it is dead air and comes back. */
.cup-panel .wordmark {
  font-size: clamp(1.4rem, 4vw, 2.5rem);
  margin-bottom: -0.12em;
}

.cup-panel__blurb {
  margin: 0;
  color: var(--muted);
  font-size: 0.95rem;
}

/* The right-hand stub. No rule between the columns: the grid behind the panel
   already draws vertical lines, and one more at an arbitrary x just read as a
   grid line that had landed in the wrong place. The gap separates them. */
.cup-panel__aside {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(16px, 2vw, 22px);
  padding-left: clamp(22px, 3.5vw, 48px);
}

.cup-facts {
  display: grid;
  gap: 7px;
}

/* Values in their own column, right-aligned, so the numbers line up under each
   other however many digits they run to. */
.cup-fact {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 10px;
}

.cup-fact__value {
  justify-self: end;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  color: #fff;
  line-height: 1.2;
}

.cup-fact__label {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--muted-dim);
  white-space: nowrap;
}

/* Deliberately not a button: there is nothing to press yet. The dashed edge is
   the tell - it reads as a placeholder rather than a disabled control, which
   would invite clicking. */
.cup-panel__status {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  margin: 0;
  padding: 11px 24px;
  border: 1px dashed var(--line-strong);
  border-radius: 999px;
  background: rgba(104, 14, 249, 0.1);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--accent-ink);
}

.cup-panel__status::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent-soft);
  box-shadow: 0 0 10px var(--accent-glow);
  animation: cup-pulse 2.4s ease-in-out infinite;
}

@keyframes cup-pulse {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 1;
  }
}

.cup-panel__cta {
  font-size: 0.95rem;
  padding: 13px 26px;
}


.stats {
  display: grid;
  /* Small enough that three tiles stay on one row inside a half-width column
     and on a phone, and auto-fit still spreads a shorter or longer list. */
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  gap: 10px;
  margin-top: clamp(18px, 2.5vw, 26px);
}

.stat {
  padding: 14px 12px;
  background: linear-gradient(180deg, var(--surface), var(--bg-2));
  border: 1px solid var(--line);
  border-top: 2px solid var(--accent);
  border-radius: var(--radius-sm);
  text-align: center;
}

.stat__value {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.5rem, 3.2vw, 1.95rem);
  color: #fff;
  text-shadow: 0 0 22px var(--accent-glow);
  line-height: 1.1;
}

.stat__label {
  display: block;
  margin-top: 5px;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  color: var(--muted-dim);
}

/* The weekly stream schedule. One row per slot: day, what it is, and roughly
   when - the accent edge on the left is what makes the stack read as a list of
   appointments rather than three loose cards. */
.schedule {
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.slot {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 14px;
  padding: 11px 15px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius-sm);
}

/* Each row is its own grid, so without a floor on this column the names would
   start at a different x on every row. Wide enough for the longest weekday
   there is, which is what keeps the list aligned whichever days are listed. */
.slot__day {
  min-width: 8em;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--accent-soft);
}

.slot__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
}

.slot__time {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted-dim);
  white-space: nowrap;
}

.schedule__note {
  margin: 12px 0 0;
  font-size: 0.82rem;
  color: var(--muted-dim);
}

.socials {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px;
}

.social {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 15px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text);
  transition: border-color 0.2s ease, transform 0.2s ease, background 0.2s ease;
}

.social:hover {
  border-color: var(--line-strong);
  background: var(--surface-2);
  transform: translateY(-2px);
  color: var(--text);
}

.social__icon {
  width: 22px;
  height: 22px;
  fill: var(--accent-soft);
  flex: none;
  transition: fill 0.2s ease;
}

.social--youtube:hover .social__icon {
  fill: #ff4b4b;
}
.social--twitch:hover .social__icon {
  fill: #a970ff;
}
.social--tiktok:hover .social__icon {
  fill: #25f4ee;
}
.social--instagram:hover .social__icon {
  fill: #ff5c8a;
}

.social__body {
  flex: 1;
  min-width: 0;
}

.social__body strong {
  font-family: var(--font-display);
  font-size: 0.95rem;
  display: block;
  line-height: 1.3;
}

.social__handle {
  font-size: 0.78rem;
  color: var(--accent-soft);
}


.social__arrow {
  width: 15px;
  height: 15px;
  color: var(--muted-dim);
  flex: none;
  transition: transform 0.2s ease, color 0.2s ease;
}

.social:hover .social__arrow {
  transform: translate(2px, -2px);
  color: var(--accent-soft);
}

.videos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
}

.video {
  text-decoration: none;
  color: var(--text);
}

.video__thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--surface-2);
}

.video__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.35s ease;
}

.video:hover .video__thumb img {
  transform: scale(1.05);
}

.video__play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(7, 6, 13, 0.32);
  transition: background 0.2s ease;
}

.video__play svg {
  width: 46px;
  height: 46px;
  fill: #fff;
  filter: drop-shadow(0 0 14px var(--accent-glow));
  transition: transform 0.2s ease;
}

.video:hover .video__play {
  background: rgba(104, 14, 249, 0.26);
}

.video:hover .video__play svg {
  transform: scale(1.12);
}

.video__title {
  margin: 10px 0 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.discord-cta {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: clamp(22px, 3vw, 32px);
  margin-bottom: clamp(40px, 6vw, 72px);
  background: linear-gradient(135deg, rgba(104, 14, 249, 0.22), rgba(16, 12, 28, 0.9));
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  overflow: hidden;
}

.discord-cta__body {
  max-width: 58ch;
}

.discord-cta h2 {
  font-size: clamp(1.3rem, 2.8vw, 1.7rem);
  text-transform: uppercase;
  margin-bottom: 0.5em;
}

.discord-cta p {
  color: var(--muted);
}


.discord-cta__mark {
  width: clamp(70px, 10vw, 110px);
  height: auto;
  fill: var(--accent);
  opacity: 0.22;
  flex: none;
}

/* --- Discord gate feedback ------------------------------------------------ */

.discord-error {
  display: block;
  margin-top: 10px;
  font-size: 0.84rem;
  color: var(--down);
}

/* Turnstile renders here; the managed widget is usually invisible. */
.turnstile-host {
  margin-top: 12px;
}

.turnstile-host:empty {
  margin-top: 0;
}

/* --- Footer --------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--line);
  /* Opaque, not translucent: it has to land on exactly the same colour as the
     header, and the page's fixed purple haze shows through anything less. */
  background: var(--chrome);
  padding: clamp(30px, 5vw, 48px) 0;
}

.site-footer__inner {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(16px, 4vw, 32px);
  display: grid;
  gap: 22px;
}

.site-footer__brand {
  display: flex;
  align-items: center;
  gap: 13px;
}

.site-footer__brand strong {
  font-family: var(--font-display);
  font-size: 1.05rem;
}

.site-footer__brand p {
  margin: 2px 0 0;
  font-size: 0.85rem;
  color: var(--muted-dim);
}

.site-footer__socials {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  font-size: 0.9rem;
}

.site-footer__socials a,
.site-footer__socials button {
  color: var(--muted);
  text-decoration: none;
}

.site-footer__socials a:hover,
.site-footer__socials button:hover {
  color: var(--accent-soft);
}

.site-footer__legal {
  margin: 0;
  font-size: 0.76rem;
  color: var(--muted-dim);
  max-width: 70ch;
}

/* --- Error pages ---------------------------------------------------------- */

.error-page {
  min-height: 70vh;
  display: grid;
  place-content: center;
  text-align: center;
  gap: 8px;
}

.error-page h1 {
  font-size: clamp(2rem, 7vw, 3.4rem);
  text-transform: uppercase;
}

.error-page .btn {
  justify-self: center;
  margin-top: 12px;
}

/* --- Responsive ----------------------------------------------------------- */

/* Both of the homepage's two-column bands stack together, so the page never
   shows one row split and the other still side by side. */
@media (max-width: 900px) {
  .home-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .cup-panel {
    grid-template-columns: minmax(0, 1fr);
  }

  .cup-panel__aside {
    padding-left: 0;
    padding-top: clamp(20px, 4vw, 26px);
  }

  .cup-facts {
    grid-auto-flow: column;
    justify-content: center;
    gap: clamp(16px, 5vw, 32px);
  }

  /* Stacked value over label once they sit side by side, so three short
     columns read as a strip rather than six loose words. */
  .cup-fact {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    gap: 2px;
  }

  .cup-fact__value {
    justify-self: center;
  }
}

@media (max-width: 460px) {
  .slot {
    grid-template-columns: auto minmax(0, 1fr);
    gap: 4px 12px;
  }

  .slot__time {
    grid-column: 2;
  }
}

@media (max-width: 860px) {
  .discord-cta {
    flex-direction: column;
    align-items: flex-start;
  }

  .discord-cta__mark {
    align-self: flex-end;
    margin-top: -40px;
  }
}

@media (max-width: 700px) {
  /* The heading and the status stamp stop fitting on one row here, so the head
     stacks and the stamp returns to the left under the heading. */
  .panel__head {
    flex-direction: column;
    gap: 10px;
  }

  .lb-status {
    justify-content: flex-start;
    text-align: left;
    padding-top: 0;
  }

  /* The line moves to the left edge here, so the hover has to open the other
     way or it hangs off the side of the panel. */
  .lb-live::after {
    right: auto;
    left: 0;
  }

  .site-nav {
    gap: 16px;
  }

  .brand__text {
    display: none;
  }

  /* Keep the tabs as a row rather than stacking them, and join the strip to the
     panel below so the two read as one control instead of three loose buttons
     floating above a card. */
  .tabs__list {
    grid-template-columns: repeat(3, 1fr);
    gap: 4px;
    padding: 5px 5px 0;
    border-bottom: 0;
    border-radius: var(--radius) var(--radius) 0 0;
  }

  /* At this size the labels are small and the icons are quiet, so without an
     outline the inactive tabs read as headings rather than something you can
     tap. A faint ring gives each one an edge. */
  .tabs__tab {
    flex-direction: column;
    gap: 5px;
    padding: 9px 4px 10px;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: rgba(255, 255, 255, 0.028);
    box-shadow: inset 0 0 0 1px var(--line);
  }

  .tabs__tab.is-active {
    box-shadow: inset 0 0 0 1px var(--line-strong);
  }

  .tabs__icon svg {
    width: 16px;
    height: 16px;
  }

  /* The active tab's underline sits flush against the panel it opens. */
  .tabs__tab.is-active::after {
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    width: auto;
    border-radius: 0;
  }

  .tabs__panel {
    margin-top: 0;
  }

  .tabs__panel .panel {
    border-top: 0;
    border-radius: 0 0 var(--radius) var(--radius);
  }

  /* Give the table back the space the panel padding was taking. */
  .panel {
    padding-inline: 12px;
  }

  /* Drop the gap column and cap the player name so the table fits the viewport
     without scrolling sideways. Capping the name is the important part: it is
     `white-space: nowrap`, so without a max-width a long name widens the whole
     table. The .lb-scroll container stays as a safety net for very long names
     on very narrow screens. */
  .lb {
    min-width: 0;
    width: 100%;
  }

  /* Points decide the standings, so the total time is what gives way. */
  .lb__th-time,
  .lb__time {
    display: none;
  }

  .lb__th-rank {
    width: 56px;
  }

  /* This cap, not the rendered name, is what sets the player column's width in
     an auto-layout table - so it has to leave room for the rank and time
     columns or the table overflows its container. */
  .lb__name {
    max-width: 30vw;
  }

  .lb__player {
    overflow: hidden;
  }

  .lb__expand {
    min-width: 0;
  }

  .lb thead th,
  .lb tbody td {
    padding-inline: 6px;
  }

  .lb__rank {
    gap: 5px;
  }

  .lb__name {
    font-size: 0.9rem;
  }

  .lb__time {
    font-size: 0.94rem;
  }

  .lb__expand {
    gap: 7px;
  }

  /* The chevron is a nicety; the whole row is tappable anyway. */
  .lb__chevron {
    display: none;
  }

  .move {
    min-width: 0;
    font-size: 0.62rem;
  }

  .countdown {
    display: block;
    width: 100%;
  }

  .countdown__unit {
    min-width: 0;
    flex: 1;
  }
}

@media (max-width: 420px) {
  .hero__strip {
    letter-spacing: 0.1em;
  }

  .countdown__units {
    gap: 6px;
  }
}

/* --- Motion preferences --------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  .hero__media img {
    transform: none !important;
  }
}
