/* Hallmark · macrostructure: Marquee Hero · genre: editorial
 * theme: custom (vibe: "alpine ascent, published standard, warm paper")
 *   paper: oklch(97.4% 0.011 85)  ·  accent: oklch(56.8% 0.213 285) cool
 *   display: Fraunces  ·  body: Inter
 *   axes: light / high-contrast-serif / cool
 * hero: H6 Photographic fold · knobs: area=full-bleed, caption=lower-left, text=overlaid
 * enrichment: scroll-seeked video push-in (assets/hero/ascent/ascent.mp4)
 * nav: N1b Canonical SaaS three-section · knobs: centre=5 links, dropdowns=none, scroll=frost-on-scroll
 * footer: Ft1 Mast-headed · knobs: wordmark=display-2xl, tagline=roman body, links=inline
 * sections: S2 hanging · features: F3 tabular spec sheet (3-col, rule every row)
 * proof: T4 numbered stat strip (4-up) · cta: C1 outlined chip (slab, spacious, arrow)
 * motion: frame-scrub · reveal-rise · row-shift
 * tone: cinematic editorial · anchor hue: indigo 285°
 * pre-emit critique: P5 H5 E5 S4 R4 V5
 */

@import url("tokens.css");

/* ==========================================================================
   Base
   ========================================================================== */

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

html {
  overflow-x: clip;
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  overflow-x: clip;
  font-family: var(--font-body);
  font-size: var(--text-lg);
  line-height: 1.62;
  color: var(--color-ink);
  background: var(--color-paper);
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2.5px solid var(--color-focus);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
  /* Never transitioned — the ring must appear the instant focus lands. */
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 1.5rem);
}

/* The nav is sticky and ~92px tall, so an un-margined anchor jump parks the
   section's heading underneath it. */
section[id] { scroll-margin-top: var(--space-3xl); }
.ascent { scroll-margin-top: 0; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: var(--space-sm);
  z-index: 300;
  background: var(--color-ink);
  color: var(--color-paper);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-sm);
}
.skip-link:focus { left: var(--space-md); }

/* The page's real <h1> — the fold says it in 15rem type, but that type is
   decorative markup, so the accessible name lives here. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   Shared type
   ========================================================================== */

.h-section {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 96;
  font-style: normal;
  font-weight: 600;
  font-size: var(--text-4xl);
  line-height: 1.1;
  letter-spacing: -0.022em;
  margin: 0;
  overflow-wrap: anywhere;
  min-width: 0;
}

.lede {
  font-size: var(--text-lg);
  line-height: 1.66;
  color: var(--color-ink-500);
  max-width: var(--measure);
  margin: var(--space-md) 0 0;
}

/* S2 · Hanging section head — floats in negative space, no rule, no left-margin
   label. Tag-above-heading only, never tag-left/heading-right. */
.head { max-width: 68ch; margin-bottom: var(--space-2xl); }
.head__tag {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 0 0 var(--space-md);
}
.band--ink .head__tag { color: var(--color-accent-soft); }

.band {
  padding-block: var(--space-3xl);
}
.band--ink {
  background: var(--color-ink);
  color: var(--color-paper);
}
.band--ink .lede { color: var(--color-on-ink-soft); }

/* ==========================================================================
   Nav · N1b canonical three-section, frost-on-scroll
   ========================================================================== */

.mast {
  position: sticky;
  top: 0;
  z-index: 100;
  color: var(--color-ink);
  background: transparent;
  border-bottom: var(--rule-hair) solid transparent;
  transition: background var(--dur-mid) var(--ease-out),
              color var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) var(--ease-out),
              backdrop-filter var(--dur-mid) var(--ease-out);
}
.mast.is-stuck {
  background: var(--color-paper-frost);
  backdrop-filter: blur(12px) saturate(1.4);
  border-bottom-color: var(--color-rule);
}

/* Over the fold the bar floats on a sunrise photograph. Navy type is
   unreadable there, so the whole bar inverts to cream and leans on the
   scrim's top band for contrast. The glyph and the burger follow via
   currentColor. */
/* The bar is never invisible. It used to go fully transparent here and lean on
   the scrim, which works over dark frames and vanishes over a bright sky — so
   the header appeared to come and go as the fold played. It now keeps a real
   surface at all times and only changes which surface: frosted ink over the
   photograph, frosted paper everywhere else. */
