.series {
  --gold: #f0eadc;
  --gold-deep: #cbbfa6;
  --cta: #db714d;
  --ink: #f3ead4;
  --ink-dim: #cbbfa6;
  --void: #122926;
  position: relative;
  /* One teal-to-void gradient runs down the page behind the title screen and the stories
     screen (stops in viewport heights), then settles into the void colour. It lives here,
     under the sections, so the fixed bubbles layer shows through over both screens. */
  /* Void only in the gaps a hold-freeze leaves; the teal-to-void wash is on
     .series-wash so it can sit still while the landing is stuck. */
  background: var(--void);
  color: var(--ink);
  min-height: 100vh;
  overflow-x: clip; /* stories nudged sideways must not create a horizontal scrollbar */
  overflow-y: clip; /* frozen wash translate must not add a gap past the footer */
}

/* Full-page teal-to-void wash. Translated during the three-card hold so the
   water stays put (no solid slab — the same gradient, just not sliding). */
.series-wash {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    linear-gradient(
      180deg,
      #42756d 0,
      #356660 30vh,
      #326862 70vh,
      #2c5651 110vh,
      #234843 150vh,
      #1a3a36 195vh,
      #122926 235vh,
      #122926 100%
    );
  background-repeat: no-repeat;
}

/* Light shafts falling from a water surface just above the page. Each ray is a soft
   vertical band (gradient across, faded out downwards by the layer's mask) that leans from
   a pivot above the top edge and sways slowly; brightness breathes on the same rhythm.
   Transform/opacity only, so it stays cheap. Sits under the text (z 3) and bubbles (z 2). */
.rays {
  position: absolute;
  inset: 0 0 auto;
  /* Long enough that the faint tips of the rays just reach the top of the stories screen,
     around the CTA line, before dying out. The vertical fade is baked into each ray's own
     mask (below), so this layer needs no mask, clipping or blend mode of its own: plain
     light-on-dark transparency looks the same as a screen blend here and is far cheaper. */
  height: 170vh;
  z-index: 1;
  pointer-events: none;
}

