/**
 * The landing page, a descent: each section paints its own slice of water,
 * ending on the color the next begins with, and none of it needs JS.
 * Colors, type and radii come from the app (src/data/taxonomies.ts for the accents).
 */

@import './fonts.css';

/* Tokens ------------------------------------------------------------ */

:root {
  --w-abyss: #030d16;

  --ink: #e9f2f9;
  --ink-dim: #c2dbec;

  /* Taxonomy accents, from src/data/taxonomies.ts, unchanged. */
  --ochre: #b5834a;
  --wine: #9e5b6e;
  --sage: #5b8a72;
  --teal: #4a8f9e;
  --plum: #8a6fae;
  --slate: #3d5a80;
  --sky: #98c1d9;

  /* The accents lightened until they carry words: each clears 4.5:1 against
     the top of the band it labels.
     INVARIANT: a tint belongs to one band; re-measure it if a section moves. */
  --sky-text: #d5e6f0; /* on #156c8a — 4.63:1 */
  --ochre-text: #eac294; /* on #1a5975 — 4.63:1 */
  --wine-text: #e5bfca; /* on #1a5975 — 4.63:1 */
  --sage-text: #a6d2bb; /* on #1a5975 — 4.60:1 */

  --teal-text: #8abdc8; /* on #154a66 — 4.63:1 */
  --plum-text: #b8a3dd; /* on #113f5a — 4.95:1 */

  /* The shelf lockups, a step deeper than the -text tints. The counts are large
     text and clear 3:1; the 15px labels do not reach 4.5:1, accepted because
     the description beneath repeats the category. */
  --ochre-lit: #cf995b; /* on #1a5975 — 3.06:1 */
  --wine-lit: #c396b3; /* on #1a5975 — 3.05:1, hue pulled off red */
  --sage-lit: #76b093; /* on #1a5975 — 3.08:1 */

  --display: 'Space Grotesk', 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif;
  --body: 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --gut: clamp(20px, 7vw, 120px);

  /* Type scale: every piece of running text takes one of these five steps.
     Outside it: section headings and the hero wordmark (display sizes). */
  --t-sub: 19px; /* sub-heads inside a section */
  --t-lede: clamp(17.5px, 1.7vw, 20px); /* section intros, the hero sentence */
  --t-body: 16.5px; /* card and list copy */
  --t-label: 13px; /* mono, tracked: depth markers, pills */
  --t-micro: 12.5px; /* mono, smallest: qualifiers, step numbers */
  /* Set from the nav's measured height by dive.js; this is the no-script fallback. */
  --nav-h: 56px;
  --measure: 62ch;
}

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

html {
  scroll-behavior: smooth;
}

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

body {
  margin: 0;
  background: var(--w-abyss);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1,
h2,
h3 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
  margin: 0;
}

p {
  margin: 0;
}

a {
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--sky);
  outline-offset: 3px;
  border-radius: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: #fff;
  color: #0f2b42;
  padding: 10px 16px;
  border-radius: 0 0 8px 0;
  font-weight: 600;
}

.skip:focus {
  left: 0;
}

/* Depths keep their capital M out of small caps: in chemistry M is molar. */
.depth {
  text-transform: none;
  font-style: normal;
}

