* { box-sizing: border-box; margin: 0; padding: 0; }

/* ---- theme ---------------------------------------------------------------
   Dark is the default (white lines on black). The toggle flips to a warm
   light theme (dark ink on paper), echoing the daytime "resplendent canvas"
   reference. The canvas reads --bg / --ink from here and re-renders. */
:root,
/* Dark is the base palette so the variables are always defined, even before
   the theme script runs or if data-theme is never set. Light overrides it. */
:root,
:root[data-theme="dark"] {
  --bg: #0d0a07; /* near-black with a slight warm tinge */
  --ink: #f4f1ea;
  --muted: rgba(244, 241, 234, 0.72);
  --hair: rgba(244, 241, 234, 0.18);
  --yarn: #c2bcae; /* solid warm grey for the wool ball */
  color-scheme: dark;
}
:root[data-theme="light"] {
  --bg: #f3ecdf;
  --ink: #1a160e;
  --muted: rgba(26, 22, 14, 0.74);
  --hair: rgba(26, 22, 14, 0.16);
  --yarn: #595348; /* solid warm grey for the wool ball */
  color-scheme: light;
}

:root {
  --display-font: "Over the Rainbow", cursive; /* wordmark only */
  --body-font: "Coming Soon", Georgia, serif; /* body font picker */
  --tag-font: "Hanken Grotesk", system-ui, sans-serif;
  --body-scale: 1;     /* body size lever */
  --hero-h: 70vh;        /* fold lever (fixed arch layer height) */
  --heading-top: 28vh;   /* heading lever (wordmark rest position) */
  --body-gap: 9rem;      /* space lever (gap below tagline) */
}

html { background: var(--bg); overflow-x: hidden; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--body-font);
  line-height: 1.6;
  overflow-x: hidden; /* a large wordmark must never shift centred content */
  /* Keep vertical swipes for scrolling, but reserve horizontal swipes for the
     mountain scrub. The canvas is pointer-events: none, so this has to live on
     the page itself rather than the canvas. Keep pinch-zoom for the photos. */
  touch-action: pan-y pinch-zoom;
  transition: background 0.4s ease, color 0.4s ease;
}

/* ---- hamburger menu button (top left) ---- */
.hamburger {
  position: fixed;
  top: clamp(0.9rem, 2vw, 1.4rem);
  left: clamp(0.9rem, 2vw, 1.4rem);
  z-index: 35;
  width: 2.2rem;
  height: 2.2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  opacity: 0.75;
  transform-origin: center;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.hamburger:hover { opacity: 1; transform: scale(1.18); }
/* One welcoming nudge once the intro spin settles (set by JS), matching
   the wool ball. */
.hamburger.nudge { animation: yarnNudge 1s ease-in-out; }
.hamburger__icon { display: block; }
/* Hand-drawn wavy lines (paths, not bars). They still slice into an X: the
   middle scales out, the outer two converge on the centre and rotate. */
.hamburger__line {
  transform-box: fill-box;
  transform-origin: center;
  transition:
    transform 0.35s cubic-bezier(0.2, 0.7, 0.3, 1),
    opacity 0.2s ease;
}
.hamburger.is-open .hamburger__line--top { transform: translateY(6px) rotate(45deg); }
.hamburger.is-open .hamburger__line--mid { transform: scaleX(0); opacity: 0; }
.hamburger.is-open .hamburger__line--bot { transform: translateY(-6px) rotate(-45deg); }

/* ---- slide-out menu ---- */
.menu {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: min(82vw, 320px);
  z-index: 34; /* above the spin levers bar; below the hamburger so the X stays on top */
  background: var(--bg);
  display: flex;
  flex-direction: column;
  padding: 5rem 2rem 2rem;
  transform: translateX(-100%);
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.menu.is-open { transform: translateX(0); }
/* Wonky hand-drawn vertical line along the menu's right edge. */
.menu__edge {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 8px;
  height: 100%;
  color: var(--ink);
  opacity: 0.22;
  pointer-events: none;
}
.menu__rule {
  display: block;
  width: 100%;
  height: 10px;
  margin-bottom: 1.8rem;
  color: var(--ink);
  opacity: 0.3;
}
/* A wavy separator used inline between menu items (above Spin). The 1.1rem flex
   gap plus this margin lands the same ~1.8rem breathing room the top rule has
   above the Home item. */
.menu__rule-li { display: block; }
.menu__rule--inline { height: 8px; margin: 0.7rem 0; }
.menu__links {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.menu__link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-family: "Coming Soon", cursive;
  font-size: 1.5rem;
  color: var(--ink);
  text-decoration: none;
  opacity: 0.82;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.menu__link:hover { opacity: 1; transform: translateX(6px); }
.menu__ico {
  width: 1.3rem;
  height: 1.3rem;
  flex: none;
  opacity: 0.85;
}
.menu__icons {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.9rem;
}
.menu__icon {
  display: inline-flex;
  align-items: center;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--ink);
  font-size: 1.45rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.7;
  transform-origin: center;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.menu__icon svg { width: 1.45rem; height: 1.45rem; }
.menu__icon:hover { opacity: 1; transform: scale(1.22); }
/* Hand-drawn wobble on the small icons (wool ball, theme, github, speaker). */
.defs { position: absolute; }
.yarn-ball,
.menu__icon svg { filter: url(#wonky); }
.menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 33;
  background: rgba(0, 0, 0, 0.75); /* dim the page more when open */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease, visibility 0.4s ease;
}
.menu-backdrop.is-open { opacity: 1; visibility: visible; }

/* ---- hero / arch ---- */
.hero {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  height: var(--hero-h);
  overflow: hidden;
  pointer-events: none; /* let scroll and the levers through */
  background: transparent;
}
/* Dissolve scrolling body text into the arch: opaque page colour over the
   mountain lines, clearing lower down so the text fades out before it reaches
   them. Sits above the body, below the line layer. */
body::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--hero-h);
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    var(--bg) 0%,
    var(--bg) var(--gone-pct, 29%),
    transparent var(--foot-pct, 37%)
  );
}
.hero canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* Let clicks pass through to whatever sits beneath (the wordmark text, the
     gallery photos, links). The mountains still drive the motion: document-level
     pointer handlers hit-test the click against the drawn lines, so only a press
     that actually lands on a line starts, stops, or scrubs the spin. */
  pointer-events: none;
  touch-action: pan-y;    /* keep vertical scroll; horizontal drags are ours */
  -webkit-tap-highlight-color: transparent; /* no blue flash when tapping */
  user-select: none;
  outline: none;
}