.mast.is-over-hero {
  color: var(--color-on-ink);
  background: rgba(var(--scrim-rgb), 0.5);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  border-bottom-color: rgba(250, 247, 241, 0.16);
  text-shadow: 0 1px 14px rgba(var(--scrim-rgb), 0.45);
}
.mast.is-over-hero .mainnav a:hover,
.mast.is-over-hero .mainnav a[aria-current="page"] { color: var(--color-on-ink); }
.mast.is-over-hero .mainnav a::after { background: var(--color-on-ink); }
.mast.is-over-hero .mainnav a[aria-current="page"]::after { transform: scaleX(1); }

.mast__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding-block: var(--space-md);
}

.brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;      /* WCAG AA hit target — the wordmark is a real link */
  gap: var(--space-xs);
  font-family: var(--font-display);
  font-variation-settings: "opsz" 40;
  font-weight: 600;
  font-size: var(--text-xl);
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.mainnav {
  display: flex;
  align-items: center;
  gap: clamp(var(--space-md), 2.4vw, var(--space-xl));
}
.mainnav a {
  font-size: var(--text-sm);
  font-weight: 500;
  white-space: nowrap;          /* gate 49 — no two-line clickable text */
  /* Padded to a 44px target rather than sized by the text. The underline is
     then pinned relative to the text, not the padded box, so growing the hit
     area doesn't drag the rule away from the word. */
  padding-block: 12px;
  position: relative;
  transition: color var(--dur-fast) var(--ease-out);
}
.mainnav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 10px;
  height: var(--rule-medium);
  background: var(--color-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-mid) var(--ease-out);
}
.mainnav a:hover, .mainnav a[aria-current="page"] { color: var(--color-accent); }
.mainnav a:hover::after, .mainnav a[aria-current="page"]::after { transform: scaleX(1); }

.navtoggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  width: 44px; height: 44px;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  cursor: pointer;
}
.navtoggle span {
  width: 22px; height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform var(--dur-mid) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.navtoggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.navtoggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.navtoggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.drawer {
  display: none;
  flex-direction: column;
  padding: 0 clamp(1rem, 4vw, 1.5rem) var(--space-md);
  background: var(--color-paper-veil);
  backdrop-filter: blur(12px);
  border-top: var(--rule-hair) solid var(--color-rule);
}
.drawer.is-open { display: flex; }
.drawer a {
  padding-block: var(--space-sm);
  font-size: var(--text-base);
  font-weight: 500;
  border-bottom: var(--rule-hair) solid var(--color-rule);
}
.drawer a:last-child { border-bottom: 0; }

/* -- The mark in the bar -------------------------------------------------
   The logo is the mark alone. The wordmark sits on the switcher next to it,
   so the bar says "Own Your Study" once and the two elements read as a single
   lockup rather than as the brand printed twice.

   Both colourways are stacked and cross-faded: charcoal on paper, white over
   the photograph, where a charcoal mark would vanish. */
.brand { position: relative; display: inline-flex; align-items: center; }
.brand__mark {
  display: block;
  height: clamp(30px, 3.2vw, 40px);
  width: auto;
  transition: opacity var(--dur-mid) var(--ease-out);
}
.brand__mark--light { position: absolute; left: 0; top: 50%; translate: 0 -50%; opacity: 0; }
.mast.is-over-hero .brand__mark--ink { opacity: 0; }
.mast.is-over-hero .brand__mark--light { opacity: 1; }
.mast.is-over-hero .brand { text-shadow: none; }

/* ==========================================================================
   Brand switcher
   --------------------------------------------------------------------------
   One master brand, several divisions. "Own Your" is constant charcoal in
   every row and only the denomination takes colour, so the shared half reads
   as the parent identity and the coloured word as the specialism.
   ========================================================================== */

.switch { position: relative; flex: none; }

.switch__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
  min-height: 44px;
  padding: 6px 10px 6px 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-display);
  font-variation-settings: "opsz" 40;
  font-weight: 600;
  font-size: var(--text-xl);
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: var(--color-ink);
}

.switch__owner { color: currentColor; }
/* The space between the two halves is a real one in the markup, so it copies
   and reads aloud correctly; no margin is needed here. */
.switch__name  { color: var(--division, currentColor); }

.switch__chev {
  margin-left: 0.15em;
  color: var(--color-ink-500);
  transition: transform var(--dur-mid) var(--ease-out);
}
.switch__trigger[aria-expanded="true"] .switch__chev { transform: rotate(180deg); }

/* Over the fold the bar inverts, and the trigger has to come with it. */
.mast.is-over-hero .switch__trigger { color: var(--color-on-ink); }
.mast.is-over-hero .switch__chev    { color: rgba(250, 247, 241, 0.7); }
.mast.is-over-hero .switch__trigger .switch__name { color: var(--color-on-ink); }

/* -- The menu -------------------------------------------------------------
   A sheet of paper with a hairline and a low, wide shadow rather than a
   floating card — the page is warm and matte, and a hard drop shadow would
   read as a different product. */
