/* =============================================================================
   J CYCLE STUDIO — DESIGN SYSTEM + SITE
   -----------------------------------------------------------------------------
   Palette is sampled, not invented: logo.jpeg is exactly #FE0002 ink on #000000.
   So the site is black, and red is treated as a LIGHT SOURCE rather than a
   colour — every glow, wash and bloom points away from a stage, the way the
   room itself is lit.

   Contrast, measured against #000000:
     #FFFFFF 21.0:1   #FE0002 5.22:1   #C9C9C9 12.7:1   #9A9A9A 7.5:1
   The brand red clears AA for body text on black, so it is never lifted or
   "fixed" — the logo ink is the UI ink.

   On the red band the maths inverts: white-on-red is only 4.03:1 (fails AA for
   body), black-on-red is 5.22:1. So that band runs BLACK on red — which also
   flips the logo's own relationship, red-on-black, inside out.
============================================================================= */

/* ---------------------------------------------------------------- tokens -- */
:root {
  /* brand — sampled from logo.jpeg */
  --black: #000000;
  --red: #fe0002;
  --white: #ffffff;

  /* System rule: WHEN RED IS A SURFACE, THE INK IS BLACK.
     White on this red measures 4.02:1 — it fails AA for anything under 18px, which
     is every button label, tab and tag on the site. Black on it is 5.22:1 and
     passes. It is also the logo's own relationship (red ink on black) turned
     inside out, so the rule is the brand's, not a workaround. */
  --on-red: #000000;

  /* layered near-blacks: depth without leaving the dark */
  --nero-1: #060606;
  --nero-2: #0d0d0d;
  --nero-3: #151515;
  --line: rgba(255, 255, 255, 0.12);
  --line-strong: rgba(255, 255, 255, 0.22);

  /* text */
  --fg: #ffffff;
  --fg-2: #c9c9c9;
  --fg-3: #9a9a9a;

  --wa: #25d366;

  /* red as light */
  --glow-sm: 0 0 24px rgba(254, 0, 2, 0.45);
  --glow-md: 0 0 60px rgba(254, 0, 2, 0.4);
  --glow-lg: 0 0 140px rgba(254, 0, 2, 0.35);
  --stage-wash: radial-gradient(90% 70% at 50% 0%, rgba(254, 0, 2, 0.28) 0%, rgba(254, 0, 2, 0.06) 45%, transparent 72%);

  /* type */
  --font-display: 'Outfit', 'Helvetica Neue', Arial, sans-serif;
  --font-body: 'Outfit', 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'Martian Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* The hero must fit one screen at every viewport, so the headline is capped by
     viewport HEIGHT as well as width — width alone overflows the moment a laptop
     is short (1366x768) or a phone turns landscape. */
  --fs-hero: clamp(2.6rem, min(11.5vw, 16vh), 9.5rem);
  --fs-giant: clamp(3.5rem, 9vw, 8rem);
  --fs-h2: clamp(2.1rem, 5vw, 4.25rem);
  --fs-h3: clamp(1.15rem, 1.6vw, 1.5rem);
  --fs-lede: clamp(1.05rem, 1.35vw, 1.3rem);
  --fs-body: clamp(0.975rem, 1vw, 1.0625rem);
  --fs-sm: 0.875rem;
  --fs-mono: 0.7rem;

  /* layout */
  --header-h: 74px;
  --maxw: 1280px;
  --gutter: clamp(1.15rem, 4vw, 3.5rem);
  --sp-section: clamp(5rem, 11vw, 10rem);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);

  /* THE RIG.
     banner-2 shows the ceiling: nested chevron light arches receding into the
     dark. It is the one thing in the room no competitor in Butterworth has, and
     it is the reason this site needs almost no decoration — every graphic mark
     here is the studio's own architecture, redrawn.

     It also solves the photo problem permanently. There is no photograph of a
     sprint, of the shopfront, of the lights down mid-class; there IS the shape of
     the ceiling, and it can be drawn at any size for free. Swap the brand out and
     this collapses, which is exactly what a brand mark should do.

     Two inks, because the surface decides: white on the black sections, black on
     the red band (white on that red is 4.03:1 and fails AA). */
  --rig-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 400' preserveAspectRatio='none'%3E%3Cg fill='none' stroke='%23ffffff' stroke-width='2.5'%3E%3Cpath d='M-40 400 L600 46 L1240 400' opacity='.55'/%3E%3Cpath d='M60 400 L600 100 L1140 400' opacity='.44'/%3E%3Cpath d='M160 400 L600 154 L1040 400' opacity='.34'/%3E%3Cpath d='M260 400 L600 208 L940 400' opacity='.25'/%3E%3Cpath d='M360 400 L600 262 L840 400' opacity='.17'/%3E%3Cpath d='M460 400 L600 316 L740 400' opacity='.1'/%3E%3C/g%3E%3C/svg%3E");
  --rig-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 400' preserveAspectRatio='none'%3E%3Cg fill='none' stroke='%23000000' stroke-width='2.5'%3E%3Cpath d='M-40 400 L600 46 L1240 400' opacity='.5'/%3E%3Cpath d='M60 400 L600 100 L1140 400' opacity='.4'/%3E%3Cpath d='M160 400 L600 154 L1040 400' opacity='.3'/%3E%3Cpath d='M260 400 L600 208 L940 400' opacity='.22'/%3E%3Cpath d='M360 400 L600 262 L840 400' opacity='.15'/%3E%3Cpath d='M460 400 L600 316 L740 400' opacity='.09'/%3E%3C/g%3E%3C/svg%3E");

  /* Club-room grain — keeps flat black from reading as dead black.
     Tiled straight into the body background, NOT a fixed blended overlay: a
     position:fixed layer with mix-blend-mode forces the compositor to re-blend
     the whole viewport every scroll frame, which made this page unscrollable. */
  --noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E");
}