.eyebrow {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

/* Navigation -------------------------------------------------------- */

/* Dark from the first pixel: the daylight band is too short for a color change. */
.nav {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 12px var(--gut);
  background: rgba(6, 18, 30, 0.55);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid rgba(152, 193, 217, 0.16);
}

/* The footer's lockup at nav size, in the hero wordmark's bold IBM Plex Sans. */
.nav__mark {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--body);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

.nav__manta {
  flex: 0 0 auto;
  width: 32px;
  height: auto;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(12px, 2vw, 26px);
  font-size: 15px;
  color: var(--ink-dim);
}

.nav__links a {
  text-decoration: none;
}

.nav__links a:hover {
  color: #fff;
}

@media (max-width: 700px) {
  .nav__links a {
    display: none;
  }
}

/* The lockup and the button overrun a 360px screen at the standard gutter. */
@media (max-width: 400px) {
  .nav {
    gap: 12px;
    padding-left: 16px;
    padding-right: 16px;
  }
}

/* The water column -------------------------------------------------- */

.dive {
  position: relative;
  background: var(--w-abyss);
}

/* Each stratum paints its own slice, so the water stays in register with the
   copy: a longer section deepens more slowly. */
/* Sunlit: warm at the surface, turquoise under it, then blue. The linear layer
 * carries the hue (water absorbs red first, then green); the radial layer is the
 * sun, offset right so it sources the light shafts.
 *
 * INVARIANT: the nav covers the first ~62px, so a stop above that is invisible;
 * the stops from 48% down must end on the color #why starts with. */
#top {
  background:
    radial-gradient(
      44% 12% at 68% -5%,
      rgba(255, 243, 213, 0.4),
      rgba(255, 236, 198, 0.14) 46%,
      rgba(255, 236, 198, 0) 80%
    ),
    linear-gradient(
      180deg,
      #fff4dc 0%,
      #fff2d8 0.25%,
      #fff0d4 0.5%,
      #ffeed2 0.75%,
      #fbedd4 1.05%,
      #f0ecd8 1.35%,
      #e4ebdc 1.65%,
      #dceade 1.9%,
      #d5e8dd 2.5%,
      #cce6db 3.2%,
      #c0e4da 4%,
      #b0e1d7 4.9%,
      #9bddd5 5.9%,
      #85d9d1 6.9%,
      #71d4ce 7.9%,
      #5ececa 8.9%,
      #4fc7c6 9.9%,
      #43bec1 10.9%,
      #3bb3bb 11.9%,
      #35a7b5 12.9%,
      #309caf 13.9%,
      #2991aa 14.9%,
      #2387a7 15.9%,
      #2185a6 16.2%,
      #1f82a5 16.5%,
      #1e80a3 16.9%,
      #1c7da2 17.4%,
      #1b7ba0 18%,
      #1a799e 18.8%,
      #19779c 19.8%,
      #18759a 21.3%,
      #177497 23.1%,
      #167294 25.4%,
      #167192 28.2%,
      #167090 31.6%,
      #166f8e 35.6%,
      #166e8c 41%,
      #156d8b 47.3%,
      #156d8a 54.7%,
      #156d8a 63.3%,
      #156c8a 71.8%,
      #156c8a 81.2%,
      #156c8a 90.2%,
      #156c8a 100%
    );
}

#why {
  background: linear-gradient(180deg, #156c8a, #1a5975);
  padding-top: clamp(44px, 6vh, 80px);
  /* 17px shallower than the other bands: this one has less lead-in above its heading. */
  scroll-margin-top: calc(var(--nav-h) - 33px);
}

#library {
  background: linear-gradient(180deg, #1a5975, #154a66);
}

#bioisosteres {
  background: linear-gradient(180deg, #154a66, #113f5a);
}

#study {
  background: linear-gradient(180deg, #113f5a, #0d314a);
}

#privacy {
  background: linear-gradient(180deg, #0d314a, #092135);
}

#questions {
  background: linear-gradient(180deg, #092135, var(--w-abyss));
}

/* Marine snow is a fixed canvas at z-index 3, in front of the section backgrounds. */
.snow {
  position: fixed;
  inset: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  opacity: 0.55;
  pointer-events: none;
}

/* Depth gauge ------------------------------------------------------- */

