/* ==========================================================================
   10th Planet Jiu Jitsu Portland
   pages.css — hero, level ladder, mat board, facility, program & contact
   ========================================================================== */

/* --------------------------------------------------------------------------
   HERO + STATS — one composition.

   The previous version put the photograph in a hard-edged right-hand panel that
   dead-ended at the hero's bottom edge, with a bordered stats band starting
   immediately below it. That read as two unrelated objects with a seam between
   them. Here the photograph is full-bleed behind the whole hero and the stats
   sit inside it as a translucent strip over the same image, so there is nothing
   left to seam. No contour lines over the photograph — the gradient carries it.
   -------------------------------------------------------------------------- */

:root {
  /* Matches the left edge of .shell on wide screens, collapses to the standard
     page padding on narrow ones. */
  --gutter: max(var(--pad-x), calc((100vw - var(--shell)) / 2 + var(--pad-x)));
}

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: clamp(620px, 92vh, 940px);
  background-color: var(--ink);
  color: var(--on-ink);
}

/* --- Full-bleed photograph --- */
.hero-bg {
  position: absolute;
  inset: -3%;                  /* slack, so the parallax never exposes an edge */
  z-index: -3;
  will-change: transform;
  transition: transform 0.9s cubic-bezier(0.16, 0.84, 0.24, 1);
}

/* Legibility gradient. Heaviest bottom-left where the copy and stats sit,
   clearing toward the top-right so the photograph still reads as a photograph. */
.hero-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg, rgba(0, 11, 31, 0.93) 0%, rgba(0, 11, 31, 0.70) 30%, rgba(0, 11, 31, 0.26) 58%, rgba(0, 11, 31, 0.34) 100%),
    linear-gradient(to top, rgba(0, 11, 31, 0.86) 0%, transparent 38%),
    radial-gradient(ellipse 78% 68% at 13% 44%, rgba(0, 102, 255, 0.34), transparent 68%);
}

.hero-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 42%;
}

/* --- Copy --- */
.hero-body {
  position: relative;
  flex: 1;
  display: grid;
  align-items: center;
  padding-block: calc(var(--nav-total) + clamp(2.5rem, 2rem + 3vw, 4.5rem)) clamp(2.5rem, 2rem + 3vw, 4rem);
}
.hero-copy { max-width: 40rem; }
.hero-copy .d1 {
  color: #fff;
  margin-top: 1.15rem;
  font-size: clamp(2.625rem, 1.9rem + 3.1vw, 4.5rem);
  text-shadow: 0 4px 40px rgba(0, 6, 20, 0.5);
}
/* White, not the muted ink tone -- the lede sits over the photograph rather
   than a flat panel, and the mid tone lost the lighter passages of the image.
   Same shadow treatment as the h1 so both hold up wherever the photo lands. */
.hero-copy .lede {
  margin-top: 1.35rem;
  max-width: 34rem;
  color: #fff;
  text-shadow: 0 2px 18px rgba(0, 6, 20, 0.55);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2.25rem;
}
.hero-fine {
  margin-top: 1.5rem;
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--on-ink-dim);
}

/* --- Stats, inside the hero --- */
.hero-stats {
  position: relative;
  border-top: 1px solid rgba(150, 190, 245, 0.16);
  background: rgba(2, 12, 30, 0.42);
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
}

.hero-stats-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin: 0;
}
@media (min-width: 860px) {
  .hero-stats-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.hero-stat {
  position: relative;
  display: block;
  padding: clamp(1.1rem, 0.9rem + 1vw, 1.75rem) clamp(1rem, 0.7rem + 1.4vw, 2rem);
  border-left: 1px solid rgba(150, 190, 245, 0.14);
  transition: background var(--dur-fast) var(--ease);
}
.hero-stat:nth-child(odd) { border-left: 0; }
@media (min-width: 860px) {
  .hero-stat:nth-child(odd) { border-left: 1px solid rgba(150, 190, 245, 0.14); }
  .hero-stat:first-child { border-left: 0; }
}
@media (max-width: 859px) {
  .hero-stat:nth-child(n+3) { border-top: 1px solid rgba(150, 190, 245, 0.14); }
}

.hero-stat:hover { background: rgba(0, 102, 255, 0.12); }
/* The accent slides up from the bottom on hover, echoing the class rails on the
   schedule board rather than inventing a new hover language. */
.hero-stat::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--blue);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform var(--dur) var(--ease);
}
.hero-stat:hover::after,
.hero-stat:focus-visible::after { transform: scaleY(1); }

.hero-stat-num {
  display: block;
  margin: 0;
  font-family: var(--mono);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.875rem, 1.4rem + 1.9vw, 2.75rem);
  letter-spacing: -0.045em;
  line-height: 1;
  color: #fff;
  transition: color var(--dur-fast) var(--ease);
}
.hero-stat:hover .hero-stat-num { color: #6FA8FF; }

.hero-stat-stars {
  display: inline-flex;
  gap: 1px;
  margin-left: 0.55rem;
  vertical-align: 0.42em;
  color: #FFC53D;
}
.hero-stat-stars svg { width: 11px; height: 11px; }

.hero-stat dd { margin: 0.5rem 0 0; }
.hero-stat dd b {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #fff;
}
.hero-stat dd span {
  display: block;
  margin-top: 0.2rem;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--on-ink-mid);
}

@media (prefers-reduced-motion: reduce) {
  .hero-bg { transition: none !important; transform: none !important; }
}

/* --------------------------------------------------------------------------
   STEPS — a descending staircase.
   Each step sits lower and one tread further right than the one before, joined
   by an elbow connector, so the layout carries the sequence rather than leaving
   the numerals to do it alone. Numbering is justified here because the content
   really is ordered. Collapses to a plain vertical list on narrow screens,
   where a staircase would just be an indent.
   -------------------------------------------------------------------------- */

.stair {
  /* One tread depth drives the indent AND the elbow's horizontal run, so the
     connector always lands exactly on the next numeral. */
  --tread: clamp(1.5rem, 5vw, 6rem);
  --sgap: clamp(1rem, 0.8rem + 1vw, 1.75rem);
  --node: 3.25rem;

  list-style: none;
  margin: clamp(2.5rem, 2rem + 3vw, 4rem) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sgap);
}

.stair-step {
  position: relative;
  display: grid;
  grid-template-columns: var(--node) minmax(0, 1fr);
  gap: clamp(1rem, 0.8rem + 1vw, 1.5rem);
  align-items: start;
}

/* The tread: each step shifts right by one tread and the card narrows, so the
   run descends diagonally instead of stacking. */
@media (min-width: 900px) {
  .stair-step { width: calc(100% - 2 * var(--tread)); }
  .stair-step:nth-child(2) { margin-left: var(--tread); }
  .stair-step:nth-child(3) { margin-left: calc(2 * var(--tread)); }
}

/* The connector: down from this numeral, then across into the next one. Drawn
   as two borders on one box with a rounded corner, so it is a single elbow
   rather than two segments that have to be kept in sync. */
.stair-step::before {
  content: '';
  position: absolute;
  left: calc(var(--node) / 2 - 1px);
  top: var(--node);
  width: 0;
  height: calc(100% - var(--node) + var(--sgap) + var(--node) / 2);
  border-left: 2px solid rgba(0, 102, 255, 0.32);
  pointer-events: none;
}
@media (min-width: 900px) {
  .stair-step::before {
    width: var(--tread);
    border-bottom: 2px solid rgba(0, 102, 255, 0.32);
    border-bottom-left-radius: 16px;
  }
}
.stair-step:last-child::before { display: none; }

.stair-n {
  display: grid;
  place-items: center;
  width: var(--node);
  height: var(--node);
  margin: 0;
  border-radius: 50%;
  border: 1px solid rgba(0, 102, 255, 0.28);
  background: var(--paper);
  box-shadow: 0 2px 12px rgba(0, 11, 31, 0.07);
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--blue);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.stair-step:hover .stair-n {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}