.switch__menu {
  position: absolute;
  top: calc(100% + 10px);
  left: -10px;
  z-index: 120;
  min-width: 16rem;
  margin: 0;
  padding: var(--space-xs);
  list-style: none;
  background: var(--color-paper);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(var(--scrim-rgb), 0.04),
              0 18px 44px -18px rgba(var(--scrim-rgb), 0.28);
}
.switch__menu[hidden] { display: none; }

/* Opening: a short fade and a few pixels of travel. Nothing springs. */
.switch__menu.is-open {
  animation: switch-in 190ms var(--ease-out);
}
@keyframes switch-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

.switch__item {
  display: flex;
  align-items: baseline;
  gap: 0;
  padding: clamp(0.6rem, 1.4vw, 0.8rem) 0.85rem;
  min-height: 44px;
  border-radius: calc(var(--radius) - 6px);
  font-family: var(--font-display);
  font-variation-settings: "opsz" 40;
  font-weight: 600;
  font-size: 1.02rem;
  line-height: 1.25;
  letter-spacing: -0.012em;
  color: var(--color-ink);
  white-space: nowrap;
  transition: background-color 190ms var(--ease-out),
              transform 190ms var(--ease-out);
}

/* The tint is the division's own colour at low alpha, so the hover state is
   derived from the brand rather than a generic grey. */
.switch__item:hover,
.switch__item:focus-visible {
  background-color: rgba(var(--division-rgb, 18, 26, 47), 0.07);
  transform: translateX(3px);
}

/* Current division: a tint plus a short coloured rule on the leading edge.
   Deliberately not a checkmark — this is a brand mark, not a settings list. */
.switch__item.is-active {
  position: relative;
  background-color: rgba(var(--division-rgb, 18, 26, 47), 0.06);
  font-weight: 700;
}
.switch__item.is-active::before {
  content: "";
  position: absolute;
  left: 0.3rem;
  top: 50%;
  translate: 0 -50%;
  width: 3px;
  height: 1.15em;
  border-radius: 2px;
  background: var(--division, var(--color-ink));
}

/* -- Division cycler ------------------------------------------------------
   Fills the gap under the section heading with the argument the section is
   making: one constant brand, eight pathways. "Own Your" is fixed and only
   the denomination rolls, each in its own colour.

   The list carries a ninth row repeating the first, so the loop lands back
   where it started instead of snapping. --roll is the row height, and the
   mask is exactly one row tall. */
.cycler {
  --roll: 1.24em;
  display: flex;
  align-items: baseline;
  gap: 0.3em;
  margin: var(--space-lg) 0 0;
  font-family: var(--font-display);
  font-variation-settings: "opsz" 96;
  font-weight: 600;
  font-size: clamp(1.55rem, 3.6vw, 2.6rem);
  line-height: var(--roll);
  letter-spacing: -0.022em;
  color: var(--color-ink);
  white-space: nowrap;
}

.cycler__roll {
  display: inline-block;
  height: var(--roll);
  overflow: hidden;
}

.cycler__list {
  display: block;
  animation: cycler-roll 20s cubic-bezier(0.76, 0, 0.24, 1) infinite;
}

.cycler__list > span {
  display: block;
  height: var(--roll);
  line-height: var(--roll);
  color: var(--division, var(--color-accent));
}

@keyframes cycler-roll {
  0%, 9.6% { transform: translateY(calc(var(--roll) * -0)); }
  12.5%, 22.1% { transform: translateY(calc(var(--roll) * -1)); }
  25%, 34.6% { transform: translateY(calc(var(--roll) * -2)); }
  37.5%, 47.1% { transform: translateY(calc(var(--roll) * -3)); }
  50%, 59.6% { transform: translateY(calc(var(--roll) * -4)); }
  62.5%, 72.1% { transform: translateY(calc(var(--roll) * -5)); }
  75%, 84.6% { transform: translateY(calc(var(--roll) * -6)); }
  87.5%, 97.1% { transform: translateY(calc(var(--roll) * -7)); }
  100% { transform: translateY(calc(var(--roll) * -8)); }
}

@media (prefers-reduced-motion: reduce) {
  /* Rests on the parent brand rather than cycling. */
  .cycler__list { animation: none; }
}

@media (max-width: 40rem) {
  .cycler { font-size: clamp(1.25rem, 6.5vw, 1.75rem); }
}

/* -- Division marks -------------------------------------------------------
   Each row carries its own colourway of the one mark: charcoal book and
   climber throughout, flag and sunlit face in the division's colour. Built by
   re-laying the master's ink/accent mix against the new accent, so the edges
   stay exactly as drawn rather than being posterised by a flat swap. */