/* Mirrored on the right, an instrument at the page's edge. */
.gauge {
  position: fixed;
  right: 26px;
  top: 84px;
  bottom: 40px;
  /* The labels are nowrap, so the longest tick sets this; the rail uses each
     section's short name. */
  width: 178px;
  z-index: 30;
  pointer-events: none;
  color: var(--ink);
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

/* Only the 0 m tick inverts, while the hero's pale water is behind it (a white
   label there is 1.01:1). dive.js sets the class. */
.gauge--bright .gauge__tick[data-tick='0'] {
  color: #0a2c40;
}

.gauge--bright .gauge__tick[data-tick='0'] span {
  text-shadow:
    0 1px 2px rgba(255, 255, 255, 0.7),
    0 0 6px rgba(255, 255, 255, 0.8);
}

.gauge__line {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(233, 242, 249, 0.26);
}

.gauge__tick {
  position: absolute;
  right: 0;
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  gap: 8px;
  transform: translateY(-50%);
  transition:
    opacity 300ms ease,
    color 300ms ease;
  opacity: 0.72;
}

.gauge__tick::before {
  content: '';
  width: 10px;
  height: 1px;
  background: currentColor;
  flex: 0 0 auto;
  transition: width 300ms ease;
}

.gauge__tick span {
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  white-space: nowrap;
  /* No chip: a tight shadow plus a wide dark glow carries the marks over the
     pale surface without drawing a box. */
  text-shadow:
    0 1px 2px rgba(2, 14, 26, 0.85),
    0 0 4px rgba(2, 14, 26, 0.8),
    0 0 11px rgba(2, 14, 26, 0.65);
  transition: text-shadow 300ms ease;
}

.gauge__tick--passed {
  opacity: 1;
}

.gauge__tick--passed::before {
  width: 18px;
}

/* The live readout rides the rail. */
.gauge__now {
  position: absolute;
  right: -4px;
  display: flex;
  align-items: baseline;
  gap: 3px;
  transform: translateY(-50%);
  padding: 2px 10px 2px 9px;
  border-right: 2px solid var(--sky);
  background: rgba(3, 16, 30, 0.62);
  backdrop-filter: blur(4px);
  border-radius: 4px 0 0 4px;
  font-size: 16px;
  font-weight: 500;
}

.gauge__now small {
  font-size: 11px;
  opacity: 0.7;
}

@media (max-width: 1180px) {
  .gauge {
    right: 14px;
    width: 92px;
  }
  .gauge__tick span {
    display: none;
  }
}

@media (max-width: 820px) {
  .gauge {
    top: auto;
    bottom: 14px;
    right: 14px;
    width: auto;
    height: auto;
  }
  .gauge__line,
  .gauge__tick {
    display: none;
  }
  .gauge__now {
    position: static;
    transform: none;
    background: rgba(7, 20, 30, 0.55);
    backdrop-filter: blur(6px);
    border-radius: 999px;
    border-right: 0;
    padding: 4px 12px;
  }
}

/* Section shell ----------------------------------------------------- */

.stratum {
  position: relative;
  z-index: 2;
  padding: clamp(56px, 8vh, 96px) var(--gut);
  /* Where an anchor jump stops, and so what the readout says on landing. The
     rail samples --nav-h + 6px down; at about 0.29 m/px, 50px under --nav-h
     lands about 15 m past the band's own number. 10px is about 3 m. */
  scroll-margin-top: calc(var(--nav-h) - 50px);
}

.stratum__inner {
  max-width: 1440px;
  margin: 0 auto;
}

/* Room for the rail wherever it is drawn as a rail. */
@media (min-width: 1181px) {
  .stratum,
  .foot {
    padding-right: 236px;
  }
}

@media (min-width: 821px) and (max-width: 1180px) {
  .stratum,
  .foot {
    padding-right: 118px;
  }
}

/* INVARIANT: the accents paint furniture, not words. On these bands they reach
   only 1.5:1 to 3.1:1, so labels run in --ink-dim and the accent marks the dot.
   A colored label takes a measured --*-text tint instead (see --plum-text). */
.stratum__label {
  color: var(--accent-text, var(--ink-dim));
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
}

.stratum__label::before {
  content: '';
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent, var(--sky));
  box-shadow: 0 0 14px 1px var(--accent, var(--sky));
  flex: 0 0 auto;
}

.stratum h2 {
  font-size: clamp(27px, 4.2vw, 42px);
  max-width: 22ch;
}

.stratum__lede {
  margin-top: 18px;
  max-width: var(--measure);
  color: var(--ink-dim);
  font-size: var(--t-lede);
}

/* Surface: the hero ------------------------------------------------- */

.surface {
  position: relative;
  overflow: hidden;
  /* The hero has nothing above it to arrive into, so its anchor offset is just
     the sticky nav's height. */
  scroll-margin-top: calc(var(--nav-h) + 20px);
  /* The first screen is the hero alone, centered, so the next band's marker
     never shows before the descent starts. */
  min-height: calc(100vh - var(--nav-h));
  min-height: calc(100svh - var(--nav-h));
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: clamp(40px, 6vh, 76px);
  padding-bottom: clamp(28px, 3.4vh, 44px);
}

/* Sunlight through the surface: six shafts, each with its own width, angle,
 * period and phase set inline, so they never move in step.
 *
 * INVARIANT: every light effect in the hero reaches zero before the section's
 * bottom edge. .surface clips its children, and an effect cut off mid-value is a
 * hard seam. The mask guarantees the fade; do not swap it for overflow: hidden. */
.rays {
  position: absolute;
  z-index: 2;
  inset: -18% -10% auto -10%;
  height: 125%;
  pointer-events: none;
  /* Fades in once the water is dark enough to show a shaft (~14% down) and out
   * by 94%. This box is 125% of the section: section fraction =
   * (mask% x 1.25) - 0.18, so recompute both if either moves. */
  -webkit-mask-image: linear-gradient(180deg, transparent 14%, #000 26%, #000 62%, transparent 90%);
  mask-image: linear-gradient(180deg, transparent 14%, #000 26%, #000 62%, transparent 90%);
}

.rays i {
  position: absolute;
  top: -24%;
  left: var(--x);
  width: var(--w);
  height: 150%;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.68), rgba(255, 255, 255, 0));
  /* Past about 16px a shaft stops having an edge and reads as a wash. */
  filter: blur(15px);
  transform: rotate(var(--rot));
  transform-origin: top center;
  animation: sway var(--dur) ease-in-out infinite;
  animation-delay: var(--delay);
}

