/* ============================================================================
   Ipic Active concept: shared design system
   ----------------------------------------------------------------------------
   Loaded AFTER scrollcraft.css on every page. The landing page adds its own
   act styles inline; supporting pages use only what is here.

   TOKENS      :root overrides of the six scroll-craft colour roles and the two
               faces. --bar-h is the fixed bar height (content clears it).
   LAYOUT      .page (a quiet supporting page shell), .page__head (title block
               under the bar), .page__body, .split (copy + figure, two columns
               above 860px), .stack (vertical rhythm, use sc-stack semantics),
               .measure (body measure), .hairline (1px rule), .visually-hidden,
               .skip (skip link).
   TYPE        .display (Archivo 700, width 85, caps) with --xl/--lg/--md/--sm;
               .lede, .body (from scrollcraft: .sc-lede/.sc-body also fine);
               .eyebrow (mono label, at most one per three headings);
               .note (quiet "confirm with the club" line, ink-soft, hairline
               above); .nums (tabular numerals).
   CHROME      .bar (fixed hairline bar: wordmark, live status line, one CTA),
               .bar.is-solid (surface at 88% once the hero has un-pinned; on
               supporting pages add is-solid in the markup).
   CONTROLS    .btn + .btn--primary (accent fill, the only primary style),
               .btn--quiet (hairline ghost), .btn--sm (bar size), .btn--block
               (full width on phones); .link (underlined inline link),
               .link--quiet (ink-soft).
   DATA        .hours (opening-hours table, time cell then day header),
               .hours__five (the accent 05:00), .timetable (class timetable
               tables on /classes), .tick-gauge (the 100-tick static figure on
               /membership: 75 filled, 25 outlined).
   FOOTER      .foot, .foot__nav, .foot__meta, .foot__small ("Concept site by
               Trevern").
   The CTA label is "Book a free day" everywhere; the URL is the wa.me link in
   RESEARCH.md. No em dashes anywhere in page text.
   ========================================================================== */

:root {
  /* The engine's small tier tops out at 15.2px; secondary lines on this
     build carry real facts, so the desktop cap goes to 16px. */
  --sc-t-sm: clamp(0.9375rem, 0.88rem + 0.24vw, 1rem);
  --sc-canvas:     #0B0D10;
  --sc-surface:    #15181D;
  --sc-ink:        #EEF0F2;
  --sc-ink-soft:   #A2A8B3;
  --sc-accent:     #EE4F24;
  --sc-accent-ink: #140E0A;
  --sc-font-display: "Archivo", "Archivo Fallback", "Arial Narrow", system-ui, sans-serif;
  --sc-font-text:    "Instrument Sans", "Instrument Sans Fallback", system-ui, -apple-system, "Segoe UI", sans-serif;
  --sc-font-mono:    ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --sc-shadow-color: 220 30% 3%;
  --sc-r-sm: 4px; --sc-r-md: 8px; --sc-r-lg: 14px;
  --bar-h: 4rem;
  --display-stretch: 85%;
}
@media (max-width: 860px) {
  :root {
    /* One row on a phone: 3.5rem, which hands 1.1rem of a small screen back to
       the photograph. The old two-row bar painted the status over the wordmark. */
    --bar-h: 3.5rem;
    /* Type floor: nothing on a phone reads below 15px (--sc-t-sm floors there
       already), and running copy stays on --sc-t-base: 16.2px at 390, 16px at 360. */
    --sc-t-xs: 0.8125rem;
  }
}

/* Self-hosted, latin-subset, axis-instanced. No CDN request on any page: the
   three faces are same-origin, preloaded in every head, and total 81 KB, which
   is inside the 90 KB font budget. Archivo is instanced to the axis ranges this
   build actually uses (wght 600..800, wdth 78..90) so the peak headline's
   variable morph still has its whole sweep. */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 600 800;
  font-stretch: 78% 90%;
  font-display: swap;
  src: url("/assets/fonts/archivo-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Archivo";
  font-style: italic;
  font-weight: 700;
  font-stretch: 85%;
  font-display: swap;
  src: url("/assets/fonts/archivo-italic-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/instrument-latin.woff2") format("woff2");
}

/* Metric-matched fallbacks so the hero does not jump when the real faces land. */
@font-face {
  font-family: "Archivo Fallback";
  src: local("Arial Narrow Bold"), local("ArialNarrow-Bold"), local("Arial Narrow"), local("Helvetica Neue Condensed Bold");
  /* Measured against the real face at 100px: the fallback ran 9% narrow, which
     re-wrapped copy when the web font landed and moved every act below it. */
  size-adjust: 108%; ascent-override: 90%; descent-override: 22%; line-gap-override: 0%;
}
@font-face {
  font-family: "Instrument Sans Fallback";
  src: local("Helvetica Neue"), local("Arial");
  size-adjust: 102.4%; ascent-override: 96%; descent-override: 24%; line-gap-override: 0%;
}

html { color-scheme: dark; }
body {
  /* Light type on a dark ground: a touch more leading, tracking and weight. */
  line-height: 1.66;
  letter-spacing: 0.004em;
  font-weight: 450;
}
p { margin: 0; text-wrap: pretty; }
h1, h2, h3 { text-wrap: balance; }
strong, b { font-weight: 600; }
:where(a) { text-decoration-thickness: 1px; text-underline-offset: 0.2em; transition: color var(--sc-d-fast) var(--sc-ease-out), opacity var(--sc-d-fast) var(--sc-ease-out); }
:where(a:hover) { color: var(--sc-ink); }
:focus-visible { outline: 2px solid var(--sc-accent); outline-offset: 4px; border-radius: var(--sc-r-sm); }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.skip {
  position: fixed; top: var(--sc-3); left: var(--sc-3); z-index: 100;
  display: inline-flex; align-items: center; min-height: 44px;
  padding: var(--sc-2) var(--sc-4);
  background: var(--sc-accent); color: var(--sc-accent-ink);
  font-weight: 600; text-decoration: none; border-radius: var(--sc-r-sm);
  transform: translateY(-200%);
}
.skip:focus-visible { transform: none; outline-offset: 2px; }