/* Wordmark sits in the dark opening under the arch. */
.hero__mark {
  position: relative;
  z-index: 1;
  margin: var(--heading-top) auto 0;
  text-align: center;
  padding: 0 1.2rem;
  /* Clip a wordmark wider than the viewport horizontally only, so it can bleed
     to the edges without pushing the centred tagline off-screen. Descenders
     stay visible because the vertical axis is not clipped. */
  overflow-x: clip;
}
.hero__title {
  font-family: var(--display-font);
  font-optical-sizing: auto;
  font-weight: 400;
  font-size: clamp(3rem, 13vw, 8.5rem);
  line-height: 0.9;
  letter-spacing: 0;
  /* Single-weight handwriting fonts can't go lighter by weight, so erode the
     strokes with a background-coloured outline to thin them. */
  -webkit-text-stroke: 0.8px var(--bg);
}
.hero__tag {
  margin: 1.1rem auto 0;
  max-width: 30ch;
  font-family: "Coming Soon", cursive;
  font-size: clamp(1rem, 2.4vw, 1.3rem);
  letter-spacing: 0.02em;
  line-height: 1.3;
  color: var(--muted);
}

/* ---- page body (scrolls; the arch scrolls away above it) ---- */
.page {
  position: relative;
  z-index: 1;
  max-width: 68ch;
  margin: 0 auto;
  padding: 0 clamp(1.4rem, 6vw, 2rem) 6rem;
}
/* Small wound-yarn ball resting in the gap between the tagline and the body,
   drawn in the same thin line style as the mountains. */
/* The divider is the gap itself: a block as tall as the space value, with the
   ball flex-centred so it always sits in the exact vertical middle. */
.yarn-divider {
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--body-gap);
  color: var(--muted); /* a touch grey, and follows the theme */
  opacity: 1;
  /* A little breathing room between the ball and the body text below. */
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}
.yarn-ball {
  width: 2.3rem;
  height: auto;
  color: var(--yarn); /* solid grey, not the semi-transparent --muted */
  cursor: pointer;
}
/* A single nudge: start at normal size, swell a little, then settle back.
   The fast rise and slower return read as a friendly bounce. Added by JS
   after the intro spin settles, and again on each hover (the class clears
   itself on animationend so it can replay). The animation rides the divider,
   not the ball, because iOS Safari skips transform animations on an element
   that also carries an SVG filter (the ball has #wonky). */
.yarn-divider.nudge { animation: yarnNudge 1s ease-in-out; }
@keyframes yarnNudge {
  0% { transform: scale(1); }
  40% { transform: scale(1.28); }
  100% { transform: scale(1); }
}