.switch__mark {
  align-self: center;
  flex: none;
  width: 1.55em;
  height: auto;
  margin-right: 0.55em;
}


.row__mark {
  align-self: start;
  width: clamp(30px, 3vw, 42px);
  height: auto;
  margin-bottom: calc(var(--space-xs) * -1);
}
@media (min-width: 60.01rem) {
  /* The rows are a three-column grid; the mark takes a lane of its own rather
     than being squeezed into the name cell. */
  .row[data-division] { grid-template-columns: auto minmax(0, 7fr) minmax(0, 9fr) minmax(0, 4fr); }
}
@media (max-width: 60rem) {
  .row__mark { width: 32px; }
}

/* An item with nowhere to go yet: dimmed, not interactive, and skipped by the
   keyboard rather than presented as a link that 404s. */
.switch__item.is-soon {
  opacity: 0.45;
  cursor: default;
  pointer-events: none;
}
.switch__item.is-soon:hover { background: none; transform: none; }

/* -- The division palette -------------------------------------------------
   One master brand, one coloured word per division. Declared as a pair per
   division — the hex for type, the channels for the low-alpha tint — so a
   division is two values and every surface that needs it (the switcher, the
   network rows) reads the same source.

   Own Your Study keeps the original brand indigo rather than taking a new
   one: it is the parent identity, not a sibling. */
