/* The site is a caisson being sunk to bedrock, in which Caisson's hexagonal box carries the
   visitor below the surface of each project to the measured work underneath, as the visitor
   scrolls it down and holds to pump the air that keeps the river out. */

/* -- tokens (brief/world.md) ---------------------------------------------------------------- */

@font-face {
  font-family: 'Archivo';
  src: url('/assets/fonts/archivo.woff2') format('woff2');
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
}
/* The wordmark's cut, fixed on both axes, so the scene can draw the submerged half of the
   letters with the same glyphs as the HTML above the line. */
@font-face {
  font-family: 'Archivo Mark';
  src: url('/assets/fonts/archivo.woff2') format('woff2');
  font-weight: 640;
  font-stretch: 68%;
  font-display: block;
}

:root {
  --air: oklch(0.968 0.007 240);
  --air-2: oklch(0.925 0.011 235);
  --haze: oklch(0.860 0.018 230);
  --ink: oklch(0.235 0.028 168);
  --ink-2: oklch(0.430 0.022 175);
  --river: oklch(0.355 0.045 165);
  --river-2: oklch(0.290 0.040 165);
  --river-ink: oklch(0.955 0.012 160);
  --river-ink-2: oklch(0.800 0.030 160);
  --lamp: oklch(0.215 0.018 75);
  --lamp-ink: oklch(0.940 0.020 95);
  --lamp-ink-2: oklch(0.780 0.045 88);
  --signal: oklch(0.912 0.156 95.4);

  /* Motion: closed forms in assets/js/lib/motion.js, sampled to linear() stops. */
  --ease-settle: linear(0, 0.163 3.8%, 0.307 7.5%, 0.431 11.3%, 0.536 15%, 0.63 19%, 0.715 23.5%, 0.788 28.5%, 0.849 34%, 0.899 40.5%, 0.94 48.5%, 0.971 59%, 0.991 74.5%, 1);
  --ease-heave: linear(0, 0.01 1.8%, 0.041 3.8%, 0.099 6.3%, 0.224 10.5%, 0.437 17.5%, 0.556 22%, 0.657 26.5%, 0.739 31%, 0.809 36%, 0.867 41.5%, 0.915 48%, 0.952 56%, 0.979 66.8%, 0.995 83.8%, 1);
  --ease-release: linear(0, 0.01 8%, 0.041 16.5%, 0.094 25.5%, 0.17 35%, 0.275 45.5%, 0.415 57.5%, 0.616 72.8%, 1);
  --t-micro: 160ms;
  --t-ui: 480ms;
  --t-reveal: 960ms;
  --t-scene: 1600ms;
  --stagger: 110ms;

  /* Type: a 4:3 scale from an 18 px body, fluid between phone and wide screens. */
  --f-small: clamp(0.8125rem, 0.79rem + 0.1vw, 0.875rem);
  --f-body: clamp(1rem, 0.94rem + 0.25vw, 1.15rem);
  --f-lead: clamp(1.25rem, 1.65vw, 1.6rem);
  --f-figure: clamp(2.25rem, 4.2vw, 4.75rem);
  --f-title: clamp(3rem, 7.6vw, 8.5rem);
  --f-word: clamp(6.5rem, 19.5vw, 20rem);

  --margin: clamp(1.25rem, 4.4vw, 4.5rem);
  --gap: clamp(1rem, 1.7vw, 1.5rem);
  --measure: 34rem;

  /* One radius scale, nested concentrically. */
  --r-1: 3px;
  --r-2: 6px;
  --r-3: 10px;
  --r-4: 16px;

  color-scheme: light;
}

/* Each tone sets the ground and the inks; components read only these. */
:root,
[data-tone='air'] {
  --ground: var(--air);
  --ground-2: var(--air-2);
  --text: var(--ink);
  --text-2: var(--ink-2);
  --rule: var(--haze);
  --focus-inner: var(--ink);
}
[data-tone='river'] {
  --ground: var(--river);
  --ground-2: var(--river-2);
  --text: var(--river-ink);
  --text-2: var(--river-ink-2);
  --rule: oklch(0.5 0.04 165);
  --focus-inner: transparent;
  color-scheme: dark;
}
[data-tone='lamp'] {
  --ground: var(--lamp);
  --ground-2: oklch(0.255 0.02 75);
  --text: var(--lamp-ink);
  --text-2: var(--lamp-ink-2);
  --rule: oklch(0.42 0.03 80);
  --focus-inner: transparent;
  color-scheme: dark;
}

/* -- base ------------------------------------------------------------------------------------ */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scrollbar-color: var(--text-2) var(--ground);
  background: var(--ground);
  transition: background-color var(--t-ui) var(--ease-settle);
}

body {
  margin: 0;
  min-height: 100svh;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: var(--f-body);
  font-weight: 400;
  font-stretch: 100%;
  line-height: 1.55;
  color: var(--text);
  font-kerning: normal;
  font-variant-numeric: lining-nums;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection {
  background: var(--signal);
  color: var(--ink);
}

:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 3px;
  box-shadow: 0 0 0 2px var(--focus-inner);
  border-radius: var(--r-1);
}

a {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in oklch, currentColor 45%, transparent);
  transition: text-underline-offset var(--t-micro) var(--ease-settle), text-decoration-color var(--t-micro) var(--ease-settle);
}
/* Water rising up a gauge: the underline climbs from under the letters into them. */
@media (hover: hover) {
  a:hover {
    text-underline-offset: -0.12em;
    text-decoration-thickness: 0.32em;
    text-decoration-color: color-mix(in oklch, currentColor 18%, transparent);
    text-decoration-skip-ink: none;
  }
}