@keyframes sway {
  0%,
  100% {
    transform: rotate(var(--rot)) translateX(0) scaleX(1);
    opacity: calc(var(--op) * 0.4);
  }
  30% {
    transform: rotate(calc(var(--rot) - 4.5deg)) translateX(46px) scaleX(1.48);
    opacity: var(--op);
  }
  62% {
    transform: rotate(calc(var(--rot) + 2.5deg)) translateX(-24px) scaleX(0.72);
    opacity: calc(var(--op) * 0.62);
  }
}

/* Surface shimmer: two banded washes at different angles and periods,
 * drifting opposite ways, so their interference shimmers. Each carries its
 * own blur, hence pseudo-elements. Masked like the shafts, for the same reason.
 */
.caustics {
  position: absolute;
  z-index: 1;
  inset: 0 -40% auto -40%;
  height: 62%;
  pointer-events: none;
  /* Starts below the pale band. This box is 62% of the section, so 23% here is
     14% of the section. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 23%, #000 55%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 23%, #000 55%, transparent 100%);
}

.caustics::before,
.caustics::after {
  content: '';
  position: absolute;
  inset: 0;
}

.caustics::before {
  background: repeating-linear-gradient(
    104deg,
    rgba(255, 255, 255, 0) 0px,
    rgba(255, 255, 255, 0.16) 58px,
    rgba(255, 255, 255, 0) 148px,
    rgba(255, 255, 255, 0.09) 226px,
    rgba(255, 255, 255, 0) 330px
  );
  filter: blur(15px);
  opacity: 0.8;
  animation: drift 19s linear infinite;
}

.caustics::after {
  background: repeating-linear-gradient(
    93deg,
    rgba(255, 255, 255, 0) 0px,
    rgba(255, 255, 255, 0.11) 34px,
    rgba(255, 255, 255, 0) 96px,
    rgba(255, 255, 255, 0.07) 150px,
    rgba(255, 255, 255, 0) 244px
  );
  filter: blur(9px);
  opacity: 0.6;
  animation: drift-back 26s linear infinite;
}

@keyframes drift {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-330px);
  }
}

@keyframes drift-back {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(244px);
  }
}

/* The scrim under the copy fades out vertically too: an inset: 0 overlay would
   end in a hard line at the next band. */
.scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    102deg,
    rgba(3, 18, 34, 0.5) 0%,
    rgba(3, 18, 34, 0.32) 34%,
    rgba(3, 18, 34, 0.1) 58%,
    rgba(3, 18, 34, 0) 72%
  );
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 50%, transparent 86%);
  mask-image: linear-gradient(180deg, #000 0%, #000 50%, transparent 86%);
}

/* No z-index of its own: a stacking context here would trap the mascot and the
   shafts together. width: 100% because auto margins on a flex item drop it
   out of stretch. */
.hero {
  position: relative;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
}

/* The manta swims off the right edge. The box is wider than the column and
 * pushed past .hero's right edge, so .surface's clip takes the rest, and rises
 * by --rise to get the height that its width needs. It cannot hang below the
 * panel: the band's bottom is the panel's. */
.hero__manta {
  /* Sized off the shorter axis: the art is 1.2x wider than tall, so height alone
     hides the wing behind the card on a narrow, tall window. The terms cross at
     about 1440x900. */
  --band: clamp(340px, min(70.6vh, 58vw - 202px), 843px);
  --drop: clamp(253px, 37vh, 481px);
  position: absolute;
  z-index: 3;
  /* Pushed past the right edge by less as the window narrows, keeping clear of
     the gauge; the left wing takes the squeeze. */
  right: calc(-20.8vw + 144px);
  top: auto;
  bottom: calc(-1 * var(--drop));
  height: var(--band);
  display: flex;
  align-items: stretch;
  pointer-events: none;
  will-change: transform;
}

/* The <svg> is empty until manta.js mounts, so aspect-ratio supplies its box,
   which also keeps dive.js's exit measurement right while it is torn down.
   INVARIANT: the ratio is the viewBox manta.js writes, -4 -5 236 196.
   overflow: visible because the flap carries the wingtips outside it. */
.hero__manta .manta {
  display: block;
  height: 100%;
  width: auto;
  aspect-ratio: 236 / 196;
  overflow: visible;
}