@media (max-width: 767px) {
  :root { --header-h: 60px; }
}

/* ----------------------------------------------------------------- reset -- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

/* overflow-x lives on <html>, not <body>. Setting it on body makes the other axis
   compute from visible to auto, turning body into a scroll container — which
   quietly breaks the sticky header's containing block and window scrolling. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  overflow-x: clip;
}

body {
  background-color: var(--black);
  background-image: var(--noise);
  background-repeat: repeat;
  background-size: 160px 160px;
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* The hidden attribute only sets display:none in the UA sheet, so ANY author
   display rule silently beats it and the element stays on screen. Every component
   here that toggles [hidden] also sets its own display, so this has to win. */
[hidden] { display: none !important; }

img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; padding: 0; }

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

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

/* ------------------------------------------------------------ primitives -- */
.container {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--sp-section); }

.skip {
  position: absolute;
  top: -100px;
  left: var(--gutter);
  z-index: 200;
  background: var(--red);
  color: var(--on-red);
  padding: 0.75rem 1.25rem;
  font-weight: 700;
  transition: top 0.2s var(--ease);
}
.skip:focus { top: 0.6rem; }

/* the mono voice — lifted straight off the logo, which sets its own phone
   number in wide-tracked monospace. Times, counts, bike numbers, coordinates. */
.mono {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.kicker {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--red);
}
.kicker::before {
  content: '';
  width: 1.75rem;
  height: 1px;
  background: currentColor;
  box-shadow: var(--glow-sm);
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 900;
  line-height: 0.88;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  text-wrap: balance;
}
h3 { font-size: var(--fs-h3); line-height: 1.05; letter-spacing: -0.02em; }

.h2 { font-size: var(--fs-h2); }
.lede { font-size: var(--fs-lede); color: var(--fg-2); max-width: 46ch; line-height: 1.5; }

/* ------------------------------------------------------------------ btns -- */
.btn {
  --btn-bg: var(--red);
  --btn-fg: var(--on-red);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 0.95rem 1.6rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  border: 1px solid var(--btn-bg);
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease), background 0.2s;
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--glow-md); }
.btn:active { transform: translateY(0); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--white);
  border-color: var(--line-strong);
}
.btn--ghost:hover { border-color: var(--white); box-shadow: none; }

.btn--sm { padding: 0.6rem 1.05rem; }

/* --------------------------------------------------------------- header --- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  height: var(--header-h);
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--line);
}
.site-header__inner {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}
.brand { display: flex; align-items: center; flex: 0 0 auto; }
.brand img { height: clamp(26px, 3.2vw, 32px); width: auto; }

.nav { display: flex; align-items: center; gap: clamp(1.25rem, 2.4vw, 2.5rem); }
.nav__link {
  position: relative;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-3);
  padding-block: 0.4rem;
  transition: color 0.2s;
}
.nav__link:hover { color: var(--fg); }
.nav__link::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 2px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s var(--ease);
}
.nav__link:hover::after { transform: scaleX(0.35); }

/* current page — required, and it has to be visible without colour alone */
.nav__link[aria-current='page'] {
  color: var(--white);
  font-weight: 700;
}
.nav__link[aria-current='page']::after {
  transform: scaleX(1);
  box-shadow: var(--glow-sm);
}

.header__cta { display: flex; align-items: center; gap: 0.75rem; }

.burger {
  display: none;
  width: 42px;
  height: 42px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
}
.burger span {
  display: block;
  width: 18px;
  height: 1.5px;
  background: var(--white);
  position: relative;
  transition: background 0.2s;
}
.burger span::before, .burger span::after {
  content: '';
  position: absolute;
  left: 0;
  width: 18px;
  height: 1.5px;
  background: var(--white);
  transition: transform 0.3s var(--ease);
}
.burger span::before { top: -6px; }
.burger span::after { top: 6px; }
[data-menu-open] .burger span { background: transparent; }
[data-menu-open] .burger span::before { transform: translateY(6px) rotate(45deg); }
[data-menu-open] .burger span::after { transform: translateY(-6px) rotate(-45deg); }