.shot {
  margin-bottom: clamp(2rem, 6vw, 3.5rem);
  text-align: center;
}
.shot__img {
  position: relative;
  display: inline-block;
  width: 100%;
  max-width: 440px;
  line-height: 0;
}
.shot__img img {
  display: block;
  width: 100%;
  height: auto;
}
/* Vignette drawn in the page background colour, so it fades to black in the
   dark theme and to paper in the light theme. */
.shot__img::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(
    ellipse at center,
    transparent 26%,
    var(--bg) 78%
  );
}
.shot figcaption {
  margin-top: 0.7rem;
  font-family: var(--body-font);
  font-size: 0.95rem;
  color: var(--muted);
}
.shot figcaption em { opacity: 0.7; }

.prose p {
  font-family: var(--body-font);
  font-size: calc(clamp(1.08rem, 2.1vw, 1.18rem) * var(--body-scale));
  line-height: 1.65;
  margin-bottom: 1.1rem;
}
/* A quiet aside: the take-down note sits softer than the body copy. */
.prose p.note {
  margin-top: 1.8rem;
  font-size: calc(clamp(0.95rem, 1.8vw, 1rem) * var(--body-scale));
  color: var(--muted);
}

/* ---- gallery grid ---- */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(0.5rem, 2vw, 0.9rem);
}
@media (min-width: 560px) {
  .gallery-grid { grid-template-columns: repeat(3, 1fr); }
}
.gallery-cell {
  display: block;
  padding: 0;
  border: 0;
  margin: 0;
  background: none;
  cursor: pointer;
  border-radius: 6px;
  overflow: hidden;
  aspect-ratio: 3 / 4;
  -webkit-tap-highlight-color: transparent;
}
.gallery-cell img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease, opacity 0.4s ease;
}
.gallery-cell:hover img,
.gallery-cell:focus-visible img { transform: scale(1.04); }
.gallery-cell:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* ---- lightbox (full-screen photo viewer) ---- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 60; /* above the menu, hamburger and spin bar */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 3rem);
  background: rgba(0, 0, 0, 0.92);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0.25s ease;
}
.lightbox.is-open { opacity: 1; visibility: visible; }
body.lb-open { overflow: hidden; } /* freeze page scroll behind the viewer */
.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 4px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
  user-select: none;
  -webkit-user-drag: none;
}
.lightbox__close,
.lightbox__nav {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
  cursor: pointer;
  border-radius: 50%;
  line-height: 1;
  transition: background 0.2s ease, opacity 0.2s ease;
  -webkit-tap-highlight-color: transparent;
}
.lightbox__close:hover,
.lightbox__nav:hover { background: rgba(0, 0, 0, 0.65); }
.lightbox__close {
  top: clamp(0.8rem, 2.5vw, 1.4rem);
  right: clamp(0.8rem, 2.5vw, 1.4rem);
  width: 2.6rem;
  height: 2.6rem;
  font-size: 1.8rem;
}
.lightbox__nav {
  top: 50%;
  transform: translateY(-50%);
  width: 3rem;
  height: 3rem;
  font-size: 2.2rem;
  padding-bottom: 0.2rem; /* optically centre the chevron glyph */
}
.lightbox__nav--prev { left: clamp(0.5rem, 2vw, 1.4rem); }
.lightbox__nav--next { right: clamp(0.5rem, 2vw, 1.4rem); }

/* ---- single-page-app subpages ---- */
/* Only the active route's subpage shows. The mountains stay as a fixed
   backdrop on every route; the wordmark shows on home alone. */
.subpage { display: none; }
.subpage.is-active { display: block; }
/* Push the inner pages (everything but home) below the arch so their text
   clears the mountains. Home keeps its own scroll-into-the-arch layout. */
.subpage[data-route]:not([data-route=""]) { padding-top: var(--heading-top); min-height: 100vh; }
.subpage__title {
  /* Mirror the hero wordmark exactly: same display face, single weight,
     size, line-height, centred, and the background-coloured stroke that
     erodes the heavy handwriting strokes. */
  font-family: var(--display-font);
  font-optical-sizing: auto;
  font-weight: 400;
  font-size: clamp(3rem, 13vw, 8.5rem);
  line-height: 0.9;
  letter-spacing: 0;
  text-align: center;
  -webkit-text-stroke: 0.8px var(--bg);
  /* The display face has deep descenders (the "p" in Apricity, the "y" in
     Treasure). Reserve room below the baseline so they never collide with the
     tagline that follows. */
  padding-bottom: 0.32em;
  margin: 0 auto;
}
.subpage__lede {
  /* The hero tagline, repeated per page: short, centred, handwritten. */
  margin: 0.6rem auto 0;
  max-width: 30ch;
  text-align: center;
  font-family: "Coming Soon", cursive;
  font-size: clamp(1.18rem, 3vw, 1.35rem);
  letter-spacing: 0.02em;
  line-height: 1.3;
  color: var(--muted);
}
/* One shared rhythm for every inner page: title, then a small gap to the
   tagline, then equal breathing room above and below the yarn ball before the
   body. Override the home divider, which is sized for the home scroll layout. */