h1,
h2,
h3,
p,
dl,
dd,
ol,
ul,
figure {
  margin: 0;
}
ol,
ul {
  padding: 0;
  list-style: none;
}
img,
svg,
canvas {
  display: block;
}
button {
  font: inherit;
  color: inherit;
}
p,
li,
dd {
  text-wrap: pretty;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip {
  position: fixed;
  z-index: 50;
  left: var(--margin);
  top: 0.75rem;
  padding: 0.6rem 1rem;
  background: var(--signal);
  color: var(--ink);
  border-radius: var(--r-2);
  transform: translateY(-160%);
  transition: transform var(--t-ui) var(--ease-settle);
}
.skip:focus-visible {
  transform: none;
}

/* -- type styles ----------------------------------------------------------------------------- */

.title,
.head__title,
.block__title {
  font-weight: 600;
  font-stretch: 74%;
  letter-spacing: -0.02em;
  line-height: 0.9;
  text-wrap: balance;
}
.title,
.head__title {
  font-size: var(--f-title);
}
/* Chapter titles are a name or two and stay on one line; the scene has room for them. */
.title {
  white-space: nowrap;
}
.lead {
  font-size: var(--f-lead);
  font-weight: 440;
  font-stretch: 96%;
  letter-spacing: -0.006em;
  line-height: 1.36;
  max-width: var(--measure);
}
.figures {
  display: grid;
  gap: 1.25rem 2.25rem;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  max-width: 40rem;
}
.figures dt {
  font-size: var(--f-figure);
  font-weight: 560;
  font-stretch: 80%;
  letter-spacing: -0.015em;
  line-height: 1;
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}
.figures dd {
  margin-top: 0.5rem;
  max-width: 17rem;
  color: var(--text-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  letter-spacing: 0.01em;
  line-height: 1.35;
}
.note,
.second {
  max-width: var(--measure);
}
.note {
  color: var(--text-2);
}
.link {
  font-weight: 520;
}

/* The primary action: a plate cut to the mark's 120 degree corners. */
.plate {
  --cut: 0.9em;
  display: inline-flex;
  align-items: center;
  min-height: 3.25rem;
  padding: 0.85rem 1.7rem;
  background: var(--signal);
  color: var(--ink);
  font-weight: 560;
  font-stretch: 92%;
  text-decoration: none;
  clip-path: polygon(var(--cut) 0, calc(100% - var(--cut)) 0, 100% 50%, calc(100% - var(--cut)) 100%, var(--cut) 100%, 0 50%);
  transition: padding var(--t-micro) var(--ease-settle);
}
.plate:focus-visible {
  clip-path: none;
  border-radius: var(--r-2);
}
@media (hover: hover) {
  .plate:hover {
    padding-inline: 2.1rem;
  }
}
/* Secondary plates are outlines in the tone's ink, cut the same way. */
.plate--quiet {
  background: transparent;
  color: var(--text);
  box-shadow: inset 0 0 0 1.5px currentColor;
  clip-path: none;
  border-radius: var(--r-2);
}

/* -- the scene and its fallbacks ------------------------------------------------------------- */

.scene,
.scene-fade {
  position: fixed;
  inset: 0 0 auto 0;
  width: 100%;
  height: 100lvh;
  z-index: 0;
  pointer-events: none;
}
.scene {
  opacity: 0;
}
.gl .scene {
  opacity: 1;
}
.scene-fade {
  z-index: 1;
  opacity: 0;
}
.scene-fade[data-on='true'] {
  opacity: 1;
}
.scene-fade[data-on='fading'] {
  opacity: 0;
  transition: opacity var(--t-scene) var(--ease-heave);
}

.poster {
  display: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -1;
}
.no-gl .poster,
html:not(.js) .poster {
  display: block;
}

/* -- navigation ------------------------------------------------------------------------------ */

.nav {
  position: fixed;
  z-index: 20;
  inset: 0 0 auto 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: max(1rem, env(safe-area-inset-top)) var(--margin) 0;
  pointer-events: none;
}
.nav > * {
  pointer-events: auto;
}
.nav__home {
  display: block;
  padding: 0.5rem;
  margin: -0.5rem;
  color: var(--text);
  transition: color var(--t-ui) var(--ease-settle);
}
.nav__mark {
  width: 1.5rem;
  height: auto;
  fill: currentColor;
}
.nav__end {
  display: flex;
  gap: 0.25rem;
  /* The last link's text, not its padding, stands on the margin, as the mark does on the left. */
  margin-right: -0.85rem;
}
.nav__menu,
.nav__email {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 0.85rem;
  border: 0;
  background: none;
  color: var(--text);
  font-size: var(--f-small);
  font-weight: 560;
  font-stretch: 90%;
  letter-spacing: 0.01em;
  text-decoration: none;
  cursor: pointer;
  border-radius: var(--r-2);
  transition: color var(--t-ui) var(--ease-settle), background-color var(--t-micro) var(--ease-settle);
}
@media (hover: hover) {
  .nav__menu:hover,
  .nav__email:hover {
    background: color-mix(in oklch, currentColor 9%, transparent);
  }
}

/* -- the menu: a section drawing with the projects at their depths --------------------------- */

.menu {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--air);
  color: var(--ink);
  overflow: auto;
  overscroll-behavior: contain;
  clip-path: inset(0 0 100% 0);
  transition: clip-path var(--t-ui) var(--ease-release);
}
.menu[data-open='true'] {
  clip-path: inset(0);
  transition: clip-path var(--t-ui) var(--ease-settle);
}
.menu-open body {
  overflow: hidden;
}
.menu__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: var(--gap) calc(var(--gap) * 3);
  min-height: 100%;
  padding: calc(env(safe-area-inset-top) + 5rem) var(--margin) 3rem;
}
.menu__section {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(11rem, 0.9fr);
  align-self: start;
}
.menu__drawing {
  width: 100%;
  height: auto;
  max-height: calc(100svh - 9rem);
  stroke: var(--ink);
  fill: none;
  stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
}
.menu__drawing .d-water {
  stroke-width: 1.6;
}
.menu__drawing .d-stratum {
  stroke: var(--ink-2);
  stroke-dasharray: 2 4;
}
.menu__drawing .d-concrete {
  fill: var(--air-2);
}
.menu__drawing .d-fill {
  fill: var(--haze);
}
.menu__drawing .d-tick {
  stroke: var(--ink-2);
  stroke-width: 1;
}
.menu__projects {
  position: relative;
}
.menu__projects li {
  position: absolute;
  left: 0.75rem;
  right: 0;
  top: calc(var(--y) / 470 * 100%);
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
}
.menu__projects a {
  font-size: clamp(1.5rem, 2.6vw, 2.4rem);
  font-weight: 600;
  font-stretch: 78%;
  letter-spacing: -0.015em;
  line-height: 1;
  text-decoration: none;
}
.menu__where {
  color: var(--ink-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
}
.menu__more {
  align-self: end;
  display: grid;
  gap: 2rem;
}
.menu__more ul {
  display: grid;
  gap: 0.35rem;
}
.menu__more a {
  font-size: var(--f-lead);
  font-weight: 500;
  font-stretch: 90%;
  text-decoration: none;
}
.menu__projects a[aria-current],
.menu__more a[aria-current] {
  text-decoration: underline;
  text-decoration-thickness: 0.08em;
}
.menu__close {
  position: absolute;
  top: max(1rem, env(safe-area-inset-top));
  right: var(--margin);
  min-height: 2.75rem;
  padding: 0 0.85rem;
  border: 0;
  background: none;
  font-size: var(--f-small);
  font-weight: 560;
  font-stretch: 90%;
  cursor: pointer;
  border-radius: var(--r-2);
}

/* -- the staff gauge: an enamel board, legible on every tone --------------------------------- */

.gauge {
  position: fixed;
  z-index: 15;
  right: var(--margin);
  top: 5.5rem;
  bottom: 7.5rem;
  width: max-content;
  display: grid;
  grid-template-rows: 1fr auto auto;
  justify-items: end;
  gap: 0.5rem;
  color: var(--ink);
  perspective: 600px;
  /* The board turns over about its own middle, not the middle of the wider readout. */
  perspective-origin: calc(100% - 0.95rem) 50%;
}
.gauge__board {
  position: relative;
  width: 1.9rem;
  height: 100%;
  background: var(--air);
  border-radius: var(--r-2);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--ink) 22%, transparent);
  transform-style: preserve-3d;
  transition: transform var(--t-reveal) var(--ease-settle);
}
.gauge[data-face='print'] .gauge__board {
  transform: rotateX(180deg);
}
.gauge__face {
  position: absolute;
  inset: 0.45rem 0;
  backface-visibility: hidden;
  /* The graduations: a long tick every unit, short ones between, the E-blocks of a river gauge. */
  background:
    repeating-linear-gradient(to bottom, var(--ink) 0 2px, transparent 2px calc(100% / 30)) right 0 top 0 / 60% 100% no-repeat,
    repeating-linear-gradient(to bottom, transparent 0 calc(100% / 60), var(--ink) calc(100% / 60) calc(100% / 60 + 1px), transparent calc(100% / 60 + 1px) calc(100% / 30)) right 0 top 0 / 35% 100% no-repeat;
}
.gauge__face--print {
  transform: rotateX(180deg);
  background:
    repeating-linear-gradient(to bottom, var(--ink) 0 2px, transparent 2px calc(100% / 23.94)) right 0 top 0 / 60% 100% no-repeat;
}
.gauge__face span {
  position: absolute;
  left: 0.2rem;
  top: calc(var(--at) * 100%);
  transform: translateY(-50%);
  font-size: 0.6875rem;
  font-weight: 600;
  font-stretch: 80%;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  background: var(--air);
  padding: 1px 0;
}
.gauge__mark {
  position: absolute;
  left: -0.7rem;
  right: -0.2rem;
  top: 0.45rem;
  height: 0;
  will-change: transform;
}
.gauge__mark::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -3px;
  height: 6px;
  background: var(--signal);
  box-shadow: 0 0 0 1px var(--ink);
  clip-path: polygon(0 0, calc(100% - 3px) 0, 100% 50%, calc(100% - 3px) 100%, 0 100%);
}
.gauge__read {
  display: grid;
  justify-items: end;
  padding: 0.35rem 0.5rem;
  background: var(--air);
  border-radius: var(--r-2);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--ink) 22%, transparent);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
  white-space: nowrap;
}
.gauge__second {
  color: var(--ink-2);
}
.still {
  min-height: 2.75rem;
  padding: 0 0.7rem;
  border: 0;
  border-radius: var(--r-2);
  background: var(--air);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--ink) 22%, transparent);
  color: var(--ink);
  font-size: var(--f-small);
  font-weight: 560;
  font-stretch: 88%;
  white-space: nowrap;
  cursor: pointer;
}
.still[aria-pressed='true'] {
  box-shadow: inset 0 0 0 2px var(--ink);
}