/* ------------------------------------------------------------------ type -- */
.display {
  font-family: var(--sc-font-display);
  font-weight: 700;
  font-stretch: var(--display-stretch);
  text-transform: uppercase;
  line-height: 0.96;
  letter-spacing: -0.005em;
  margin: 0;
  text-wrap: balance;
}
.display--xl { font-size: clamp(3rem, 2rem + 5.2vw, 6.6rem); letter-spacing: -0.012em; }
.display--lg { font-size: clamp(2.4rem, 1.6rem + 3.6vw, 5rem); letter-spacing: -0.01em; }
.display--md { font-size: clamp(1.8rem, 1.3rem + 2vw, 3rem); }
.display--sm { font-size: clamp(1.25rem, 1.05rem + 0.9vw, 1.75rem); letter-spacing: 0; }
.display em { font-style: italic; }

.lede {
  font-size: var(--sc-t-lg); line-height: 1.5; letter-spacing: -0.008em;
  color: var(--sc-ink); max-width: 46ch; margin: 0;
}
.body { color: var(--sc-ink-soft); max-width: var(--sc-measure); margin: 0; }
.measure { max-width: var(--sc-measure); }
.small { font-size: var(--sc-t-sm); }
.nums, time { font-variant-numeric: tabular-nums; }
.eyebrow {
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  letter-spacing: var(--sc-track-wide); text-transform: uppercase;
  color: var(--sc-ink-soft); margin: 0;
}
.note {
  color: var(--sc-ink-soft); font-size: var(--sc-t-base);
  padding-top: var(--sc-3); border-top: 1px solid var(--sc-hairline);
  max-width: var(--sc-measure);
}
.hairline { height: 1px; border: 0; background: var(--sc-hairline); margin: 0; }

/* ---------------------------------------------------------------- layout -- */
/* Sibling navigation, supporting pages only. The landing page carries no page
   links on purpose: its austerity is the argument. An inner page has to say
   where you are and what else there is without opening the front desk, so the
   six page links sit in one quiet row under the bar, 44px each, with the
   current page marked. It wraps rather than scrolls, so nothing focusable can
   sit off screen on a narrow phone. */
.subnav { margin-top: var(--bar-h); border-bottom: 1px solid var(--sc-hairline); }
.subnav + .page { padding-top: 0; }
.subnav ul {
  display: flex; flex-wrap: wrap; column-gap: clamp(0.75rem, 2.2vw, 1.75rem);
  list-style: none; margin: 0 auto; padding: 0 var(--sc-gutter);
  max-width: var(--sc-maxw);
}
.subnav a {
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--sc-ink-soft); text-decoration: none;
  font-size: var(--sc-t-sm); font-weight: 600; letter-spacing: 0.01em;
  border-bottom: 2px solid transparent;
  transition: color 140ms var(--sc-ease-out), border-color 140ms var(--sc-ease-out);
}
.subnav a:hover { color: var(--sc-ink); }
.subnav a[aria-current="page"] { color: var(--sc-ink); border-bottom-color: var(--sc-accent); }
.page { padding-top: var(--bar-h); }
.page__head {
  padding: clamp(3rem, 8vw, 7rem) var(--sc-gutter) clamp(2rem, 5vw, 4rem);
  max-width: var(--sc-maxw); margin-inline: auto;
}
.page__head > * + * { margin-top: var(--sc-5); }
.page__body { padding: 0 var(--sc-gutter) var(--sc-section); max-width: var(--sc-maxw); margin-inline: auto; }
.page__body > * + * { margin-top: var(--sc-8); }
.stack > * + * { margin-top: var(--sc-4); }
.stack > :is(h1, h2, h3) { margin-top: var(--sc-7); }
.stack > :is(h1, h2, h3):first-child { margin-top: 0; }
.split { display: grid; gap: var(--sc-7); align-items: start; }
@media (min-width: 861px) { .split { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: var(--sc-9); } }
.split figure { margin: 0; }
.split img { width: 100%; height: auto; border-radius: var(--sc-r-md); }
.figure-note { color: var(--sc-ink-soft); font-size: var(--sc-t-sm); margin-top: var(--sc-3); }

/* ----------------------------------------------------------------- chrome -- */
.bar {
  position: fixed; inset: 0 0 auto 0; z-index: var(--sc-z-chrome);
  height: var(--bar-h);
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: var(--sc-5);
  padding-inline: var(--sc-gutter);
  border-bottom: 1px solid var(--sc-hairline);
  background: transparent;
  transition: background-color var(--sc-d-slow) var(--sc-ease-out);
}
.bar.is-solid { background: color-mix(in oklab, var(--sc-surface) 88%, transparent); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }
/* One primary per frame: the bar's CTA steps back to a ghost once the page's
   own end CTA is on screen, so two identical buttons never share a frame. */
