/* ===========================================================================
   DIRTYBIRD.NO — design system
   ---------------------------------------------------------------------------
   Black page, the mark's own colours on top. The three accents are sampled
   from the logo (the same values the dirtybird.store storefront uses):
   cyan #009CE0, hi-vis yellow #FFE000, grey #B4B5B6.

   Each colour has one job:
     cyan   = interactive. Buttons, links, focus, the news card edge.
     yellow = signal, never clickable. News dates, the ticker, eyebrows.
     grey   = the disc. Quiet surfaces and decoration.

   Display type is Exo 2 Black Italic, the closest open face to the logo's
   slanted techno wordmark. Body is Barlow. Both self-hosted (OFL.txt).

   Motion is only ever added inside prefers-reduced-motion: no-preference,
   and anything hidden-until-revealed is gated on html.js, so the page reads
   in full with JavaScript off.
   ======================================================================== */

@font-face {
  font-family: "Exo 2";
  font-style: italic;
  font-weight: 700 900;
  font-display: swap;
  src: url("../fonts/exo2-italic-700-900-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/barlow-400-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/barlow-600-latin.woff2") format("woff2");
}

:root {
  --bg: #070708;
  --bg-2: #0d0e10;
  --surface: #121417;
  --surface-2: #181b1f;
  --line: rgba(255, 255, 255, 0.1);
  --line-strong: rgba(255, 255, 255, 0.22);
  --text: #f4f4f4;
  --muted: #a2a4a5;          /* 8.1:1 on --bg */

  --cyan: #009ce0;           /* 6.4:1 on --bg; also the button surface */
  --cyan-hi: #4ec3f5;        /* cyan as small text and focus ring */
  --yellow: #ffe000;         /* always with --on-accent on top */
  --grey: #b4b5b6;
  --on-accent: #0c0c0c;

  --font-display: "Exo 2", "Segoe UI", system-ui, sans-serif;
  --font-body: "Barlow", system-ui, -apple-system, "Segoe UI", sans-serif;

  --step--1: clamp(0.8rem, 0.78rem + 0.1vw, 0.86rem);
  --step-0: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --step-1: clamp(1.15rem, 1.05rem + 0.5vw, 1.45rem);
  --step-2: clamp(1.4rem, 1.2rem + 1vw, 2rem);
  --step-3: clamp(2.3rem, 1.5rem + 4vw, 4.8rem);

  /* The hazard ticker under the hero, shared by .hero and .ticker. The band
     is tilted 2deg, so its right end sits tan(2deg) x 100vw = 3.49vw higher. */
  --ticker-font: clamp(1.05rem, 0.8rem + 1.3vw, 1.9rem);
  --ticker-h: calc(var(--ticker-font) + 1.8rem);
  --ticker-lean: 3.49vw;
  /* Extra hero height that pushes the whole band below the first screen:
     its highest point (right end) sits lean + half the band above the hero's end. */
  --ticker-drop: calc(var(--ticker-lean) + var(--ticker-h) / 2 + 1.5rem);

  --gutter: clamp(16px, 4vw, 48px);
  --maxw: 1240px;
  --header-h: 84px;

  /* Double bezel: a card is a core sitting in a thin tray. The tray is two
     box-shadow rings outside the core, so it needs no extra markup and its
     corners stay concentric (a spread shadow adds its width to the radius).
     Grids that hold bezelled cards leave at least 2 x --tray between them. */
  --r-core: 1.5rem;
  --tray: 6px;
  --bezel:
    inset 0 0 0 1px rgba(255, 255, 255, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.09),
    0 0 0 var(--tray) rgba(255, 255, 255, 0.025),
    0 0 0 calc(var(--tray) + 1px) rgba(255, 255, 255, 0.07);
  --bezel-hot:
    inset 0 0 0 1px rgba(78, 195, 245, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    0 0 0 var(--tray) rgba(0, 156, 224, 0.07),
    0 0 0 calc(var(--tray) + 1px) rgba(78, 195, 245, 0.3);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-snap: cubic-bezier(0.2, 1.5, 0.4, 1);
  --ease-wave: cubic-bezier(0.37, 0, 0.63, 1);
}

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

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

html {
  color-scheme: dark;
  scroll-padding-top: calc(var(--header-h) + 16px);
  -webkit-text-size-adjust: 100%;
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 var(--step-0)/1.6 var(--font-body);
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
}

/* Fine film grain over the whole page, so the flat black reads as a surface
   rather than an empty screen. A data: SVG, which the CSP's img-src allows. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 70;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .6 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.045;
  pointer-events: none;
}

p { text-wrap: pretty; }

/* Links and buttons: no double-tap-zoom delay, and no grey flash on tap
   (the :focus-visible ring and the hover states already show the response). */
a, button, summary {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Text for screen readers only (live regions). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

img, svg { display: block; max-width: 100%; }
img { height: auto; }

a { color: var(--cyan-hi); text-decoration-thickness: 1px; text-underline-offset: 0.2em; }
a:hover { color: #fff; }

/* No border-radius here: the outline follows each element's own corners, so
   a pill button keeps a pill-shaped ring. */
:focus-visible {
  outline: 3px solid var(--cyan-hi);
  outline-offset: 4px;
}

h1, h2, h3, p, ul, dl, dd, blockquote, figure { margin: 0; }
ul { padding: 0; list-style: none; }

::selection { background: var(--yellow); color: var(--on-accent); }

.skip-link {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 100;
  padding: 0.7em 1.1em;
  background: var(--cyan);
  color: var(--on-accent);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-200%);
}
.skip-link:focus { transform: none; }

/* A small pill badge above a heading: a glowing dot, then the label. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  justify-self: start;
  gap: 0.75em;
  padding: 0.55em 1.1em 0.55em 0.9em;
  border-radius: 999px;
  background: rgba(255, 224, 0, 0.06);
  box-shadow: inset 0 0 0 1px rgba(255, 224, 0, 0.18);
  color: var(--yellow);
  font: 600 0.7rem/1.2 var(--font-body);
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.eyebrow::before {
  content: "";
  width: 0.5em;
  height: 0.5em;
  flex: none;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 10px currentColor;
}

.icon-ext {
  width: 0.85em;
  height: 0.85em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Line icons, drawn on a 32 unit grid and inked with the text colour. The
   small size is drawn heavier so it still reads at text size. An icon chip is
   an icon in a quiet cyan disc. Icons are decoration: always aria-hidden,
   beside a label that says the same thing. */
.ico {
  width: 2rem;
  height: 2rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ico--sm { width: 1.2em; height: 1.2em; stroke-width: 2.6; }
.ico-fill { fill: currentColor; stroke: none; }
.icon-chip {
  display: grid;
  place-items: center;
  flex: none;
  width: 3.6rem;
  height: 3.6rem;
  border-radius: 50%;
  background: rgba(0, 156, 224, 0.1);
  box-shadow: inset 0 0 0 1px rgba(78, 195, 245, 0.3);
  color: var(--cyan-hi);
}

/* ---- Buttons: pills with the icon in a button of its own ------------------
   The arrow never sits loose beside the label: it rides in a circle flush
   with the pill's right edge, and on hover it is nudged out along its own
   direction while the whole pill gives a little under the press. */

.btn {
  --btn-bg: rgba(255, 255, 255, 0.04);
  --btn-ring: rgba(255, 255, 255, 0.14);
  --btn-fg: var(--text);
  --btn-icon-bg: rgba(255, 255, 255, 0.08);
  --btn-px: 1.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.9em;
  min-height: 52px;
  padding: 0.4rem var(--btn-px);
  border: 0;
  border-radius: 999px;
  /* The glint waits off the left edge and crosses on hover (motion section). */
  background:
    linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent) -60% 0 / 35% 100% no-repeat,
    var(--btn-bg);
  box-shadow: inset 0 0 0 1px var(--btn-ring), inset 0 1px 0 rgba(255, 255, 255, 0.12);
  color: var(--btn-fg);
  font: 600 0.86rem/1.1 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    transform 0.5s var(--ease-spring),
    background-color 0.5s var(--ease-spring),
    box-shadow 0.5s var(--ease-spring),
    color 0.5s var(--ease-spring);
}
.btn:hover { color: var(--btn-fg); }
.btn:active { transform: scale(0.98); transition-duration: 0.15s; }

.btn-icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.4em;
  height: 2.4em;
  margin-right: calc(-1 * var(--btn-px) + 0.4rem);
  border-radius: 50%;
  background: var(--btn-icon-bg);
  transition: transform 0.6s var(--ease-spring), background-color 0.5s var(--ease-spring);
}
.btn-icon svg { width: 0.95em; height: 0.95em; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.btn:hover .btn-icon { transform: translate(3px, -1px) scale(1.06); }
/* A downward arrow moves down, not out to the corner. A back arrow leads the
   label, flush with the left edge, and moves back. */
.btn:hover .btn-icon--down { transform: translateY(3px) scale(1.06); }
.btn-icon--back { margin-right: 0; margin-left: calc(-1 * var(--btn-px) + 0.4rem); }
.btn:hover .btn-icon--back { transform: translateX(-3px) scale(1.06); }

.btn-primary,
.btn-shop {
  --btn-bg: var(--cyan);
  --btn-ring: rgba(255, 255, 255, 0.22);
  --btn-fg: var(--on-accent);
  --btn-icon-bg: rgba(0, 0, 0, 0.14);
}
.btn-primary:hover,
.btn-shop:hover { --btn-bg: #2bb3ee; --btn-icon-bg: rgba(0, 0, 0, 0.2); }

.btn-ghost:hover { --btn-ring: rgba(78, 195, 245, 0.6); --btn-fg: var(--cyan-hi); --btn-icon-bg: rgba(0, 156, 224, 0.16); }

.btn-shop { --btn-px: 1.1rem; min-height: 42px; font-size: 0.72rem; }
.btn-xl { --btn-px: 1.9rem; min-height: 62px; font-size: clamp(0.88rem, 0.84rem + 0.3vw, 1.02rem); }

.text-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--cyan-hi);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 0.85rem;
  text-decoration: none;
  border-bottom: 2px solid currentColor;
  padding-bottom: 0.2em;
}
/* A chevron that nudges forward when its card is hovered. */
.text-link::after {
  content: "";
  width: 0.45em;
  height: 0.45em;
  border-top: 2px solid currentColor;
  border-right: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.35s var(--ease-out);
}
.news-card:hover .text-link::after,
.news-card:focus-within .text-link::after { transform: translateX(4px) rotate(45deg); }

/* ---- Scroll progress ----------------------------------------------------- */

.progress {
  position: fixed;
  inset: 0 0 auto;
  z-index: 60;
  height: 3px;
  pointer-events: none;
}
.progress-bar {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--cyan), var(--cyan-hi));
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* ---- Header: a glass island floating under the top edge ------------------ */

