/* motion.css: everything the motion layer needs from CSS.
   Every rule here is inert on the static page: it either sits behind a class the motion
   layer adds (html.motion, .is-*), behind the preboot classes (html.preload, html.intro),
   or is a cross-document view transition that browsers without support ignore.
   Tokens only: --dur-1/2/3 (.4/.8/1.2 s), --ease-out and --ease-inout. See docs/MOTION.md. */

/* =========================================================
   Preloader (first page of a session only; shown by preboot.js before first paint)
   ========================================================= */
html.preload { overflow: hidden; }
html.preload .preloader[data-preloader][hidden] { display: block !important; }
html.preload .brand__mark { opacity: 0; }
.preloader {
  position: fixed;
  inset: 0;
  z-index: var(--z-preloader);
  pointer-events: none;
  color: var(--sand);
}
html.preload .preloader { pointer-events: auto; }
.preloader__veil {
  position: absolute;
  inset: 0;
  background: var(--ink);
}
/* the surface: when the veil lifts, its lower edge is a waterline with a bright meniscus */
.preloader__surface {
  position: absolute;
  inset: auto 0 0 0;
  height: 1px;
  background: color-mix(in srgb, var(--sand) 70%, transparent);
  box-shadow: 0 0 18px 2px color-mix(in srgb, var(--sand) 22%, transparent);
  opacity: 0;
}
.preloader__mark {
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(120px, 20vmin, 200px);
  height: auto;
  aspect-ratio: 1;
  translate: -50% -55%;
  overflow: visible;
  transform-origin: 0 0;
}
.preloader__body { fill: currentColor; opacity: 0; }
.preloader__lines path, .preloader__lines ellipse {
  fill: none;
  stroke: currentColor;
  stroke-width: 6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
/* line by line: the shell, then the three vertebral scutes, then the ribs pair by pair */
html.preload .preloader__lines > * {
  animation: scute-draw var(--dur-2) var(--ease-inout) forwards;
  animation-delay: calc(0.12s + var(--i) * 0.06s);
}
html.preload .preloader__body { animation: body-in var(--dur-2) var(--ease-out) 0.7s forwards; }
@keyframes scute-draw { to { stroke-dashoffset: 0; } }
@keyframes body-in { to { opacity: 0.22; } }
.preloader__count {
  position: absolute;
  left: var(--gutter);
  bottom: max(var(--s-6), env(safe-area-inset-bottom));
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--step--1);
  letter-spacing: var(--track-label);
  color: var(--sand-2);
  max-width: none;
}
.preloader__pct { margin-left: 0.15em; color: var(--sand-3); }

/* =========================================================
   Intro: the hero words wait for their reveal (preboot adds html.intro)
   ========================================================= */
html.intro [data-section="hero"] .hero__title,
html.intro [data-section="hero"] .hero__action { opacity: 0; }
/* The lead is the page's LCP element, so it is never hidden: it paints in the first frame a
   little low and rises into place on its beat (transform only). Under the preloader veil
   html.intro is not set and the full fade plays behind the veil. If the motion layer is late,
   preboot drops html.intro and the lead settles with the same ease. */
html.intro [data-section="hero"] .hero__lead { translate: 0 16px; }
html.js:not(.motion) [data-section="hero"] .hero__lead { transition: translate var(--dur-2) var(--ease-out); }

/* Split text: words and chars become inline blocks so they can move */
.split-word { display: inline-block; }
.split-char { display: inline-block; }
.statement .split-word { white-space: nowrap; }

/* =========================================================
   Light follows depth (html.depth-driven comes from base.css; --water is set on body)
   ========================================================= */
html.motion body { transition: none; }

/* =========================================================
   Navigation: a persistent overlay; the controls keep their own liquid surfaces
   ========================================================= */
html.motion.nav-on-water .site-nav { color: var(--sand); }
/* the sound label sits with the nav links, at their size */
.sound-toggle { font-size: 0.9375rem; font-weight: 520; white-space: nowrap; }

/* =========================================================
   The depth gauge (built by gauge.js into aside[data-gauge])
   ========================================================= */