.bar.is-quiet .bar__cta {
  background: color-mix(in oklab, var(--sc-ink) 8%, transparent); color: var(--sc-ink);
  border-color: color-mix(in oklab, var(--sc-ink) 40%, transparent); box-shadow: none;
}
.bar__mark { display: flex; align-items: center; min-height: 44px; padding-block: 0.35rem; line-height: 0; }
.bar__mark img { height: 2.6rem; width: auto; max-width: none; }
.bar__status {
  justify-self: center; text-align: center;
  color: var(--sc-ink-soft); font-size: var(--sc-t-sm); font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em; margin: 0;
}
.bar__status::before {
  content: ""; display: inline-block; width: 0.45em; height: 0.45em; border-radius: 50%;
  background: var(--sc-ink-soft); margin-right: 0.55em; vertical-align: 0.1em;
}
.bar__status.is-open::before { background: var(--sc-accent); }
.bar__cta { justify-self: end; }
.bar__mark picture { display: contents; }
/* The one control the bar gains: the front desk. Austere, hairline, 44px. */
.bar__menu {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px; padding: 0 0.9rem;
  font: inherit; font-size: var(--sc-t-sm); font-weight: 600; letter-spacing: 0.01em;
  color: var(--sc-ink); text-decoration: none; white-space: nowrap;
  background: transparent; border: 1px solid var(--sc-hairline-strong);
  border-radius: var(--sc-r-md); cursor: pointer;
  transition: border-color 140ms var(--sc-ease-out), background-color 140ms var(--sc-ease-out);
}
.bar__menu:hover { border-color: var(--sc-ink); color: var(--sc-ink); }
.bar__menu[aria-expanded="true"] { background: color-mix(in oklab, var(--sc-ink) 10%, transparent); border-color: var(--sc-ink); }
.bar { grid-template-columns: auto 1fr auto auto; }
@media (max-width: 860px) {
  /* auto 1fr auto auto, one row. The lockup becomes the crescent so the
     status line, the Menu control and the CTA all fit at 360px. */
  .bar { gap: 0.5rem; padding-inline: 0.875rem; }
  /* The crescent alone reads as a clipped logo at 28px: the lockup keeps
     its wordmark at every width and costs 14px more of a 360px bar. */
  .bar__mark img { height: 28px; width: auto; }
  .bar__mark { min-height: 44px; min-width: 44px; justify-content: center; }
  /* The row is tight, so the sentence wraps to two short lines inside the
     56px bar rather than being truncated. Every word of a fact survives. */
  .bar__status {
    justify-self: stretch; text-align: left; min-width: 0;
    line-height: 1.22; text-wrap: balance; overflow-wrap: break-word;
  }
  .bar__status::before { display: none; }
  .bar__status.is-open { color: var(--sc-ink); }
  .bar__menu { padding: 0 0.6rem; border-radius: var(--sc-r-pill); }
  .bar .bar__cta { padding: 0 0.9rem; }
}
/* Under 420px the four things in the bar cannot share one row without the
   status line starting a few pixels off the wordmark. The fact moves down to a
   row of its own, 28px at 14px, so the top row holds the mark, the Menu control
   and the CTA at full size. The line itself is untouched: same live computation,
   same no-JS fallback string, same short phone wording. */