.stair-card {
  position: relative;
  overflow: hidden;
  padding: clamp(1.35rem, 1.1rem + 1.1vw, 2rem);
  border-radius: var(--r-lg);
  border: 1px solid rgba(0, 102, 255, 0.14);
  background: linear-gradient(112deg, #FFFFFF 0%, #EDF4FE 52%, #D8E7FA 100%);
  box-shadow: var(--lift-1);
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}
.stair-step:hover .stair-card {
  transform: translateY(-3px);
  box-shadow: var(--lift-2);
  border-color: rgba(0, 102, 255, 0.34);
}

/* Accent down the leading edge, wiping in on hover. */
.stair-card::after {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: linear-gradient(to bottom, var(--blue), #6FA8FF);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform var(--dur) var(--ease);
}
.stair-step:hover .stair-card::after { transform: scaleY(1); }

.stair-card h3 {
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 118%;
  font-size: clamp(1.25rem, 1.1rem + 0.6vw, 1.625rem);
  line-height: 1.05;
  text-transform: uppercase;
  color: var(--ink);
}
.stair-card p {
  margin-top: 0.6rem;
  max-width: 56ch;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--slate);
}
.stair-card .link-go { margin-top: 1.1rem; }

/* --------------------------------------------------------------------------
   PROGRAMS SWITCHER
   The four names as a list on the left, a large photograph and its copy on the
   right. Real tablist semantics, so it works from the keyboard, and every panel
   stays in the DOM — hidden panels are still crawlable, and with JavaScript off
   all four simply show in sequence.
   -------------------------------------------------------------------------- */

.switch {
  display: grid;
  gap: clamp(1.5rem, 1.2rem + 2vw, 3rem);
  margin-top: clamp(2.5rem, 2rem + 3vw, 4rem);
}
@media (min-width: 980px) {
  .switch { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); align-items: start; }
}

/* --- the list of names --- */
.switch-tabs {
  display: flex;
  gap: 0.35rem;
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: 0.25rem;
  margin-inline: calc(var(--pad-x) * -1);
  padding-inline: var(--pad-x);
}
.switch-tabs::-webkit-scrollbar { display: none; }
@media (min-width: 980px) {
  .switch-tabs {
    flex-direction: column;
    overflow: visible;
    margin-inline: 0;
    padding-inline: 0;
    position: sticky;
    top: 6.5rem;
  }
}

.switch-tab {
  flex: 0 0 auto;
  display: flex;
  gap: 0.85rem;
  align-items: stretch;
  text-align: left;
  padding: 0.9rem 1.1rem;
  border-radius: var(--r);
  background: var(--paper);
  border: 1px solid var(--rule-light);
  cursor: pointer;
  transition:
    background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
}
@media (min-width: 980px) { .switch-tab { width: 100%; } }

.switch-tab:hover { border-color: color-mix(in srgb, var(--rail) 45%, transparent); transform: translateX(2px); }

.switch-tab[aria-selected='true'] {
  background: linear-gradient(112deg, #FFFFFF 0%, #EDF4FE 55%, #DCE9FB 100%);
  border-color: color-mix(in srgb, var(--rail) 55%, transparent);
  box-shadow: var(--lift-1);
  transform: none;
}

/* The rail is the tell for which one is live, and it uses the same colour the
   class carries everywhere else on the site. */
.switch-rail {
  flex: 0 0 3px;
  border-radius: 2px;
  background: var(--rail);
  opacity: 0.28;
  transition: opacity var(--dur-fast) var(--ease);
}
.switch-tab[aria-selected='true'] .switch-rail { opacity: 1; }

.switch-name {
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 116%;
  font-size: 1.0625rem;
  line-height: 1.1;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}
.switch-meta {
  display: block;
  margin-top: 0.2rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--slate);
  white-space: nowrap;
}

/* --- the panel --- */
.switch-panels { position: relative; }

.switch-panel {
  display: grid;
  gap: clamp(1.25rem, 1rem + 1.5vw, 2rem);
}
.switch-panel[hidden] { display: none; }
@media (min-width: 720px) {
  .switch-panel { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; }
}

/* Fades in on switch. Deliberately short — this fires on a click, so anything
   longer reads as lag rather than polish. */
.switch-panel.is-entering { animation: switch-in 0.32s var(--ease) both; }
@keyframes switch-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .switch-panel.is-entering { animation: none; }
}

.switch-media {
  margin: 0;
  border-radius: var(--r-lg);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--ink-2);
  border: 1px solid var(--rule-light);
  box-shadow: var(--lift-2);
}
@media (min-width: 720px) { .switch-media { aspect-ratio: 4 / 4.6; } }
.switch-media img { width: 100%; height: 100%; object-fit: cover; }

.switch-copy .chip { margin-bottom: 1rem; }
.switch-copy .d4 { color: var(--ink); }
.switch-copy > p {
  margin-top: 0.85rem;
  max-width: 46ch;
  font-size: 1rem;
  line-height: 1.62;
  color: var(--slate);
}

.switch-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 1.35rem;
  padding: 0;
  list-style: none;
}
.switch-pills li {
  padding: 0.4rem 0.8rem;
  border-radius: 100px;
  border: 1px solid var(--rule-light);
  background: var(--paper);
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--slate);
}
.switch-copy .btn { margin-top: 1.75rem; }

/* --------------------------------------------------------------------------
   THE CURRICULUM — a comparison matrix.
   Real <table> markup: the content genuinely is a comparison, so screen readers
   and crawlers should see it as one. Each column carries its level's own colour,
   the same one the schedule board uses.
   -------------------------------------------------------------------------- */

/* The table scrolls sideways on a phone, so say so rather than leaving people
   to discover it. Hidden once all three columns fit. */
.matrix-swipe {
  display: none;
  margin: clamp(2.5rem, 2rem + 3vw, 4rem) 0 0.75rem;
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--on-ink-dim);
}

.matrix-wrap {
  margin-top: clamp(2.5rem, 2rem + 3vw, 4rem);
  padding: clamp(1rem, 0.8rem + 1vw, 1.75rem);
  border: 1px solid rgba(150, 190, 245, 0.22);
  border-radius: var(--r-lg);
  /* Its own dark ground. The section's line-field gradient is too light for a
     data table to hold contrast against on its own. */
  background: rgba(1, 9, 24, 0.66);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

/* Below 900px the table is replaced outright by one card per level, built from
   the table's own cells in main.js. A 720px-wide comparison table on a 390px
   screen meant a sideways swipe to read a row, and the swipe hint was an apology
   for that rather than a fix. The table stays in the markup — it is still the
   honest structure for the content, and it is what a crawler reads. */
.matrix-stack { display: none; }
@media (max-width: 899px) {
  .matrix-swipe,
  .matrix-wrap { display: none; }
  .matrix-stack { display: grid; gap: 0.9rem; margin-top: clamp(1.5rem, 1.2rem + 1.5vw, 2.25rem); }
}

.mstack {
  border: 1px solid color-mix(in srgb, var(--rail) 30%, rgba(150, 190, 245, 0.2));
  border-radius: var(--r-lg);
  background: rgba(1, 9, 24, 0.66);
  overflow: hidden;
}
.mstack-head {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  padding: 0.85rem 1.15rem;
  background: color-mix(in srgb, var(--rail) 16%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--rail) 26%, transparent);
}
.mstack-code {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--rail) 76%, #fff);
}
.mstack-name {
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 108%;
  font-size: 0.9375rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: #fff;
}
.mstack-list { margin: 0; padding: 0.25rem 1.15rem 0.9rem; display: grid; }
/* Label beside the value, not stacked above it. Four of the six rows hold two or
   three words, and giving each of those its own line doubled the card's height for
   nothing. Long values still wrap inside their own column. */