/* The header box spans the width but lets clicks through; only the pill
   inside it takes them. Its top padding is the island's gap from the edge,
   so translateY(-100%) hides island and gap together. */
.site-header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 50;
  padding: 12px var(--gutter) 0;
  pointer-events: none;
  transition: transform 0.7s var(--ease-spring);
}
.site-header.is-hidden:not(:focus-within) { transform: translateY(calc(-100% - 4px)); }

.header-inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  max-width: var(--maxw);
  min-height: 60px;
  margin-inline: auto;
  padding: 0 8px 0 12px;
  border-radius: 999px;
  pointer-events: auto;
}
/* The glass lives on a pseudo-element: a backdrop-filter on an ancestor of
   the fixed phone menu would trap the menu inside the pill. */
.header-inner::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: rgba(16, 17, 20, 0.42);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.08),
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    0 18px 40px -18px rgba(0, 0, 0, 0.7);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  backdrop-filter: blur(18px) saturate(140%);
  transition: background-color 0.6s var(--ease-spring);
}
.site-header.is-scrolled .header-inner::before,
html:not(.js) .header-inner::before { background-color: rgba(16, 17, 20, 0.78); }

.brand {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--text);
  text-decoration: none;
}
.brand-disc {
  width: 40px;
  transition: transform 0.6s var(--ease-snap);
}
.brand:hover .brand-disc { transform: rotate(-18deg) scale(1.08); }

.brand-word {
  font: 900 italic 1.35rem/1 var(--font-display);
  letter-spacing: 0.02em;
}
.brand-word span { color: var(--cyan); }

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem;
}
.site-nav ul a {
  position: relative;
  display: inline-block;
  padding: 0.75em 1.05em;
  border-radius: 999px;
  color: var(--text);
  font: 600 0.76rem/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 0.5s var(--ease-spring), color 0.5s var(--ease-spring);
}
/* aria-current is "page" on news.html and "true" for the homepage section in
   view (set by main.js, section 7): a lit dot under the label. */
.site-nav ul a::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0.3em;
  width: 4px;
  height: 4px;
  margin-left: -2px;
  border-radius: 50%;
  background: var(--cyan-hi);
  box-shadow: 0 0 8px var(--cyan);
  transform: scale(0);
  transition: transform 0.5s var(--ease-snap);
}
.site-nav ul a:hover { background: rgba(255, 255, 255, 0.06); color: #fff; }
.site-nav ul a[aria-current]::after { transform: none; }

.menu-toggle {
  position: relative;
  z-index: 2;
  display: none;
  align-items: center;
  gap: 0.75rem;
  min-height: 44px;
  padding: 0 1rem 0 1.15rem;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  color: var(--text);
  font: 600 0.72rem/1 var(--font-body);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 0.5s var(--ease-spring), transform 0.5s var(--ease-spring);
}
.menu-toggle:hover { background: rgba(255, 255, 255, 0.12); }
.menu-toggle:active { transform: scale(0.96); }
/* Two lines that rotate into an X: the box is the same height either way,
   so each line only travels to the centre and turns. */
.menu-toggle-bars {
  position: relative;
  order: 2;
  width: 20px;
  height: 8px;
}
.menu-toggle-bars::before,
.menu-toggle-bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  border-radius: 1px;
  background: currentColor;
  transition: transform 0.6s var(--ease-spring);
}
.menu-toggle-bars::before { top: 0; }
.menu-toggle-bars::after { bottom: 0; }
.menu-toggle[aria-expanded="true"] .menu-toggle-bars::before { transform: translateY(3.25px) rotate(45deg); }
.menu-toggle[aria-expanded="true"] .menu-toggle-bars::after { transform: translateY(-3.25px) rotate(-45deg); }

/* From 1120px the island shrinks to its contents and sits centred. */
@media (min-width: 1120px) {
  .header-inner { width: max-content; gap: 2.2rem; }
  .site-nav {
    display: flex;
    align-items: center;
    gap: 1.4rem;
  }
}

/* Without JavaScript the nav simply wraps under the brand on a phone. */
@media (max-width: 1119.98px) {
  html:not(.js) .header-inner { flex-wrap: wrap; padding: 0.6rem 1rem; border-radius: var(--r-core); }
  html:not(.js) .site-nav { width: 100%; padding-bottom: 0.6rem; }
  html:not(.js) .btn-shop { margin-top: 0.6rem; }
  html:not(.js) .site-header { position: relative; }
  html:not(.js) .hero { padding-top: 2rem; }

  html.js .menu-toggle { display: inline-flex; }
  /* A full-screen sheet of dark glass. It opens as a circle from the toggle,
     then each link rises out of its own row one after another. */
  html.js .site-nav {
    position: fixed;
    inset: 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2.5rem;
    padding: calc(var(--header-h) + 1rem) calc(var(--gutter) + 0.5rem) 2rem;
    background:
      radial-gradient(circle at 100% 100%, rgba(255, 224, 0, 0.12), transparent 45%),
      radial-gradient(circle at 0% 0%, rgba(0, 156, 224, 0.14), transparent 50%),
      rgba(7, 7, 8, 0.8);
    -webkit-backdrop-filter: blur(40px) saturate(150%);
    backdrop-filter: blur(40px) saturate(150%);
    clip-path: circle(0 at calc(100% - var(--gutter) - 52px) 42px);
    visibility: hidden;
    transition: clip-path 0.6s var(--ease-spring), visibility 0s 0.6s;
  }
  html.menu-open { overflow: hidden; }
  html.menu-open .site-nav {
    overscroll-behavior: contain;
    clip-path: circle(150% at calc(100% - var(--gutter) - 52px) 42px);
    visibility: visible;
    transition: clip-path 0.9s var(--ease-spring), visibility 0s;
  }
  html.js .site-nav ul { flex-direction: column; gap: 0.3rem; }
  /* Each row clips its link, so the words rise out of an invisible slot.
     The padding leaves room for the focus ring inside the clip. */
  html.js .site-nav li { overflow: hidden; padding: 6px; margin: -6px; }
  html.js .site-nav ul a {
    padding: 0.08em 0;
    border-radius: 0;
    background: none;
    font: 900 italic clamp(2.2rem, 10vw, 3.6rem)/1.05 var(--font-display);
    letter-spacing: 0;
    opacity: 0;
    transform: translateY(110%);
    transition: opacity 0.5s var(--ease-spring), transform 0.9s var(--ease-spring), color 0.4s var(--ease-spring);
  }
  html.js .site-nav ul a:hover { color: var(--cyan-hi); }
  html.js .site-nav ul a::after { left: auto; right: -0.5em; bottom: 50%; width: 8px; height: 8px; margin: 0 0 -4px; }
  html.js .site-nav .btn-shop {
    align-self: flex-start;
    opacity: 0;
    transform: translateY(1.5rem);
    transition: opacity 0.5s var(--ease-spring), transform 0.9s var(--ease-spring);
  }
  html.menu-open .site-nav ul a,
  html.menu-open .site-nav .btn-shop { opacity: 1; transform: none; }
  html.menu-open .site-nav li:nth-child(1) a { transition-delay: 0.1s; }
  html.menu-open .site-nav li:nth-child(2) a { transition-delay: 0.15s; }
  html.menu-open .site-nav li:nth-child(3) a { transition-delay: 0.2s; }
  html.menu-open .site-nav li:nth-child(4) a { transition-delay: 0.25s; }
  html.menu-open .site-nav li:nth-child(5) a { transition-delay: 0.3s; }
  html.menu-open .site-nav .btn-shop { transition-delay: 0.37s; }
}

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

.hero {
  position: relative;
  display: grid;
  align-items: center;
  min-height: calc(100svh + var(--ticker-drop));
  padding: calc(var(--header-h) + 2rem) var(--gutter) calc(5rem + var(--ticker-lean) + var(--ticker-drop));
  overflow: hidden;
  /* The bottom edge leans with the ticker and runs along its centre line,
     so the hero's light carries on right under the band. */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--ticker-lean)), 0 100%);
  background:
    radial-gradient(ellipse 80% 60% at 85% 45%, rgba(0, 156, 224, 0.12), transparent 70%),
    radial-gradient(ellipse 60% 50% at 0% 100%, rgba(255, 224, 0, 0.06), transparent 70%),
    var(--bg);
}

/* Spotlight that follows the pointer (position set from main.js). */
.hero-spot {
  position: absolute;
  inset: 0;
  background: radial-gradient(520px circle at var(--mx, 70%) var(--my, 40%), rgba(78, 195, 245, 0.13), transparent 65%);
  pointer-events: none;
}

/* A segment of the disc's yellow, turning slowly in the corner. */
.hero-arc {
  position: absolute;
  right: -30vmax;
  bottom: -45vmax;
  width: 90vmax;
  aspect-ratio: 1;
  border-radius: 50%;
  background: conic-gradient(from 300deg, var(--yellow) 0 22%, transparent 22% 60%, var(--grey) 60% 64%, transparent 64%);
  -webkit-mask: radial-gradient(circle, transparent 66%, #000 66.3%, #000 67.5%, transparent 67.8%);
  mask: radial-gradient(circle, transparent 66%, #000 66.3%, #000 67.5%, transparent 67.8%);
  opacity: 0.35;
  pointer-events: none;
}

/* Water droplets rising through the dark, as in the product photography. */
.hero-drops { position: absolute; inset: 0; pointer-events: none; }
.hero-drops i {
  --l: 50%; --s: 10px; --t: 14s; --dl: 0s; --dx: 20px;
  position: absolute;
  left: var(--l);
  bottom: -40px;
  width: var(--s);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, rgba(255, 255, 255, 0.95) 0 8%, rgba(255, 255, 255, 0.18) 22%, rgba(160, 220, 255, 0.06) 55%, transparent 70%);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22), inset -2px -3px 6px rgba(78, 195, 245, 0.25);
  opacity: 0;
}
.hero-drops i:nth-child(1)  { --l: 6%;  --s: 14px; --t: 16s; --dl: -2s;  --dx: 30px; }
.hero-drops i:nth-child(2)  { --l: 14%; --s: 8px;  --t: 12s; --dl: -7s;  --dx: -20px; }
.hero-drops i:nth-child(3)  { --l: 22%; --s: 22px; --t: 19s; --dl: -11s; --dx: 40px; }
.hero-drops i:nth-child(4)  { --l: 31%; --s: 6px;  --t: 10s; --dl: -4s;  --dx: 14px; }
.hero-drops i:nth-child(5)  { --l: 39%; --s: 12px; --t: 15s; --dl: -9s;  --dx: -36px; }
.hero-drops i:nth-child(6)  { --l: 47%; --s: 18px; --t: 18s; --dl: -15s; --dx: 24px; }
.hero-drops i:nth-child(7)  { --l: 55%; --s: 9px;  --t: 13s; --dl: -1s;  --dx: -12px; }
.hero-drops i:nth-child(8)  { --l: 62%; --s: 26px; --t: 21s; --dl: -6s;  --dx: 30px; }
.hero-drops i:nth-child(9)  { --l: 70%; --s: 7px;  --t: 11s; --dl: -8s;  --dx: -24px; }
.hero-drops i:nth-child(10) { --l: 77%; --s: 16px; --t: 17s; --dl: -13s; --dx: 18px; }
.hero-drops i:nth-child(11) { --l: 84%; --s: 10px; --t: 14s; --dl: -3s;  --dx: -30px; }
.hero-drops i:nth-child(12) { --l: 90%; --s: 20px; --t: 20s; --dl: -17s; --dx: 22px; }
.hero-drops i:nth-child(13) { --l: 95%; --s: 8px;  --t: 12s; --dl: -10s; --dx: -16px; }
.hero-drops i:nth-child(14) { --l: 3%;  --s: 11px; --t: 15s; --dl: -12s; --dx: 26px; }