/* mobile menu */
.menu {
  position: fixed;
  inset: var(--header-h) 0 0;
  z-index: 55;
  background: var(--black);
  background-image: var(--stage-wash);
  padding: clamp(2rem, 8vw, 4rem) var(--gutter);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  transform: translateY(-100%);
  visibility: hidden;
  /* same reasoning as the lightbox: visible immediately on open so the links are
     focusable, hidden only once it has finished sliding away */
  transition: transform 0.45s var(--ease), visibility 0s linear 0.45s;
}
[data-menu-open] .menu {
  transform: translateY(0);
  visibility: visible;
  transition: transform 0.45s var(--ease), visibility 0s;
}
.menu__link {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 9vw, 3.5rem);
  font-weight: 900;
  line-height: 1.08;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  color: var(--fg-3);
  border-bottom: 1px solid var(--line);
  padding-block: 0.5rem;
}
.menu__link[aria-current='page'] { color: var(--red); }
.menu__foot { margin-top: auto; display: flex; flex-direction: column; gap: 1rem; }

@media (max-width: 900px) {
  .nav, .header__cta .btn { display: none; }
  .burger { display: flex; }
}
@media (min-width: 901px) {
  .menu { display: none; }
}

/* ----------------------------------------------------------------- hero --- */
/* header (var(--header-h)) + hero = exactly one viewport, at every width.
   The photograph is the studio's own chevron light tunnel, uncropped and
   unwashed — the rig is white, so nothing red is laid over it. */
.hero {
  position: relative;
  min-height: calc(100svh - var(--header-h));
  display: grid;
  grid-template-rows: 1fr auto;
  overflow: hidden;
  isolation: isolate;
}
.hero__media { position: absolute; inset: 0; z-index: -2; }
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 38%;   /* keeps the apex of the rig in frame when cropped tall */
}
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, rgba(0, 0, 0, 0.94) 0%, rgba(0, 0, 0, 0.74) 26%, rgba(0, 0, 0, 0.3) 58%, rgba(0, 0, 0, 0.5) 100%),
    linear-gradient(to right, rgba(0, 0, 0, 0.82) 0%, rgba(0, 0, 0, 0.24) 52%, rgba(0, 0, 0, 0) 100%);
}

.hero__inner {
  align-self: end;
  padding-block: clamp(1.5rem, 4vh, 3rem);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(0.75rem, 1.6vh, 1.35rem);
}
.hero h1 {
  font-size: var(--fs-hero);
  text-wrap: balance;
}
.hero__em { color: var(--red); text-shadow: 0 0 60px rgba(254, 0, 2, 0.55); }
.hero__lede { max-width: 40ch; }
.hero__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.4rem; }

/* the phone number set vertically up the right edge, the way the logo sets it */
.hero__rail {
  position: absolute;
  right: clamp(0.5rem, 1.4vw, 1.1rem);
  top: 50%;
  transform: translateY(-50%) rotate(180deg);
  writing-mode: vertical-rl;
  color: var(--fg-3);
  z-index: 2;
}

/* fact strip — on the fold, so the first screen answers the first four questions */
.hero__facts {
  border-top: 1px solid var(--line);
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.hero__facts ul {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(0.75rem, 2vw, 2rem);
  padding-block: clamp(0.85rem, 2vh, 1.4rem);
}
.hero__facts li { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.hero__stat-v {
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 2vw, 1.6rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1;
  text-transform: uppercase;
}
.hero__stat-v span {
  font-family: var(--font-mono);
  font-size: 0.5em;
  font-weight: 500;
  letter-spacing: 0.14em;
  color: var(--red);
  margin-left: 0.35em;
}
.hero__stat-l {
  font-family: var(--font-mono);
  font-size: 0.56rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-3);
}

@media (max-width: 900px) {
  .hero__facts ul { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 1100px) { .hero__rail { display: none; } }
/* Landscape phones: the fact strip is the first thing to go, then the lede. The
   headline and the two buttons are the hero; everything else is support. */
@media (max-height: 620px) and (orientation: landscape) {
  .hero__facts { display: none; }
  .hero__lede { display: none; }
}
/* Small screens: the headline wraps to three lines and the stack stops fitting.
   Spacing gives way before type size does — a 320px phone still gets the full
   headline, just breathing less. Measured at 320x568, the tightest shape tested. */
@media (max-width: 380px), (max-height: 620px) {
  .hero__inner { gap: 0.5rem; padding-block: 1rem; }
  .hero__facts ul { padding-block: 0.6rem; gap: 0.5rem; }
  .hero__lede { font-size: 0.95rem; }
  .hero__actions { gap: 0.5rem; margin-top: 0.15rem; }
  .hero__actions .btn { padding: 0.7rem 1.05rem; }
}

/* --------------------------------------------------------------- ticker --- */
/* The only thing on the page that moves sideways by itself. Carries the verified
   proof points as texture instead of as one more list to read. */
.ticker {
  border-block: 1px solid var(--line);
  background: var(--nero-1);
  overflow: hidden;
  padding-block: 0.95rem;
}
.ticker__track {
  display: flex;
  width: max-content;
  animation: ticker 42s linear infinite;
}
.ticker:hover .ticker__track { animation-play-state: paused; }
.ticker__set { display: flex; align-items: center; flex: 0 0 auto; }
.ticker__set b {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--fg-2);
  white-space: nowrap;
}
/* the separator is the rig, one chevron of it */
.ticker__set i {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  margin-inline: clamp(1.1rem, 2.5vw, 2.1rem);
  border-left: 1.5px solid var(--red);
  border-top: 1.5px solid var(--red);
  transform: rotate(45deg);
}
/* exactly two identical sets, so half a lap is a seamless loop */
@keyframes ticker { to { transform: translateX(-50%); } }

/* ----------------------------------------------------------------- room --- */
/* asymmetric split: statement type and facts stacked on the left, a tall photo
   bleeding off the right edge of the container. */
.room { border-top: 1px solid var(--line); overflow: hidden; }
.room__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr);
  grid-template-areas:
    'text  media'
    'facts media';
  column-gap: clamp(2rem, 5vw, 5rem);
  row-gap: clamp(2rem, 4vw, 3.25rem);
  align-items: start;
}
.room__text { grid-area: text; display: flex; flex-direction: column; gap: 1.15rem; align-items: flex-start; }
.room__text h2 em { font-style: normal; color: var(--red); }