[data-division="study"]          { --division: var(--color-accent); --division-rgb: 99, 91, 255; }
[data-division="ib"]             { --division: #002D72; --division-rgb: 0, 45, 114; }
[data-division="alevel"]         { --division: #B95745; --division-rgb: 185, 87, 69; }
[data-division="ap"]             { --division: #6DAE45; --division-rgb: 109, 174, 69; }
[data-division="atar"]           { --division: #EDA623; --division-rgb: 237, 166, 35; }
[data-division="uni-admissions"] { --division: #D9684A; --division-rgb: 217, 104, 74; }
[data-division="med"]            { --division: #168C8C; --division-rgb: 22, 140, 140; }
[data-division="uni-studies"]    { --division: #A04465; --division-rgb: 160, 68, 101; }

/* -- Inside the drawer ----------------------------------------------------
   Expands in place. A floating panel over a panel is never right on a phone. */
.switch--inline { width: 100%; }
.switch--inline .switch__trigger {
  width: 100%;
  justify-content: flex-start;
  padding: var(--space-sm) 0;
  font-size: var(--text-base, 1rem);
  border-bottom: var(--rule-hair) solid var(--color-rule);
}
.switch--inline .switch__chev { margin-left: auto; }
.switch--inline .switch__menu {
  position: static;
  min-width: 0;
  margin: var(--space-xs) 0 var(--space-sm);
  padding: var(--space-xs) 0;
  background: none;
  border: 0;
  border-bottom: var(--rule-hair) solid var(--color-rule);
  border-radius: 0;
  box-shadow: none;
  animation: none;
}
.switch--inline .switch__item { white-space: normal; }

/* The drawer only exists below the nav breakpoint, and the floating menu only
   above it — so each instance is hidden where the other one is doing the job. */
.drawer .switch { display: block; }
@media (min-width: 60.01rem) { .drawer { }  }
@media (max-width: 60rem) {
  .mast__inner .switch { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .switch__menu.is-open { animation: none; }
  .switch__item:hover, .switch__item:focus-visible { transform: none; }
  .switch__chev { transition: none; }
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-height: 44px;
  padding: 12px var(--space-md);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
  border: var(--rule-medium) solid transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.btn__arrow { transition: transform var(--dur-mid) var(--ease-out); }
.btn:hover .btn__arrow { transform: translateX(3px); }

.btn--solid {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-paper-3);
  box-shadow: 0 1px 2px rgba(var(--scrim-rgb), 0.12);
}
.btn--solid:hover { background: var(--color-accent-soft); border-color: var(--color-accent-soft); }
.btn--solid:active { transform: translateY(1px); box-shadow: none; }

/* C1 · Outlined chip — slab shape, spacious, arrow adornment */
.btn--chip {
  border-radius: var(--radius-sm);
  border-color: currentColor;
  padding: 16px var(--space-lg);
  font-size: var(--text-base);
  background: transparent;
}
.btn--chip:hover { background: var(--color-ink); color: var(--color-paper); border-color: var(--color-ink); }
.btn--chip:active { transform: translateY(1px); }
.band--ink .btn--chip:hover { background: var(--color-paper); color: var(--color-ink); border-color: var(--color-paper); }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* ==========================================================================
   The Ascent · scroll-driven photographic sequence
   --------------------------------------------------------------------------
   Twenty stills of one continuous push-in. A rAF-throttled listener writes a
   single scalar --p (0 → 1) onto .ascent; JS maps it to a frame index and
   cross-dissolves that frame into the next.

   Two properties move: opacity, cross-dissolving frame to frame, and a scale
   that grows each frame across its own interval so the push is continuous
   rather than stepped. Both are compositor-only — no layout, no paint.
   ========================================================================== */

@property --p {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

.ascent {
  --p: 0;
  position: relative;
  /* 200vh of scroll for 2.02x of travel — about 100vh per doubling, which
     holds the perceived push rate of the previous build (which spent ~123vh
     per doubling across a far longer 11.5x). Shortening this makes the move
     feel hurried; lengthening it makes a 2x push feel like drift. */
  height: 300vh;
  background: var(--color-massif-deep);
}

.ascent__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: clip;
  isolation: isolate;
  background: var(--color-massif-deep);
}

.ascent__frames { position: absolute; inset: 0; z-index: 1; }

/* The camera move is inside the file. Nothing here is transformed, nothing is
   cross-faded, and the only thing scroll changes is which frame is decoded —
   so the fold costs no layout and no paint, just the decode.

   object-position sits above centre because the summit does: the clip is 16:9
   and the stage is usually taller than that, so the crop has to keep the peak
   and the flag in frame rather than the foreground rock. */
.ascent__video,
.ascent__still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  background: var(--color-massif-deep);
}

/* The still is the fallback layer and sits under the video, revealed by the
   video being hidden rather than by being raised. That way a video that fails
   to decode uncovers a real image instead of a dark rectangle. */
.ascent__still { z-index: 0; opacity: 0; }
.ascent__video { z-index: 1; }

/* -- Scrim ----------------------------------------------------------------
   Two bands in one element. The top band is always on — the nav sits over a
   genuinely bright sunrise and needs it. The bottom band ramps in with
   "YOUR STUDY", so the wide opening frame stays ungraded.                    */

.ascent__scrim {
  --your:  clamp(0, calc((var(--p) - 0.42) / 0.22), 1);
  --words: clamp(0, calc((var(--p) - 0.14) / 0.16), 1);   /* tracks "OWN" */
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    /* Side grade. The lockup is left-aligned, so the left third is where the
       cream type has to survive — and at sunrise that third is bright orange,
       which cream alone only reaches ~2.5:1 against. Grading the type's own
       side leaves the climber and the flag completely untouched. */
    linear-gradient(to right,
      rgba(var(--scrim-rgb), calc(0.58 * var(--words))) 0%,
      rgba(var(--scrim-rgb), calc(0.34 * var(--words))) 26%,
      rgba(var(--scrim-rgb), calc(0.08 * var(--words))) 44%,
      rgba(var(--scrim-rgb), 0) 58%),
    /* Top band — buys the nav its contrast for the whole fold. */
    linear-gradient(to bottom,
      rgba(var(--scrim-rgb), 0.62) 0%,
      rgba(var(--scrim-rgb), 0.3)  12%,
      rgba(var(--scrim-rgb), 0)    30%),
    /* Bottom band — arrives with "YOUR STUDY". */
    linear-gradient(to top,
      rgba(var(--scrim-rgb), calc(0.86 * var(--your))) 0%,
      rgba(var(--scrim-rgb), calc(0.52 * var(--your))) 18%,
      rgba(var(--scrim-rgb), calc(0.16 * var(--your))) 34%,
      rgba(var(--scrim-rgb), 0) 48%);
}

/* -- The words ------------------------------------------------------------
   OWN and YOUR STUDY stack on one left axis as a single lockup. Left-aligned
   rather than centred: the flag flies right-of-centre through most of the
   sequence, and a centred word runs straight into it.                        */

.ascent__lockup {
  position: absolute;
  left: 0;
  top: 13%;
  z-index: 3;
  width: 100%;
  max-width: var(--shell);
  padding-inline: clamp(1rem, 4vw, 1.5rem);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(2px, 0.6vh, 10px);
  pointer-events: none;
}
@media (min-width: 1180px) {
  /* Same left rail as every section below the fold. */
  .ascent__lockup { left: 50%; transform: translateX(-50%); }
}

/* The page's display serif, as originally set. */
.ascent__word {
  position: static;
  margin: 0;
  font-family: var(--font-display);
  font-variation-settings: "opsz" 144;
  font-weight: 700;
  font-style: normal;              /* roman display — never italic */
  line-height: 0.88;
  color: var(--color-on-ink);
  text-wrap: balance;
  pointer-events: none;
  will-change: transform, opacity;
}

.ascent__word--own {
  --own: clamp(0, calc((var(--p) - 0.16) / 0.18), 1);
  font-size: var(--text-summit);
  letter-spacing: -0.045em;
  opacity: var(--own);
}

.ascent__word--your {
  --your: clamp(0, calc((var(--p) - 0.5) / 0.2), 1);
  font-size: var(--text-base-camp);
  letter-spacing: -0.02em;
  /* The two words were running together at this weight and tracking. The gap
     is opened on word-spacing rather than by loosening letter-spacing, which
     would have slackened the whole line to fix one join. */
  word-spacing: 0.22em;
  opacity: var(--your);
}

/* The rise rides `translate`, not `transform`, so it composes with the
   centring translateX above instead of overwriting it. */
.ascent__word--own  { translate: 0 calc((1 - var(--own))  * 30px); }
.ascent__word--your { translate: 0 calc((1 - var(--your)) * 24px); }

/* -- Fold caption + scroll cue -------------------------------------------- */

.ascent__caption,
.ascent__cue {
  position: absolute;
  bottom: clamp(1rem, 4vw, 1.75rem);
  z-index: 4;
  margin: 0;
  font-size: var(--text-xs);
  line-height: 1.5;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--color-on-ink);
  text-shadow: 0 1px 12px rgba(var(--scrim-rgb), 0.6);
}
.ascent__caption {
  left: clamp(1rem, 4vw, 1.5rem);
  max-width: 30ch;
  opacity: clamp(0, calc((0.14 - var(--p)) / 0.1), 1);
}
.ascent__cue {
  right: clamp(1rem, 4vw, 1.5rem);
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  opacity: clamp(0, calc((0.1 - var(--p)) / 0.08), 1);
}
.ascent__cue svg { animation: cue-drift 2.6s var(--ease-in-out) infinite; }
@keyframes cue-drift {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(5px); }
}

/* ==========================================================================
   The climb · manifesto
   Whitespace is the layout here. Each idea gets its own band with air around
   it, so the section reads as a sequence of statements rather than a wall of
   prose — which is the argument the copy is making.
   ========================================================================== */

.climb {
  padding-block: clamp(5.5rem, 14vw, 12rem);
  background: var(--color-paper);
}

/* -- Flat layout ----------------------------------------------------------
   Laid across the width rather than stacked down it. The statement holds one
   column, the narrative the other, and the four steps run as a single row
   beneath both — which is what stops a page of short lines turning into a
   very tall page of short lines.                                            */

.climb__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: clamp(2rem, 6vw, 5rem);
  row-gap: clamp(2.75rem, 6vw, 4.5rem);
  align-items: start;
}

@media (min-width: 60rem) {
  .climb__grid { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
  .climb__open  { grid-column: 1; }
  .climb__intro { grid-column: 2; }
  .climb__steps { grid-column: 1 / -1; }
  /* Let the column set the measure. A ch-based cap fought the grid and
     squeezed the statement into two thirds of its own column. Scoped through
     the grid so it beats the base rule, which is declared further down. */
  .climb__grid .climb__open { max-width: none; }
}

/* The narrative column carries its own rhythm; the grid handles the rest. */
.climb__intro { display: flex; flex-direction: column; gap: clamp(1.5rem, 3vw, 2.25rem); }

/* -- Opening statement ----------------------------------------------------
   Two lines, one thought. The second carries the turn, so it takes the accent
   and a step up in size — the emphasis is structural, not decorative. */
.climb__open {
  margin: 0;
  font-family: var(--font-display);
  font-variation-settings: "opsz" 144;
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.03em;
  text-wrap: balance;
  max-width: 22ch;
}
.climb__open-a,
.climb__open-b { display: block; }
.climb__open-a {
  font-size: clamp(1.85rem, 5.4vw, 3.9rem);
  color: var(--color-ink);
}
.climb__open-b {
  font-size: clamp(2.15rem, 6.4vw, 4.7rem);
  font-weight: 700;
  color: var(--color-accent);
  margin-top: 0.12em;
}

/* -- The turn -------------------------------------------------------------
   The hinge from the opening into the story. Given its own band and a rule
   above it so it reads as a new movement rather than the next sentence. */
.climb__turn {
  margin: 0;
  font-family: var(--font-display);
  font-variation-settings: "opsz" 72;
  font-weight: 600;
  font-size: clamp(1.5rem, 3.6vw, 2.6rem);
  line-height: 1.2;
  letter-spacing: -0.02em;
  color: var(--color-ink);
  max-width: 24ch;
}

.climb__body {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(1rem, 1.5vw, 1.16rem);
  line-height: 1.76;
  color: var(--color-ink-500);
  max-width: 62ch;
}

/* A single line that has to land, so it is pulled out of the prose entirely. */
.climb__aside {
  margin: 0;
  font-family: var(--font-display);
  font-variation-settings: "opsz" 60;
  font-weight: 600;
  font-size: clamp(1.15rem, 2.4vw, 1.7rem);
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--color-ink);
  max-width: 26ch;
}

/* -- The four steps -------------------------------------------------------
   An ordered list because the order is real — the pathway cannot be built
   before the first three are done. Numerals are suppressed; the sequence is
   carried by a tick that lengthens step by step, which is the climb read left
   to right instead of drawn.                                                */
.climb__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: clamp(1.25rem, 3vw, 2.5rem);
}
.climb__steps li {
  padding-top: clamp(1rem, 2vw, 1.4rem);
  border-top: var(--rule-medium) solid var(--color-rule);
  font-family: var(--font-display);
  font-variation-settings: "opsz" 48;
  font-weight: 600;
  font-size: clamp(1.02rem, 1.5vw, 1.32rem);
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--color-ink);
  text-wrap: balance;
}
/* The rule above each step grows with the ascent. */
.climb__steps li { position: relative; }
.climb__steps li::before {
  content: "";
  position: absolute;
  top: calc(var(--rule-medium) * -1);
  left: 0;
  height: var(--rule-medium);
  background: var(--color-accent);
  transition: width var(--dur-mid) var(--ease-out);
}
.climb__steps li:nth-child(1)::before { width: 22%; }
.climb__steps li:nth-child(2)::before { width: 42%; }
.climb__steps li:nth-child(3)::before { width: 66%; }
.climb__steps li:nth-child(4)::before { width: 100%; }
.climb__steps li:last-child { color: var(--color-accent); }
.climb__steps em {
  font-style: normal;
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.16em;
  text-decoration-color: rgba(99, 91, 255, 0.4);
}

/* Staggered reveals — the same rise the rest of the page uses, just walked
   down the list so the four steps arrive in order rather than together. */
.climb__steps.is-in li {
  animation: climb-step 0.62s var(--ease-out) backwards;
}
.climb__steps.is-in li:nth-child(1) { animation-delay: 0.02s; }
.climb__steps.is-in li:nth-child(2) { animation-delay: 0.10s; }
.climb__steps.is-in li:nth-child(3) { animation-delay: 0.18s; }
.climb__steps.is-in li:nth-child(4) { animation-delay: 0.28s; }
@keyframes climb-step {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .climb__steps.is-in li { animation: none; }
}


/* ==========================================================================
   Practices · F3 tabular spec sheet, 3 columns, hairline rule every row
   ========================================================================== */

.roster {
  border-top: var(--rule-hair) solid var(--color-rule);
}

.row {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 9fr) minmax(0, 4fr);
  gap: var(--space-md) var(--space-lg);
  align-items: baseline;
  padding-block: var(--space-lg);
  border-bottom: var(--rule-hair) solid var(--color-rule);
  transition: background-color var(--dur-mid) var(--ease-out);
}
.row:hover { background: rgba(var(--division-rgb, 18, 26, 47), 0.05); }

/* Each practice is a division of the one brand, so the row carries the same
   coloured-denomination logic as the switcher: "Own Your" stays charcoal and
   only the specialism takes colour. */
.row__name b {
  font-weight: inherit;
  color: var(--division, var(--color-ink));
}
.row[data-division]:hover .row__go { color: var(--division, var(--color-accent)); }
.row[data-division] { position: relative; }
/* A short coloured edge on hover — the same device the switcher uses to mark
   the active division, borrowed here as the hover signal. */
.row[data-division]::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  translate: 0 -50%;
  width: 3px;
  height: 0;
  border-radius: 2px;
  background: var(--division, var(--color-accent));
  transition: height var(--dur-mid) var(--ease-out);
}
.row[data-division]:hover::before { height: 62%; }