.ray {
  position: absolute;
  top: -22vh;
  left: var(--x);
  width: var(--w);
  height: 215vh;
  margin-left: calc(var(--w) / -2);
  transform-origin: 50% 0;
  overflow: hidden;
  /* A flat wash, shaped by one mask: soft across the whole width (a wash, not a beam) and
     fading out down its length (solid for the title screen, dying away by the CTA line).
     The mask also shapes the shimmer layer inside it. */
  background: rgba(224, 248, 238, 0.6);
  -webkit-mask-image:
    linear-gradient(90deg, transparent, #000 50%, transparent),
    linear-gradient(180deg, #000 0, #000 22%, rgba(0, 0, 0, 0.6) 46%, rgba(0, 0, 0, 0.18) 72%, transparent 89%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(90deg, transparent, #000 50%, transparent),
    linear-gradient(180deg, #000 0, #000 22%, rgba(0, 0, 0, 0.6) 46%, rgba(0, 0, 0, 0.18) 72%, transparent 89%);
  mask-composite: intersect;
  opacity: var(--glow);
  transform: rotate(var(--tilt));
  animation: ray-sway var(--dur) ease-in-out var(--delay) infinite alternate;
  contain: paint;
}

/* The shimmer: brighter bands that drift sideways across the shaft, the way light under a
   rippling surface wanders back and forth. The layer is wider than the shaft and eases
   left and right within it, so the bands are always present and never jump. */
.ray::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -50%;
  width: 150%;
  background: repeating-linear-gradient(
    90deg,
    transparent 0,
    rgba(255, 255, 255, 0.1) 8%,
    rgba(255, 255, 255, 0.5) 16.667%,
    rgba(255, 255, 255, 0.1) 25%,
    transparent 33.333%
  );
  animation: ray-shimmer var(--shimmer) ease-in-out var(--delay) infinite alternate;
}

@keyframes ray-sway {
  0% {
    transform: rotate(calc(var(--tilt) - 2.2deg)) translateX(-1.2vw) scaleX(0.82);
    opacity: calc(var(--glow) * 0.3);
  }
  45% {
    opacity: var(--glow);
  }
  100% {
    transform: rotate(calc(var(--tilt) + 2.2deg)) translateX(1.2vw) scaleX(1.18);
    opacity: calc(var(--glow) * 0.45);
  }
}

@keyframes ray-shimmer {
  from {
    transform: translateX(-16%);
  }
  to {
    transform: translateX(16%);
  }
}

/* Landing: series label, title, standfirst and a few paragraphs of introduction. It fills
   the first screen; the text may run past it. */
.intro {
  position: relative;
  z-index: 3;
  display: flex;
  justify-content: center;
  padding: 0 1.5rem 3rem;
}

.intro__copy {
  width: min(100%, 72rem);
  text-align: center;
}

/* The title group sits a little above the middle of the first screen, with the article
   text starting beneath it while still on screen. */
.intro__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: clamp(3.25rem, 8.8vh, 6rem) 0 0;
}

.intro__body {
  margin: 3.2rem auto 0;
  max-width: 38rem;
  text-align: left;
}

.intro__body p {
  margin: 0 0 1.35em;
  font-family: "Merriweather", var(--font-serif);
  font-weight: 400;
  font-size: 1.02rem;
  line-height: 1.75;
  color: var(--ink);
}

.intro__body p:last-child {
  margin-bottom: 0;
}

/* Drop cap: sit flush with line 1 cap-height → line 2 baseline (all sizes). */
.intro__body p:first-child::first-letter {
  font-family: "Noe Display", var(--font-display);
  font-weight: 700;
  color: var(--gold);
  -webkit-initial-letter: 2;
  initial-letter: 2;
  margin-right: 0.4em;
  padding: 0;
}

@supports not (initial-letter: 2) {
  .intro__body p:first-child::first-letter {
    float: left;
    /* 2 × body line-height (1.75em) — glyph box matches two wrapped lines. */
    font-size: 3.5em;
    line-height: 1;
    margin: 0 0.4em 0 0;
    /* Noe’s tall ascent — sink so the top meets the first line, bottom the second. */
    padding-top: 0.12em;
  }
}

.intro__kicker,
.kicker {
  font-family: "Pangram", var(--font-sans);
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  margin: 0 0 0.85rem;
}

/* "Quanta Magazine presents": word shares Magazine’s x-height band. */
.intro__presents {
  --logo-w: clamp(190px, 20vw, 270px);
  --logo-h: calc(var(--logo-w) * 49.5 / 353.5);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  margin: 0 0 clamp(4.5rem, 12vh, 8.5rem);
}

.intro__logo {
  display: block;
  width: var(--logo-w);
  line-height: 0;
  color: inherit;
  text-decoration: none;
}

.intro__logo:hover,
.intro__logo:focus {
  color: inherit;
  text-decoration: none;
  opacity: 1;
}

.intro__logo .qm-logo {
  display: block;
  width: 100%;
  height: auto;
}

body:not(.hub-2a) .qm-logo--presents .qm-logo__mark,
body:not(.hub-2a) .qm-logo--presents .qm-logo__quanta {
  fill: #f28b63; /* brighter than the CTA orange for contrast on the teal */
}

.qm-logo--presents .qm-logo__magazine {
  fill: #fff;
}

/*
  Magazine’s getBBox is ~y 19–34.5 in a 49.5-tall viewBox (mid ≈ 54% down).
  Flex-centering the word on the full logo (mark + type) lands a hair high;
  nudge down onto that band. Size ≈ Magazine letter height.
*/
.intro__presents-word {
  font-family: "Merriweather", var(--font-serif);
  font-size: calc(var(--logo-h) * 0.42);
  letter-spacing: 0.06em;
  line-height: 1;
  color: #fff;
  /* Seat the Merriweather baseline on Magazine’s (bbox includes the g descender). */
  transform: translateY(calc(var(--logo-h) * 0.08));
}

.intro__kicker {
  color: #7ed9c8;
  font-size: clamp(0.78rem, 1.05vw, 0.95rem);
  letter-spacing: 0.3em;
  margin-bottom: 2.2rem;
}

.kicker {
  color: var(--cta);
}

.intro__title {
  font-family: "Noe Display", var(--font-display);
  font-weight: 700;
  font-size: clamp(4.2rem, 11.5vw, 10.5rem);
  line-height: 1.06;
  letter-spacing: -0.01em;
  margin: 0 0 1.8rem;
  color: var(--gold);
  /* The <br> carries the line break. Extra source newlines must not become blank lines. */
  /* A faint lift off the teal: a tight, slightly darker green-blue shadow. */
  --title-shadow: rgba(8, 14, 44, 0.38);
  text-shadow: 0.012em 0.018em 0.028em var(--title-shadow);
}

/* The "i" in Life: a dotless i with a diamond star where the dot would be.
   Noe Display Bold metrics: ascent 0.75em, the i's dot spans 0.55–0.70em above the baseline,
   the stem is 0.28em wide on a 0.31em advance. With line-height 1.06 the baseline sits
   0.78em below the top of the span's box. Letter + star share one drop-shadow so the
   diamond does not cast a second lift onto the stem. */
.star-i {
  position: relative;
  display: inline-block;
  width: 0.31em; /* the glyph's advance, so percentages below are measured on the letter */
  text-shadow: none;
  filter: drop-shadow(0.012em 0.018em 0.028em var(--title-shadow));
}

/* Anchor: the big star's bottom point (x = 42% of the drawing, y = its bottom edge) sits on
   the centre of the i's stem (52% across the advance), just touching the top of the stem
   (x-height is 0.495em above the baseline, i.e. 0.775em above the bottom of the box). */
.star-i__mark {
  position: absolute;
  left: 52%;
  bottom: 0.76em;
  width: 0.4em;
  height: 0.4em;
  transform: translateX(-42%);
  color: var(--gold);
}

.intro__dek {
  margin: 0 auto;
  max-width: 42rem;
  font-family: "Merriweather", var(--font-serif);
  font-size: clamp(1.1rem, 1.5vw, 1.35rem);
  line-height: 1.65;
  color: var(--hub2a-dek, #a9e6da);
}

/* Extra scroll while the three stories stay on screen, so the landing can be
   sat with before anything leaves. */
.stories-hold {
  position: relative;
  height: 100vh;
}

.stories-sticky {
  position: sticky;
  top: var(--header-h);
  z-index: 3;
  min-height: calc(100vh - var(--header-h));
  display: flex;
  justify-content: center;
  /* Share of the window kept clear on each side so the fitted cluster does not
     pin the side cards to the edge on laptops. */
  --stories-gutter: 0.055;
  padding-inline: calc(100vw * var(--stories-gutter));
}

.stories-fit {
  width: 100%;
  min-width: 0;
  container-type: inline-size;
  container-name: stories;
}

/* The second screen: the three stories, large, vertically centred.
   Laid out in the real window width — no CSS zoom. Safari cannot report
   usable boxes for a zoomed 1880px block, and that was shoving the shared
   3D window off to the side. */
.stories {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: auto minmax(0, 1fr);
  column-gap: clamp(1.2rem, 2.4cqi, 3rem);
  align-items: center;
  min-height: calc(100vh - var(--header-h));
  width: 100%;
  max-width: 1880px;
  margin: 0 auto;
  /* Even top/bottom air; bottom stays just enough for float wobble clearance. */
  padding:
    clamp(1.25rem, 4.5vh, 3.75rem)
    clamp(1.5rem, 5cqi, 6rem)
    clamp(1.75rem, 5.5vh, 4.25rem);
  --stories-zoom: 1;
  --nudge: clamp(1.5rem, 5.5cqi, 6.5rem);
}

/* Invitation line across the top of the stories screen, with a chevron that nods downward.
   Fades in with the section; the three stories slide up beneath it. */
.stories__cta {
  grid-column: 1 / -1;
  align-self: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.7em;
  margin: clamp(0.75rem, 4.2vh, 3.25rem) 0 clamp(0.5rem, 1.6vh, 1.25rem);
  font-family: "Merriweather", var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: 1.2rem;
  letter-spacing: 0.01em;
  color: #7ed9c8;
  opacity: 0;
  transition: opacity 1.2s ease-out;
}

.stories.is-revealed .stories__cta {
  opacity: 1;
}

.stories__chevron {
  width: 1.6em;
  height: 1.6em;
  flex-shrink: 0;
  animation: chevron-nod 1.8s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

@keyframes chevron-nod {
  0%, 100% { transform: translateY(-0.1em); }
  50% { transform: translateY(0.18em); }
}

/* Entrance: each story waits below the viewport, and when the section scrolls into view
   (`.is-revealed`, set from JS) slides up into place, one after another in the order set by
   --entrance-delay. Afterwards the whole card — picture and text together — floats gently,
   each on its own beat. */
.story {
  display: flex;
  justify-content: center;
  text-align: left;
  padding: 1.5rem 1.25rem 0.5rem;
  transform: translateY(95vh);
  opacity: 0;
  transition:
    transform 3.6s cubic-bezier(0.3, 0.9, 0.3, 1) var(--entrance-delay, 0s),
    opacity 2s ease-out var(--entrance-delay, 0s);
}

.stories:not(.is-revealed) .story {
  will-change: transform, opacity;
}

/* Resting ("hold") positions are deliberately uneven: --hold lifts or drops each story,
   --hold-x (a share of --nudge) pushes it sideways. */
.stories.is-revealed .story {
  transform: translate(calc(var(--hold-x, 0) * var(--nudge)), var(--hold, 0px));
  opacity: 1;
}

/* Set briefly from JS while the stories are parked back below the fold: no reverse slide. */
.stories.is-parked .story {
  transition: none;
}

.story__inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: min(100%, 29rem);
  /* Suspended in water: a slow, deep rise and fall that turns around very gradually
     (sine-like easing), with a hint of sideways drift and tilt. */
  animation: story-float 22s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  animation-delay: var(--float-delay, 0s);
}

/* Two bobs per cycle, one very slight sway: the card traces a slow, narrow figure-eight.
   Keep the amplitude modest so the lowest hold (liposome) stays above the viewport pad. */
@keyframes story-float {
  0%, 100% { transform: translate(0, 9px) rotate(-0.55deg); }
  25% { transform: translate(2px, -10px) rotate(0.55deg); }
  50% { transform: translate(0, 9px) rotate(-0.55deg); }
  75% { transform: translate(-2px, -10px) rotate(0.55deg); }
}

/* The picture drifts a little more than the text beneath it, on its own slower beat. */
.story__visual {
  animation: visual-float 8.6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  animation-delay: calc(var(--float-delay, 0s) - 1.7s);
}

@keyframes visual-float {
  0%, 100% { transform: translateY(5px); }
  50% { transform: translateY(-6px); }
}

.story__visual {
  position: relative;
  display: block;
  width: 100%;
  height: min(440px, 48vh);
  margin-bottom: 2.4rem;
}

.story__visual img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  transform-origin: center;
}

/* Phone §3: static PNG stills. Desktop / iPad keep live .story__figure WebGL. */
.story__still {
  display: none;
  pointer-events: none;
}

/* Phone still drift — light bob + ±5° rock (CSS compositor only). */
@keyframes still-drift {
  0%,
  100% {
    translate: 0 0;
    rotate: -5deg;
  }
  50% {
    translate: 0 -1.2%;
    rotate: 5deg;
  }
}

@media (max-width: 767px) {
  .story__still {
    display: block;
    z-index: 1;
    will-change: translate, rotate;
    transform-origin: center center;
    animation: still-drift 6.5s ease-in-out infinite;
  }

  /* Stagger so the three cards don’t bob in sync. */
  .story:nth-of-type(2) .story__still {
    animation-duration: 7.2s;
    animation-delay: -2.1s;
  }

  .story:nth-of-type(3) .story__still {
    animation-duration: 7.8s;
    animation-delay: -4.4s;
  }

  .story__figure {
    display: none;
  }

  /* Phone stills — enlarge to match live card presence (frozen scale). */
  body.hub-2a .story__visual {
    overflow: visible;
  }

  body.hub-2a .story__visual:has([data-figure="strand"]) .story__still,
  body.hub-2a .story__visual:has(.story__figure--strand) .story__still {
    scale: 1.41; /* was 1.25 × 1.13 */
    /* Optical center — helix reads a hair left in the square frame. */
    left: 3.5%;
    right: -3.5%;
  }

  body.hub-2a .story__visual:has([data-figure="cell"]) .story__still,
  body.hub-2a .story__visual:has(.story__figure--cell) .story__still,
  body.hub-2a .story__visual:has([data-figure="metabolism"]) .story__still,
  body.hub-2a .story__visual:has(.story__figure--metabolism) .story__still {
    scale: 1.45;
  }
}

@media (max-width: 767px) and (prefers-reduced-motion: reduce) {
  .story__still {
    animation: none;
    will-change: auto;
  }
}

/* Cropped card stills sit inside the original square, so the model stays
   where it was and the phone does not unpack the black border. */
body.hub-2a .story__still {
  position: absolute;
  inset: 0;
}

body.hub-2a .story__still picture {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

body.hub-2a .story__still img {
  position: absolute;
  inset: auto;
  left: var(--l, 0%);
  top: var(--t, 0%);
  width: var(--w, 100%);
  height: var(--h, 100%);
  max-width: none;
  object-fit: fill;
}

@media (min-width: 768px) {
  .story__still {
    display: none !important;
  }
}

/* Live clay figures (RNA strand, liposome). The figure box bleeds past the 260px slot on
   every side so the object reads larger without moving the text below; the view is locked,
   so clicks fall through to the story link. */
.story__figure {
  position: absolute;
  inset: -2.7rem -1.3rem;
}

/* Metabolism visitors rush in from off-host — bleed the figure box so they aren’t scissored. */
.story__figure--metabolism {
  inset: -7.2rem -6.2rem;
}

.story__visual--live canvas {
  position: absolute;
  inset: 0;
  display: block;
  /* Own compositor layer so Chrome can slide the card without re-uploading WebGL. */
  transform: translateZ(0);
  /* The renderer writes its own pixel size onto the canvas as an inline style; the box, not
     the renderer, decides how big the picture is on screen. */
  width: 100% !important;
  height: 100% !important;
}

/* Safari: any CSS transform motion under the scissored figures leaves a ghost trail.
   Skip the slide-up ease — cards sit in their hold pose and only fade in. */
html.is-safari .story {
  transform: translate(calc(var(--hold-x, 0) * var(--nudge)), var(--hold, 0px));
  transition: opacity 1.4s ease-out var(--entrance-delay, 0s);
}

html.is-safari .stories:not(.is-revealed) .story {
  will-change: opacity;
}

html.is-safari .stories.is-revealed .story {
  transform: translate(calc(var(--hold-x, 0) * var(--nudge)), var(--hold, 0px));
}

html.is-safari .story__visual--live,
html.is-safari .stories .story__inner {
  animation: none;
}

.story__title {
  font-family: "Noe Display", var(--font-display);
  font-weight: 700;
  font-size: clamp(1.55rem, 6.9cqi, 2.75rem);
  line-height: 1.05;
  letter-spacing: 0.015em;
  margin: 0 0 0.4rem;
}

.story__title a {
  font-family: "Noe Display", var(--font-display);
  font-weight: 700;
  text-decoration: none;
  color: var(--gold);
}

.byline {
  margin: 0 0 0.7rem;
  font-family: "Merriweather", var(--font-serif);
  font-style: italic;
  font-size: 0.82rem;
  color: var(--ink);
}

.byline__prefix {
  margin-right: 0.55em; /* a clear gap between "By" and the name */
}

.byline__name {
  font-style: normal;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.11em;
}

.story__dek {
  margin: 0 0 2.15rem;
  font-family: "Merriweather", var(--font-serif);
  font-size: 0.95rem;
  line-height: 1.78;
  color: var(--ink-dim);
}

.cta {
  display: inline-block;
  width: fit-content;
  justify-self: start;
  font-family: "Pangram", var(--font-sans);
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  background: var(--cta);
  color: #0a1615;
  border-radius: 13px;
  padding: 0.95rem 0.95rem;
}

.cta:hover {
  filter: brightness(1.08);
}

.wobble {
  animation: wobble-8 2.8s steps(1) infinite;
}

@keyframes wobble-8 {
  0% { transform: translate(0, 0.4px) rotate(-0.25deg); }
  12.5% { transform: translate(1px, -0.6px) rotate(0.35deg); }
  25% { transform: translate(-0.7px, 1px) rotate(-0.2deg); }
  37.5% { transform: translate(0.8px, 0) rotate(0.4deg); }
  50% { transform: translate(-1px, -0.6px) rotate(-0.35deg); }
  62.5% { transform: translate(0.4px, 1px) rotate(0.15deg); }
  75% { transform: translate(-0.4px, -1px) rotate(-0.3deg); }
  87.5% { transform: translate(1px, 0.4px) rotate(0.3deg); }
  100% { transform: translate(0, 0.4px) rotate(-0.25deg); }
}

/* The essay sits a little below the stories screen: a left-aligned block of text set on
   the right-hand side of the page (a 3D element will go on the left), laid out like the
   story cards (label, title, byline, blurb, button). */
/* Merge flight removed — chase (divide) follows the stories more closely. */
.journey {
  display: none;
}

/* Divide finale: sticky viewport for the splitting cell. */
.chase-hold {
  height: 100vh;
  margin-top: -2vh;
}

.chase-sticky {
  position: sticky;
  top: var(--header-h);
  z-index: 6;
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100svh - var(--header-h));
}

.chase-wrap {
  position: relative;
  z-index: 3;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  min-height: calc(100vh - var(--header-h));
  max-width: 1760px;
  margin: 0 auto;
  padding: 6vh 3.5rem 8vh;
}

/* Divide loop only in the space left of the essay card. */
.chase__stage {
  flex: 1 1 0;
  align-self: stretch;
  min-height: calc(100vh - var(--header-h) - 14vh);
  margin-right: 2rem;
}

.chase {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  width: min(100%, 34rem);
}

.chase__title {
  font-family: "Noe Display", var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 3.4vw, 3rem);
  line-height: 1.05;
  letter-spacing: 0.015em;
  margin: 0 0 0.5rem;
  white-space: pre-line;
}

.chase__title a {
  font-family: "Noe Display", var(--font-display);
  font-weight: 700;
  text-decoration: none;
  color: var(--gold);
  white-space: pre-line;
}

.chase__dek {
  margin: 0 0 2rem;
  max-width: 34rem;
  font-family: "Merriweather", var(--font-serif);
  font-size: 0.95rem;
  line-height: 1.78;
  color: var(--ink-dim);
}

/* Chrome: the shimmer is a repeating gradient sliding behind a CSS mask on 215vh
   shafts — cheap on Safari, a compositor stall on Blink. Keep the shafts, freeze
   the wander. */
html.is-blink .ray::before {
  animation: none;
}

@media (prefers-reduced-motion: reduce) {
  .wobble,
  .story__inner,
  .story__visual,
  .stories__chevron,
  .ray,
  .ray::before {
    animation: none;
  }

  .story {
    transition: none;
  }
}