.room__media { grid-area: media; position: relative; margin-right: calc(var(--gutter) * -1); }
.room__media img { display: block; width: 100%; height: auto; }
.room__media figcaption {
  position: absolute;
  left: 0;
  bottom: 0;
  padding: 0.5rem 0.85rem;
  background: var(--red);
  color: var(--on-red);
  font-family: var(--font-mono);
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.room__facts { grid-area: facts; border-top: 1px solid var(--line); }
.room__facts li {
  display: grid;
  grid-template-columns: 6.5rem 1fr;
  gap: 1rem;
  align-items: baseline;
  padding-block: 0.8rem;
  border-bottom: 1px solid var(--line);
}
.room__facts b {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--red);
}
.room__facts span { color: var(--fg-2); font-size: var(--fs-sm); }

@media (max-width: 900px) {
  .room__grid {
    grid-template-columns: 1fr;
    grid-template-areas: 'text' 'media' 'facts';
  }
  .room__media { margin-right: 0; }
}

/* ---------------------------------------------------------------- curve --- */
/* The 45 minutes as the effort profile it actually is.
   This section used to be six photographs strung along a timeline — but all three
   source photos are posed group shots taken at the END of a class, so "climbs" and
   "sprints" were the same smiling room relabelled. A curve is true, it is the only
   chart on the page, and it needs no photograph of a thing we have no photograph of. */
.curve { border-top: 1px solid var(--line); background: var(--nero-1); }
.curve__head {
  display: grid;
  grid-template-columns: 1fr minmax(0, 26rem);
  align-items: end;
  gap: 2rem;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}

.curve__plot { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: clamp(0.6rem, 1.5vw, 1.25rem); }
.curve__yaxis {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: var(--plot-h, clamp(190px, 26vw, 300px));
  padding-block: 0.2rem;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 0.54rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.curve__area { position: relative; min-width: 0; }
.curve__svg {
  display: block;
  width: 100%;
  height: var(--plot-h, clamp(190px, 26vw, 300px));
  color: var(--line);
  overflow: visible;
}
.curve__line { filter: drop-shadow(0 0 12px rgba(254, 0, 2, 0.55)); }

/* One DOM, two layouts. On desktop every mark is pinned to its real position on
   the axis — 00:26 sits under the peak because that is where the sprint block is. */
.curve__marks { position: relative; height: 10.25rem; margin-top: 0.9rem; }
.cmark {
  position: absolute;
  top: 0;
  left: var(--x);
  transform: translateX(-50%);
  width: 8.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  text-align: center;
}
.cmark::before {
  content: '';
  position: absolute;
  top: -0.9rem;
  left: 50%;
  width: 1px;
  height: 0.65rem;
  background: var(--line-strong);
}
/* The marks are spaced by real time, not evenly — 00:00 and 00:05 are eleven
   percent of the ride apart and their captions are wider than that, so they used
   to sit on top of each other. Alternate marks drop to a second row and their
   leader line grows to reach back up to the axis. */
.cmark:nth-child(even) { top: 3.1rem; }
.cmark:nth-child(even)::before { top: -4rem; height: 3.75rem; }
.cmark:first-child { transform: none; text-align: left; }
.cmark:first-child::before { left: 0; }
.cmark:last-child { transform: translateX(-100%); text-align: right; }
.cmark:last-child::before { left: auto; right: 0; }

.cmark b {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--red);
}
.cmark span {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.01em;
}
.cmark small { font-size: 0.76rem; color: var(--fg-3); line-height: 1.4; }

@media (max-width: 900px) {
  .curve__head { grid-template-columns: 1fr; align-items: start; gap: 1.25rem; }
}
@media (max-width: 780px) {
  /* Six labels cannot share a phone's width. The curve keeps its shape and the
     marks fall out of the axis into a plain two-column list underneath it. */
  .curve__plot { grid-template-columns: 1fr; }
  .curve__yaxis { display: none; }
  .curve__marks {
    position: static;
    height: auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem 1rem;
    margin-top: 1.5rem;
  }
  .cmark,
  .cmark:first-child,
  .cmark:last-child { position: static; transform: none; width: auto; text-align: left; }
  .cmark::before { content: none; }
}

