/* ==========================================================================
   The ascent — shared scroll-seeked summit fold
   --------------------------------------------------------------------------
   Drop-in for any page in the network. Self-contained: every value it needs
   is declared here with a fallback, so it works alongside the root site's
   tokens.css and alongside the sub-sites' styles.css without either.

   Where tokens.css IS present its values win, so the root page and the six
   practice sites stay in step automatically.

   Requires: assets/ascent/ascent.js and the markup in the README.
   ========================================================================== */

/* Declared on :root, not on .ascent — the masthead inverts to cream over the
   fold, and it is NOT a descendant of .ascent, so a palette scoped to the
   section would leave var(--a-on-ink) unresolved on the nav. */
:root {
  /* Brand surface. Falls back to the network's midnight navy. */
  --a-ink:        var(--color-massif-deep, #0a1020);
  --a-on-ink:     var(--color-on-ink, #faf7f1);
  --a-scrim-rgb:  var(--scrim-rgb, 18, 26, 47);
  --a-shell:      var(--shell, 1180px);
  --a-display:    var(--font-display, "Fraunces", "Playfair Display", Georgia, serif);

  /* "OWN" and the practice name. Sized against both vw and vh so a long name
     ("YOUR UNI ADMISSION") cannot overflow a short landscape viewport. */
  --a-summit:     var(--text-summit, clamp(2.75rem, min(15vw, 17vh), 12rem));
  --a-base-camp:  var(--text-base-camp, clamp(1.5rem, min(7vw, 8.5vh), 5.5rem));
}

.ascent {
  --p: 0;
  position: relative;
  /* 200vh of scroll for 2.02x of travel — about 100vh per doubling. Shorten
     it and the push feels hurried; lengthen it and a 2x move reads as drift. */
  height: 300vh;
  background: var(--a-ink);
  scroll-margin-top: 0;
}

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

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

/* The camera move is inside the file. Nothing here is transformed and nothing
   is cross-faded — scroll only changes 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, so the crop must keep the peak and 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(--a-ink);
}

/* The still sits UNDER the video and is revealed by the video hiding, not by
   being raised — so a failed decode uncovers a photograph, not a dark box. */
.ascent__still { z-index: 0; opacity: 0; }
.ascent__video { z-index: 1; }

/* -- Scrim ----------------------------------------------------------------
   Three bands in one element. The top band is always on (the nav sits over a
   genuinely bright sunrise). The side and bottom bands ramp in with the type,
   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);
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    /* Side grade — the lockup is left-aligned, and at sunrise that third of
       frame is bright orange, which cream alone only reaches ~2.5:1 against.
       Grading the type's own side leaves the climber and flag untouched. */
    linear-gradient(to right,
      rgba(var(--a-scrim-rgb), calc(0.58 * var(--words))) 0%,
      rgba(var(--a-scrim-rgb), calc(0.34 * var(--words))) 26%,
      rgba(var(--a-scrim-rgb), calc(0.08 * var(--words))) 44%,
      rgba(var(--a-scrim-rgb), 0) 58%),
    /* Top band — buys the nav its contrast for the whole fold. */
    linear-gradient(to bottom,
      rgba(var(--a-scrim-rgb), 0.62) 0%,
      rgba(var(--a-scrim-rgb), 0.3)  12%,
      rgba(var(--a-scrim-rgb), 0)    30%),
    /* Bottom band — arrives with the practice name. */
    linear-gradient(to top,
      rgba(var(--a-scrim-rgb), calc(0.86 * var(--your))) 0%,
      rgba(var(--a-scrim-rgb), calc(0.52 * var(--your))) 18%,
      rgba(var(--a-scrim-rgb), calc(0.16 * var(--your))) 34%,
      rgba(var(--a-scrim-rgb), 0) 48%);
}

/* -- The words ------------------------------------------------------------
   Left-aligned, not centred: the flag flies right-of-centre through most of
   the sequence and a centred word runs straight into it. Split top and bottom
   on one left axis, the phrase still reads down the page with the climb
   happening between its two halves.                                          */

.ascent__word {
  position: absolute;
  left: 0;
  z-index: 3;
  width: 100%;
  max-width: var(--a-shell);
  padding-inline: clamp(1rem, 4vw, 1.5rem);
  margin: 0;
  font-family: var(--a-display);
  font-variation-settings: "opsz" 144;
  font-style: normal;              /* roman display — never italic */
  font-weight: 700;
  line-height: 0.88;
  color: var(--a-on-ink);
  text-wrap: balance;
  pointer-events: none;
  will-change: transform, opacity;
}

@media (min-width: 1180px) {
  /* Keep the lockup on the same left rail as every section below it. */
  .ascent__word { left: 50%; transform: translateX(-50%); }
}

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

.ascent__word--your {
  --your: clamp(0, calc((var(--p) - 0.5) / 0.2), 1);
  bottom: 15%;
  font-size: var(--a-base-camp);
  letter-spacing: -0.02em;
  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); }