/* Name and stage stack, always. Sharing one line meant "Own Your IB" fitted
   beside its stage label while "Own Your A Levels / GCSE" did not, so the
   block was one line tall on some rows and two on others. Stacking them makes
   every row identical at every width, which is the point of a list. */
.row__name {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  font-family: var(--font-display);
  font-variation-settings: "opsz" 72;
  font-weight: 600;
  font-size: var(--text-2xl);
  line-height: 1.15;
  letter-spacing: -0.018em;
  margin: 0;
  overflow-wrap: anywhere;
  min-width: 0;
  transition: transform var(--dur-mid) var(--ease-out);
}
.row:hover .row__name { transform: translateX(6px); }

.row__text {
  margin: 0;
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--color-ink-500);
}

.row__go {
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 44px;
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
  color: var(--color-accent);
}
.row__go svg { transition: transform var(--dur-mid) var(--ease-out); }
.row:hover .row__go svg { transform: translateX(4px); }

.row__stage {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-ink-300);
  white-space: nowrap;
}

.roster__note {
  margin: var(--space-lg) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-500);
}

/* ==========================================================================
   Standard · the five tenets
   ========================================================================== */

.tenets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
  gap: var(--space-xl) var(--space-2xl);
}
.tenet {
  border-top: var(--rule-hair) solid var(--color-rule-on-ink);
  padding-top: var(--space-md);
}
.tenet__name {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 48;
  font-weight: 600;
  font-size: var(--text-xl);
  line-height: 1.2;
  letter-spacing: -0.015em;
  margin: 0 0 var(--space-xs);
  overflow-wrap: anywhere;
  min-width: 0;
}
.tenet__text {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.62;
  color: var(--color-on-ink-soft);
}