/* -------------------------------------------------------------- numbers --- */
/* full-bleed red band, giant numerals, the rig drawn along the bottom edge.
   On this band the contrast maths inverts: white on this red is 4.03:1 and fails
   AA for body text, black on it is 5.22:1 and passes. So the band runs BLACK on
   red — which also turns the logo's own red-on-black inside out. */
.numbers {
  background: var(--red);
  color: var(--on-red);
  padding-top: clamp(3rem, 6vw, 5rem);
  overflow: hidden;
}
.numbers__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
}
.num { display: flex; flex-direction: column; }
.num__v {
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 6vw, 5.25rem);
  font-weight: 900;
  line-height: 0.8;
  letter-spacing: -0.045em;
  text-transform: uppercase;
}
.num__u {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-top: 0.7rem;
}
.num__l { font-size: var(--fs-sm); line-height: 1.5; margin-top: 0.55rem; opacity: 0.75; max-width: 26ch; }

/* the light rig, drawn: black on red, the room's own ceiling as a footer rule */
.numbers__rig {
  height: clamp(70px, 10vw, 130px);
  margin-top: clamp(2rem, 4vw, 3.25rem);
  background-image: var(--rig-dark);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: bottom center;
}

@media (max-width: 900px) { .numbers__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 460px) { .numbers__grid { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------------- shots --- */
/* Three photographs exist, so they are shown three times — whole, large and
   alternating across the page. The old build sliced the same three files into
   eleven tiles and the seams showed: one member's face turned up in four
   different sections doing four different things. */
.shots { border-top: 1px solid var(--line); }
.shots__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
  margin-bottom: clamp(2rem, 4vw, 3.25rem);
}
.shots__note { color: var(--fg-3); }

.shots__stack { display: flex; flex-direction: column; gap: clamp(2.5rem, 5vw, 4.5rem); }
.shot { display: flex; flex-direction: column; gap: 0.9rem; width: min(100%, 74%); }
.shot--right { align-self: flex-end; }

.shots .gallery__item {
  position: relative;
  display: block;
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--nero-2);
}
.shots .gallery__item img {
  display: block;
  width: 100%;
  height: auto;
  transition: transform 0.7s var(--ease), opacity 0.4s var(--ease);
}
.shots .gallery__item::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px solid var(--red);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}
.shots .gallery__item:hover img,
.shots .gallery__item:focus-visible img { transform: scale(1.03); }
.shots .gallery__item:hover::after,
.shots .gallery__item:focus-visible::after { opacity: 1; }

.shot figcaption { display: flex; align-items: baseline; gap: 1rem; }
.shot figcaption b {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--red);
  flex: 0 0 auto;
}
.shot figcaption span { color: var(--fg-3); font-size: var(--fs-sm); }

@media (max-width: 900px) {
  .shot, .shot--right { width: 100%; align-self: stretch; }
}

/* ------------------------------------------------------------- lightbox --- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: rgba(0, 0, 0, 0.94);
  display: grid;
  grid-template-rows: auto 1fr auto;
  opacity: 0;
  visibility: hidden;
  /* visibility flips instantly on open and is held back until the fade finishes
     on close. Transitioning it over 0.3s instead means the dialog is still
     visibility:hidden — and therefore unfocusable — for the first frames after
     opening, so moving focus into it silently fails. */
  transition: opacity 0.3s var(--ease), visibility 0s linear 0.3s;
}
.lightbox[data-open] {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.3s var(--ease), visibility 0s;
}
.lightbox__bar { display: flex; align-items: center; justify-content: space-between; padding: 1rem var(--gutter); }
.lightbox__count { color: var(--fg-3); font-family: var(--font-mono); font-size: var(--fs-mono); letter-spacing: 0.2em; }
.lightbox__stage { display: grid; place-items: center; padding: 0 clamp(0.5rem, 4vw, 4.5rem); min-height: 0; }
.lightbox__stage img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  box-shadow: 0 0 120px rgba(254, 0, 2, 0.18);
}
.lightbox__cap { padding: 1rem var(--gutter) 1.5rem; color: var(--fg-3); font-size: var(--fs-sm); text-align: center; }
.lightbox__btn {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-strong);
  color: var(--white);
  transition: background 0.2s, border-color 0.2s;
}
.lightbox__btn:hover { background: var(--red); border-color: var(--red); color: var(--on-red); }
.lightbox__nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; }
.lightbox__nav--prev { left: clamp(0.5rem, 2vw, 1.5rem); }
.lightbox__nav--next { right: clamp(0.5rem, 2vw, 1.5rem); }

/* --------------------------------------------------------------- prices --- */
/* A ladder, not a card grid. Each rung draws its per-class rate as a meter, so the
   bars step down the section and the argument — ride more, pay less — is made by
   the layout before a single price is read. The two packages that aren't ride
   bundles can't be compared on that scale, so they sit underneath as plates. */