.mstack-list > div {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  align-items: baseline;
  gap: 0.25rem 0.85rem;
  padding: 0.6rem 0;
  border-bottom: 1px dotted rgba(150, 190, 245, 0.18);
}
.mstack-list > div:last-child { border-bottom: 0; padding-bottom: 0.15rem; }
.mstack-list dt {
  font-family: var(--mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  line-height: 1.6;
  text-transform: uppercase;
  color: var(--on-ink-dim);
}
.mstack-list dd {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.45;
  color: var(--on-ink);
}
@media (max-width: 359px) {
  /* At 320px a 7.5rem label column leaves nothing for the value. */
  .mstack-list > div { grid-template-columns: 1fr; gap: 0.2rem; }
}
.matrix-stack-go { margin-top: 0.25rem; }
.mstack-list dd .link-go { color: color-mix(in srgb, var(--rail) 70%, #fff); }

.matrix {
  width: 100%;
  min-width: 720px;                 /* below this it scrolls rather than crushes */
  border-collapse: separate;
  border-spacing: 0;
}

/* --- column heads --- */
.lvl-head {
  vertical-align: bottom;
  width: 27%;
  padding: 0 clamp(0.75rem, 0.6rem + 0.6vw, 1.25rem) 1rem;
  text-align: left;
  border-bottom: 2px solid var(--rail);
}
.lvl-code {
  display: block;
  font-family: var(--mono);
  font-weight: 700;
  font-size: clamp(1.75rem, 1.4rem + 1.6vw, 2.5rem);
  letter-spacing: -0.05em;
  line-height: 1;
  color: var(--rail);
}
.lvl-name {
  display: block;
  margin-top: 0.45rem;
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 116%;
  font-size: 0.875rem;
  line-height: 1.15;
  text-transform: uppercase;
  color: #fff;
}

/* --- rows --- */
.matrix tbody th {
  width: 19%;
  padding: clamp(0.85rem, 0.7rem + 0.5vw, 1.1rem) 1.25rem clamp(0.85rem, 0.7rem + 0.5vw, 1.1rem) 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid rgba(150, 190, 245, 0.20);
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(206, 222, 247, 0.86);
}
.matrix tbody td {
  padding: clamp(0.85rem, 0.7rem + 0.5vw, 1.1rem) clamp(0.75rem, 0.6rem + 0.6vw, 1.25rem);
  vertical-align: top;
  border-bottom: 1px solid rgba(150, 190, 245, 0.20);
  font-size: 0.9375rem;
  line-height: 1.5;
  color: #EDF3FD;
  transition: background var(--dur-fast) var(--ease);
}

/* A faint wash of each level's colour down its column, so the eye can follow a
   single level down the table without counting across. */
.matrix tbody td { background: color-mix(in srgb, var(--rail) 10%, transparent); }
.matrix tbody tr:hover td { background: color-mix(in srgb, var(--rail) 18%, transparent); }

/* Short values read as data; the long ones read as prose. */
.matrix tbody tr:nth-child(-n+4) td {
  font-family: var(--mono);
  font-size: 0.875rem;
  color: #fff;
}

.matrix tfoot td {
  padding: 1.25rem clamp(0.75rem, 0.6rem + 0.6vw, 1.25rem) 0;
  vertical-align: top;
}
.matrix tfoot th {
  padding: 1.25rem 1.25rem 0 0;
  text-align: left;
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(206, 222, 247, 0.86);
}
.matrix tfoot .link-go { color: var(--rail); }

.matrix-note {
  margin-top: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem);
  max-width: 68ch;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: rgba(214, 228, 250, 0.84);
}
.matrix-note strong { color: #fff; font-weight: 600; }

/* --------------------------------------------------------------------------
   THE ACADEMY — a photo mosaic.
   "2,500 square feet and room to roll" is a claim about a space, and one photo
   cannot carry it. Four tiles at three sizes: a large lead shot, two squares and
   a wide one. Each carries a short caption naming what it shows, so the mosaic
   is evidence rather than decoration.
   -------------------------------------------------------------------------- */

.mosaic {
  display: grid;
  gap: clamp(0.5rem, 0.4rem + 0.5vw, 0.85rem);
  margin-top: clamp(2.5rem, 2rem + 3vw, 4rem);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 860px) {
  .mosaic {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-auto-rows: clamp(150px, 17vw, 240px);
  }
  /* The lead shot takes a 2x2 block; the wide one closes the second row. */
  .mos-lead { grid-column: span 2; grid-row: span 2; }
  .mos-wide { grid-column: span 2; }
}

.mos {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: var(--r);
  background: var(--ink-2);
  border: 1px solid var(--rule-light);
  aspect-ratio: 1;
}
@media (min-width: 860px) { .mos { aspect-ratio: auto; } }

.mos img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.1s var(--ease);
}
.mos:hover img { transform: scale(1.05); }

/* This frame has a thumb in the lower-left corner, so it is pulled up past it. */
.mos-wide img { object-position: 50% 20%; }

.mos figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: clamp(0.7rem, 0.6rem + 0.5vw, 1.1rem);
  background: linear-gradient(to top, rgba(0, 11, 31, 0.9) 0%, rgba(0, 11, 31, 0.5) 55%, transparent 100%);
}
.mos figcaption b {
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 116%;
  font-size: clamp(0.8125rem, 0.76rem + 0.25vw, 1rem);
  line-height: 1.1;
  text-transform: uppercase;
  color: #fff;
}
.mos figcaption span {
  display: block;
  margin-top: 0.2rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(214, 228, 250, 0.72);
}
.mos-lead figcaption b { font-size: clamp(1rem, 0.9rem + 0.6vw, 1.375rem); }

/* --- the foot: hours as data, amenities as pills --- */
.academy-foot {
  display: grid;
  gap: clamp(1.5rem, 1.2rem + 1.6vw, 2.5rem);
  margin-top: clamp(2rem, 1.6rem + 1.6vw, 3rem);
  padding-top: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem);
  border-top: 1px solid var(--rule-light);
}
@media (min-width: 860px) {
  .academy-foot { grid-template-columns: auto minmax(0, 1fr); align-items: start; }
}

.academy-hours {
  display: flex;
  flex-wrap: wrap;
  gap: 0 clamp(1.5rem, 1.2rem + 1.6vw, 2.75rem);
  margin: 0;
}
.academy-hours dt {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--slate);
}
.academy-hours dd {
  margin: 0.3rem 0 0;
  font-family: var(--mono);
  font-size: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--ink);
}

.academy-meta { display: flex; flex-direction: column; align-items: flex-start; gap: 1.25rem; }
@media (min-width: 860px) { .academy-meta { align-items: flex-end; } }

.facility-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.facility-facts li {
  padding: 0.45rem 0.9rem;
  border-radius: 100px;
  border: 1px solid rgba(0, 102, 255, 0.18);
  background: linear-gradient(150deg, #FFFFFF 0%, #EDF4FE 100%);
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--slate);
}

/* --------------------------------------------------------------------------
   WHAT 10TH PLANET IS — an origin timeline.
   Four dated nodes on a single rule. No numbering here: the years already carry
   the sequence. The node for this academy's own founding is marked, so the story
   lands somewhere instead of trailing off.
   -------------------------------------------------------------------------- */

.tl {
  list-style: none;
  margin: clamp(2.5rem, 2rem + 3vw, 4rem) 0 0;
  padding: 0;
  display: grid;
  gap: clamp(1.75rem, 1.4rem + 1.6vw, 2.5rem);
}
@media (min-width: 880px) {
  /* One variable drives the column gap AND how far each node's rule reaches, so
     the line is continuous across the row instead of breaking at every gap. */
  .tl {
    --tlgap: clamp(1rem, 0.6rem + 1.4vw, 2rem);
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--tlgap);
  }
}

.tl-node {
  position: relative;
  padding-top: 2.5rem;
}

/* The rule and its marker. */
.tl-node::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.5rem;
  width: 100%;
  height: 2px;
  background: rgba(0, 102, 255, 0.22);
}
@media (min-width: 880px) {
  /* Reach across the gap into the next node. The last one stops at its column,
     so the line ends with the timeline rather than running off. */
  .tl-node::before { width: calc(100% + var(--tlgap)); }
  .tl-node:last-child::before { width: 100%; }
}
.tl-node::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--blue);
  background: var(--mist);
}
.tl-node.is-here::after {
  background: var(--blue);
  box-shadow: 0 0 0 4px rgba(0, 102, 255, 0.18);
}

.tl-year {
  font-family: var(--mono);
  font-size: clamp(1.5rem, 1.25rem + 1.1vw, 2.125rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--blue);
}
.tl-node.is-here .tl-year { color: var(--ink); }

.tl-head {
  margin-top: 0.7rem;
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 118%;
  font-size: clamp(1rem, 0.94rem + 0.35vw, 1.1875rem);
  line-height: 1.1;
  text-transform: uppercase;
  color: var(--ink);
}
.tl-text {
  margin-top: 0.55rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--slate);
}