.subpage[data-route]:not([data-route=""]) .yarn-divider {
  height: auto;
  margin: clamp(1.8rem, 5vw, 2.6rem) auto;
}
/* Wordmark only on the home route. */
.hero__mark { display: none; }
body[data-route=""] .hero__mark { display: block; }
/* A little gap above the Spin item so it reads as an action, not a page. */
/* Inline text link inside body copy. */
.textlink {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.textlink:hover { opacity: 0.75; }

/* ---- mobile: less side margin, wider + lower arch handled in JS ---- */
@media (max-width: 640px) {
  :root {
    --heading-top: 28vh; /* wordmark (and the body below it) sit a little lower on mobile */
    --body-gap: 7rem;    /* a little less space below the tagline on mobile */
  }
  .page { padding-left: 2.5rem; padding-right: 2.5rem; }
  .hero__mark { padding: 0 0.5rem; }
  .hero__title { font-size: clamp(3.4rem, 17vw, 5.6rem); }
  .hero__tag { font-size: clamp(1.05rem, 4vw, 1.2rem); }
  .subpage__title { font-size: clamp(3.5rem, 15vw, 5.6rem); }
  .yarn-ball { width: 2rem; }
}

/* ---- lead line (opening of the letter, body-text format, above photo) ---- */
.lead {
  margin: 0 0 clamp(1.4rem, 4vw, 2.2rem);
  text-align: left;
  font-family: var(--body-font);
  font-size: calc(clamp(1.08rem, 2.1vw, 1.18rem) * var(--body-scale));
  line-height: 1.65;
}

/* ---- scroll cue ---- */
.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: clamp(3rem, 7vh, 4.5rem);
  transform: translateX(-50%);
  z-index: 3;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  color: var(--muted);
  text-decoration: none;
  font-family: "Space Grotesk", system-ui, sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  transition: color 0.2s ease;
}
.hero__scroll:hover { color: var(--ink); }
.hero__chev {
  width: 0.6rem;
  height: 0.6rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  animation: chevBounce 1.9s ease-in-out infinite;
}
@keyframes chevBounce {
  0%, 100% { transform: rotate(45deg) translate(-2px, -2px); opacity: 0.4; }
  50% { transform: rotate(45deg) translate(2px, 2px); opacity: 1; }
}


/* ---- temporary tuning levers (remove before launch) ---- */
.levers {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  gap: 1.2rem;
  padding: 0.6rem 0.9rem;
  background: color-mix(in srgb, var(--bg) 84%, transparent);
  backdrop-filter: blur(8px);
  font-family: "Space Grotesk", system-ui, sans-serif;
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  max-width: 100%;
  overflow-x: hidden;
}
.levers label {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
.levers input[type="range"] { accent-color: var(--ink); width: 9rem; }
.levers.is-hidden { display: none; } /* hidden while the menu is open */

/* Hide the whole tuning bar on mobile; defaults still apply. The controls
   remain available on desktop. Placed after the .levers rules so it wins. */
@media (max-width: 640px) {
  .levers { display: none; }
}
.levers__reset {
  border: 1px solid var(--hair);
  background: transparent;
  color: var(--ink);
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  padding: 0.3rem 0.7rem;
  border-radius: 0.3rem;
  cursor: pointer;
  opacity: 0.8;
}
.levers__reset:hover { opacity: 1; border-color: var(--ink); }
.levers__step { padding: 0.2rem 0.5rem; }
#out-tagfont, #out-bodyfont { min-width: 9rem; text-transform: none; letter-spacing: 0; }

/* Play-mode ("Spin") control panel, pinned to the bottom. Hidden until the
   experiment is open, then shown prominently. */
.scrub-pick {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 32;
  display: none; /* shown only inside the experiment */
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
  padding: 1rem 1.2rem calc(1rem + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  backdrop-filter: blur(6px);
  border-top: 1px solid var(--hair);
  font-family: "Coming Soon", cursive;
  transform: translateY(100%);
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1);
}
body.experiment .scrub-pick {
  display: flex;
  transform: translateY(0);
}
.scrub-pick__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem 0.8rem;
}
.scrub-pick__label {
  color: var(--muted);
  font-size: 0.95rem;
}
.scrub-pick__opts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.scrub-pick__opt {
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--hair);
  border-radius: 999px;
  padding: 0.3rem 0.95rem;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.scrub-pick__opt:hover { opacity: 1; border-color: var(--muted); }