.hero-inner {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 2.5rem;
  align-items: center;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
}

.hero-copy { display: grid; gap: 1.6rem; justify-items: start; }

.hero-title {
  font: 900 italic clamp(2.75rem, 12.5vw, 4.6rem)/0.9 var(--font-display);
  letter-spacing: -0.01em;
  text-transform: uppercase;
}
.hero-title .word { display: inline-block; margin-right: 0.18em; }
/* The cyan, white-edged fill of the wordmark. */
.word-hot {
  color: var(--cyan);
  -webkit-text-stroke: 0.05em #fff;
  paint-order: stroke fill;
  text-shadow: 0.06em 0.06em 0 #000, 0 0 0.6em rgba(0, 156, 224, 0.35);
}

.hero-lede {
  max-width: 36ch;
  color: var(--muted);
  font-size: var(--step-1);
  line-height: 1.45;
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 0.8rem 1rem; margin-top: 0.4rem; }

/* The hero turntable: the five containers on an ellipse seen from slightly
   above. One stands in front, two at the sides, two behind. main.js
   (section 11) writes each item's transform, z-index and --shade; the
   nth-child values below are the same layout frozen at the start, so the
   page shows the full set without JavaScript.

   Every number is in units of the item's own box, so nothing is measured:
   x = sin(a) x 140% x (1 - 0.22t), y = -10t %, scale = 1 - 0.4t and
   shade = 1 - 0.6t, where t = (1 - cos a) / 2 is 0 in front, 1 at the back. */
.hero-mark {
  position: relative;
  justify-self: center;
  width: calc(100% + 2 * var(--gutter));
  margin-inline: calc(-1 * var(--gutter));
}
.hero-mark-glow {
  position: absolute;
  inset: 8% 18% 30%;
  border-radius: 40%;
  background: conic-gradient(from 0deg, var(--cyan), var(--yellow), var(--grey), var(--cyan));
  filter: blur(60px);
  opacity: 0.3;
  pointer-events: none;
}

.spin { position: relative; }
.spin-stage {
  position: relative;
  width: min(100%, 85svh);
  aspect-ratio: 10 / 7;
  margin-inline: auto;
  cursor: grab;
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.spin-stage.is-dragging { cursor: grabbing; }

/* The turntable: a cyan ring through the bottles' feet, on a pool of light. */
.spin-floor {
  position: absolute;
  left: 14%;
  right: 14%;
  bottom: 8.6%;
  height: 8.6%;
  border-radius: 50%;
  border: 1px solid rgba(78, 195, 245, 0.42);
  box-shadow: 0 0 22px rgba(0, 156, 224, 0.28), inset 0 0 18px rgba(0, 156, 224, 0.2);
  pointer-events: none;
}
.spin-floor::before {
  content: "";
  position: absolute;
  inset: -180% -22%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(0, 156, 224, 0.2), rgba(0, 156, 224, 0.05) 60%, transparent);
}

.spin-ring { position: absolute; inset: 0; margin: 0; padding: 0; list-style: none; }
.spin-item {
  --shade: 1;
  position: absolute;
  left: 50%;
  bottom: 9%;
  width: 29%;
  aspect-ratio: 357 / 720;
  transform-origin: 50% 100%;
  will-change: transform;
}
.spin-item:not(.is-front) { cursor: pointer; }
/* Contact shadow on the floor, under the bottle and its reflection. */
.spin-item::after {
  content: "";
  position: absolute;
  left: 6%;
  right: 6%;
  bottom: -3.5%;
  z-index: -1;
  height: 7%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(0, 0, 0, 0.85), transparent);
}
.spin-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: 50% 100%;
  filter: brightness(var(--shade));
  pointer-events: none;
  -webkit-user-drag: none;
  /* A wet showroom floor (Chromium and Safari; elsewhere simply no reflection). */
  -webkit-box-reflect: below 0 linear-gradient(transparent 87%, rgba(255, 255, 255, 0.16));
}
.spin-item:nth-child(1) { z-index: 100; transform: translate(-50%, 0) scale(1); }
.spin-item:nth-child(2) { z-index: 65; --shade: 0.793; transform: translate(73%, -3.455%) scale(0.8618); }
.spin-item:nth-child(3) { z-index: 10; --shade: 0.457; transform: translate(15.9%, -9.045%) scale(0.6382); }
.spin-item:nth-child(4) { z-index: 10; --shade: 0.457; transform: translate(-115.9%, -9.045%) scale(0.6382); }
.spin-item:nth-child(5) { z-index: 65; --shade: 0.793; transform: translate(-173%, -3.455%) scale(0.8618); }

/* Name of the bottle in front, and the controls. Needs main.js to work. */
.spin-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: min(100% - 2 * var(--gutter), 30rem);
  margin: 0.4rem auto 0;
}
html:not(.js) .spin-bar { display: none; }
.spin-label { display: grid; gap: 0.15rem; min-width: 0; }
.spin-count {
  color: var(--yellow);
  font: 600 0.72rem/1 var(--font-body);
  letter-spacing: 0.24em;
}
.spin-name {
  overflow: hidden;
  font: 900 italic var(--step-1)/1.1 var(--font-display);
  text-transform: uppercase;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.spin-controls { display: flex; flex-shrink: 0; gap: 0.5rem; }
.spin-btn {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.4s var(--ease-spring), color 0.4s var(--ease-spring), background-color 0.4s var(--ease-spring);
}
.spin-btn:hover { border-color: var(--cyan-hi); color: var(--cyan-hi); background: rgba(0, 156, 224, 0.1); }
.spin-btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.spin-btn .spin-ico-play { fill: currentColor; stroke-width: 1; }
.spin-ico-play,
.spin.is-stopped .spin-ico-pause { display: none; }
.spin.is-stopped .spin-ico-play { display: block; }
/* A phone swipes or taps a bottle instead, which leaves the name room. */
@media (max-width: 479.98px) {
  .spin-prev,
  .spin-next { display: none; }
  .spin-name { font-size: clamp(0.95rem, 4.6vw, 1.15rem); }
}

.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: calc(1.5rem + var(--ticker-drop));
  z-index: 1;
  display: grid;
  justify-items: center;
  gap: 0.6rem;
  color: var(--text);
  font: 600 0.8rem/1 var(--font-body);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  text-decoration: none;
  transform: translateX(-50%);
}
.scroll-cue i {
  position: relative;
  width: 2px;
  height: 44px;
  background: var(--line-strong);
  overflow: hidden;
}
.scroll-cue i::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--yellow);
  transform: translateY(-100%);
}

@media (max-width: 959.98px) {
  .hero-inner { gap: 1.8rem; }
  .hero-copy { gap: 1.2rem; }
  .scroll-cue { display: none; }
}

@media (min-width: 960px) {
  .hero-inner { grid-template-columns: 1fr 1fr; gap: 3rem; }
  .hero-title { font-size: clamp(4rem, 6.6vw, 7.4rem); }
  .hero-mark { width: 100%; max-width: 640px; margin-inline: 0; justify-self: end; }
  /* The bar spans the stage; the longest name, Extreme Cold Degreasing, is
     about 14em wide and must fit beside the three 44px buttons. */
  .spin-bar { width: min(100%, 85svh); }
  .spin-name { font-size: clamp(1rem, 1.85vw, 1.45rem); }
}

/* ---- Hazard ticker ------------------------------------------------------- */

/* Pulled up so the band's centre line lies on the hero's slanted bottom
   edge: at mid-width that edge is half the lean above the hero's end. */