/* -- the pump -------------------------------------------------------------------------------- */

.pump {
  position: fixed;
  z-index: 15;
  left: var(--margin);
  bottom: max(1.5rem, env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 0.75rem;
  opacity: 0;
  translate: 0 0.75rem;
  transition: opacity var(--t-reveal) var(--ease-settle), translate var(--t-reveal) var(--ease-settle);
}
.ready .pump {
  opacity: 1;
  translate: none;
}
/* In the finale the pump's work is done and the copy scrolls where it stands, so it sinks away. */
.ready[data-finale='true'] .pump {
  opacity: 0;
  translate: 0 calc(100% + 1.5rem);
  visibility: hidden;
  transition: opacity calc(var(--t-reveal) * 0.6) var(--ease-release), translate calc(var(--t-reveal) * 0.6) var(--ease-release), visibility 0s var(--ease-release) calc(var(--t-reveal) * 0.6);
}
.pump__button {
  --cut: 1.05rem;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 3.5rem;
  padding: 0.4rem 1.5rem 0.4rem 1.1rem;
  border: 0;
  background: var(--signal);
  color: var(--ink);
  font-weight: 560;
  font-stretch: 92%;
  cursor: pointer;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  clip-path: polygon(var(--cut) 0, calc(100% - var(--cut)) 0, 100% 50%, calc(100% - var(--cut)) 100%, var(--cut) 100%, 0 50%);
  transition: padding var(--t-micro) var(--ease-settle);
}
.pump__button:focus-visible {
  clip-path: none;
  border-radius: var(--r-3);
}
.pump[data-state='pumping'] .pump__button {
  padding-inline: 1.25rem 1.75rem;
}
.pump[data-state='filled'] .pump__button {
  background: var(--air-2);
  color: var(--ink-2);
  cursor: default;
}
.pump__dial {
  width: 2.5rem;
  height: 2.5rem;
  fill: none;
  stroke: var(--ink);
  stroke-linecap: round;
}
.pump__arc {
  stroke-width: 1.6;
}
.pump__ticks {
  stroke-width: 1.4;
}
.pump__needle {
  stroke-width: 2.2;
  transform-box: view-box;
  transform-origin: 50% 50%;
  transform: rotate(-120deg);
}
.pump__hub {
  fill: var(--ink);
  stroke: none;
}
/* A card like the gauge's, since the pump sits over water in some chapters and ground in others. */
.pump__read {
  padding: 0.35rem 0.5rem;
  background: var(--air);
  border-radius: var(--r-2);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--ink) 22%, transparent);
  color: var(--ink);
  font-size: var(--f-small);
  font-weight: 560;
  font-stretch: 90%;
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
  white-space: nowrap;
}

/* The valve wheel that stands in for the pointer over the pressable scene. */
.valve {
  position: fixed;
  z-index: 30;
  left: -12px;
  top: -12px;
  width: 24px;
  height: 24px;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-micro) var(--ease-settle);
}
.valve[data-on='true'] {
  opacity: 1;
}
.valve svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}
.valve__halo {
  fill: none;
  stroke: var(--air);
  stroke-width: 4;
}
.valve__wheel {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.6;
}
.valve__hub {
  fill: var(--ink);
}
html:has(.valve[data-on='true']) body {
  cursor: none;
}

/* -- the loader: the plan poured as the files arrive ----------------------------------------- */

.loader {
  position: fixed;
  z-index: 2;
  inset: 0;
  pointer-events: none;
  display: none;
}
/* The loader covers the scene until the hand-off, so the scene dissolves in whole as the
   drawing goes, its walls on the drawing's walls. */