.prices { border-top: 1px solid var(--line); background: var(--nero-1); }
.prices__head {
  display: grid;
  grid-template-columns: 1fr minmax(0, 26rem);
  align-items: end;
  gap: 2rem;
  margin-bottom: clamp(2rem, 4vw, 3.25rem);
}

.ladder { border-top: 1px solid var(--line); }
.tier {
  position: relative;
  display: grid;
  grid-template-columns: minmax(8.5rem, 1fr) minmax(0, 2.1fr) auto auto;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 2.25rem);
  padding: clamp(1rem, 2vw, 1.5rem) 0;
  border-bottom: 1px solid var(--line);
  color: inherit;
  text-decoration: none;
  transition: background 0.25s var(--ease), padding 0.25s var(--ease);
}
.tier:hover { background: var(--nero-2); padding-inline: clamp(0.5rem, 1.5vw, 1.25rem); }

.tier__id { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.tier__name {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  line-height: 1.1;
}
.tier__valid {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* the meter: red as light again — it glows at the end of the bar, not along it */
.tier__meter { height: 6px; background: var(--nero-3); min-width: 0; }
.tier__meter i {
  display: block;
  width: var(--w);
  height: 100%;
  background: linear-gradient(90deg, rgba(254, 0, 2, 0.35), var(--red));
  box-shadow: var(--glow-sm);
  transition: width 0.5s var(--ease);
}

.tier__rate {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--red);
  white-space: nowrap;
}
.tier__rate em { font-style: normal; color: var(--fg-3); font-weight: 400; }
.tier__total {
  font-family: var(--font-mono);
  font-size: clamp(1.15rem, 2vw, 1.6rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  text-align: right;
  min-width: 4.5ch;
  white-space: nowrap;
}
.tier__tag {
  position: absolute;
  top: 0;
  left: 0;
  transform: translateY(-50%);
  background: var(--red);
  color: var(--on-red);
  font-family: var(--font-mono);
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.15rem 0.45rem;
}
.tier--best { background: rgba(254, 0, 2, 0.05); }

/* the two that sit outside the ladder */
.plates {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: 1px;
  margin-top: clamp(2rem, 4vw, 3rem);
  background: var(--line);
  border: 1px solid var(--line);
}
/* Layered upward from the plate's own background rather than with negative
   z-indexes: this element paints a background (the panels elsewhere on the site
   don't), and a z-index:-1 child sits behind that background, not in front of it —
   which hid the photo completely. */
.plate {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  min-height: clamp(190px, 22vw, 250px);
  padding: clamp(1.25rem, 2.5vw, 1.85rem);
  background: var(--black);
  color: inherit;
  text-decoration: none;
  overflow: hidden;
}
.plate__img {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.5;
  transition: transform 0.6s var(--ease), opacity 0.4s var(--ease);
}
.plate::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.92) 15%, rgba(0, 0, 0, 0.55) 70%, rgba(0, 0, 0, 0.3) 100%);
}
/* no photo of an empty month, so the unlimited plate is lit instead of shot */
.plate--unlimited::before { background: var(--stage-wash), linear-gradient(to top, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.55)); }
.plate:hover .plate__img { transform: scale(1.05); opacity: 0.62; }

.plate__body { position: relative; z-index: 2; display: flex; flex-direction: column; gap: 0.3rem; }
.plate__n {
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 2.4vw, 1.9rem);
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1;
  margin-top: 0.35rem;
}
.plate__p {
  font-family: var(--font-mono);
  font-size: clamp(1.5rem, 2.8vw, 2.1rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--red);
  line-height: 1.1;
}
.plate__note {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-2);
}

@media (max-width: 900px) {
  .prices__head { grid-template-columns: 1fr; align-items: start; gap: 1.25rem; }
}

@media (max-width: 780px) {
  /* The meter drops to its own full-width row under the name, with the rate beside
     it — the comparison survives, it just reads top-to-bottom instead of across. */
  .tier {
    grid-template-columns: 1fr auto;
    gap: 0.5rem 1rem;
    padding-block: 1.1rem;
  }
  .tier__meter { grid-column: 1; grid-row: 2; align-self: center; }
  .tier__rate { grid-column: 2; grid-row: 2; text-align: right; }
  .tier__total { grid-column: 2; grid-row: 1; }
  .tier:hover { padding-inline: 0; }
  /* Same call as the booking flow: 9-10px mono is unreadable on a phone. */
  .tier__valid, .plate__note { font-size: 0.72rem; letter-spacing: 0.08em; }
  .tier__rate { font-size: 0.78rem; }
  .tier__tag { font-size: 0.6rem; }
}

/* ----------------------------------------------------------------- week --- */
/* The whole timetable as a seven-column grid. No day tabs, no fetch, no loading
   state — it is in the HTML, so it is on screen in the first paint.

   .days / .day below are NOT dead: booking.html still renders a live day rail and
   styles it from here. Only the home page's version went away. */
.week { border-top: 1px solid var(--line); }
.week__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
  margin-bottom: clamp(2rem, 4vw, 3rem);
}