.ticker {
  position: relative;
  z-index: 2;
  margin-top: calc(-1 * (var(--ticker-lean) + var(--ticker-h)) / 2);
  margin-bottom: -1.4rem;
  padding-block: 0.9rem;
  background: var(--yellow);
  color: var(--on-accent);
  transform: rotate(-2deg) scaleX(1.05);
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.ticker-track {
  display: flex;
  align-items: center;
  width: max-content;
}
.ticker span {
  padding-inline: 1.3em;
  font: 900 italic var(--ticker-font)/1 var(--font-display);
  text-transform: uppercase;
  white-space: nowrap;
}
.ticker .sep {
  width: 0.7rem;
  height: 0.7rem;
  flex: none;
  background: var(--on-accent);
  transform: rotate(45deg);
}

/* ---- Sections ------------------------------------------------------------ */

.section {
  max-width: calc(var(--maxw) + 2 * var(--gutter));
  margin-inline: auto;
  padding: clamp(5rem, 11vw, 9.5rem) var(--gutter);
  /* A menu link scrolls to the section's box, whose top is empty padding.
     Pull the landing point down by that padding so the eyebrow and title
     stop just below the header (html scroll-padding-top) instead of far under it. */
  scroll-margin-top: calc(-1 * clamp(5rem, 11vw, 9.5rem));
}

.section-head {
  display: grid;
  gap: 1.1rem;
  max-width: 54rem;
  margin-bottom: clamp(3rem, 7vw, 5.5rem);
}

.section-title {
  font: 900 italic var(--step-3)/0.95 var(--font-display);
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-wrap: balance;
}
.section-title em { color: var(--cyan); font-style: inherit; }

.section-lede {
  max-width: 46ch;
  color: var(--muted);
  font-size: var(--step-1);
  line-height: 1.5;
}

/* ---- Grime wipe ---------------------------------------------------------- */

.wipe {
  --p: 1;
  /* Half the text height times tan(12deg), set by main.js. The clip edge
     between clean and dirty leans by the same 12deg as the blade, so the
     blade always sits exactly on it. */
  --k: 0px;
  position: relative;
  background:
    radial-gradient(ellipse 70% 50% at 50% 50%, rgba(0, 156, 224, 0.08), transparent 70%),
    linear-gradient(var(--bg), var(--bg-2) 18%, var(--bg-2) 82%, var(--bg));
}
.wipe-stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  justify-items: center;
  gap: 1.5rem;
  min-height: 70svh;
  padding: 5rem var(--gutter);
  overflow: hidden;
}
.wipe-text { position: relative; width: 100%; max-width: 1100px; }
.wipe-clean,
.wipe-dirty {
  font: 900 italic clamp(2.6rem, 11vw, 9.5rem)/0.92 var(--font-display);
  letter-spacing: -0.01em;
  text-align: center;
  text-transform: uppercase;
}
.wipe-clean {
  color: transparent;
  background: linear-gradient(100deg, #fff 0 42%, var(--cyan-hi) 50%, #fff 58% 100%) 0 0 / 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  clip-path: polygon(-10% -10%, calc(var(--p) * 100% + var(--k) * 1.2) -10%, calc(var(--p) * 100% - var(--k) * 1.2) 110%, -10% 110%);
}
.wipe-dirty {
  position: absolute;
  inset: 0;
  color: transparent;
  background: url("../img/grime.svg") 0 0 / 360px;
  -webkit-background-clip: text;
  background-clip: text;
  clip-path: polygon(calc(var(--p) * 100% + var(--k) * 1.2) -10%, 110% -10%, 110% 110%, calc(var(--p) * 100% - var(--k) * 1.2) 110%);
}
.wipe-blade {
  position: absolute;
  top: -12%;
  bottom: -12%;
  left: calc(var(--p) * 100%);
  width: 5px;
  margin-left: -2px;
  background: linear-gradient(180deg, transparent, #fff 15%, var(--cyan-hi) 50%, #fff 85%, transparent);
  box-shadow: 0 0 28px 6px rgba(78, 195, 245, 0.55);
  transform: skewX(-12deg);
  opacity: calc(min(var(--p), 1 - var(--p)) * 25);
}

/* ---- News ---------------------------------------------------------------- */

/* An asymmetric bento: from 900px the newest card takes seven of twelve
   columns and the rest five; with three cards the newest spans both rows. */
.news-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.75rem, 3vw, 2.25rem);
}
@media (min-width: 900px) {
  .news-grid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .news-card { grid-column: span 5; }
  .news-card:first-child { grid-column: span 7; }
  .news-grid:has(> :nth-child(3)) > .news-card:first-child { grid-row: span 2; }
  .news-card:first-child .news-title { font-size: clamp(1.9rem, 1.2rem + 1.6vw, 2.7rem); }
}
.news-card {
  --pad: clamp(1.6rem, 3vw, 2.4rem);
  position: relative;
  display: grid;
  gap: 0.9rem;
  align-content: start;
  justify-items: start;
  padding: var(--pad);
  background: linear-gradient(180deg, var(--surface-2), var(--surface) 60%);
  border-radius: var(--r-core);
  box-shadow: var(--bezel);
  overflow: hidden;
  transition: transform 0.8s var(--ease-spring), box-shadow 0.6s var(--ease-spring);
}
.news-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  z-index: 1;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--cyan-hi), transparent);
  transform: scaleX(0);
  transition: transform 0.8s var(--ease-spring);
}
.news-card:hover,
.news-card:focus-within { transform: translateY(-6px); box-shadow: var(--bezel-hot); }
.news-card:hover::before,
.news-card:focus-within::before { transform: scaleX(1); }
.news-card:active { transform: translateY(-3px) scale(0.99); transition-duration: 0.15s; }
/* An optional photo runs edge to edge at the top of the card. */
.news-card img {
  width: calc(100% + 2 * var(--pad));
  max-width: none;
  margin: calc(-1 * var(--pad)) calc(-1 * var(--pad)) 0.4rem;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  transition: transform 1.2s var(--ease-spring);
}
.news-card:hover img { transform: scale(1.03); }

.news-date {
  color: var(--yellow);
  font: 600 0.75rem/1.3 var(--font-body);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.news-title {
  font: 900 italic clamp(1.5rem, 1.2rem + 1.2vw, 2.1rem)/1.05 var(--font-display);
  text-transform: uppercase;
  text-wrap: balance;
}
.news-title a { color: var(--text); text-decoration: none; }
/* The title link covers the whole card. */
.news-title a::after { content: ""; position: absolute; inset: 0; }
.news-intro { color: var(--muted); }
.news-card .text-link { margin-top: auto; }
.news-foot { margin-top: 2.4rem; }

/* ---- News page ----------------------------------------------------------
   A card grid: the two newest posts run large, two to a row, and older ones
   three to a row. Each card holds the whole post, so there is no "read more". */

.news-page { padding-top: calc(var(--header-h) + clamp(3rem, 7vw, 5rem)); }

.news-head {
  display: grid;
  gap: 1.6rem 3rem;
  margin-bottom: clamp(2.5rem, 6vw, 4rem);
}
.news-head .section-head { margin-bottom: 0; }

/* Topic filter. main.js builds the buttons from the posts' own topic labels. */
.news-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  justify-self: start;
  padding: 6px;
  background: rgba(255, 255, 255, 0.025);
  border-radius: calc(20px + 6px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
}
.news-filter[hidden] { display: none; }
.news-filter button {
  min-height: 40px;
  padding: 0.5em 1.15em;
  background: none;
  border: 0;
  border-radius: 999px;
  color: var(--muted);
  font: 600 0.72rem/1 var(--font-body);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.5s var(--ease-spring), background-color 0.5s var(--ease-spring), transform 0.5s var(--ease-spring);
}
.news-filter button:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }
.news-filter button:active { transform: scale(0.96); }
.news-filter button[aria-pressed="true"] {
  background: var(--cyan);
  color: var(--on-accent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.news-filter-count {
  margin-left: 0.6em;
  font-variant-numeric: tabular-nums;
  opacity: 0.6;
}
/* Topics without posts yet stay in the list, a step quieter. */
.news-filter button.is-empty:not([aria-pressed="true"]) { color: rgba(162, 164, 165, 0.6); }

@media (min-width: 1040px) {
  .news-head { grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
  /* Wraps to a second row, as in the mock-up, rather than crowd the title. */
  .news-filter { max-width: 34rem; justify-self: end; }
}

.post-list { display: grid; gap: clamp(1.75rem, 3vw, 2.25rem); }
.post {
  --pad: clamp(1.3rem, 2.4vw, 1.9rem);
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: var(--pad);
  background: linear-gradient(180deg, var(--surface-2), var(--surface) 50%);
  border-radius: var(--r-core);
  box-shadow: var(--bezel);
  overflow: hidden;
  transition: box-shadow 0.6s var(--ease-spring);
}
.post[hidden] { display: none; }
/* The cyan edge of the homepage cards, drawn on hover and for a linked post. */
.post::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  z-index: 1;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--cyan-hi), transparent);
  transform: scaleX(0);
  transition: transform 0.8s var(--ease-spring);
}
.post:hover::before,
.post:target::before { transform: scaleX(1); }
/* Arriving from a homepage card (news.html#slug): mark the post asked for. */
.post:hover,
.post:target { box-shadow: var(--bezel-hot); }

.post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem 1rem;
}
.post-topic {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  color: var(--yellow);
  font: 600 0.75rem/1.3 var(--font-body);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.post-topic::before {
  content: "";
  width: 0.5em;
  height: 0.5em;
  background: currentColor;
  transform: skewX(-20deg);
}
.post-meta .news-date { color: var(--muted); }

.post-title {
  font: 900 italic clamp(1.4rem, 1.2rem + 0.8vw, 1.75rem)/1.02 var(--font-display);
  text-transform: uppercase;
  text-wrap: balance;
  transition: color 0.5s var(--ease-spring);
}
.post:hover .post-title { color: var(--cyan-hi); }

.post-media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  /* Concentric with the card: its radius less the card's padding, floored. */
  border-radius: max(0.6rem, calc(var(--r-core) - var(--pad)));
  background: var(--bg-2);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
}
.post-media > img { width: 100%; height: 100%; object-fit: cover; }
.post figcaption { margin-top: 0.5rem; color: var(--muted); font-size: var(--step--1); }

/* A short label pinned to the photo's corner. */
.post-badge {
  position: absolute;
  top: 0.7rem;
  right: 0.7rem;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.6em 0.95em;
  background: rgba(7, 7, 8, 0.82);
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
  color: var(--text);
  font: 600 0.68rem/1 var(--font-body);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.post-badge svg { width: 1.1em; height: 1.1em; fill: var(--yellow); }
.post-badge-dot { width: 0.55em; height: 0.55em; border-radius: 50%; background: var(--yellow); }

.post-intro { font-weight: 600; line-height: 1.5; }
.post > p:not(.post-intro) { color: var(--muted); }

.post-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem 1.2rem;
  margin-top: auto;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.post-byline {
  display: inline-flex;
  align-items: center;
  gap: 0.65em;
  color: var(--muted);
  font: 600 0.72rem/1 var(--font-body);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.post-byline img { width: 28px; }
.post-link { font-size: 0.78rem; }
.post:hover .post-link::after,
.post-link:focus-visible::after { transform: translateX(4px) rotate(45deg); }

@media (min-width: 700px) {
  .post-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1040px) {
  .post-list { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .post { grid-column: span 2; }
  /* The first two posts the topic filter still shows run large. The plain
     nth-child rule is the fallback for browsers without "of S". */
  .post:nth-child(-n + 2) { grid-column: span 3; }
  .post:nth-child(-n + 2 of :not([hidden])) { grid-column: span 3; }
  .post:nth-child(-n + 2) .post-title { font-size: clamp(1.9rem, 1rem + 1.6vw, 2.6rem); }
  .post:nth-child(-n + 2 of :not([hidden])) .post-title { font-size: clamp(1.9rem, 1rem + 1.6vw, 2.6rem); }
}

/* A topic with no posts yet: said plainly, with the way back. */
.news-empty {
  display: grid;
  justify-items: start;
  gap: 0.8rem;
  padding: clamp(2rem, 5vw, 3.5rem);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-core);
}
.news-empty[hidden] { display: none; }
.news-empty-title {
  font: 900 italic var(--step-2)/1.05 var(--font-display);
  text-transform: uppercase;
}
.news-empty-text { max-width: 46ch; color: var(--muted); }
.news-empty-reset { margin-top: 0.6rem; }

.news-actions { margin-block: clamp(2.5rem, 5vw, 3.5rem); }

/* Follow band: TikTok and the shop, where Dirtybird is outside this site. */
.news-follow {
  display: grid;
  gap: 1.5rem;
  align-items: center;
  padding: clamp(1.5rem, 4vw, 2.6rem);
  background:
    radial-gradient(ellipse 60% 120% at 0% 0%, rgba(0, 156, 224, 0.12), transparent 70%),
    linear-gradient(100deg, var(--surface-2), var(--bg-2) 75%);
  border-radius: calc(var(--r-core) + 0.5rem);
  box-shadow: var(--bezel);
}
.news-follow-icon {
  display: grid;
  place-items: center;
  width: 3.4rem;
  height: 3.4rem;
  background: rgba(0, 156, 224, 0.12);
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(78, 195, 245, 0.22);
  color: var(--cyan-hi);
}
.news-follow-icon svg { width: 1.5rem; height: 1.5rem; fill: currentColor; }
.news-follow-copy { display: grid; gap: 0.55rem; justify-items: start; }
.news-follow-title {
  font: 900 italic var(--step-2)/1.05 var(--font-display);
  text-transform: uppercase;
  text-wrap: balance;
}
.news-follow-copy > p:last-child { max-width: 52ch; color: var(--muted); }
.news-follow-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; }

@media (min-width: 900px) {
  .news-follow { grid-template-columns: auto minmax(0, 1fr) auto; gap: 1.6rem 2rem; }
  .news-follow-icon { align-self: start; }
}

/* ---- Who we supply: the logo as a hub, lines out to each group ------------
   main.js draws the lines into .reach-lines from the laid-out positions. On a
   phone the hub sits top left and the lines run down a trunk beside the list;
   from 900px the hub is centred and the lines fan out to four columns. */

.reach { position: relative; }
.reach-hub {
  position: relative;
  z-index: 1;
  width: 96px;
}
.reach-hub img { width: 100%; filter: drop-shadow(0 16px 30px rgba(0, 0, 0, 0.6)); }
.reach-hub::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 2px solid var(--cyan);
  border-radius: 50%;
  opacity: 0;
}
.reach-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.reach-line { fill: none; stroke: rgba(78, 195, 245, 0.32); stroke-width: 2; }
.reach-flow {
  fill: none;
  stroke: var(--cyan-hi);
  stroke-width: 4;
  stroke-linecap: round;
  filter: drop-shadow(0 0 6px var(--cyan));
  opacity: 0;
}
.reach-dot { fill: var(--yellow); }