.gauge { position: fixed; z-index: var(--z-gauge); pointer-events: none; color: var(--sand); }
.gauge.is-built { opacity: 0; transition: opacity var(--dur-2) var(--ease-out), color var(--dur-1) var(--ease-out); }
.gauge.is-on-sand { color: var(--ink); }
.gauge.is-awake { opacity: 1; }
.gauge__readout {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  line-height: 1;
  white-space: nowrap;
}

@media (min-width: 1024px) {
  .gauge {
    right: 0;
    top: 50%;
    width: 92px;
    height: min(54vh, 500px);
    translate: 0 -54%;
  }
  /* the scale crosses pale sky, bright photographs and dark water: sand marks with a soft
     ink halo stay legible over all of them (a static filter on a small fixed element) */
  .gauge__scale { position: absolute; inset: 0; filter: drop-shadow(0 0 1.5px rgb(11 26 28 / 0.75)); }
  .gauge.is-on-sand .gauge__scale { filter: none; }
  .gauge.is-on-sand .gauge__mark::before { background: var(--sand); }
  .gauge__ticks { position: absolute; right: 16px; top: 0; width: 10px; height: 100%; overflow: visible; }
  .gauge__ticks line { stroke: currentColor; stroke-width: 1; vector-effect: non-scaling-stroke; }
  .gauge__ticks .is-major { opacity: 0.85; }
  .gauge__ticks .is-minor { opacity: 0.35; }
  .gauge__label {
    position: absolute;
    right: 31px;
    translate: 0 -50%;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.625rem;
    letter-spacing: 0.04em;
    opacity: 0.75;
  }
  /* the dive log: depth over page progress, a small graph under the scale like a dive
     computer's screen, traced up to where you are, the full plan faint behind it */
  .gauge__log { position: absolute; top: calc(100% + 16px); right: 10px; width: 78px; height: 30px; }
  .gauge__profile { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
  .gauge__plan, .gauge__trace { fill: none; stroke: currentColor; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
  .gauge__plan { stroke-width: 1; opacity: 0.25; }
  .gauge__trace { stroke-width: 1.25; }
  .gauge__head {
    position: absolute;
    left: 0;
    top: 0;
    width: 5px;
    height: 5px;
    margin: -2.5px 0 0 -2.5px;
    border-radius: 50%;
    background: currentColor;
    will-change: transform;
  }
  .gauge__marker {
    position: absolute;
    right: 12px;
    top: 0;
    width: 18px;
    height: 1px;
    background: currentColor;
    will-change: transform;
  }
  /* the depth readout sits under the scale, like a dive computer's big number */
  .gauge__depth {
    position: absolute;
    top: calc(100% + 56px);
    right: 8px;
    padding: 5px 8px 4px;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--ink) 82%, transparent);
    color: var(--sand);
    font-size: 0.8125rem;
  }
  /* section marks: a notch per chapter, a link, labelled on hover */
  .gauge__mark {
    position: absolute;
    right: 6px;
    width: 24px;
    height: 18px;
    translate: 0 -50%;
    pointer-events: auto;
    cursor: pointer;
    display: grid;
    place-items: center end;
    color: inherit;
  }
  .gauge__mark::before {
    content: "";
    width: 7px;
    height: 7px;
    margin-right: 6px;
    border-radius: 50%;
    border: 1px solid currentColor;
    background: var(--ink);
    transition: background-color var(--dur-1) var(--ease-out), scale var(--dur-1) var(--ease-out);
  }
  .gauge__mark.is-current::before { background: currentColor; }
  .gauge__mark-label {
    position: absolute;
    right: 30px;
    top: 50%;
    translate: 6px -50%;
    padding: 6px 10px;
    border-radius: var(--r-pill);
    background: var(--ink);
    color: var(--sand);
    font-size: 0.8125rem;
    font-weight: 520;
    white-space: nowrap;
    opacity: 0;
    transition: opacity var(--dur-1) var(--ease-out), translate var(--dur-1) var(--ease-out);
  }
  .gauge__mark-label:empty { display: none; }
  @media (hover: hover) and (pointer: fine) {
    .gauge__mark:hover .gauge__mark-label { opacity: 1; translate: 0 -50%; }
    .gauge__mark:hover::before { scale: 1.3; }
  }
  .gauge__bar { display: none; }
}