.week__grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.wday {
  background: var(--black);
  padding: clamp(0.7rem, 1.2vw, 1rem) clamp(0.5rem, 0.9vw, 0.85rem);
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  min-width: 0;
}
.wday--end { background: var(--nero-2); }        /* the weekend reads different */
.wday__n {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--fg-3);
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--line);
}

.ride {
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
  padding: 0.5rem 0.55rem;
  border: 1px solid var(--line);
  background: var(--nero-1);
  color: inherit;
  min-width: 0;
  transition: border-color 0.2s, background 0.2s, transform 0.2s var(--ease);
}
.ride:hover { border-color: var(--red); background: rgba(254, 0, 2, 0.09); transform: translateY(-2px); }
.ride b { font-family: var(--font-mono); font-size: 0.78rem; font-weight: 700; letter-spacing: 0.02em; }
.ride span {
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  line-height: 1.12;
}
.ride i {
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 0.52rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-3);
}
.ride--first { border-color: rgba(255, 255, 255, 0.3); }
.ride--theme b, .ride--theme span { color: var(--red); }

.week__note { margin-top: clamp(1.25rem, 2.5vw, 2rem); color: var(--fg-3); font-size: var(--fs-sm); max-width: 64ch; }
.week__note a { color: var(--red); border-bottom: 1px solid currentColor; }

/* Seven columns need ~900px to stay legible. Below that the week wraps rather
   than squeezing — a 90px-wide Wednesday helps nobody. */
@media (max-width: 1100px) { .week__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .week__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 420px)  { .week__grid { grid-template-columns: 1fr; } }

/* ------------------------------------------- day rail (booking page only) --- */
.days {
  display: flex;
  gap: 0.4rem;
  overflow-x: auto;
  padding-bottom: 0.75rem;
  margin-bottom: 0.5rem;
  scrollbar-width: none;
}
.days::-webkit-scrollbar { display: none; }
.day {
  flex: 0 0 auto;
  padding: 0.6rem 0.95rem;
  border: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-3);
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  align-items: center;
  transition: border-color 0.2s, color 0.2s, background 0.2s;
}
.day b { font-size: 0.95rem; letter-spacing: 0; color: var(--fg); font-weight: 700; }
.day:hover { border-color: var(--line-strong); color: var(--fg); }
.day[aria-selected='true'] { background: var(--red); border-color: var(--red); color: var(--on-red); }
.day[aria-selected='true'] b { color: var(--on-red); }

/* ----------------------------------------------------------------- find --- */
/* a full-bleed band of the room, then a three-column fact sheet under it.
   NOT a shopfront: no shopfront photograph exists, so the section shows the room
   and says so rather than pretending. */
.find { border-top: 1px solid var(--line); background: var(--nero-1); }
.find__band { position: relative; height: clamp(240px, 34vw, 420px); overflow: hidden; }
.find__band > img { width: 100%; height: 100%; object-fit: cover; }
.find__band::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to right, rgba(0, 0, 0, 0.88) 0%, rgba(0, 0, 0, 0.3) 58%, rgba(0, 0, 0, 0.1) 100%);
}
.find__stamp {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: clamp(1.25rem, 3vw, 2.25rem);
}
.find__stamp h2 { font-size: clamp(1.9rem, 4.2vw, 3.25rem); margin-top: 0.75rem; }

.find__facts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.25rem, 3vw, 3rem);
  padding-block: clamp(2.5rem, 5vw, 3.5rem);
}
.fact { display: flex; flex-direction: column; gap: 0.6rem; }
.fact h3 {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  color: var(--red);
  text-transform: uppercase;
}
.fact p, .fact li { color: var(--fg-2); font-size: var(--fs-sm); line-height: 1.65; }
.fact address { font-style: normal; }
.fact__coords { font-family: var(--font-mono); font-size: 0.58rem; letter-spacing: 0.14em; color: var(--fg-3); }

@media (max-width: 780px) {
  .find__facts { grid-template-columns: 1fr; gap: 2rem; }
}

/* ------------------------------------------------------------------ cta --- */
.cta {
  position: relative;
  min-height: clamp(400px, 60vh, 620px);
  display: grid;
  place-items: center;
  text-align: center;
  overflow: hidden;
  isolation: isolate;
  border-top: 1px solid var(--line);
}
.cta__media { position: absolute; inset: 0; z-index: -3; }
.cta__media img { width: 100%; height: 100%; object-fit: cover; }
.cta::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(58% 58% at 50% 58%, rgba(254, 0, 2, 0.34), transparent 70%),
    rgba(0, 0, 0, 0.8);
}
/* the rig again, closing the page the way it opened it */
.cta__rig {
  position: absolute;
  inset: auto 0 0;
  z-index: -1;
  height: clamp(120px, 22vw, 260px);
  background-image: var(--rig-light);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: bottom center;
  opacity: 0.5;
}
.cta__inner { display: flex; flex-direction: column; align-items: center; gap: 1.5rem; }
.cta h2 { font-size: var(--fs-giant); max-width: 16ch; }
.cta p { color: var(--fg-2); max-width: 44ch; }