.segment-grid {
  display: grid;
  gap: 2.5rem;
  margin-top: 2.5rem;
}
html.js .segment-grid { padding-left: 5rem; }
.segment {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 0.8rem;
  align-content: start;
  justify-items: start;
  padding: clamp(1.4rem, 2.4vw, 1.9rem);
  background: linear-gradient(180deg, var(--surface-2), var(--surface) 70%);
  border-radius: var(--r-core);
  box-shadow: var(--bezel);
}

@media (min-width: 900px) {
  .reach-hub { width: 128px; margin-inline: auto; }
  .segment-grid,
  html.js .segment-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: clamp(1.4rem, 2.4vw, 2.2rem);
    margin-top: clamp(7.5rem, 12vw, 10.5rem);
    padding-left: 0;
  }
  .segment { justify-items: center; text-align: center; }
}
.segment-icon {
  width: 60px;
  height: 60px;
  fill: none;
  stroke: var(--cyan-hi);
  stroke-width: 1.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.segment h3 { font: 900 italic var(--step-1)/1.1 var(--font-display); text-transform: uppercase; }
.segment p { max-width: 26ch; color: var(--muted); text-wrap: balance; }

/* ---- The story: the brand and the people in it ---------------------------
   A preview of history.html, right under the results. Two people cards, each
   wearing the yellow plate of its stage (a label, not a link), then the
   company in one band, then the road book on to the stages. From 900px the
   two cards stand side by side and the band runs under both. */

.story-grid { display: grid; gap: clamp(1.75rem, 3vw, 2.25rem); }

.person {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  overflow: hidden;
  border-radius: var(--r-core);
  background: linear-gradient(180deg, var(--surface-2), var(--surface) 70%);
  box-shadow: var(--bezel);
}
.person-photo {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--bg-2);
}
.person-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Keep the rider of 919 in the frame when the photo is cut narrower. */
.person--mx .person-photo img { object-position: 68% 40%; }
.person--rx .person-photo img { object-position: 50% 45%; }
.person-photo figcaption {
  position: absolute;
  inset: auto 0 0;
  display: grid;
  gap: 0.35rem;
  padding: 4rem 1.3rem 1.1rem;
  background: linear-gradient(180deg, transparent, rgba(7, 7, 8, 0.62) 45%, rgba(7, 7, 8, 0.9));
}
/* The stage plate, as on the history page's route line. */
.person-plate {
  position: absolute;
  top: 1rem;
  left: 1rem;
  display: grid;
  place-items: center;
  width: 3.4rem;
  height: 3.4rem;
  border-radius: 50%;
  background: var(--yellow);
  box-shadow: 0 0 0 5px rgba(7, 7, 8, 0.55), 0 0 24px rgba(255, 224, 0, 0.35);
  color: var(--on-accent);
  font: 900 italic 1rem/1 var(--font-display);
}
.person-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.9rem;
  padding: clamp(1.5rem, 3vw, 2.2rem);
}
.person-kicker {
  color: var(--yellow);
  font: 600 0.75rem/1.35 var(--font-body);
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.person-name {
  font: 900 italic clamp(1.8rem, 1.3rem + 1.8vw, 2.7rem)/0.98 var(--font-display);
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-wrap: balance;
}
.person-body > p:not(.person-kicker) { max-width: 52ch; color: var(--muted); }
/* Merits as pills, pushed to the card's foot so the two cards line up. */
.person-merits {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.5rem;
}
.person-merits li {
  padding: 0.55em 1em;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.04);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
  font: 600 0.78rem/1.2 var(--font-body);
  letter-spacing: 0.03em;
}
.person-merits b { margin-right: 0.2em; color: var(--yellow); font: 900 italic 1.05em/1 var(--font-display); }

/* The company band. */
.story-company {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
  padding: clamp(1.5rem, 4vw, 2.8rem);
  border-radius: var(--r-core);
  background:
    radial-gradient(ellipse 55% 120% at 0% 0%, rgba(0, 156, 224, 0.14), transparent 70%),
    linear-gradient(100deg, var(--surface-2), var(--bg-2) 75%);
  box-shadow: var(--bezel);
}
.story-badge { width: min(56vw, 15rem); justify-self: center; }
/* The badge: Sweden in cyan, a yellow pin on Karlstad, the year beside it. */
.story-map {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  filter: drop-shadow(0 26px 40px rgba(0, 0, 0, 0.7));
}
.story-map-land {
  fill: url(#story-map-land);
  stroke: var(--cyan-hi);
  stroke-width: 1.4;
  stroke-linejoin: round;
}
.story-map-ripple { fill: none; stroke: var(--yellow); stroke-width: 1; }
.story-map-pin-head { fill: var(--yellow); stroke: var(--on-accent); stroke-width: 0.8; }
.story-map-pin-dot { fill: var(--on-accent); }
.story-map-city {
  fill: var(--text);
  font: 800 italic 22px var(--font-display);
  letter-spacing: 0.14em;
}
.story-map-year {
  fill: var(--yellow);
  font: 900 italic 84px var(--font-display);
  letter-spacing: 0.01em;
}
.story-company-copy { display: grid; gap: 1rem; justify-items: start; }
/* The motto carries the band on its own, so it runs large. */
.story-motto {
  max-width: 18ch;
  font: 900 italic clamp(1.8rem, 1.2rem + 2.6vw, 3.2rem)/1 var(--font-display);
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-wrap: balance;
}
.story-motto em { color: var(--cyan); font-style: inherit; }

.story-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.4rem 2rem;
  margin-top: clamp(2rem, 4vw, 3rem);
}

@media (min-width: 900px) {
  .story-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .person-photo { aspect-ratio: 5 / 4; }
  .story-company { grid-column: 1 / -1; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); }
  .story-badge { width: 100%; }
}

/* Fact tiles: surface plates with a yellow edge, like the label stickers. */
/* Two by two: four tiles never leave one stranded on a row of its own. */
.facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.1rem;
  width: 100%;
  margin-top: 0.4rem;
}
.facts > div {
  position: relative;
  display: grid;
  gap: 0.35rem;
  align-content: start;
  padding: 1.15rem 1.3rem 1.2rem 1.5rem;
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  border-radius: 1.1rem;
  box-shadow: var(--bezel);
  overflow: hidden;
  transition: transform 0.7s var(--ease-spring), box-shadow 0.6s var(--ease-spring);
}
/* The yellow edge of the label stickers, as a short lit bar. */
.facts > div::before {
  content: "";
  position: absolute;
  top: 1.2rem;
  bottom: 1.2rem;
  left: 0;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--yellow);
  box-shadow: 0 0 12px rgba(255, 224, 0, 0.5);
}
.facts > div:hover { transform: translateY(-4px); }
.facts dt { color: var(--muted); font: 600 0.7rem/1.4 var(--font-body); letter-spacing: 0.18em; text-transform: uppercase; }
.facts dd { font: 800 italic clamp(1.1rem, 1rem + 0.5vw, 1.35rem)/1.2 var(--font-display); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.facts a { color: var(--cyan-hi); text-decoration: none; }
.facts a:hover { color: #fff; }

/* ---- Rally: a photo bento ------------------------------------------------
   From 1040px the first photo takes eight of twelve columns and two rows,
   the rest four. From 700px two to a row, the first full width. Below that
   the photos run in a row that scrolls sideways and snaps, so six photos do
   not stretch a phone page; the next one peeks in from the edge. */
.rally-note { max-width: 60ch; color: var(--muted); }

.rally-grid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(84%, 26rem);
  gap: calc(2 * var(--tray) + 0.6rem);
  /* Out to the page edge, with the tray's room top and bottom. */
  margin-inline: calc(-1 * var(--gutter));
  padding: calc(var(--tray) + 2px) var(--gutter);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gutter);
  scrollbar-width: none;
}
.rally-grid::-webkit-scrollbar { display: none; }

