/* ============================================================
   KEFI STUDIO — DESIGN SYSTEM PROTOTYPE v2
   Colours: exact brand hex values supplied by client
   Assets: real logo + icon files supplied by client
   ============================================================ */

/* --- Real brand fonts (licensed files, self-hosted) ---
   Matches the live kefistudiopilates.com.au site: IvyPresto Headline
   for display type, Gopher for body/UI. Only the weights actually
   used on this site are loaded (checked against every font-weight in
   this file) — not the full families, to keep the font payload lean.
   IvyPresto Headline: only a Light (300) cut was available, no plain
   Regular — this is also what the live site effectively renders as
   its "normal" heading weight, so it's mapped to font-weight:400 here
   rather than 300, matching that convention. No italic cut exists yet
   for IvyPresto Headline, so italic accents (see .beat-punch, h2 em,
   .offer-single-price .amount, .review-card .quote) fall back to
   the browser's synthetic/oblique italic until a real italic file is
   sourced. */
@font-face {
  font-family: "IvyPresto Headline";
  src: url("../fonts/ivypresto-headline-light.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Gopher";
  src: url("../fonts/gopher-medium.woff2") format("woff2"),
       url("../fonts/gopher-medium.woff") format("woff");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Gopher";
  src: url("../fonts/gopher-bold.woff2") format("woff2"),
       url("../fonts/gopher-bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  /* --- Brand palette (exact hex, as supplied) --- */
  --ivory: #F5F1ED;
  --white: #FFFFFF;
  --forest: #333C16;
  --forest-dark: #262c10;
  --marine: #2A3B69;
  --sky: #A9C6F9;
  --apple: #E0FCAB;
  --ink: #14140F;

  /* --- Type --- */
  --font-display: "IvyPresto Headline", "Iowan Old Style", Georgia, serif;
  --font-body: "Gopher", "Century Gothic", sans-serif;

  /* --- Scale --- */
  --step-0: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  --step-1: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
  --step-2: clamp(1.75rem, 1.5rem + 1vw, 2.25rem);
  --step-3: clamp(2.5rem, 2rem + 2vw, 3.75rem);
  --step-4: clamp(3rem, 2.2rem + 3.5vw, 5.5rem);

  --radius-lg: 28px;
  --radius-md: 16px;
  --radius-pill: 999px;

  --container: 1200px;
  --gutter: clamp(1.25rem, 4vw, 4rem);

  --hairline: 1px solid rgba(20,20,15,0.09);

  /* --- Spacing system ---
     Two families. Change either one and every section referencing it
     updates together — that's the whole point of this block.

     1) Fixed steps: component-level spacing (grid gaps, card padding).
     2) Rhythm tokens: the page's macro vertical structure. These define
        "the" padding system — every section/zone/band pulls from these
        rather than inventing its own one-off clamp(). */
  --space-1: 0.5rem;
  --space-2: 0.75rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2rem;
  --space-6: 2.5rem;
  --space-7: 3rem;
  --space-8: 4rem;

  --space-section: clamp(3.5rem, 7vw, 6rem);    /* the target TOTAL gap between two adjacent chapters — never applied whole to one side, see --space-half */
  --space-half: calc(var(--space-section) / 2); /* what each side of a boundary actually contributes. Two adjacent blocks each add this once — sums to --space-section, structurally cannot double, and leaves visible breathing room on BOTH sides of every transition instead of one side owning it all */
  --space-bookend: 64px;                        /* nav→content and the footer specifically — a deliberately different, smaller number by request */
  --space-band-gap: clamp(2rem, 5vw, 3.5rem);   /* gap BETWEEN sections grouped inside one coloured zone (smaller on purpose — these are meant to read as one related group, not a new chapter) */
  --space-card: clamp(1.75rem, 4vw, 2.75rem);   /* internal padding for large standalone cards (offer, modal) */

  --header-height: 82px; /* rendered .site-header height, desktop nav */
}
@media (max-width: 860px) {
  :root { --header-height: 73px; } /* .site-header is shorter once the nav collapses to the hamburger */
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: var(--header-height, 82px); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--ink);
  background: var(--white);
  font-size: var(--step-0);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

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

/* Lets the browser rebalance a paragraph's last lines so it doesn't end
   on one stranded word ("of.", "doing."). Progressive enhancement:
   browsers without support just wrap normally. */
p { text-wrap: pretty; }

em { font-style: italic; color: var(--forest); }
a { color: inherit; }
img { max-width: 100%; height: auto; display: block; }
/* No long-press "Save image" menu on photos or the logo; see js/image-guard.js. Other linked images keep theirs. */
img:not(a img), .logo img { -webkit-touch-callout: none; -webkit-user-drag: none; user-select: none; }

.wrap {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

a:focus-visible, button:focus-visible {
  outline: 2px solid var(--forest);
  outline-offset: 3px;
}

/* --- Eyebrow / label --- */
.eyebrow {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--forest);
  display: inline-flex;
  align-items: center;
  gap: 0.65em;
}

/* --- Brand icon (real asset, pre-coloured PNGs — plain <img>, no CSS mask) ---
   Earlier version used CSS mask-image with currentColor, which wasn't
   rendering reliably. Swapped to two pre-coloured exports of the real
   icon file instead: a single-triangle crop for compact inline use, and
   the full 3-triangle strip (sized wide enough to stay legible) for the
   section divider. */
.icon-mark { display: inline-block; width: 17px; height: auto; flex: none; }
.icon-strip { display: block; width: 220px; height: auto; margin-inline: auto; }

.divider {
  display: flex;
  justify-content: center;
  margin: 0 auto;
  padding: 0.5rem 0 2.5rem;
  color: var(--sky);
}

/* --- Logo lockup (real asset) --- */
.logo { display: inline-flex; align-items: center; text-decoration: none; }
.logo img { height: 34px; width: auto; }
.logo.on-dark img { filter: invert(1); }

/* --- Buttons --- */
.btn {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.95em 1.8em;
  border-radius: var(--radius-pill);
  border: 1.5px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.btn:hover { transform: translateY(-2px); }

.btn-primary { background: var(--forest); color: var(--white); }
.btn-primary:hover { background: var(--forest-dark); }

.btn-ghost { background: transparent; color: var(--forest); border-color: var(--forest); }
.btn-ghost:hover { background: var(--forest); color: var(--white); }

.btn-on-dark { background: transparent; color: var(--white); border-color: rgba(255,255,255,0.55); }
.btn-on-dark:hover { background: var(--white); color: var(--forest); }

.btn-outline-dark { background: transparent; color: var(--ink); border-color: rgba(20,20,15,0.3); }
.btn-outline-dark:hover { background: var(--ink); color: var(--white); border-color: var(--ink); }

/* ============================== NAV ============================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255,255,255,0.9);
  backdrop-filter: blur(8px);
  border-bottom: var(--hairline);
}
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: 1.05rem;
}
.nav-links {
  display: flex;
  gap: 2.1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links a {
  text-decoration: none;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  padding-bottom: 3px;
  border-bottom: 1.5px solid transparent;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.nav-links a:hover, .nav-links a[aria-current="page"] { border-color: var(--forest); color: var(--forest); }
.nav-links-cta { display: none; }

.nav-right { display: flex; align-items: center; gap: 1.6rem; }
.nav-backdrop { display: none; }

.nav-toggle {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.4rem;
}
.nav-toggle span { display: block; width: 24px; height: 2px; background: var(--ink); margin: 5px 0; border-radius: 2px; }

/* --- Studios nav dropdown --- */
.nav-dropdown { position: relative; }
.nav-dropdown-toggle {
  font-family: var(--font-body);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: none;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  padding: 0 0 3px;
  border-bottom: 1.5px solid transparent;
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.nav-dropdown-toggle::after {
  content: "";
  width: 6px; height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.2s ease;
  margin-top: -3px;
}
.nav-dropdown-toggle:hover,
.nav-dropdown.open .nav-dropdown-toggle { color: var(--forest); }
.nav-dropdown.open .nav-dropdown-toggle::after { transform: rotate(-135deg); margin-top: 1px; }

.nav-dropdown-menu { list-style: none; margin: 0; padding: 0; display: none; }
.nav-dropdown.open .nav-dropdown-menu { display: block; }

@media (min-width: 861px) {
  .nav-dropdown-menu {
    position: absolute;
    top: calc(100% + 14px);
    left: 50%;
    transform: translateX(-50%);
    background: var(--white);
    border: var(--hairline);
    border-radius: var(--radius-md);
    box-shadow: 0 20px 40px -20px rgba(20,20,15,0.25);
    min-width: 240px;
    white-space: nowrap;
    padding: 0.5rem;
    z-index: 60;
  }
  .nav-dropdown-menu a {
    display: block;
    padding: 0.65rem 0.85rem;
    border-radius: 10px;
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: none;
    color: var(--ink);
    text-decoration: none;
  }
  .nav-dropdown-menu a:hover { background: var(--ivory); color: var(--forest); border-color: transparent; }
}

@media (max-width: 860px) {
  .nav-links { position: fixed; inset: 64px 0 auto 0; background: var(--white); flex-direction: column; gap: 0; padding: 0.5rem var(--gutter) 1.5rem; border-bottom: var(--hairline); transform: translateY(-8px); opacity: 0; pointer-events: none; transition: opacity 0.2s ease, transform 0.2s ease; overflow-y: auto; max-height: calc(100vh - 64px); }
  .nav-links a { display: block; padding: 0.9rem 0; border-bottom: var(--hairline); }
  .nav-links.open { opacity: 1; transform: translateY(0); pointer-events: auto; }
  .nav-toggle { display: block; }
  .nav-right .btn-primary { display: none; }
  .nav-links-cta { display: block; margin-top: 0.9rem; }
  .nav-links a.btn { display: inline-flex; width: 100%; justify-content: center; padding: 0.95em 1.8em; border-bottom: none; color: var(--white); }

  .nav-backdrop {
    display: block;
    position: fixed;
    inset: 64px 0 0 0;
    background: rgba(20,20,15,0.45);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    z-index: 40;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
  }
  .nav-backdrop.open { opacity: 1; pointer-events: auto; }

  .nav-dropdown-toggle { width: 100%; justify-content: space-between; padding: 0.9rem 0; border-bottom: var(--hairline); }
  .nav-dropdown-menu { padding-left: 1.25rem; }
  .nav-dropdown-menu a { display: block; padding: 0.75rem 0; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #55554e; text-decoration: none; border-bottom: var(--hairline); }
}

/* ============================== HERO ============================== */
.hero { padding-top: var(--space-bookend); padding-bottom: var(--space-half); }
.hero-grid {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
.hero h1 { font-size: var(--step-4); margin-bottom: 0.35em; }
.hero p.lede { font-size: var(--step-1); max-width: 34ch; color: #2c2c28; margin-bottom: 2.2rem; }
/* Supporting detail under a short hero lede (the studio pages' rooms
   and equipment): body size, so the lede stays the headline line. */
.hero p.lede:has(+ .hero-detail) { margin-bottom: 1rem; }
.hero .hero-detail { font-size: 1rem; line-height: 1.6; color: #3a3a35; max-width: 44ch; margin: 0 0 2.2rem; }
.hero-ctas { display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 2rem; }
.hero-figure { border-radius: var(--radius-lg); overflow: hidden; }
.hero-figure-photo { position: relative; aspect-ratio: 4/5; }
.hero-figure-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-figure-photo::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08); }

/* --- Services footer: a solid band directly BELOW the photo, not an
   overlay on top of it. This is the key change from every earlier
   attempt — those all covered part of the photo, which is what forced
   a tradeoff between "fits" and "doesn't obstruct the photo" on short
   mobile crops. A footer covers zero photo pixels and has its own
   height budget, so it's free to take however much room 4 full labels
   actually need (even wrapping a label to 2 lines is harmless here —
   the card just gets a touch taller). One component, every breakpoint. --- */
.hero-services {
  background: var(--forest);
  padding: 1.35rem 1.5rem;
  display: grid;
  grid-template-columns: repeat(2, max-content);
  justify-content: start;
  column-gap: 2.5rem;
  row-gap: 0.85rem;
}
.hero-services span { display: flex; align-items: center; gap: 0.6rem; font-size: 0.85rem; font-weight: 700; color: var(--white); white-space: nowrap; }

@media (max-width: 860px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-figure { order: -1; }
  .hero-figure-photo { aspect-ratio: 5/4; }
  .hero-services { padding: 1.1rem 1.25rem; column-gap: 1.25rem; row-gap: 0.65rem; }
  .hero-services span { font-size: 0.8rem; }
}

/* Below ~400px, two columns of max-content text no longer both fit
   without wrapping — drop to a single column instead so every label
   always stays on one line, at any width. */
@media (max-width: 400px) {
  .hero-services { grid-template-columns: 1fr; justify-content: start; row-gap: 0.55rem; }
}

/* ============================== SECTION RHYTHM (design-system padding) ============================== */
/* Every section contributes HALF the target gap on each of its own edges.
   Two adjacent blocks sum to the full --space-section automatically — no
   doubling possible, and unlike a top-only model, BOTH sides of every
   transition get visible breathing room instead of one side owning it all. */
.section { padding-block: var(--space-half); }

/* Utility overrides — same specificity as .section, source order decides the winner */
.pt-0 { padding-top: 0; }
.pb-0 { padding-bottom: 0; }
.section-snug { padding-block: var(--space-5); }
/* Directly after a hero, that hero already contributes real breathing
   room of its own (.hero's padding-bottom plus .hero-ctas' own bottom
   margin — both shared with index.html's hero, so not touched here),
   and stacking this section's full padding-top on top of that read as
   an excessive gap before the next heading. Scoped to this adjacency
   only; .section-snug after a .page-header (group-classes.html) is
   covered by the page-header rule below instead. */
.hero + .section-snug { padding-top: 0.75rem; }
/* Same doubling as above, different pair: .page-header already owns a
   dedicated padding-bottom of its own (not indirect, like the hero
   case), so a directly-following .section adding its own full
   padding-top on top reads as an oversized gap before the content
   starts (contact.html's studio cards, clinical-pilates.html's first
   section). Zeroed rather than reduced, since page-header's padding-
   bottom is already the intended --space-half of breathing room.
   Every page nests .page-header inside its own <div class="wrap">
   rather than putting the wrap class directly on .page-header (needed
   so .page-header's own narrower max-width doesn't get centered as a
   standalone box by .wrap's margin-inline:auto, which would pull it
   out of alignment with the gutter every other block on the page
   lines up against) — so the element actually adjacent to the
   following .section is that wrapping div, not .page-header itself.
   Same for .section-snug (group-classes.html's class formats section),
   which stacked its own padding-top the same way. */
.wrap:has(> .page-header) + .section,
.wrap:has(> .page-header) + .section-snug { padding-top: 0; }
/* A section that belongs to the block right above it (e.g. the
   follow-on copy under group-classes.html's timetable band): the block's
   own margin supplies the gap, so the section's top padding would only
   double it and leave the section floating closer to what's below. */
.section.is-attached { padding-top: 0; }

.section-head { margin-bottom: 2.75rem; max-width: 60ch; }
/* The bottom margin separates a heading from the content under it. When
   the head is the whole section (nothing under it), it would only stack
   on top of the section's own padding and double the gap to the next one. */
.section-head:last-child { margin-bottom: 0; }
.section-head h2 { font-size: var(--step-3); margin-bottom: 0.85rem; }
.section-head .sub { max-width: 55ch; color: #3a3a35; margin: 0; }
.section-head .sub + .sub { margin-top: 1rem; }
/* Two section-heads side by side (about.html's Our story / Our approach):
   for a pair of text sections of similar length, so the page fills its
   width instead of stacking two long blocks down the left. */
.text-pair { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
.text-pair .section-head { max-width: none; margin-bottom: 0; }
@media (max-width: 860px) { .text-pair { grid-template-columns: 1fr; gap: 2.5rem; } }
/* The pair as two plain cards with small labels instead of large headings
   (about.html), so the page title is the only big heading at the top.
   White with the site hairline, not ivory: the next section is an ivory
   zone and ivory cards would blur into it. */
.text-pair.is-cards { align-items: stretch; }
.text-pair.is-cards .section-head { border: var(--hairline); border-radius: var(--radius-lg); padding: clamp(1.75rem, 4vw, 2.5rem); background: var(--white); }
.text-pair.is-cards .eyebrow { display: inline-flex; margin-bottom: 1rem; }
@media (max-width: 860px) { .text-pair.is-cards { gap: 1.25rem; } }

/* ============================== CLASS CARDS ============================== */
.class-card {
  background: var(--white);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.class-card:hover { transform: translateY(-4px); box-shadow: 0 20px 40px -24px rgba(20,20,15,0.28); }
.class-card .thumb { aspect-ratio: 4/3; overflow: hidden; }
.class-card .thumb img { width: 100%; height: 100%; object-fit: cover; }
.class-card .body { padding: 1.5rem 1.4rem 1.8rem; }
.class-card h3 { font-size: 1.25rem; margin-bottom: 0.5rem; }
.class-card p { margin: 0; font-size: 0.92rem; color: #3a3a35; }
.class-card .tag { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--forest); margin-bottom: 0.6rem; display: block; }

/* .class-card sits in a fluid carousel row (group-classes.html, next card
   peeking), which needs an explicit width since flex doesn't size
   children the way grid columns would. */
.class-carousel .class-card { flex: 0 0 clamp(280px, 36vw, 300px); }
@media (max-width: 640px) { .class-carousel .class-card { flex-basis: 72vw; } }

/* ============================== SEGMENTED CONTROL ============================== */
/* True native-style segmented control (macOS/tvOS), not a scrolling pill
   list: a tinted on-surface track (deliberately NOT the same colour as
   either an ivory or white section, so it always reads as a distinct
   surface) with one sliding thumb behind whichever option is active,
   animated between positions instead of each option owning its own
   background. Paired with js/segmented-control.js, which measures and
   drives all of this — moves the raw <button class="segmented-option">
   markup into a track, animates the thumb, and only falls back to
   horizontal scroll (with edge chevrons) if shrinking the options as
   small as they'll reasonably go still isn't enough to fit them all,
   same order native segmented controls fall back in. js/segmented-filter.js
   layers [data-filter-item] visibility on top for pages that use this as
   a content filter; js/timetable.js listens the same way for its own
   Sessions/Workshops switch — this file owns none of that, only the
   shared look and motion. */
.segmented-control {
  position: relative;
  display: inline-flex;
  max-width: 100%;
  background: rgba(20,20,15,0.07);
  border-radius: var(--radius-pill);
  padding: 4px;
  --segmented-scale: 1;
}
.segmented-track {
  position: relative;
  display: flex;
  gap: 4px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* Unconditional (not just while .is-scrollable), so the thumb's own
     top/bottom below never has to know or care which state the control
     is in — overflow-x is what actually turns clipping on, this padding
     alone does nothing in the non-scrollable case. Gives the thumb's
     shadow room to render before overflow-x clips it flat; the matching
     negative margin cancels the layout shift it'd otherwise cause, same
     trick as .carousel-track. */
  padding-block: 10px;
  margin-block: -10px;
}
.segmented-track::-webkit-scrollbar { display: none; }
.segmented-control.is-scrollable .segmented-track {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}
/* A real child of the track (see js/segmented-control.js for why — it
   needs to scroll with the content, not be tracked in JS), positioned to
   match the track's own content box: top/bottom sit at 10px, the exact
   padding-block above, so the thumb's rendered height matches the actual
   option row instead of the track's own (10px taller) padded box — that
   mismatch was what let it poke out past the control's rounded edge. */
.segmented-thumb {
  position: absolute;
  top: 10px; bottom: 10px; left: 0;
  width: 0;
  background: var(--white);
  border-radius: var(--radius-pill);
  box-shadow: 0 2px 8px rgba(20,20,15,0.14);
  transition: transform 0.32s cubic-bezier(0.65, 0, 0.35, 1), width 0.32s cubic-bezier(0.65, 0, 0.35, 1);
  pointer-events: none;
  z-index: 0;
}
.segmented-option {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  scroll-snap-align: start;
  border: none;
  background: transparent;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: calc(0.85rem * var(--segmented-scale));
  color: #5a5a52;
  padding: calc(0.6rem * var(--segmented-scale)) calc(1.6rem * var(--segmented-scale));
  border-radius: var(--radius-pill);
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.2s ease;
}
.segmented-option:hover { color: var(--forest); }
.segmented-option.is-active { color: var(--forest); }

/* Edge nudge buttons — only ever shown once shrinking alone couldn't fit
   every option, and only on whichever side still has more to reveal.
   Sized down from an earlier pass that used a fixed 26px regardless of
   --segmented-scale — at the smallest shrink step that stayed the same
   size while the option text under it kept shrinking, so it ended up
   covering more and more of the pill it was supposed to be pointing at.
   Pushed further outside the track (-8px, not -4px) so it reads as a
   separate affordance sitting at the seam instead of overlapping the
   content it's obscuring. */
.segmented-edge {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: none;
  background: var(--white);
  color: var(--forest);
  box-shadow: 0 2px 6px rgba(20,20,15,0.2);
  cursor: pointer;
  z-index: 2;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.segmented-edge svg { width: 10px; height: 10px; }
.segmented-edge-prev { left: -8px; }
.segmented-edge-next { right: -8px; }
.segmented-control.is-scrollable .segmented-edge.is-visible { opacity: 1; pointer-events: auto; }

/* ============================== CTA BAND ============================== */
.cta-band {
  background: var(--forest);
  color: var(--white);
  border-radius: var(--radius-lg);
  margin-block: var(--space-half);
  padding: clamp(2.75rem, 6vw, 4.5rem) clamp(1.75rem, 6vw, 4.5rem);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
}
.cta-band h2 { font-size: var(--step-3); max-width: 16ch; margin: 0; color: var(--white); }
.cta-band h2 em { color: var(--apple); font-style: italic; }
.cta-band .cta-actions { display: flex; gap: 1.5rem; flex-wrap: wrap; }
/* Two long-label buttons (clinical-pilates.html) stacked on the right, so
   they stay beside the copy instead of wrapping underneath it at ~1000px. */
.cta-band .cta-actions.is-stacked { flex-direction: column; align-items: stretch; gap: 0.75rem; }
.cta-action-group { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; }
.google-rating-note { font-size: 0.78rem; color: #6b6b62; white-space: nowrap; text-decoration: none; }
.google-rating-note:hover { color: var(--forest); text-decoration: underline; }
.cta-action-group .google-rating-note { color: rgba(255,255,255,0.7); }
.cta-action-group .google-rating-note:hover { color: var(--white); }

/* ============================== FEATURE BAND ==============================
   A forest band holding a grid of short points (about.html's qualified
   staff section). Was the "values band" until the About page's values
   were replaced by staff credentials, so it's named for the shape, not
   one piece of content. Same padding as .cta-band: generous top and
   bottom, narrower sides on phones so the text column isn't squeezed;
   identical from ~470px up. */
.feature-band { background: var(--forest); color: var(--white); border-radius: var(--radius-lg); padding: clamp(2.75rem, 6vw, 4.5rem) clamp(1.75rem, 6vw, 4.5rem); }
.feature-band .section-head h2 { color: var(--white); }
.feature-band .section-head .sub { color: rgba(255,255,255,0.75); }
.feature-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(1.75rem, 4vw, 2.25rem); }
.feature-card h3 { font-size: 1.02rem; color: var(--apple); margin-bottom: 0.6rem; }
.feature-card p { font-size: 0.9rem; line-height: 1.6; color: rgba(255,255,255,0.8); margin: 0; }
/* Each card as its own outlined box in the brand lime (about.html's
   qualified staff points), laid out 2 x 2: four bordered columns
   squeezed the text into 10-line slivers at laptop widths. */
.feature-grid.is-boxed { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 1fr; }
/* Every row as tall as the tallest (grid-auto-rows: 1fr above), with the
   text centred in each box so the shorter points don't sit on a strip of
   empty space. */
.feature-grid.is-boxed .feature-card { border: 1px solid var(--apple); border-radius: var(--radius-md); padding: 1.35rem 1.25rem; display: flex; align-items: center; }
/* One per row on phones. Needs its own rule: .feature-grid.is-boxed out-
   ranks the plain .feature-grid breakpoints below, so they can't undo the
   2 x 2. Rows go back to content height, since equal heights in a single
   stacked column just pad the shorter points. */
@media (max-width: 700px) { .feature-grid.is-boxed { grid-template-columns: 1fr; grid-auto-rows: auto; } }
/* The team photo inside a feature band (about.html: the qualified staff
   section, so the claim and the people sit together). Wider crop than the
   standalone 16:9 to keep the band from getting too tall, framed a little
   above centre so nobody's head is clipped. Back to 16:10 on phones,
   where 21:9 of a narrow band is a thin strip. */
.feature-band .section-head:has(+ .wide-figure) { margin-bottom: 2rem; }
.feature-band .wide-figure { aspect-ratio: 21/9; border-radius: var(--radius-md); margin-bottom: clamp(1.75rem, 4vw, 2.25rem); }
.feature-band .wide-figure img { object-position: 50% 30%; }
@media (max-width: 700px) { .feature-band .wide-figure { aspect-ratio: 16/10; } }
@media (max-width: 980px) { .feature-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .feature-grid { grid-template-columns: 1fr; } }

/* ============================== CONTACT CARD ============================== */
.contact-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.75rem; }
.contact-card { background: var(--white); border-radius: var(--radius-lg); overflow: hidden; border: var(--hairline); display: flex; flex-direction: column; }
.contact-card-photo { aspect-ratio: 4 / 3; overflow: hidden; }
.contact-card-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.contact-card-body { padding: 1.75rem 2rem 2rem; display: flex; flex-direction: column; flex: 1; }
.contact-card-body .eyebrow { margin-bottom: 0.6rem; }
.contact-card-body h2 { font-size: 1.6rem; margin-bottom: 0.3rem; }
.contact-card-body .google-rating-note { margin-top: 0.5rem; }
.contact-rows { margin: 0.75rem 0 1.5rem; }
.contact-row { padding-block: 0.9rem; border-top: var(--hairline); }
.contact-row:first-child { border-top: none; padding-top: 0; }
.contact-row-label { display: block; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--forest); margin-bottom: 0.35rem; }
.contact-row a, .contact-row p { margin: 0; color: #2c2c28; font-size: 0.95rem; line-height: 1.5; }
.contact-row a { text-decoration: none; }
.contact-row a:hover { text-decoration: underline; }
.contact-card-ctas { display: flex; gap: 0.85rem; flex-wrap: wrap; margin-top: auto; }
@media (max-width: 760px) { .contact-grid { grid-template-columns: 1fr; } }

/* ============================== PRICING CARD ============================== */
/* Reusable for any per-studio pricing page (Memberships / Packs / Clinical &
   Private Pilates). Clinical & Private Pilates uses a plain <div> card
   with one .pricing-tier row (single price + its own "Buy Now" button).
   Memberships and Packs use a tappable <a> card instead (.pricing-card-link
   below) — one product, one link, no in-card button — since Memberships'
   price depends on whichever commitment term is currently selected
   site-wide (not something each card should need its own button-per-term
   for), and Packs just doesn't need the extra click once the card itself
   is already the whole tap target. */
.pricing-subhead { text-align: center; font-size: 1.05rem; margin-bottom: 1.25rem; }
/* Sub-title within an already-labelled section (e.g. "Group Fitness
   Classes" under the page's own "Membership & Pricing" eyebrow) — same
   small-caps/forest treatment as .timetable-daystrip-title, deliberately
   without the .eyebrow icon-mark since that icon marks a top-level
   section, not a subordinate one. */
.pricing-section-label { display: block; font-family: var(--font-body); line-height: 1.55; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--forest); margin-bottom: 1.5rem; }
.pricing-group { margin-bottom: clamp(2.5rem, 5vw, 3.5rem); }
.pricing-group:last-child { margin-bottom: 0; }
.pricing-note { text-align: left; max-width: 55ch; margin: 0 0 2rem; color: #3a3a35; font-size: 0.85rem; font-weight: 400; }
.pricing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1.5rem; }
.pricing-card { background: var(--white); border: var(--hairline); border-radius: var(--radius-lg); padding: 2.25rem 1.75rem 1.75rem; text-align: center; display: flex; flex-direction: column; position: relative; }
/* Hidden unless the card is currently .is-popular, rather than only ever
   existing in the DOM when popular — Memberships decides that per
   commitment term at runtime (js/membership-terms.js toggles the class
   when the term switch flips), so a badge that only exists for
   permanently-popular cards can't react to that. */
.pricing-card-badge { display: none; position: absolute; top: -0.8rem; left: 50%; transform: translateX(-50%); background: var(--forest); color: var(--white); font-size: 0.65rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; padding: 0.4rem 0.9rem; border-radius: var(--radius-pill); white-space: nowrap; }
.pricing-card.is-popular { border-color: var(--forest); box-shadow: 0 0 0 2px var(--forest) inset; }
.pricing-card.is-popular .pricing-card-badge { display: block; }
.pricing-card h4 { font-family: var(--font-display); font-size: 1.3rem; margin: 0 0 0.3rem; }
.pricing-card .pricing-freq { font-size: 0.85rem; color: #6b6b62; margin-bottom: 1.1rem; }
.pricing-tier { padding-block: 1.1rem; border-top: var(--hairline); }
.pricing-tier:first-of-type { border-top: none; padding-top: 0; }
.pricing-card .price { font-family: var(--font-display); font-style: italic; font-size: 1.7rem; color: var(--forest); display: block; }
.pricing-card .price-note { font-size: 0.78rem; color: #6b6b62; margin: 0.2rem 0 0.9rem; }
.pricing-tier .btn { width: 100%; }

@media (max-width: 640px) { .pricing-grid { grid-template-columns: 1fr; } }

/* ---- Tappable pricing cards: one link per product/plan, no in-card
   button — used by both Memberships (whose price/link swap together via
   js/membership-terms.js) and Packs (which just has nothing else to
   click). .pricing-carousel is the shared carousel-sizing half; whichever
   section wraps it (.membership-terms today) can add its own spacing on
   top without this needing to know which section it's in. ---- */
.pricing-carousel .carousel-track { gap: 1.25rem; }
.pricing-card-link {
  flex: 0 0 auto;
  width: clamp(220px, 70vw, 270px);
  scroll-snap-align: start;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.pricing-card-link:hover { transform: translateY(-4px); box-shadow: 0 20px 40px -24px rgba(20,20,15,0.28); border-color: rgba(51,60,22,0.3); }
.pricing-card-link:active { transform: scale(0.98); transition: transform 0.1s ease; }
.pricing-card-link .price { margin-top: 0.5rem; }

/* ---- Commitment-term switch: deliberately not another segmented
   control — this sits directly under the Memberships/Packs/Clinical
   segmented control, and two stacked same-looking controls read as a
   confusing duplicate of each other, worse once this one ended up
   narrower than the control above it on mobile. A switch is the
   established, different pattern most subscription pricing pages use
   for exactly this binary choice ("Monthly / Annual" and the like), so
   it reads as a different kind of control doing a different job. */
.term-switch { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 2rem; }
/* Color alone was too subtle to read as "this one, not that one" at a
   glance — and leaning on color as the only cue is a real accessibility
   gap besides, not just a legibility nicety (WCAG 1.4.1: don't convey
   state through color alone). Weight now carries the same signal
   redundantly, so the active label still reads clearly even for someone
   who can't distinguish the color difference well. The inactive label
   used to be faded with opacity too, which dropped it below WCAG's 4.5:1
   text contrast; the grey alone passes. */
.term-switch-label {
  font-size: 0.85rem;
  font-weight: 500;
  color: #6b6b62;
  cursor: pointer;
  transition: color 0.2s ease, font-weight 0.2s ease;
}
.term-switch-label.is-active { color: var(--forest); font-weight: 700; }
.term-switch-toggle {
  position: relative;
  flex: none;
  width: 44px;
  height: 26px;
  padding: 0;
  border-radius: var(--radius-pill);
  border: none;
  background: rgba(20,20,15,0.15);
  cursor: pointer;
  transition: background 0.2s ease;
}
.term-switch-toggle[aria-checked="true"] { background: var(--forest); }
.term-switch-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--white);
  box-shadow: 0 2px 4px rgba(20,20,15,0.25);
  transition: transform 0.25s cubic-bezier(0.65, 0, 0.35, 1);
}
.term-switch-toggle[aria-checked="true"] .term-switch-thumb { transform: translateX(18px); }

/* ============================== LOCATIONS ==============================
   Photo-led, not data-led — a homepage teaser's job is to help someone
   picture the space and pick a studio, not list logistics. Address and
   hours belong on the dedicated location/contact pages where someone
   who's already decided to visit actually needs them.
   The whole card is the link (no separate button, which was repeating
   the location name a third time) — same hover lift/shadow as
   .class-card for a consistent "this is tappable" language site-wide,
   plus a small arrow next to the heading that's visible up front (not
   just on hover, since mobile has no hover state) and nudges right on
   hover as a secondary cue. */
.loc-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.75rem; }
.loc-card {
  display: flex;
  flex-direction: column;
  background: var(--white);
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: var(--hairline);
  text-decoration: none;
  color: inherit;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.loc-card:hover { transform: translateY(-4px); box-shadow: 0 20px 40px -24px rgba(20,20,15,0.28); }
.loc-card:active { transform: scale(0.98); transition: transform 0.1s ease; }
.loc-card-photo { aspect-ratio: 4 / 3; overflow: hidden; }
.loc-card-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.loc-card-body { padding: 1.75rem 2rem 2rem; }
.loc-card-body .eyebrow { margin-bottom: 0.6rem; }
.loc-card-body h3 { font-size: 1.6rem; margin-bottom: 0.5rem; }
.loc-card-body p { margin: 0; color: #3a3a35; font-size: 0.95rem; }

@media (max-width: 760px) { .loc-grid { grid-template-columns: 1fr; } }

/* ============================== FOOTER ============================== */
.site-footer { background: var(--marine); color: var(--white); padding-block: var(--space-bookend); }
.footer-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 2.5rem; padding-bottom: 2.5rem; border-bottom: 1px solid rgba(255,255,255,0.18); }
.footer-grid p { color: rgba(255,255,255,0.75); font-size: 0.9rem; max-width: 32ch; }
.footer-app { margin-top: 2.5rem; }  /* same as .footer-grid gap */
.footer-app:empty { display: none; }
/* h2, not h4: the footer columns follow each page's own h2s (or only its h1), so a lower level skips one.
   The body font, weight and line height undo the display-heading defaults h2 picks up. */
.footer-col h2 { font-family: var(--font-body); font-weight: 700; line-height: 1.55; font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; margin: 0 0 1rem; color: var(--sky); }
.footer-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.footer-col a { text-decoration: none; color: rgba(255,255,255,0.85); font-size: 0.9rem; }
.footer-col a:hover { color: var(--apple); }
/* Kefi app links (js/footer-app.js): a second titled list under Socials,
   only adding the store glyph beside each label. Its heading, list and
   link styles all come from .footer-col above, so the placeholder has
   to sit inside a .footer-col (not the plain brand-column div). */
.footer-app a { display: inline-flex; align-items: center; gap: 0.5rem; }
.footer-app svg { flex: none; }
.footer-bottom { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; padding-top: 1.5rem; font-size: 0.78rem; color: rgba(255,255,255,0.6); }

@media (max-width: 860px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .footer-grid { grid-template-columns: 1fr; } }

/* ============================== PAGE HEADER (interior pages) ============================== */
.page-header { padding-top: var(--space-bookend); padding-bottom: var(--space-half); max-width: 62ch; }
.page-header h1 { font-size: var(--step-3); }
.page-header p { color: #3a3a35; font-size: var(--step-1); max-width: 42ch; margin-bottom: 0; }
/* A quieter second line under the page-header lede, same treatment as
   .hero-detail on the hero pages (contact.html). */
.page-header p:has(+ .page-header-detail) { margin-bottom: 1rem; }
.page-header .page-header-detail { font-size: 1rem; line-height: 1.6; max-width: 44ch; }

/* ============================== IVORY ZONE (chapter alternation) ============================== */
.zone-ivory {
  background: var(--ivory);
  padding-block: var(--space-half);
}
/* Sections grouped inside a zone don't stack their own full padding —
   the zone owns the outer edges, this owns the gap between siblings.
   Change --space-band-gap once, every zone's internal rhythm updates. */
.zone-ivory > .section { padding-block: 0; }
.zone-ivory > .section + .section { margin-top: var(--space-band-gap); }

/* ============================== INTRO / MANIFESTO ============================== */
.beats { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.75rem, 4vw, 3rem); }
.beat { padding-left: clamp(1.5rem, 3vw, 2.25rem); border-left: 1.5px solid rgba(51,60,22,0.15); }
.beat:first-child { padding-left: 0; border-left: none; }
.beat .eyebrow { display: block; margin-bottom: 0.9rem; }
.beat p { font-size: 1rem; line-height: 1.6; color: #2c2c28; margin: 0 0 0.9rem; }
.beat p:last-child { margin-bottom: 0; }
.beat-punch { font-family: var(--font-display); font-size: 1.15rem; }
.beat-punch em { color: var(--forest); }
.beat p a { color: var(--forest); text-decoration: underline; text-underline-offset: 3px; }
/* A takeaway line under a group of beats, rather than inside one of them
   (clinical-pilates.html's session types and how-it-works). */
.section-note { margin: 2rem 0 0; max-width: 60ch; }

@media (max-width: 860px) {
  .beats { grid-template-columns: 1fr; gap: 1.75rem; }
  .beat { padding-left: 0; padding-top: 1.5rem; border-left: none; border-top: 1.5px solid rgba(51,60,22,0.15); }
  .beat:first-child { padding-top: 0; border-top: none; }
}
/* Beats stacked in a single column at every width, with the same
   divider style as the mobile layout above (clinical-pilates.html's
   session types, beside a photo where three columns won't fit). */
.beats.is-stacked { grid-template-columns: 1fr; gap: 1.5rem; margin-top: 1.5rem; }
.beats.is-stacked .beat { padding-left: 0; padding-top: 1.5rem; border-left: none; border-top: 1.5px solid rgba(51,60,22,0.15); }
.beats.is-stacked .beat:first-child { padding-top: 0; border-top: none; }

/* ============================== MISSION ============================== */
.mission-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.mission-grid .figure { border-radius: var(--radius-lg); overflow: hidden; aspect-ratio: 4/5; }
.mission-grid .figure img { width: 100%; height: 100%; object-fit: cover; }
.mission-grid h2 { font-size: var(--step-3); }
.mission-grid p { color: #3a3a35; margin-bottom: 1.1rem; }
/* A labelled, dot-separated credentials line in a mission-grid bio
   (about.html's Meet Naike): small print, label on its own line. */
.mission-grid .qualifications { font-size: 0.9rem; color: #55554e; margin-top: 1.5rem; }
.mission-grid .qualifications strong { display: block; color: var(--ink); margin-bottom: 0.25rem; }
.mission-ctas { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 1.5rem; }
@media (max-width: 860px) {
  .mission-grid { grid-template-columns: 1fr; }
  .mission-grid .figure { order: -1; aspect-ratio: 16/10; }
  /* Tight headshots lose their subject's head to the default center crop once
     squeezed into this wide mobile ratio — nudge the crop window upward. */
  #founder .figure img { object-position: 50% 15%; }
}

/* .mission-grid.is-matched (clinical-pilates.html, about.html's Meet
   Naike): the photo matches
   the text column exactly, top edge with the first line and bottom edge
   with the last, instead of a fixed 4:5 portrait centred on the text and
   overhanging both ends. The image is taken out of the height
   calculation so the text decides the row height and the photo crops to
   fit. Desktop only; below 860px the stacked 16:10 photo applies. */
@media (min-width: 861px) {
  .mission-grid.is-matched { align-items: stretch; }
  .mission-grid.is-matched .figure { position: relative; aspect-ratio: auto; }
  .mission-grid.is-matched .figure img { position: absolute; inset: 0; }
  .mission-grid.is-matched > div > :last-child { margin-bottom: 0; }
}

/* Wide, unpaired photo (e.g. a full team shot) — mission-grid's .figure is
   portrait-cropped for its two-column pairing, so a photo that needs to
   stay wide uses this instead. Its only use right now is inside the feature
   band on about.html, which overrides the ratio and corner radius (see
   .feature-band .wide-figure). */
.wide-figure { border-radius: var(--radius-lg); overflow: hidden; aspect-ratio: 16/9; }
.wide-figure img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ============================== CAROUSEL (reusable component) ==============================
   Swipeable card row (same idea as apple.com/mac's "Why Apple is the best
   place to buy Mac" section) — a fluid row saves a lot of vertical space
   versus a stacked grid, and native CSS scroll-snap gives real touch/
   trackpad swipe for free instead of reimplementing drag physics in JS.
   No dot indicators: with several cards visible at once (fluid width, not
   one-per-view), "one dot per card" has no stable meaning. Arrows (moving
   one card at a time) plus native swipe is the correct pattern here.

   Reusable on any page/section: markup is just
     <div class="carousel">
       <div class="carousel-track">...same-sized items...</div>
       <div class="carousel-controls">
         <button class="carousel-arrow carousel-arrow-prev">...</button>
         <button class="carousel-arrow carousel-arrow-next">...</button>
       </div>
     </div>
   js/carousel.js auto-discovers every .carousel on the page and
   wires it up — no per-instance JS or IDs needed. Card sizing/appearance
   (e.g. .help-card below) is left to the caller; the track only handles
   the scroll-snap mechanics, applied generically to its direct children. */
.carousel { margin-bottom: 2.5rem; }
.pricing-carousel { margin-bottom: 0; }
.carousel-track {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  position: relative;
  /* Setting overflow-x alone makes the browser compute overflow-y as
     "auto" too (per spec), which clips any hover shadow/lift on cards
     inside. Padding pushes the clip boundary outward to give that room;
     the matching negative margin cancels the layout shift it'd otherwise
     cause. */
  padding-block: 36px;
  margin-block: -36px;
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track:focus-visible { outline: 2px solid var(--forest); outline-offset: 4px; }
.carousel-track > * { scroll-snap-align: start; flex: 0 0 auto; }

/* Right-aligned ("trailing edge") rather than centered — on mobile this
   sits closer to where a thumb naturally rests, same placement Apple uses. */
.carousel-controls { display: flex; align-items: center; justify-content: flex-end; gap: 0.75rem; margin-top: 1.5rem; padding-right: 1.75rem; }
.carousel-arrow {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
  width: 40px; height: 40px;
  border-radius: 50%;
  border: 1.5px solid var(--forest);
  background: var(--white);
  color: var(--forest);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, opacity 0.2s ease;
}
.carousel-arrow:hover { background: var(--forest); color: var(--white); }
.carousel-arrow:disabled { opacity: 0.35; cursor: default; pointer-events: none; }

/* --- "Who we help" card content (this instance's own styling — the
   carousel component above only supplies the scroll/snap mechanics) --- */
.help-card {
  width: clamp(220px, 42vw, 280px);
  aspect-ratio: 4 / 4.2;
  display: flex;
  align-items: center;
  background: var(--ivory);
  border-radius: var(--radius-md);
  padding: 1.6rem 1.5rem;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ink);
}

@media (max-width: 640px) { .help-card { width: 78vw; } }
/* Closing note under the "Who We Help" carousel, before its CTAs. */
.help-closing { max-width: 60ch; color: #3a3a35; margin-bottom: 2rem; }
.help-closing h3 { font-size: 1.6rem; margin-bottom: 0.75rem; }
.help-closing p:last-child { margin-bottom: 0; }

/* ============================== INTRO OFFER ============================== */
/* Title/copy on the leading half, card on the trailing half — same
   established two-column pattern as .mission-grid (equal columns,
   vertically centered, single column under 860px), rather than a new
   layout idiom just for this section. */
.offer-section { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.offer-intro { max-width: 42ch; }
.offer-intro .eyebrow { margin-bottom: 0.9rem; }
.offer-intro h2 { font-size: var(--step-3); margin-bottom: 0.85rem; }
.offer-intro .sub { color: #3a3a35; margin: 0; }
.offer-aside { margin: 1rem 0 0; font-size: 0.9rem; color: #6b6b62; }
.offer-aside:empty { display: none; }
.offer-aside a { color: var(--forest); text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 700px) { .offer-section { grid-template-columns: 1fr; } }

/* ============================== SINGLE OFFER CARD ============================== */
.offer-single {
  background: var(--forest);
  color: var(--white);
  border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(1.5rem, 4vw, 2.5rem);
  align-items: start;
}
.offer-single-price { display: flex; flex-direction: column; }
.offer-single-price .label { font-size: 0.95rem; color: rgba(255,255,255,0.8); margin-bottom: 0.2rem; }
/* Price and fine print are shared with the /get-started tier cards
   (.offer-tier, below), so the two layouts can't drift apart. */
.offer-single-price .amount,
.offer-tier .amount { font-family: var(--font-display); font-style: italic; font-size: clamp(2.5rem, 5vw, 3.25rem); color: var(--apple); line-height: 1; }
.offer-single-body .desc { margin: 0 0 1.25rem; color: rgba(255,255,255,0.92); font-size: 0.98rem; }
.offer-single-body .fine-print,
.offer-tier .fine-print { margin: 1.1rem 0 0; font-size: 0.8rem; color: rgba(255,255,255,0.65); line-height: 1.6; }
.offer-single-body .fine-print a,
.offer-tier .fine-print a { color: var(--sky); text-decoration: underline; }
@media (max-width: 640px) { .offer-single { grid-template-columns: 1fr; text-align: left; } }

/* ============================== REVIEWS ============================== */
/* Fluid multi-card row (next card peeking at the edge), same sizing
   pattern and shared carousel mechanics (js/carousel.js) as
   .help-card in "Who We Help" — card width is what makes it behave
   like a slideshow-ish row, not different JS. Quote is line-clamped so
   cards of wildly different testimonial lengths still sit level with
   their peeking neighbour instead of the row looking jagged. */
/* Unlike "Who We Help", this carousel is the last thing in its section
   (nothing trails it), so the generic .carousel margin-bottom — sized
   for separating the carousel from following content — just adds dead
   space before the next section. Zero it out here. */
.reviews-carousel { margin-bottom: 0; }
.review-card {
  flex: 0 0 clamp(300px, 45vw, 360px);
  min-height: 230px;
  background: var(--ivory);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
  margin: 0;
}
@media (max-width: 640px) { .review-card { flex-basis: 82vw; } }
.review-card .review-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.85rem; }
.review-card .attribution { display: flex; align-items: center; gap: 0.6rem; margin: 0; }
.review-card .name-wrap { display: flex; flex-direction: column; gap: 0.15rem; }
.review-card .review-meta { font-size: 0.72rem; color: #6b6b62; }
.review-card .avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--forest);
  color: var(--ivory);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.68rem;
}
.review-card .stars { display: flex; gap: 0.2rem; flex: none; }
.review-card .stars svg { width: 15px; height: 15px; }
.review-card .stars .star-filled { fill: var(--forest); }
.review-card .stars .star-empty { fill: rgba(51,60,22,0.18); }
.review-card .review-class-meta { font-size: 0.78rem; color: #6b6b62; margin: 0 0 0.75rem; }
.review-card .quote {
  font-family: var(--font-display);
  font-size: 1.05rem;
  line-height: 1.5;
  color: #2c2c28;
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 6;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Shown in place of cards while a reviews carousel's data is still being
   fetched (real network round-trip for the Momence path, not instant like
   a local JSON file). flex: 1 1 100% takes over the track's full width
   instead of behaving like a card; min-height roughly matches a populated
   row's height so the section doesn't collapse then jump taller once
   cards land. */
.carousel-loading {
  flex: 1 1 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  /* .review-card's own floor is 230px, but real cards with a few lines of
     quote text typically render closer to 320-380px — splitting the
     difference keeps the jump-to-loaded-height smaller than either
     extreme would. */
  min-height: 320px;
  color: #6b6b62;
  font-size: 0.9rem;
}
/* Generic small spinner — used by any "fetching live data" loading state
   (reviews carousel, timetable, ...). Not scoped to one component so every
   loading state stays visually identical. */
.spinner {
  width: 18px;
  height: 18px;
  border: 2px solid rgba(51,60,22,0.18);
  border-top-color: var(--forest);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  flex: none;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ============================== TIMETABLE ============================== */
/* Custom-built schedule — Momence supplies the raw session data via their
   read-only API, nothing else; every pixel of this is ours.

   Full section width, not squeezed into a narrow column — a fixed
   max-width looked deliberately offset against the rest of the page on
   wide viewports. Instead the card list itself goes to two columns above
   860px (leaders in this space — ClassPass, Mindbody's web booker — grid
   session cards on desktop rather than stretch a single-column list edge
   to edge), so the extra width gets used rather than left empty.

   Day picker is a horizontal scrollable strip (reuses .carousel/
   carousel.js verbatim — same scroll-snap mechanics as every other
   carousel on the site, just carrying day pills instead of cards)
   instead of one-day-at-a-time arrows — every nearby day is a single tap
   away instead of several clicks deep, which is how Calendly/ClassPass/
   Mindbody all handle date choice.

   Whole card is the tap target (an <a>, not a card + separate button) —
   a trailing arrow implies "opens something" the way .loc-card already
   does elsewhere on the site, rather than repeating "Book Now" dozens of
   times down the page. Card content is top-aligned throughout (not
   vertically centered) specifically so a long "covering for" line, or a
   two-line class name, doesn't drag the avatar or time out of alignment
   with everything else — center-aligned flex rows only look right when
   every row happens to be the same height, which real content isn't. */
.timetable-toggle { margin-bottom: 1rem; }

/* Segmented-option badge (workshop count) — small enough to sit inline
   with the label without disturbing the pill's own height. */
.timetable-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.35rem;
  margin-left: 0.4rem;
  border-radius: 999px;
  background: var(--apple);
  color: var(--forest-dark);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 1;
}
.timetable-badge[hidden] { display: none; }

/* Shared, always-visible regardless of Sessions/Workshops tab — the one
   piece of context a traveller booking from another timezone needs no
   matter which view they're looking at. */
.timetable-tz { font-size: 0.78rem; color: #6b6b62; margin: 0 0 1.5rem; }

/* ---- Day strip: quick-pick, next week only ---- */
.timetable-daystrip-header { margin-bottom: 1rem; }
.timetable-daystrip-title { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--forest); }

/* "More" is a normal flex item at the end of the row, not an overlay —
   it only ever sits beside the strip, never on top of it. flex: 0 1 auto
   on the track (shrink, never grow) is what gets both sizes right with
   a single rule: on a wide viewport the track needs no room back, so it
   renders at its natural content width and "More" lands right after the
   last pill with the same gap as between pills; once the row's own
   width is capped by the viewport, the track — the only item allowed to
   shrink — gets forced below its content's natural width, so its own
   pills start clipping/scrolling at that boundary same as any
   scrollable strip, with "More" sitting cleanly at the edge beside it,
   not layered over it. */
.timetable-daystrip-row { display: flex; align-items: stretch; gap: 0.6rem; }
/* overflow-x:auto (needed for the actual scrolling) makes the track's
   own intrinsic size ignore its overflowing content by default — width:
   max-content restores "as wide as every pill actually needs" as its
   real preferred size, which flex-basis: auto (from flex: 0 1 auto)
   then uses. Without it the track had nothing sizing it at all and
   collapsed to 0 whenever it wasn't being forced to grow. */
.timetable-daystrip-row .carousel-track { flex: 0 1 auto; min-width: 0; width: max-content; }

/* Toggle + its calendar popover share one positioning context, so the
   panel can anchor directly under the button that opens it instead of
   rendering disconnected, lower down the page. */
.timetable-cal-wrap { position: relative; flex: none; }
.timetable-cal-toggle {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  height: 100%;
  min-width: 4rem;
  padding: 0.65rem 0.6rem;
  border-radius: var(--radius-md);
  border: 1.5px dashed rgba(51,60,22,0.35);
  background: transparent;
  font-family: var(--font-body);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--forest);
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.timetable-cal-toggle:hover { border-color: rgba(51,60,22,0.6); background: rgba(51,60,22,0.05); }
.timetable-cal-toggle.is-active { background: var(--forest); border-style: solid; border-color: var(--forest); color: var(--white); }
.timetable-cal-toggle svg { flex: none; }

.timetable-daystrip { margin-bottom: 1.75rem; }
/* This instance doesn't need the base carousel-track's hover-shadow
   breathing room (day pills don't lift/shadow on hover) — cancelling it
   out here matters more than usual, since the negative block margin
   otherwise pulls the track up into whatever sits above it. */
.timetable-daystrip .carousel-track { gap: 0.75rem; padding-block: 0; margin-block: 0; }
.day-pill {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  min-width: 4.75rem;
  padding: 0.65rem 0.5rem;
  border-radius: var(--radius-md);
  border: var(--hairline);
  background: var(--white);
  color: inherit;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.day-pill:hover:not(.is-active) { border-color: rgba(51,60,22,0.3); }
.day-pill .pill-label { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #6b6b62; }
.day-pill .pill-date { font-family: var(--font-display); font-size: 1rem; }
.day-pill.is-active { background: var(--forest); border-color: var(--forest); }
.day-pill.is-active .pill-label, .day-pill.is-active .pill-date { color: var(--white); }
.timetable-daystrip.is-hidden, .timetable-daystrip-header.is-hidden { display: none; }

/* Right-aligned, pushed clear of the pill row above it. Arrows only ever
   appear when the strip actually needs scrolling — on wide viewports 8
   pills fit without it, and a pair of permanently-disabled arrows there
   was dead UI, so JS marks the row unscrollable and this hides them. */
.timetable-daystrip-foot { display: flex; justify-content: flex-end; width: 100%; margin-top: 1rem; }
.timetable-daystrip-foot .carousel-controls { margin: 0; padding: 0; }
.timetable-daystrip-foot.is-hidden, .timetable-daystrip-foot.no-scroll-needed { display: none; }

/* ---- Month calendar: everything beyond the quick-pick strip ----
   Anchored as a popover directly under the "More dates" button (see
   .timetable-cal-wrap) rather than a block further down the page, so
   opening it is never disconnected from the control that opened it. */
.timetable-calendar {
  position: absolute;
  top: calc(100% + 0.6rem);
  right: 0;
  z-index: 20;
  width: min(320px, calc(100vw - 2.5rem));
  padding: 1.5rem;
  background: var(--white);
  border: var(--hairline);
  border-radius: var(--radius-md);
  box-shadow: 0 20px 45px -18px rgba(20,20,15,0.3);
}
.timetable-calendar[hidden] { display: none; }
.timetable-calendar-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; }
.timetable-calendar-head .cal-month-label { font-family: var(--font-display); font-size: 1.05rem; }
.timetable-calendar-head .carousel-arrow { width: 30px; height: 30px; }
.timetable-calendar-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); margin-bottom: 0.4rem; }
.timetable-calendar-weekdays span { text-align: center; font-size: 0.64rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: #6b6b62; }
.timetable-calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 0.2rem; }
.cal-day {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  border-radius: 50%;
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: #2c2c28;
  cursor: pointer;
}
.cal-day:hover:not(:disabled):not(.is-selected) { background: rgba(51,60,22,0.1); }
.cal-day:disabled { color: rgba(20,20,15,0.25); cursor: default; }
.cal-day.is-today { font-weight: 700; color: var(--forest); }
.cal-day.is-selected { background: var(--forest); color: var(--white); font-weight: 700; }
.cal-day-blank { visibility: hidden; }

.timetable-group { margin-bottom: 2rem; }
.timetable-group:last-child { margin-bottom: 0; }
.timetable-group-label { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--forest); margin-bottom: 0.85rem; }

.timetable-list { display: grid; grid-template-columns: 1fr; gap: 0.65rem; }
@media (min-width: 860px) { .timetable-list { grid-template-columns: repeat(2, 1fr); } }

.timetable-session {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  background: var(--white);
  border: var(--hairline);
  border-radius: var(--radius-md);
  padding: 0.85rem 1rem;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.timetable-session:hover { border-color: rgba(51,60,22,0.3); box-shadow: 0 12px 28px -20px rgba(20,20,15,0.35); transform: translateY(-1px); }

/* Time column carries every "fact" about the session — when, how long,
   where, and any schedule change — stacked as small icon+text rows. This
   keeps the main column to just what/who, which is what stopped it
   ballooning on mobile when a class also had a substitute teacher note. */
.timetable-time { flex: none; width: 7.25rem; padding-top: 0.1rem; display: flex; flex-direction: column; gap: 0.3rem; }
.timetable-time .start {
  display: flex; align-items: center; gap: 0.35rem;
  font-family: var(--font-display); font-style: italic; font-size: 1rem; color: var(--forest); line-height: 1.2;
}
.timetable-time .fact { display: flex; align-items: center; gap: 0.35rem; font-size: 0.72rem; color: #6b6b62; line-height: 1.3; }
.timetable-time .fact svg { flex: none; opacity: 0.75; }

/* Capacity fact — same row as duration/location, but its state matters
   more than a plain fact does, so each state gets its own weight/colour
   instead of all reading as equally-muted background information.
   "low" gets the same emphasis as "waitlist": Momence only ever
   surfaces remainingSpots once a session is down to 2 spots or fewer,
   so every time this fact appears at all it's already an urgency
   signal, never a routine "8/12 seats used" headcount. */
.timetable-time .fact-capacity.is-low,
.timetable-time .fact-capacity.is-waitlist { color: var(--forest); font-weight: 700; }
.timetable-time .fact-capacity.is-low svg,
.timetable-time .fact-capacity.is-waitlist svg { opacity: 1; }
.timetable-time .fact-capacity.is-full { color: #a3a39a; font-style: italic; }

.timetable-main { flex: 1 1 auto; min-width: 0; }
.timetable-main h3 { font-family: var(--font-body); font-weight: 700; letter-spacing: normal; font-size: 0.95rem; margin: 0 0 0.4rem; line-height: 1.3; }

.timetable-teacher { display: flex; align-items: center; gap: 0.5rem; }
.timetable-avatar {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--forest);
  color: var(--ivory);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.55rem;
}
.timetable-teacher-name { font-size: 0.8rem; color: #3a3a35; }
.timetable-covering { font-size: 0.72rem; color: #6b6b62; font-style: italic; margin: 0.35rem 0 0; }

.timetable-empty, .timetable-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 0.75rem;
  min-height: 240px;
  color: #6b6b62;
}
.timetable-empty p { margin: 0; max-width: 40ch; }
.timetable-loading { flex-direction: row; gap: 0.65rem; font-size: 0.9rem; }

/* ============================== LOCATION PREFERENCE (demo) ============================== */
.studio-indicator { }
.studio-indicator a { color: rgba(255,255,255,0.85); text-decoration: underline; }
.studio-indicator a:hover { color: var(--apple); }

.location-modal {
  position: fixed; inset: 0; z-index: 100;
  display: flex;
  align-items: center; justify-content: center;
  visibility: hidden;
  pointer-events: none;
  transition: visibility 0s linear 0.2s;
}
.location-modal.open {
  visibility: visible;
  pointer-events: auto;
  transition: visibility 0s linear 0s;
}
/* Backdrop tint and blur transition together, on the element that
   actually owns them — animating an ancestor's opacity doesn't
   reliably interpolate a descendant's backdrop-filter, which is what
   made the blur snap in late instead of tracking the tint. */
.location-modal-backdrop {
  position: absolute; inset: 0;
  background: rgba(20,20,15,0);
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  transition: background-color 0.2s ease, backdrop-filter 0.2s ease, -webkit-backdrop-filter 0.2s ease;
}
.location-modal.open .location-modal-backdrop {
  background: rgba(20,20,15,0.45);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.location-modal-card {
  position: relative;
  background: var(--ivory);
  border-radius: var(--radius-lg);
  padding: var(--space-card);
  max-width: 420px; width: calc(100% - 2.5rem);
  box-shadow: 0 30px 60px -20px rgba(20,20,15,0.35);
  opacity: 0;
  transform: translateY(12px) scale(0.98);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.location-modal.open .location-modal-card { opacity: 1; transform: translateY(0) scale(1); }
.location-modal-card h3 { font-size: 1.6rem; margin-bottom: 0.5rem; }
.location-modal-card p { color: #3a3a35; font-size: 0.92rem; margin: 0 0 1.5rem; }
.location-modal-options { display: flex; flex-direction: column; gap: 0.75rem; }
.location-modal-options .btn { width: 100%; justify-content: center; }
.location-modal-close {
  position: absolute; top: 1rem; right: 1rem;
  background: none; border: none; font-size: 1.4rem; line-height: 1; cursor: pointer;
  color: #55554e; padding: 0.25rem 0.5rem;
}
.location-modal-close:hover { color: var(--ink); }

.toast {
  position: fixed; left: 50%; bottom: 1.5rem; transform: translate(-50%, 12px);
  background: var(--ink); color: var(--white);
  padding: 0.85rem 1.3rem; border-radius: var(--radius-md);
  font-size: 0.85rem; max-width: min(90vw, 420px); text-align: center;
  opacity: 0; pointer-events: none; transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 110; box-shadow: 0 12px 30px -10px rgba(0,0,0,0.4);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
.toast.toast-warn { background: var(--apple); color: var(--forest-dark); font-weight: 700; }

/* ============================== TERMS & CONDITIONS ============================== */
/* Sticky TOC sidebar + content column — the standard pattern for a long
   reference document (Stripe/GitHub/Notion terms all use it) so a
   14-section, 90-clause document is scannable and jump-to-able instead
   of a single unbroken scroll. Collapses to a plain top-of-page list on
   mobile, where "sticky" has no useful screen real estate to stick to. */
.terms-layout { display: grid; grid-template-columns: 240px 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
.terms-toc { position: sticky; top: calc(var(--header-height, 82px) + 1.5rem); max-height: calc(100vh - var(--header-height, 82px) - 3rem); overflow-y: auto; }
.terms-toc-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.15rem; counter-reset: terms-toc; }
.terms-toc-list li { counter-increment: terms-toc; }
.terms-toc-list a {
  display: block; padding: 0.45rem 0.75rem; border-radius: var(--radius-md);
  font-size: 0.85rem; color: #55554e; text-decoration: none; line-height: 1.35;
}
.terms-toc-list a::before { content: counter(terms-toc) ". "; color: var(--forest); font-weight: 700; }
.terms-toc-list a:hover { background: var(--ivory); color: var(--ink); }
.terms-toc-list a.is-active { background: var(--ivory); color: var(--ink); font-weight: 700; }
@media (max-width: 860px) {
  .terms-layout { grid-template-columns: 1fr; }
  .terms-toc { position: static; max-height: none; margin-bottom: 1rem; }
  .terms-toc-list { flex-direction: row; flex-wrap: wrap; gap: 0.5rem; }
  .terms-toc-list a { padding: 0.4rem 0.7rem; background: var(--ivory); }
}

.terms-updated { font-size: 0.82rem; color: #6b6b62; margin: 0 0 1.5rem; }
.terms-intro { font-size: 1.05rem; color: #2c2c28; max-width: 65ch; margin: 0 0 2.25rem; }

/* Every section (including the first) keeps the same border-top and
   padding-top — a first-of-type exception used to strip both to avoid
   a "double line" right after .terms-intro's own border-bottom, but
   that meant its heading landed flush against the header on anchor
   scroll while every other section's heading sits below its own hairline
   and padding first — a real, visible inconsistency, not just a missing
   line. A small *negative* scroll-margin-top pulls the landing point a
   few px past html's own scroll-padding-top (see html's own rule), so
   the break line itself tucks just slightly under the sticky header
   instead of leaving a gap of bare white space floating above it. */
.terms-section { padding-block: 2.25rem; border-top: var(--hairline); scroll-margin-top: -3px; }
.terms-section h2 { font-size: clamp(1.3rem, 1.05rem + 1vw, 1.6rem); margin: 0 0 1.1rem; }
.terms-section h3 { font-family: var(--font-body); font-size: 0.95rem; font-weight: 700; color: var(--forest); margin: 1.75rem 0 0.75rem; }
.terms-section > p { max-width: 70ch; color: #2c2c28; margin: 0 0 1rem; font-size: 0.92rem; }

.terms-clauses { display: flex; flex-direction: column; gap: 0.75rem; max-width: 70ch; }
/* scroll-margin-top belongs on .terms-clause, not .terms-clause-number —
   the id a clause anchor (#clause-10-2) resolves to is the row itself
   (.terms-clause-number is just the visible link inside it, with no id
   of its own), so that's the element the browser actually scrolls to.
   A solid highlight box sitting flush against the header reads as a
   collision, unlike a thin section hairline that can tuck just under
   it — so this gets a genuinely positive gap, not the same small
   negative offset .terms-section uses. */
.terms-clause { display: flex; gap: 0.85rem; align-items: baseline; margin: 0; color: #2c2c28; font-size: 0.92rem; line-height: 1.6; scroll-margin-top: 16px; }
.terms-clause-number {
  flex: none; min-width: 3.4rem; font-size: 0.85rem; font-weight: 700; color: var(--forest);
  text-decoration: none;
}
.terms-clause-number:hover { text-decoration: underline; }
.terms-clause.is-target { background: var(--apple); border-radius: var(--radius-md); margin-inline: -0.6rem; padding: 0.3rem 0.6rem; }

.terms-back-to-top { display: inline-block; margin-top: 1rem; font-size: 0.85rem; color: var(--forest); font-weight: 700; }
.terms-loading { color: #6b6b62; font-size: 0.95rem; }

/* ============================== GUIDELINES ============================== */
/* Collapsed-by-default accordion (native <details>/<summary>, styled) —
   scannable, practical guidance reads better a click at a time than as
   one long scroll, the opposite call from terms.html's always-expanded
   layout, and for the right reason: this isn't a document anyone needs
   to see in full at once. Flat list, no section headings — reuses
   .terms-intro/.terms-loading for the page-level intro/loading text,
   but not .terms-section, since there are no sections here. */
.guideline-toggle-all {
  display: inline-flex; align-items: center;
  margin: 0 0 1rem; padding: 0; border: 0; background: none;
  font-family: var(--font-body); font-size: 0.85rem; font-weight: 700;
  color: var(--forest); cursor: pointer;
}
.guideline-toggle-all:hover { text-decoration: underline; }
.guideline-list { display: flex; flex-direction: column; gap: 0.75rem; max-width: 70ch; }
.guideline-item { background: var(--white); border: var(--hairline); border-radius: var(--radius-md); overflow: hidden; }
.guideline-item-summary {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1.1rem 1.35rem; cursor: pointer; list-style: none;
  font-family: var(--font-body); font-weight: 700; font-size: 0.98rem; color: var(--ink);
}
.guideline-item-summary::-webkit-details-marker { display: none; }
.guideline-item-summary:hover { background: var(--ivory); }
.guideline-item-icon { flex: none; color: var(--forest); transition: transform 0.2s ease; }
.guideline-item[open] .guideline-item-icon { transform: rotate(180deg); }
.guideline-item-body { margin: 0; padding: 0 1.35rem 1.35rem; color: #3a3a35; font-size: 0.92rem; line-height: 1.65; }
/* Global `a { color: inherit }` means a link in accordion body copy is
   indistinguishable from the text around it — needs an explicit
   treatment to read as clickable at all. */
.guideline-item-body a { color: var(--forest); text-decoration: underline; }

/* ============================== FAQ ============================== */
/* Same accordion visual language as GUIDELINES above (.guideline-list/
   .guideline-item/.guideline-toggle-all, reused directly, not
   reimplemented) — this is the same kind of content, a Q&A card. What's
   new here is specific to having three real categories and enough
   items that finding one specific answer matters: a search box and a
   .segmented-control category filter, both wired up in faq.js rather
   than styled as anything bespoke. This is also the first text input
   anywhere on the site, so its look is set here rather than reused
   from an existing pattern. */
.faq-search-wrap {
  display: flex; align-items: center; gap: 0.65rem;
  background: var(--white); border: var(--hairline); border-radius: var(--radius-pill);
  padding: 0.85rem 1.25rem; max-width: 32rem; margin: 1.5rem 0 1.25rem;
}
.faq-search-icon { flex: none; display: flex; color: #6b6b62; }
.faq-search {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  font-family: var(--font-body); font-size: 0.95rem; color: var(--ink);
}
.faq-search::placeholder { color: #8a8a80; }
.faq-search::-webkit-search-cancel-button { cursor: pointer; }
.faq-filter { margin-bottom: 1.75rem; }
.faq-category { margin-bottom: 2.5rem; }
.faq-category:last-of-type { margin-bottom: 0; }
.faq-category-title { font-size: clamp(1.15rem, 1rem + 0.6vw, 1.35rem); margin: 0 0 1.1rem; }
.faq-empty { color: #6b6b62; font-size: 0.95rem; }

/* ============================== GET STARTED ============================== */
/* /get-started is built from the site's existing pieces (the home hero,
   .beats, .cta-band, the reviews carousel, the guideline accordion);
   the only thing new to it is the two-offer layout below. */

/* A short headline that otherwise wraps as "Get moving with / us.";
   balance evens the two lines. Scoped here so the other pages' approved
   heroes keep their current breaks. */
.get-started-page .hero h1 { text-wrap: balance; }

/* Every intro offer side by side, rendered by js/offer.js into
   .offer-tiers. Same forest card and apple price as .offer-single, but
   stacked top to bottom rather than price | details, since two of the
   wide single-card layout won't fit side by side. Both cards are equal
   weight on purpose: neither is "recommended", the "suits" line under
   each price does the choosing. Buy Now sits at the foot of each card
   (margin-top: auto) so the two buttons line up whatever the copy
   length above them. */
.offer-tiers { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; }
.offer-tier {
  background: var(--forest); color: var(--white);
  border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  display: flex; flex-direction: column;
}
.offer-tier .label { font-size: 1.5rem; color: var(--white); margin: 0 0 0.5rem; }
.offer-tier .amount { margin: 0 0 1.5rem; }  /* rest shared with .offer-single-price .amount */
.offer-tier .suits { font-family: var(--font-display); font-size: 1.15rem; line-height: 1.4; color: var(--white); margin: 0 0 0.85rem; }
.offer-tier .desc { color: rgba(255,255,255,0.85); font-size: 0.98rem; margin: 0 0 1.75rem; }
.offer-tier-foot { margin-top: auto; }
/* A season with a single offer: the card fills the row rather than
   sitting in the left column beside an empty one, with its copy capped
   at a readable line length. */
.offer-tier:only-child { grid-column: 1 / -1; }
.offer-tier:only-child .suits,
.offer-tier:only-child .desc { max-width: 60ch; }
@media (max-width: 760px) { .offer-tiers { grid-template-columns: 1fr; } }

/* The two "not this, but…" routes under the cards: help choosing, and
   clinical for anyone injured. One column rather than one note under
   each card, so neither reads as belonging to the offer above it. */
.offer-tiers-notes { margin-top: 1.75rem; display: flex; flex-direction: column; gap: 0.6rem; max-width: 65ch; }
.offer-tiers-notes p { margin: 0; font-size: 0.95rem; color: #3a3a35; }
.offer-tiers-notes a { color: var(--forest); text-decoration: underline; text-underline-offset: 3px; }

/* Accordion cards are white on an ivory band on /get-started, so .guideline-item-
   summary's default ivory hover matched the band behind it and read as
   the card vanishing rather than responding. Rather than invent a new
   tint, fall back to the border+shadow hover this site already uses
   for bordered cards on a tinted ground (.timetable-session, same
   values) — no transform, since a card that lifts and then expands on
   click reads as two competing responses to one gesture. */
.zone-ivory .guideline-item-summary:hover { background: none; }
.zone-ivory .guideline-item:hover { border-color: rgba(51,60,22,0.3); box-shadow: 0 12px 28px -20px rgba(20,20,15,0.35); }