@media (max-width: 860px) {
  /* Phones: four rays instead of seven, and shorter, to keep the blended area small. */
  .ray:nth-child(1),
  .ray:nth-child(3),
  .ray:nth-child(5) {
    display: none;
  }

  .rays {
    height: 120vh;
  }

  .ray {
    height: 150vh;
    width: calc(var(--w) * 1.6);
    margin-left: calc(var(--w) * -0.8);
  }

  /* One screen of three staggered rows. Each row grows so the model fills
     the leftover height; the dek stays visible beside it. */
  .stories-hold {
    height: 108vh;
  }

  .stories-sticky {
    position: sticky;
    top: var(--header-h);
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100svh - var(--header-h));
    padding-inline: 0;
  }

  .stories {
    display: flex;
    flex-direction: column;
    grid-template-columns: none;
    width: auto;
    max-width: none;
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100svh - var(--header-h));
    padding: 0.8vh 1rem 1.4vh;
    justify-content: flex-start;
    gap: 0.35rem;
  }

  .stories__cta {
    flex: 0 0 auto;
    margin: 0.15vh 0 0.55vh;
    font-size: 0.86rem;
    gap: 0.22em;
  }

  .stories__chevron {
    width: 1.05em;
    height: 1.05em;
  }

  .story {
    display: flex;
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    padding: 0;
    align-items: stretch;
    transform: translateY(16vh);
    transition:
      transform 0.85s cubic-bezier(0.3, 0.9, 0.3, 1),
      opacity 0.6s ease-out;
  }

  .stories.is-revealed .story {
    transform: none;
  }

  html.is-safari .story,
  html.is-safari .stories.is-revealed .story {
    transform: none;
    transition: opacity 0.7s ease-out var(--entrance-delay, 0s);
  }

  .story__inner {
    display: grid;
    grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
    align-items: center;
    gap: 0.8rem;
    width: 100%;
    height: 100%;
    max-width: none;
    animation: none;
  }

  .story--flip .story__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr);
  }

  .story--flip .story__visual {
    order: 2;
  }

  .story--flip .story__copy {
    order: 1;
    align-items: flex-end;
    text-align: right;
  }

  .story__copy {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    min-width: 0;
    padding-block: 0.15rem;
  }

  .story__visual {
    width: 100%;
    height: auto;
    aspect-ratio: 1;
    max-height: 100%;
    align-self: center;
    margin-bottom: 0;
    animation: none;
  }

  .story__figure,
  .story__figure--metabolism {
    inset: 0.2rem;
  }

  .story__title {
    font-size: clamp(1.28rem, 5.8vw, 1.85rem);
    margin: 0 0 0.16rem;
  }

  .kicker {
    font-size: 0.58rem;
    margin: 0 0 0.28rem;
  }

  .byline {
    font-size: 0.7rem;
    margin: 0 0 0.4rem;
  }

  .story__dek {
    display: block;
    margin: 0 0 0.7rem;
    font-size: 0.8rem;
    line-height: 1.4;
  }

  .cta {
    padding: 0.48rem 0.62rem;
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    white-space: nowrap;
  }

  .journey {
    display: none;
  }

  .chase-hold {
    height: 105vh;
    margin-top: -1vh;
  }

  .chase-sticky {
    position: sticky;
    top: var(--header-h);
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100svh - var(--header-h));
  }

  /* Upper half: divide. Lower half: essay. */
  .chase-wrap {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    max-width: none;
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100svh - var(--header-h));
    padding: 0 1.45rem 2.2vh;
  }

  .chase__stage {
    display: block;
    flex: 0 0 50%;
    align-self: auto;
    width: 100%;
    height: calc((100vh - var(--header-h)) * 0.5);
    height: calc((100svh - var(--header-h)) * 0.5);
    min-height: calc((100vh - var(--header-h)) * 0.5);
    min-height: calc((100svh - var(--header-h)) * 0.5);
    margin: 0;
  }

  .chase {
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    padding-top: 0.85rem;
    justify-content: center;
  }

  .chase__title {
    font-size: clamp(1.45rem, 6.4vw, 2.05rem);
  }

  .chase__dek {
    font-size: 0.84rem;
    line-height: 1.52;
    margin: 0 0 1rem;
  }
}