/* The copy's ground: translucent, so the blurred light still moves behind it,
   with a wide soft shadow so it never reads as a box dropped on the scene. */
.hero__panel {
  position: relative;
  z-index: 4;
  max-width: min(640px, 50%);
  padding: clamp(24px, 2.8vw, 36px);
  text-align: center;
}

/* The frosted pane, for the hero and the 404 page; layout belongs to the wearer. */
.glass {
  border-radius: 22px;
  /* Indigo whatever the water behind: saturate(0.5) strips the backdrop's hue,
     and the tint is solved (compositing is linear) to land on #151E38, the
     mascot's body. Lower the tint's alpha and saturate must come down too. */
  background: rgba(7, 15, 45, 0.74);
  backdrop-filter: blur(10px) saturate(0.34);
  border: 1px solid rgba(152, 193, 217, 0.13);
  /* INVARIANT: this shadow's reach (spread + blur = 40px) stays under
     .surface's smallest padding, or overflow clips it into a hard line. */
  box-shadow:
    0 0 36px 4px rgba(4, 19, 34, 0.32),
    0 20px 44px -34px rgba(0, 0, 0, 0.7);
}

/* The wordmark, in bold IBM Plex Sans to match the lockup; the tagline keeps
   Space Grotesk so the two read apart. */
.hero h1 {
  font-family: var(--body);
  font-weight: 700;
  font-size: clamp(36px, 4.8vw, 56px);
  letter-spacing: -0.025em;
  margin-top: 0;
}

.hero__tagline {
  margin-top: 10px;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(18px, 2.1vw, 23px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--sky);
}

/* The plain "what is this" sentence, above the fold but outside the panel,
   which carries brand and call to action only. */
.hero__foot {
  position: relative;
  z-index: 4;
  width: 100%;
  max-width: 1440px;
  margin: 58px auto 0;
}

.hero__foot .stratum__label {
  margin-bottom: 12px;
}

.hero__note {
  /* Held clear of the tail's arc as the mascot starts to swim. */
  max-width: 41ch;
  font-size: var(--t-lede);
  color: rgba(233, 242, 249, 0.92);
}

/* Stacked layout. .hero becomes display: contents, so the panel and mascot are
 * flex items of the section and space-between spreads the slack around the
 * mascot: panel, mascot, sentence, cue. On short phones the mascot is what
 * shrinks, since it is the one element that costs no information.
 */
@media (max-width: 980px) {
  .surface {
    justify-content: space-between;
    padding-top: clamp(14px, 2.2vh, 30px);
    padding-bottom: clamp(14px, 2vh, 28px);
  }
  .hero {
    display: contents;
  }
  .hero__panel {
    max-width: 100%;
  }
  .hero__manta {
    position: static;
    /* Height comes from the artwork here, not the desktop's fixed band. */
    height: auto;
    width: 58%;
    margin: 0 0 0 auto;
    transform: none !important;
  }
  .hero__manta .manta {
    width: 100%;
    height: auto;
  }
  .hero__foot {
    margin-top: 0;
  }
  .cue {
    margin-top: 14px;
  }
}

/* The mascot's size on a phone is the slack the copy leaves, which narrow
   screens eat by wrapping the badges. Measured slack at 58%:
     430x932  218px      390x844  131px
     360x780   42px      375x667   19px
   hence two tiers. */
@media (max-width: 980px) and (min-width: 380px) and (min-height: 800px) {
  .hero__manta {
    width: 74%;
  }
}

@media (max-width: 980px) and (max-height: 700px) {
  .hero__manta {
    width: 40%;
  }
  .hero__panel {
    padding: 20px;
  }
  .hero h1 {
    font-size: 32px;
  }
}

.badges {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 28px;
}

/* The nav's call to action is the download badge at nav size: one design for
   two buttons that mean the same thing. */
.badge,
.nav__cta {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 11px 20px;
  border-radius: 12px;
  background: #eef5fa;
  color: #0d2438;
  text-decoration: none;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.2;
  transition: box-shadow 220ms ease;
}

.nav__cta {
  padding: 8px 16px;
  font-size: 15px;
  white-space: nowrap;
}

.badge small {
  display: block;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.06em;
  opacity: 0.68;
}

/* The glow lifting is the whole hover state; no movement. */
.badge--glow,
.nav__cta {
  background: var(--sky);
  box-shadow: 0 0 18px -6px rgba(152, 193, 217, 0.62);
}