@media (max-width: 420px) {
  :root { --bar-h: calc(3.5rem + 28px); }
  .bar { grid-template-columns: auto 1fr auto; grid-template-rows: 3.5rem 28px; align-items: center; }
  .bar__mark { grid-area: 1 / 1; }
  .bar__menu { grid-area: 1 / 2; justify-self: end; }
  .bar .bar__cta { grid-area: 1 / 3; font-size: var(--sc-t-sm); padding: 0 0.9rem; }
  .bar__status {
    grid-area: 2 / 1 / 2 / -1; align-self: start;
    min-width: 0; font-size: 0.875rem; line-height: 28px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
}
/* Contrast on the bar cannot rest on a backdrop filter alone. */
@media (prefers-reduced-transparency: reduce) {
  .bar.is-solid { background: var(--sc-surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* --------------------------------------------------------------- controls -- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5em;
  min-height: 3rem; padding: 0 1.35rem;
  font-weight: 600; font-size: var(--sc-t-base); letter-spacing: 0.005em;
  text-decoration: none; white-space: nowrap;
  border-radius: var(--sc-r-md); border: 1px solid transparent;
  transition: transform 140ms var(--sc-ease-out), background-color 140ms var(--sc-ease-out), border-color 140ms var(--sc-ease-out), color 140ms var(--sc-ease-out);
}
.btn:active { transform: translateY(1px) scale(0.985); }
.btn--primary { background: var(--sc-accent); color: var(--sc-accent-ink); box-shadow: var(--sc-e1), var(--sc-edge); outline: 1px solid transparent; outline-offset: 3px; transition: transform 140ms var(--sc-ease-out), background-color 140ms var(--sc-ease-out), outline-color 140ms var(--sc-ease-out); }
/* Hover steps an outline away from the edge. Never a glow, never at zero offset. */
.btn--primary:hover { background: color-mix(in oklab, var(--sc-accent) 88%, white); color: var(--sc-accent-ink); outline-color: var(--sc-accent); }
/* An accent ring on an accent fill is invisible: focus rings in ink instead.
   #EEF0F2 on #EE4F24 is 3.8:1, and on the page ground 17:1, so both sides pass. */
.btn--primary:focus-visible { outline: 2px solid var(--sc-ink); outline-offset: 3px; }
.btn--quiet { background: transparent; color: var(--sc-ink); border-color: var(--sc-hairline-strong); }
.btn--quiet:hover { border-color: var(--sc-ink); color: var(--sc-ink); }
/* 44px is the floor for a tap target, on a phone and on a trackpad alike. */
.btn--sm { min-height: 2.75rem; padding: 0 1rem; font-size: var(--sc-t-sm); }
@media (max-width: 860px) {
  .btn--sm { min-height: 2.75rem; border-radius: var(--sc-r-pill); }
  .btn--block { width: 100%; min-height: 3rem; }
}
.link { color: var(--sc-ink); text-decoration: underline; text-decoration-color: color-mix(in oklab, var(--sc-ink) 40%, transparent); }
.link:hover { text-decoration-color: var(--sc-accent); }
.link--quiet { color: var(--sc-ink-soft); }

/* ------------------------------------------------------------------- data -- */
.hours { border-collapse: collapse; font-variant-numeric: tabular-nums; }
.hours td, .hours th { padding: 0.2em 0; text-align: left; font-weight: inherit; vertical-align: baseline; }
.hours th { color: var(--sc-ink-soft); padding-left: 1.5em; font-weight: 450; }
.hours__time { font-family: var(--sc-font-display); font-stretch: var(--display-stretch); font-weight: 600; letter-spacing: -0.005em; white-space: nowrap; }
.hours__five { color: var(--sc-accent); font-weight: 700; }
.hours--display .hours__time { font-size: clamp(1.6rem, 4.8vh, 2.75rem); line-height: 1.05; }
.hours--display th { font-size: var(--sc-t-base); }
@media (max-width: 860px) {
  .hours--display { width: 100%; }
  .hours--display tr { display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: 1rem; padding-block: 0.3rem; border-bottom: 1px solid var(--sc-hairline); }
  .hours--display th { padding-left: 0; text-align: right; }
  .hours--display .hours__time { font-size: clamp(1.5rem, 3.4vh, 2.2rem); }
}
@media (max-width: 400px) {
  /* "Monday to Thursday" must not wrap, or the pinned close outgrows 100svh. */
  .hours--display .hours__time { font-size: clamp(1.25rem, 3.2vh, 1.9rem); }
  .hours--display th { font-size: var(--sc-t-sm); }
}

.timetable { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.timetable caption { text-align: left; color: var(--sc-ink-soft); font-size: var(--sc-t-sm); padding-bottom: var(--sc-3); caption-side: top; }
.timetable th, .timetable td { text-align: left; padding: 0.55em 1em 0.55em 0; border-bottom: 1px solid var(--sc-hairline); vertical-align: top; }
.timetable th { color: var(--sc-ink-soft); font-weight: 500; font-size: var(--sc-t-sm); }
.timetable td:first-child { font-family: var(--sc-font-display); font-stretch: var(--display-stretch); font-weight: 600; white-space: nowrap; width: 5.5em; }

.tick-gauge { display: block; width: 100%; max-width: 56rem; height: auto; }
.tick-gauge__rule { stroke: var(--sc-hairline-strong); stroke-width: 1; }
.tick-gauge__num { fill: var(--sc-ink-soft); font-family: var(--sc-font-display); font-stretch: var(--display-stretch); font-weight: 700; font-size: 30px; }
.tick-gauge__num--on { fill: var(--sc-accent); }
.tick-gauge__on { fill: var(--sc-accent); }
.tick-gauge__off { fill: none; stroke: var(--sc-ink-soft); stroke-width: 1; }

/* ----------------------------------------------------------------- footer -- */
.foot {
  border-top: 1px solid var(--sc-hairline);
  padding-top: var(--sc-5);
  color: var(--sc-ink-soft); font-size: var(--sc-t-sm);
  display: grid; gap: var(--sc-3);
}
.foot__nav { display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem; margin: 0; padding: 0; list-style: none; }
.foot__nav a { color: var(--sc-ink); text-decoration: none; }
@media (max-width: 860px) {
  .foot__nav { gap: 0 1.4rem; }
  .foot__nav a { display: block; padding-block: 0.75rem; }
}
.foot__nav a:hover { text-decoration: underline; }
.foot__meta { display: flex; flex-wrap: wrap; gap: 0.4rem 1.2rem; }
.foot__meta a { color: var(--sc-ink-soft); }
.foot__small { color: color-mix(in oklab, var(--sc-ink-soft) 88%, transparent); font-size: var(--sc-t-sm); }
.foot--page { padding-block: var(--sc-6) var(--sc-7); margin-inline: var(--sc-gutter); }
@media (min-width: 861px) { .foot--page { max-width: calc(var(--sc-maxw) - 2 * var(--sc-gutter)); margin-inline: auto; } }

/* Grain sits at 4% on this build (the plates ship without baked grain). */
.sc-grain { opacity: 0.04; contain: strict; }

/* ------------------------------------------------- supporting pages -- */
/* The entrance is native (see "supporting pages: entrances" below): no
   observer paints opacity here any more, so a browser without view timelines
   shows a finished, static document instead of a hidden one. club.js still
   marks each [data-reveal] section .is-in on entry, which is what fires the
   one-shot tick sweep on /membership. */

.page__head .lede { max-width: 52ch; }
.page__head .body { margin-top: var(--sc-4); }
.page__body > section > h2 { margin-bottom: var(--sc-5); }
.page__body > section > * + * { margin-top: var(--sc-4); }
.actions { display: flex; flex-wrap: wrap; gap: var(--sc-3); margin-top: var(--sc-6); }
.links { display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem; color: var(--sc-ink-soft); font-size: var(--sc-t-sm); }
.links a { color: var(--sc-ink); }
.quote { font-family: var(--sc-font-display); font-stretch: var(--display-stretch); font-weight: 600; font-size: clamp(1.4rem, 1.1rem + 1.4vw, 2.2rem); line-height: 1.12; text-transform: uppercase; letter-spacing: -0.005em; margin: 0; max-width: 24ch; }
.quote em { color: var(--sc-accent); font-style: normal; }
ul.plain { list-style: none; margin: 0; padding: 0; }
ul.plain > li + li { margin-top: var(--sc-2); }
.list { display: grid; gap: var(--sc-3); margin: 0; padding: 0; list-style: none; }
.list li { padding-left: 1.1em; position: relative; }
.list li::before { content: ""; position: absolute; left: 0; top: 0.72em; width: 0.4em; height: 0.4em; border-radius: 50%; background: var(--sc-accent); }
@media (min-width: 861px) { .list--2 { grid-template-columns: 1fr 1fr; column-gap: var(--sc-7); } }

/* Contact rows: label, value */
.rows { display: grid; gap: 0; margin: 0; max-width: 40rem; }
.rows > div { display: grid; grid-template-columns: 9rem minmax(0, 1fr); gap: var(--sc-3); padding: 0.75em 0; border-top: 1px solid var(--sc-hairline); align-items: baseline; }
.rows > div:last-child { border-bottom: 1px solid var(--sc-hairline); }
.rows dt { color: var(--sc-ink-soft); font-size: var(--sc-t-sm); }
.rows dd { margin: 0; }
.rows dd a { color: var(--sc-ink); }
@media (max-width: 480px) { .rows > div { grid-template-columns: 1fr; gap: 0.2rem; } }

/* Gallery of rooms (facilities) */
.rooms { display: grid; gap: var(--sc-7) var(--sc-6); margin: 0; padding: 0; list-style: none; }
@media (min-width: 861px) { .rooms { grid-template-columns: 1fr 1fr; } .rooms > li:nth-child(4n+2), .rooms > li:nth-child(4n+3) { margin-top: var(--sc-8); } }
.rooms figure { margin: 0; }
.rooms picture { display: contents; }
.rooms img { width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--sc-r-md); box-shadow: var(--sc-e1); }
.rooms figcaption { margin-top: var(--sc-4); }
.rooms figcaption > * + * { margin-top: var(--sc-2); }
.rooms h3 { margin: 0; }
.pool { padding: var(--sc-7) var(--sc-6); background: var(--sc-surface); border-radius: var(--sc-r-lg); box-shadow: var(--sc-edge); display: grid; gap: var(--sc-5); }
@media (min-width: 861px) { .pool { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; } }
.pool .hours th { padding-left: 1em; }

/* Timetable grid (classes) */
.tt-grid { display: grid; gap: var(--sc-7) var(--sc-8); }
@media (min-width: 700px) { .tt-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .tt-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.tt-grid .timetable caption { font-family: var(--sc-font-display); font-stretch: var(--display-stretch); font-weight: 700; text-transform: uppercase; color: var(--sc-ink); font-size: var(--sc-t-lg); padding-bottom: var(--sc-2); }
.tt-grid .timetable td:first-child { width: 4.5em; }
.tt-grid .timetable td:first-child.tt-five { color: var(--sc-accent); }
.tt-grid .timetable td { padding-top: 0.42em; padding-bottom: 0.42em; }

/* Class descriptions: native disclosure, no JS */
.acc { border-top: 1px solid var(--sc-hairline); }
.acc details { border-bottom: 1px solid var(--sc-hairline); }
.acc summary { cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: var(--sc-4); padding: 0.85em 0; font-weight: 600; color: var(--sc-ink); }
.acc summary::-webkit-details-marker { display: none; }
.acc summary::after { content: "+"; color: var(--sc-ink-soft); font-family: var(--sc-font-display); font-size: 1.4em; line-height: 1; transition: transform 160ms var(--sc-ease-out); }
.acc details[open] summary::after { transform: rotate(45deg); }
.acc summary:hover::after { color: var(--sc-ink); }
.acc summary .tag { color: var(--sc-ink-soft); font-size: var(--sc-t-xs); font-weight: 450; margin-left: auto; white-space: nowrap; }
.acc .body { padding: 0 0 1.2em; }

/* FAQ */
.faq { display: grid; gap: 0; }
.faq > section { padding: var(--sc-6) 0; border-top: 1px solid var(--sc-hairline); }
@media (min-width: 861px) { .faq > section { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: var(--sc-7); } }
.faq h2 { font-size: var(--sc-t-lg); line-height: 1.3; letter-spacing: -0.005em; margin: 0; font-weight: 600; }
.faq .a > * + * { margin-top: var(--sc-3); }
.faq .a { color: var(--sc-ink-soft); }
.faq .a a { color: var(--sc-ink); }

/* Team */
.team-pics { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sc-4); align-items: start; }
.team-pics figure { margin: 0; }
.team-pics figure:nth-child(2) { margin-top: clamp(2rem, 6vw, 5rem); }
.team-pics picture { display: contents; }
.team-pics img { width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; border-radius: var(--sc-r-md); box-shadow: var(--sc-e2); }
.people-list { display: grid; gap: var(--sc-5); margin: 0; padding: 0; list-style: none; }
@media (min-width: 861px) { .people-list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sc-7); } }
.people-list li { padding-top: var(--sc-3); border-top: 1px solid var(--sc-hairline); }
.people-list b { display: block; font-family: var(--sc-font-display); font-stretch: var(--display-stretch); font-weight: 700; text-transform: uppercase; font-size: var(--sc-t-lg); letter-spacing: -0.005em; }
.people-list span { color: var(--sc-ink-soft); font-size: var(--sc-t-sm); }

/* Visit */
.visit-grid { display: grid; gap: var(--sc-7); }
@media (min-width: 861px) { .visit-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sc-9); } }
.visit-figs { display: grid; gap: var(--sc-4); grid-template-columns: 1.8fr 1fr; align-items: start; }
@media (max-width: 700px) { .visit-figs { grid-template-columns: 1fr; } }
.visit-figs figure { margin: 0; }
.visit-figs picture { display: contents; }
.visit-figs img { width: 100%; height: auto; border-radius: var(--sc-r-md); box-shadow: var(--sc-e1); object-fit: cover; }
.visit-figs figure:first-child img { aspect-ratio: 16 / 9; }
.visit-figs figure:last-child img { aspect-ratio: 3 / 4; }
.visit-figs figcaption { margin-top: var(--sc-2); color: var(--sc-ink-soft); font-size: var(--sc-t-sm); line-height: 1.5; }
address { font-style: normal; }