/* Phones and narrow tablets: a thin edge bar and the number only */
@media (max-width: 1023.98px) {
  .gauge {
    right: 0;
    top: 0;
    bottom: 0;
    width: 3px;
  }
  .gauge__scale { display: none; }
  .gauge__bar {
    position: absolute;
    inset: 0;
    background: color-mix(in srgb, var(--sand) 12%, transparent);
  }
  .gauge__bar-fill {
    position: absolute;
    inset: 0;
    background: color-mix(in srgb, var(--sand) 70%, transparent);
    transform-origin: 50% 0;
    transform: scaleY(0.001); /* gauge.js sets the real value at once */
    will-change: transform;
  }
  .gauge__pill {
    position: fixed;
    right: 8px;
    bottom: max(12px, env(safe-area-inset-bottom));
    padding: 6px 10px;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--ink) 78%, transparent);
    color: var(--sand);
    transition: opacity var(--dur-1) var(--ease-out);
  }
  /* shore sections hold the forms: the pill steps aside rather than sit on a field */
  .gauge.is-on-sand .gauge__pill { opacity: 0; }
}

/* =========================================================
   Root hero: the descent. Sticky stage inside a taller hero (no JS pinning)
   ========================================================= */
html.motion .hero--waterline.is-descent { min-height: 0; height: var(--runway, 240svh); overflow: clip; }
html.motion .hero--waterline.is-descent .hero__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  min-height: 0;
  overflow: clip;
}
/* the end of the descent: a soft shade from the surface down, for the statement that arrives
   over the final frame. The turtle and the seabed stay in the light. */
.hero__dim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--reef) 82%, transparent) 0%, color-mix(in srgb, var(--reef) 45%, transparent) 34%, transparent 62%);
  opacity: 0;
  pointer-events: none;
}
/* the descent chapter rises over the last of the runway: its statement arrives over the
   turtle frame, then its own plate follows */
html.motion .descent.is-arrival { position: relative; z-index: 1; margin-top: calc(var(--overlap, 92svh) * -1); padding-top: 5svh; }
html.motion .descent.is-arrival .descent__text { align-self: start; margin-top: 0; padding-bottom: 0; }
/* a calm field for the paragraph: a soft directional pool of water colour behind the text
   column only (no box, no hard edge). It fades out to the right so the turtle stays in the light. */
html.motion .descent.is-arrival .descent__text { position: relative; isolation: isolate; }
html.motion .descent.is-arrival .descent__text::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -12svh -18vw -12svh -30vw;
  pointer-events: none;
  background: radial-gradient(ellipse closest-side at 60% 50%,
    color-mix(in srgb, var(--reef) 95%, transparent) 0%,
    color-mix(in srgb, var(--reef) 91%, transparent) 55%,
    color-mix(in srgb, var(--reef) 58%, transparent) 80%,
    color-mix(in srgb, var(--reef) 18%, transparent) 93%,
    transparent 100%);
}
@media (max-width: 767.98px) {
  html.motion .descent.is-arrival .descent__text::before { inset: -9svh -16vw -9svh -16vw; background: radial-gradient(ellipse closest-side at 50% 50%, color-mix(in srgb, var(--reef) 92%, transparent) 0%, color-mix(in srgb, var(--reef) 86%, transparent) 55%, color-mix(in srgb, var(--reef) 40%, transparent) 82%, transparent 100%); }
}
html.motion .descent.is-arrival .descent__media { margin-top: 70svh; }
@media (min-width: 768px) {
  html.motion .descent.is-arrival .descent__media { margin-top: 80svh; }
}
html.motion .is-descent .hero__title, html.motion .is-descent .hero__action { will-change: clip-path; }

/* =========================================================
   Species: pinned horizontal gallery (desktop), sticky stage, rail moves
   ========================================================= */