/* ==========================================================================
   Close
   ========================================================================== */

.close { padding-block: var(--space-3xl); text-align: center; }
.close .head { margin-inline: auto; margin-bottom: var(--space-xl); }
.close .lede { margin-inline: auto; }
.close__title {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 120;
  font-weight: 600;
  font-size: var(--text-display-s);
  line-height: 1.06;
  letter-spacing: -0.028em;
  margin: 0;
  overflow-wrap: anywhere;
  min-width: 0;
}

/* ==========================================================================
   Footer · Ft1 mast-headed
   ========================================================================== */

.foot {
  background: var(--color-ink);
  color: var(--color-paper);
  padding-block: var(--space-2xl) var(--space-xl);
}
.foot__mast {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-lg);
  padding-bottom: var(--space-lg);
  border-bottom: var(--rule-hair) solid var(--color-rule-on-ink-soft);
}
.foot__wordmark {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 120;
  font-weight: 600;
  font-size: var(--text-display-s);
  line-height: 0.95;
  letter-spacing: -0.03em;
  margin: 0;
  overflow-wrap: anywhere;
  min-width: 0;
}
.foot__wordmark span { color: var(--color-accent-soft); }
.foot__tagline {
  max-width: 34ch;
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--color-on-ink-soft);
}
.foot__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-lg);
  padding-block: var(--space-lg);
}
.foot__links a {
  font-size: var(--text-sm);
  font-weight: 500;
  white-space: nowrap;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  border-bottom: var(--rule-medium) solid transparent;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.foot__links a:hover { color: var(--color-accent-soft); border-bottom-color: var(--color-accent-soft); }
.foot__base {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-lg);
  justify-content: space-between;
  font-size: var(--text-xs);
  color: var(--color-on-ink-muted);
}