/* ── Hub 2A: dialable field, centered essay, no water / divide ───────────── */
body.hub-2a {
  background: var(--hub2a-bg, #000);
  /* Bake locked mint/teal before JS dials (phones skip experience.js). */
  --hub2a-accent: #86fddf;
  --hub2a-logo-accent: #86fddf;
  --hub2a-cta: #86fddf;
  --hub2a-cta-hover: #f26d40;
  --hub2a-essay-tag-color: #86fddf;
  --hub2a-essay-btn-color: #86fddf;
  --hub2a-essay-btn-hover: #f26d40;
  --hub2a-card-tag-color: #86fddf;
  --hub2a-card-by-color: #86fddf;
  --hub2a-card-author-color: #86fddf;
  --hub2a-card-btn-color: #86fddf;
  --hub2a-card-btn-hover: #f26d40;
  --hub2a-explore-color: #86fddf;
  --hub2a-explore-arrow-color: #86fddf;
}

body.hub-2a .series {
  background: transparent;
  --void: var(--hub2a-bg, #000);
  --gold: var(--hub2a-title, #f0eadc);
  --ink: var(--hub2a-intro-text, #f3ead4);
  --cta: var(--hub2a-accent, #86fddf);
}

/*
 * §3 secondary type locks to §2 essay sizes (tag / by / author / dek / button).
 * Titles alone are JS-fitted to a shared size so every card stays ≤3 lines —
 * secondary type does NOT shrink with that fit (keeps §2↔§3 symmetry).
 */
body.hub-2a .stories .kicker {
  color: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-essay-tag-font, "Pangram", var(--font-sans));
  font-size: var(--hub2a-essay-tag-size, 0.86rem);
  font-weight: var(--hub2a-essay-tag-weight, 700);
  font-style: var(--hub2a-essay-tag-style, normal);
  letter-spacing: var(--hub2a-essay-tag-track, 0.22em);
  line-height: var(--hub2a-essay-tag-lead, 1.2);
  margin-bottom: var(--hub2a-card-tag-gap, 1.15rem);
}

/* Per-card container so title type can track column width (desktop → laptop). */
body.hub-2a .story {
  container-type: inline-size;
  container-name: hub2a-story;
}

body.hub-2a .story__title {
  color: #fff;
  font-family: var(--hub2a-card-title-font, "Noe Display", var(--font-display));
  /* Dial is the target size; JS shrinks all titles to one shared size (≤3 lines). */
  font-size: var(--hub2a-card-title-size, 3.3rem);
  font-weight: var(--hub2a-card-title-weight, 700);
  font-style: var(--hub2a-card-title-style, normal);
  letter-spacing: var(--hub2a-card-title-track, 0.015em);
  line-height: var(--hub2a-card-title-lead, 1.14);
  margin-bottom: var(--hub2a-card-title-gap, 1.05rem);
}

body.hub-2a .story__title a {
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  font-size: inherit;
}

/* Noe Display’s hyphen is a hairline, so the glyph is hidden and one bar is drawn in its place. */
body.hub-2a .story__hyphen {
  display: inline-block;
  position: relative;
  color: transparent;
}

body.hub-2a .story__hyphen::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 0.36em;
  height: 0.085em;
  bottom: 0.62em;
  transform: translateX(-50%);
  background: #fff;
  border-radius: 1px;
  pointer-events: none;
}

body.hub-2a .story .byline__prefix {
  color: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-essay-by-font, "Merriweather", var(--font-serif));
  font-size: var(--hub2a-essay-by-size, 0.97rem);
  font-weight: var(--hub2a-essay-by-weight, 400);
  font-style: var(--hub2a-essay-by-style, italic);
  letter-spacing: var(--hub2a-essay-by-track, 0em);
  line-height: var(--hub2a-essay-by-lead, 1.2);
}

body.hub-2a .story .byline__name {
  color: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-essay-author-font, "Merriweather", var(--font-serif));
  font-size: var(--hub2a-essay-author-size, 0.99rem);
  font-weight: var(--hub2a-essay-author-weight, 400);
  font-style: var(--hub2a-essay-author-style, normal);
  letter-spacing: var(--hub2a-essay-author-track, 0em);
  line-height: var(--hub2a-essay-author-lead, 1.2);
}

body.hub-2a .story__dek {
  color: var(--hub2a-card-dek-color, #f0fdff);
  font-family: var(--hub2a-essay-dek-font, "Merriweather", var(--font-serif));
  font-size: var(--hub2a-essay-dek-size, 1.08rem);
  font-weight: var(--hub2a-essay-dek-weight, 400);
  font-style: italic;
  letter-spacing: var(--hub2a-essay-dek-track, 0em);
  line-height: var(--hub2a-essay-dek-lead, 1.78);
  margin-bottom: var(--hub2a-card-dek-gap, 1.85rem);
}

body.hub-2a .stories .cta {
  background: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-essay-btn-font, "Pangram", var(--font-sans));
  font-size: var(--hub2a-essay-btn-size, 0.7rem);
  font-weight: var(--hub2a-essay-btn-weight, 700);
  font-style: var(--hub2a-essay-btn-style, normal);
  letter-spacing: var(--hub2a-essay-btn-track, 0.12em);
  line-height: var(--hub2a-essay-btn-lead, 1);
  border-radius: 13px;
  padding: 0.95rem;
}

body.hub-2a .stories .cta:hover {
  background: var(--hub2a-card-btn-hover, var(--hub2a-cta-hover, #f26d40));
  filter: none;
}

/*
 * Full-page gradient on body (scroll height). Split = where top→bottom blend
 * finishes; soft hold keeps solid top before the blend. Sections stay clear
 * so the body wash shows through the whole essay.
 */
body.hub-2a.hub-2a--grad {
  /* Overscroll / side letterbox always black — never the mid-page teal. */
  background-color: #000;
  background-image: linear-gradient(
    180deg,
    var(--hub2a-bg-grad-top, #000) 0%,
    var(--hub2a-bg-grad-top, #000) var(--hub2a-bg-grad-hold, 0%),
    var(--hub2a-bg-grad-bottom, #0f3a48) var(--hub2a-bg-grad-split, 42vh),
    /* Settle back to black through §3 so teal never peeks at the page end. */
    #000 100%
  );
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

html:has(body.hub-2a) {
  background: #000;
}

body.hub-2a.hub-2a--grad .series,
body.hub-2a.hub-2a--grad .chase-hold,
body.hub-2a.hub-2a--grad .chase-sticky,
body.hub-2a.hub-2a--grad .intro {
  background: transparent;
}

/* Stories sit on the black rest — never punch through to the teal body wash. */
body.hub-2a.hub-2a--grad .stories-hold,
body.hub-2a.hub-2a--grad .stories-sticky {
  background: #000;
}

body.hub-2a .bubbles,
body.hub-2a .series-wash,
body.hub-2a #plankton-root {
  display: none !important;
}

body.hub-2a .series {
  position: relative;
  overflow: visible;
}

/* Page spotlight — under intro text, above membrane bleed so it stays visible.
   Full-bleed width so the cone doesn’t hard-clip at the sides.
   Top of the shaft stays dim so mint logo/kicker sit on darker slate (legibility). */
body.hub-2a .hub2a-spot {
  position: absolute;
  left: 0;
  width: 100%;
  top: -43vh;
  height: 160vh;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(
    180deg,
    transparent 0%,
    rgba(0, 0, 0, 0.22) 6%,
    rgba(0, 0, 0, 0.55) 12%,
    #000 22%,
    #000 calc(100% - var(--spot-fade, 28%)),
    transparent 100%
  );
  mask-image: linear-gradient(
    180deg,
    transparent 0%,
    rgba(0, 0, 0, 0.22) 6%,
    rgba(0, 0, 0, 0.55) 12%,
    #000 22%,
    #000 calc(100% - var(--spot-fade, 28%)),
    transparent 100%
  );
}

body.hub-2a .hub2a-spot__inner {
  position: relative;
  width: 100%;
  height: 100%;
}

body.hub-2a .hub2a-spot__inner picture {
  position: absolute;
  inset: 0;
  display: block;
}

/* Baked volumetric cone — fills the same box the live SpotLight used to.
   `fill` stretches with the spot host (wide page × tall cone), so the shaft
   reads as broad as the old live soft cone rather than a thin portrait crop. */
body.hub-2a .hub2a-spot__cone {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: fill;
  object-position: center top;
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 0;
  /* Extra horizontal open — dial via --hub2a-cone-sx if needed. */
  transform: scaleX(var(--hub2a-cone-sx, 1.85));
  transform-origin: center top;
}

body.hub-2a .hub2a-spot canvas {
  position: relative;
  z-index: 1;
  display: block;
  width: 100% !important;
  height: 100% !important;
  /* Drop muddy black fringe — black composite as clear over the page. */
  mix-blend-mode: screen;
}

/* One hub light shaft — sits in the cone, same frame/mask as the volumetric spot.
   Same top fade as .hub2a-spot so the branding band isn’t washed out. */
body.hub-2a .hub2a-rays {
  position: absolute;
  left: 0;
  width: 100%;
  top: -43vh;
  height: 160vh;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
  mix-blend-mode: screen;
  -webkit-mask-image: linear-gradient(
    180deg,
    transparent 0%,
    rgba(0, 0, 0, 0.18) 6%,
    rgba(0, 0, 0, 0.5) 12%,
    #000 22%,
    #000 calc(100% - var(--spot-fade, 28%)),
    transparent 100%
  );
  mask-image: linear-gradient(
    180deg,
    transparent 0%,
    rgba(0, 0, 0, 0.18) 6%,
    rgba(0, 0, 0, 0.5) 12%,
    #000 22%,
    #000 calc(100% - var(--spot-fade, 28%)),
    transparent 100%
  );
}

body.hub-2a .hub2a-ray {
  /* Cool tint to match the haze cone; held back so mint type still pops. */
  background: rgba(204, 247, 255, 0.4);
  top: -8vh;
  height: 175vh;
  animation-name: hub2a-ray-dance;
}

@keyframes hub2a-ray-dance {
  0% {
    transform: rotate(calc(var(--tilt) - 3.4deg)) translateX(-2vw) scaleX(0.76);
    opacity: calc(var(--glow) * 0.28);
  }
  40% {
    opacity: var(--glow);
  }
  70% {
    opacity: calc(var(--glow) * 0.72);
  }
  100% {
    transform: rotate(calc(var(--tilt) + 3.4deg)) translateX(2vw) scaleX(1.22);
    opacity: calc(var(--glow) * 0.4);
  }
}

@media (prefers-reduced-motion: reduce) {
  body.hub-2a .hub2a-ray,
  body.hub-2a .hub2a-ray::before {
    animation: none;
  }
}

/* Section 2: shorter than a full viewport so stories arrive sooner after the CTA. */
body.hub-2a .chase-hold {
  height: auto;
  min-height: 0;
  /* Breathing room after intro before the bilayer — dial via --hub2a-intro-gap. */
  margin-top: var(--hub2a-intro-gap, 12vh);
  padding-top: clamp(1rem, 2.5vh, 2rem);
  padding-bottom: 0;
  /* Transparent so body / full-page gradient shows through. */
  background: transparent;
}

/* Keep §2 (and its membrane bleed) under the intro so GL never covers copy. */
body.hub-2a .chase-hold,
body.hub-2a .chase-sticky {
  position: relative;
  top: auto;
  z-index: 1;
  min-height: 0;
}

body.hub-2a .chase-wrap.hub2a-membrane {
  position: relative;
  display: block;
  max-width: none;
  /* Hug the essay card — leftover wrap height was orphan air above the rule. */
  min-height: 0;
  padding: 0;
  padding-top: clamp(6rem, 18vh, 11rem);
  padding-bottom: var(--hub2a-essay-section-pad, 0.6rem);
  overflow: visible;
  z-index: 0;
}

/*
 * Section 3: keep the real composition (stagger, CTA padding, figure sizes),
 * then squish the whole block into one viewport via scale — no reflow.
 * Center + overflow clip keeps one screen; natural min-height (not full vh)
 * lets a higher scale read larger without spilling.
 */
body.hub-2a .stories-hold {
  --hub2a-stories-scale: 0.84;
  /* CTA↔model air — room under explore before the card meshes. */
  --hub2a-card-model-air: 0.9rem;
  margin-top: 0;
  /* Hug content — do NOT force 100vh+ (that was the chunky blank under §3). */
  height: auto;
  min-height: 0;
  box-sizing: border-box;
  display: flex;
  /* Top-align so CTA sits under the essay button — not mid-viewport with orphan air. */
  align-items: flex-start;
  justify-content: center;
  /* Must be visible on BOTH axes — if x is hidden, y computes to auto and
     shaves the explore line. Horizontal clip is already on html/body. */
  overflow: visible;
  /* Above membrane bleed so the explore line isn’t covered by the GL veil. */
  position: relative;
  z-index: 2;
  padding-top: 0;
  /* Float wobble clearance — ~1.25× the prior tight pad, plus a touch more end air. */
  padding-bottom: calc(clamp(1.4rem, 3vh, 2.3rem) + 0.5rem);
}

body.hub-2a .stories-sticky {
  position: relative;
  top: auto;
  flex: 0 0 auto;
  width: 100%;
  min-height: 0;
  transform: scale(var(--hub2a-stories-scale));
  transform-origin: top center;
  padding-block: 0;
  box-sizing: border-box;
  /* JS sets margin-bottom: (scale-1)*height so transform scale doesn’t leave a hole. */
}

body.hub-2a .stories {
  /* Content-sized — forcing full-vh before scale left empty air / overflow. */
  min-height: 0;
  height: auto;
  padding-block: 0;
  /* Pull the card row up toward the explore line. */
  row-gap: 0;
}

/* Hub 2A: no slide-up entrance. Cards share one height; keep sideways nudge. */
body.hub-2a .story,
body.hub-2a .stories.is-revealed .story,
body.hub-2a .stories:not(.is-revealed) .story {
  transform: translate(calc(var(--hold-x, 0) * var(--nudge)), 0);
  opacity: 1;
  transition: none;
  will-change: auto;
}

body.hub-2a .stories__cta {
  opacity: 1;
  transition: none;
  position: relative;
  z-index: 4;
  overflow: visible;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Shrink-wrap to the sentence (not a fixed column) so the rule can match it. */
  width: max-content;
  max-width: min(100% - 2rem, 42rem);
  justify-self: center;
  margin-inline: auto;
  /*
   * exploreRuleAir = rem on EACH side of the rule (button→line = line→CTA).
   * Essay section pad is above this box; CTA padding makes up the rest of 2×air.
   */
  --hub2a-explore-pad: var(--hub2a-essay-section-pad, 0rem);
  --hub2a-explore-air: var(--hub2a-explore-rule-air, 3.5rem);
  margin-top: 0;
  /* Explore → first card models (desktop / laptop / iPad). */
  margin-bottom: calc(var(--hub2a-card-model-air, 0.9rem) - 1.35rem);
  padding-top: max(0rem, calc(var(--hub2a-explore-air) * 2 - var(--hub2a-explore-pad)));
  padding-bottom: 0.1em;
  gap: 0.35em;
  color: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-explore-font, "Pangram", var(--font-sans));
  font-size: var(--hub2a-explore-size, 1.35rem);
  font-weight: var(--hub2a-explore-weight, 400);
  font-style: var(--hub2a-explore-style, normal);
  letter-spacing: var(--hub2a-explore-track, 0.01em);
  line-height: var(--hub2a-explore-lead, 1.45);
  text-align: center;
}

/* Rule rides the sentence span — same width as the CTA copy, not the chevron/column. */
body.hub-2a .stories__cta > span {
  position: relative;
  display: block;
  width: max-content;
  max-width: 100%;
}

body.hub-2a .stories__cta > span::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 0.5px;
  transform: translateY(calc(-1 * (var(--hub2a-explore-air) - var(--hub2a-explore-pad)) - 50%));
  background: var(--hub2a-explore-rule-color, #ffffff);
  opacity: var(--hub2a-explore-rule-opacity, 0.85);
  pointer-events: none;
}

body.hub-2a .stories__chevron {
  color: var(--hub2a-accent, #86fddf);
  width: calc(1.6em * var(--hub2a-explore-arrow-scale, 1));
  height: calc(1.6em * var(--hub2a-explore-arrow-scale, 1));
}

/* Per-card hop chevron — mobile only (see ≤900px). */
body.hub-2a .story__hop {
  display: none;
}

/*
 * Card type stack: equal air above/below the byline.
 * Title uses tight leading (1.05); byline gets matching line-height so the
 * italic/uppercase glyphs don’t sit high in a tall line-box (which made the
 * gap under the author read larger than the gap under the title).
 */
body.hub-2a .story .byline {
  margin-top: 0;
  margin-bottom: var(--hub2a-card-author-gap, 1.25rem);
  line-height: var(--hub2a-card-author-lead, 1.2);
}

/*
 * Card figures — one dial (--hub2a-card-model-air) for CTA→model and model→type.
 * Figures may bloom upward into the CTA air, but must not spill into the titles.
 */
body.hub-2a .story {
  padding-top: 0;
}

/* Cards / type / placement stay put — only the model window grows. */
body.hub-2a .story {
  overflow: visible;
}

body.hub-2a .story__visual {
  --hub2a-model-only-scale: 1.5;
  /* Square 1.5× window — size/aspect untouched for spacing tweaks. */
  width: calc(100% * var(--hub2a-model-only-scale));
  aspect-ratio: 1;
  height: auto;
  max-height: none;
  margin-top: 0;
  margin-inline: calc((100% - 100% * var(--hub2a-model-only-scale)) / 2);
  margin-bottom: 0;
  overflow: hidden;
}

/* Pull type up under the models — window/mesh unchanged. Center the type stack. */
body.hub-2a .story__inner {
  align-items: center;
}

body.hub-2a .story__copy {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 100%;
  /* Model→tag air — pulled less so EXPLAINER doesn’t kiss the meshes. */
  margin-top: -2.35rem;
}

body.hub-2a .stories .kicker,
body.hub-2a .story__title,
body.hub-2a .story .byline,
body.hub-2a .story__dek {
  text-align: center;
}

body.hub-2a .stories .cta {
  align-self: center;
}

/* iPad / short desktop: a bit less pull than phone, still clearer model→tag. */
@media (min-width: 901px) and (max-width: 1180px) {
  body.hub-2a .story__copy {
    margin-top: -1.5rem;
  }
}

/* Desktop / iPad / laptop: §2 Yasemin type + section air.
   Intro stack gaps are set AFTER the dial type rules below (cascade). */
@media (min-width: 768px) {
  /* §2 essay: larger Yasemin title + looser leading + chunkier gaps. */
  body.hub-2a .chase__title {
    font-size: var(--hub2a-essay-title-size, clamp(2.15rem, 4.2vw, 3.65rem));
    line-height: var(--hub2a-essay-title-lead, 1.14);
    margin-bottom: var(--hub2a-essay-title-gap, 1.15rem);
  }

  body.hub-2a .chase .byline {
    margin-bottom: var(--hub2a-essay-author-gap, 1.35rem);
  }

  /* Ease §2 copy lower so it clears floating models (desktop / laptop / iPad). */
  body.hub-2a .chase-wrap.hub2a-membrane {
    padding-top: calc(clamp(7.5rem, 22vh, 13.5rem) + 36px);
  }

  body.hub-2a .chase {
    margin-top: clamp(0.75rem, 2.2vh, 1.75rem);
  }

  /* Tighten explore CTA → card models (empty canvas headroom reads as air). */
  body.hub-2a .stories__cta {
    margin-bottom: calc(var(--hub2a-card-model-air, 0.9rem) - 2.35rem);
  }

  body.hub-2a .story__visual {
    margin-top: -1.65rem;
  }
}

body.hub-2a .story__figure,
body.hub-2a .story__figure--strand,
body.hub-2a .story__figure--metabolism {
  top: 0.45rem;
  right: 0.45rem;
  bottom: 0.45rem;
  left: 0.45rem;
}

/*
 * Membrane in §2. Bleeds well into the intro so above-bilayer floaters aren’t
 * clipped by the canvas edge. Camera view-offset keeps the bilayer locked.
 * Bottom extends far enough for under-membrane floaters, then soft-masks to
 * transparent so lipids never hard-cut — black-rest / stories show through.
 */
body.hub-2a .hub2a-membrane__stage {
  position: absolute;
  left: 0;
  right: 0;
  top: -48vh;
  height: calc(58% + 48vh + 24vh);
  z-index: 0;
  pointer-events: none;
  overflow: visible;
}

/* Clickable membrane scene — under essay type, above the GL stage. */
body.hub-2a .hub2a-membrane__link {
  position: absolute;
  left: 0;
  right: 0;
  top: -48vh;
  height: calc(58% + 48vh + 24vh);
  z-index: 2;
  pointer-events: auto;
  text-indent: -9999px;
  overflow: hidden;
  color: transparent;
}

body.hub-2a .hub2a-membrane__stage canvas {
  position: relative;
  z-index: 1;
  display: block;
  width: 100% !important;
  height: 100% !important;
}

/*
 * Essay sits just beneath the bilayer.
 * Absolute so flex/margins can’t pin it to the bottom of the section.
 */

/* Mobile-only hard black behind the membrane canvas (under the GL). Desktop off. */
body.hub-2a .hub2a-black-floor {
  display: none;
}

/* One black wash over the membrane GL (under essay type) — covers shell rim,
   side fade, and mouth floor. Top is relative to .chase-wrap. */
body.hub-2a .hub2a-black-rest {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--hub2a-black-top, 42%);
  bottom: -40vh;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0) 0%,
    rgba(0, 0, 0, 0.4) 6%,
    rgba(0, 0, 0, 0.88) 16%,
    #000 26%,
    #000 100%
  );
}

/* Phone §2 transparent cutouts — desktop/iPad keep live membrane; hidden ≥768. */
body.hub-2a .hub2a-parallax {
  display: none;
  position: absolute;
  /* Wide past the viewport so membrane / side RNA can bleed off-screen. */
  left: -24%;
  right: -24%;
  top: -22vh;
  height: calc(64% + 22vh + 30vh);
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
  --s2-membrane-y: 6vh;
  --s2-membrane-s: 0.94;
  --s2-rna-y: 10vh;
  --s2-rna-s: 0.76;
  --s2-lipid-y: 2vh;
  --s2-lipid-s: 0.94;
}

body.hub-2a .hub2a-parallax__plate {
  position: absolute;
  inset: -3% -6%;
}

body.hub-2a .hub2a-parallax__plate picture {
  display: block;
  width: 100%;
  height: 100%;
}

body.hub-2a .hub2a-parallax__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 44%;
  max-width: none;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
  transform-origin: center 52%;
}

/* Full-stage transparent plates — independent bob per kind. */
body.hub-2a .hub2a-parallax__plate--below {
  z-index: 0;
  container-type: size;
}

body.hub-2a .hub2a-parallax__fit {
  position: absolute;
  inset: 0;
  overflow: hidden;
  transform-origin: center 52%;
  animation: hub2a-below-bob 6.4s ease-in-out infinite;
  animation-delay: -2.1s;
}

body.hub-2a .hub2a-parallax__cover {
  position: absolute;
  left: 50%;
  top: 54%;
  width: max(100cqw, calc(100cqh * 2000 / 2168));
  height: max(100cqh, calc(100cqw * 2168 / 2000));
  translate: -50% -54%;
}

body.hub-2a .hub2a-parallax__cover picture {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

body.hub-2a .hub2a-parallax__plate--below .hub2a-parallax__img {
  position: absolute;
  inset: auto;
  left: var(--l, 0%);
  top: var(--t, 0%);
  width: var(--w, 100%);
  height: var(--h, 100%);
  object-fit: fill;
  object-position: center center;
  animation: none;
}

body.hub-2a .hub2a-parallax__plate--membrane {
  z-index: 1;
  /* Smaller arc, lifted — leaves a clear band above for floaters. */
  top: -20%;
  bottom: 10%;
  left: -8%;
  right: -8%;
}

body.hub-2a .hub2a-parallax__plate--membrane .hub2a-parallax__img {
  object-position: center 40%;
  animation: hub2a-piece-bob 7.4s ease-in-out infinite;
}

/* Molecules stay behind the membrane and every other cutout, so a wider phone
   tucks them under the bowl instead of stacking them on top of it. */
body.hub-2a [data-piece="Molecules"] {
  z-index: 0;
}

body.hub-2a [data-piece="Molecules"] .hub2a-parallax__img {
  opacity: 0.4;
}

body.hub-2a [data-piece="Lipid 5"] .hub2a-parallax__img {
  opacity: 1;
}

body.hub-2a [data-piece="RNA 2"] .hub2a-parallax__img {
  opacity: 0.35;
}

/* Cropped pieces sit inside the old full-frame box, so --x / --y / --s still
   refer to that frame and the object does not jump when the bitmap is trimmed. */
body.hub-2a .hub2a-parallax__frame {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  pointer-events: none;
}

body.hub-2a .hub2a-parallax__stage {
  position: relative;
  height: 100%;
  width: auto;
  aspect-ratio: 2880 / 3122;
  flex: 0 0 auto;
  min-width: 0;
  min-height: 0;
}

body.hub-2a .hub2a-parallax__stage picture {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

body.hub-2a .hub2a-parallax__stage .hub2a-parallax__img {
  position: absolute;
  left: var(--l, 0%);
  top: var(--t, 0%);
  width: var(--w, 100%);
  height: var(--h, 100%);
  max-width: none;
  object-fit: fill;
  object-position: center center;
  transform-origin: var(--ox, 50%) var(--oy, 50%);
  animation: hub2a-piece-bob-full 5.2s ease-in-out infinite;
  animation-delay: var(--bob-delay, 0s);
}

/* Placement stays on the frame center. The picture itself rocks around its own middle
   so a few degrees of turn does not swing it across the page. */
body.hub-2a .hub2a-parallax__turn {
  position: absolute;
  left: var(--l, 0%);
  top: var(--t, 0%);
  width: var(--w, 100%);
  height: var(--h, 100%);
  transform-origin: var(--ox, 50%) var(--oy, 50%);
  animation: hub2a-piece-bob-full 5.2s ease-in-out infinite;
  animation-delay: var(--bob-delay, 0s);
}

body.hub-2a .hub2a-parallax__turn .hub2a-parallax__img {
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  transform-origin: center center;
  animation: none;
}

@keyframes hub2a-piece-turn {
  0%,
  100% { transform: rotate(-3deg); }
  50% { transform: rotate(3deg); }
}

@media (max-width: 767px) {
  body.hub-2a .hub2a-parallax__turn .hub2a-parallax__img {
    animation: hub2a-piece-turn 7.4s ease-in-out infinite;
    animation-delay: var(--turn-delay, var(--bob-delay, 0s));
  }

  /* Molecules share one plate, so stagger them or they rock as one sheet. */
  body.hub-2a [data-piece="Molecules"] .hub2a-parallax__turn:nth-child(even) .hub2a-parallax__img {
    animation-direction: reverse;
  }

  body.hub-2a [data-piece="Molecules"] .hub2a-parallax__turn:nth-child(3n) {
    --turn-delay: -2.6s;
  }

  body.hub-2a [data-piece="Molecules"] .hub2a-parallax__turn:nth-child(3n + 2) {
    --turn-delay: -5s;
  }
}

@media (max-width: 767px) and (prefers-reduced-motion: reduce) {
  body.hub-2a .hub2a-parallax__turn .hub2a-parallax__img {
    animation: none;
  }
}

body.hub-2a .hub2a-parallax__plate--mols {
  z-index: 2;
}

body.hub-2a .hub2a-parallax__plate--mols .hub2a-parallax__img {
  /* Back floaters sit lower — more air under lipids / RNA. */
  object-position: center 58%;
  animation: hub2a-mols-bob 5.6s ease-in-out infinite;
  animation-delay: -0.8s;
}

body.hub-2a .hub2a-parallax__plate--lipids {
  z-index: 3;
}

body.hub-2a .hub2a-parallax__plate--lipids .hub2a-parallax__img {
  /* Mid band between RNA (high) and membrane rim. */
  object-position: center 46%;
  animation: hub2a-lipid-bob 4.5s ease-in-out infinite;
  animation-delay: -1.9s;
}

body.hub-2a .hub2a-parallax__plate--rna {
  /* Hero RNA in front of every other cutout plate. */
  z-index: 6;
  left: -10%;
  right: -10%;
  top: 0;
  bottom: 12%;
}

body.hub-2a .hub2a-parallax__plate--rna .hub2a-parallax__img {
  object-position: center 32%;
  animation: hub2a-rna-bob 5.2s ease-in-out infinite;
  animation-delay: -0.4s;
}

@keyframes hub2a-piece-bob-full {
  0%,
  100% {
    transform: translate3d(var(--x, 0vw), calc(var(--y, 0vh) + var(--py, 0px)), 0) scale(var(--s, 1));
  }
  50% {
    transform: translate3d(var(--x, 0vw), calc(var(--y, 0vh) + var(--py, 0px) - 12px), 0) scale(var(--s, 1));
  }
}

@keyframes hub2a-piece-bob {
  0%,
  100% {
    transform: translate3d(var(--x, 0vw), calc(var(--y, 0vh) + var(--py, 0px) + var(--membrane-pin, 0px)), 0) scale(var(--s, 1));
  }
  50% {
    transform: translate3d(var(--x, 0vw), calc(var(--y, 0vh) + var(--py, 0px) + var(--membrane-pin, 0px) - 12px), 0) scale(var(--s, 1));
  }
}

@keyframes hub2a-mols-bob {
  0%,
  100% {
    transform: translate3d(0, 20vh, 0) scale(0.92);
  }
  50% {
    transform: translate3d(0, calc(20vh - 10px), 0) scale(0.92);
  }
}

@keyframes hub2a-below-bob {
  0%,
  100% {
    transform: translate3d(0, calc(12px + var(--py, 0px)), 0) scale(0.95);
  }
  50% {
    transform: translate3d(0, calc(4px + var(--py, 0px)), 0) scale(0.95);
  }
}

@media (prefers-reduced-motion: reduce) {
  body.hub-2a .hub2a-parallax__plate--membrane .hub2a-parallax__img,
  body.hub-2a .hub2a-parallax__plate--rna .hub2a-parallax__img,
  body.hub-2a .hub2a-parallax__plate--lipids .hub2a-parallax__img,
  body.hub-2a .hub2a-parallax__plate--mols .hub2a-parallax__img,
  body.hub-2a .hub2a-parallax__plate--below .hub2a-parallax__img {
    animation: none;
  }
}

body.hub-2a .stories-hold,
body.hub-2a .stories-sticky {
  background: #000;
}

body.hub-2a .chase {
  /* In-flow so wrap height ends at the button — rule gap lives on the CTA. */
  position: relative;
  left: auto;
  top: auto;
  transform: none;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  /* Wide enough that the dek doesn’t orphan the last two words. */
  width: min(100% - 3rem, 40rem);
  margin: 0 auto;
  padding: 0 1.5rem;
}

body.hub-2a .chase .byline,
body.hub-2a .chase .kicker,
body.hub-2a .chase .cta {
  text-align: center;
}

/* §2 essay title card — separate Text dials from §3 story cards.
   Title alone is JS-fitted on phones; tag/author/dek/btn stay at dial size
   and wrap like §3 secondary type. */
body.hub-2a .chase .kicker {
  color: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-essay-tag-font, "Pangram", var(--font-sans));
  font-size: var(--hub2a-essay-tag-size, 0.86rem);
  font-weight: var(--hub2a-essay-tag-weight, 700);
  font-style: var(--hub2a-essay-tag-style, normal);
  letter-spacing: var(--hub2a-essay-tag-track, 0.22em);
  line-height: var(--hub2a-essay-tag-lead, 1.2);
  margin-bottom: var(--hub2a-essay-tag-gap, 1.25rem);
}

body.hub-2a .chase__title {
  color: var(--hub2a-essay-title-color, var(--hub2a-title, #fff));
  font-family: var(--hub2a-essay-title-font, "Noe Display", var(--font-display));
  /* Dial is the target size; JS may shrink via inline px on phones. */
  font-size: var(--hub2a-essay-title-size, clamp(1.68rem, 3.4vw, 2.9rem));
  font-weight: var(--hub2a-essay-title-weight, 700);
  font-style: var(--hub2a-essay-title-style, normal);
  line-height: var(--hub2a-essay-title-lead, 1.05);
  letter-spacing: var(--hub2a-essay-title-track, 0.015em);
  margin: 0 0 var(--hub2a-essay-title-gap, 0.55rem);
}

body.hub-2a .chase__title a {
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  font-size: inherit;
}

body.hub-2a .chase .byline {
  margin-bottom: var(--hub2a-essay-author-gap, 0.85rem);
}

body.hub-2a .chase .byline__prefix {
  color: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-essay-by-font, "Merriweather", var(--font-serif));
  font-size: var(--hub2a-essay-by-size, 0.97rem);
  font-weight: var(--hub2a-essay-by-weight, 400);
  font-style: var(--hub2a-essay-by-style, italic);
  letter-spacing: var(--hub2a-essay-by-track, 0em);
  line-height: var(--hub2a-essay-by-lead, 1.2);
}

body.hub-2a .chase .byline__name {
  color: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-essay-author-font, "Merriweather", var(--font-serif));
  font-size: var(--hub2a-essay-author-size, 0.99rem);
  font-weight: var(--hub2a-essay-author-weight, 400);
  font-style: var(--hub2a-essay-author-style, normal);
  letter-spacing: var(--hub2a-essay-author-track, 0em);
  line-height: var(--hub2a-essay-author-lead, 1.2);
}

body.hub-2a .chase__dek {
  margin-left: auto;
  margin-right: auto;
  margin-bottom: var(--hub2a-essay-dek-gap, 2.35rem);
  max-width: 40rem;
  color: var(--hub2a-essay-dek-color, var(--hub2a-dek, #f0fdff));
  font-family: var(--hub2a-essay-dek-font, "Merriweather", var(--font-serif));
  font-size: var(--hub2a-essay-dek-size, 1.08rem);
  font-weight: var(--hub2a-essay-dek-weight, 400);
  font-style: italic;
  letter-spacing: var(--hub2a-essay-dek-track, 0em);
  line-height: var(--hub2a-essay-dek-lead, 1.78);
}

body.hub-2a .chase .cta {
  background: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-essay-btn-font, "Pangram", var(--font-sans));
  font-size: var(--hub2a-essay-btn-size, 0.7rem);
  font-weight: var(--hub2a-essay-btn-weight, 700);
  font-style: var(--hub2a-essay-btn-style, normal);
  letter-spacing: var(--hub2a-essay-btn-track, 0.12em);
  line-height: var(--hub2a-essay-btn-lead, 1);
  border-radius: 13px;
  padding: 0.95rem;
  justify-self: center;
}

body.hub-2a .chase .cta:hover {
  background: var(--hub2a-essay-btn-hover, var(--hub2a-cta-hover, #f26d40));
  filter: none;
}

/* Intro above membrane; transparent so the spotlight can read through. */
body.hub-2a .intro {
  position: relative;
  z-index: 10;
  background: transparent;
  /* Keep body copy clear of the membrane / floaters bleeding up. */
  padding-bottom: clamp(4.5rem, 14vh, 9rem);
}

/* Body paragraphs only — intro title/dek stay on their own measure. */
body.hub-2a .intro__body {
  max-width: var(--hub2a-intro-body-width, 38rem);
  margin-bottom: clamp(1.5rem, 4vh, 3rem);
  text-align: left;
}

body.hub-2a .intro__kicker {
  color: var(--hub2a-accent, #86fddf);
  font-family: var(--hub2a-intro-kicker-font, "Pangram", var(--font-sans));
  font-size: var(--hub2a-intro-kicker-size, clamp(0.78rem, 1.05vw, 0.95rem));
  font-weight: var(--hub2a-intro-kicker-weight, 700);
  font-style: var(--hub2a-intro-kicker-style, normal);
  letter-spacing: var(--hub2a-intro-kicker-track, 0.22em);
  line-height: var(--hub2a-intro-kicker-lead, 1.2);
  margin-bottom: var(--hub2a-intro-kicker-gap, 0.85rem);
}

body.hub-2a .intro__title {
  color: var(--hub2a-intro-title-color, var(--hub2a-title, #fff));
  font-family: var(--hub2a-intro-title-font, "Noe Display", var(--font-display));
  font-size: var(--hub2a-intro-title-size, clamp(4.2rem, 11.5vw, 10.5rem));
  font-weight: var(--hub2a-intro-title-weight, 700);
  font-style: var(--hub2a-intro-title-style, normal);
  letter-spacing: var(--hub2a-intro-title-track, 0.01em);
  line-height: var(--hub2a-intro-title-lead, 0.92);
  margin-bottom: var(--hub2a-intro-title-gap, 0.85rem);
}

body.hub-2a .intro__dek {
  color: var(--hub2a-intro-dek-color, var(--hub2a-dek, #a9e6da));
  font-family: var(--hub2a-intro-dek-font, "Merriweather", var(--font-serif));
  font-size: var(--hub2a-intro-dek-size, clamp(1.1rem, 1.5vw, 1.35rem));
  font-weight: var(--hub2a-intro-dek-weight, 400);
  font-style: var(--hub2a-intro-dek-style, normal);
  letter-spacing: var(--hub2a-intro-dek-track, 0.01em);
  line-height: var(--hub2a-intro-dek-lead, 1.45);
  margin-bottom: var(--hub2a-intro-dek-gap, 1.2rem);
}

/*
 * Desktop / iPad / laptop intro stack — MUST follow the dial margin rules above
 * (same specificity; earlier “triple” media query was overwritten by dial gaps).
 * At least ~2× the previous on-page air (kicker 1.85 / title 0.85 / dek 1.2).
 */
@media (min-width: 768px) {
  body.hub-2a .intro {
    --hub2a-intro-stack-gap: clamp(3.8rem, 7.5vh, 5.75rem);
  }

  body.hub-2a .intro__kicker {
    margin-bottom: clamp(3.8rem, 7.5vh, 5.75rem);
  }

  body.hub-2a .intro__title {
    margin-bottom: clamp(2.2rem, 4.8vh, 3.6rem);
  }

  body.hub-2a .intro__dek {
    margin-bottom: clamp(2.8rem, 5.5vh, 4.4rem);
  }

  body.hub-2a .intro__body {
    margin-top: 0;
  }
}

body.hub-2a .intro__body p {
  color: #fff;
  font-family: var(--hub2a-intro-body-font, "Merriweather", var(--font-serif));
  font-size: var(--hub2a-intro-body-size, 1.02rem);
  font-weight: var(--hub2a-intro-body-weight, 400);
  font-style: var(--hub2a-intro-body-style, normal);
  letter-spacing: var(--hub2a-intro-body-track, 0em);
  line-height: var(--hub2a-intro-body-lead, 1.75);
  margin-bottom: var(--hub2a-intro-body-gap, 1.35em);
}

body.hub-2a .intro__body p:first-child::first-letter {
  /* Phones never boot the text dial, so the fallback has to be the desktop teal.
     A rem gap stays the same on every phone; 0.4em blew up wherever the
     browser sized it off the enlarged drop cap. */
  color: var(--hub2a-accent, #86fddf);
  font-family: "Noe Display", var(--font-display);
  font-weight: 700;
  -webkit-initial-letter: 2;
  initial-letter: 2;
  margin-right: 0.4rem;
  padding: 0;
  /* Let initial-letter own the box — don’t fight it with float/size. */
  float: none;
  font-size: unset;
  line-height: unset;
}

@supports not (initial-letter: 2) {
  body.hub-2a .intro__body p:first-child::first-letter {
    float: left;
    font-size: calc(
      var(--hub2a-intro-body-size, 1.02rem)
      * var(--hub2a-intro-body-lead, 1.75)
      * 2
    );
    line-height: 1;
    margin: 0 0.4rem 0 0;
    padding-top: 0.12em;
  }
}

body.hub-2a .qm-logo--presents .qm-logo__mark,
body.hub-2a .qm-logo--presents .qm-logo__quanta {
  fill: var(--hub2a-accent, #86fddf);
  /* Soft dark halo — keeps mint readable when the shaft still brushes the logo. */
  filter: drop-shadow(0 0 0.55rem rgba(0, 8, 12, 0.55));
}

body.hub-2a .intro__kicker {
  text-shadow: 0 0 0.7rem rgba(0, 8, 12, 0.5), 0 1px 2px rgba(0, 0, 0, 0.35);
}

body.hub-2a .hub2a-membrane-panel .hub2a-hex-color__row {
  display: flex;
  gap: 8px;
  align-items: center;
  width: 100%;
}

body.hub-2a .hub2a-membrane-panel .hub2a-hex-color__row input[type='color'] {
  flex: 0 0 36px;
  width: 36px;
  height: 28px;
  padding: 0;
  border: 1px solid rgba(243, 239, 230, 0.28);
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
}

body.hub-2a .hub2a-membrane-panel .hub2a-hex-color__hex {
  flex: 1 1 auto;
  display: inline-flex;
  align-items: center;
  min-width: 0;
  height: 28px;
  padding: 0 8px;
  border: 1px solid rgba(243, 239, 230, 0.28);
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.35);
  color: #f3efe6;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  letter-spacing: 0.02em;
  box-sizing: border-box;
}

body.hub-2a .hub2a-membrane-panel .studio-look__ctrl--check {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

body.hub-2a .hub2a-membrane-panel .studio-look__ctrl--check input[type='checkbox'] {
  width: 18px;
  height: 18px;
  accent-color: #db714d;
  cursor: pointer;
}

body.hub-2a .hub2a-membrane-panel .studio-look__ctrl--check {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

body.hub-2a .hub2a-membrane-panel .studio-look__ctrl--check input[type='checkbox'] {
  width: 18px;
  height: 18px;
  accent-color: #7ed9c8;
  cursor: pointer;
}

/* Live membrane dials — left dock, collapses to a slim tab. */
body.hub-2a .hub2a-membrane-panel-dock {
  position: fixed;
  top: calc(var(--header-h, 46px) + 12px);
  left: 12px;
  z-index: 40;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 0;
  max-height: calc(100vh - var(--header-h, 46px) - 24px);
}

body.hub-2a .hub2a-membrane-panel__rails {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
}

body.hub-2a .hub2a-membrane-panel__studio {
  display: block;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  margin: 0;
  padding: 0.7rem 0.35rem;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  background: rgba(8, 18, 22, 0.88);
  color: #d7f4f0;
  font: 600 0.72rem/1.1 Pangram, system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
}

body.hub-2a .hub2a-membrane-panel__studio:hover {
  border-color: rgba(243, 239, 230, 0.4);
  color: #fff;
}

body.hub-2a .hub2a-membrane-panel__tab {
  flex: 0 0 auto;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  margin: 0;
  padding: 0.7rem 0.35rem;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  background: rgba(8, 18, 22, 0.88);
  color: #d7f4f0;
  font: 600 0.72rem/1.1 Pangram, system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}

body.hub-2a .hub2a-membrane-panel__tab.is-active {
  border-color: rgba(243, 239, 230, 0.45);
  color: #fff;
}

body.hub-2a .hub2a-text-font {
  width: 100%;
  margin-top: 4px;
  padding: 4px 6px;
  border: 1px solid rgba(243, 239, 230, 0.28);
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.35);
  color: #f3efe6;
  font: 500 0.72rem/1.2 Pangram, system-ui, sans-serif;
}

body.hub-2a .hub2a-text-style-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 10px;
}

body.hub-2a .hub2a-text-chapter {
  border-top: 1px solid rgba(243, 239, 230, 0.14);
}

body.hub-2a .hub2a-text-chapter__head {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  column-gap: 8px;
  width: 100%;
  margin: 0;
  padding: 10px 2px;
  border: 0;
  background: transparent;
  color: #f3efe6;
  text-align: left;
  cursor: pointer;
}

body.hub-2a .hub2a-text-chapter__title {
  grid-column: 1;
  font: 700 0.92rem/1.2 "Pangram", system-ui, sans-serif;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

body.hub-2a .hub2a-text-chapter__note {
  grid-column: 1;
  font: 400 0.68rem/1.35 "Pangram", system-ui, sans-serif;
  color: rgba(215, 244, 240, 0.62);
}

body.hub-2a .hub2a-text-chapter__chev {
  grid-row: 1 / span 2;
  grid-column: 2;
  align-self: center;
  font: 600 1rem/1 ui-monospace, Menlo, monospace;
  color: rgba(215, 244, 240, 0.7);
}

body.hub-2a .hub2a-text-chapter__body {
  padding: 0 0 12px;
}

body.hub-2a .hub2a-text-layout {
  padding: 0 0 8px;
  margin-bottom: 6px;
  border-bottom: 1px dashed rgba(243, 239, 230, 0.12);
}

body.hub-2a .hub2a-text-role {
  padding: 8px 0 10px;
  border-bottom: 1px solid rgba(243, 239, 230, 0.08);
}

body.hub-2a .hub2a-text-role:last-child {
  border-bottom: 0;
}

body.hub-2a .hub2a-text-role__title {
  margin: 0 0 6px;
  font: 600 0.72rem/1.2 "Pangram", system-ui, sans-serif;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(215, 244, 240, 0.82);
}

body.hub-2a .hub2a-membrane-panel-dock.is-open .hub2a-membrane-panel__tab {
  border-radius: 0 0 0 8px;
  border-right: none;
}

body.hub-2a .hub2a-membrane-panel-dock.is-collapsed .hub2a-membrane-panel__tab {
  border-radius: 8px;
}

body.hub-2a .hub2a-membrane-panel {
  position: relative !important;
  top: auto !important;
  left: auto !important;
  right: auto !important;
  width: 280px !important;
  max-height: calc(100vh - var(--header-h, 46px) - 24px);
  overflow: auto;
  border-radius: 0 8px 8px 0 !important;
  margin: 0 !important;
}

body.hub-2a .hub2a-membrane-panel[hidden] {
  display: none !important;
}

/* Slim roundness dock — always on (no ?dials=1 needed). */
body.hub-2a .hub2a-round-dock {
  position: fixed;
  top: calc(var(--header-h, 46px) + 12px);
  left: 12px;
  z-index: 40;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  max-height: calc(100vh - var(--header-h, 46px) - 24px);
}

body.hub-2a .hub2a-round-dock__tab {
  flex: 0 0 auto;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  margin: 0;
  padding: 0.7rem 0.35rem;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 0 8px 8px 0;
  background: rgba(8, 18, 22, 0.88);
  color: #d7f4f0;
  font: 600 0.72rem/1.1 Pangram, system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}

body.hub-2a .hub2a-round-dock.is-open .hub2a-round-dock__tab {
  border-radius: 0;
  border-right: none;
}

body.hub-2a .hub2a-round-dock.is-collapsed .hub2a-round-dock__tab {
  border-radius: 8px;
}

body.hub-2a .hub2a-round-panel {
  width: 240px;
  max-height: calc(100vh - var(--header-h, 46px) - 24px);
  overflow: auto;
  padding: 0.85rem 0.9rem 1rem;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-left: none;
  border-radius: 0 10px 10px 0;
  background: rgba(8, 18, 22, 0.92);
  color: #e8f7f4;
  backdrop-filter: blur(8px);
}

body.hub-2a .hub2a-round-panel[hidden] {
  display: none !important;
}

body.hub-2a .hub2a-round-panel__kicker {
  margin: 0 0 0.2rem;
  font: 600 0.65rem/1 Pangram, system-ui, sans-serif;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7ed9c8;
}

body.hub-2a .hub2a-round-panel__title {
  margin: 0 0 0.35rem;
  font: 700 1.05rem/1.15 "Noe Display", Georgia, serif;
  color: #fff;
}

body.hub-2a .hub2a-round-panel__note {
  margin: 0 0 0.85rem;
  font: 400 0.72rem/1.35 Pangram, system-ui, sans-serif;
  color: rgba(215, 244, 240, 0.72);
}

body.hub-2a .hub2a-round-panel__ctrl {
  display: grid;
  gap: 0.28rem;
  margin: 0 0 0.85rem;
}

body.hub-2a .hub2a-round-panel__label {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  font: 600 0.78rem/1.2 Pangram, system-ui, sans-serif;
}

body.hub-2a .hub2a-round-panel__val {
  font-variant-numeric: tabular-nums;
  color: #7ed9c8;
}

body.hub-2a .hub2a-round-panel__ctrl input[type='range'] {
  width: 100%;
  accent-color: #7ed9c8;
  cursor: pointer;
}

body.hub-2a .hub2a-round-panel__hint {
  font: 400 0.65rem/1.3 Pangram, system-ui, sans-serif;
  color: rgba(215, 244, 240, 0.55);
}

body.hub-2a .hub2a-round-panel__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  margin-top: 0.35rem;
}

body.hub-2a .hub2a-round-panel__set,
body.hub-2a .hub2a-round-panel__reset {
  padding: 0.45rem 0.6rem;
  border-radius: 6px;
  font: 600 0.7rem/1 Pangram, system-ui, sans-serif;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}

body.hub-2a .hub2a-round-panel__set {
  border: 1px solid rgba(126, 217, 200, 0.55);
  background: rgba(126, 217, 200, 0.16);
  color: #7ed9c8;
}

body.hub-2a .hub2a-round-panel__set:hover {
  background: rgba(126, 217, 200, 0.28);
}

body.hub-2a .hub2a-round-panel__reset {
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: transparent;
  color: #d7f4f0;
}

body.hub-2a .hub2a-round-panel__reset:hover {
  border-color: rgba(126, 217, 200, 0.55);
  color: #7ed9c8;
}

body.hub-2a .hub2a-round-panel__note strong {
  color: #7ed9c8;
  font-weight: 600;
}

body.hub-2a .intro__head {
  /* Nudge Quanta Magazine presents a touch closer to the top edge. */
  padding-top: calc(clamp(3.25rem, 8.8vh, 6rem) - 10px);
}

body.hub-2a .intro__copy {
  position: relative;
  z-index: 1;
}

body.hub-2a .intro__title {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: none;
  gap: 0.01em;
  line-height: 0.98;
  color: var(--hub2a-title, var(--gold));
  /* Match first-line size, then shrink the whole stack ~0.85×. */
  font-size: clamp(calc(4.2rem * 0.85), calc(11.5vw * 0.85), calc(10.5rem * 0.85));
}

body.hub-2a .intro__title-line {
  display: block;
  text-align: center;
  white-space: nowrap;
  font-size: 0.78em;
}

/*
 * Hub 2A — phones / tablets only (≤900px).
 * Desktop dials + layout above this breakpoint stay untouched.
 */
@media (max-width: 900px) {
  /* Compact top chrome — presents sits closer to the viewport edge. */
  body.hub-2a .intro {
    padding-inline: 1.1rem;
    padding-top: 0.15rem;
    padding-bottom: clamp(2.4rem, 8vh, 5rem);
  }

  body.hub-2a .intro__head {
    padding-top: max(0px, calc(clamp(0.55rem, 2vh, 1.1rem) - 10px));
  }

  /* Phones keep a tighter stack; iPad (768–900) inherits the tripled desktop air. */
  body.hub-2a .intro__presents {
    --logo-w: clamp(132px, 42vw, 168px);
    gap: 0.4rem;
    margin: 0 0 var(--hub2a-intro-stack-gap, clamp(2.15rem, 5.6vh, 3.2rem));
  }

  body.hub-2a .intro__title {
    font-size: clamp(4rem, 16.5vw, 7rem);
    gap: 0.04em;
    line-height: 0.92;
  }

  body.hub-2a .intro__title-line {
    font-size: 0.9em;
    white-space: normal;
  }

  body.hub-2a .intro__dek {
    max-width: min(100%, 21.5rem);
  }

  body.hub-2a .intro__body {
    max-width: min(100%, 22.5rem);
    margin-top: 0;
    margin-inline: auto;
  }
}

/* ~480 CSS px (Pixel 7 Pro). Narrower phones and iPad keep their own measure. */
@media (min-width: 450px) and (max-width: 540px) {
  body.hub-2a .intro {
    padding-inline: 0.85rem;
  }

  body.hub-2a .intro__title {
    font-size: clamp(4rem, 17.6vw, 7rem);
  }

  body.hub-2a .intro__dek {
    max-width: 25rem;
    font-size: 1.36rem;
  }

  body.hub-2a .intro__body {
    max-width: 25rem;
  }
}

@media (max-width: 767px) {
  body.hub-2a .intro {
    --hub2a-intro-stack-gap: clamp(2.15rem, 5.6vh, 3.2rem);
    /* Close the orphan teal gap before §2 / cutouts. */
    padding-bottom: clamp(1rem, 3vh, 1.75rem);
  }

  body.hub-2a .intro__kicker {
    margin-bottom: var(--hub2a-intro-stack-gap);
  }

  /*
   * Visible gap under the title and under the subhed, 83% of the space
   * between the letters. The margin alone was only part of that space;
   * the rest sits in the line box under the type.
   */
  body.hub-2a .intro__title {
    margin-bottom: calc(var(--hub2a-intro-stack-gap) * 0.83 - 10px);
  }

  body.hub-2a .intro__dek {
    margin-bottom: calc(var(--hub2a-intro-stack-gap) * 0.83 - 8px);
  }

  body.hub-2a .intro__dek {
    color: #fff;
    font-style: italic;
  }

  /* Desktop keeps the baked 19vh intro→§2 air (inline on <body>); phones need
     a clear floater band — !important beats the inline custom property. */
  body.hub-2a {
    --hub2a-intro-gap: 36vh !important;
  }

  body.hub-2a .chase-hold {
    margin-top: var(--hub2a-intro-gap, 36vh);
    padding-top: 0.5rem;
  }
}

@media (max-width: 767px) {
  /* Phone §2 feature stack — same rhythm as §3 stacked cards. */
  body.hub-2a .chase .kicker {
    margin-bottom: 1.25rem;
  }

  body.hub-2a .chase__title {
    margin-bottom: 1.15rem;
  }

  body.hub-2a .chase .byline {
    margin-bottom: 1.35rem;
  }

  body.hub-2a .chase__dek {
    margin-bottom: 2.35rem;
  }

  /* Big floater band between intro copy and the membrane rim. */
  body.hub-2a .chase-wrap.hub2a-membrane {
    padding-top: clamp(16rem, 42vh, 26rem);
  }

  body.hub-2a .hub2a-parallax {
    top: -62vh;
    height: calc(78% + 62vh + 40vh);
  }

  body.hub-2a .chase {
    margin-top: clamp(2rem, 6vh, 3.4rem);
  }

  /* Halfway back up from the last drop. */
  body.hub-2a .hub2a-parallax__plate--membrane {
    top: 10%;
    bottom: -6%;
  }

  /* 5% smaller than 0.94, scaled from the rim’s highest pixel so that point stays put. */
  body.hub-2a .hub2a-parallax__plate--membrane .hub2a-parallax__img {
    --s: 0.893;
    --membrane-pin: 0.871%;
    transform-origin: center 37.48%;
  }

  body.hub-2a .hub2a-parallax__plate--rna {
    top: 2%;
    bottom: 22%;
  }

  body.hub-2a .hub2a-parallax__plate--rna .hub2a-parallax__img {
    object-position: center 8%;
  }

  body.hub-2a .hub2a-parallax__plate--lipids .hub2a-parallax__img {
    object-position: center 12%;
  }

  body.hub-2a .hub2a-parallax__plate--mols .hub2a-parallax__img {
    object-position: center 14%;
  }

  /* Open the baked cone a bit wider on phones — tall viewports squish it.
     iPad / laptop / desktop keep the default --hub2a-cone-sx.
     Keep the PNG cone bright (screen-blend image of light, not a real light). */
  body.hub-2a .hub2a-spot__cone {
    transform: scaleX(calc(var(--hub2a-cone-sx, 1.85) * 1.28));
    opacity: 1;
  }

  body.hub-2a .hub2a-rays {
    opacity: 0.85;
  }

  /* No WebGL on phones — hide membrane + spot canvases; keep CSS cone. */
  body.hub-2a #hub2a-membrane-stage,
  body.hub-2a .hub2a-membrane__stage,
  body.hub-2a .hub2a-spot canvas,
  body.hub-2a #hubgl-root,
  body.hub-2a .hubgl {
    display: none !important;
  }

  body.hub-2a .hub2a-parallax {
    display: block;
  }

  /* Wash sits ABOVE parallax cutouts and reaches into §3; stories-hold stays higher.
     Harder top fade on phones so the lit membrane bowl can’t white-slab the title. */
  body.hub-2a .hub2a-black-rest {
    top: calc(var(--hub2a-black-top, 42%) - 48px);
    bottom: -120vh;
    z-index: 3;
    background: linear-gradient(
      180deg,
      rgba(0, 0, 0, 0) 0%,
      rgba(0, 0, 0, 0.72) 8%,
      rgba(0, 0, 0, 0.94) 16%,
      #000 24%,
      #000 100%
    );
  }

  body.hub-2a .hub2a-membrane__link {
    z-index: 3;
  }

  body.hub-2a .stories-hold {
    position: relative;
    z-index: 4;
  }
}

@media (max-width: 900px) {
  /* Vertical scroll only — rays / membrane bleed must not open a sideways pan. */
  html:has(body.hub-2a),
  body.hub-2a {
    overflow-x: hidden;
    overscroll-behavior-x: none;
    max-width: 100%;
  }

  body.hub-2a {
    width: 100%;
    touch-action: pan-y;
  }

  body.hub-2a .series {
    overflow-x: clip;
    max-width: 100%;
    width: 100%;
  }

  /*
   * Hard black floor behind the membrane GL — starts mid-model so the upper
   * teal gradient still reads, while shell gaps / lower bowl sit on black.
   */
  body.hub-2a .hub2a-black-floor {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    top: var(--hub2a-black-floor-top, 30%);
    bottom: -40vh;
    z-index: 0;
    pointer-events: none;
    background: #000;
  }

  /* Match card author mint on phones/tablets. */
  body.hub-2a .chase .byline__prefix {
    color: var(--hub2a-accent, #86fddf);
  }

  body.hub-2a .chase .byline__name {
    color: var(--hub2a-accent, #86fddf);
  }

  /* Feature + card CTAs — a touch larger on phones/tablets. */
  body.hub-2a .chase .cta,
  body.hub-2a .stories .cta {
    background: var(--hub2a-accent, #86fddf);
    font-family: var(--hub2a-essay-btn-font, "Pangram", var(--font-sans));
    font-size: 0.8rem;
    font-weight: var(--hub2a-essay-btn-weight, 700);
    font-style: var(--hub2a-essay-btn-style, normal);
    letter-spacing: var(--hub2a-essay-btn-track, 0.12em);
    line-height: var(--hub2a-essay-btn-lead, 1);
    padding: 1.1rem 1.15rem;
    border-radius: 14px;
  }

  body.hub-2a .chase .cta:hover {
    background: var(--hub2a-essay-btn-hover, var(--hub2a-card-btn-hover, var(--hub2a-cta-hover, #f26d40)));
  }

  body.hub-2a .stories .cta:hover {
    background: var(--hub2a-card-btn-hover, var(--hub2a-cta-hover, #f26d40));
  }

  /* §2 membrane — same stage geometry / padding band as desktop so the
     view-offset camera lock doesn’t skew when the viewport narrows. */
  body.hub-2a .chase-hold {
    height: auto;
    min-height: 0;
  }

  body.hub-2a .chase-wrap.hub2a-membrane {
    min-height: 0;
    padding-bottom: var(--hub2a-essay-section-pad, 1rem);
    overflow-x: clip;
    overflow-y: visible;
    /*
     * Membrane / floaters stay crop-locked. As the phone widens, shrink this
     * pad so the essay stack + black wash ride UP into the bowl (not the GL).
     * 390 → ~9.25rem · 820 → ~5.3rem
     * Phones keep this as-is; iPad (768–900) adds 20px below.
     */
    padding-top: clamp(3.5rem, calc(9.25rem - (100vw - 390px) * 0.14), 9.25rem);
  }

  /* iPad / small laptop in the ≤900 band — drop feature text further off the floaters. */
  @media (min-width: 768px) {
    body.hub-2a .chase-wrap.hub2a-membrane {
      padding-top: calc(
        clamp(3.5rem, calc(9.25rem - (100vw - 390px) * 0.14), 9.25rem) + 48px
      );
    }
  }

  body.hub-2a .hub2a-membrane__stage {
    left: 0;
    right: 0;
    width: auto;
    top: -48vh;
    height: calc(58% + 48vh + 24vh);
    transform: none;
    overflow: visible;
    z-index: 1; /* above hard black floor */
  }

  body.hub-2a .hub2a-membrane__link {
    left: 0;
    right: 0;
    width: auto;
    top: -48vh;
    height: calc(58% + 48vh + 24vh);
    z-index: 2;
  }

  body.hub-2a .hub2a-membrane__stage canvas {
    overflow: visible;
  }

  body.hub-2a .hub2a-black-rest {
    top: var(--hub2a-black-top, 42%);
  }

  body.hub-2a .chase {
    width: min(100% - 1.5rem, 36rem);
    padding: 0 0.75rem;
    /* Extra lift as width grows — fills the bilayer gap without moving the GL. */
    margin-top: clamp(-6.5rem, calc((420px - 100vw) * 0.12), 0rem);
  }

  /*
   * Feature title — hero-sized vs dek/byline. On phones the breaks lock three
   * lines; fit JS keeps the largest size that stays on those three.
   */
  body.hub-2a .chase__title-break {
    display: none;
  }

  /* Phones: three locked lines. Wider phones were fitting the whole title on two. */
  @media (max-width: 767px) {
    body.hub-2a .chase__title-break {
      display: inline;
    }
  }

  body.hub-2a .chase__title {
    font-size: clamp(1.95rem, 1.05rem + 5.4vw, 3.2rem);
    line-height: 1.08;
  }

  /* §3 — stacked cards with generous gaps (need not fit one screen). */
  body.hub-2a .stories-hold {
    --hub2a-stories-scale: 1;
    height: auto;
    min-height: 0;
    display: block;
    align-items: stretch;
    padding: 0 0 calc(clamp(3rem, 7.5vh, 4.9rem) + 0.5rem);
  }

  body.hub-2a .stories-sticky {
    transform: none;
    width: 100%;
    padding-inline: 0.85rem;
    padding-block: 0;
  }

  body.hub-2a .stories {
    display: flex;
    flex-direction: column;
    grid-template-columns: none;
    width: 100%;
    max-width: 40rem;
    margin-inline: auto;
    min-height: 0;
    height: auto;
    padding: 0;
    /* Gap only between story cards — NOT between explore CTA and RNA. */
    gap: 0;
    row-gap: 0;
  }

  body.hub-2a .stories > .story + .story {
    margin-top: 3.1rem;
  }

  body.hub-2a .stories__cta {
    width: max-content;
    max-width: min(100%, 22rem);
    font-size: clamp(1.22rem, 4.8vw, 1.4rem);
    padding-top: calc(var(--hub2a-explore-rule-air, 2.4rem) * 1.35);
    /* Was 0.85rem and sat after a 3.1rem flex gap — that was the huge void. */
    margin-bottom: 0.2rem;
  }

  /* Phones: button→line was ~12px while line→explore copy was ~58px.
     The rule is pinned to the sentence, so extra padding pushes the line,
     the explore copy, and the cards down together. */
  @media (max-width: 767px) {
    body.hub-2a .stories__cta {
      padding-top: calc(
        2 * (var(--hub2a-explore-air) - var(--hub2a-explore-pad))
        - var(--hub2a-essay-section-pad, 1rem)
      );
    }
  }

  /* Pull the first model window up under the chevron (empty canvas reads as air). */
  body.hub-2a .stories__cta + .story .story__visual {
    margin-top: -2.75rem;
  }

  body.hub-2a .story,
  body.hub-2a .stories.is-revealed .story,
  body.hub-2a .stories:not(.is-revealed) .story {
    display: flex;
    flex: 0 0 auto;
    width: 100%;
    min-height: 0;
    padding: 0;
    /* Kill desktop sideways hold nudges on stacked cards. */
    transform: none;
    opacity: 1;
  }

  /* Stacked + centered: model on top, type underneath — no side-by-side overlap. */
  body.hub-2a .story__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2.35rem;
    width: 100%;
    max-width: 22rem;
    margin-inline: auto;
    height: auto;
    animation: none;
  }

  body.hub-2a .story--flip .story__inner {
    flex-direction: column;
  }

  body.hub-2a .story--flip .story__visual,
  body.hub-2a .story--flip .story__copy {
    order: unset;
  }

  body.hub-2a .story__copy {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    text-align: center;
    min-width: 0;
    width: 100%;
    padding-block: 0.1rem;
    /* Desktop-only pull — don’t lift type into the stacked model window. */
    margin-top: 0;
  }

  body.hub-2a .story--flip .story__copy {
    align-items: center;
    text-align: center;
  }

  body.hub-2a .story__visual {
    width: min(86vw, 21rem);
    height: auto;
    aspect-ratio: 1;
    max-height: none;
    min-height: 0;
    align-self: center;
    margin-top: 0;
    margin-bottom: 0.35rem;
    animation: none;
  }

  /* RNA card only — 1.35× window; camera fill stays ≥1 so no scissor crop. */
  body.hub-2a .story__visual:has(.story__figure--strand),
  body.hub-2a .story__visual:has([data-figure="strand"]) {
    width: min(calc(86vw * 1.35), calc(21rem * 1.35));
  }

  /* Keep figures inside the scissor box — size comes from camera fill, not CSS scale. */
  body.hub-2a .story__figure,
  body.hub-2a .story__figure--strand,
  body.hub-2a .story__figure--metabolism {
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    transform: none;
  }

  /* Stacked cards: secondary type already tracks essay; keep roomy gaps. */
  body.hub-2a .stories .kicker {
    margin-bottom: 1.25rem;
  }

  body.hub-2a .story__title {
    /* Phones skip text dials. 2.625rem is the largest size that still
       holds the longest title to 3 lines on a typical phone. */
    font-size: var(--hub2a-card-title-size, 2.625rem);
    line-height: var(--hub2a-card-title-lead, 1.12);
    margin-bottom: 1.15rem;
    text-align: center;
  }

  body.hub-2a .story .byline {
    margin-bottom: 1.35rem;
    text-align: center;
  }

  body.hub-2a .story__dek {
    margin-bottom: 2.35rem;
    margin-inline: auto;
    max-width: 20rem;
    text-align: center;
  }

  body.hub-2a .stories .cta {
    margin-top: 0.15rem;
    align-self: center;
  }

  /* Hopping arrow under each card button — more stories below. */
  body.hub-2a .story__hop {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 1.15rem;
    color: #fff;
  }

  /* No hop after the last (metabolism) card — nothing below to point to. */
  body.hub-2a .stories > .story:last-of-type .story__hop {
    display: none;
  }

  body.hub-2a .story__hop .stories__chevron {
    width: 2.05rem;
    height: 2.05rem;
    color: #fff;
  }

  body.hub-2a .stories__cta > .stories__chevron {
    width: 2.75rem;
    height: 2.75rem;
    color: var(--hub2a-accent, #86fddf);
  }

  /* Studio docks — keep out of the way on touch devices. */
  body.hub-2a .hub2a-membrane-panel-dock,
  body.hub-2a .hub2a-round-dock {
    display: none;
  }
}