html.motion .species.is-pinned { height: calc(100svh + var(--runway, 0px)); padding-block: 0; }
html.motion .species.is-pinned .species__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
  display: flex;
  align-items: center;
}
html.motion .species.is-pinned .species__rail {
  display: flex;
  align-items: center;
  width: max-content;
  will-change: transform;
}
html.motion .species.is-pinned .species__head {
  flex: none;
  width: min(36vw, 520px);
  max-width: none;
  margin: 0;
  padding-right: 0;
}
html.motion .species.is-pinned .species__viewport { margin-top: 0; flex: none; }
html.motion .species.is-pinned .species-track.is-horizontal { padding-inline: clamp(3rem, 7vw, 8rem) 18vw; align-items: center; }
html.motion .species.is-pinned .species-track.is-horizontal .plate {
  width: max(240px, calc(min(54svh, 560px) * 2 / 3));
  margin: 0;
}
html.motion .species.is-pinned .species-track.is-horizontal .plate:nth-child(even) { transform: none; }
html.motion .species.is-pinned .plate__figure { transform-origin: 50% 60%; will-change: transform; }
html.motion .species.is-pinned .plate__desc { max-width: 34ch; }
html.motion .species.is-pinned .species__rail.is-dragging { cursor: grabbing; user-select: none; }

/* "Drag": an information label that follows a fine pointer over the gallery (cursor stays native) */
.drag-tip {
  position: fixed;
  left: 0;
  top: 0;
  z-index: var(--z-overlay);
  padding: 8px 14px 7px;
  border-radius: var(--r-pill);
  background: var(--sand);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  translate: 18px 18px;
  transition: opacity var(--dur-1) var(--ease-out);
}
.drag-tip.is-on { opacity: 1; }

/* =========================================================
   Threats: the fishing line draws, then is cut
   ========================================================= */
html.motion .fishing-line__path { stroke-dasharray: 1; stroke-dashoffset: 1; transform-box: view-box; }

/* =========================================================
   T.U.R.B.E.I.N.G.s: a pinned sequence of giant glyphs (desktop)
   ========================================================= */
html.motion .letters.is-sequence { height: calc(100svh + var(--runway, 450svh)); padding-block: 0; }
html.motion .letters.is-sequence .letters__grid {
  position: sticky;
  top: 0;
  height: 100svh;
  grid-template-rows: auto minmax(0, 1fr) auto;
  padding-block: calc(var(--nav-h) + var(--s-5)) var(--s-7);
  overflow: clip;
}
html.motion .letters.is-sequence .letters__head { position: static; grid-column: 1 / 7; grid-row: 1; }
html.motion .letters.is-sequence .letters__head .lead { margin-top: var(--s-3); font-size: var(--step-0); font-weight: var(--w-body); }
html.motion .letters.is-sequence .letter-list {
  grid-column: 1 / -1;
  grid-row: 2;
  position: relative;
  display: block;
  margin: 0;
  /* the row between the heading block and the tracker is the glyph's measure (cqh below) */
  container-type: size;
}
html.motion .letters.is-sequence .letter {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gap);
  align-items: center;
  font-size: 1rem;
  pointer-events: none;
}
html.motion .letters.is-sequence .letter.is-active { pointer-events: auto; }
/* The giant glyph: the accent face in capitals (Max, 2026-10-08), every letter the same size, the
   final S included. It is sized from the row it sits in, not from the screen: CS Edna's capitals
   are 0.70em tall, so a font size of 76cqh / 0.70 makes the capital 76% of the row, and a line
   box of exactly one capital height (line-height 0.7; Edna's ascent, descent and cap height
   balance, so the capital is centred in it) is centred by the row's align-items. That leaves the
   same 12% of the row above and below at every screen size, clear of the lead paragraph and the
   tracker. The italic leans about 0.1em past its advance on the right; the column has room. */