.js .loader {
  display: block;
  background: var(--air);
}
.loader__plan {
  position: absolute;
  height: 46svh;
  width: auto;
  aspect-ratio: 208 / 282;
  right: 17vw;
  top: 50%;
  translate: 0 -54%;
  overflow: visible;
}
.loader__outline {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
  fill-rule: evenodd;
}
.loader__pour {
  fill: oklch(0.66 0.008 80);
}
.loader__bytes {
  position: absolute;
  right: 17vw;
  top: calc(50% + 23svh - 46svh * 0.04);
  width: calc(46svh * 208 / 282);
  text-align: center;
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.loader[data-state='out'] {
  opacity: 0;
  transition: opacity var(--out, 480ms) var(--ease-release);
}
.loader[data-state='mark'] .loader__bytes {
  opacity: 0;
}

/* -- the story: chapters, tracks and sticky frames ------------------------------------------- */

.page--home main {
  position: relative;
  z-index: 3;
}
.chapter {
  position: relative;
  height: calc((var(--morph) + var(--hold)) * 1svh);
}
.track {
  position: absolute;
  inset: calc(var(--morph) * 1svh) 0 auto 0;
  height: calc((var(--hold) + 100) * 1svh);
}
/* Each frame carries its chapter's tone, so its words take the inks of the ground they were set
   on whether the scene or a poster is behind them. */
.frame {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
  color: var(--text);
  user-select: none;
  -webkit-user-select: none;
}
.frame .copy,
.frame .copy *,
.frame .tag,
.frame .word {
  user-select: text;
  -webkit-user-select: text;
}

.chapter--studio {
  height: auto;
  padding-top: calc(var(--morph) * 1svh);
}
.chapter--studio .track {
  position: relative;
  inset: auto;
  height: auto;
}
.frame--finale {
  /* How deep into the band between copy and index the waterline runs, and how many vh before the
     end of the pour the copy arrives (home.js reads both). */
  --line-in-band: 0.5;
  --arrive: 18;
  position: relative;
  height: auto;
  min-height: 100svh;
}

/* Reveals: lines rise out of their masks on the house curve; exits sink faster. */
.title {
  overflow: clip;
  overflow-clip-margin: 0.1em;
  padding-bottom: 0.06em;
}
.title__line {
  display: inline-block;
}
.js .frame .title__line {
  transform: translateY(125%);
  transition: transform calc(var(--t-reveal) * 0.6) var(--ease-release);
}
.js .frame .copy > :not(.title),
.js .frame .finale {
  opacity: 0;
  translate: 0 0.9rem;
  transition: opacity calc(var(--t-reveal) * 0.6) var(--ease-release), translate calc(var(--t-reveal) * 0.6) var(--ease-release);
}
.js .frame[data-in='true'] .title__line {
  transform: none;
  transition: transform var(--t-reveal) var(--ease-settle);
}
.js .frame[data-in='true'] .copy > :not(.title),
.js .frame[data-in='true'] .finale {
  opacity: 1;
  translate: none;
  transition: opacity var(--t-reveal) var(--ease-settle), translate var(--t-reveal) var(--ease-settle);
}
.js .frame[data-in='true'] .copy > :nth-child(2) { transition-delay: calc(var(--stagger) * 1); }
.js .frame[data-in='true'] .copy > :nth-child(3) { transition-delay: calc(var(--stagger) * 2); }
.js .frame[data-in='true'] .copy > :nth-child(4) { transition-delay: calc(var(--stagger) * 3); }
.js .frame[data-in='true'] .copy > :nth-child(5) { transition-delay: calc(var(--stagger) * 4); }
.js .frame[data-in='true'] .copy > :nth-child(6) { transition-delay: calc(var(--stagger) * 5); }
.js .frame[data-in='true'] .finale { transition-delay: calc(var(--stagger) * 6); }

.copy {
  position: absolute;
  left: var(--margin);
  width: min(38rem, 42vw);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.35rem;
  justify-items: start;
}
.copy .figures {
  margin-block: 0.4rem;
}

/* -- hero ------------------------------------------------------------------------------------ */

/* The waterline crosses the lens 58% of the way down (world.js, the hero pose), and the
   wordmark is set so the line cuts it through the middle of its lowercase letters. */
.word {
  position: absolute;
  left: calc(var(--margin) - 0.04em);
  top: calc(58lvh - 0.464em);
  margin: 0;
  font-size: var(--f-word);
  font-weight: 640;
  font-stretch: 68%;
  letter-spacing: -0.022em;
  line-height: 0.86;
  white-space: nowrap;
  color: var(--ink);
}
.word__base {
  display: inline-block;
  width: 0;
  height: 0;
}
.copy--hero {
  top: calc(58lvh + 0.42 * var(--f-word));
  color: var(--river-ink);
}
.hero__support {
  font-size: var(--f-lead);
  font-weight: 440;
  font-stretch: 96%;
  letter-spacing: -0.006em;
  line-height: 1.36;
  max-width: 26rem;
}
.hero__hint {
  position: absolute;
  left: 50%;
  bottom: max(1.75rem, env(safe-area-inset-bottom));
  translate: -50% 0;
  color: var(--river-ink-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  transition: opacity var(--t-ui) var(--ease-release);
}
.hero__hint[data-gone='true'] {
  opacity: 0;
}
.js .chapter--caisson .frame:not([data-in='true']) .copy--hero,
.js .chapter--caisson .frame:not([data-in='true']) .hero__hint {
  opacity: 0;
}
.chapter--caisson .copy--hero,
.chapter--caisson .hero__hint {
  transition: opacity var(--t-reveal) var(--ease-settle);
}

/* -- chapter compositions -------------------------------------------------------------------- */

.chapter--stargazer .copy {
  top: 15svh;
}
.chapter--liquox .copy {
  top: 14svh;
  color: var(--river-ink);
}
.chapter--venus .copy {
  top: 11svh;
  width: min(44rem, 50vw);
  gap: 1rem;
}
.chapter--pileon-sql .copy {
  top: 12svh;
  width: clamp(28rem, 30vw, 34rem);
  color: var(--river-ink);
}
.chapter--arachnid .copy {
  top: 13svh;
  color: var(--lamp-ink);
}
/* Where a chapter's copy crosses ground of every shade (water, sand, clay, a lamp), the scene is
   darkened behind it. */
.chapter--pileon-sql {
  --scrim: var(--river);
}
.chapter--arachnid {
  --scrim: var(--lamp);
}
.js .chapter--pileon-sql .frame::before,
.js .chapter--arachnid .frame::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: min(64vw, 58rem);
  background: linear-gradient(to right, color-mix(in oklch, var(--scrim) 84%, transparent) 0%, color-mix(in oklch, var(--scrim) 70%, transparent) 50%, transparent 100%);
  pointer-events: none;
  opacity: 0;
  transition: opacity calc(var(--t-reveal) * 0.6) var(--ease-release);
}
.js .chapter--pileon-sql .frame[data-in='true']::before,
.js .chapter--arachnid .frame[data-in='true']::before {
  opacity: 1;
  transition: opacity var(--t-reveal) var(--ease-settle);
}
.chapter--liquox .figures dd,
.chapter--pileon-sql .figures dd {
  color: var(--river-ink-2);
}
.chapter--arachnid .figures dd,
.chapter--arachnid .print-read {
  color: var(--lamp-ink-2);
}
.print-read {
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  font-variant-numeric: tabular-nums;
}

.copy--finale {
  position: relative;
  left: auto;
  /* The right padding keeps the copy and the index clear of the gauge column. */
  padding: 18svh calc(var(--margin) + 6rem) 0 var(--margin);
  width: auto;
  max-width: calc(var(--measure) + 2 * var(--margin));
}
.copy--finale .action {
  margin-top: 0.5rem;
}
.copy--finale .under {
  max-width: 28rem;
  color: var(--text-2);
}
.finale {
  position: relative;
  display: grid;
  gap: 2.5rem;
  /* Clear of the gauge column on the right; the pump has gone by the time the index is up. */
  padding: 18svh calc(var(--margin) + 6rem) calc(max(1.5rem, env(safe-area-inset-bottom)) + 3rem) var(--margin);
  color: var(--river-ink);
}
.finale__index ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.6rem;
  max-width: 46rem;
}
.finale__index a {
  font-size: var(--f-lead);
  font-weight: 500;
  font-stretch: 90%;
  text-decoration-color: color-mix(in oklch, currentColor 35%, transparent);
}

.credit a {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  text-decoration: none;
}
.credit__mark {
  width: 1rem;
  height: auto;
  fill: var(--signal);
}
.finale__end {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
}
/* The finale's own Still water, for phones, where the gauge leaves with the pump. */
.still--foot {
  display: none;
}

/* -- labels on the scene --------------------------------------------------------------------- */

.tags {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.tag {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  transition: opacity var(--t-ui) var(--ease-release);
}
.tag[data-on='true'] {
  opacity: 1;
  transition: opacity var(--t-reveal) var(--ease-settle);
}
/* Tags sit on water, ground and concrete alike, so the words are carded like the gauge's
   readout and the leader is ink with a pale edge. */
.tag::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: var(--len, 0);
  height: 1px;
  background: var(--ink);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--air) 65%, transparent);
  transform-origin: 0 50%;
  transform: translate3d(var(--ax), var(--ay), 0) rotate(var(--ang, 0));
}
.tag::after {
  content: '';
  position: absolute;
  left: -3.5px;
  top: -3.5px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 0 0 1.5px var(--air);
  transform: translate3d(var(--ax), var(--ay), 0);
}
.tag__words {
  position: absolute;
  left: 0;
  top: 0;
  padding: 0.2rem 0.45rem;
  border-radius: var(--r-2);
  background: var(--air);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--ink) 22%, transparent);
  color: var(--ink-2);
  transform: translate3d(var(--tx), var(--ty), 0);
  white-space: nowrap;
  font-size: var(--f-small);
  font-weight: 560;
  font-stretch: 90%;
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
}
.tag[data-lit='true'] .tag__words,
.tags--reach .tag__words,
.tags--strata .tag__words {
  color: var(--ink);
}
.tag[data-lit='true'] .tag__words {
  box-shadow: inset 0 0 0 1.5px var(--ink);
}
.reading {
  position: fixed;
  left: 0;
  top: 0;
  padding: 0.3rem 0.55rem;
  border-radius: var(--r-2);
  background: var(--air);
  color: var(--ink);
  font-size: var(--f-small);
  font-weight: 560;
  font-stretch: 90%;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-micro) var(--ease-settle);
}
.reading[data-on='true'] {
  opacity: 1;
}