/* A quiet CTA block that ends every supporting page */
.end { padding: var(--sc-8) 0 0; border-top: 1px solid var(--sc-hairline); display: grid; gap: var(--sc-4); }
@media (min-width: 861px) { .end { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--sc-7); } }
.end .display { font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.6rem); }
.end .body { margin-top: var(--sc-2); }
.stack > .quote, .page__body .quote { margin-top: var(--sc-6); }

/* ------------------------------------------------------- the front desk -- */
/* One control in the bar opens this. It indexes nothing on the landing page:
   it lists the other documents, the real hours and a phone number, which is a
   front desk, not a chapter list, so the filmic one-shot survives intact.
   Without JavaScript it is a plain navigation block at the foot of the page:
   the correct failure mode is too much navigation, never none. */
.sheet {
  border-top: 1px solid var(--sc-hairline);
  background: var(--sc-canvas);
  padding: var(--sc-7) var(--sc-gutter) var(--sc-8);
}
.js .sheet {
  position: fixed; inset: var(--bar-h) 0 0 0; z-index: calc(var(--sc-z-chrome) - 1);
  overflow-y: auto; overscroll-behavior: contain;
  padding: var(--sc-6) var(--sc-gutter) var(--sc-8);
  border-top: 0; contain: layout paint;
  /* visibility flips at once on the way in, so the first link is focusable in
     the same tick the panel opens; on the way out it waits for the fade. */
  visibility: hidden; opacity: 0;
  transition: opacity 160ms var(--sc-ease-out), visibility 0s linear 160ms;
}
.js .sheet.is-open { visibility: visible; opacity: 1; transition: opacity 160ms var(--sc-ease-out), visibility 0s; }
@media (prefers-reduced-motion: reduce) { .js .sheet { transition: none; } }
html.is-desk { overflow: hidden; scrollbar-gutter: stable; }
.sheet__inner { max-width: var(--sc-maxw); margin-inline: auto; display: grid; gap: var(--sc-7); }
@media (min-width: 861px) { .sheet__inner { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: var(--sc-9); } }
.sheet h2 { margin: 0 0 var(--sc-4); }
.sheet ul { list-style: none; margin: 0; padding: 0; }
.sheet__nav li { border-top: 1px solid var(--sc-hairline); }
.sheet__nav li:last-child { border-bottom: 1px solid var(--sc-hairline); }
.sheet__nav a {
  display: block; min-height: 44px; padding: 0.7rem 0; text-decoration: none; color: var(--sc-ink);
}
.sheet__nav b {
  display: block; font-family: var(--sc-font-display); font-stretch: var(--display-stretch);
  font-weight: 700; text-transform: uppercase; font-size: var(--sc-t-lg);
  letter-spacing: -0.005em; line-height: 1.1;
}
.sheet__nav span { display: block; color: var(--sc-ink-soft); font-size: var(--sc-t-sm); line-height: 1.5; }
.sheet__nav a:hover b { color: var(--sc-accent); }
.sheet__nav a[aria-current="page"] b::after {
  content: ""; display: inline-block; width: 0.34em; height: 0.34em; border-radius: 50%;
  background: var(--sc-accent); margin-left: 0.5em; vertical-align: 0.2em;
}
.sheet__side > * + * { margin-top: var(--sc-6); }
.sheet__side .hours { width: 100%; }
.sheet__side .hours__time { font-size: var(--sc-t-base); }
.sheet__side .hours th { font-size: var(--sc-t-base); }
.sheet__contact li { border-top: 1px solid var(--sc-hairline); }
.sheet__contact li:last-child { border-bottom: 1px solid var(--sc-hairline); }
.sheet__contact a {
  display: flex; align-items: center; min-height: 44px; padding: 0.5rem 0;
  color: var(--sc-ink); text-decoration: none; font-size: var(--sc-t-sm); line-height: 1.5;
}
.sheet__contact a:hover { text-decoration: underline; text-decoration-color: var(--sc-accent); }
.sheet__cta { margin-top: var(--sc-6); width: 100%; }
.sheet__trial { color: var(--sc-ink-soft); font-size: var(--sc-t-base); line-height: 1.55; margin-top: var(--sc-3); max-width: 52ch; }