html.motion .letters.is-sequence .letter__glyph {
  grid-column: 1 / 8;
  --glyph-cap: 0.7;
  font-size: min(calc(76cqh / var(--glyph-cap)), 150cqw);
  line-height: var(--glyph-cap);
  text-align: left;
  letter-spacing: 0;
  padding-left: 0.02em;
  will-change: transform, opacity;
}
html.motion .letters.is-sequence .letter__text { grid-column: 8 / 13; padding: 0; will-change: transform, opacity; }
html.motion .letters.is-sequence .letter__name {
  font-family: var(--font-display);
  font-weight: var(--w-display);
  font-size: var(--step-4);
  line-height: var(--lh-display);
  letter-spacing: -0.02em;
  display: block;
}
html.motion .letters.is-sequence .letter__gloss { display: block; margin-top: var(--s-2); }
html.motion .letters.is-sequence .letter__text p { font-size: var(--step-1); line-height: 1.5; margin-top: var(--s-5); max-width: 32ch; }
.letters__index {
  grid-column: 1 / -1;
  grid-row: 3;
  display: flex;
  gap: 0.35em;
  font-family: var(--font-display);
  font-weight: var(--w-title);
  font-size: var(--step-3);
  line-height: 1;
  color: var(--fg-2);
}
.letters__index span { opacity: 0.35; transition: opacity var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out); }
.letters__index span.is-past { opacity: 0.7; }
.letters__index span.is-active { opacity: 1; color: var(--fg); }

/* =========================================================
   Coastline: the chart unfolds (fold panels), the coast draws, towns arrive
   ========================================================= */
.coastmap__fold { position: absolute; inset: 0; perspective: 1600px; pointer-events: none; }
.fold-panel {
  position: absolute;
  left: 0;
  right: 0;
  overflow: hidden;
  backface-visibility: hidden;
  will-change: transform;
}
.fold-panel > svg { position: absolute; left: 0; width: 100%; overflow: visible; }
.fold-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink);
  opacity: var(--shade, 0);
}
.coastmap.is-folding .coastmap__frame > .coastmap__svg { visibility: hidden; }
html.motion .coastmap__coast { stroke-dasharray: 1 1; }
.coastmap__ring { fill: none; stroke: var(--sand); stroke-width: 1; vector-effect: non-scaling-stroke; opacity: 0; }

/* =========================================================
   Partner ticker: speed follows scroll velocity, still at rest
   ========================================================= */
.ticker {
  overflow: clip;
  padding-block: var(--s-5);
  margin-bottom: var(--s-8);
  border-block: 1px solid var(--line);
  user-select: none;
}
.ticker__track { display: flex; width: max-content; will-change: transform; }
.ticker__group {
  display: flex;
  align-items: center;
  gap: clamp(1.5rem, 3vw, 3rem);
  padding-right: clamp(1.5rem, 3vw, 3rem);
  font-family: var(--font-display);
  font-weight: var(--w-display);
  font-size: var(--step-4);
  line-height: 1.1;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.ticker__mark { width: 0.55em; height: 0.55em; --shell: transparent; --scute: currentColor; flex: none; }

/* Footer: the wordmark surfaces letter by letter */
.site-footer__mark .split-char { will-change: transform; }

/* =========================================================
   Page transitions: cross-document view transitions with a waterline wipe.
   The new page rises from below like water filling the frame; the old page settles.
   ========================================================= */
@view-transition { navigation: auto; }
.site-nav { view-transition-name: site-nav; }
::view-transition-group(root) { animation-duration: var(--dur-2); }
::view-transition-old(root) { animation: vt-settle var(--dur-2) var(--ease-inout) both; }
::view-transition-new(root) { animation: vt-rise var(--dur-2) var(--ease-inout) both; }
::view-transition-group(site-nav) { animation-duration: var(--dur-1); animation-timing-function: var(--ease-out); }
@keyframes vt-rise { from { clip-path: inset(100% 0 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes vt-settle { to { transform: translateY(-4%); opacity: 0.5; } }
@keyframes vt-fade-in { from { opacity: 0; } }
@keyframes vt-fade-out { to { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root) { animation: vt-fade-out 0.15s linear both; }
  ::view-transition-new(root) { animation: vt-fade-in 0.15s linear both; }
  html.preload .preloader[data-preloader][hidden] { display: none !important; }
}