.badge--glow:hover,
.badge--glow:focus-visible,
.nav__cta:hover,
.nav__cta:focus-visible {
  box-shadow:
    0 0 26px -5px rgba(152, 193, 217, 0.72),
    0 0 10px -3px rgba(214, 236, 248, 0.45);
}

/* The call to action centers in the slate; the headline and lede stay left. */
.hero__panel .badges {
  justify-content: center;
}

.hero__panel .hero__meta {
  text-align: center;
}

.hero__meta span {
  white-space: nowrap;
}

.hero__meta {
  margin-top: 20px;
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* The scroll cue closes the hero, so it is on screen at load. */
.cue {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  /* About midway between the sentence and the bottom of the first screen;
     .surface centers its column, so the gap below moves about twice this.
     Overridden in the stacked layout. */
  margin-top: clamp(56px, 11.5vh, 132px);
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
  text-decoration: none;
}

.cue:hover {
  color: #fff;
}

/* A shaft with a head: at this size a bare arrowhead reads as punctuation. */
.cue__arrow {
  width: 12px;
  height: 34px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.1;
  stroke-linecap: round;
  stroke-linejoin: round;
  animation: sink 2.4s ease-in-out infinite;
}

@keyframes sink {
  0%,
  100% {
    transform: translateY(-3px);
    opacity: 0.34;
  }
  50% {
    transform: translateY(4px);
    opacity: 0.95;
  }
}

/* Why: the four reasons --------------------------------------------- */

/* Four across, then two and two, then one: never 3:1, as auto-fit would deal
   them. Two columns from 1180px, where the rail's gutter is released. */
.reasons {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  margin-top: 36px;
  background: rgba(152, 193, 217, 0.18);
  border: 1px solid rgba(152, 193, 217, 0.18);
  border-radius: 14px;
  overflow: hidden;
}

@media (max-width: 1180px) {
  .reasons {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 700px) {
  .reasons {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Two inks per card: the icon takes the heading's, leaving the accent to mark
   the section. The heading is a weight below the section h2. */
.reason {
  background: rgba(9, 24, 39, 0.28);
  padding: 28px 22px 30px;
}

.reason h3 {
  font-size: var(--t-sub);
  font-weight: 500;
  text-align: center;
  margin-top: 12px;
}

.reason p {
  margin-top: 15px;
  font-size: var(--t-body);
  color: var(--ink-dim);
}

/* A thinner stroke than the markup's 1.6 at this size; CSS beats the attribute. */
.reason svg {
  width: 56px;
  height: 56px;
  display: block;
  margin: 0 auto;
  stroke-width: 1.2;
  color: var(--ink);
}

/* At phone width the icon returns to the heading's line. */
@media (max-width: 700px) {
  .reason {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 12px;
    align-items: center;
    padding: 17px 18px 19px;
  }

  .reason h3 {
    text-align: left;
    margin-top: 0;
  }

  .reason p {
    grid-column: 2;
    margin-top: 6px;
  }

  .reason svg {
    width: 24px;
    height: 24px;
    stroke-width: 1.6;
    margin: 0;
  }
}

/* Specimens: three shelves ------------------------------------------ */

.shelf {
  display: grid;
  /* Always three across: wrapping to two left one library alone on its row. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 3.4vw, 44px);
  margin-top: 40px;
}

/* Built by dive.js; hidden on the desktop grid. The page's blue, not the three
   accents, which in a row read as a status light. */
.shelf-nav {
  display: none;
}

.shelf-nav__dot {
  width: 9px;
  height: 9px;
  padding: 0;
  border: 1px solid rgba(152, 193, 217, 0.55);
  border-radius: 50%;
  background: none;
  cursor: pointer;
  transition:
    background-color 200ms ease,
    transform 200ms ease;
}

.shelf-nav__dot[aria-current='true'] {
  border-color: var(--sky);
  background: var(--sky);
  transform: scale(1.25);
}

/* One shelf at a time on a phone, snapped sideways, with the next card's edge
   showing. The strip scrolls, so it is a tab stop and arrow keys move it. */
@media (max-width: 700px) {
  .shelf {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 84%;
    gap: 12px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    /* Bleeds to both edges, with the gutter back as padding, so the next card peeks. */
    margin-inline: calc(var(--gut) * -1);
    padding-inline: var(--gut);
    scroll-padding-inline: var(--gut);
    scrollbar-width: none;
  }

  .shelf::-webkit-scrollbar {
    display: none;
  }

  /* An accent hairline, so the peeking card reads as a card, not clipped text. */
  .shelf__item {
    scroll-snap-align: center;
    scroll-snap-stop: always;
    padding: 20px 18px 22px;
    border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
    border-radius: 16px;
    background: rgba(9, 24, 39, 0.3);
  }

  .shelf__item .phone {
    max-width: 100%;
  }

  /* Above the cards, where it reads as a control. */
  .shelf-nav {
    display: flex;
    justify-content: center;
    gap: 9px;
    margin-top: 28px;
  }

  .shelf {
    margin-top: 14px;
  }
}

/* Badge and screenshot centered; the description keeps its left edge. Grid
   items stretch to the tallest in their row, and the phone takes up the slack
   above it, so however many lines a description wraps to, the three phones
   share one top and one bottom edge. */
.shelf__item {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* A rule, not a box: a solid accent was the heaviest object on the band. The
   rule keeps the taxonomy hex; the lines take its lit tint (see --ochre-lit). */
.shelf__badge {
  align-self: stretch;
  padding: 0 0 10px;
  border-bottom: 2px solid var(--accent);
  color: var(--accent-lit);
  text-align: center;
}

.shelf__item h3 {
  font-family: var(--mono);
  font-weight: 500;
  /* Off the type scale by 1.5px, the size at which the number and its label balance. */
  font-size: 15px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: inherit;
}

.shelf__count {
  margin-top: 2px;
  font-family: var(--mono);
  font-size: 26px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.03em;
  color: inherit;
  font-variant-numeric: tabular-nums;
}

.shelf__line {
  width: 100%;
  margin: 12px 0 20px;
  font-size: var(--t-body);
  color: var(--ink-dim);
}

.shelf__item .phone {
  margin-top: auto;
}

/* App captures (a 411 × 683 px phone viewport, nav at the top) in a frame. */
.phone {
  position: relative;
  width: 100%;
  max-width: 232px;
  border-radius: 24px;
  padding: 7px;
  background: linear-gradient(180deg, #22405c, #0e1c2b);
  box-shadow:
    0 22px 54px -24px rgba(0, 0, 0, 0.78),
    0 0 52px -18px var(--accent);
}

.phone__screen {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 18px;
  background: #eef4fa;
}

/* The swap, performed ----------------------------------------------- */

/* Structure and reasoning side by side; stacked below 820px. */
.plate {
  margin-top: 38px;
  border: 1px solid rgba(152, 193, 217, 0.24);
  border-radius: 18px;
  background: rgba(152, 193, 217, 0.05);
  padding: 30px 30px 24px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: clamp(26px, 4vw, 54px);
}

/* INVARIANT: the ratio matches the viewBox on #sym-acid and #sym-tetrazole in
   index.html, which .devtools/art/render-structures.ts prints. The pair share
   one box so the benzene lands on the same pixels as they crossfade. */
.plate__struct {
  flex: 0 0 auto;
  position: relative;
  width: clamp(190px, 32vw, 330px);
  aspect-ratio: 197 / 104;
  color: var(--ink);
}

.plate__struct svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.plate__cap {
  max-width: 52ch;
  font-size: var(--t-body);
  color: var(--ink-dim);
}

@media (max-width: 820px) {
  .plate {
    flex-direction: column;
    align-items: center;
    padding: 30px 26px 26px;
    gap: 22px;
  }

  .plate__cap {
    text-align: center;
  }
}

/* The swap heads the caption; the notes are one claim per line, left-aligned. */
.plate__swap {
  font-size: var(--t-sub);
  color: var(--ink);
  font-weight: 600;
}

.plate__notes {
  display: grid;
  gap: 6px;
  margin: 14px 0 0;
  padding-left: 21px;
  text-align: left;
}

.plate__notes li::marker {
  color: var(--accent);
}

/* A 7 s crossfade with a long hold on each structure. */
@keyframes hold-a {
  0%,
  44% {
    opacity: 1;
  }
  52%,
  94% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

@keyframes hold-b {
  0%,
  44% {
    opacity: 0;
  }
  52%,
  94% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

.swap-a {
  animation: hold-a 7s ease-in-out infinite;
}

.swap-b {
  opacity: 0;
  animation: hold-b 7s ease-in-out infinite;
}

/* The daily dive: four steps ---------------------------------------- */

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(196px, 1fr));
  gap: 24px;
  margin-top: 38px;
  counter-reset: step;
}

.step {
  counter-increment: step;
  border-top: 1px solid rgba(152, 193, 217, 0.3);
  padding-top: 15px;
}

/* The step number is a chip on the heading's line, in mono like the shelves.
   The heading takes --plum-text: the accent is 2.6:1 on this band. */
.step h3 {
  display: flex;
  align-items: center;
  gap: 11px;
  font-family: var(--mono);
  font-weight: 500;
  font-size: var(--t-sub);
  letter-spacing: 0.01em;
  color: var(--accent-text);
}

.step h3::before {
  content: counter(step, decimal-leading-zero);
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--plum);
  color: #fff;
  font-size: var(--t-micro);
  letter-spacing: 0.02em;
}

.step p {
  margin-top: 7px;
  font-size: var(--t-body);
  color: var(--ink-dim);
}

/* The floor --------------------------------------------------------- */

/* The claim on the left, what backs it on the right. */
.seal {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: 30px clamp(30px, 4vw, 56px);
  align-items: start;
}

.nots {
  display: grid;
  gap: 15px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nots li {
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--mono);
  /* The section's argument, so a step above label size. */
  font-size: var(--t-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* The list is about the app; the note under it covers the website. */
.nots__label {
  margin: 7px 0 15px;
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-text, var(--ink-dim));
}

.nots__note {
  margin: 22px 0 0;
  max-width: 36ch;
  font-size: var(--t-micro);
  line-height: 1.6;
  color: var(--ink-dim);
}

.nots__note a {
  white-space: nowrap;
}

/* The depth markers' lit bead, marking each item. */
.nots li::before {
  content: '';
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent, var(--sky));
  box-shadow: 0 0 14px 1px var(--accent, var(--sky));
}

@media (max-width: 820px) {
  .seal {
    grid-template-columns: minmax(0, 1fr);
    gap: 26px;
  }
}

/* Questions --------------------------------------------------------- */

/* A plain list, not an accordion: four answers are not worth hiding. */
/* Read down, not across, so the page order is the markup order.
   INVARIANT: two rows, four questions; a fifth needs grid-template-rows. */
.qs {
  display: grid;
  gap: 26px 44px;
}

@media (min-width: 1000px) {
  .qs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(2, auto);
    grid-auto-flow: column;
  }
}

.q h3 {
  font-size: var(--t-sub);
  font-family: var(--body);
  font-weight: 600;
  letter-spacing: 0;
}

.q p {
  margin-top: 7px;
  font-size: var(--t-body);
  color: var(--ink-dim);
}

/* Foot -------------------------------------------------------------- */

.foot {
  position: relative;
  z-index: 2;
  background: var(--w-abyss);
  border-top: 1px solid rgba(152, 193, 217, 0.14);
  padding: 48px var(--gut) 44px;
  color: var(--ink-dim);
  font-size: var(--t-body);
}

/* Stacked on the left; the right is the depth rail's gutter. */
/* The wordmark, with the mascot's outline at cap height beside it. */
.foot__mark {
  display: flex;
  align-items: center;
  gap: 11px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 21px;
  letter-spacing: -0.02em;
  color: var(--ink);
}

/* assets/manta-mark.svg is drawn in --ink, so mark and word read as one. */
.foot__manta {
  flex: 0 0 auto;
  width: 38px;
  height: auto;
}

/* Name, then its fine print as a caption, then the link set apart. */
.foot__inner {
  max-width: 1440px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.foot__mark {
  margin-bottom: 10px;
}

.foot__fine {
  margin-bottom: 26px;
}

.foot nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
}

/* The bar between the links is decoration; once they wrap it would dangle. */
.foot__sep {
  opacity: 0.45;
}

@media (max-width: 480px) {
  .foot__sep {
    display: none;
  }
}

.foot a {
  text-decoration: none;
  border-bottom: 1px solid rgba(152, 193, 217, 0.3);
  padding-bottom: 1px;
}

.foot a:hover {
  color: #fff;
}

/* Its own <br />s end its lines, so it takes no width cap. */
.foot__fine {
  font-size: 14.5px;
  opacity: 0.75;
}

/* A name that must not split at its hyphen when a narrow screen wraps it. */
.foot__nobreak {
  white-space: nowrap;
}

/* Enhancement layer ------------------------------------------------- */

/* Reveals are opt-in: .js goes on <html> in the head, so without JavaScript
   nothing is ever left at opacity 0. */
.js .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity 520ms ease,
    transform 520ms cubic-bezier(0.16, 1, 0.3, 1);
}

.js .reveal.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal,
  .js .reveal.is-in {
    opacity: 1;
    transform: none;
    transition: none;
  }
  /* Most motion keeps running, as the water itself: the shafts, shimmer, snow,
     the mascot's swim, and the bioisostere crossfade (a still frame loses the
     substitution it explains). What stops is the bouncing cue arrow; reveals
     and smooth scrolling are off above. */
  .cue__arrow {
    animation: none;
  }
}
