/* Throughput world, mock-e1. One fixed page over a 3D canvas; every word is real page text.
   Sizes: never under 16 px (T5); headings and reading text at least D7's at the same window size
   (T3, T4, measured in out/d7-sizes.json). Three layouts: wide (desktop), tall (phone upright: the 3D on
   top, the text below, T6) and short-wide (a rotated phone: the text beside the 3D). */
@font-face { font-family: "Geist"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(assets/fonts/geist-100-400.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

:root {
  color-scheme: dark;
  --canvas: #07080a;
  --ink: #f4f4f6;
  --body: #d6d6d6;
  --soft: #a9a9ab;
  --ember: 255, 82, 24;
  --deep: 168, 30, 18;
  --accent: #ff6161;
  --btn: #ffffff;
  --btn-ink: #18191a;
  --font: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --gut: 16px;
  --bar-h: 72px;
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --w-display: 680;
  --w-text: 460;

  /* wide (desktop) type: D7's curve, a step bigger, floors from T3 and T4 */
  --fs-hero: clamp(72px, min(7.2vw, 13vh), 150px);    /* T8 (Noah, 2026-10-05: "a lot larger and more bold"): about 104 px at 1440 x 900 (e2b 68) */
  --w-hero: 800;                                        /* T8: extra-bold (e2b 680) */
  --fs-lede: clamp(20px, 1.66vw, 30px);                 /* T8: 24 px at 1440 (e2b 20) */
  --w-lede: 500;                                        /* T8: a little heavier */
  --fs-claim: clamp(40px, min(3.6vw, 6.4vh), 76px);     /* T9: about 52 px at 1440 x 900 (e2b 37) */
  --fs-twin: clamp(20px, 1.6vw, 30px);                  /* T9: 23 px at 1440 (e2b 19) */
  --fs-count: clamp(140px, min(10.4vw, 21vh), 170px);
  --fs-caption: clamp(16px, 0.92vw, 20px);
  --fs-event: clamp(40px, min(3.6vw, 6.4vh), 76px); /* T9, S2b; T11 (his mock-e2 feedback, 2026-10-05: "text needs to be bigger"): the Stellantis line's size, 52 px at 1440 x 900 (e2 37) */
  --fs-h2: clamp(66px, 4.45vw, 88px);
  --fs-statement: clamp(76px, min(5.6vw, 11.6vh), 88px);
  --fs-fitbody: clamp(26px, 1.85vw, 30px);
  --fs-line: clamp(24px, 2.5vw, 44px);                  /* T10: "Three kinds of system." 36 px at 1440 (e2b 20) */
  --fs-build-t: clamp(52px, 4.45vw, 88px);              /* T10: 64 px at 1440 (e2b 46) */
  --fs-build-d: clamp(21px, 1.15vw, 24px);
  --fs-built-t: clamp(40px, 3.6vw, 64px);               /* T10: 52 px at 1440 (e2b 34) */
  --fs-built-d: clamp(22px, 1.8vw, 30px);               /* T10: 26 px at 1440 (e2b 21) */
  --fs-stat: clamp(40px, 3.4vw, 60px);                  /* T10: the two numbers, big like the 90% */
  --fs-quote: clamp(28px, min(2.5vw, 4.6vh), 46px);     /* T10: 36 px at 1440 x 900 (e2b 30) */
  --fs-name: clamp(28px, 2.2vw, 40px);                  /* T10: the name bigger (e2b 24) */
  --fs-role: clamp(18px, 1.3vw, 22px);
  --fs-book: clamp(110px, 7.6vw, 128px);
  --fs-bookline: clamp(24px, 1.9vw, 32px);              /* B7: the smaller lines bigger (e2b 21) */
  --fs-closer: clamp(46px, min(3.2vw, 7vh), 52px);
  --fs-id: 20px;                                        /* B7 (e2b 17) */
  --fs-idname: 28px;                                    /* B7 (e2b 22) */
  --fs-more: 20px;                                      /* ST6: the read-more button */
  --fs-dive: 36px;                                      /* ST6: an open stone's description, bigger and bolder than Igloo's */
  --gap: clamp(14px, 2.2vh, 26px);
}

/* tall: a phone held upright */
@media (max-aspect-ratio: 1/1) {
  :root {
    --bar-h: 60px;
    --fs-hero: 46px; --fs-lede: 19px; --fs-claim: 28px; --fs-twin: 18px; --fs-count: 76px; --fs-caption: 16px; --fs-event: clamp(27px, 7.6vw, 33px); /* J8: about 30 px at 390 wide (e2 24) */
    --fs-h2: 42px; --fs-statement: clamp(44px, 11.2vw, 48px); --fs-fitbody: 20px; --fs-line: 22px;
    --fs-build-t: 34px; --fs-build-d: 18px; --fs-built-t: 30px; --fs-built-d: 20px; --fs-stat: 30px;
    --fs-quote: 20px; --fs-name: 20px; --fs-role: 16px; /* W19 (E8): e2b's sizes; 22 and 24 left the slabs 0.4 to 0.9 of e2b's size */
    --fs-book: 50px; --fs-bookline: 19px; --fs-closer: 32px; --fs-id: 17px; --fs-idname: 22px;
    --fs-dive: 22px;
    --gap: 12px;
  }
}
/* short-wide: a rotated phone (phone sizes, text beside the 3D) */
@media (min-aspect-ratio: 1/1) and (max-height: 500px) {
  :root {
    --bar-h: 50px;
    --fs-hero: 44px; --fs-lede: 18px; --fs-claim: 24px; --fs-twin: 17px; --fs-count: 76px; --fs-caption: 16px; --fs-event: 22px; --fs-stat: 26px;
    --fs-h2: 42px; --fs-statement: 48px; --fs-fitbody: 20px; --fs-line: 18px;
    --fs-build-t: 30px; --fs-build-d: 18px; --fs-built-t: 24px; --fs-built-d: 18px;
    --fs-quote: 20px; --fs-name: 20px; --fs-role: 16px;
    --fs-book: 50px; --fs-bookline: 18px; --fs-closer: 32px; --fs-id: 16px; --fs-idname: 20px;
    --fs-dive: 22px;
    --gap: 8px;
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; background: var(--canvas); color: var(--ink); font-family: var(--font);
  font-weight: var(--w-text); -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
h1, h2, h3, p, figure, blockquote, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; }
img { display: block; max-width: 100%; height: auto; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
:focus-visible { outline: 3px solid rgb(var(--ember)); outline-offset: 3px; border-radius: 6px; }

/* the world: a fixed page that never scrolls (M3). Pinch zoom is off (M22 as changed, his answer 2026-10-09: "I would keep
   the zoom off as you recommended"); a page zoomed in anyway can be pinched back out (html.zoomed, view.js), and one
   finger moves the world either way. The menu scrolls on its own pan-y */
html.js:not(.no-3d), html.js:not(.no-3d) body { position: fixed; inset: 0; overflow: hidden; overscroll-behavior: none; touch-action: none; }
html.js.zoomed:not(.no-3d), html.js.zoomed:not(.no-3d) body { touch-action: pinch-zoom; }
.world { position: fixed; inset: 0; width: 100%; height: 100%; display: block; z-index: 0; }

/* the loading line (LD2): a thin ember line that fills; no number */
.loader { position: fixed; inset: 0; z-index: 70; background: var(--canvas); display: grid; place-items: center;
  transition: opacity 0.6s var(--ease-out), visibility 0s linear 0.6s; }
.loader.is-done { opacity: 0; visibility: hidden; }
.loader__line { width: min(28vw, 22rem); height: 1px; background: rgba(var(--ember), 0.16); overflow: hidden; }
.loader__fill { height: 100%; transform-origin: 0 50%; transform: scaleX(0);
  background: linear-gradient(90deg, rgb(var(--deep)), rgb(var(--ember)) 60%, #ffb347);
  animation: tp-creep 1.6s var(--ease-out) 0.15s forwards; }
.loader__fill.is-driven { animation: none; transition: transform 0.45s var(--ease-out); }
@keyframes tp-creep { to { transform: scaleX(0.12); } }

/* the header on every stop (L1, B2) */
.bar { position: fixed; top: 0; left: 0; right: 0; z-index: 40; height: calc(var(--bar-h) + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) max(clamp(16px, 2.6vw, 48px), env(safe-area-inset-right)) 0 max(clamp(16px, 2.6vw, 48px), env(safe-area-inset-left));
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: linear-gradient(to bottom, rgba(7, 8, 10, 0.72), rgba(7, 8, 10, 0)); }
.bar__brand { display: flex; align-items: center; color: var(--ink); text-decoration: none; padding: 6px 0; }
.lockup { display: block; fill: currentColor; color: var(--ink); }
/* L4 (Noah, 2026-10-04: "Logo Throughput on the top left corner needs to be bigger"): 36 px on desktops; on upright
   phones the Menu word becomes a three-line button (still named Menu for screen readers), so the logo can grow */
.bar .lockup { height: 36px; width: calc(36px * 1894 / 278); }
.bar__end { display: flex; align-items: center; gap: 10px; }
.bar__menu { font: 560 16px/1 var(--font); color: var(--ink); background: rgba(7, 8, 10, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 8px; min-height: 44px; padding: 0 16px; cursor: pointer; }
.bar__menu-icon { display: none; width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.btn { display: inline-flex; align-items: center; justify-content: center; min-height: 48px; padding: 0 24px; border-radius: 8px;
  background: var(--btn); color: var(--btn-ink); font: 620 18px/1 var(--font); letter-spacing: -0.005em; text-decoration: none; white-space: nowrap; }
.btn--sm { min-height: 44px; padding: 0 16px; font-size: 16px; }
.btn--big { min-height: 64px; padding: 0 36px; font-size: 22px; }
@media (max-aspect-ratio: 1/1) {
  .bar .lockup { height: 23px; width: calc(23px * 1894 / 278); }
  .bar__menu { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0; }
  .bar__menu-icon { display: block; }
  .bar__menu-word { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
}
@media (max-aspect-ratio: 1/1) and (min-width: 390px) { .bar .lockup { height: 24px; width: calc(24px * 1894 / 278); } }
@media (min-aspect-ratio: 1/1) and (max-height: 500px) { .bar .lockup { height: 26px; width: calc(26px * 1894 / 278); } .bar__menu, .btn--sm { min-height: 38px; } }

/* stops: all twenty are in the page from the start (LD6); the world fades one in at a time */
.stop { position: fixed; inset: 0; z-index: 10; pointer-events: none; opacity: 0; }
.stop.is-on .stop__in, .stop.is-on .labels { pointer-events: auto; }
.stop__in { position: absolute; display: flex; flex-direction: column; gap: var(--gap); pointer-events: none;
  left: max(clamp(24px, 5.2vw, 104px), env(safe-area-inset-left)); top: var(--bar-h); bottom: 0;
  width: min(46vw, 46rem); justify-content: center; padding: 20px 0 clamp(24px, 6vh, 64px); }
/* a soft dark backing so the words always read over the 3D (T6, A4) */
.stop__in::before { content: ""; position: absolute; z-index: -1; pointer-events: none;
  inset: -4vh -14vw -4vh calc(-1 * max(clamp(24px, 5.2vw, 104px), env(safe-area-inset-left)));
  background: radial-gradient(farthest-side at 30% 50%, rgba(5, 5, 7, 0.82), rgba(5, 5, 7, 0.62) 55%, rgba(5, 5, 7, 0)); }
@media (max-aspect-ratio: 1/1) {
  .stop__in { left: 0; right: 0; top: auto; bottom: 0; width: auto; justify-content: flex-end;
    padding: 44px max(var(--gut), env(safe-area-inset-right)) calc(18px + env(safe-area-inset-bottom)) max(var(--gut), env(safe-area-inset-left)); }
  .stop__in::before { inset: 0; background: linear-gradient(to bottom, rgba(5, 5, 7, 0), rgba(5, 5, 7, 0.84) 44px, rgba(5, 5, 7, 0.93)); }
}
@media (min-aspect-ratio: 1/1) and (max-height: 500px) {
  .stop__in { left: max(16px, env(safe-area-inset-left)); width: 64vw; padding: 4px 0 10px; }
  .stop--hero .stop__in { width: 54vw; } /* room beside it for the volcanoes' words (H2, T6) */
}
/* A1 (O7's fault, 2026-10-13): on a phone held sideways the calm version shows the wide stills, whose bright subjects can
   sit just past the words' right end; there the backing stays dark further across, so every line reads at 4.5 or over */
@media (min-aspect-ratio: 1/1) and (max-height: 500px) {
  html.calm-still .stop__in::before { background: radial-gradient(farthest-side at 30% 50%, rgba(5, 5, 7, 0.86), rgba(5, 5, 7, 0.72) 65%, rgba(5, 5, 7, 0)); }
}

/* type */
.t-hero { width: fit-content; max-width: 100%; font-size: var(--fs-hero); font-weight: var(--w-hero); line-height: 1.02; letter-spacing: -0.035em; text-wrap: balance; }
.t-lede { font-size: var(--fs-lede); font-weight: var(--w-lede); line-height: 1.5; color: var(--body); max-width: 28em; }
.stop--hero .btn { min-height: 60px; padding: 0 32px; font-size: 20px; } /* T8: the hero's button grows with its words */
.stop--hero .t-cta { width: fit-content; } /* the hero's words take only their own room, so the volcanoes keep theirs */
@media (max-aspect-ratio: 1/1) { .stop--hero .btn { min-height: 56px; font-size: 19px; } }
.t-cta { margin-top: 6px; }
.t-claim { font-size: var(--fs-claim); font-weight: 600; line-height: 1.2; letter-spacing: -0.02em; text-wrap: pretty; }
.t-twin { font-size: var(--fs-twin); line-height: 1.45; color: var(--body); }
.count { display: flex; align-items: center; gap: 0.6em; }
.count__num { font-size: var(--fs-count); font-weight: 700; line-height: 0.86; letter-spacing: -0.05em;
  background: linear-gradient(180deg, #fff 30%, #ffc79e); -webkit-background-clip: text; background-clip: text; color: transparent;
  /* T9 (Noah, 2026-10-05: "the 90% sign ... slightly gets cut off"): the gradient gets room on every side, so no edge of a
     glyph falls outside it; the margins keep the layout as it was */
  padding: 0.08em 0.14em 0.06em 0.04em; margin: -0.08em -0.14em -0.06em -0.04em; }
.count__line { font-size: var(--fs-twin); line-height: 1.4; color: var(--body); max-width: 17em; }
.event { display: flex; align-items: center; gap: 16px; }
.event__img { width: clamp(108px, 9.4vw, 190px); border-radius: 8px; flex: none; }
.event__cap { display: flex; flex-direction: column; gap: 8px; font-size: var(--fs-caption); line-height: 1.4; color: var(--soft); }
.event__mark { width: 96px; height: auto; opacity: 0.92; }
.t-h2 { font-size: var(--fs-h2); font-weight: var(--w-display); line-height: 1; letter-spacing: -0.035em; }
.t-statement { font-size: var(--fs-statement); font-weight: 600; line-height: 1.02; letter-spacing: -0.035em; text-wrap: balance; }
.t-body { font-size: var(--fs-fitbody); line-height: 1.4; color: var(--body); }
.t-line { font-size: var(--fs-line); line-height: 1.4; color: var(--body); }
.card { display: flex; flex-direction: column; gap: calc(var(--gap) * 0.75); }
.card__t { font-weight: 650; line-height: 1.06; letter-spacing: -0.03em; text-wrap: balance; }
.card__t a { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 0.12em; text-decoration-color: rgba(var(--ember), 0.9); }
.card__d { line-height: 1.5; color: var(--body); max-width: 32em; }
.card--build .card__t { font-size: var(--fs-build-t); }
.card--build .card__d { font-size: var(--fs-build-d); }
.card--built .card__t { font-size: var(--fs-built-t); }
.card--built .card__d { font-size: var(--fs-built-d); }
/* T10: "10 hours a week" and "520 hours a year." stand out like the 90%, the same words */
.stat { display: block; width: fit-content; font-size: var(--fs-stat); font-weight: 700; line-height: 1.04; letter-spacing: -0.03em;
  color: transparent; background: linear-gradient(180deg, #fff 30%, #ffc79e); -webkit-background-clip: text; background-clip: text;
  padding: 0.06em 0.12em 0.06em 0.03em; margin: 0.12em 0 0.04em -0.03em; }
/* ST6 (his mock-e2b feedback and his note on the plan, 2026-10-05: "The user should be prompted ... So especially for
   older people, they know to click into it"): each What I build stone shows its title and a real read-more button with a
   slow ring of light; its description stays in the page, read by Google and screen readers (T2 as changed), and shows big
   and bold once the stone is open (main.js sets .is-open), with a labelled Close by it */
.card--build .card__d { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.more { display: inline-flex; align-items: center; gap: 10px; width: fit-content; min-height: 48px; padding: 0 20px;
  font: 600 var(--fs-more)/1 var(--font); letter-spacing: -0.005em; color: var(--ink); cursor: pointer;
  background: rgba(7, 8, 10, 0.72); border: 2px solid rgb(var(--ember)); border-radius: 999px; animation: more-pulse 2.4s ease-out infinite, more-in 0.4s ease-out; }
.more__arrow { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.more__coarse { display: none; }
@media (pointer: coarse) { .more__fine { display: none; } .more__coarse { display: inline; } }
@keyframes more-pulse { 0% { box-shadow: 0 0 0 0 rgba(var(--ember), 0.6); } 70%, 100% { box-shadow: 0 0 0 14px rgba(var(--ember), 0); } }
@keyframes more-in { from { opacity: 0; } } /* back after a stone closes */
.close { display: inline-flex; align-items: center; gap: 10px; width: fit-content; min-height: 56px; padding: 0 28px 0 22px;
  font: 700 22px/1 var(--font); color: #1a0a04; background: rgb(var(--ember)); border: 0; border-radius: 999px; cursor: pointer; }
.close svg { width: 22px; height: 22px; flex: none; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; }
.close[hidden] { display: none; }
.stop.is-open .more { display: none; }
.stop.is-open .card--build .card__d { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal;
  font-size: var(--fs-dive); font-weight: 600; line-height: 1.3; letter-spacing: -0.012em; color: var(--ink); max-width: 22em; }
/* the description and Close arrive as the camera reaches the cave, and leave first as it pulls out */
.stop.is-open .card--build .card__d { animation: dive-in 0.5s var(--ease-out) 0.5s both; }
.stop.is-open .close { animation: dive-in 0.4s var(--ease-out) 0.8s both; }
.stop.is-closing .card--build .card__d, .stop.is-closing .close { animation: dive-out 0.25s ease-in both; }
@keyframes dive-in { from { opacity: 0; transform: translate3d(0, 14px, 0); } }
@keyframes dive-out { to { opacity: 0; } }
/* ST8 (his mock-e3 feedback, 2026-10-08: "only the text Lead generation and outreach, and ... should show, nothing
   else"): an open stone's stop shows only the stone's own words. The section's heading and line fade out first (ui.js
   fadeLeads), then leave the layout while it is open, still in the page for screen readers and Google */
.stop.is-open > .stop__in > .t-h2, .stop.is-open > .stop__in > .t-line { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* ST9 (his mock-e3 feedback, 2026-10-08: "more clear and better RESolution content within when you click into the rock"):
   each stone's symbol over its cave, drawn by the page at the screen's own resolution as the sky logo is. Three strokes
   per line make the glow, no filter: a wide dim ember, a narrower orange and a white-hot core. main.js places it at the
   dive's point (--sym-x, --sym-y, its size --sym) and fades it in with the cave (--sym-o); .is-in starts its drawing-in */
.cave-symbol { position: absolute; left: 0; top: 0; width: var(--sym, 240px); height: var(--sym, 240px); overflow: visible; pointer-events: none;
  transform: translate3d(calc(var(--sym-x, -9999px) - 50%), calc(var(--sym-y, -9999px) - 50%), 0); opacity: var(--sym-o, 0);
  fill: none; stroke-linecap: round; stroke-linejoin: round; visibility: hidden; }
.stop.is-open .cave-symbol { visibility: visible; }
.cave-symbol .g2 { stroke: #ff4d1a; stroke-width: 9.5; stroke-opacity: 0.16; }
.cave-symbol .g1 { stroke: #ff9a3c; stroke-width: 5; stroke-opacity: 0.4; }
.cave-symbol .c { stroke: #fff1d6; stroke-width: 2.4; }
/* a soft dark backing, so the lines read clearly over the cave's brightest wall; the slow glow is the glow strokes' own */
.cave-symbol.is-in .g1, .cave-symbol.is-in .g2 { animation: sym-glow 3.2s ease-in-out 1.3s infinite; }
.cave-symbol.is-in .sym-draw { stroke-dasharray: 1; animation: sym-draw 0.6s cubic-bezier(0.23, 1, 0.32, 1) both; }
.cave-symbol.is-in .sym-fly { animation: sym-fly 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0.5s both; }
.cave-symbol.is-in .sym-arc { stroke-dasharray: 1; animation: sym-draw 0.35s ease-out both; }
.cave-symbol.is-in .sym-arc1 { animation-delay: 0.5s; } .cave-symbol.is-in .sym-arc2 { animation-delay: 0.65s; } .cave-symbol.is-in .sym-arc3 { animation-delay: 0.8s; }
.cave-symbol.is-in .sym-write { stroke-dasharray: 1; animation: sym-draw 0.45s ease-out 0.4s both; }
.cave-symbol.is-in .sym-bars { transform-box: fill-box; transform-origin: 50% 100%; animation: sym-rise 0.4s cubic-bezier(0.23, 1, 0.32, 1) 0.75s both; }
@keyframes sym-draw { from { stroke-dashoffset: 1; opacity: 0; } 4% { opacity: 1; } to { stroke-dashoffset: 0; } } /* hidden until it starts: an empty dash's round caps show as dots */
@keyframes sym-fly { from { opacity: 0; transform: translate(26px, -26px); } }
@keyframes sym-rise { from { transform: scaleY(0); } }
@keyframes sym-glow { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
html.calm-still .stop.is-open .cave-symbol { --sym-o: 1; animation: calm-in 0.35s ease-out both; } /* calm.js: whole and still, with the words' fade */
html.calm-still .stop.is-closing .cave-symbol { opacity: 0; transition: opacity 0.25s ease-in; }
@media (prefers-reduced-motion: reduce) { .cave-symbol.is-in, .cave-symbol.is-in * { animation: none; } }
html:not(.js) .cave-symbol, html.no-3d .cave-symbol, html.static .cave-symbol { display: none; }
html.over-stone .world { cursor: pointer; } /* desktop: a hand over the stone at its stop */
.words__card { display: flex; flex-direction: column; gap: calc(var(--gap) * 1.1); }
.words__q { font-size: var(--fs-quote); line-height: 1.42; letter-spacing: -0.006em; color: color-mix(in oklab, var(--body) 80%, var(--soft)); }
.words__pull { font-weight: var(--w-display); font-size: 1.16em; line-height: 1.28; letter-spacing: -0.018em; color: var(--ink);
  transition: color 0.7s ease, text-shadow 0.7s ease; }
/* WQ1: the key line lights last. While the world runs it keeps the quote's own colour until the camera rests at its stop
   (ui.js is-lit, 350 ms after the words are full), then e2c's colour with a warm glow as the mark's cracks flare. With no
   script, no 3D or one stop on its own it stays e2c's; the calm version shows it lit (F14) */
html.js:not(.no-3d):not(.static):not(.calm-still) .stop--words:not(.is-lit) .words__pull { color: inherit; }
.stop--words.is-lit .words__pull { text-shadow: 0 0 22px rgba(255, 110, 40, 0.5); }
.words__cite { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; padding-top: 12px; border-top: 1px solid rgba(255, 255, 255, 0.14); }
.words__name { font-size: var(--fs-name); font-weight: 700; letter-spacing: -0.03em; }
.words__role { font-size: var(--fs-role); color: var(--soft); }
.t-book { font-size: var(--fs-book); font-weight: var(--w-display); line-height: 0.92; letter-spacing: -0.045em; }
/* W7 (Corn): the section headings arrive as outlines, then fill in solid; every arrival draws them again (is-on is the
   panel's arrival, ui.js). Only the start and the hold are written, so the end is each heading's own colour */
.t-h2, .t-book { -webkit-text-stroke: 0.022em transparent; }
@keyframes tp-ink { 0%, 50% { color: transparent; -webkit-text-stroke-color: rgba(var(--ember), 0.95); } }
.stop.is-on .t-h2, .stop.is-on .t-book { animation: tp-ink 1.1s cubic-bezier(0.23, 1, 0.32, 1) both; }
html.calm .t-h2, html.calm .t-book, html.calm-still .t-h2, html.calm-still .t-book { animation: none; }
.t-lead { font-size: var(--fs-bookline); line-height: 1.5; color: var(--body); max-width: 30em; }
.t-closer { font-size: var(--fs-closer); font-weight: 600; line-height: 1.08; letter-spacing: -0.03em; }
.book__main { display: flex; flex-direction: column; gap: var(--gap); }
.id { display: flex; align-items: center; gap: 14px; font-style: normal; }
.id__text { display: flex; flex-direction: column; gap: 3px; font-size: var(--fs-id); line-height: 1.35; color: var(--body); }
.id__name { font-size: var(--fs-idname); font-weight: 680; color: var(--ink); letter-spacing: -0.02em; }
.id__mail { color: var(--ink); text-underline-offset: 0.15em; }

/* per-stop layouts */
@media (min-aspect-ratio: 1/1) and (min-height: 501px) {
  /* W19 (E8): e2b's column; T10's min(56vw, 58rem) here and for the Jeep took the room their 3D objects had in e2b (0.84
     of e2b's size at 1366 to 1911 wide). The bigger words wrap onto more lines instead */
  .stop--words .stop__in, .stop--card .stop__in { width: min(48vw, 50rem); }
  /* T8: the bigger headline low on the left, the volcanoes above and to its right at e2b's size */
  .stop--hero .stop__in { width: min(52vw, 56rem); justify-content: flex-end; padding-bottom: clamp(36px, 9vh, 110px); }
}
@media (min-aspect-ratio: 1/1) and (max-height: 500px) {
  .count__num { line-height: 0.8; }
  .stop--fit .stop__in { width: 82vw; }
  .stop--proof .stop__in { width: 94vw; display: grid; grid-template-columns: minmax(0, 1.8fr) minmax(0, 1fr); column-gap: 18px; align-content: center; }
  .stop--proof .t-claim, .stop--proof .t-twin, .stop--proof .count { grid-column: 1; }
  .stop--proof .event { grid-column: 2; grid-row: 1 / span 3; align-self: end; flex-direction: column; align-items: flex-start; gap: 10px; }
}

/* the links stand (S20, LS3, LS6): centred under the stand in every layout */
.stop--stand .stop__in { left: 0; right: 0; width: auto; top: auto; bottom: 0; align-items: center; text-align: center;
  padding: 0 var(--gut) calc(clamp(18px, 5vh, 56px) + env(safe-area-inset-bottom)); }
.stop--stand .stop__in::before { inset: auto 0 0 0; height: 100%; background: linear-gradient(to bottom, rgba(5, 5, 7, 0), rgba(5, 5, 7, 0.7) 50%); }
.stand__logo .lockup { height: clamp(34px, 4.4vw, 64px); width: calc(clamp(34px, 4.4vw, 64px) * 1894 / 278); max-width: 86vw; }
.picker { display: flex; align-items: center; gap: 8px; }
.picker__arrow { position: relative; width: 52px; height: 52px; display: grid; place-items: center; border-radius: 50%; cursor: pointer;
  background: rgba(7, 8, 10, 0.5); border: 0; color: var(--ink); }
/* W7: a round button draws its own circle as its stop arrives */
.ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); pointer-events: none; }
.ring circle { fill: none; stroke: rgba(255, 255, 255, 0.22); stroke-width: 1.5; stroke-dasharray: 1; stroke-dashoffset: 1; }
.stop.is-on .ring circle { animation: tp-ring 0.9s cubic-bezier(0.23, 1, 0.32, 1) 0.2s forwards; }
@keyframes tp-ring { to { stroke-dashoffset: 0; } }
html.calm .ring circle, html.calm-still .ring circle, html:not(.js) .ring circle, html.no-3d .ring circle, html.static .ring circle { stroke-dashoffset: 0; animation: none; }
.picker__arrow svg:not(.ring) { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* every item is one width, so a neighbour sits a full item away and never covers the selected link;
   the neighbours peek in at the list's faded edges on wide screens and are hidden on narrow ones */
.picker__list { display: grid; grid-template-areas: "one"; place-items: center; width: clamp(200px, 30vw, 420px); height: 56px; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 24%, #000 76%, transparent); mask-image: linear-gradient(to right, transparent, #000 24%, #000 76%, transparent); }
.picker__item { grid-area: one; width: 150px; display: flex; justify-content: center;
  transition: transform 0.45s var(--ease-out), opacity 0.45s var(--ease-out); opacity: 0; transform: translateX(calc(200% + 16px)); }
.picker__item a { display: inline-flex; align-items: center; justify-content: center; width: 100%; min-height: 48px; padding: 0 12px; font-size: 22px; font-weight: 620;
  text-decoration: none; letter-spacing: -0.01em; border: 1px solid rgba(var(--ember), 0.55); border-radius: 8px; background: rgba(7, 8, 10, 0.55); }
.picker__item.is-sel { opacity: 1; transform: none; }
.picker__item.is-prev { opacity: 0.4; transform: translateX(calc(-100% - 8px)); }
.picker__item.is-next { opacity: 0.4; transform: translateX(calc(100% + 8px)); }
.picker__item.is-far-prev { transform: translateX(calc(-200% - 16px)); }
.picker__item.is-prev a, .picker__item.is-next a { border-color: transparent; background: none; }
@media (max-width: 760px) {
  .picker__list { width: 168px; -webkit-mask-image: none; mask-image: none; }
  .picker__item.is-prev, .picker__item.is-next { opacity: 0; }
}
@media (min-aspect-ratio: 1/1) and (max-height: 500px) { .stand__logo .lockup { height: 30px; width: calc(30px * 1894 / 278); } }

/* the hero's words, each glowing in its volcano's heat (H2, T5): no box and no dot, a soft dark haze behind for
   contrast (T6, A4), the glow growing with --heat; positioned by the world each frame */
.labels { position: absolute; inset: 0; pointer-events: none; }
.label { --heat: 0.32; position: absolute; left: 0; top: 0; font-size: 18px; font-weight: 600; letter-spacing: 0.015em;
  color: #fff4e8; padding: 12px 22px; white-space: nowrap; will-change: transform;
  background: radial-gradient(closest-side, rgba(8, 5, 4, 0.86), rgba(8, 5, 4, 0.74) 55%, rgba(8, 5, 4, 0.3) 80%, rgba(8, 5, 4, 0) 100%);
  text-shadow: 0 0 calc(3px + 12px * var(--heat)) rgba(255, 112, 26, calc(0.15 + 0.75 * var(--heat))),
    0 0 calc(10px + 26px * var(--heat)) rgba(255, 70, 10, calc(0.1 * var(--heat))); }
@media (max-aspect-ratio: 1/1) { .label { font-size: 16px; padding: 10px 16px; } }

/* the slim marker among the seven sections (M6) */
.marker { position: fixed; z-index: 30; right: max(clamp(14px, 2vw, 36px), env(safe-area-inset-right)); top: 50%; transform: translateY(-50%);
  display: flex; flex-direction: column; align-items: flex-end; gap: 10px; pointer-events: none; opacity: 0; transition: opacity 0.6s; }
.marker.is-on { opacity: 1; }
.marker i { display: block; width: 14px; height: 2px; border-radius: 2px; background: rgba(255, 255, 255, 0.3); transition: width 0.4s var(--ease-out), background 0.4s; }
.marker i.is-cur { width: 30px; background: rgb(var(--ember)); box-shadow: 0 0 8px rgba(var(--ember), 0.8); }
@media (max-aspect-ratio: 1/1) { .marker { top: calc(var(--bar-h) + env(safe-area-inset-top) + 18px); transform: none; gap: 7px; } .marker i { width: 10px; } .marker i.is-cur { width: 22px; } }

/* how to move (M7), bold and at every stop (M23, his mock-e3b answer, 2026-10-08: "the encouragement to swipe should be
   more bolder, or more obvious that they need to throughout the website ... trying to think of older people"): white
   words on a dark pill with a lava edge and a moving arrow. At the hero it shows once the world is built; at every stop
   it comes back after 5 s at rest; it goes the moment the world moves (ui.js), and while a stone or the menu is open or
   the page is zoomed. ui.js puts it clear of the stop's words (--cue-y) */
/* M30 to M32 (his answer on mock-e5h, 2026-10-15): the arrow up with a mouse too; a fingertip glides slowly up the arrow's
   track, pauses and comes again ("guide the user to like kinda swipe very gradually, but like it's noticeable"); back after
   --cue-wait at rest (2.5 s, 1.5 s at the last three stops, cue.core.js); "Keep swiping" before the last stop; where no
   centred place is clear, ui.js puts it by the words' left edge or the right gutter (--cue-x), or as the chip (the
   fingertip alone, data-form="chip") */
.hint { position: fixed; z-index: 30; left: 0; top: 0; transform: translate(var(--cue-x, calc(50vw - 50%)), var(--cue-y, calc(100vh - 68px)));
  display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 0 22px 0 16px; border-radius: 999px;
  font: 640 18px/1 var(--font); letter-spacing: 0.01em; color: #fff; white-space: nowrap;
  background: rgba(10, 8, 8, 0.86); border: 1.5px solid rgba(var(--ember), 0.95);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 0 18px rgba(var(--ember), 0.45), 0 6px 18px rgba(0, 0, 0, 0.5);
  margin-bottom: env(safe-area-inset-bottom); /* read by ui.js as the foot's safe area */
  opacity: 0; pointer-events: none; transition: opacity 0.25s; }
.hint.is-on { opacity: 1; transition: opacity 0.6s ease 0.3s; }
.hint.is-on.is-rest { transition-delay: var(--cue-wait, 2.5s); }
body:has(.stop.is-open) .hint.is-on, body:has(.menu:not([hidden])) .hint.is-on, html.zoomed .hint.is-on { opacity: 0; transition: opacity 0.2s; }
.hint__mv { position: relative; display: block; flex: none; width: 22px; height: 36px; }
.hint__track { position: absolute; left: 50%; top: 3px; bottom: 1px; width: 2.6px; margin-left: -1.3px; border-radius: 2px;
  background: linear-gradient(to top, rgba(var(--ember), 0.12), rgba(var(--ember), 0.9)); }
.hint__arrow { position: absolute; left: 0; top: 0; width: 22px; height: 22px; fill: none; stroke: rgb(var(--ember)); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
/* the fingertip: up the track in 1.6 s, eased both ways, a fading trail under it, gone just under the arrowhead (never
   over it), 0.8 s and again */
.hint__tip { position: absolute; left: 50%; top: 0; width: 10px; height: 10px; margin-left: -5px; border-radius: 50%; background: #fff;
  box-shadow: 0 0 6px 2px rgba(var(--ember), 0.9), 0 0 14px rgba(var(--ember), 0.55); opacity: 0; transform: translateY(26px);
  animation: tp-tip 2.4s cubic-bezier(0.45, 0, 0.4, 1) infinite; }
.hint__tip::after { content: ''; position: absolute; left: 50%; top: 6px; width: 4px; height: 14px; margin-left: -2px; border-radius: 2px;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0.6), rgba(var(--ember), 0)); }
@keyframes tp-tip { 0% { transform: translateY(26px); opacity: 0; } 8% { opacity: 1; } 66.7% { transform: translateY(10px); opacity: 1; }
  80%, 100% { transform: translateY(10px); opacity: 0; } }
.hint__go, .hint__keep, .hint__again { display: none; }
.hint[data-word="go"] .hint__go, .hint[data-word="keep"] .hint__keep, .hint[data-word="again"] .hint__again { display: inline; }
.hint[data-form="chip"] { width: 48px; padding: 0; justify-content: center; }
.hint[data-form="chip"] .hint__go, .hint[data-form="chip"] .hint__keep, .hint[data-form="chip"] .hint__again { display: none; }
.hint__coarse { display: none; }
@media (pointer: coarse) { .hint__fine { display: none; } .hint__coarse { display: inline; } }
@media (min-aspect-ratio: 1/1) and (min-height: 501px) { .hint { font-size: 17px; min-height: 44px; } .hint[data-form="chip"] { width: 44px; } }
/* M42 (his pick on the moving mock, 2026-10-17: "Honestly probably just the words. For one."): between the hero and Keep
   swiping the words are back, on a quieter pill than the hero's: 17 px words, 44 px tall, a softer ring and glow on
   lighter glass, the arrow and fingertip a little smaller (ui.js sets data-tone="quiet"; where it has no room the chip
   shows, as before) */
.hint[data-tone="quiet"]:not([data-form="chip"]) { min-height: 44px; padding: 0 18px 0 14px; gap: 9px; font-size: 17px;
  background: rgba(10, 8, 8, 0.65); border-width: 1.25px; border-color: rgba(var(--ember), 0.65);
  box-shadow: 0 0 12px rgba(var(--ember), 0.24), 0 4px 14px rgba(0, 0, 0, 0.35); }
.hint[data-tone="quiet"]:not([data-form="chip"]) .hint__mv { transform: scale(0.9); }
/* SD2: the sound switch, in a corner of every stop (ui.js picks the corner clear of the stop's words, sound.core.js) */
.sound { position: fixed; z-index: 30; left: 0; top: 0; transform: translate3d(var(--snd-x, 16px), var(--snd-y, calc(100vh - 68px)), 0);
  display: flex; align-items: center; gap: 8px; height: 48px; padding: 0 18px 0 13px; border: 0; border-radius: 999px; cursor: pointer;
  background: rgba(7, 8, 10, 0.72); color: var(--ink); font: 600 16px/1 var(--font); transition: opacity 0.3s; }
.sound[hidden] { display: none; }
.sound__icon { width: 22px; height: 22px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.sound__waves, .sound.is-on .sound__mute { display: none; }
.sound.is-on .sound__waves { display: inline; }
.sound.is-on { color: rgb(var(--ember)); }
.sound .ring { display: none; }
/* an upright phone: a round button, its words kept for screen readers, its circle drawn as it first shows (W7) */
@media (max-aspect-ratio: 1/1) {
  .sound { width: 48px; padding: 0; justify-content: center; }
  .sound__word { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .sound .ring { display: block; }
  .sound.is-shown .ring circle { animation: tp-ring 0.9s cubic-bezier(0.23, 1, 0.32, 1) 0.2s forwards; }
}
/* L13: during a flight the switch fades out and takes no touch; it moves to the next corner unseen (ui.js) */
.sound.is-away, body:has(.stop.is-open) .sound, body:has(.menu:not([hidden])) .sound, html.zoomed .sound { opacity: 0; pointer-events: none; }
html:not(.js) .sound, html.no-3d .sound, html.static .sound, html.calm .sound, html.calm-still .sound { display: none; }
/* M22 (his mock-e3b answer, 2026-10-08: pinch zoom freezes the site): the one-tap way out of a zoom, shown only while
   zoomed in, at its normal size at the foot of what the visitor sees (view.js places and scales it) */
.unzoom { position: fixed; z-index: 70; left: 0; top: 0; transform-origin: 0 0; min-height: 52px; padding: 0 24px;
  border-radius: 999px; font: 640 18px/1 var(--font); color: #fff; white-space: nowrap; cursor: pointer;
  background: rgba(10, 8, 8, 0.92); border: 1.5px solid rgb(var(--ember)); box-shadow: 0 0 18px rgba(var(--ember), 0.5); }
.unzoom[hidden] { display: none; }

/* S2b (J5, T9): the event photo big, its line a bold heading. Desktop: the line left, the photo right at about half
   the screen; phone: the photo the full width on top, the line below it */
.t-event { font-size: var(--fs-event); font-weight: 700; line-height: 1.12; letter-spacing: -0.025em; text-wrap: balance; }
.stop--event .event { display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); grid-template-areas: "cap img"; align-items: center; column-gap: clamp(24px, 3vw, 56px); }
.stop--event .event__img { grid-area: img; width: 100%; border-radius: 10px; }
.stop--event .event__cap { grid-area: cap; gap: 16px; color: var(--ink); }
.stop--event .event__mark { width: 120px; }
@media (min-aspect-ratio: 1/1) and (min-height: 501px) { .stop--event .stop__in { width: min(92vw, 112rem); } .stop--event .event__mark { width: clamp(150px, 12vw, 230px); } } /* T11: the mark grows with the line */
/* J8 (his mock-e2 feedback, 2026-10-05: "Picture and text look too crammed here"): on a phone held upright the photo
   edge to edge (e2's max-width held it 32 px short of the right edge), the mark and the line bigger, and the whole stop
   centred in the room under the header */
@media (max-aspect-ratio: 1/1) {
  .stop--event .stop__in { top: calc(var(--bar-h) + env(safe-area-inset-top)); justify-content: center; padding-top: 0;
    padding-bottom: calc(20px + env(safe-area-inset-bottom)); }
  .stop--event .stop__in::before { inset: 0; background: rgba(5, 5, 7, 0.6); }
  .stop--event .event { grid-template-columns: 1fr; grid-template-areas: "img" "cap"; row-gap: 26px; }
  .stop--event .event__img { width: 100vw; max-width: none; margin: 0 0 0 calc(-1 * max(var(--gut), env(safe-area-inset-left))); border-radius: 0; }
  .stop--event .event__cap { gap: 18px; }
  .stop--event .event__mark { width: 150px; }
}
@media (min-aspect-ratio: 1/1) and (max-height: 500px) { .stop--event .stop__in { width: 94vw; } }
/* J10 (his brother's feedback, passed on by Noah, 2026-10-07): the fire that reveals the event photo as the camera
   arrives, over the photo and round it; reveal.js places and sizes it and cuts the photo below the burning edge while it
   runs. Hidden at rest, and wherever the world does not run: the photo whole */
.stop--event .event { position: relative; }
.event__fire { position: absolute; left: 0; top: 0; display: none; pointer-events: none; }
html:not(.js) .event__fire, html.no-3d .event__fire, html.static .event__fire, html.calm .event__fire, html.calm-still .event__fire { display: none !important; }
html:not(.js) .event__img, html.no-3d .event__img, html.static .event__img, html.calm .event__img, html.calm-still .event__img { clip-path: none !important; -webkit-clip-path: none !important; }
/* S21: the logo in the night sky, centred; the sparks land on this box (sparklogo.js), then it shows (--formed) */
.stop--sky .stop__in { inset: 0; width: auto; top: 0; bottom: 0; align-items: center; justify-content: center; padding: 0; }
.stop--sky .stop__in::before { display: none; }
.stop--sky.is-on .stop__in { pointer-events: none; } /* nothing to click: the world under it keeps the pointer */
.sky__logo { opacity: var(--formed, 0); }
.sky__logo .lockup { width: min(72vw, 880px); height: calc(min(72vw, 880px) * 278 / 1894); }

/* the menu (M4, K1) */
.menu { position: fixed; inset: 0; z-index: 60; background: rgba(6, 6, 8, 0.96); overflow: auto; overscroll-behavior: contain; touch-action: pan-y; }
.menu[hidden] { display: none; }
.menu__in { min-height: 100%; display: flex; flex-direction: column; justify-content: center; gap: clamp(24px, 5vh, 56px);
  padding: calc(var(--bar-h) + 12px) max(clamp(24px, 6vw, 120px), env(safe-area-inset-right)) 40px max(clamp(24px, 6vw, 120px), env(safe-area-inset-left)); }
.menu__close { position: absolute; top: calc(env(safe-area-inset-top) + 14px); right: max(clamp(16px, 2.6vw, 48px), env(safe-area-inset-right));
  font: 560 16px/1 var(--font); color: var(--ink); background: none; border: 1px solid rgba(255, 255, 255, 0.24); border-radius: 8px; min-height: 44px; padding: 0 16px; cursor: pointer; }
.menu__sections { display: flex; flex-direction: column; gap: clamp(6px, 1.2vh, 14px); }
.menu__sections a { font-size: clamp(30px, min(4.4vw, 6.2vh), 60px); font-weight: var(--w-display); letter-spacing: -0.03em; line-height: 1.12; text-decoration: none; }
.menu__sections a:hover, .menu__sections a[aria-current] { color: rgb(var(--ember)); }
.menu__links { display: flex; flex-wrap: wrap; gap: 10px 26px; font-size: 18px; color: var(--body); }
.menu__links a { text-underline-offset: 0.2em; }
/* MN1 (Noah, 2026-10-05: "Make the email in the menu more obvious", "Send an Email"): a button that opens a draft, the
   address under it for people without an email app, Privacy a small link */
.menu__mail { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.btn--mail { min-height: 56px; padding: 0 28px; font-size: 20px; }
.menu__addr { font-size: 16px; color: var(--soft); }
.menu__privacy a { font-size: 16px; color: var(--soft); }

/* no 3D, no script, or the page's graphics flag said it gave up: one plain readable column (LD6) */
html:not(.js) .world, html.no-3d .world, html:not(.js) .loader, html.no-3d .loader,
html:not(.js) .marker, html.no-3d .marker, html:not(.js) .hint, html.no-3d .hint,
html:not(.js) .bar__menu { display: none; }
html:not(.js) .stops, html.no-3d .stops { max-width: 50rem; margin: 0 auto; padding: calc(var(--bar-h) + 40px) var(--gut) 80px; }
html:not(.js) .stop, html.no-3d .stop { position: static; opacity: 1; pointer-events: auto; margin-bottom: 96px; }
html:not(.js) .stop__in, html.no-3d .stop__in { position: static; width: auto; padding: 0; pointer-events: auto; }
html:not(.js) .stop__in::before, html.no-3d .stop__in::before { display: none; }
html:not(.js) .labels, html.no-3d .labels { position: static; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
html:not(.js) .label, html.no-3d .label { position: static; }
html:not(.js) .picker__list, html.no-3d .picker__list { display: flex; flex-wrap: wrap; height: auto; width: auto; gap: 10px; -webkit-mask-image: none; mask-image: none; }
html:not(.js) .picker__item, html.no-3d .picker__item { opacity: 1; transform: none; width: auto; }
html:not(.js) .picker__arrow, html.no-3d .picker__arrow { display: none; }
/* ST6: with no stone to open, each system's description shows in the column and the buttons go */
html:not(.js) .card--build .card__d, html.no-3d .card--build .card__d { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal; }
html:not(.js) .more, html.no-3d .more { display: none; }
html:not(.js) .bar, html.no-3d .bar { background: rgba(7, 8, 10, 0.92); }

/* a single stop on its own, for layout checks (?static=N) */
html.static .world, html.static .loader, html.static .hint, html.static .marker { display: none; }
html.static .stop.is-static { opacity: 1; }
html.static .stop.is-static .stop__in, html.static .stop.is-static .labels { pointer-events: auto; }
html.static body { background: radial-gradient(120% 90% at 70% 40%, #2a0d07, #07080a 70%); }

@media (prefers-reduced-motion: reduce) { .hint__tip { animation: none; opacity: 1; transform: translateY(18px); } .picker__item, .words__pull { transition: none; } .more, .stop.is-open .card--build .card__d, .stop.is-open .close { animation: none; }
  .stop.is-on .t-h2, .stop.is-on .t-book { animation: none; } .ring circle { stroke-dashoffset: 0; animation: none; } .sound { transition: none; } }

/* B7, B8 (Noah, 2026-10-05: "NOAHS WHOLE picture that has the background removed should take up most of the space",
   "would my face and body show up twice?"): his one portrait. Desktop: on the right, 90% of the window's height, standing
   on the podium at the bottom edge; the words on the left; his name and details as a caption over the photo's lower
   part. Phone: the full width at the top, the words over its lower part on a dark fade. Placed each frame on the podium
   (--px, --py: the podium's top centre in css px, set by portrait.js); --foot is how far down the picture his feet
   meet it (the cut-out fades out below his hands) */
.stop--book { --pw: calc(90vh * 1800 / 2587); --ph: 90vh; --foot: 0.93; }
.portrait { position: absolute; left: 0; top: 0; margin: 0; width: var(--pw); height: var(--ph); pointer-events: none; will-change: transform;
  transform: translate3d(calc(var(--px, 74vw) - 50%), calc(var(--py, 96vh) - var(--foot) * 100%), 0); }
.portrait__img, .portrait__holo, .portrait__rim, .portrait__rim i { position: absolute; inset: 0; width: 100%; height: 100%; }
.portrait__img { opacity: var(--real, 1); }
.portrait__holo { opacity: var(--holo, 0); display: var(--holo-on, none); }
/* the lava light only rims his outline from behind: an ember glow in his own shape under the photo. The blur sits on
   the outer box and the mask on the inner one, since a box's own mask is applied after its own blur */
.portrait__rim { --rim-blur: 14px; --rim-in: rgba(255, 96, 24, 0.95); --rim-out: rgba(255, 60, 10, 0.6);
  filter: blur(var(--rim-blur)); transform: scale(1.025); transform-origin: 50% 92%; opacity: calc(0.75 * var(--real, 1)); }
.portrait__rim i { background: radial-gradient(60% 50% at 50% 70%, var(--rim-in), var(--rim-out));
  /* the mask is his photo as the browser picked it (portrait.js sets --rim-mask from the img's currentSrc; O3b: e2
     downloaded a second copy for the mask); until it is set the rim shows nothing */
  -webkit-mask: var(--rim-mask, linear-gradient(transparent, transparent)) center / contain no-repeat;
  mask: var(--rim-mask, linear-gradient(transparent, transparent)) center / contain no-repeat; }
/* M41 (his pick, 2026-10-16: A): once portrait.js has drawn this glow as a picture at its own size (bake.js; --rim-img,
   with --rim-pad of room round it for the blur), the picture shows and nothing here is blurred or cut out live */
.portrait__rim.is-baked { filter: none; }
.portrait__rim.is-baked i { inset: calc(-1 * var(--rim-pad, 0px)); width: auto; height: auto; -webkit-mask: none; mask: none;
  background: var(--rim-img) 0 0 / 100% 100% no-repeat; }
.portrait__embers { position: absolute; inset: 0; }
.portrait__embers i { position: absolute; bottom: 6%; width: 4px; height: 4px; border-radius: 50%; background: #ffb066;
  box-shadow: 0 0 8px 2px rgba(255, 110, 30, 0.8); opacity: 0; animation: tp-rise 5.5s linear infinite; animation-play-state: paused; }
.stop--book.is-on .portrait__embers i { animation-play-state: running; }
.portrait__embers i:nth-child(1) { left: 6%; --dx: 26px; animation-delay: -0.2s; }
.portrait__embers i:nth-child(2) { left: 14%; --dx: -18px; animation-delay: -3.1s; }
.portrait__embers i:nth-child(3) { left: 22%; --dx: 34px; animation-delay: -1.4s; width: 3px; height: 3px; }
.portrait__embers i:nth-child(4) { left: 31%; --dx: -30px; animation-delay: -4.6s; }
.portrait__embers i:nth-child(5) { left: 39%; --dx: 14px; animation-delay: -2.3s; width: 3px; height: 3px; }
.portrait__embers i:nth-child(6) { left: 47%; --dx: -22px; animation-delay: -0.9s; }
.portrait__embers i:nth-child(7) { left: 55%; --dx: 28px; animation-delay: -3.8s; width: 5px; height: 5px; }
.portrait__embers i:nth-child(8) { left: 63%; --dx: -12px; animation-delay: -1.9s; }
.portrait__embers i:nth-child(9) { left: 71%; --dx: 22px; animation-delay: -5.0s; width: 3px; height: 3px; }
.portrait__embers i:nth-child(10) { left: 79%; --dx: -26px; animation-delay: -2.7s; }
.portrait__embers i:nth-child(11) { left: 87%; --dx: 16px; animation-delay: -0.5s; width: 3px; height: 3px; }
.portrait__embers i:nth-child(12) { left: 94%; --dx: -20px; animation-delay: -4.2s; }
@keyframes tp-rise { 0% { transform: translate3d(0, 0, 0); opacity: 0; } 12% { opacity: 0.9; } 100% { transform: translate3d(var(--dx, 20px), -70vh, 0); opacity: 0; } }
.stop--book .book__main { display: flex; flex-direction: column; gap: var(--gap); }
.stop--book .t-book { white-space: nowrap; }
.id__text { text-shadow: 0 1px 12px rgba(5, 5, 7, 0.9); }
/* desktop and a phone held sideways: the words on the left; the caption over the photo's lower left, its foot just
   above the podium, on a soft dark backing: a blurred dark rounded box, even behind every line (E8: the radial fade
   left his name at 2.6 to 2.8 against 3.0 over his shirt at every landscape size, and the degree line at 2.0 at 2560) */
@media (min-aspect-ratio: 1/1) {
  .stop--book .stop__in { width: min(46vw, 48rem); }
  .stop--book .id { position: fixed; left: 0; top: 0; max-width: calc(100vw - 24px - var(--px, 74vw) + 0.42 * var(--pw));
    transform: translate3d(calc(var(--px, 74vw) - 0.42 * var(--pw)), calc(var(--py, 96vh) - 0.02 * var(--ph) - 100%), 0); }
  .stop--book .id { --idb-t: -16px; --idb-r: -24px; --idb-b: -12px; --idb-l: -20px; --idb-color: rgba(5, 5, 7, 0.82); --idb-radius: 28px; --idb-blur: 22px; }
  .stop--book .id::before { content: ""; position: absolute; z-index: -1; inset: var(--idb-t) var(--idb-r) var(--idb-b) var(--idb-l); pointer-events: none;
    background: var(--idb-color); border-radius: var(--idb-radius); filter: blur(var(--idb-blur)); }
}
@media (min-aspect-ratio: 1/1) and (max-height: 500px) { .stop--book .stop__in { width: 52vw; } .stop--book .btn--big { min-height: 52px; font-size: 19px; } }
/* phone held upright (B9, his mock-e2 feedback, 2026-10-05: "BOOK A CALL SECTION DOES NOT WORK AND LOOK GOOD ON PHONE.
   FIX.", and his note on the preview: "noahs whole body should be shown on the phone version, just like the desktop
   version", "make some text smaller"): D7's order, the words on top and smaller; he stands whole and centred on the
   podium under them, as big as the room left allows (ui.js bookBox sets --pw and --ph); his name and details a caption
   in three rows over his legs (the place and the address side by side), as the desktop caption sits over his lower part.
   B10 (his mock-e2c feedback, 2026-10-06: the degree and the school "all in one like line", "make the bottom text a little
   bit smaller besides like my name which is bolded"; his pick on the previews: e2c's caption as it looks on the 430 wide
   phone, "words are all in one line where it makes sense. Like this one is shown"): e2c's three rows at the left, the
   degree on one line on every phone, the details shrinking only as far as that needs (13.9 px at 375 wide, 14.5 at 390,
   16 from 430; the line is 25.31 times its size wide); his bold name unchanged at 19 px */
@media (max-aspect-ratio: 1/1) {
  .stop--book { --pw: 62vw; --ph: calc(62vw * 2587 / 1800);
    --fs-book: 48px; --fs-bookline: 18px; --fs-closer: 22px; --fs-id: clamp(13px, calc((100vw - 24px) / 25.31), 16px); --fs-idname: 19px; } /* T3 and T4's phone floors kept: 48 and 18 */
  .stop--book .stop__in { z-index: 1; gap: 6px; top: calc(var(--bar-h) + env(safe-area-inset-top)); bottom: auto;
    justify-content: flex-start; padding-top: 8px; padding-bottom: 0; }
  .stop--book .stop__in::before { inset: -20px 0 -36px 0; background: linear-gradient(to bottom, rgba(5, 5, 7, 0.62), rgba(5, 5, 7, 0.62) 72%, rgba(5, 5, 7, 0)); }
  .stop--book .book__main { gap: 6px; }
  .stop--book .t-lead { padding-right: 28px; line-height: 1.42; } /* clear of the section marker */
  .stop--book .t-closer { line-height: 1.04; }
  .stop--book .btn--big { min-height: 48px; font-size: 18px; }
  .stop--book .id { position: fixed; left: 0; top: 0; max-width: calc(100vw - var(--gut) - 6px); /* the place and the address side by side on a 375 phone too */
    transform: translate3d(max(var(--gut), env(safe-area-inset-left)), calc(var(--py, 96vh) - 0.02 * var(--ph) - 100%), 0); }
  .stop--book .id { --idb-t: -12px; --idb-r: -18px; --idb-b: -10px; --idb-l: -14px; --idb-color: rgba(5, 5, 7, 0.8); --idb-radius: 22px; --idb-blur: 18px; }
  .stop--book .id::before { content: ""; position: absolute; z-index: -1; inset: var(--idb-t) var(--idb-r) var(--idb-b) var(--idb-l); pointer-events: none;
    background: var(--idb-color); border-radius: var(--idb-radius); filter: blur(var(--idb-blur)); }
  .stop--book .id__text { flex-direction: row; flex-wrap: wrap; column-gap: 10px; row-gap: 0; line-height: 1.3; }
  .stop--book .id__name, .stop--book .id__degree { flex-basis: 100%; }
  .stop--book .id__degree { white-space: nowrap; } /* B10: one line on every phone */
}
/* M41 (his pick, 2026-10-16: A): once portrait.js has drawn the caption's backing as a picture at its own size (bake.js;
   --id-back, with --id-pad of room round it for the blur), the picture shows and nothing is blurred live */
.stop--book .id.is-baked::before { inset: calc(var(--idb-t) - var(--id-pad, 0px)) calc(var(--idb-r) - var(--id-pad, 0px))
    calc(var(--idb-b) - var(--id-pad, 0px)) calc(var(--idb-l) - var(--id-pad, 0px));
  background: var(--id-back) 0 0 / 100% 100% no-repeat; border-radius: 0; filter: none; }
@media (prefers-reduced-motion: reduce) { .portrait__embers { display: none; } }
/* no 3D or no script: the photo sits in the column above the words */
html:not(.js) .portrait, html.no-3d .portrait { position: static; transform: none; width: min(100%, 24rem); height: auto; margin: 0 0 24px; }
html:not(.js) .portrait__img, html.no-3d .portrait__img { position: static; width: 100%; height: auto; opacity: 1; }
html:not(.js) .portrait__rim, html.no-3d .portrait__rim, html:not(.js) .portrait__holo, html.no-3d .portrait__holo,
html:not(.js) .portrait__embers, html.no-3d .portrait__embers { display: none; }
html:not(.js) .stop--book .id, html.no-3d .stop--book .id { position: static; transform: none; max-width: none; }

/* V5, V6 (his mock-e2b feedback, 2026-10-05: hydrotrax.ca as "a short looping capture of the site itself", sharp and
   large): the site's own loop, page content standing on its stone (main.js puts its foot on the stone's top each frame;
   ui.js sizes it, --hw and --hh), above the world and under the words, so out of its glow, haze and grain. A wide screen
   shows the whole card; an upright phone shows its photo, the capture's top 1248 of 1520 rows (the card's own words
   would be too small to read there) */
/* V8 (his mock-e3 feedback, 2026-10-08, his pick "Real colours, rock frame"): the site keeps its own colours, set in a
   thin frame of dark lava rock (--hf thick; ui.js leaves room for it), its rim lit warm by the lava below, brightest at
   its foot. The navy edge and the ember outline are gone; the video keeps its own pixels */
.hydro { --hf: clamp(8px, 0.9vw, 14px); position: absolute; left: 0; top: 0; margin: 0; padding: var(--hf);
  width: calc(var(--hw, 40vw) + 2 * var(--hf)); height: calc(var(--hh, calc(40vw * 1520 / 1664)) + 2 * var(--hf));
  pointer-events: none; border-radius: 20px; overflow: hidden; will-change: transform;
  background: linear-gradient(to top, rgba(255, 112, 36, 0.42), rgba(255, 112, 36, 0) 34%), url(assets/tex/rock-albedo.webp) center / 220px #0d0907;
  box-shadow: inset 0 1px 0 rgba(255, 190, 140, 0.18), inset 0 -2px 8px rgba(255, 120, 40, 0.5), 0 22px 60px -14px rgba(var(--ember), 0.38); }
.stop.is-on .hydro { pointer-events: auto; }
.hydro__video { display: block; width: 100%; height: 100%; border-radius: 10px; object-fit: cover; object-position: 50% 0; background: #0d0907; }
@media (min-aspect-ratio: 1/1) and (max-height: 500px) { .stop--hydro .stop__in { width: 50vw; } } /* T6: room beside the words for the loop */
/* no script, the plain column or one stop on its own: no stone to stand on, so the card's words carry it (the calm
   version shows its poster on the stone in its still, F14) */
html:not(.js) .hydro, html.no-3d .hydro, html.static .hydro { display: none; }

/* A1, LD7 (e3 F14): the calm version (calm.js), for people who turn off motion and visits where the 3D cannot start. The
   world's canvas and loading line give way to a fixed layer of two stills under the words: each stop's frame of the world
   covering the screen, its subject where the words leave room (object-position, set per still), one fading into the next
   as the words do. An open stone dims its still and its words fade in (no dive, no slide); a quote's key line shows lit;
   his photo, hydrotrax.ca's poster and the sky logo stand where their still has them (calm.js places them) */
html.calm-still .world, html.calm-still .loader { display: none; }
.still { position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; background: var(--canvas); }
.still__img { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; object-fit: cover; opacity: 0; }
.still::after { content: ""; position: absolute; inset: 0; z-index: 2; background: #050507; opacity: 0; transition: opacity 0.35s ease; }
.still.is-dim::after { opacity: 0.4; }
html.calm-still .stop--words .words__pull { text-shadow: 0 0 22px rgba(255, 110, 40, 0.5); }
html.calm-still .stop.is-open .card--build .card__d { animation: calm-in 0.35s ease-out both; }
html.calm-still .stop.is-open .close { animation: calm-in 0.3s ease-out 0.1s both; }
@keyframes calm-in { from { opacity: 0; } }
html.calm-still .sky__logo { opacity: 1; transform: translate3d(var(--sky-x, 0px), var(--sky-y, 0px), 0); }
html.calm-still .sky__logo .lockup { width: var(--sky-w, min(72vw, 880px)); height: calc(var(--sky-w, min(72vw, 880px)) * 278 / 1894); }