.rally-shot {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--r-core);
  background: var(--bg-2);
  box-shadow: var(--bezel);
  scroll-snap-align: start;
}
/* Out of flow, so a photo never sets the height of its grid row. */
.rally-shot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.rally-shot figcaption {
  position: absolute;
  inset: auto 0 0;
  display: grid;
  gap: 0.35rem;
  padding: 4rem 1.3rem 1.15rem;
  background: linear-gradient(180deg, transparent, rgba(7, 7, 8, 0.62) 45%, rgba(7, 7, 8, 0.9));
}
.rally-event {
  color: var(--yellow);
  font: 600 0.75rem/1.3 var(--font-body);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.rally-credit { color: rgba(244, 244, 244, 0.72); font-size: 0.78rem; }

@media (min-width: 700px) {
  .rally-grid {
    grid-auto-flow: row;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(1.75rem, 3vw, 2.25rem);
    margin-inline: 0;
    padding: 0;
    overflow: visible;
  }
  .rally-shot { aspect-ratio: 3 / 2; }
  .rally-shot:first-child { grid-column: 1 / -1; }
}
@media (min-width: 1040px) {
  .rally-grid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .rally-shot { grid-column: span 4; }
  .rally-shot:first-child { grid-column: span 8; grid-row: span 2; aspect-ratio: auto; }
  .rally-shot:first-child figcaption { padding: 6rem 2rem 1.7rem; }
  .rally-shot:first-child .rally-event { font-size: 0.85rem; }
  /* The big photo and two beside it fill two rows; after that, three to a
     row. A last photo that would leave a hole takes the rest of its row. */
  .rally-shot:last-child:nth-child(3n + 5) { grid-column: span 8; aspect-ratio: auto; }
  .rally-shot:last-child:nth-child(3n + 4) { grid-column: 1 / -1; aspect-ratio: 21 / 9; }
}
@media (min-width: 700px) and (max-width: 1039.98px) {
  .rally-shot:last-child:nth-child(even) { grid-column: 1 / -1; }
}

/* ---- Results: before/after slider -----------------------------------------
   Two photos in one frame. Without JavaScript they sit side by side. With it
   they are stacked: the "before" photo is clipped at --pos and a blade marks
   the edge. The control is a real range input laid over the frame, so drag,
   touch and the arrow keys all work; main.js (section 9) only copies its
   value into --pos. */

.results-grid { display: grid; gap: clamp(1.75rem, 3vw, 2.25rem); }

.ba { display: grid; gap: 0.9rem; align-content: start; }
.ba figcaption { color: var(--muted); font-size: var(--step--1); }
.ba-frame {
  --pos: 50%;
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px;
  overflow: hidden;
  border-radius: var(--r-core);
  background: var(--bg-2);
  box-shadow: var(--bezel);
}
.ba-img { width: 100%; height: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
.ba-before { order: -1; }
.ba-tag {
  position: absolute;
  top: 1rem;
  left: 1rem;
  padding: 0.55em 0.95em;
  border-radius: 999px;
  background: rgba(7, 7, 8, 0.78);
  box-shadow: inset 0 0 0 1px rgba(255, 224, 0, 0.25);
  color: var(--yellow);
  font: 600 0.7rem/1 var(--font-body);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  pointer-events: none;
}
.ba-tag--after { left: calc(50% + 1rem); }
.ba-blade,
.ba-range { display: none; }

html.js .ba-frame { display: block; aspect-ratio: 4 / 5; }
html.js .ba-img { position: absolute; inset: 0; aspect-ratio: auto; }
html.js .ba-before { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }
html.js .ba-tag--after { left: auto; right: 1rem; }
html.js .ba-blade {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--pos);
  display: block;
  width: 4px;
  margin-left: -2px;
  background: linear-gradient(180deg, #fff, var(--cyan-hi) 50%, #fff);
  box-shadow: 0 0 28px 6px rgba(78, 195, 245, 0.5);
  pointer-events: none;
}
/* The grip: a cyan disc with a chevron pointing each way. */
.ba-blade i {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 54px;
  height: 54px;
  margin: -27px 0 0 -27px;
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: inset 0 0 0 2px #fff, 0 12px 30px rgba(0, 0, 0, 0.55);
  transition: transform 0.5s var(--ease-spring);
}
.ba-blade i::before,
.ba-blade i::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  margin-top: -5px;
  border-top: 2.5px solid var(--on-accent);
  border-left: 2.5px solid var(--on-accent);
}
.ba-blade i::before { left: 13px; transform: rotate(-45deg); }
.ba-blade i::after { right: 13px; transform: rotate(135deg); }
.ba-frame:hover .ba-blade i { transform: scale(1.08); }
html.js .ba-range {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
  -webkit-appearance: none;
  appearance: none;
  background: none;
  opacity: 0;
  cursor: ew-resize;
  /* A sideways drag moves the blade; an up-down swipe still scrolls the page. */
  touch-action: pan-y;
}
.ba-range::-webkit-slider-thumb { -webkit-appearance: none; width: 4px; height: 100vh; }
.ba-range::-moz-range-thumb { width: 4px; height: 100vh; border: 0; }
/* The input is invisible, so the frame carries its focus ring. */
.ba-frame:has(.ba-range:focus-visible) { outline: 3px solid var(--cyan-hi); outline-offset: 4px; }

.results-side { display: grid; gap: clamp(1.75rem, 3vw, 2.25rem); align-content: start; }
.results-note {
  display: grid;
  gap: 0.9rem;
  justify-items: start;
  padding: clamp(1.5rem, 3vw, 2.2rem);
  border-radius: var(--r-core);
  background: linear-gradient(180deg, var(--surface-2), var(--surface) 70%);
  box-shadow: var(--bezel);
}
.results-note h3 { font: 900 italic var(--step-2)/1.05 var(--font-display); text-transform: uppercase; text-wrap: balance; }
.results-note p { color: var(--muted); max-width: 44ch; }
.results-note .btn { margin-top: 0.4rem; }
.results-shot {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--r-core);
  background: var(--bg-2);
  box-shadow: var(--bezel);
}
.results-shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 38%; }
.results-shot figcaption {
  position: absolute;
  inset: auto 0 0;
  padding: 3.5rem 1.3rem 1.15rem;
  background: linear-gradient(180deg, transparent, rgba(7, 7, 8, 0.85));
}

/* A landscape pair (the car) spans the whole row under the first pair. */
.ba--wide { grid-column: 1 / -1; }
.ba--wide .ba-img { aspect-ratio: 4 / 3; }
html.js .ba--wide .ba-frame { aspect-ratio: 4 / 3; }
@media (min-width: 900px) {
  html.js .ba--wide .ba-frame { aspect-ratio: 16 / 9; }
}

@media (min-width: 900px) {
  .results-grid { grid-template-columns: minmax(0, 34rem) minmax(0, 1fr); }
  /* The foam photo takes whatever height the slider leaves beside the note. */
  .results-side { grid-template-rows: auto minmax(0, 1fr); align-content: stretch; }
  .results-shot { aspect-ratio: auto; min-height: 16rem; margin-bottom: calc(0.9rem + var(--step--1) * 1.6); }
}

/* ---- How it works: the wash guide ----------------------------------------- */

.how-grid { display: grid; gap: clamp(2rem, 5vw, 4rem); }
.how-photo {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--r-core);
  background: var(--bg-2);
  box-shadow: var(--bezel);
}
.how-photo img { width: 100%; height: 100%; object-fit: cover; }

.steps { display: grid; gap: 1.1rem; margin: 0; padding: 0; list-style: none; }
.step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(1rem, 2.4vw, 1.5rem);
  align-items: start;
  padding: clamp(1.2rem, 2.4vw, 1.6rem);
  border-radius: 1.4rem;
  background: var(--surface);
  box-shadow: var(--bezel);
}
/* As in the printed guide: each step has its icon, and its number rides on
   the icon's corner as a small yellow disc. */
.step-mark { position: relative; display: block; }
.step-num {
  position: absolute;
  top: -0.45rem;
  left: -0.45rem;
  display: grid;
  place-items: center;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  background: var(--yellow);
  box-shadow: 0 0 0 3px var(--surface);
  color: var(--on-accent);
  font: 900 italic 0.95rem/1 var(--font-display);
}
.step h3 { font: 900 italic var(--step-1)/1.1 var(--font-display); text-transform: uppercase; text-wrap: balance; }
.step p { margin-top: 0.4rem; color: var(--muted); }

.how-notes { display: grid; gap: 1.1rem; margin-top: 1.1rem; }
.how-note {
  display: grid;
  gap: 0.7rem;
  align-content: start;
  padding: clamp(1.2rem, 2.4vw, 1.6rem);
  border-radius: 1.4rem;
  box-shadow: inset 0 0 0 1px var(--line-strong);
  color: var(--muted);
}
.how-note h3 { display: flex; align-items: center; gap: 0.55em; color: var(--text); font: 900 italic 1.05rem/1.1 var(--font-display); letter-spacing: 0.02em; text-transform: uppercase; }
.how-note ul { display: grid; gap: 0.4rem; }
.how-note li { position: relative; padding-left: 1.3rem; }
.how-note li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--yellow);
}
.how-note--warn { background: rgba(255, 224, 0, 0.05); box-shadow: inset 0 0 0 1px rgba(255, 224, 0, 0.4); color: var(--text); }
.how-note--warn h3 { color: var(--yellow); }

@media (min-width: 700px) {
  .how-notes { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
}
@media (min-width: 960px) {
  .how-grid { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: start; }
  .how-photo { position: sticky; top: 2rem; aspect-ratio: 4 / 5; max-height: calc(100svh - 4rem); }
}

/* ---- The range ------------------------------------------------------------
   Five containers. A sideways snap-scroll row on a phone, like the rally
   photos; three to a row from 700px and all five in one row from 1040px. */

.range-grid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(74%, 19rem);
  gap: calc(2 * var(--tray) + 0.6rem);
  margin-inline: calc(-1 * var(--gutter));
  padding: calc(var(--tray) + 2px) var(--gutter);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gutter);
  scrollbar-width: none;
}
.range-grid::-webkit-scrollbar { display: none; }
.range-card {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  overflow: hidden;
  border-radius: var(--r-core);
  background: linear-gradient(180deg, var(--surface-2), var(--surface) 70%);
  box-shadow: var(--bezel);
  scroll-snap-align: start;
  transition: transform 0.8s var(--ease-spring), box-shadow 0.6s var(--ease-spring);
}
.range-card img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; }
.range-body {
  display: grid;
  gap: 0.6rem;
  align-content: start;
  justify-items: start;
  padding: 1.2rem 1.25rem 1.4rem;
}
.range-body h3 { font: 900 italic 1.2rem/1.05 var(--font-display); text-transform: uppercase; text-wrap: balance; }
.range-body p { color: var(--muted); font-size: 0.95rem; line-height: 1.45; }
.range-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  margin-top: 0.3rem;
  color: var(--yellow);
  font: 600 0.68rem/1.3 var(--font-body);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.range-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.2rem 2rem;
  margin-top: clamp(2rem, 4vw, 3rem);
}
.range-foot p { max-width: 40ch; color: var(--muted); }

@media (min-width: 700px) {
  .range-grid {
    grid-auto-flow: row;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(1.75rem, 3vw, 2.25rem);
    margin-inline: 0;
    padding: 0;
    overflow: visible;
  }
}
@media (min-width: 1040px) {
  .range-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: clamp(1.25rem, 1.8vw, 1.6rem); }
  .range-body h3 { font-size: 1.08rem; }
  .range-body p { font-size: 0.9rem; }
}

/* ---- History page: four special stages -------------------------------------
   A route line runs down the left edge; each stage hangs its "SS" plate on
   it. main.js (section 10) fills the line as the page scrolls. Without it the
   line is simply drawn in full. */