/* -------------------------------------------- readability and targets --- */
/* Every in-page anchor lands its target below the fixed bar. */
:target { scroll-margin-top: calc(var(--bar-h) + 1.5rem); }
/* Running answers get a measure; 62ch at 18px is 55 to 78 characters. */
.faq .a, .faq .a p { max-width: var(--sc-measure); }
.rooms figcaption .body, .split .body { max-width: var(--sc-measure); }
@media (max-width: 860px) {
  /* Tap targets: footer and contact lines match what .foot__nav a already does. */
  .foot__meta a, .links a, .close__lines a {
    display: inline-flex; align-items: center; min-height: 44px;
  }
  .foot__meta { gap: 0 1.2rem; }
  .links { gap: 0 1.4rem; }
}

/* ============================================================================
   THE ENHANCEMENT LAYER
   ----------------------------------------------------------------------------
   Everything below sits on top of the shipped film. Nothing here changes an
   act's span, a bank window, the strike curve or a photographic grade.

   Motion gate, stated once and used everywhere below: the engine writes
   --sc-p on every live act on every frame regardless of the reader's motion
   setting, so a rule that consumes --sc-p ships motion to a reader who asked
   for none unless it is gated by hand. Every moving rule below therefore sits
   inside @media (prefers-reduced-motion: no-preference) AND
   :root:not([data-calm]) (?calm=1, set synchronously in each page's head).
   The default when either applies is the composed end state.
   ========================================================================== */

@property --gy    { syntax: "<percentage>"; inherits: false; initial-value: 116%; }
@property --sweep { syntax: "<number>";     inherits: false; initial-value: 1; }
@property --q     { syntax: "<number>";     inherits: false; initial-value: 8; }
@property --t     { syntax: "<number>";     inherits: true;  initial-value: 1; }

/* ---------------------------------------------- the ground: first light -- */
/* One fixed layer behind everything: a soft band of sky that starts below the
   frame and rises through it as the page is read. A horizon, not a glow, and
   no pointer term at all: this build spends the pointer on the torch alone.
   Painted at 320x200 and composited up 6.4x, which is the cheapest thing a
   browser can put behind a page. No canvas, no shader, no rAF, no timer. */
#ground {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  contain: layout paint;
}
/* Transparent at its foot so the engine's own canvas drift (hero #0A0C10
   through close #1E1D1B, painted by body) still reads through the ground
   exactly as it does today. The band is light added, never a sheet laid over. */