/* Without the scene the labels are a plain list in the copy's reading order, positioned so they
   paint above the frame's darkened ground as the copy does. */
html:not(.gl) .tags {
  position: relative;
  padding: 0 var(--margin);
}
html:not(.gl) .tag {
  position: static;
  opacity: 1;
}
html:not(.gl) .tag::before,
html:not(.gl) .tag::after {
  display: none;
}
html:not(.gl) .tag__words {
  position: static;
  transform: none;
}

/* -- pages: the header ---------------------------------------------------------------------- */

/* Every page but the home page opens on one view of the scene; the page then slides over it. */
.page:not(.page--home) main,
.foot {
  position: relative;
  z-index: 2;
}
.head {
  position: relative;
  isolation: isolate;
  height: 100svh;
  min-height: 34rem;
}
.poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The header's poster is the loading frame while the live scene starts, and the frame itself
   without WebGL. */
.head .poster {
  display: block;
  transition: opacity var(--t-scene) var(--ease-heave);
}
.live .head .poster {
  opacity: 0;
}
.head__copy {
  position: absolute;
  left: var(--margin);
  top: 14svh;
  width: min(38rem, 42vw);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
  justify-items: start;
}
.head__kind {
  margin-top: -0.5rem;
  color: var(--text-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  letter-spacing: 0.01em;
}
.head__copy .figures {
  margin-top: 0.4rem;
}
.head[data-view='stargazer'] .head__copy {
  top: 15svh;
}
.head[data-view='venus'] .head__copy {
  top: 11svh;
  width: min(44rem, 50vw);
  gap: 1rem;
}
.head[data-view='pileon-sql'] .head__copy {
  top: 12svh;
  width: min(30rem, 36vw);
}
.head[data-view='arachnid'] .head__copy {
  top: 13svh;
}
.head[data-view='studio'] .head__copy {
  top: 16svh;
}
/* Where the copy crosses ground of every shade, the scene is darkened behind it, as on the home
   page's chapters. */
.head[data-view='pileon-sql']::before,
.head[data-view='arachnid']::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: min(64vw, 58rem);
  background: linear-gradient(to right, color-mix(in oklch, var(--ground) 84%, transparent) 0%, color-mix(in oklch, var(--ground) 70%, transparent) 50%, transparent 100%);
  pointer-events: none;
}
/* Past the header the navigation steps away while reading down and comes back on the way up,
   over a fade of the page's ground so it never sits on the text. */
.nav {
  transition: translate var(--t-ui) var(--ease-settle);
}
.nav::before {
  content: '';
  position: absolute;
  inset: 0 0 -2rem;
  z-index: -1;
  background: linear-gradient(var(--ground) 50%, color-mix(in oklch, var(--ground) 0%, transparent));
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-ui) var(--ease-settle);
}
[data-nav='over'] .nav::before,
[data-nav='away'] .nav::before {
  opacity: 1;
}
/* On the home page the fade is wanted only where the page scrolls freely under the nav, the
   hero and the finale, both in daylight; over the river it would be a dark band on the scene. */
[data-tone='river'] .page--home .nav::before,
[data-tone='lamp'] .page--home .nav::before {
  opacity: 0;
}
[data-nav='away'] .nav:not(:focus-within) {
  translate: 0 -130%;
  transition: translate calc(var(--t-ui) * 0.6) var(--ease-release);
}
.still--head {
  position: absolute;
  right: var(--margin);
  bottom: max(1.25rem, env(safe-area-inset-bottom));
}
html:not(.js) .still,
.no-gl .still--head {
  display: none;
}

/* Text pages: the title on the river, cut by the waterline like the hero's wordmark. */
.head--water {
  --line: 52lvh;
  --f-page: clamp(3.25rem, min(var(--f-word), calc((100vw - 2 * var(--margin)) / (var(--chars) * 0.39))), 20rem);
}
.head--water .word--page {
  top: calc(var(--line) - 0.464em);
  font-size: var(--f-page);
}
/* The 404's title leaves the right of the river to the survey stake. */
.head--missing {
  --f-page: clamp(3rem, 9vw, 9.5rem);
}
.head--water .lead {
  position: absolute;
  left: var(--margin);
  top: calc(var(--line) + 0.5 * var(--f-page));
  width: min(30rem, calc(100vw - 2 * var(--margin)));
  color: var(--river-ink);
}
.head--water .more {
  position: absolute;
  left: var(--margin);
  top: calc(var(--line) + 0.5 * var(--f-page) + 7.5rem);
}
.no-gl .head--water .word,
html:not(.js) .head--water .word {
  position: absolute;
  top: calc(var(--line) - 0.464em);
  margin: 0;
}

/* -- pages: the body ------------------------------------------------------------------------ */