.history-head { padding-top: calc(var(--header-h) + clamp(3rem, 7vw, 5rem)); padding-bottom: clamp(2rem, 5vw, 3.5rem); }
.history-head .section-head { margin-bottom: clamp(2rem, 4vw, 3rem); }

/* The road book: jump links to the stages. */
.roadbook ol { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
.roadbook a {
  display: inline-flex;
  align-items: center;
  gap: 0.8em;
  min-height: 44px;
  padding: 0.3rem 1.2rem 0.3rem 0.35rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.04);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
  color: var(--text);
  font: 600 0.76rem/1 var(--font-body);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 0.5s var(--ease-spring), box-shadow 0.5s var(--ease-spring), color 0.5s var(--ease-spring);
}
.roadbook a span {
  display: grid;
  place-items: center;
  min-width: 3.1em;
  height: 2.6em;
  padding-inline: 0.6em;
  border-radius: 999px;
  background: var(--cyan);
  color: var(--on-accent);
  font: 900 italic 0.9rem/1 var(--font-display);
  letter-spacing: 0.02em;
}
.roadbook a:hover { background: rgba(0, 156, 224, 0.12); box-shadow: inset 0 0 0 1px rgba(78, 195, 245, 0.6); color: var(--cyan-hi); }

.stages {
  --rail: 1.5rem;
  position: relative;
  max-width: calc(var(--maxw) + 2 * var(--gutter));
  margin-inline: auto;
  padding: 0 var(--gutter) clamp(4rem, 9vw, 7rem);
}
.stages-route {
  position: absolute;
  top: 0;
  bottom: clamp(4rem, 9vw, 7rem);
  left: calc(var(--gutter) + var(--rail) - 1px);
  width: 2px;
  background: var(--line);
}
.stages-route i {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, var(--yellow), var(--cyan-hi));
  box-shadow: 0 0 14px rgba(78, 195, 245, 0.5);
  transform: scaleY(var(--route, 1));
  transform-origin: 50% 0;
}

.stage {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 3rem);
  padding: clamp(3rem, 7vw, 5.5rem) 0 clamp(3rem, 7vw, 5.5rem) calc(2 * var(--rail) + 1rem);
  /* Lands the stage clear of the header when a road book link jumps to it. */
  scroll-margin-top: 1rem;
}
/* The plate on the route line. Yellow: a label, never a link. */
.stage-no {
  position: absolute;
  top: clamp(3rem, 7vw, 5.5rem);
  left: 0;
  display: grid;
  place-items: center;
  width: calc(2 * var(--rail));
  height: calc(2 * var(--rail));
  border-radius: 50%;
  background: var(--yellow);
  box-shadow: 0 0 0 6px var(--bg), 0 0 24px rgba(255, 224, 0, 0.35);
  color: var(--on-accent);
  font: 900 italic 0.95rem/1 var(--font-display);
}

.stage-intro { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }
.stage-copy { display: grid; gap: 1.2rem; justify-items: start; }
.stage-copy > p { max-width: 52ch; color: var(--muted); font-size: var(--step-1); line-height: 1.5; }
.stage-copy > .stage-kicker {
  color: var(--yellow);
  font: 600 0.78rem/1.3 var(--font-body);
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.stage-copy > .stage-motto {
  max-width: none;
  margin-top: 0.6rem;
  color: var(--text);
  font: 900 italic var(--step-2)/1.1 var(--font-display);
  text-transform: uppercase;
}
.stage-motto em { color: var(--cyan); font-style: inherit; }
.stage-actions { display: flex; flex-wrap: wrap; gap: 0.8rem 1rem; margin-top: 0.6rem; }

.stage-photo {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--r-core);
  background: var(--bg-2);
  box-shadow: var(--bezel);
}
.stage-photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 60%; }

/* Merits: a big figure, then what it stands for. */
.laurels { display: grid; gap: 1.1rem; margin: 0; padding: 0; list-style: none; }
.laurel {
  display: grid;
  grid-template-columns: 4.4rem minmax(0, 1fr);
  gap: 0.2rem 1rem;
  align-items: center;
  padding: 1.15rem 1.3rem;
  border-radius: 1.4rem;
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  box-shadow: var(--bezel);
}
.laurel-num {
  grid-row: span 2;
  color: var(--yellow);
  font: 900 italic clamp(1.9rem, 1.5rem + 1.4vw, 2.6rem)/1 var(--font-display);
  text-align: center;
  text-shadow: 0 0 24px rgba(255, 224, 0, 0.35);
}
.laurel-num .ico { width: 2.6rem; height: 2.6rem; margin-inline: auto; filter: drop-shadow(0 0 10px rgba(255, 224, 0, 0.3)); }
.laurel h3 { align-self: end; font: 900 italic 1.1rem/1.1 var(--font-display); text-transform: uppercase; text-wrap: balance; }
.laurel p { align-self: start; color: var(--muted); font-size: 0.95rem; line-height: 1.4; }

.stage-callout {
  display: grid;
  gap: 0.9rem;
  padding: clamp(1.5rem, 4vw, 2.6rem);
  border-radius: var(--r-core);
  background:
    radial-gradient(ellipse 60% 120% at 0% 0%, rgba(255, 224, 0, 0.1), transparent 70%),
    linear-gradient(100deg, var(--surface-2), var(--bg-2) 75%);
  box-shadow: var(--bezel);
}
.stage-callout-title { font: 900 italic var(--step-2)/1.05 var(--font-display); text-transform: uppercase; text-wrap: balance; }
.stage-callout-title em { color: var(--yellow); font-style: inherit; }
.stage-callout p:not(.stage-callout-title) { max-width: 68ch; color: var(--muted); }

.stage .facts { margin-top: 0; }