/* ==========================================================================
   Reveal · reveal-rise
   ========================================================================== */

.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
.reveal.is-in { opacity: 1; transform: none; }

/* ==========================================================================
   Responsive · verified at 320 / 375 / 414 / 768
   ========================================================================== */

@media (max-width: 60rem) {
  .row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-xs);
  }
  .row__go { justify-self: start; }
  .row:hover .row__name { transform: none; }


  .mainnav, .mast__inner .btn { display: none; }
  .navtoggle { display: flex; }

  /* Mobile scroll has its own physics — shorten the track and shrink the
     dolly so the zoom lands well inside a thumb-flick. */
  .ascent { height: 240vh; }
  /* 16:9 against a portrait viewport, so `cover` crops hard to the sides.
     Bias the crop toward the summit rather than the centre. */
  .ascent__video, .ascent__still { object-position: 50% 42%; }
}

@media (max-width: 40rem) {
  body { font-size: var(--text-base); }
  .band { padding-block: var(--space-2xl); }
  .ascent__word--own { top: 15%; }
  .ascent__word--your { top: 68%; }
  .ascent__cue { display: none; }   /* the caption alone carries the fold */
  .foot__base { flex-direction: column; }
}

/* ==========================================================================
   Reduced motion — the scene resolves to its final composition, no scrubbing
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .ascent {
    --p: 0.82 !important;
    height: auto;
  }
  .ascent__stage { position: relative; }
  /* The still is revealed by JS (showStill), but pin it here too so the
     resolved composition survives even if the script never runs. */
  .ascent__video { opacity: 0; }
  .ascent__still { opacity: 1; }
  .ascent__word, .ascent__mark { will-change: auto; }
  .ascent__cue { display: none; }

  .reveal { opacity: 1; transform: none; transition: opacity 140ms linear; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 140ms !important;
  }
}