.scrub-pick__opt.is-on {
  opacity: 1;
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}
/* Transport buttons share one width so the row reads as a set. */
.scrub-pick__transport { gap: 0.6rem 0.8rem; }
.scrub-pick__transport .scrub-pick__opt {
  min-width: 7rem;
  text-align: center;
}
/* One labelled slider per parameter, in two rows of three on desktop, with
   the labels all the same width so the sliders line up. */
.scrub-pick__sliders {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.5rem 1.6rem;
  width: 100%;
  max-width: 760px;
}
.slider-unit {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
  color: var(--muted);
}
.slider-unit span {
  flex: none;
  width: 5rem;
  text-align: right; /* hug the slider; right edges align across the grid */
}
.exp-range {
  accent-color: var(--ink);
  flex: 1;
  min-width: 0;
}
/* Speed is a centre-zero control, so show just a knob on a thin track with no
   left-fill (a filled bar would read as a level rather than a direction). */
.exp-range--speed {
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  height: 1rem;
}
.exp-range--speed::-webkit-slider-runnable-track {
  height: 2px;
  border-radius: 2px;
  background: var(--hair);
}
.exp-range--speed::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--ink);
}
.exp-range--speed::-moz-range-track { height: 2px; border-radius: 2px; background: var(--hair); }
.exp-range--speed::-moz-range-progress { background: transparent; }
.exp-range--speed::-moz-range-thumb { width: 14px; height: 14px; border: none; border-radius: 50%; background: var(--ink); }

/* On narrow screens, drop to two columns. */
@media (max-width: 640px) {
  .scrub-pick {
    padding-left: 0.8rem;
    padding-right: 0.8rem;
    box-sizing: border-box;
    max-width: 100vw;
  }
  .scrub-pick__sliders {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0.7rem 1rem;
  }
  /* Stack the label above its slider: taller, but a wider, easier-to-hit track.
     Pad the track vertically so the touch target is larger. */
  .slider-unit {
    flex-direction: column;
    align-items: stretch;
    gap: 0.2rem;
  }
  .slider-unit span { width: auto; }
  .exp-range { padding: 0.3rem 0; } /* a little taller touch target */
  /* Stop, Reset and Done on one row: equal width, a little space between and
     above, with a slightly heavier outline. */
  .scrub-pick__transport {
    flex-wrap: nowrap;
    gap: 1rem;
    margin-top: 0.3rem;
  }
  .scrub-pick__transport .scrub-pick__opt {
    min-width: 0;
    flex: 1 1 0;
    padding: 0.35rem 0.5rem;
    font-size: 0.85rem;
    border-width: 1.8px;
    border-color: var(--muted);
    white-space: nowrap;
  }
}

/* While the experiment is open: lock the page to the mountains, fade the
   prose and wordmark out, and float the wool ball top-right as the way out. */
.hero__mark, .lead, .shot, .prose, .hero__chev, .yarn-divider {
  transition: opacity 0.4s ease;
}
/* In spin mode the page does not scroll, so hand every touch gesture to the
   scrub logic. Without this the browser claims a horizontal swipe as a pan and
   cancels the pointer stream, and the spin never picks up speed. The canvas is
   pointer-events: none, so the gesture lands on the body; setting touch-action
   here is what reaches it. */
body.experiment { overflow: hidden; touch-action: none; }
/* The arch's downward slide in spin mode is done in JS (lowering the feet
   within the canvas) so a tall peak is never clipped at the canvas top. */
body.experiment .hero__mark,
body.experiment .lead,
body.experiment .shot,
body.experiment .prose,
body.experiment .hero__chev,
body.experiment .yarn-divider {
  opacity: 0;
  pointer-events: none;
}
body.experiment .hero canvas { touch-action: none; }

@media (prefers-reduced-motion: reduce) {
  /* No motion anywhere: the mountain intro is already skipped in JS; here the
     menu, hamburger, and fades stop sliding too. */
  body, .menu, .menu-backdrop, .hamburger__line,
  .menu__link, .menu__icon { transition: none; }
  .hero__chev, .yarn-ball, .yarn-divider, .hamburger { animation: none; }
}