@media (min-width: 960px) {
  .stages { --rail: 1.9rem; }
  .stage { padding-left: calc(2 * var(--rail) + 2.5rem); }
  .stage-no { font-size: 1.15rem; }
  .laurel { grid-template-columns: 5.6rem minmax(0, 1fr); }
  .stage-intro { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .stage-intro--media { align-items: center; }
  .stage-photo { aspect-ratio: 4 / 5; }
}

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

/* Each question is a bezelled card; the plus sits in a circle of its own
   and turns into a cross when the answer opens. */
.faq-list { display: grid; gap: 1.1rem; max-width: 54rem; }
.faq-list details {
  padding-inline: clamp(1.2rem, 3vw, 1.8rem);
  background: var(--surface);
  border-radius: 1.4rem;
  box-shadow: var(--bezel);
  transition: background-color 0.6s var(--ease-spring), box-shadow 0.6s var(--ease-spring);
}
.faq-list details[open] { background: var(--surface-2); }
.faq-list summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.25rem 0;
  font: 600 var(--step-1)/1.3 var(--font-body);
  list-style: none;
  cursor: pointer;
  transition: color 0.5s var(--ease-spring);
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary:hover { color: var(--cyan-hi); }
.faq-list summary:active { color: var(--cyan); }
.faq-list summary::after {
  content: "";
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background:
    linear-gradient(currentColor, currentColor) center / 13px 1.5px no-repeat,
    linear-gradient(currentColor, currentColor) center / 1.5px 13px no-repeat,
    rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  transition: transform 0.7s var(--ease-spring), color 0.5s var(--ease-spring);
}
.faq-list details[open] summary::after { transform: rotate(135deg); color: var(--yellow); }
.faq-answer { padding-bottom: 1.5rem; color: var(--muted); max-width: 46rem; }

/* ---- Closing CTA --------------------------------------------------------- */

.cta {
  position: relative;
  padding: clamp(6rem, 15vw, 12rem) var(--gutter);
  overflow: hidden;
  text-align: center;
}
/* The disc, huge and faint, behind the words. */
.cta::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(120vw, 1100px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: conic-gradient(from 60deg, var(--yellow) 0 24%, var(--grey) 24% 100%);
  opacity: 0.07;
  transform: translate(-50%, -50%);
}
.cta-inner { position: relative; display: grid; gap: 2.6rem; justify-items: center; }
.cta-title {
  font: 900 italic clamp(2.8rem, 10vw, 8.6rem)/0.92 var(--font-display);
  letter-spacing: -0.01em;
  text-transform: uppercase;
}
.cta-title span {
  display: inline-block;
  color: transparent;
  background: linear-gradient(110deg, var(--cyan) 0 38%, #fff 47%, var(--cyan-hi) 52%, var(--cyan) 62% 100%) 0 0 / 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  padding-right: 0.08em;
}

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

/* The footer is one large bezelled panel, inset from the page edges. */
.site-footer {
  margin: 0 var(--gutter) var(--gutter);
  padding: clamp(2.5rem, 6vw, 4.5rem) clamp(1.4rem, 5vw, 4rem) 1.75rem;
  background:
    radial-gradient(ellipse 50% 80% at 100% 0%, rgba(0, 156, 224, 0.08), transparent 70%),
    var(--bg-2);
  border-radius: 2rem;
  box-shadow: var(--bezel);
}
.footer-inner {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  gap: 2.5rem 2rem;
  max-width: var(--maxw);
  margin-inline: auto;
}
.footer-brand img { width: 64px; margin-bottom: 1rem; }
.footer-brand .brand-word { font-size: 1.6rem; }
.footer-tag {
  margin-top: 0.4rem;
  color: var(--yellow);
  font: 600 0.72rem/1.3 var(--font-body);
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.footer-col { display: grid; gap: 0.8rem; align-content: start; font-style: normal; color: var(--muted); }
.footer-head { color: var(--text); font: 900 italic 1rem/1.2 var(--font-display); text-transform: uppercase; letter-spacing: 0.04em; }
.footer-col a { color: var(--text); text-decoration: none; }
.footer-col a:hover { color: var(--cyan-hi); text-decoration: underline; }
.footer-contact { display: inline-flex; align-items: center; gap: 0.6em; }
.footer-contact .ico { color: var(--cyan-hi); }
.footer-col .social-link {
  display: inline-grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  color: var(--text);
  transition: background-color 0.5s var(--ease-spring), border-color 0.5s var(--ease-spring), color 0.5s var(--ease-spring), transform 0.5s var(--ease-snap);
}
.footer-col .social-link svg { width: 20px; height: 20px; fill: currentColor; }
.footer-col .social-link:hover {
  background: var(--cyan);
  border-color: var(--cyan);
  color: var(--on-accent);
  transform: rotate(-10deg) scale(1.08);
}
.footer-legal {
  max-width: var(--maxw);
  margin: 3rem auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  color: var(--muted);
  font-size: var(--step--1);
}
/* privacy.html's footer holds the legal line alone. */
.footer-legal:first-child { margin-top: 0; padding-top: 0; border-top: 0; text-align: center; }

/* ---- Simple pages (privacy, 404) ----------------------------------------- */

.page {
  max-width: 46rem;
  margin-inline: auto;
  padding: calc(var(--header-h) + 4rem) var(--gutter) 6rem;
}
.page h1 { font: 900 italic var(--step-3)/0.95 var(--font-display); text-transform: uppercase; margin-bottom: 1.5rem; }
.page h2 { font: 900 italic var(--step-1)/1.2 var(--font-display); text-transform: uppercase; margin: 2.4rem 0 0.8rem; }
.page p, .page li { color: var(--muted); }
.page p + p { margin-top: 1rem; }
.page ul { list-style: square; padding-left: 1.2rem; display: grid; gap: 0.4rem; }
.page .btn { margin-top: 2rem; }
.page-draft {
  display: inline-block;
  margin-bottom: 1.5rem;
  padding: 0.3em 0.7em;
  background: var(--yellow);
  color: var(--on-accent);
  font: 600 0.75rem/1.3 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.page-404 { min-height: 100svh; display: grid; align-content: center; text-align: center; justify-items: center; }
.page-404 .big {
  color: transparent;
  font: 900 italic clamp(7rem, 30vw, 16rem)/0.85 var(--font-display);
  -webkit-text-stroke: 3px var(--cyan);
}

/* ===========================================================================
   Motion. Everything below runs only when the visitor has not asked for
   reduced motion. Hidden-until-revealed states also need html.js, so they
   can never strand content when the script fails to load.
   ======================================================================== */

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }

  .btn:hover {
    background-position: 160% 0;
    transition:
      transform 0.5s var(--ease-spring),
      background-color 0.5s var(--ease-spring),
      box-shadow 0.5s var(--ease-spring),
      color 0.5s var(--ease-spring),
      background-position 0.9s var(--ease-spring);
  }

  /* Hero entrance */
  .hero-title .word { animation: word-in 0.9s var(--ease-snap) both; }
  .hero-title .word:nth-child(1) { animation-delay: 0.15s; }
  .hero-title .word:nth-child(2) { animation-delay: 0.27s; }
  .hero-title .word:nth-child(3) { animation-delay: 0.39s; }
  .hero-title .word:nth-child(4) { animation-delay: 0.47s; }
  .hero-title .word:nth-child(5) { animation-delay: 0.62s; animation-name: word-slam; animation-duration: 1s; }
  .hero-eyebrow { animation: fade-up 0.8s var(--ease-out) 0.05s both; }
  .hero-lede { animation: fade-up 0.9s var(--ease-out) 0.85s both; }
  .hero-actions { animation: fade-up 0.9s var(--ease-out) 1s both; }
  .scroll-cue { animation: fade-in 1s var(--ease-out) 1.6s both; }
  .scroll-cue i::after { animation: drip 2.2s var(--ease-out) 2s infinite; }

  .hero-mark-glow { animation: glow 3.5s var(--ease-wave) infinite alternate; }
  .spin-stage { animation: fade-up 1.1s var(--ease-out) 0.2s both; }
  .spin-floor { animation: fade-in 1.4s var(--ease-out) 0.5s both; }
  .spin-bar { animation: fade-up 0.9s var(--ease-out) 0.9s both; }
  .spin-name.is-new { animation: name-in 0.6s var(--ease-spring) both; }
  .hero-arc { animation: spin 90s linear infinite; }
  .hero-drops i { animation: rise var(--t) linear var(--dl) infinite; }

  /* Ticker */
  .ticker-track { animation: marquee 32s linear infinite; }
  .ticker:hover .ticker-track { animation-play-state: paused; }

  /* Scroll reveals: a heavy rise out of a soft blur. The filter is dropped
     outright once in, so nothing stays on a filtered layer afterwards. */
  html.js .reveal {
    opacity: 0;
    transform: translateY(56px);
    filter: blur(10px);
    transition:
      opacity 1s var(--ease-spring),
      transform 1.2s var(--ease-spring),
      filter 1s var(--ease-spring);
    transition-delay: var(--d, 0s);
  }
  html.js .reveal.is-in { opacity: 1; transform: none; filter: none; }

  html.js .segment .segment-icon * { stroke-dasharray: 260; stroke-dashoffset: 260; }
  html.js .segment.is-in .segment-icon * { stroke-dashoffset: 0; transition: stroke-dashoffset 1.6s var(--ease-out) calc(var(--d, 0s) + 0.2s); }

  /* Lines draw out from the hub, then pulses run along them for good. Each
     icon pings when its pulse arrives: same duration, delayed by one trip. */
  html.js .reach-line {
    stroke-dasharray: var(--len);
    stroke-dashoffset: var(--len);
    transition: stroke-dashoffset 1.4s var(--ease-out) var(--d, 0s);
  }
  html.js .reach.is-drawn .reach-line { stroke-dashoffset: 0; }
  html.js .reach-dot {
    transform: scale(0);
    transform-box: fill-box;
    transform-origin: center;
    transition: transform 0.5s var(--ease-snap) calc(var(--d, 0s) + 1.1s);
  }
  html.js .reach.is-drawn .reach-dot { transform: none; }
  .reach.is-drawn .reach-flow {
    opacity: 1;
    stroke-dasharray: 14px var(--len);
    animation: flow 2.6s cubic-bezier(0.45, 0, 0.55, 1) var(--d2, 1.6s) infinite both;
  }
  .reach.is-drawn .segment-icon { animation: ping 2.6s var(--ease-out) calc(var(--d2, 1.6s) + 2.6s) infinite; }
  .reach.is-drawn .reach-hub::before { animation: ripple 2.6s var(--ease-out) 1.4s infinite; }

  /* Story badge: the map floats, the pin drops onto Karlstad once the
     band is in view and then keeps pinging. */
  .story-map { animation: float 6s var(--ease-wave) infinite alternate; }
  .story-map-ripple { transform-box: fill-box; transform-origin: center; animation: ripple 2.4s var(--ease-out) infinite; }
  html.js .reveal .story-map-pin-head,
  html.js .reveal .story-map-pin-dot { opacity: 0; translate: 0 -24px; }
  html.js .reveal.is-in .story-map-pin-head,
  html.js .reveal.is-in .story-map-pin-dot {
    opacity: 1;
    translate: 0 0;
    transition: opacity 0.2s var(--ease-out) 0.7s, translate 0.55s var(--ease-snap) 0.7s;
  }

  /* Story cards: the stage plate pops on once the card has arrived, and the
     photo eases in under the pointer, like the history page's photos. */
  html.js .person .person-plate { transform: scale(0) rotate(-20deg); }
  html.js .person.is-in .person-plate { transform: none; transition: transform 0.7s var(--ease-snap) calc(var(--d, 0s) + 0.45s); }
  .person-photo img { transition: transform 1.2s var(--ease-spring); }
  .person:hover .person-photo img { transform: scale(1.04); }

  /* Grime wipe: a tall band with a pinned stage, scrubbed from main.js */
  html.js .wipe { --p: 0; height: 230svh; }
  html.js .wipe-stage { position: sticky; top: 0; height: 100svh; min-height: 0; }

  /* FAQ height animation where the browser supports it */
  @supports (interpolate-size: allow-keywords) {
    :root { interpolate-size: allow-keywords; }
    .faq-list details::details-content {
      block-size: 0;
      overflow: clip;
      transition: block-size 0.45s var(--ease-out), content-visibility 0.45s allow-discrete;
    }
    .faq-list details[open]::details-content { block-size: auto; }
  }

  .cta-title span { animation: gloss 3.8s var(--ease-wave) infinite; }

  /* News page: photos ease in on hover. */
  .post-media > img { transition: transform 0.8s var(--ease-out); }
  .post:hover .post-media > img { transform: scale(1.04); }
  .rally-shot img { transition: transform 1.2s var(--ease-spring); }
  .rally-shot:hover img { transform: scale(1.04); }

  /* Range and step cards lift a little under the pointer. */
  .range-card:hover { transform: translateY(-6px); box-shadow: var(--bezel-hot); }
  .range-card img { transition: transform 1.2s var(--ease-spring); }
  .range-card:hover img { transform: scale(1.03); }

  /* Each step's icon draws itself, like the "who we supply" icons. */
  html.js .step .ico * { stroke-dasharray: 120; stroke-dashoffset: 120; }
  html.js .step.is-in .ico * { stroke-dashoffset: 0; transition: stroke-dashoffset 1.4s var(--ease-out) calc(var(--d, 0s) + 0.25s); }

  /* The step numbers pop in once their card has arrived. */
  html.js .step .step-num { transform: scale(0); }
  html.js .step.is-in .step-num { transform: none; transition: transform 0.6s var(--ease-snap) calc(var(--d, 0s) + 0.35s); }

  /* The stage plate lands on the route when its stage comes into view. */
  html.js .stage .stage-no { transform: scale(0); }
  html.js .stage:has(.reveal.is-in) .stage-no { transform: none; transition: transform 0.7s var(--ease-snap); }
}

@keyframes word-in {
  from { opacity: 0; transform: translateX(-0.5em) skewX(-24deg); filter: blur(10px); }
  to { opacity: 1; transform: none; filter: none; }
}
@keyframes word-slam {
  0% { opacity: 0; transform: scale(2.4) skewX(-20deg); filter: blur(12px); }
  55% { opacity: 1; transform: scale(0.94) skewX(-4deg); filter: none; }
  75% { transform: scale(1.04) rotate(-1deg); }
  100% { opacity: 1; transform: none; }
}
@keyframes fade-up {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: none; }
}
@keyframes fade-in { from { opacity: 0; } }
@keyframes name-in {
  from { opacity: 0; transform: translateX(0.6em) skewX(-12deg); }
  to { opacity: 1; transform: none; }
}
@keyframes spin { to { rotate: 360deg; } }
@keyframes glow {
  from { opacity: 0.22; scale: 0.94; }
  to { opacity: 0.5; scale: 1.06; }
}
@keyframes rise {
  0% { opacity: 0; transform: translate(0, 0); }
  10% { opacity: 0.9; }
  80% { opacity: 0.6; }
  100% { opacity: 0; transform: translate(var(--dx), -105svh); }
}
@keyframes drip {
  0% { transform: translateY(-100%); }
  60%, 100% { transform: translateY(100%); }
}
@keyframes marquee { to { transform: translateX(-50%); } }
@keyframes float {
  from { transform: translateY(-8px) rotate(-2deg); }
  to { transform: translateY(8px) rotate(2deg); }
}
@keyframes flow {
  from { stroke-dashoffset: 14px; }
  to { stroke-dashoffset: calc(-1 * var(--len)); }
}
@keyframes ping {
  0% { scale: 1.15; filter: drop-shadow(0 0 10px var(--cyan)); }
  35%, 100% { scale: 1; filter: none; }
}
@keyframes ripple {
  from { opacity: 0.8; scale: 0.95; }
  to { opacity: 0; scale: 1.7; }
}
@keyframes gloss {
  0%, 30% { background-position: 100% 0; }
  70%, 100% { background-position: 0% 0; }
}