/* -- Caption and cue ------------------------------------------------------ */

.ascent__caption {
  position: absolute;
  left: 0; bottom: 6%;
  z-index: 3;
  width: 100%;
  max-width: var(--a-shell);
  padding-inline: clamp(1rem, 4vw, 1.5rem);
  margin: 0;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--a-on-ink);
  /* An opening subtitle, not a closing one — it is present on the wide shot
     and gone by the time the climb starts, matching the root page. */
  opacity: clamp(0, calc((0.14 - var(--p)) / 0.1), 1);
  pointer-events: none;
}
@media (min-width: 1180px) {
  .ascent__caption { left: 50%; transform: translateX(-50%); }
}

.ascent__cue {
  position: absolute;
  right: clamp(1rem, 4vw, 2rem);
  bottom: 6%;
  z-index: 3;
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--a-on-ink);
  /* Fades out as soon as the reader has taken the hint. */
  opacity: calc(1 - clamp(0, calc(var(--p) / 0.12), 1));
  pointer-events: none;
}

/* -- Masthead over the fold ------------------------------------------------
   The bar floats on a sunrise photograph for the length of the fold, where
   navy type is unreadable. `is-over-hero` is set by ascent.js on whichever
   header the page nominates. Sub-sites use .site-header; the root uses .mast,
   which carries its own richer rules in index.css.                           */

.site-header.is-over-hero {
  background: transparent !important;
  border-bottom-color: transparent !important;
  box-shadow: none !important;
  color: var(--a-on-ink);
  text-shadow: 0 1px 14px rgba(var(--a-scrim-rgb), 0.55);
}
.site-header.is-over-hero .logo,
.site-header.is-over-hero .main-nav a { color: var(--a-on-ink); }
.site-header.is-over-hero .hamburger span { background: var(--a-on-ink); }

/* -- Reduced motion --------------------------------------------------------
   No scrubbing, no pin. The fold collapses to one viewport showing the
   resolved composition, with --p frozen where the lockup is fully present. */

@media (prefers-reduced-motion: reduce) {
  .ascent {
    --p: 0.82 !important;
    height: auto;
  }
  .ascent__stage { position: relative; }
  .ascent__video { opacity: 0; }
  .ascent__still { opacity: 1; }
  .ascent__word  { will-change: auto; }
  .ascent__cue   { display: none; }
}

/* -- Route back to the network -------------------------------------------
   The practice sites had no way home: six sites presented as one network,
   with no link between them. This is deliberately quieter than the peer nav
   items — it leaves the site rather than moving within it. */
.to-network {
  position: relative;
  padding-left: 1.1em;
  opacity: 0.75;
}
.to-network::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 0.55em; height: 0.55em;
  margin-top: -0.28em;
  border-left: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
}
.to-network:hover { opacity: 1; }