/* --- the foot: the positions the system is known for, and the badge --- */
.tl-foot {
  display: grid;
  gap: clamp(1.75rem, 1.4rem + 2vw, 3rem);
  margin-top: clamp(2.5rem, 2rem + 2.4vw, 4rem);
  padding-top: clamp(2rem, 1.6rem + 1.6vw, 3rem);
  border-top: 1px solid var(--rule-light);
}
@media (min-width: 880px) {
  .tl-foot { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
}

.tl-moves {
  display: grid;
  gap: 1px;
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--rule-light);
  border-radius: var(--r);
  overflow: hidden;
}
@media (min-width: 620px)  { .tl-moves { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .tl-moves { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.tl-moves li {
  padding: 1rem 1.1rem;
  background: linear-gradient(158deg, #FFFFFF 0%, #EDF4FE 100%);
}
.tl-moves b {
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 116%;
  font-size: 0.9375rem;
  text-transform: uppercase;
  color: var(--ink);
}
.tl-moves span {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--slate);
}

.tl-stamp {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.1rem;
}
@media (min-width: 880px) { .tl-stamp { align-items: center; } }
.tl-stamp img {
  width: clamp(130px, 14vw, 190px);
  height: auto;
  opacity: 0.9;
}

/* --------------------------------------------------------------------------
   FROM THE MATS — a single marquee row.
   One track, six quotes, drifting slowly enough to read. The track holds the
   set twice, so translating exactly -50% lands on the copy and the loop has no
   visible seam; the duplicate set is aria-hidden in the markup so each quote is
   announced once. Cards are deliberately lighter than the section behind them
   so they read as objects sitting on the navy, not holes cut into it.
   -------------------------------------------------------------------------- */

.mq-rating {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.85rem 1.5rem;
  margin-top: 1.35rem;
}

.mq {
  margin-top: clamp(2.5rem, 2rem + 3vw, 4rem);
  /* Fade both edges so cards enter and leave instead of being chopped. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}

/* The row is padded so the cards' shadows and glow are not clipped by the
   overflow that makes the marquee work. */
.mq-row { overflow: hidden; padding: 0.75rem 0; }

.mq-track {
  display: flex;
  align-items: stretch;
  gap: clamp(0.85rem, 0.7rem + 0.6vw, 1.35rem);
  width: max-content;
  will-change: transform;
}
/* Slow: about 35px a second, so a card stays legible as it crosses. */
.mq-a { animation: mq-drift 84s linear infinite; }

/* Hovering anywhere in the marquee holds the row, so a quote you want to read
   stops moving. */
.mq:hover .mq-track { animation-play-state: paused; }

@keyframes mq-drift {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.mq-card {
  flex: 0 0 auto;
  width: clamp(292px, 31vw, 424px);
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin: 0;
  padding: clamp(1.35rem, 1.1rem + 1.1vw, 1.85rem);
  border-radius: var(--r-lg);
  /* Lifted well clear of --ink so the card edge is unmistakable: a lighter
     ground, a visible border, and a soft blue halo separating it from the
     section behind. */
  border: 1px solid rgba(132, 178, 245, 0.42);
  background: linear-gradient(158deg, #17427C 0%, #0F3164 52%, #0A2549 100%);
  box-shadow:
    0 24px 50px -22px rgba(0, 0, 0, 0.92),
    0 0 0 1px rgba(0, 102, 255, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.10);
}

.mq-stars {
  display: flex;
  gap: 3px;
  color: #FFC53D;
}
.mq-stars svg { width: 15px; height: 15px; }

.mq-card blockquote {
  font-size: clamp(1rem, 0.95rem + 0.35vw, 1.1875rem);
  line-height: 1.5;
  color: #F2F7FE;
  text-wrap: pretty;
}
.mq-card blockquote::before { content: '\201C'; }
.mq-card blockquote::after  { content: '\201D'; }
.mq-card blockquote strong { color: #fff; font-weight: 600; }

.mq-card figcaption {
  margin-top: auto;
  padding-top: 0.9rem;
  border-top: 1px solid rgba(132, 178, 245, 0.22);
}
.mq-card figcaption b {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 112%;
  font-size: 0.875rem;
  text-transform: uppercase;
  color: #fff;
}
.mq-card figcaption span {
  display: block;
  margin-top: 0.25rem;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(206, 224, 250, 0.74);
}

/* No motion: the row stops and becomes a plain horizontal scroller, so every
   quote is still reachable. */
@media (prefers-reduced-motion: reduce) {
  .mq-track { animation: none !important; }
  .mq-row {
    overflow-x: auto;
    scrollbar-width: thin;
    padding-bottom: 0.75rem;
  }
}
/* --------------------------------------------------------------------------
   THE MAT BOARD — a detail strip, then the week.
   The live read and the colour key sit above the grid, because both explain the
   grid and neither belongs inside it. Below, the seven days run three across as
   one card each: the day name centred in its own bar, then one hairline-ruled
   row per class — programme colour as a dot, the class, the time hard right so
   a column of times can be scanned. The free-class tile closes the last row.
   Nothing decorates the card edge: the seven-colour gradient that used to run
   along the top read as a progress bar and fought the dots for attention, which
   are the real colour signal. All 54 classes stay as real text. Padding is
   deliberately generous — a nine-row card with tight rows reads as a wall of
   text. Sunday is pinned under Saturday and the free-class tile takes the rest
   of that row, so the block resolves square. Two dark cards break out of the
   light ground — the live read above the grid and the free-class tile below it —
   and they are the only two, which is what makes them read as calls to attention
   rather than as more timetable.
   -------------------------------------------------------------------------- */

/* --- Detail strip --- */
.board-top { display: grid; gap: clamp(0.75rem, 0.6rem + 0.6vw, 1.25rem); }
@media (min-width: 980px) { .board-top { grid-template-columns: 1.15fr 1fr; align-items: stretch; } }

.strip-h {
  font-family: var(--mono); font-size: 0.5625rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--slate);
}

.board-now {
  position: relative; overflow: hidden;
  border-radius: var(--r-lg);
  padding: clamp(1.15rem, 1rem + 0.6vw, 1.6rem);
  display: grid; gap: 1.1rem;
  background:
    radial-gradient(120% 140% at 100% 0%, rgba(0, 102, 255, 0.42) 0%, transparent 58%),
    linear-gradient(150deg, var(--ink) 0%, #0A2454 58%, #113A7E 100%);
  color: var(--on-ink);
  box-shadow: 0 22px 44px -28px rgba(0, 20, 60, 0.6);
}
@media (min-width: 620px) { .board-now { grid-template-columns: 1.3fr 1fr; align-items: center; } }
.board-now .strip-h { color: #8CBCFF; }
.board-now-main { display: grid; gap: 0.7rem; }
.board-live-item { display: grid; gap: 0.15rem; }
.board-live-item + .board-live-item { border-top: 1px solid rgba(150, 190, 245, 0.2); padding-top: 0.7rem; }
.board-live-key {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--mono); font-size: 0.625rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: rgba(206, 224, 250, 0.85);
}
.board-live-val { font-size: var(--t-sm); color: #fff; }
.board-live-val em { font-style: normal; color: rgba(206, 224, 250, 0.82); }
.board-now-cta {
  display: grid; gap: 0.8rem; justify-items: start;
  padding-top: 0.9rem; border-top: 1px solid rgba(150, 190, 245, 0.2);
}
@media (min-width: 620px) {
  .board-now-cta {
    padding-top: 0; border-top: 0;
    padding-left: clamp(1rem, 0.7rem + 1.4vw, 2rem);
    border-left: 1px solid rgba(150, 190, 245, 0.2);
  }
}
.board-now-cta p { font-size: var(--t-sm); color: rgba(219, 231, 250, 0.9); }
.board-now-cta b { color: #fff; }
.pulse {
  width: 8px; height: 8px; border-radius: 50%; background: #2FD08A;
  box-shadow: 0 0 0 0 rgba(47, 208, 138, 0.6);
  animation: pulse 2.4s ease-out infinite;
}
.pulse.is-idle { background: var(--slate); animation: none; box-shadow: none; }
@keyframes pulse {
  70%  { box-shadow: 0 0 0 9px rgba(47, 208, 138, 0); }
  100% { box-shadow: 0 0 0 0 rgba(47, 208, 138, 0); }
}

/* --- Colour key, as a row of chips --- */
.board-key {
  border-radius: var(--r-lg); border: 1px solid var(--rule-light); background: #fff;
  padding: clamp(1.15rem, 1rem + 0.6vw, 1.6rem);
  display: grid; gap: 0.9rem; align-content: start;
  box-shadow: 0 18px 38px -30px rgba(0, 20, 60, 0.42);
}
.key-row { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.key-item {
  display: inline-flex; align-items: baseline; gap: 0.45rem;
  padding: 0.4rem 0.7rem; border-radius: 100px;
  background: color-mix(in srgb, var(--rail) 14%, #fff);
  border: 1px solid color-mix(in srgb, var(--rail) 32%, var(--rule-light));
}
.key-item b {
  font-family: var(--mono); font-size: 0.625rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: color-mix(in srgb, var(--rail) 40%, #06142C);
}
.key-item span { font-size: 0.75rem; color: var(--slate); }

/* --- Filters --- */
.board-bar { margin: clamp(1.25rem, 1rem + 0.8vw, 1.75rem) 0; }
.board-filters { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.filt {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.95rem; border-radius: 100px;
  border: 1px solid color-mix(in srgb, var(--rail, var(--blue)) 34%, var(--rule-light));
  background: color-mix(in srgb, var(--rail, var(--blue)) 8%, #fff);
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: color-mix(in srgb, var(--rail, var(--blue)) 45%, var(--ink));
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.filt::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--rail, var(--blue)); flex: 0 0 auto; }
.filt:hover { transform: translateY(-1px); background: color-mix(in srgb, var(--rail, var(--blue)) 16%, #fff); }
.filt[aria-pressed='true'] { background: var(--rail, var(--blue)); border-color: var(--rail, var(--blue)); color: #fff; }
.filt[aria-pressed='true']::before { background: #fff; }
.filt-all::before { background: var(--blue); }

/* --- The grid --- */
.board { display: grid; gap: clamp(0.9rem, 0.7rem + 0.8vw, 1.5rem); align-items: start; }
@media (min-width: 620px)  { .board { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .board { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* Seven days never fill three columns evenly, and the sixth and seventh are the
   two shortest. So Saturday and Sunday share one cell as a stack (built in
   main.js), which lands beside Thursday and Friday and uses the height those two
   set instead of leaving a slab of white column under Saturday. That makes six
   grid items, two full rows, and the free-class tile takes a full-width row of
   its own underneath. */
.board-stack {
  display: grid;
  gap: clamp(0.9rem, 0.7rem + 0.8vw, 1.5rem);
  align-content: start;
  min-width: 0;
}
@media (min-width: 620px) {
  .board-cta-tile { grid-column: 1 / -1; }
}

.board-col {
  position: relative;
  border-radius: var(--r-lg);
  border: 1px solid var(--rule-light);
  background: #fff;
  box-shadow: 0 18px 38px -30px rgba(0, 20, 60, 0.42);
  overflow: hidden;
  transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.board-col:hover { transform: translateY(-2px); box-shadow: 0 26px 48px -28px rgba(0, 20, 60, 0.5); }
.board-col.is-quiet { opacity: 0.38; }
.board-col.is-today {
  border-color: color-mix(in srgb, var(--blue) 55%, #fff);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--blue) 16%, transparent),
              0 22px 44px -26px rgba(0, 60, 160, 0.45);
}
/* Weekend cards get a warmer ground, so five-days-a-week reads at a glance. */
.board-col.is-weekend .board-col-head { background: #FFF6EC; }

/* The day name sits centred and letter-spaced, the way the printed board reads.
   The count and the "today" flag are taken out of the flow, so the centring is
   optical and does not shift when one card gains a badge the others lack. */
.board-col-head {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  min-height: 3.5rem;
  padding: 1.05rem clamp(2.75rem, 2.3rem + 1.5vw, 3.5rem);
  background: var(--mist);
  border-bottom: 1px solid var(--rule-light);
}
.board-col-head b {
  font-family: var(--display); font-weight: 700; font-stretch: 112%;
  font-size: 0.875rem; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--ink); text-align: center; line-height: 1.2;
}
.board-col-head em {
  position: absolute; left: 0.7rem; top: 50%; transform: translateY(-50%);
  font-style: normal; font-family: var(--mono); font-size: 0.5625rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  padding: 0.22rem 0.45rem; border-radius: 100px; background: var(--blue); color: #fff;
}
.board-col-head span {
  position: absolute; right: 0.7rem; top: 50%; transform: translateY(-50%);
  font-family: var(--mono); font-size: 0.6875rem; font-weight: 700;
  min-width: 1.6rem; text-align: center;
  padding: 0.18rem 0.42rem; border-radius: 100px;
  background: #fff; border: 1px solid var(--rule-light); color: var(--slate);
}
.board-col.is-today .board-col-head { background: var(--blue); border-bottom-color: var(--blue); }
.board-col.is-today .board-col-head b { color: #fff; }
.board-col.is-today .board-col-head em { background: #fff; color: var(--blue); }
.board-col.is-today .board-col-head span { background: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.36); color: #fff; }

/* Rows sit flush and are separated by a rule rather than floating as pills —
   a timetable is a list, and a list reads faster without gaps in it. */
/* `ul.board-list`, not `.board-list`: base.css resets `ul[class] { padding: 0 }`
   at specificity 0,1,1, which beats a lone class selector. That is why this card
   had no inner padding at all — the rows ran to the card's edge. Qualifying with
   the element ties the specificity and pages.css wins on source order. */
ul.board-list {
  list-style: none; margin: 0;
  padding: clamp(0.5rem, 0.4rem + 0.35vw, 0.75rem) clamp(1.05rem, 0.85rem + 0.6vw, 1.45rem);
  display: grid; gap: 0;
}

/* --- A class --- */
/* Rows are given real height. Nine of them stacked at 0.6rem of padding read as
   one block of text; at 0.85rem each line is its own thing to look at. The
   negative inline margin lets the hover tint bleed to the card's inner edge
   without the text losing its measure. */
.slot {
  display: flex; align-items: center; gap: 0.85rem;
  padding: 0.85rem 0.6rem;
  border-bottom: 1px dotted var(--rule-light);
  border-radius: var(--r-sm);
  transition: background var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
/* Inside a card the rule and the hover tint reach past the text toward the
   card's edge, which is how a printed timetable rules its rows. Scoped to the
   card so the homepage strip, which has no padding to bleed into, is unaffected. */
.board-list .slot { margin: 0 -0.6rem; }
.slot:last-child { border-bottom: 0; }
.slot:hover { background: color-mix(in srgb, var(--rail) 11%, transparent); }
/* The programme colour, carried by a dot rather than a filled row — seven
   colours as backgrounds turn a nine-class day into a swatch book. */
.slot-dot {
  flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%;
  background: var(--rail);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rail) 16%, transparent);
}
.slot-body { flex: 1 1 auto; min-width: 0; display: grid; gap: 0.2rem; }
.slot-name { font-size: 0.875rem; font-weight: 600; line-height: 1.35; color: var(--ink); }
.slot-note { font-size: 0.6875rem; line-height: 1.4; color: var(--slate); }
.slot-time {
  flex: 0 0 auto; margin-left: auto; padding-left: 0.5rem;
  white-space: nowrap; text-align: right;
  font-family: var(--mono); font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.03em;
  /* One accent for every time, not the programme colour. The dot already carries
     the programme; colouring the time too made a nine-row card read as seven
     different kinds of thing, and the column of times stopped reading as a
     column. 70/30 toward ink keeps 11px bold blue over 4.5:1 on white. */
  color: color-mix(in srgb, var(--blue) 70%, #06142C);
}
.slot.is-dim { opacity: 0.2; }
.slot.is-now {
  background: var(--blue); border-bottom-color: transparent;
  box-shadow: 0 8px 20px -10px rgba(0, 102, 255, 0.7);
}
.slot.is-now .slot-time, .slot.is-now .slot-name, .slot.is-now .slot-note { color: #fff; }
.slot.is-now .slot-dot { background: #fff; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.3); }
.board-empty {
  padding: 1.35rem 0.6rem; text-align: center;
  font-family: var(--mono); font-size: var(--t-xs); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--slate);
}

@media (prefers-reduced-motion: reduce) {
  .board-col:hover { transform: none; }
  .pulse { animation: none; }
}

/* The free-class tile that closes the last row of the board. It lays out wide
   rather than tall on purpose: stacked, it made the last row twice Sunday's
   height and left a hole beside it. Copy left, button right keeps the row the
   height of the two-class Sunday card. */
.board-cta-tile {
  display: grid; align-content: center; justify-items: start; gap: 1.1rem;
  align-self: stretch;
  border-radius: var(--r-lg);
  padding: clamp(1.35rem, 1.1rem + 1vw, 1.9rem);
  border: 1px solid rgba(0, 102, 255, 0.38);
  background:
    radial-gradient(130% 150% at 100% 0%, rgba(0, 102, 255, 0.36) 0%, transparent 62%),
    linear-gradient(150deg, rgba(10, 28, 58, 0.95), rgba(4, 18, 43, 0.98));
  box-shadow: 0 26px 52px -34px rgba(0, 0, 0, 0.8);
}
.board-cta-copy { display: grid; gap: 0.45rem; }
.board-cta-tile b {
  font-family: var(--display); font-weight: 800; font-stretch: 112%;
  font-size: var(--t-lg); line-height: 1.15; color: #fff;
}
.board-cta-tile p { font-size: var(--t-sm); line-height: 1.5; color: var(--on-ink-mid); max-width: 48ch; }
/* Horizontal only once the tile actually spans two columns. In the two-column
   range it is one column wide, and side-by-side squeezed the copy to a 90px
   measure. */
@media (min-width: 1000px) {
  .board-cta-tile {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center; justify-items: start;
    gap: clamp(1.25rem, 0.9rem + 1.6vw, 2.25rem);
  }
}

/* --------------------------------------------------------------------------
   PROGRAM PAGE BLOCKS
   -------------------------------------------------------------------------- */

.who { display: grid; gap: 1px; background: var(--rule-light); border-radius: var(--r); overflow: hidden; }
@media (min-width: 720px) { .who { grid-template-columns: repeat(2, 1fr); } }
.who > div { background: var(--paper); padding: 1.35rem 1.5rem; }
.who dt {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue);
}
.who dd { margin-top: 0.4rem; font-size: 0.9375rem; line-height: 1.55; color: var(--slate); }

/* Every gallery on the site holds three images, so a 3-up row is the right
   shape. The earlier 2x2-span-plus-two arrangement left an empty cell. */

/* --------------------------------------------------------------------------
   CONTACT
   -------------------------------------------------------------------------- */

.contact-grid { display: grid; gap: clamp(2rem, 1.5rem + 3vw, 4rem); }
@media (min-width: 940px) { .contact-grid { grid-template-columns: 1fr 1fr; align-items: start; } }

.contact-card {
  padding: clamp(1.5rem, 1.2rem + 1.5vw, 2.25rem);
  background: var(--paper);
  border: 1px solid var(--rule-light);
  border-radius: var(--r-lg);
  box-shadow: var(--lift-1);
}

/* --------------------------------------------------------------------------
   DARK FORM SURFACES — the contact panel and the free-trial modal.
   .form-panel is the shared scope: the base .field styles in components.css are
   tuned for light grounds, so anything sitting on a dark panel needs its own
   input, label and error colours. Both forms carry the class, and the programme
   picker below is shared between them rather than written twice.
   Inputs are set at 1rem deliberately: iOS zooms the viewport on focus for
   anything under 16px, which on a phone reads as the page jumping when you tap a
   field. Radii are 10-14px — rounded, not pill.
   -------------------------------------------------------------------------- */

.cform-panel {
  position: relative;
  overflow: hidden;
  padding: clamp(1.5rem, 1.2rem + 1.6vw, 2.5rem);
  border-radius: var(--r-lg);
  border: 1px solid rgba(140, 188, 255, 0.16);
  background:
    radial-gradient(125% 125% at 100% 0%, rgba(0, 102, 255, 0.2) 0%, transparent 58%),
    linear-gradient(168deg, #0A1C3A 0%, #04122B 52%, #000B1F 100%);
  box-shadow: 0 30px 60px -34px rgba(0, 6, 20, 0.85);
}
/* One lit hairline along the top edge — the panel's only ornament. */
.cform-panel::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(140, 188, 255, 0.45) 26%, var(--blue) 50%, rgba(140, 188, 255, 0.45) 74%, transparent);
}

/* --- Fields --- */
.form-grid { display: grid; gap: clamp(0.9rem, 0.8rem + 0.5vw, 1.25rem); }
@media (min-width: 560px) { .form-grid { grid-template-columns: 1fr 1fr; } }

/* The base .field carries a bottom margin for stacked forms; here the grid gap
   does that job, so it would double up. */
.form-panel .field { margin-bottom: 0; }
.form-panel .field > span { color: rgba(206, 224, 250, 0.8); }
.form-panel .field > span .req { color: #8CBCFF; }

.form-panel .field input {
  padding: 0.9rem 1.05rem;
  font-size: 1rem;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid rgba(140, 188, 255, 0.18);
  color: #fff;
  transition: border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.form-panel .field input::placeholder { color: rgba(190, 208, 236, 0.42); }
.form-panel .field input:hover { border-color: rgba(140, 188, 255, 0.32); }
.form-panel .field input:focus {
  background: rgba(255, 255, 255, 0.075);
  border-color: var(--blue);
  outline: none;
  box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.3);
}
.form-panel .field input[aria-invalid='true'] {
  border-color: #FF6B76;
  background: rgba(255, 77, 90, 0.1);
  box-shadow: none;
}
/* Has to out-specify the base `.field > .field-error` rule, hence the child
   combinator here too. */
.form-panel .field > .field-error,
.form-panel .picks > .field-error { color: #FFA3AB; }

/* --- Program picker --- */
.picks { margin: clamp(1.35rem, 1.1rem + 1vw, 1.85rem) 0 0; padding: 0; border: 0; min-width: 0; }
.picks legend {
  padding: 0;
  margin-bottom: 0.6rem;
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(206, 224, 250, 0.8);
}
.picks legend .req { color: #8CBCFF; }

/* Two up from 560px. Four across would give each card about 110px in this column
   and "Women's Only Jiu-Jitsu" does not fit in that. Stacked below. */
.picks-row { display: grid; gap: 0.6rem; }
@media (min-width: 560px) { .picks-row { grid-template-columns: 1fr 1fr; } }

/* height:100% on both so a two-line card ("Women's Only Jiu-Jitsu") and a
   one-line card next to it end up the same height instead of leaving the shorter
   one floating in a stretched cell. */
.pick { position: relative; display: block; min-width: 0; height: 100%; }
/* The input covers the card rather than hiding off-screen, so the whole card is
   the hit area and focus still lands somewhere real. */
.pick input {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.pick-face {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  height: 100%;
  min-height: 3.4rem;
  padding: 0.8rem 0.95rem;
  border: 1px solid rgba(140, 188, 255, 0.18);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.035);
  transition: border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.pick-dot {
  flex: 0 0 auto;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--rail);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rail) 22%, transparent);
}
.pick-name {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 106%;
  font-size: 0.9375rem;
  line-height: 1.2;
  color: rgba(255, 255, 255, 0.88);
}
.pick-tick {
  flex: 0 0 auto;
  opacity: 0;
  transform: scale(0.6);
  color: var(--rail);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}

.pick:hover .pick-face { border-color: rgba(140, 188, 255, 0.34); background: rgba(255, 255, 255, 0.062); }
.pick input:focus-visible + .pick-face { outline: 2px solid var(--blue); outline-offset: 2px; }

/* Selected state says it four ways — border, ground, glow, tick. One signal is
   easy to miss on a phone in daylight, and getting this wrong means someone
   submits the form for a programme they did not choose. */
.pick input:checked + .pick-face {
  border-color: color-mix(in srgb, var(--rail) 68%, transparent);
  background: color-mix(in srgb, var(--rail) 13%, rgba(255, 255, 255, 0.05));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--rail) 42%, transparent),
              0 0 24px -8px color-mix(in srgb, var(--rail) 60%, transparent);
}
.pick input:checked + .pick-face .pick-name { color: #fff; }
.pick input:checked + .pick-face .pick-tick { opacity: 1; transform: none; }

/* Nothing picked, and the form was submitted. */
.picks[data-invalid] .pick-face { border-color: rgba(255, 107, 118, 0.5); }

/* --- Submit --- */
.form-submit {
  width: 100%;
  justify-content: center;
  gap: 0.6rem;
  min-height: 3.5rem;
  margin-top: clamp(1.35rem, 1.1rem + 1vw, 1.85rem);
  font-size: 0.9375rem;
  letter-spacing: 0.12em;
}
.form-submit svg { transition: transform var(--dur-fast) var(--ease); }
.form-submit:hover svg { transform: translateX(4px); }

.form-panel .form-note { color: rgba(206, 224, 250, 0.68); }
.form-panel .form-note a { color: #8CBCFF; text-decoration: underline; text-underline-offset: 2px; }
.form-panel .form-status { color: #A8D8FF; }

@media (prefers-reduced-motion: reduce) {
  .form-submit:hover svg { transform: none; }
  .pick-tick { transition: none; }
}

.nap { display: grid; gap: 1.25rem; }
.nap-item { display: flex; gap: 0.9rem; align-items: flex-start; }
.nap-ico {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  border-radius: 10px;
  background: var(--mist);
  color: var(--blue);
}
.nap-ico svg { width: 17px; height: 17px; }
.nap-item dt {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--slate);
}
.nap-item dd { margin-top: 0.2rem; font-size: 1rem; font-weight: 600; color: var(--ink); }
.nap-item dd a:hover { color: var(--blue); }
.nap-item dd small { display: block; margin-top: 0.15rem; font-size: 0.8125rem; font-weight: 400; color: var(--slate); }

/* --------------------------------------------------------------------------
   OPENING HOURS — the ledger, with the week drawn next to it.
   Seven rows of times do not show that the weekend is short until you have read
   all seven of them; a bar per day says it at a glance. The bar's percentages are
   derived from the same numbers as the text in the row beside it, so the drawing
   and the type cannot disagree.
   -------------------------------------------------------------------------- */

.hours-stats {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.7rem;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
}
.hours-stat {
  padding: 0.9rem 1rem;
  border-radius: var(--r);
  border: 1px solid var(--rule-light);
  background: var(--paper);
  box-shadow: var(--lift-1);
}
.hours-stat b {
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 112%;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--ink);
}
.hours-stat b i { font-style: normal; font-size: 0.5em; letter-spacing: 0.06em; margin-left: 0.12em; color: var(--slate); }
.hours-stat span {
  display: block;
  margin-top: 0.35rem;
  font-family: var(--mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--slate);
}

.hours-card {
  margin-top: 1.5rem;
  padding: clamp(1.1rem, 0.9rem + 0.8vw, 1.55rem);
  border-radius: var(--r-lg);
  border: 1px solid var(--rule-light);
  background: var(--paper);
  box-shadow: var(--lift-1);
}
.hours-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding-bottom: 0.8rem;
  border-bottom: 1px solid var(--rule-light);
}
.hours-status {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.32rem 0.65rem;
  border-radius: 100px;
  background: var(--mist);
  border: 1px solid var(--rule-light);
  font-family: var(--mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--slate);
  white-space: nowrap;
}
.hours-status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--slate); flex: 0 0 auto; }
/* Green only when the mats are actually open, driven by the same liveState() the
   board and the nav strip read. */
.hours-status.is-open {
  background: color-mix(in srgb, #2FD08A 12%, #fff);
  border-color: color-mix(in srgb, #2FD08A 36%, transparent);
  color: #0E7A50;
}
.hours-status.is-open .hours-status-dot { background: #17A667; box-shadow: 0 0 0 3px rgba(47, 208, 138, 0.22); }

.hours-table { width: 100%; border-collapse: collapse; margin-top: 0.35rem; }
.hours-table th,
.hours-table td { padding: 0.6rem 0; border-bottom: 1px solid var(--rule-light); text-align: left; vertical-align: middle; }
.hours-table tbody tr:last-child th,
.hours-table tbody tr:last-child td { border-bottom: 0; }
.hours-table th {
  width: 1%;
  white-space: nowrap;
  padding-right: clamp(0.75rem, 0.6rem + 0.5vw, 1.25rem);
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--slate);
}
.hours-track { width: auto; padding-right: clamp(0.75rem, 0.6rem + 0.5vw, 1.25rem); }
.hours-time {
  width: 1%;
  white-space: nowrap;
  text-align: right;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink);
}

.hours-bar {
  position: relative;
  display: block;
  height: 6px;
  min-width: 5rem;
  border-radius: 100px;
  background: rgba(0, 11, 31, 0.06);
}
.hours-bar::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: var(--from);
  right: calc(100% - var(--to));
  border-radius: inherit;
  background: linear-gradient(90deg, color-mix(in srgb, var(--blue) 55%, #fff), var(--blue));
}

/* Today gets the row, not just the type: a tinted ground, a chip after the day
   name, and the bar at full strength. */
.hours-table tr.is-today th,
.hours-table tr.is-today .hours-time { color: var(--blue); font-weight: 700; }
.hours-table tr.is-today th::after {
  content: 'Today';
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0.1rem 0.36rem;
  border-radius: 100px;
  background: var(--blue);
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: #fff;
  vertical-align: 0.1em;
}
.hours-table tr.is-today .hours-bar { background: var(--blue-14); }
.hours-table tr.is-today .hours-bar::after { background: linear-gradient(90deg, var(--blue), #0043C4); }

/* The axis sits in the track column so it lines up with the bars above it. */
.hours-axis-row td { border-bottom: 0; padding-top: 0.55rem; padding-bottom: 0; }
.hours-axis {
  display: flex;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--slate);
}
.hours-axis i { font-style: normal; }

/* Below 560px the day name and the time range already fill the card's width, and
   the bar column collapsed to about 60px — too narrow to mean anything, and the
   three axis labels ran into each other ("6 AM12 PM9 PM"). The drawing is an
   enhancement, so it is the thing that goes; every word stays. Restructuring the
   row into a stack would have meant setting `display: block` on the table, which
   costs the table semantics that an opening-hours table should keep. */
@media (max-width: 559px) {
  .hours-track,
  .hours-axis-row { display: none; }
  .hours-table th { padding-right: 0.75rem; }
}

.hours-note {
  margin-top: 1rem;
  font-size: var(--t-sm);
  line-height: 1.55;
  color: var(--slate);
}
.hours-note a { color: color-mix(in srgb, var(--blue) 82%, var(--ink)); text-decoration: underline; text-underline-offset: 2px; }

/* --------------------------------------------------------------------------
   THE LIVE READ, standing on its own.
   .board-live-key and .board-live-val are light-on-dark — they were written for
   the dark card on the schedule board. On the contact page this block had no
   panel under it at all, so white type sat on a near-white ground and the whole
   thing was invisible. It gets the panel it always needed.
   -------------------------------------------------------------------------- */

.board-live {
  display: grid;
  gap: 0.85rem;
  padding: clamp(1.15rem, 1rem + 0.7vw, 1.6rem);
  border-radius: var(--r-lg);
  border: 1px solid rgba(140, 188, 255, 0.16);
  background:
    radial-gradient(125% 130% at 100% 0%, rgba(0, 102, 255, 0.22) 0%, transparent 60%),
    linear-gradient(168deg, #0A1C3A 0%, #04122B 55%, #000B1F 100%);
  box-shadow: 0 26px 52px -34px rgba(0, 6, 20, 0.8);
}
.board-live .strip-h { color: #8CBCFF; }
.board-live-go {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.15rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #8CBCFF;
  transition: color var(--dur-fast) var(--ease), gap var(--dur-fast) var(--ease);
}
.board-live-go:hover { color: #fff; gap: 0.7rem; }

.map-frame {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--rule-light);
  aspect-ratio: 16 / 10;
  background: var(--mist);
}
.map-frame iframe { width: 100%; height: 100%; border: 0; display: block; }

/* --------------------------------------------------------------------------
   THE BRIEF — copy beside a spec ledger, with a photo band underneath.
   The previous version put three small photos in the right column beside a tall
   block of copy, which left a third of the section empty. The ledger is roughly
   as tall as the copy because it holds the same number of facts, and the photos
   run full width below where their aspect ratio cannot fight anything.
   -------------------------------------------------------------------------- */

.brief { display: grid; gap: clamp(2rem, 1.6rem + 1.6vw, 3rem); }
@media (min-width: 940px) {
  .brief {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    align-items: start;
    gap: clamp(2rem, 1.4rem + 2.4vw, 4rem);
  }
}

.ledger-cap {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  align-items: baseline;
  margin-bottom: 0.85rem;
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--blue);
}
.ledger-cap span { color: var(--slate); letter-spacing: 0.1em; }

.ledger {
  margin: 0;
  border: 1px solid var(--rule-light);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: 0 24px 44px -34px rgba(0, 20, 60, 0.35);
  overflow: hidden;
}
.ledger > div {
  display: grid;
  grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr);
  gap: 0.35rem 1.25rem;
  align-items: baseline;
  padding: clamp(0.85rem, 0.72rem + 0.4vw, 1.1rem) clamp(1rem, 0.85rem + 0.6vw, 1.5rem);
}
.ledger > div + div { border-top: 1px solid var(--rule-light); }
.ledger dt {
  font-family: var(--mono);
  font-size: 0.5625rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--slate);
}
.ledger dd {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.4;
  font-weight: 500;
  color: var(--slate-deep);
}
@media (max-width: 439px) {
  /* Too narrow for two columns without hyphenating every value. */
  .ledger > div { grid-template-columns: minmax(0, 1fr); }
}

/* --- Photo band --- */
.band {
  display: grid;
  gap: clamp(0.6rem, 0.5rem + 0.4vw, 1rem);
  margin-top: clamp(2rem, 1.6rem + 1.6vw, 3rem);
}
.band figure {
  margin: 0;
  border-radius: var(--r);
  overflow: hidden;
  background: var(--ink-2);
  aspect-ratio: 3 / 2;
}
@media (min-width: 700px) {
  /* Deliberately uneven widths, so three photos read as a composition rather
     than a row of thumbnails. A fixed row height keeps their bottoms aligned:
     with per-figure aspect ratios, three different widths gave three different
     heights and the band looked accidental. */
  .band-3 {
    grid-template-columns: 1.15fr 1.6fr 1fr;
    grid-auto-rows: clamp(210px, 23vw, 340px);
  }
  .band-3 figure { aspect-ratio: auto; }
  .band-1 figure { aspect-ratio: 21 / 9; }
}
.band img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.8s var(--ease); }
.band figure:hover img { transform: scale(1.04); }


/* --------------------------------------------------------------------------
   THE THREE LEVELS — a ladder, set in type.
   Replaces the .journey / .jstop progression path, and the three photographs
   with it. This section's whole job is to tell someone which room to walk into,
   and a gym shot per level was not helping with that — it made each level a card
   to look at rather than a thing to read. So: one rung per level, the number
   oversized and ghosted in that level's own colour, the level code carrying the
   colour at full strength, the name doing the heading's work, and the three hard
   facts pulled out to a right-hand column where they line up and can be read
   straight down across all three rungs. Each rung starts further right than the
   one above it, so the left edges draw the climb — no graphic needed.
   -------------------------------------------------------------------------- */

.levels-head { align-items: end; }

/* A notch down from the global .d2. At full --t-4xl this heading is three words
   long and set at 84px, which put it on three lines and made it the loudest thing
   on a page whose point is the three rungs below it. Scoped here rather than
   changed globally, the same way .page-head and .cta-band already carry their own
   .d2 sizes. */
.levels-head .d2 { font-size: clamp(2.375rem, 1.8rem + 2.8vw, 4.25rem); }

.levels {
  list-style: none;
  margin: clamp(2.5rem, 2rem + 2.5vw, 4rem) 0 0;
  padding: 0;
  display: grid;
  gap: 0;
}

.level-step {
  position: relative;
  display: grid;
  gap: clamp(1rem, 0.8rem + 1vw, 2rem);
  padding: clamp(1.75rem, 1.4rem + 1.6vw, 2.75rem) 0;
  padding-left: clamp(1.25rem, 1rem + 1.2vw, 2.25rem);
  border-left: 3px solid var(--rail);
  border-top: 1px solid var(--rule-light);
}
.level-step:last-child { border-bottom: 1px solid var(--rule-light); }

/* The rail colour comes from the r-* class already on each step (.r-lv2, .r-thai,
   .r-kids …), not from nth-child. Three ascending level colours were only ever
   right for the adult page; the Muay Thai hour and the kids age groups are each
   one programme in one colour. */

/* Two columns is the base. .has-facts adds the right-hand facts column, and the
   staircase indent starts at 900px — narrower than that, indenting a single
   column just eats the measure the copy needs. */
@media (min-width: 900px) {
  .level-step {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
  }
  .has-facts .level-step {
    grid-template-columns: auto minmax(0, 1fr) clamp(12.5rem, 10rem + 6vw, 16.5rem);
  }
  .level-step:nth-child(2) { margin-left: 6%; }
  .level-step:nth-child(3) { margin-left: 12%; }
}

/* The number is a ghost, not a headline: it marks the rung and then gets out of
   the way. The level code below it carries the same hue at full strength, so the
   colour is stated once where it has to be read. */
.level-num {
  font-family: var(--display);
  font-weight: 900;
  font-stretch: 125%;
  font-size: clamp(2.25rem, 1.5rem + 3.4vw, 4.25rem);
  white-space: nowrap;
  line-height: 0.82;
  letter-spacing: -0.03em;
  color: color-mix(in srgb, var(--rail) 38%, var(--paper));
}

.level-body { min-width: 0; }
/* Every element inside the body is optional — the adult ladder runs marker, code,
   name, lead, copy; the Muay Thai hour runs code, name, copy; the kids ages run
   marker, name, copy. So the top margin belongs to whatever ends up first, and
   the copy needs its own spacing when no lead line precedes it. */
.level-body > :first-child { margin-top: 0; }
.level-name + .level-copy { margin-top: 0.95rem; }

.level-when {
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--slate);
}

.level-name {
  margin-top: 0.55rem;
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 118%;
  font-size: clamp(1.375rem, 1.1rem + 1.2vw, 2rem);
  line-height: 1.06;
  text-transform: uppercase;
}
/* 62/38 toward ink: enough of the hue to read as the level's colour, dark enough
   to clear 3:1 at this size on the mist ground. */
.level-name b {
  display: block;
  font-weight: 900;
  letter-spacing: 0.06em;
  font-size: 0.72em;
  color: color-mix(in srgb, var(--rail) 62%, var(--ink));
}
.level-name span { display: block; color: var(--ink); }

.level-lead {
  margin-top: 0.95rem;
  font-size: var(--t-lg);
  font-weight: 600;
  line-height: 1.35;
  color: var(--slate-deep);
}
.level-copy {
  margin-top: 0.55rem;
  max-width: 56ch;
  line-height: 1.65;
  color: var(--slate);
}

/* The facts. Same three slots per rung, ruled, so the eye can compare one level
   against another by running down the column rather than back across the page. */
.level-facts { display: grid; gap: 0.85rem; align-content: start; margin: 0; }
.level-facts > div {
  display: grid;
  gap: 0.2rem;
  padding-bottom: 0.85rem;
  border-bottom: 1px dotted var(--rule-light);
}
.level-facts > div:last-child { padding-bottom: 0; border-bottom: 0; }
.level-facts dt {
  font-family: var(--mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--slate);
}
.level-facts dd {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 108%;
  font-size: 1.0625rem;
  line-height: 1.25;
  color: var(--ink);
}

/* .is-ages puts an age range in the numeral slot instead of 01 / 02 / 03. Five
   characters at the full size made the first column wider than the copy needed —
   and unlike a step counter, an age range is the thing a parent is scanning for,
   so it comes out of ghost strength and up to the same weight the level code
   carries. Amber at 38% on paper was almost not there at all. */
.is-ages .level-num {
  font-size: clamp(1.625rem, 1.2rem + 1.9vw, 2.625rem);
  color: color-mix(in srgb, var(--rail) 62%, var(--ink));
}

/* Stacked, the facts read better as a row of three than as a tall list. */
@media (max-width: 899px) {
  .level-facts {
    grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
    gap: 1rem clamp(1rem, 0.8rem + 1vw, 1.75rem);
  }
  .level-facts > div { padding-bottom: 0; border-bottom: 0; }
}

/* --------------------------------------------------------------------------
   MOBILE SPACING PASS
   Measured at 390px before writing any of this. Every value here was a real
   number on the page, not a guess: section padding was 72px top and bottom, the
   lede sat 22px under its heading, the button row 36px under the lede, the block
   after a .split 44px down, the footer's column gap 32px, and a ladder rung had
   29px of padding above and below its own content. All of them were the desktop
   figures, because their clamp minimums were already above the mobile value.
   Scoped to one query so nothing above 900px moves.
   -------------------------------------------------------------------------- */
@media (max-width: 899px) {
  /* Heading to paragraph, and paragraph to buttons. */
  .lede { margin-top: 0.85rem !important; }
  .hero-actions,
  .page-head-actions { margin-top: 1.35rem; }

  /* A .split's heading block and the thing under it. 44px was a desktop gap. */
  .split + *,
  .journey-head + *,
  .levels-head + * { margin-top: 1.75rem; }

  /* Cards: 18-24px of padding, 14-20px between them. */
  .board-list { padding: 0.6rem 1rem; }
  .level-step { padding-top: 1.35rem; padding-bottom: 1.35rem; }
  .hours-card { padding: 1.15rem; }
  .cform-panel { padding: 1.25rem; }

  /* The ladder's staircase indent is off below 900px anyway, so its rungs only
     need the vertical rhythm tightened. */
  .levels { margin-top: 1.75rem; }
}

/* --------------------------------------------------------------------------
   THE BOARD, COLLAPSED — phones only.
   Seven stacked day cards ran to 4352px at 390px wide, on a page that was 8172px
   in total. Collapsed to their headers with today open, the same board opens at
   about a screen and a half and a visitor can get to the FAQ underneath it.
   Above 760px nothing collapses and the toggle does nothing.
   -------------------------------------------------------------------------- */

.board-col-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  width: 100%;
  min-height: 3rem;
  padding: 0;
  color: inherit;
  text-align: center;
  cursor: default;
}
.board-col-head { padding: 0; }
.board-col-head > .board-col-toggle {
  padding: 1.05rem clamp(2.75rem, 2.3rem + 1.5vw, 3.5rem);
  min-height: 3.5rem;
}
.board-col-sign { display: none; }

@media (max-width: 759px) {
  .board-col-toggle { cursor: pointer; }
  /* The class count and the new chevron were both pinned to right: 0.85rem and sat
     exactly on top of each other. The count moves in to make room. */
  .board-col-head span:not(.board-col-sign) { right: 2.35rem; }
  .board-col-head > .board-col-toggle { padding-right: 3.9rem; }
  /* The chevron only exists where the header actually does something. */
  .board-col-sign {
    display: block;
    position: absolute;
    right: 0.85rem;
    top: 50%;
    width: 9px; height: 9px;
    margin-top: -6px;
    border-right: 2.2px solid currentColor;
    border-bottom: 2.2px solid currentColor;
    border-radius: 1px;
    transform: rotate(45deg);
    /* 8px at 1.8px and 65% read as a stray dash rather than a chevron, which
       matters — it is the only signal that the header does anything. */
    opacity: 0.85;
    transition: transform var(--dur-fast) var(--ease);
  }
  [aria-expanded='true'] .board-col-sign { transform: translateY(3px) rotate(-135deg); }
  .board-col.is-collapsed .board-list { display: none; }
  .board-col.is-collapsed .board-col-head { border-bottom: 0; }
}