.ground__paint {
  position: absolute; left: 50%; top: 50%;
  width: 320px; height: 200px;
  transform: translate(-50%, -50%) scale(6.4);
  transform-origin: 50% 50%;
  /* 26% of soft lower shoulder and 9% of soft upper shoulder, so at 6.4x
     there is no visible edge and no banding. The alphas are the contrast
     budget: the brightest pixel the ground can composite is the warm stop,
     and it is held dark enough that --sc-ink-soft over it still measures
     better than 6:1 at the worst scroll position on the page. */
  background: linear-gradient(to top,
    transparent 0%,
    color-mix(in oklab, #16243F 42%, transparent) calc(var(--gy) - 26%),
    color-mix(in oklab, color-mix(in oklab, var(--sc-accent) 12%, #16243F) 72%, transparent) var(--gy),
    transparent calc(var(--gy) + 9%));
}
@supports (animation-timeline: scroll(root)) {
  @media (prefers-reduced-motion: no-preference) {
    :root:not([data-calm]) .ground__paint {
      animation: dawn linear both; animation-timeline: scroll(root block);
    }
  }
}
@keyframes dawn { from { --gy: 116%; } to { --gy: 38%; } }
/* Reduce gets a resting position, not the animation's first frame. */
@media (prefers-reduced-motion: reduce) { .ground__paint { --gy: 62%; } }
:root[data-calm] .ground__paint { --gy: 62%; }

/* ------------------------------------------- the bar's wordmark settles -- */
/* Native scroll timeline, so it runs off the main thread entirely. */
.bar__mark img { transform-origin: 0 50%; }
@supports (animation-timeline: scroll(root)) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 861px) {
    :root:not([data-calm]) .bar__mark img {
      animation: markset linear both;
      animation-timeline: scroll(root block); animation-range: 0 100vh;
    }
  }
}
@keyframes markset { from { transform: scale(1); } to { transform: scale(0.82); } }

/* --------------------------------------- supporting pages: entrances ----- */
/* Replaces the observer-driven fade. Compositor only, off the main thread,
   0 KB of JavaScript. The range is bounded inside `entry` on both ends so a
   section taller than the viewport can never sit faded for a screen of scroll,
   which is what `cover` would do to /faq and /classes. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    :root:not([data-calm]) :is(
      .page__body > section, .page__body > figure, .rooms > li, .tt-grid > *, .faq > section
    ) {
      animation: rise linear both;
      animation-timeline: view(); animation-range: entry 5% entry 45%;
    }
  }
}
@keyframes rise { from { opacity: 0; transform: translate3d(0, 16px, 0); } to { opacity: 1; transform: none; } }
/* The timetable rises without fading. axe composites the colour it actually
   sees, so a table still part-way through an opacity ramp is scored as a real
   1.29:1 contrast failure and costs /classes four accessibility points on
   every audit, for a row that is genuinely near-invisible while it enters. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    :root:not([data-calm]) :is(.tt-grid > *, .page__body > section:has(table)) { animation-name: riseonly; }
  }
}
@keyframes riseonly { from { transform: translate3d(0, 16px, 0); } to { transform: none; } }

/* /membership: the 100-tick figure fills once, on entry, and holds. It is an
   entrance, not a scroll-linked gauge: the ticks are not a progress readout
   and never move again once they are up. club.js adds .is-in exactly once.

   Deliberately class-driven rather than animation-timeline: view(). The 75
   ticks are SVG <rect> children of one 56rem-wide figure, so a view() timeline
   would need a named timeline on the <svg> plus a per-band animation-range to
   keep the stagger, in exchange for an observer that is eight lines long and
   is also the only entrance path on a browser without view(). Reopening this
   costs more code than it saves. */
@media (prefers-reduced-motion: no-preference) {
  :root.js:not([data-calm]) .tick-gauge__on {
    transform-box: fill-box; transform-origin: 50% 100%;
    transform: scaleY(0);
  }
  :root:not([data-calm]) .is-in .tick-gauge__on {
    animation: tickup 420ms var(--sc-ease-out) both;
  }
  /* Bands of 15. The <title> is child 1, so the 75 filled ticks are children
     2 to 76 and the band boundaries are offset by one. */
  :root:not([data-calm]) .is-in .tick-gauge__on:nth-child(n+17) { animation-delay: 90ms; }
  :root:not([data-calm]) .is-in .tick-gauge__on:nth-child(n+32) { animation-delay: 180ms; }
  :root:not([data-calm]) .is-in .tick-gauge__on:nth-child(n+47) { animation-delay: 270ms; }
  :root:not([data-calm]) .is-in .tick-gauge__on:nth-child(n+62) { animation-delay: 360ms; }
}
@keyframes tickup { from { transform: scaleY(0); } to { transform: scaleY(1); } }

/* ----------------------------------- the site becomes one object --------- */
/* Cross-document view transitions. The front desk does not move: the bar and
   the one button stay nailed in place while the room behind them changes.
   Chromium and Safari 18.2+ morph; Firefox does today's ordinary navigation.
   0 KB, nothing to polyfill.

   Lighthouse reports a phantom CLS of exactly 1.0 on this page in a minority
   of runs. It is not this at-rule: the deployed pre-enhancement build reports
   the same 1.0000 six runs out of six on the same machine, and eight real page
   loads here measure 0.0005, a same-URL navigation 0.0004, and a footer link
   out of the fully scrolled landing page 0, with the transition running. */
@view-transition { navigation: auto; }
.bar { view-transition-name: club-bar; }
.bar__cta { view-transition-name: club-cta; }
/* Named on the supporting pages' footer only. The landing page's footer lives
   inside a pinned act eleven screens down, and naming it there would fly an
   off-screen snapshot across the frame for nothing. */
.foot--page { view-transition-name: club-foot; }
/* Mandatory explicit opt-out: the UA default cross-fade is still motion. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}
:root[data-calm]::view-transition-group(*),
:root[data-calm]::view-transition-old(*),
:root[data-calm]::view-transition-new(*) { animation: none !important; }

/* -------------------------------------------------- the companion cursor -- */
/* A 28px rack pin, not a ring. It lags the pointer, squares up to what it is
   over, and dies over every table and every form control. The system cursor is
   never hidden: there is no `cursor: none` anywhere in this build. Every part
   of it is pointer-events: none, in every state, so it cannot change a hit
   area or intercept a click. */
.cur {
  position: fixed; left: 0; top: 0; z-index: calc(var(--sc-z-chrome) + 2);
  width: 28px; height: 28px;
  border: 1px solid var(--sc-accent); border-radius: 2px;
  background: transparent;
  pointer-events: none; opacity: 0; will-change: transform;
  transition: opacity 160ms var(--sc-ease-out), width 180ms var(--sc-ease-out),
              height 180ms var(--sc-ease-out), border-radius 180ms var(--sc-ease-out),
              background-color 180ms var(--sc-ease-out);
}
.cur.is-on { opacity: 1; }
/* Over a link it shrinks and dims: the underline stays the affordance. */
.cur.is-on.cur--link { width: 10px; height: 10px; opacity: 0.5; }
/* Over a photograph it becomes a small filled dot, so it never sits on a face. */
.cur.is-on.cur--dot { width: 12px; height: 12px; border-radius: 50%; background: var(--sc-accent); }
/* Over a control it seats into the box, 1px accent inset 4px from the edge.
   It can never be mistaken for the focus ring, which is 2px --sc-ink at 3px
   offset on the OUTSIDE of the same edge, and focusin hides this entirely. */
.cur.is-on.cur--box { width: var(--cw, 28px); height: var(--ch, 28px); border-radius: var(--sc-r-md); }
.cur.is-on.cur--cta { border-color: var(--sc-accent-ink); background: color-mix(in oklab, var(--sc-accent-ink) 12%, transparent); }
/* Tables, form controls and the peak stage: gone. The I-beam is information,
   and on the peak the torch is the cursor. */
.cur.cur--off { opacity: 0; }
@media (max-width: 860px), (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .cur { display: none; }
}

/* ============================================================================
   ROUND-1 FIXES
   ========================================================================== */

/* ---- no JavaScript: every word still renders ----------------------------- */
/* scrollcraft.css sets `[data-sc-cue] { opacity: 0 }` with no `.js` guard, and
   `.js` is added by the first inline script in <head>, so a reader without
   JavaScript got photographs with 15 of the page's 52 text blocks invisible:
   the hero lede, the 75% cap claim, the 2,050m2 claim, the whole People
   section, the address and the Directions link. Because `.js` is set
   synchronously whenever scripts run, this rule can never apply on the normal
   path and costs it nothing. */
html:not(.js) :is([data-sc-cue], [data-sc-in], [data-sc-reveal], [data-sc-kinetic],
                  [data-sc-stagger] > *, [data-reveal]) {
  opacity: 1 !important;
  transform: none !important;
  clip-path: none !important;
  visibility: visible !important;
}
/* Making them visible is only half of it. Without JavaScript the engine never
   adds .sc-stage or .sc-act--pinned, so a stage has no height and is not a
   containing block: every absolutely positioned copy block escaped its own act
   and three of them piled up on the hero. The stages get their height and
   their position back, the rail becomes an ordinary scroll region (which is
   what the engine's own reduced-motion floor does), and the close grows to fit
   instead of clipping its footer. */
html:not(.js) [data-sc-stage] { position: relative; min-height: 100svh; }
html:not(.js) [data-sc-act="pan"] [data-sc-stage] {
  overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain;
}
html:not(.js) .close__stage { height: auto; min-height: 100svh; }
html:not(.js) .people [data-sc-stage] { min-height: 0; }

/* ---- focus never lands on an invisible element --------------------------- */
/* The reveal wrappers are driven by an observer and by scroll progress, and
   programmatic focus scrolling does not advance either, so a keyboard user
   could tab onto a link at opacity 0. Written at the wrapper, so anything
   focusable added inside a reveal block later is covered too. */
:is([data-sc-in], [data-sc-reveal], [data-sc-cue], [data-reveal]):focus-within,
:is([data-sc-in], [data-sc-reveal], [data-sc-cue], [data-reveal]):focus-within > * {
  opacity: 1 !important;
  transform: none !important;
  clip-path: none !important;
}

/* ---- the bar carries its own contrast ------------------------------------ */
/* The bar is transparent until the hero un-pins, so before that its live
   status line sits on whatever the act composites underneath. The hero's
   corner scrim relaxes from 1 to 0.55 across p 0.55 to 0.90 (an act effect),
   which took "Open now, until 21:00" down to 2.4:1 for the last quarter of the
   hero. This scrim belongs to the bar, not to any act, so no act can move it. */
.bar::before {
  content: ""; position: absolute; inset: 0 0 auto 0;
  height: calc(100% + 1.75rem);
  background: linear-gradient(to bottom,
    color-mix(in oklab, #06080B 86%, transparent) 0%,
    color-mix(in oklab, #06080B 72%, transparent) 55%,
    transparent 100%);
  pointer-events: none; z-index: -1;
  transition: opacity var(--sc-d-slow) var(--sc-ease-out);
}
/* Once the bar has its own surface the scrim is redundant. */
.bar.is-solid::before { opacity: 0; }
@media (prefers-reduced-transparency: reduce) { .bar::before { opacity: 0; } }

/* ---- tap targets ---------------------------------------------------------- */
/* Three-letter footer labels occupied a 30px column at 360. */
.foot__nav a { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; }
/* Standalone links: a link that is the only thing in its paragraph, or the
   value half of a definition row, is a control, not inline prose, so the
   WCAG inline exemption does not cover it. Links inside running sentences
   are deliberately left alone. */
.rows dd > a:only-child,
.links a,
.sheet__contact a,
p > a.link:only-child {
  display: inline-flex; align-items: center; min-height: 44px;
}
/* A link that ends a running sentence keeps its place in the line: the hit box
   grows by padding and the negative margin hands the space straight back, so
   the leading is unchanged and the rect measures 44px. */
.where__copy .link, .rail__lead .link, .faq .a a, .rows dd a, .note a {
  display: inline-block; padding-block: 10px; margin-block: -10px;
}
.foot__meta a { min-width: 44px; justify-content: center; }

/* ---- the class list stops being a spreadsheet ----------------------------- */
/* At 1440 the row ran the full 1170px content width, so a class name at x 135
   had its tag and its "+" at x 1300, sixteen times down the page. */
.acc { max-width: 52rem; }

/* ---- the stage has its height before the engine mounts -------------------- */
/* Root cause of the CLS 1.0000 that three critics and the previous round all
   saw and none of us pinned down. The engine adds `.sc-stage` (height: 100svh,
   sticky) in JavaScript. Until it lands the stage is zero-height, so the
   `container-type: size` query inside `.frame-host` resolves against the wrong
   box: measured under 4x CPU and slow 4G, the hero plate sits at 412x459 and
   then jumps to 412x673 at y 150 the moment the engine mounts, at t 1708 ms.
   That is one full-viewport element moving a full viewport, which is exactly
   1.0. Declaring the same height up front makes the mount a no-op. */
:is([data-sc-act="pin"], [data-sc-act="pan"], [data-sc-act="scrub"]) > [data-sc-stage] { height: 100svh; }

/* ---- paint containment on every full-bleed layer -------------------------- */
/* The plan names #ground, .relight__stage and .sheet. Lighthouse then reported
   CLS 1.0000 in three runs of three, attributed to a nodeless full-viewport
   shift, and the previous round's note reached the same place: five sticky
   full-viewport stages plus a fixed grain overlay, none of them telling the
   engine that a full-bleed layer cannot affect layout outside itself. */
.relight__stage, .sheet, .hero__stage, .rail__stage, .close__stage,
.where [data-sc-stage], .people [data-sc-stage] { contain: layout paint; }

/* ---- the middots at the close do not orphan at 360 ----------------------- */
.close__lines .sep { white-space: nowrap; }