/* --------------------------------------------------------------- footer --- */
.site-footer { border-top: 1px solid var(--line); background: var(--black); }
.site-footer__top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: clamp(1.75rem, 3vw, 3rem);
  padding-block: clamp(3rem, 6vw, 4.5rem);
}
.site-footer__brand { display: flex; flex-direction: column; gap: 1.1rem; align-items: flex-start; }
.site-footer__brand img { height: 34px; width: auto; }
.site-footer__brand p { color: var(--fg-3); font-size: var(--fs-sm); max-width: 30ch; }
.fcol h3 { font-family: var(--font-mono); font-size: 0.62rem; font-weight: 700; letter-spacing: 0.2em; color: var(--fg-3); text-transform: uppercase; margin-bottom: 1rem; }
.fcol li + li { margin-top: 0.55rem; }
.fcol a, .fcol p { color: var(--fg-2); font-size: var(--fs-sm); transition: color 0.2s; }
.fcol a:hover { color: var(--red); }

.site-footer__bar {
  border-top: 1px solid var(--line);
  padding-block: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.site-footer__bar p, .site-footer__bar a { font-family: var(--font-mono); font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg-3); }
.site-footer__bar a:hover { color: var(--red); }
.signature a { color: var(--fg-2); border-bottom: 1px solid var(--line-strong); }

@media (max-width: 900px) {
  .site-footer__top { grid-template-columns: 1fr 1fr; }
  .site-footer__brand { grid-column: 1 / -1; }
}
@media (max-width: 520px) {
  .site-footer__top { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------ floating whatsapp -- */
.wa-float {
  position: fixed;
  right: clamp(1rem, 2.5vw, 1.75rem);
  bottom: clamp(1rem, 2.5vw, 1.75rem);
  z-index: 90;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--wa);
  color: #04140a;
  display: grid;
  place-items: center;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
  transition: transform 0.25s var(--ease);
}
.wa-float:hover { transform: scale(1.08); }
.wa-float svg { width: 28px; height: 28px; }
.wa-float::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--wa);
  z-index: -1;
  animation: wa-pulse 2.6s var(--ease-in-out) infinite;
}
@keyframes wa-pulse {
  0% { transform: scale(1); opacity: 0.55; }
  70%, 100% { transform: scale(1.7); opacity: 0; }
}

/* ---------------------------------------------------- mobile legibility --- */
/* Tracked-out mono at 9–10px is fine texture at desk distance and simply
   unreadable in a hand. On a phone, anything carrying information — class codes,
   times, spots left, addresses — goes back above 12px. The tracking comes down
   with it, or the labels wrap to three lines. */
@media (max-width: 900px) {
  :root { --fs-mono: 0.78rem; }

  .slot__meta, .slot__left, .hero__stat-l, .num__u, .room__facts b,
  .cmark b, .shot figcaption b, .curve__yaxis,
  .fact h3, .fcol h3, .site-footer__bar p, .site-footer__bar a {
    font-size: 0.75rem;
    letter-spacing: 0.1em;
  }
  .day { font-size: 0.72rem; }
  .room__media figcaption { font-size: 0.72rem; letter-spacing: 0.1em; }
  .ticker__set b { font-size: 0.75rem; letter-spacing: 0.14em; }
  .hero__stat-v span { font-size: 0.7em; }
}

/* --------------------------------------------------------------- motion --- */
/* The reveal OPTS IN to hiding, rather than hiding by default and relying on
   JavaScript to undo it.

   It used to be the other way round: [data-reveal] was opacity:0, and the only
   things that brought it back were .is-in or the .no-js class — which the inline
   script in <head> strips on the first line it runs. So from that instant the whole
   page was invisible, and STAYED invisible unless site.js loaded, executed, and its
   IntersectionObserver actually fired. Any one of those failing left the site as a
   stack of empty black boxes: a red band with no numbers in it, sections with no
   headings. site.js now adds .reveal-ready only at the moment it can guarantee the
   undo, and takes it back if the observer goes quiet. */
[data-reveal] { opacity: 1; transform: none; }

.reveal-ready [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
  transition-delay: var(--d, 0ms);
}
.reveal-ready [data-reveal].is-in { opacity: 1; transform: none; }

.hero__inner > * {
  animation: rise 0.9s var(--ease) backwards;
}
.hero__inner > *:nth-child(1) { animation-delay: 0.05s; }
.hero__inner > *:nth-child(2) { animation-delay: 0.15s; }
.hero__inner > *:nth-child(3) { animation-delay: 0.25s; }
.hero__inner > *:nth-child(4) { animation-delay: 0.35s; }
@keyframes rise {
  from { opacity: 0; transform: translateY(26px); }
  to { opacity: 1; transform: none; }
}

@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;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
  .wa-float::before { animation: none; }
  /* An infinite marquee can't be "sped up to 0.001ms" — that just makes it flicker
     in place. It has to actually stop, and its content has to stay readable. */
  .ticker__track { animation: none !important; transform: none !important; }
  .ticker { overflow-x: auto; }
}