.body {
  position: relative;
  display: grid;
  gap: clamp(3.5rem, 7vw, 6.5rem);
  padding: clamp(4rem, 8vw, 7.5rem) var(--margin) clamp(3.5rem, 7vw, 6.5rem);
  background: var(--ground);
  /* A lift line where the page meets the scene. */
  box-shadow: inset 0 1px 0 var(--rule);
}
.block,
.clause {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: 1.25rem clamp(2rem, 5vw, 5rem);
  max-width: 90rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
}
.block__title {
  font-size: clamp(1.75rem, 2.7vw, 2.75rem);
  line-height: 0.95;
}
.block__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
  justify-items: start;
  align-content: start;
}
.block__body > p,
.clause > p {
  max-width: var(--measure);
}
.block__body > .big,
.big {
  max-width: 38rem;
  font-size: var(--f-lead);
  font-weight: 440;
  font-stretch: 96%;
  letter-spacing: -0.006em;
  line-height: 1.36;
}
.note {
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  letter-spacing: 0.01em;
  line-height: 1.45;
}
.plates {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* Lists, questions and tables: rows separated by lift lines. */
.list,
.faq,
.table {
  width: 100%;
  max-width: 54rem;
}
.list > div,
.faq > div {
  display: grid;
  gap: 0.3rem 2rem;
  padding: 1rem 0 1.15rem;
  border-top: 1px solid var(--rule);
}
.list > div:last-child,
.faq > div:last-child {
  border-bottom: 1px solid var(--rule);
}
/* A list that opens a block starts on the block's own lift line. */
.block__body > :is(.list, .faq):first-child > div:first-child {
  padding-top: 0;
  border-top: 0;
}
.list > div {
  grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
}
.list dt,
.faq dt {
  font-weight: 560;
  font-stretch: 92%;
}
.list dd,
.faq dd {
  max-width: var(--measure);
  color: var(--text-2);
}
.faq dt {
  font-size: calc(var(--f-body) * 1.08);
}
/* Positioned, so the screen-reader notes inside it scroll with the table instead of widening
   the page. */
.table {
  position: relative;
  overflow-x: auto;
}
.table table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums lining-nums;
}
.table th,
.table td {
  padding: 0.8rem 0 0.85rem 1.5rem;
  border-bottom: 1px solid var(--rule);
  text-align: right;
  white-space: nowrap;
}
.table th:first-child {
  padding-left: 0;
  text-align: left;
  white-space: normal;
}
.table thead th {
  vertical-align: bottom;
  color: var(--text-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  letter-spacing: 0.01em;
}
.table tbody th {
  font-weight: 440;
}
/* The better figure in a row carries the mark's hexagon, not a weight or a colour. */
.table .best::before {
  content: '';
  display: inline-block;
  width: 0.5em;
  height: 0.58em;
  margin-right: 0.45em;
  background: currentColor;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
}

/* Specification pages: numbered clauses. */
.clause h2 {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  align-items: baseline;
  font-size: clamp(1.5rem, 2.2vw, 2.25rem);
  font-weight: 600;
  font-stretch: 74%;
  letter-spacing: -0.02em;
  line-height: 1;
  text-wrap: balance;
}
.clause__no {
  color: var(--text-2);
  font-variant-numeric: tabular-nums lining-nums;
}

/* The next project. */
.next {
  position: relative;
  padding: 0 var(--margin) clamp(4rem, 8vw, 7rem);
  background: var(--ground);
}
.next__link {
  display: grid;
  justify-items: start;
  gap: 0.4rem;
  max-width: 90rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
  text-decoration: none;
}
.next__label,
.next__kind {
  color: var(--text-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  letter-spacing: 0.01em;
}
.next__name {
  font-size: var(--f-title);
  font-weight: 600;
  font-stretch: 74%;
  letter-spacing: -0.02em;
  line-height: 0.95;
  text-decoration-line: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.12em;
  text-decoration-color: color-mix(in oklch, currentColor 30%, transparent);
  transition: text-underline-offset var(--t-ui) var(--ease-settle), text-decoration-color var(--t-ui) var(--ease-settle);
}
@media (hover: hover) {
  .next__link:hover .next__name {
    text-underline-offset: -0.08em;
    text-decoration-thickness: 0.3em;
    text-decoration-color: color-mix(in oklch, currentColor 16%, transparent);
    text-decoration-skip-ink: none;
  }
}

/* -- pages: the footer ---------------------------------------------------------------------- */

.foot {
  display: grid;
  gap: 3rem;
  padding: clamp(4rem, 8vw, 7rem) var(--margin) max(2rem, env(safe-area-inset-bottom));
  background: var(--ground);
  color: var(--text);
}
.foot__contact {
  display: grid;
  gap: 1rem;
}
.foot__lead {
  max-width: 40rem;
  font-size: var(--f-lead);
  font-weight: 440;
  font-stretch: 96%;
  letter-spacing: -0.006em;
  line-height: 1.36;
}
.foot__under {
  max-width: var(--measure);
  color: var(--text-2);
}
.foot__index ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.6rem;
  max-width: 54rem;
}
.foot__index a {
  font-size: var(--f-lead);
  font-weight: 500;
  font-stretch: 90%;
  text-decoration-color: color-mix(in oklch, currentColor 35%, transparent);
}
.foot__index a[aria-current] {
  text-decoration-color: currentColor;
}

/* -- pages: demos --------------------------------------------------------------------------- */

.demo {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
  width: 100%;
  max-width: 54rem;
}
.demo__caption {
  display: grid;
  gap: 0.35rem;
  max-width: 40rem;
}
.demo__title {
  font-size: calc(var(--f-body) * 1.2);
  font-weight: 600;
  font-stretch: 84%;
  line-height: 1.15;
}
.demo__caption p {
  color: var(--text-2);
}
.demo__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.5rem;
}
.demo--wide .demo__body {
  grid-template-columns: minmax(0, 1.55fr) minmax(13rem, 1fr);
  align-items: start;
}
.demo--wide .demo__body > :only-child {
  grid-column: 1 / -1;
}
.demo__well {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: var(--ground-2);
  border-radius: var(--r-4);
}
.demo__well--square {
  aspect-ratio: 1;
}
.demo__well canvas {
  width: 100%;
  height: 100%;
  touch-action: none;
}
.demo__well--scene {
  cursor: grab;
  touch-action: none;
}
.demo__well--scene:active {
  cursor: grabbing;
}
.demo__well img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.demo__side {
  display: grid;
  gap: 1.25rem;
  align-content: start;
}
.demo__note,
.demo__status {
  max-width: 34rem;
  color: var(--text-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  letter-spacing: 0.01em;
  line-height: 1.45;
}
.demo__status:empty,
.demo__off {
  display: none;
}
html:not(.js) .demo__body,
html:not(.js) .demo__wait {
  display: none;
}
html:not(.js) .demo__off {
  display: inline;
}

/* Gauge-faced fields: figures in tnum over a graduated underline, the unit outside. */
.fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: 1.25rem 2rem;
  width: 100%;
}
.field {
  display: grid;
  gap: 0.3rem;
  min-width: 0;
}
.field label {
  color: var(--text-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  letter-spacing: 0.01em;
}
.field__box {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  max-width: 15rem;
}
.field__unit {
  min-width: 2.2em;
  color: var(--text-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  white-space: nowrap;
}
.field__unit:empty {
  display: none;
}
.field input[type='number'],
.field input[type='text'] {
  width: 100%;
  min-width: 0;
  padding: 0.1rem 0 0.7rem;
  border: 0;
  border-radius: 0;
  color: var(--text);
  font: inherit;
  font-size: clamp(1.5rem, 2.1vw, 1.9rem);
  font-weight: 560;
  font-stretch: 84%;
  font-variant-numeric: tabular-nums lining-nums;
  line-height: 1.1;
  appearance: textfield;
  -moz-appearance: textfield;
  /* The underline is a gauge board on its side: a long tick every 2.5 rem, short ones between. */
  background:
    linear-gradient(currentColor, currentColor) left bottom / 100% 1.5px no-repeat,
    repeating-linear-gradient(to right, currentColor 0 1.5px, transparent 1.5px 2.5rem) left bottom / 100% 0.6rem no-repeat,
    repeating-linear-gradient(to right, color-mix(in oklch, currentColor 45%, transparent) 0 1px, transparent 1px 0.5rem) left bottom / 100% 0.32rem no-repeat;
}
.field input::-webkit-outer-spin-button,
.field input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.field--range {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 1rem;
}
.field--range output {
  grid-row: 1;
  grid-column: 2;
  font-size: var(--f-small);
  font-weight: 560;
  font-stretch: 90%;
  font-variant-numeric: tabular-nums lining-nums;
}
.field--range input {
  grid-column: 1 / -1;
}
input[type='range'] {
  width: 100%;
  height: 2rem;
  margin: 0;
  background: transparent;
  color: var(--text);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}
input[type='range']::-webkit-slider-runnable-track {
  height: 0.6rem;
  background:
    linear-gradient(currentColor, currentColor) left bottom / 100% 1.5px no-repeat,
    repeating-linear-gradient(to right, color-mix(in oklch, currentColor 50%, transparent) 0 1px, transparent 1px 10%) left bottom / calc(100% + 1px) 0.6rem no-repeat;
}
input[type='range']::-moz-range-track {
  height: 0.6rem;
  background:
    linear-gradient(currentColor, currentColor) left bottom / 100% 1.5px no-repeat,
    repeating-linear-gradient(to right, color-mix(in oklch, currentColor 50%, transparent) 0 1px, transparent 1px 10%) left bottom / calc(100% + 1px) 0.6rem no-repeat;
}
/* The thumb is the mark's hexagon, point down on the board like a gauge pointer. */
input[type='range']::-webkit-slider-thumb {
  width: 1.05rem;
  height: 1.2rem;
  margin-top: -0.25rem;
  border: 0;
  background: currentColor;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  -webkit-appearance: none;
}
input[type='range']::-moz-range-thumb {
  width: 1.05rem;
  height: 1.2rem;
  border: 0;
  border-radius: 0;
  background: currentColor;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
}
/* The develop demo: the frame across the column, its controls in columns under it. */
.demo--develop .demo__body {
  grid-template-columns: minmax(0, 1fr);
}
.sliders {
  grid-template-columns: repeat(auto-fill, minmax(min(12rem, 100%), 1fr));
  gap: 0.5rem 2rem;
}
.sliders > p {
  grid-column: 1 / -1;
  margin-top: 0.5rem;
}
.sliders .field label {
  color: var(--text);
}

/* Readouts: the label small, the value set as a figure. */
.readout {
  display: grid;
  gap: 0.9rem;
  width: 100%;
}
.readout > div {
  display: grid;
  gap: 0.15rem;
}
.readout dt {
  color: var(--text-2);
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
  letter-spacing: 0.01em;
}
.readout dd {
  font-size: clamp(1.25rem, 1.6vw, 1.5rem);
  font-weight: 520;
  font-stretch: 86%;
  font-variant-numeric: tabular-nums lining-nums;
  line-height: 1.15;
}
.demo:not(.demo--wide) .readout {
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  align-items: end;
}
.demo:not(.demo--wide) .readout__main {
  grid-column: 1 / -1;
}
.readout .readout__main dd {
  font-size: var(--f-figure);
  font-weight: 560;
  font-stretch: 80%;
  letter-spacing: -0.015em;
  line-height: 1;
}

/* Chips: hexagon-ended outlines in the tone's ink, filled when chosen. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.chip {
  --cut: 0.7rem;
  position: relative;
  isolation: isolate;
  min-height: 2.75rem;
  padding: 0.5rem 1.3rem;
  border: 0;
  background: none;
  color: var(--text);
  font-size: var(--f-small);
  font-weight: 560;
  font-stretch: 90%;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: color var(--t-micro) var(--ease-settle);
}
.chip::before,
.chip::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  clip-path: polygon(var(--cut) 0, calc(100% - var(--cut)) 0, 100% 50%, calc(100% - var(--cut)) 100%, var(--cut) 100%, 0 50%);
  transition: background-color var(--t-micro) var(--ease-settle);
}
.chip::before {
  background: currentColor;
}
.chip::after {
  inset: 1.5px;
  --cut: calc(0.7rem - 0.85px);
  background: var(--ground);
}
.chip[aria-pressed='true'] {
  color: var(--ground);
}
.chip[aria-pressed='true']::before,
.chip[aria-pressed='true']::after {
  background: var(--text);
}
.chip:focus-visible {
  outline-offset: 2px;
}
@media (hover: hover) {
  .chip:hover:not([aria-pressed='true'])::after {
    background: color-mix(in oklch, var(--text) 9%, var(--ground));
  }
}
.answer {
  display: grid;
  gap: 0.5rem;
  min-height: 9rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--rule);
}
.answer__name {
  font-size: clamp(1.75rem, 3vw, 2.75rem);
  font-weight: 600;
  font-stretch: 78%;
  letter-spacing: -0.015em;
  line-height: 1;
}
.answer__why {
  max-width: var(--measure);
  color: var(--text-2);
}

/* Legends and the flow bar. */
.legend {
  display: grid;
  gap: 0.45rem;
  font-size: var(--f-small);
  font-weight: 520;
  font-stretch: 90%;
}
.legend li {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}
.legend i {
  flex: none;
  width: 1.75rem;
  border-top: 2.5px solid currentColor;
}
.legend .legend__b {
  border-top: 2px dashed color-mix(in oklch, currentColor 70%, transparent);
}
.demo--layers .legend .legend__b {
  border-top: 2px solid color-mix(in oklch, currentColor 38%, transparent);
}
.bar {
  position: relative;
  width: 100%;
  height: 0.9rem;
  margin-block: 0.5rem;
  background:
    linear-gradient(currentColor, currentColor) left bottom / 100% 1.5px no-repeat,
    repeating-linear-gradient(to right, color-mix(in oklch, currentColor 50%, transparent) 0 1px, transparent 1px 10%) left bottom / calc(100% + 1px) 0.5rem no-repeat;
}
.bar__feed {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0.35rem;
  width: var(--feed, 0%);
  background: currentColor;
  border-radius: var(--r-1);
  transition: width var(--t-ui) var(--ease-settle);
}
.bar__cmd {
  position: absolute;
  top: -0.35rem;
  bottom: 0;
  left: var(--cmd, 0%);
  width: 2px;
  margin-left: -1px;
  background: currentColor;
  transition: left var(--t-ui) var(--ease-settle);
}

/* -- without the scene: posters, and every word in reading order ----------------------------- */

/* Without WebGL (.no-gl) or without script the story is a plain page. The hero keeps its
   composition on its poster; every other chapter shows its poster as a picture and its words
   under it on the chapter's own ground, so no word sits on an image at any size. The .js.no-gl
   and html:not(.js) .frame selectors outrank the reveal and portrait rules wherever they sit. */
.no-gl .chapter,
html:not(.js) .chapter {
  height: auto;
  padding-top: 0;
}
.no-gl .track,
html:not(.js) .track {
  position: relative;
  inset: auto;
  height: auto;
}
.no-gl .frame,
html:not(.js) .frame {
  position: relative;
  display: flex;
  flex-direction: column;
  height: auto;
  padding: 0 0 5rem;
  background: var(--ground);
  user-select: text;
}
.js.no-gl .chapter .frame:has(.copy)::before,
html:not(.js) .chapter .frame::before {
  display: none;
}
.no-gl .frame .poster,
html:not(.js) .frame .poster {
  position: relative;
  inset: auto;
  z-index: auto;
  order: -1;
  height: auto;
  margin-bottom: 3rem;
}
.no-gl .frame .poster img,
html:not(.js) .frame .poster img {
  display: block;
  height: auto;
}
.js.no-gl .frame .copy,
html:not(.js) .frame .copy {
  position: relative;
  top: auto;
  left: auto;
  margin: 0 var(--margin);
}
.no-gl .tags,
html:not(.js) .tags {
  margin-top: 1.5rem;
}

/* The hero: the wordmark stands on the poster's waterline and the line under it is set on the
   water, as in the live hero. The poster is placed so its waterline (58% of its height, 55% in
   the tall frame) falls on the same line of the screen. */
.no-gl .chapter--caisson .frame,
html:not(.js) .chapter--caisson .frame {
  display: block;
  height: 100svh;
  padding: 0;
  background: none;
}
.no-gl .chapter--caisson .poster,
html:not(.js) .chapter--caisson .poster {
  position: absolute;
  inset: 0;
  z-index: -1;
  height: 100%;
  margin: 0;
}
.no-gl .chapter--caisson .poster img,
html:not(.js) .chapter--caisson .poster img {
  height: 100%;
  object-position: 50% 58%;
}
.no-gl .word,
html:not(.js) .word {
  top: calc(58svh - 0.82em);
}
.js.no-gl .frame .copy--hero,
html:not(.js) .frame .copy--hero {
  position: absolute;
  top: calc(58svh + 1.75rem);
  margin: 0;
  left: var(--margin);
  color: var(--river-ink);
}

/* The finale: its picture, then the copy on the sky's ground, then the index on the river's. */
.no-gl .frame--finale,
html:not(.js) .frame--finale {
  padding-bottom: 0;
}
.js.no-gl .frame .copy--finale,
html:not(.js) .frame .copy--finale {
  margin: 0;
  padding-top: 0;
}
.no-gl .finale,
html:not(.js) .finale {
  margin-top: 4rem;
  padding-top: 3.5rem;
  background: var(--river);
}

.no-gl .pump,
html:not(.js) .pump,
.no-gl .gauge,
html:not(.js) .gauge,
html:not(.js) .reading,
.no-gl .reading,
.no-gl .still--foot,
.no-gl .hero__hint,
html:not(.js) .hero__hint {
  display: none;
}
/* Without script the navigation scrolls away with the page, since nothing moves it aside. */
html:not(.js) .nav {
  position: absolute;
}
/* Nothing drives the reveals, so every frame's words stand from the start. */
.js.no-gl .frame .title__line {
  transform: none;
}
.js.no-gl .frame .title,
.js.no-gl .frame .copy > :not(.title),
.js.no-gl .frame .finale {
  opacity: 1;
  translate: none;
}

/* -- reduced motion -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .js .frame .title__line,
  .js .frame[data-in='true'] .title__line {
    transform: none;
  }
  .js .frame .copy > :not(.title),
  .js .frame .finale,
  .js .frame .title {
    translate: none;
  }
  .js .frame .title {
    opacity: 0;
    transition: opacity var(--t-ui) var(--ease-heave);
  }
  .js .frame[data-in='true'] .title {
    opacity: 1;
  }
  .menu,
  .menu[data-open='true'] {
    transition: none;
  }
  .gauge__board {
    transition: none;
  }
}

/* -- portrait: phones, and tablets held upright ---------------------------------------------- */

/* Narrower than 4:5, every chapter's copy stands at the top, on a ground that fades into the
   scene, and the scene's subject is framed below it (world.js: the tall presets take over at
   the same aspect, and the hero's waterline follows the wordmark). */
@media (max-width: 760px), (max-aspect-ratio: 4/5) {
  .frame .copy:not(.copy--hero, .copy--finale) {
    top: 5.25rem;
    width: min(36rem, calc(100vw - 2 * var(--margin) - 3rem));
    gap: 1.1rem;
  }
  .chapter--stargazer,
  .chapter--liquox,
  .chapter--pileon-sql {
    --scrim: var(--river);
  }
  .chapter--venus {
    --scrim: var(--air);
  }
  .chapter--arachnid {
    --scrim: var(--lamp);
  }
  .js .frame:has(.copy:not(.copy--hero, .copy--finale))::before {
    content: '';
    position: absolute;
    inset: 0 0 auto;
    width: auto;
    height: 70svh;
    background: linear-gradient(color-mix(in oklch, var(--scrim) 84%, transparent) 0%, color-mix(in oklch, var(--scrim) 72%, transparent) 50%, transparent 100%);
    pointer-events: none;
    opacity: 0;
    transition: opacity calc(var(--t-reveal) * 0.6) var(--ease-release);
  }
  .js .frame[data-in='true']:has(.copy:not(.copy--hero, .copy--finale))::before {
    opacity: 1;
    transition: opacity var(--t-reveal) var(--ease-settle);
  }
  .word {
    top: calc(55lvh - 0.464em);
  }
  .copy--hero {
    top: calc(55lvh + 0.46 * var(--f-word));
  }
  /* The finale's pier stands under the copy here, so the band between the copy and the index
     holds it whole: the waterline runs low in the band and the pier rises from it to just short
     of the last line of copy. The copy waits until the camera has tilted up off the pier. */
  .frame--finale {
    --line-in-band: 0.78;
    --arrive: 4;
  }
  .finale {
    padding-top: 28svh;
  }
}

/* Without the scene, portrait: the tall pictures are cut to 4:5 round their subject, which the
   tall presets frame low, and the hero's line moves with the wordmark to 55% of the screen. */
@media (max-width: 760px), (max-aspect-ratio: 4/5) {
  .no-gl .chapter:not(.chapter--caisson) .poster img,
  html:not(.js) .chapter:not(.chapter--caisson) .poster img {
    aspect-ratio: 4 / 5;
    object-fit: cover;
    object-position: 50% 85%;
  }
  .no-gl .chapter--caisson .poster img,
  html:not(.js) .chapter--caisson .poster img {
    object-position: 50% 55%;
  }
  .no-gl .word,
  html:not(.js) .word {
    top: calc(55svh - 0.82em);
  }
  .js.no-gl .frame .copy--hero,
  html:not(.js) .frame .copy--hero {
    top: calc(55svh + 1.5rem);
  }
}

/* -- phones ---------------------------------------------------------------------------------- */

@media (max-width: 760px) {
  .copy {
    width: calc(100vw - 2 * var(--margin) - 3rem);
  }
  .figures {
    grid-template-columns: repeat(auto-fit, minmax(min(8.5rem, 100%), 1fr));
    gap: 1rem 1.25rem;
  }
  .hero__hint {
    left: var(--margin);
    bottom: calc(max(1.5rem, env(safe-area-inset-bottom)) + 4.5rem);
    translate: none;
  }
  .gauge {
    top: 4.75rem;
    bottom: 8.5rem;
    perspective-origin: calc(100% - 0.75rem) 50%;
  }
  .gauge__board {
    width: 1.5rem;
  }
  .gauge__read,
  .still {
    font-size: 0.75rem;
  }
  .gauge__read {
    padding: 0.3rem 0.4rem;
  }
  .loader__plan {
    right: 50%;
    translate: 50% -80%;
    height: 34svh;
  }
  .loader__bytes {
    right: 50%;
    translate: 50% 0;
    top: calc(50% - 34svh * 0.3 + 2rem);
    width: auto;
  }
  .menu__inner {
    grid-template-columns: 1fr;
  }
  /* The finale takes the whole width: the gauge goes with the pump once its copy is up, and
     Still water moves into the finale's last row. */
  .gauge {
    transition: opacity var(--t-reveal) var(--ease-settle), translate var(--t-reveal) var(--ease-settle);
  }
  .ready[data-finale='true'] .gauge {
    opacity: 0;
    translate: calc(100% + var(--margin)) 0;
    visibility: hidden;
    transition: opacity calc(var(--t-reveal) * 0.6) var(--ease-release), translate calc(var(--t-reveal) * 0.6) var(--ease-release), visibility 0s var(--ease-release) calc(var(--t-reveal) * 0.6);
  }
  .still--foot {
    display: inline-flex;
    align-items: center;
  }
  .copy--finale {
    padding-right: var(--margin);
  }
  .finale {
    padding-right: var(--margin);
  }
}

/* -- pages, portrait ------------------------------------------------------------------------- */

@media (max-width: 760px), (max-aspect-ratio: 4/5) {
  /* The copy stands at the top on a ground that fades into the scene, and the tall presets frame
     the subject below it, as on the home page. */
  .head.head--scene .head__copy {
    top: 5.25rem;
    width: min(40rem, calc(100vw - 2 * var(--margin)));
    gap: 1rem;
  }
  .head.head--scene::before {
    content: '';
    position: absolute;
    inset: 0 0 auto;
    width: auto;
    height: 72svh;
    background: linear-gradient(color-mix(in oklch, var(--ground) 86%, transparent) 0%, color-mix(in oklch, var(--ground) 74%, transparent) 55%, transparent 100%);
    pointer-events: none;
  }
  /* The waterline views split: title and lead on the sky, the subject on the line, and the
     figures under the water (head.js reads --split to place the line). */
  .head[data-view='tantalum'],
  .head[data-view='studio'] {
    --split: 1;
  }
  .head[data-view='tantalum']::before,
  .head[data-view='studio']::before {
    display: none;
  }
  .head[data-view='tantalum'] .head__copy .figures,
  .head[data-view='studio'] .head__copy .figures {
    margin-top: 25svh;
    color: var(--river-ink);
  }
  .head[data-view='tantalum'] .figures dd,
  .head[data-view='studio'] .figures dd {
    color: var(--river-ink-2);
  }
}

/* -- pages on phones ------------------------------------------------------------------------ */

@media (max-width: 760px) {
  .head--water {
    --line: 44lvh;
  }
  .block,
  .clause {
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
  }
  .list > div {
    grid-template-columns: minmax(0, 1fr);
  }
  .demo--wide .demo__body {
    grid-template-columns: minmax(0, 1fr);
  }
  .table th,
  .table td {
    padding-left: 1rem;
  }
  .table thead th {
    white-space: normal;
  }
}
