/* ==========================================================================
   LIMM — Clinical Editorial
   home.css · hero, featured event, research, numbers, news, publications,
             photo

   Home is image-led: the group photograph lands inside the first screen on a
   phone, the conference closes the page as its one dark block,
   and every section afterwards leads with a picture rather than a paragraph.
   ========================================================================== */

/* ---- Section rhythm ------------------------------------------------------
   The page breathes unevenly on purpose. Research opens wide after the dark
   block, the ledger and the counts are held close, and the two picture
   sections get room again. --section-y stays the middle setting.            */
.section--close { padding-block: clamp(68px, 6.2vw, 104px); }

/* Research now ends on the films band, and three film cards need far less air
   under them than a paragraph did: the run-out into the dark block is about a
   third shorter than --section-y at every width. */
.research { padding-bottom: clamp(46px, 5vw, 76px); }

/* ==========================================================================
   Hero

   One field, not two columns. The display line is set to a 13ch measure so it
   falls in four tapering lines — 663 / 576 / 490 / 370 at 1440 — and the
   photograph, bled to the page edge and a third larger than it was, climbs
   back up until its top edge sits level with the third of those lines. Lines
   two, three and four all end well short of the picture's edge, so the two
   masses interlock without a glyph ever crossing the image. The lede stretches
   to the picture's full height and pins the scroll cue to its foot, which is
   what closed the 420px of dead paper the old bottom-aligned row left behind.
   ========================================================================== */
.hero {
  position: relative;
  /* The one number the interlock is built on: it sets the leading of the
     display block AND, through --hero-rise below, the exact distance the
     photograph climbs, so the picture's head stays level with the top of the
     fourth line whatever the leading is. .display's own 0.98 was set for a
     one-line mark; over four stacked lines it closed the ascenders and
     descenders up into one solid mass, which is what read as stuffy. */
  --hero-lh: 1.00;

  /* Home's own display size. --text-hero is not touched: members.html and
     photo.html set their page titles from it and neither of them has a
     photograph to interlock with.

     110px at the top rather than 104. The extra 6% is paid for by the tighter
     tracking below, so the four lines still measure 664 / 440 / 546 / 419 at
     1440 — within a pixel of what they measured before — and line three still
     stops 14px short of the photograph's edge, against the 15px it stopped
     short by before. Bigger and denser type, the same composition. */
  --hero-size: clamp(3.5rem, 1.06rem + 6.7vw, 6.875rem);   /* 56 → 110 */
  padding-bottom: clamp(40px, 5vw, 88px);
}

/* A whisper of atmosphere behind the display line — a diffuse breath of the
   accent on paper, not a gradient wash. It drifts, very slowly, so the frame
   is never quite still; see the motion block at the foot of this section. */
.hero::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset-block-start: -4%;
  inset-inline-start: -8%;
  width: min(64%, 720px);
  aspect-ratio: 3 / 2;
  background: radial-gradient(closest-side at 42% 48%,
              color-mix(in srgb, var(--accent) 11%, transparent) 0%,
              color-mix(in srgb, var(--accent) 4%, transparent) 52%,
              transparent 78%);
  pointer-events: none;
}

.hero__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding-block: 14px;
  border-bottom: 1px solid var(--rule);
  color: var(--navy);
}
/* Only a second span — a right-hand item such as a coordinate pair — is held
   on one line. Scoped to `span + span` so the affiliation line, which is the
   only span today, still wraps on a narrow screen instead of overflowing. */
.hero__meta span + span { color: var(--muted); white-space: nowrap; }
.hero__meta em { font-style: normal; color: var(--accent-ink); }

/* Four lines, tapering. The measure is set in ch rather than in columns so the
   same four breaks hold at every width — ch scales with the face, a column
   does not. Greedy wrapping, not .display's balance: balance would even the
   four lines out and take away the taper the picture nests into. */
/* --hero-lh is the one number the interlock is built on. It sets the leading
   of the display block AND, through --hero-rise below, the exact distance the
   photograph climbs, so the picture's head stays level with the top of the
   fourth line whatever the leading is. .display's own 0.98 was set for a
   one-line mark; over four stacked lines it closed the ascenders and
   descenders up into a solid block, which is what read as stuffy. 1.06 opens
   the stack without loosening the taper the picture nests into — the line
   widths are unchanged, so line three still clears the picture's edge by the
   same 15px it did before. */
/* Larger, heavier and tighter than it was, and set on the same four lines.
   Weight 320 rather than 300 — Fraunces is loaded across 300-600, so this is a
   setting of the face already on the page and not another face. -0.044em
   rather than .display's -0.022em: at 110px the loose tracking left the words
   reading as separate objects instead of as one carved line. Leading 1.00
   rather than 1.06, which at the larger size is 110px of leading against the
   old 110.2px — the stack is no tighter in absolute terms than it was, only in
   proportion to the glyphs, so it gains density without the closed-up mass
   that 0.98 produced. */
.hero__title {
  grid-column: 1 / 10;
  font-size: var(--hero-size);
  font-weight: 320;
  font-variation-settings: "opsz" 144, "SOFT" 0, "WONK" 0, "wght" 320;
  letter-spacing: -0.044em;
  line-height: var(--hero-lh);
  margin-block: clamp(36px, 4vw, 68px) 0;
  max-width: 11.5ch;
  text-wrap: normal;
}

/* stretch, not end: the lede fills the picture's height and puts the cue at
   its foot, so neither column has a hole in it. */
.hero__body { align-items: stretch; row-gap: 40px; margin-top: 0; }

.hero__lede { grid-column: 1 / 6; display: flex; flex-direction: column; gap: 28px; }
/* 1.55 rather than 1.5, and 38 characters rather than 34: at 21px a 34ch
   column is a narrow enough measure that four short lines stack up as a block
   of grey. The wider measure and the extra half-step of leading give the same
   sentence room without leaving the five-column lede. */
.hero__sub {
  font-size: var(--text-lead);
  line-height: 1.55;
  color: var(--navy);
  max-width: 38ch;
  text-wrap: pretty;
}
.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* The climb. Bottom-aligned in the row and pulled up by its own margin, so the
   picture's head rises into the display block while its foot stays level with
   the buttons. --hero-rise is the one number that tunes the interlock. */
.hero__figure {
  grid-column: 6 / 13;
  margin: 0;
  align-self: end;
}
.hero__figure img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: 50% 42%;
  background: var(--paper-2);
}
.hero__cap {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding-top: 12px;
  border-top: 1px solid var(--ink);
  margin-top: 12px;
  color: var(--navy);
}
.hero__cap span:last-child { color: var(--muted); }

/* From 1024 up the photograph runs off the right edge of the page — it breaks
   the last column rule on purpose. Past the shell maximum "the edge" is the
   shell's, not the window's: the centred hairline frame is the stronger figure
   there, and the picture measures the same 816px either way. */
@media (min-width: 1024px) {
  /* Exactly one line of the display face. The picture's head then sits level
     with the top of "of medicine." — the short fourth line — and tucks into
     the notch the taper leaves, without ever reaching line three, whose right
     edge clears the picture by only 15px. */
  .hero { --hero-rise: calc(var(--hero-size) * var(--hero-lh)); }

  /* Narrow desktops. Between 1024 and 1280 the twelve columns shrink faster
     than a vw-driven display size does, so the picture's left edge — column
     six — walks in under the tail of line three while the type stays wide.
     Measured by scanning glyph pixels rather than line boxes, the italic tail
     of "century" was inking 30 pixels inside the photograph at 1024 and 9 at
     1280 before this pass. The ramp below tracks the grid instead of the
     window: 79px at 1024, 101px at 1280, where it meets the main ramp within
     a pixel. Above 1280 nothing changes.

     The line box still overlaps the picture's top strip by a few pixels at
     these widths — that box is the face's ascent and descent, and it is empty
     there. What matters is that no glyph reaches the picture, and none does. */
  @media (max-width: 1279px) {
    .hero { --hero-size: clamp(3.5rem, -0.7rem + 8.8vw, 6.875rem); }
  }
  .hero__lede { padding-top: clamp(18px, 2vw, 30px); }
  .hero__figure {
    margin-top: calc(var(--hero-rise) * -1);
    margin-right: calc(var(--page-x) * -1);
  }
  .hero__figure img { aspect-ratio: 5 / 4; }
  .hero__cap { margin-right: var(--page-x); }
}

/* No column of the hairline grid is drawn at full strength behind the picture,
   and none should be: .rule-grid divides the shell into twelve equal parts
   with no gutter, while .grid12 lays out twelve columns *plus* an 8-32px
   gutter, so the two only ever meet at the left edge. At 1440 the picture's
   edge stands at 624 and the sixth hairline at 611 — thirteen pixels apart,
   which reads as a mistake rather than as structure. The picture's own edge is
   the line instead, and it crosses the grid's closing rule at the page's right
   margin on purpose. */

.hero__cue {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: auto;
  padding-top: clamp(40px, 4vw, 64px);
  color: var(--muted);
}
.hero__cue::before {
  content: "";
  width: 56px; height: 1px; background: var(--rule);
}
.hero__cue svg { animation: nudge 2.4s var(--ease) infinite; }
@keyframes nudge {
  0%, 100% { transform: translateY(0); opacity: .5; }
  50%      { transform: translateY(4px); opacity: 1; }
}

/* Tablet: the picture leaves the column entirely and becomes a full-width band
   between the display block and the lede — the same rhythm the phone uses. */
@media (max-width: 1023px) {
  .hero__title  { grid-column: 1 / 7; max-width: 15ch; margin-bottom: 0; }
  .hero__lede   { grid-column: 1 / 7; }
  .hero__figure {
    grid-column: 1 / 7;
    order: -1;
    margin-inline: calc(var(--page-x) * -1);
  }
  .hero__figure img { aspect-ratio: 16 / 10; }
  .hero__cap    { margin-inline: var(--page-x); }
  .hero__body   { row-gap: 40px; margin-top: clamp(28px, 4vw, 44px); }
  .hero__cue    { padding-top: clamp(32px, 4vw, 48px); }
}

@media (max-width: 640px) {
  .hero { padding-bottom: clamp(28px, 5vw, 48px); }
  .hero::before { width: 92%; inset-inline-start: -18%; }
  .hero__meta { flex-direction: column; gap: 4px; padding-block: 11px; }
  .hero__title, .hero__lede, .hero__figure { grid-column: 1 / 5; }

  /* Sized so the sentence holds three lines from 320 up — at 320 the longest
     of them measures 244px in a 256px column — and so the photograph still
     lands well inside a 390 × 844 screen. */
  .hero__title {
    /* 11.4vw rather than 10.8. The room for it comes from the tighter tracking
       the block now carries at every width: at 320 the longest of the three
       lines measured 244px in a 256px column at -0.022em, and -0.044em gives
       back about eleven of those pixels, which is what the larger size then
       spends. Still three lines at 320, verified. */
    font-size: min(11.4vw, 3.0625rem);
    /* Slightly looser than the desktop stack: at 36-45px the face needs a
       little more than it does at 110px to stop three lines closing up. The
       photograph's cap below absorbs the growth, so the picture still lands
       inside the first screen — verified at 390 x 844 and 320 x 568. */
    line-height: 1.05;
    margin-block: 18px 0;
    max-width: none;
  }

  .hero__body { row-gap: 24px; margin-top: 22px; }
  /* Taller than the 4/3 it was: the group is small in a full-width band on a
     phone. 9/8 crops 7.8% off each side of a 4:3 original and the outermost
     member stands at 14%, so nobody is clipped.

     The cap is what keeps the promise that the photograph is in the first
     screen. The reserve is everything standing above it — the strip, the
     masthead, the meta line and the three display lines — which comes to
     about 340px and does not grow with the screen's height. On a tall phone
     nothing is clamped; on a 375 x 667 the picture gives back the 40px it
     would otherwise have hung below the fold. */
  .hero__figure img {
    aspect-ratio: 9 / 8;
    max-height: 60vh;
    max-height: max(180px, calc(100svh - 360px));
  }
  .hero__cap { padding-top: 10px; margin-top: 10px; }
  .hero__cue { padding-top: 26px; }
}

/* ---- Entrance ------------------------------------------------------------
   Declared only where motion is welcome, so a reader who has asked for less
   gets the page as written with nothing to undo. Every step is transform,
   clip-path or opacity on an element that blends with nothing, and none of it
   needs JavaScript: the page arrives the same way with scripts switched off. */
@media (prefers-reduced-motion: no-preference) {
  .hero__meta   { animation: rise-in 620ms var(--ease) both; }
  .hero__title  { animation: wipe-up 900ms var(--ease) 90ms both; }
  .hero__sub    { animation: rise-in 700ms var(--ease) 300ms both; }
  .hero__actions{ animation: rise-in 700ms var(--ease) 400ms both; }
  .hero__cue    { animation: rise-in 700ms var(--ease) 700ms both; }
  .hero__figure { animation: unveil 1100ms var(--ease) 180ms both; }
  .hero__figure img { animation: settle 1400ms var(--ease) 180ms both; }
  /* The one thing on the page that never stops: a 34-second drift of the
     accent breath behind the display block, about 2% of its own width. It is
     below the threshold at which you would call it movement, and a reader who
     has asked for less motion is never given the layer at all. */
  .hero::before { will-change: transform; animation: breath 34s var(--ease) 1s infinite alternate; }
}

@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
/* The display block is uncovered from its own baseline upward rather than
   faded, which keeps the four lines reading as one carved mass. */
@keyframes wipe-up {
  from { opacity: 0; clip-path: inset(0 0 100% 0); transform: translateY(18px); }
  40%  { opacity: 1; }
  to   { opacity: 1; clip-path: inset(0 0 0 0); transform: none; }
}
@keyframes unveil {
  from { clip-path: inset(0 0 18% 0); }
  to   { clip-path: inset(0 0 0 0); }
}
@keyframes settle {
  from { transform: scale(1.045); }
  to   { transform: none; }
}
@keyframes breath {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(2.5%, 1.5%, 0) scale(1.06); }
}

/* ==========================================================================
   Featured event — the page's one dark block
   ========================================================================== */
.event {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ink);
  color: var(--paper);
  display: flex;
  align-items: center;
  /* Asymmetric: the block still opens on a full measure of ink, but the foot
     of the page needs about a third less now that the credit line has gone. */
  padding-block: clamp(56px, 6vh, 88px) clamp(35px, 3.8vh, 55px);
}

/* The backdrop, its washes and the grain travel together: on a phone the whole
   layer shrinks to a band and everything inside follows. */
.event__bg { position: absolute; inset: 0; z-index: -1; overflow: hidden; }
/* The crop is chosen, not centred: it puts the left roof behind the display
   line as a silhouette and lets the right roof and the blossom sit clear of
   the poster, where the wash is thinnest. */
.event__bg img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 22% 62%;
}

/* Two washes. The first runs across: near-opaque under the text, thin under
   the palace so the roofs and the blossom read. The second darkens the top
   and the foot so the block seals against the paper above and below. */
.event__wash {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(101deg,
      rgba(6, 18, 31, 0.965) 0%,
      rgba(6, 18, 31, 0.945) 34%,
      rgba(7, 21, 36, 0.860) 50%,
      rgba(8, 24, 40, 0.560) 63%,
      rgba(8, 24, 40, 0.320) 80%,
      rgba(8, 24, 40, 0.260) 100%),
    linear-gradient(to bottom,
      rgba(6, 18, 31, 0.72) 0%,
      rgba(6, 18, 31, 0.10) 24%,
      rgba(6, 18, 31, 0.10) 58%,
      rgba(6, 18, 31, 0.78) 100%);
}

/* Film grain. Static, cheap, and only ever here. */
.event__grain {
  position: absolute;
  inset: 0;
  opacity: 0.15;
  mix-blend-mode: overlay;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

.event__inner { width: 100%; row-gap: 40px; align-items: center; }
.event__body  { grid-column: 1 / 8; }
.event__poster-cell { grid-column: 8 / 13; display: flex; justify-content: flex-end; }

/* The block carries no "0N": like the hero it is a featured surface, not a
   numbered section. Its marker is the accent rule and the kicker from the
   record file. */
.event__kicker {
  display: inline-flex;
  align-items: center;
  gap: 0.75em;
  color: var(--accent);
  margin-bottom: clamp(18px, 2vw, 28px);
}
.event__kicker::before {
  content: "";
  flex: none;
  width: 30px; height: 1px;
  background: var(--accent);
}

.event__title {
  font-size: clamp(3.5rem, 8vw, 8.5rem);
  font-weight: 300;
  line-height: 0.92;
  letter-spacing: -0.03em;
  margin-bottom: clamp(18px, 1.8vw, 28px);
  /* .display balances lines, which on a tablet splits the name at its hyphen
     ("PSST-" / "PR 2027"). Ordinary wrapping breaks at the space instead. */
  text-wrap: wrap;
}

/* Paper on ink needs more leading than ink on paper — light type on a dark
   ground blooms, and 1.45 closed the lines up. */
.event__sub {
  font-size: var(--text-lead);
  line-height: 1.55;
  color: color-mix(in srgb, var(--paper) 84%, transparent);
  max-width: 40ch;
  text-wrap: pretty;
  margin-bottom: clamp(28px, 3vw, 44px);
}

/* Meta grid — labels and values, hairline led. */
.event__meta {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 var(--gutter);
  max-width: 46rem;
  margin-bottom: clamp(30px, 3.2vw, 48px);
}
.event__meta > div {
  padding-block: 14px 16px;
  border-top: 1px solid color-mix(in srgb, var(--paper) 22%, transparent);
}
.event__meta dt { color: var(--accent); margin-bottom: 7px; }
.event__meta dd {
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--paper);
  text-wrap: pretty;
}
.event__meta dd small {
  display: block;
  font-size: 0.8125rem;
  color: color-mix(in srgb, var(--paper) 68%, transparent);
  margin-top: 2px;
}
.event__meta .event__meta-status dd { color: var(--accent); }

/* Switched off before the first paint. index.html reads data/home.json out of
   its own <head> and marks the root, so a section that is not running today is
   never painted and then taken away under the reader. home.js repeats the
   decision for the copy the server hands back. */
html.event-off .event { display: none; }

/* Countdown — the markup carries the block empty and hidden; home.js works out
   the number and un-hides it, because a figure typed into the page is wrong by
   one the next morning. The rule below is needed: `display: flex` beats the
   browser's own [hidden] rule. */
.event__count[hidden] { display: none; }

.event__count {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px clamp(18px, 2vw, 30px);
  padding-top: clamp(4px, 1vw, 12px);
  margin-bottom: clamp(28px, 3vw, 44px);
}
.event__count-value {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 144, "SOFT" 0, "WONK" 0;
  font-weight: 300;
  font-size: clamp(2.75rem, 5.4vw, 4.75rem);
  line-height: 0.9;
  letter-spacing: -0.035em;
  color: var(--accent);
  font-variant-numeric: lining-nums tabular-nums;
  white-space: nowrap;
}
/* "In session" / "Concluded" are words, not a count — they take a word's size. */
.event__count-value--word { font-size: clamp(1.875rem, 3.2vw, 2.875rem); letter-spacing: -0.02em; }
.event__count-side { display: flex; flex-direction: column; gap: 5px; padding-bottom: 0.25em; }
.event__count-label { color: color-mix(in srgb, var(--paper) 82%, transparent); }
.event__note {
  color: color-mix(in srgb, var(--paper) 66%, transparent);
  text-transform: none;
  letter-spacing: 0.03em;
  max-width: 54ch;
  line-height: 1.55;
}

.event__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 14px clamp(20px, 2.4vw, 34px); }

/* Solid accent call to action — ink on cyan, 8.1:1. */
.btn--accent {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
}
.btn--accent:hover,
.btn--accent:focus-visible {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--ink);
}
/* The default focus ring is the accent, which would vanish on this pill. */
.btn--accent:focus-visible { outline-color: var(--ink); }
.event__poster-link:focus-visible { outline-offset: 7px; }
.event__site { color: color-mix(in srgb, var(--paper) 88%, transparent); }
.event__site:hover { color: var(--accent); }

/* Poster — layered shadow, parallax from home.js, lift on hover.
   home.js adds --drift while the poster is actually drifting: the promise to
   the compositor is worth making then and not on a narrow screen, where the
   poster does not move at all. */
.event__poster {
  margin: 0;
  width: min(100%, 460px, 49vh);
  transform: translate3d(0, var(--parallax, 0px), 0);
  transition: transform var(--dur) var(--ease);
}
.event__poster--drift { will-change: transform; }
.event__poster img {
  display: block;
  width: 100%;
  height: auto;
  background: color-mix(in srgb, var(--navy) 60%, var(--ink));
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.30),
    0 10px 26px rgba(0, 0, 0, 0.34),
    0 34px 74px rgba(0, 0, 0, 0.40);
}
.event__poster-link { display: block; }
.event__poster-link:hover .event__poster,
.event__poster-link:focus-visible .event__poster {
  transform: translate3d(0, calc(var(--parallax, 0px) - 7px), 0);
}

@media (max-width: 1023px) {
  .event { padding-block: clamp(56px, 6vw, 88px) clamp(35px, 3.8vw, 55px); }
  .event__inner { row-gap: clamp(36px, 5vw, 56px); }
  /* Half and half on a tablet: the poster is the argument, not a footnote. */
  .event__body { grid-column: 1 / 4; }
  .event__poster-cell { grid-column: 4 / 7; }
  .event__poster { width: 100%; }
  .event__title { font-size: clamp(3rem, 8.6vw, 5rem); }
  .event__sub { font-size: var(--text-body); max-width: none; }
  .event__meta { max-width: none; }
}

@media (max-width: 640px) {
  /* The backdrop becomes a band across the top and the poster hangs over its
     lower edge; everything else stacks on the ink ground below. */
  .event {
    --band: clamp(230px, 38vh, 330px);
    display: block;
    padding-block: 0 clamp(35px, 5.6vw, 50px);
  }
  .event__bg { inset: 0 0 auto; height: var(--band); }
  .event__bg img { object-position: 50% 58%; }
  .event__wash {
    background: linear-gradient(to bottom,
      rgba(6, 18, 31, 0.62) 0%,
      rgba(6, 18, 31, 0.20) 34%,
      rgba(9, 26, 43, 0.72) 78%,
      var(--ink) 100%);
  }

  .event__inner { padding-top: calc(var(--band) * 0.42); row-gap: 0; }
  .event__body, .event__poster-cell { grid-column: 1 / 5; }

  .event__poster-cell { order: -1; justify-content: center; margin-bottom: clamp(28px, 6vw, 40px); }
  .event__poster { width: 80%; }
  .event__poster img { box-shadow: 0 2px 6px rgba(0,0,0,.34), 0 18px 44px rgba(0,0,0,.46); }

  .event__kicker { margin-bottom: 16px; }
  .event__title { font-size: min(3.5rem, 14vw); margin-bottom: 14px; }
  .event__sub { font-size: var(--text-body); margin-bottom: 26px; }

  /* Countdown before the meta grid on a phone: it is the one number that
     answers "when" before any label is read. */
  .event__body { display: flex; flex-direction: column; align-items: stretch; }
  .event__kicker { order: -5; align-self: flex-start; }
  .event__title  { order: -4; }
  .event__sub    { order: -3; }
  .event__count  { order: -2; margin-bottom: 22px; padding-top: 0; }
  .event__meta   { order: -1; margin-bottom: 30px; }
  .event__actions { order: 1; flex-direction: column; align-items: stretch; gap: 18px; }
  .event__actions .btn { justify-content: center; padding-block: 16px; }
  .event__site { align-self: center; }

  .event__meta > div { padding-block: 11px 13px; }
  .event__meta dd { font-size: 0.875rem; }
}

/* ==========================================================================
   01 / Research — three picture cards
   ========================================================================== */
.research__list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(26px, 2.8vw, 44px);
}

.research__card { display: flex; flex-direction: column; }

.research__n {
  padding-top: 13px;
  margin-bottom: clamp(12px, 1.2vw, 18px);
  border-top: 1px solid var(--rule);
  color: var(--accent-ink);
  transition: border-color var(--dur) var(--ease);
}
.research__card:hover .research__n,
.research__card:focus-within .research__n { border-top-color: var(--accent); }

/* The gap between a card's picture and its title. 26px under a picture that is
   itself 230px tall read as the title being stuck to the plate; 34px lets the
   serif line stand on its own. */
.research__fig {
  position: relative;
  margin: 0 0 clamp(22px, 2.2vw, 34px);
  overflow: hidden;
  background: var(--navy);
}
.research__fig img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  transform: scale(1.001);
  transition: transform 720ms var(--ease);
}
/* A navy veil sits over the picture and lifts on hover. The pictures are now
   dark low-key photographs rather than bright illustrations, so the veil is
   held to a whisper — enough to seat them on the paper, not enough to close
   up the shadows. */
.research__fig::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--ink) 18%, transparent) 0%,
    color-mix(in srgb, var(--ink) 4%, transparent) 62%,
    transparent 100%);
  transition: opacity var(--dur) var(--ease);
}
.research__card:hover .research__fig img,
.research__card:focus-within .research__fig img { transform: scale(1.045); }
.research__card:hover .research__fig::after,
.research__card:focus-within .research__fig::after { opacity: 0.3; }

/* 1.14, not 1.06: two of the three titles run to two lines at every width
   below 1440, and at 1.06 the descender of "Biomaterials" all but touched the
   line under it. The gap to the paragraph grew with it — 12px was less than
   the title's own leading, so the two blocks read as one. */
.research__title {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 96;
  font-weight: 400;
  font-size: clamp(1.5rem, 0.95rem + 1.25vw, 2.125rem);
  line-height: 1.14;
  letter-spacing: -0.022em;
  margin-bottom: clamp(14px, 1.4vw, 20px);
  text-wrap: balance;
}

/* This is the paragraph the owner pointed at. It was set at --text-sm, which
   put 45 characters of 14.6px type on a 27px rhythm inside a 411px column —
   small text on a wide measure, the combination that reads as cramped. At
   --text-body the same column measures 44 characters of 17px type, so the
   measure is unchanged in reading terms and every line has room. The tablet
   and phone rules used to raise it to --text-body one breakpoint at a time;
   they no longer have to. */
.research__desc {
  color: var(--navy);
  font-size: var(--text-body);
  line-height: 1.7;
  /* A ceiling, not a column. It is above every measure the three-across row
     and the pinned panel produce and only bites between 768 and 1023, where
     one column of body text would otherwise run to a hundred characters. */
  max-width: 58ch;
  text-wrap: pretty;
}

/* ---- Below 1024: the sequence told vertically ----------------------------
   Nothing is pinned and nothing moves sideways here — a horizontal track
   fights the thumb. The same three beats are one column instead: a picture
   that runs the full width of the screen, then the number, the title and the
   line, and enough air between the cards that a card and its type is what you
   are looking at when you stop scrolling. The tablet's old two-up magazine row
   is gone: it made the picture 371px wide inside a 700px page, which is the
   opposite of the "seen large" the section is now about.                    */
@media (max-width: 1023px) {
  .research__list {
    grid-template-columns: minmax(0, 1fr);
    /* ~0.8 of a phone screen between one card's last line and the next card's
       picture, so they arrive one at a time rather than as a list. */
    gap: clamp(64px, 12vw, 104px);
  }
  .research__card { display: flex; flex-direction: column; }
  /* Picture first and edge to edge, then the number, title and line. */
  .research__fig {
    order: -1;
    margin-inline: calc(var(--page-x) * -1);
    margin-bottom: 0;
  }
  .research__fig img { aspect-ratio: 16 / 9; }
  .research__n { padding-top: 14px; margin-bottom: 14px; }
}

@media (max-width: 640px) {
  /* 4:3 rather than 16:9 — a 390px-wide band 219px tall is a strip, not a
     photograph. The crop takes 25% off the width of a 16:9 original and every
     one of the three has its subject in the middle third. */
  .research__fig img { aspect-ratio: 4 / 3; }
}

/* ==========================================================================
   01 / Research — the scroll-driven sequence (added by script)

   Everything below is scoped to [data-seq], which home.js sets on the section
   only when motion is welcome. With no script, or under
   prefers-reduced-motion: reduce, none of it applies and the section is the
   plain layout above: three cards, the diagram, no reserved scroll space.

   Above 1024 the section owns a tall block (.research__seq), a pane inside it
   sticks to the top of the screen for that whole distance (.research__pin),
   and the three cards become one horizontal track the script translates from
   the scroll position. The track pauses on each card and moves between them,
   so each interest is held still and large before the next arrives.
   ========================================================================== */
@media (min-width: 1024px) {
  .research[data-seq] {
    --seq-top: var(--nav-h-compact);
    --seq-pane: calc(100svh - var(--seq-top));
    /* The pinned distance. Three holds and two moves are shared out across it
       by home.js; 3 x the pane is enough that a move takes about two thirds of
       a screen of scrolling and never feels like a jump cut. */
    --seq-travel: calc(var(--seq-pane) * 3);
    --seq-gap: clamp(32px, 3.4vw, 56px);
    /* One card. 86% of the window rather than 78, capped at 1320 rather than
       1180: the card is the only thing on the screen while it is held, so it
       should fill the screen. A slice of the next one still shows — 124px at
       1440 — which is what says the deck runs sideways. */
    --seq-panel: min(1320px, 88vw);
    /* 46% of the panel puts the plate at about 3:2 at every width — a 17% crop
       from the 16:9 originals, which all three survive with room to spare, and
       tall enough that the deck fills the pane rather than floating in it. The
       second term is the guard for a short window, the third the ceiling on a
       tall one. */
    --seq-fig-h: min(calc(var(--seq-panel) * 0.46), calc(var(--seq-pane) - 210px), 600px);
  }

  .research[data-seq] .research__seq { height: calc(var(--seq-pane) + var(--seq-travel)); }

  .research[data-seq] .research__pin {
    position: sticky;
    top: var(--seq-top);
    height: var(--seq-pane);
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  /* The deck runs to the shell's own edges — past 1440 that is the centred
     frame's edge and not the window's, the same reading the hero photograph
     takes. overflow:hidden clips the track without forming a stacking context,
     so nothing here can reach the diagram's blend further down the page. */
  .research[data-seq] .research__viewport {
    margin-inline: calc(var(--page-x) * -1);
    overflow: hidden;
  }

  .research[data-seq] .research__list {
    display: flex;
    gap: var(--seq-gap);
    width: max-content;
    /* The trailing pad is what lets the third card come to rest at the same
       left edge as the first two: it is exactly the window less one panel and
       the leading pad, so the track ends flush with the window at the last
       stop and no empty paper is ever pulled in from beyond it. */
    padding-inline: var(--page-x)
                    max(var(--page-x), calc(100% - var(--page-x) - var(--seq-panel)));
    will-change: transform;
  }

  .research[data-seq] .research__card {
    flex: 0 0 auto;
    width: var(--seq-panel);
    display: grid;
    /* 1.72 rather than 1.85. The panel grew, and at the old ratio all of the
       growth went to the plate and none to the words beside it, which left the
       paragraph on a 35-character measure next to a 772px photograph. */
    grid-template-columns: minmax(0, 1.72fr) minmax(0, 1fr);
    grid-template-rows: 1fr auto auto auto 1fr;
    column-gap: var(--seq-gap);
  }

  /* The card is the only thing on the screen while it is held, so its title is
     not one of three in a row any more — it is the line for that screen, and
     it takes the size of one. 46px at 1440 against the row's 34. */
  .research[data-seq] .research__title {
    font-size: clamp(2rem, 1.05rem + 2.2vw, 2.875rem);
    line-height: 1.08;
    letter-spacing: -0.028em;
  }
  .research[data-seq] .research__fig {
    grid-column: 1;
    grid-row: 1 / -1;
    height: var(--seq-fig-h);
    margin: 0;
  }
  .research[data-seq] .research__fig img {
    height: 100%;
    aspect-ratio: auto;
    object-fit: cover;
    opacity: 0.5;
    transition: transform 720ms var(--ease), opacity 620ms var(--ease);
  }
  .research[data-seq] .research__n     { grid-column: 2; grid-row: 2; margin-bottom: 0; }
  .research[data-seq] .research__title { grid-column: 2; grid-row: 3; margin-block: 20px 16px; }
  .research[data-seq] .research__desc  { grid-column: 2; grid-row: 4; }

  /* The type belongs to its card and arrives with it: held back while the card
     is passing, staggered in once it has come to rest. Only the three text
     elements move — the picture is what carries the travel. */
  .research[data-seq] .research__n,
  .research[data-seq] .research__title,
  .research[data-seq] .research__desc {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 600ms var(--ease), transform 600ms var(--ease);
  }
  .research[data-seq] .research__card.is-current .research__n,
  .research[data-seq] .research__card.is-current .research__title,
  .research[data-seq] .research__card.is-current .research__desc {
    opacity: 1;
    transform: none;
  }
  .research[data-seq] .research__card.is-current .research__n     { transition-delay:  40ms; }
  .research[data-seq] .research__card.is-current .research__title { transition-delay: 120ms; }
  .research[data-seq] .research__card.is-current .research__desc  { transition-delay: 200ms; }
  .research[data-seq] .research__card.is-current .research__fig img { opacity: 1; }
  .research[data-seq] .research__card.is-current .research__n { border-top-color: var(--accent); }

  /* Where you are in the three. The vocabulary is the card's own number rule —
     a hairline over a mono figure that turns accent for the current one — so
     the section says its position with the marks it already had rather than
     with a new piece of furniture. */
  .research[data-seq] .research__rail { display: flex; }
}

/* The rail is furniture the pinned deck needs and nothing else does, so it is
   built by the script and hidden everywhere the deck is not running. */
.research__rail { display: none; gap: var(--gutter); margin-top: clamp(22px, 3vh, 44px); }
.research__rail li {
  flex: 1 1 0;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.research__rail li.is-on { border-top-color: var(--accent); color: var(--ink); }

/* Below 1024 the story is vertical, so the card's entrance is vertical too:
   the picture is uncovered from its own foot as it arrives. Scoped to
   [data-seq] like everything else, so with no script the picture is simply
   there. */
@media (max-width: 1023px) {
  .research[data-seq] .research__card.reveal .research__fig {
    clip-path: inset(0 0 18% 0);
    transition: clip-path 1000ms var(--ease);
  }
  .research[data-seq] .research__card.reveal.is-in .research__fig { clip-path: inset(0 0 0 0); }
}

/* ==========================================================================
   01 / Research — platforms in motion

   The section's closing part, and the one place on Home where the ground goes
   dark inside a paper section. That is deliberate: a film is a lit rectangle,
   and three of them on the warm ground would read as three holes. The band
   gives them a ground of their own — the hero's midnight — announced by the
   same hairline-over-mono head every other part of Home is announced by.

   The card is one 16/9 frame with the title card behind it: the preview lies
   over that still at zero and is faded up only once it is actually playing, so
   a card that never plays (no script, reduced motion, a phone, a file that did
   not arrive) is a poster with a caption on it and nothing is missing. The
   poster is named here rather than only in the markup's poster attribute
   because the attribute paints the media element itself, which is the layer
   being faded — the still has to sit on the layer underneath.
   ========================================================================== */
.films { margin-top: clamp(56px, 6vw, 96px); padding-top: clamp(22px, 2.4vw, 34px); border-top: 1px solid var(--ink); }
.films__head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 10px 28px; margin-bottom: clamp(20px, 2.2vw, 32px); }
.films__label { color: var(--accent-ink); }
.films__aside { color: var(--muted); }

.films__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
.film { min-width: 0; }

.film__open {
  position: relative; display: block; width: 100%; aspect-ratio: 16 / 9;
  overflow: hidden; background: var(--midnight) center / cover no-repeat;
  color: var(--paper); text-align: left;
}
.film[data-film="nbsd"] .film__open { background-image: url(../video/film-nbsd-poster.webp); }
.film[data-film="cpmd"] .film__open { background-image: url(../video/film-cpmd-poster.webp); }
.film[data-film="5ap"]  .film__open { background-image: url(../video/film-5ap-poster.webp); }

.film__preview {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 700ms var(--motion-ease), transform 900ms var(--motion-ease);
}
.film.is-playing .film__preview { opacity: 1; }

/* The caption stands on a scrim rather than on the frame: the three films end
   on very different values and a fixed text colour cannot hold against all of
   them. The scrim is the band's own ground, so it reads as the card being cut
   off by the caption rather than as a plate laid over the picture. */
.film__cap {
  position: absolute; inset: auto 0 0; display: grid; gap: 7px;
  padding: clamp(28px, 3.4vw, 46px) clamp(16px, 1.6vw, 22px) clamp(15px, 1.5vw, 20px);
  background: linear-gradient(to top, #061820f2 0%, #061820b8 46%, transparent 100%);
}
/* Cased, not upper: NBSD-Chol and 4-CPMD are the compounds' own names and the
   band's one uppercase mono voice would rewrite them. */
.film__name { font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: .1em; text-transform: none; color: var(--mint); }
.film__tag { font-size: clamp(15px, 1.15vw, 17px); line-height: 1.5; color: var(--paper); }
.film__meta { font-size: 10px; color: #a4babc; transition: color var(--dur) var(--ease); }

.film__open:hover .film__preview,
.film__open:focus-visible .film__preview { transform: scale(1.02); }
.film__open:hover .film__cap,
.film__open:focus-visible .film__cap { background: linear-gradient(to top, #061820f7 0%, #061820cc 52%, #06182026 100%); }
.film__open:hover .film__meta,
.film__open:focus-visible .film__meta { color: var(--mint); }
/* The frame is the target, so the ring goes round the frame. */
.film__open:focus-visible { outline-offset: -2px; }

/* The raw file, which is the card with no script: a plain link to the MP4,
   set in the band's own mono voice so the fallback is a designed state rather
   than a loose line of text. home.js marks the band once it has a working
   player, and only then is the link taken out of the flow — the button then
   does everything the link did, and the two together would read to a screen
   reader as the same film twice. It stays where a keyboard can reach it. */
.film__file {
  display: inline-block; margin-top: 11px; padding-block: 9px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: .08em;
  color: var(--accent-ink); border-bottom: 1px solid var(--rule);
}
.films.has-player .film__file {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

@media (max-width: 900px) {
  .films__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
}
@media (max-width: 640px) {
  .films__grid { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .films__head { margin-bottom: 18px; }
}
@media (prefers-reduced-motion: reduce) {
  .film__preview { transition: none; }
  .film__open:hover .film__preview, .film__open:focus-visible .film__preview { transform: none; }
}

/* ==========================================================================
   02 / In numbers

   No surface of its own, and no rules closing it. It was white — the one white
   on the site — and against the warm ground that read as a stripe pasted over
   the page rather than as a section of it. The band is now the page: same
   colour above, inside and below.

   Nothing replaced the two hairlines. They existed to hold a surface that was
   too close to the paper to stand on its own, and with the surface gone they
   would only be two rules with nothing between them. What says "section" here
   is what says it in 01, 03, 04 and 05 — the ink rule over the head, and the
   counts themselves, which are the largest figures on the page.
   ========================================================================== */

.stats {
  display: grid;
  grid-template-columns: 1.2fr 0.85fr 1.15fr 1.15fr 0.9fr;
}
.stat { padding: 8px clamp(14px, 1.6vw, 28px) 4px; border-left: 1px solid var(--rule); }
.stat:first-child { padding-left: 0; border-left: 0; }
.stat:last-child  { padding-right: 0; }
.stat__label { display: block; color: var(--navy); margin-bottom: 18px; }
.stat__value {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 144;
  font-weight: 300;
  font-size: clamp(2.75rem, 0.9rem + 5vw, 5.5rem);
  line-height: 0.86;
  letter-spacing: -0.035em;
}
.stat:first-child .stat__value { font-size: var(--text-stat); }

/* The papers still out for a decision, under the count of the ones that
   landed. It sits inside the value cell so the counts stay a clean dl. */
.numbers__sub {
  font-size: clamp(0.6875rem, 0.66rem + 0.1vw, 0.75rem);
  color: var(--muted);
  margin-top: 6px;
}
.numbers__sub b { font-weight: 600; color: var(--navy); }
/* Doubled class on purpose: "Established" is the first cell until home.js
   inserts the four counts before it, and with JavaScript off it stays first.
   A single class loses to :first-child and 2017 would be set at 105px. */
.stat.stat--est .stat__value { font-size: clamp(2rem, 0.9rem + 3vw, 3.5rem); color: var(--navy); }

/* Faces — the two counts that are people get their faces. */
.stat__faces-cell { margin-top: clamp(16px, 1.6vw, 22px); }
.stat__faces {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
}
.stat__face {
  --face: clamp(28px, 2.35vw, 36px);
  flex: none;
  width: var(--face);
  height: var(--face);
  margin-left: calc(var(--face) / -3.4);
  border-radius: 999px;
  overflow: hidden;
  background: var(--paper-3);
  /* The ring is the band showing through the overlap, so it follows the band's
     own ground. It was --paper-2, then white; the band is now the page, so it
     is --paper. It has to match exactly — the ring is not a border, it is the
     hole the face is punched out of. */
  box-shadow: 0 0 0 2px var(--paper);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.stat__face:first-child { margin-left: 0; }
.stat__face img {
  /* The portraits are uploaded at whatever shape the camera gave them and
     people.json carries no dimensions for them, so width/height attributes
     would not describe the box they are drawn in. The circle is square, and
     saying so here is what stops the picture reflowing as it decodes. */
  aspect-ratio: 1;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 20%;
  filter: grayscale(0.35) contrast(1.02);
  transition: filter var(--dur) var(--ease);
}
.stat__more {
  flex: none;
  margin-left: 12px;
  color: var(--muted);
  transition: color var(--dur) var(--ease);
}
.stat__faces:hover .stat__face,
.stat__faces:focus-visible .stat__face {
  transform: translateY(-3px);
  box-shadow: 0 0 0 2px var(--paper), 0 5px 12px rgba(11, 31, 51, 0.20);
}
.stat__faces:hover .stat__face img { filter: none; }
.stat__faces:hover .stat__more,
.stat__faces:focus-visible .stat__more { color: var(--accent-ink); }
/* A short cascade so the row reads as a group lifting, not a slab. */
.stat__face:nth-child(2) { transition-delay: 30ms; }
.stat__face:nth-child(3) { transition-delay: 60ms; }
.stat__face:nth-child(4) { transition-delay: 90ms; }
.stat__face:nth-child(5) { transition-delay: 120ms; }
.stat__face:nth-child(6) { transition-delay: 150ms; }
.stat__face:nth-child(7) { transition-delay: 180ms; }

@media (max-width: 1023px) {
  .stats { grid-template-columns: repeat(2, 1fr); row-gap: 40px; }
  .stat { border-left: 0; padding-inline: 0; border-top: 1px solid var(--rule); padding-top: 14px; }
  .stat:nth-child(even) { padding-left: var(--gutter); border-left: 1px solid var(--rule); }
  .stat--est { grid-column: 1 / -1; }
}

@media (max-width: 640px) {
  /* The two face rows take the full width so the cluster is never squeezed. */
  .stat--faces {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    grid-template-areas: "label label" "value faces";
    align-items: end;
    column-gap: 16px;
    padding-left: 0;
    border-left: 0;
  }
  .stat--faces .stat__label { grid-area: label; }
  .stat--faces .stat__value { grid-area: value; }
  .stat--faces .stat__faces-cell {
    grid-area: faces;
    margin-top: 0;
    padding-bottom: 8px;
    display: flex;
    justify-content: flex-end;
  }
  .stat__face { --face: 30px; margin-left: -11px; }
}

/* ==========================================================================
   03 / News
   ========================================================================== */
.ledger { border-top: 1px solid var(--rule); }
.ledger__row {
  display: grid;
  grid-template-columns: 140px minmax(0, 1fr) 120px;
  column-gap: var(--gutter);
  align-items: baseline;
  padding-block: clamp(20px, 2vw, 30px);
  border-bottom: 1px solid var(--rule);
}
.ledger__date { color: var(--accent-ink); padding-top: 0.3em; }
.ledger__row:not(:first-child) .ledger__date { color: var(--muted); }
/* One step up — 18-23px rather than 17-21. The head above this list is now set
   at display size, and a 20px line under a 68px word read as a caption for it
   rather than as the section's content. The measure improves with the size
   rather than suffering: the column is fixed, so the same 989px at 1440 falls
   from 82 characters to 73. Most of these lines are Korean, where that is
   nearer 36 syllables. Leading stays at 1.6. */
.ledger__text {
  font-size: clamp(1.125rem, 1rem + 0.42vw, 1.4375rem);
  line-height: 1.6;
  color: var(--ink);
  text-wrap: pretty;
}
.ledger__flag { justify-self: end; }
@media (max-width: 767px) {
  .ledger__row { grid-template-columns: minmax(0, 1fr) auto; row-gap: 8px; }
  .ledger__date { grid-row: 1; }
  .ledger__flag { grid-row: 1; }
  .ledger__text { grid-column: 1 / -1; }
}

/* ==========================================================================
   04 / Publications
   ========================================================================== */
.pubs { border-top: 1px solid var(--rule); }
.pub {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr) 260px;
  column-gap: var(--gutter);
  padding-block: clamp(26px, 2.6vw, 40px);
  border-bottom: 1px solid var(--rule);
  transition: background var(--dur) var(--ease);
}
.pub:hover { background: color-mix(in srgb, var(--paper-2) 55%, transparent); }
.pub__n { color: var(--muted); padding-top: 0.45em; }
.pub__title {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 48;
  font-weight: 400;
  font-size: var(--text-h3);
  line-height: 1.3;
  letter-spacing: -0.012em;
  /* A paper title runs to three or four lines; 10px under it was less than a
     third of its own leading, so the author line read as a fifth line of the
     title rather than as the next thing. */
  margin-bottom: 14px;
  text-wrap: pretty;
}
.pub__authors { font-size: var(--text-sm); color: var(--muted); line-height: 1.55; }
.pub__authors b { color: var(--ink); font-weight: 600; }
.pub__side { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; padding-top: 0.4em; }
.pub__journal { color: var(--navy); }
.pub__journal i { font-style: normal; color: var(--muted); }
.pub__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.pubs__more { display: flex; justify-content: flex-end; padding-top: 28px; }
@media (max-width: 1023px) {
  .pub { grid-template-columns: 48px minmax(0, 1fr); row-gap: 16px; }
  .pub__side { grid-column: 2; padding-top: 0; }
}

/* ---- Cover strip ----------------------------------------------------------
   Bigger, tactile, and obviously draggable. */
.covers { position: relative; margin-top: clamp(64px, 7vw, 104px); }
.covers__head {
  position: relative;
  z-index: 1;                 /* stays clear of the strip's edge fade */
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding-bottom: 16px; border-bottom: 1px solid var(--rule);
  background: var(--paper);
}
.covers__head h3 { color: var(--ink); }
.covers__note { display: flex; align-items: baseline; gap: 16px; }
.covers__swipe {
  display: none;
  align-items: center;
  gap: 7px;
  color: var(--accent-ink);
  white-space: nowrap;
}
.covers__swipe svg { animation: swipe-nudge 2.6s var(--ease) infinite; }
@keyframes swipe-nudge {
  0%, 100% { transform: translateX(0); opacity: .55; }
  55%      { transform: translateX(5px); opacity: 1; }
}

.covers__scroller {
  display: flex;
  gap: var(--gutter);
  overflow-x: auto;
  padding-block: 30px 10px;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
  overscroll-behavior-x: contain;
}
.covers__scroller::-webkit-scrollbar { height: 6px; }
.covers__scroller::-webkit-scrollbar-thumb { background: var(--rule); border-radius: 99px; }

/* The strip runs out under a fade rather than stopping at a hard edge. */
.covers::after {
  content: "";
  position: absolute;
  top: 46px; right: 0; bottom: 10px;
  width: clamp(40px, 5.5vw, 68px);
  background: linear-gradient(to right, rgba(246, 245, 241, 0), var(--paper) 86%);
  pointer-events: none;
}

.cover {
  flex: 0 0 clamp(210px, 19.5vw, 292px);
  scroll-snap-align: start;
}
.cover figure { margin: 0; }
.cover img {
  width: 100%;
  aspect-ratio: 451 / 600;
  object-fit: cover;
  background: var(--paper-2);
  box-shadow: 0 1px 2px rgba(11, 31, 51, 0.10), 0 6px 18px rgba(11, 31, 51, 0.09);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.cover:hover img {
  transform: translateY(-5px);
  box-shadow: 0 2px 4px rgba(11, 31, 51, 0.12), 0 16px 34px rgba(11, 31, 51, 0.18);
}
.cover figcaption { padding-top: 14px; }
.cover__journal {
  display: block;
  font-size: var(--text-sm);
  line-height: 1.35;
  color: var(--ink);
  margin-bottom: 4px;
}
.cover__issue { color: var(--muted); }

@media (max-width: 900px) {
  .covers__swipe { display: inline-flex; }
  .covers__scroller { scroll-snap-type: x mandatory; }
}
@media (max-width: 640px) {
  .covers__head { flex-wrap: wrap; row-gap: 8px; }
  .covers__note { width: 100%; justify-content: space-between; }
  .cover { flex: 0 0 clamp(200px, 58vw, 232px); }
}

/* ==========================================================================
   05 / Photo
   ========================================================================== */
/* The bento takes the page's own edges rather than the text column, and its
   rows grew with it: 14.5vw put a 208px band under a 68px section head, which
   is a contact sheet rather than an archive. The call to action stays on the
   text margin — the pictures bleed, the words do not, which is the rule the
   hero photograph and the research pictures already follow. */
.photos {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: clamp(150px, 17.6vw, 280px);
  gap: var(--gutter);
  margin-inline: calc(var(--page-x) * -1);
}
.ph { position: relative; margin: 0; overflow: hidden; }
.ph a { display: block; height: 100%; }
.ph img {
  width: 100%; height: 100%;
  object-fit: cover;
  background: var(--paper-2);
  transition: transform 620ms var(--ease);
}
.ph:hover img { transform: scale(1.045); }
.ph figcaption {
  position: absolute; inset-inline: 0; bottom: 0;
  padding: 30px 16px 14px;
  background: linear-gradient(to top, rgba(11, 31, 51, 0.88), rgba(11, 31, 51, 0.30) 46%, rgba(11, 31, 51, 0));
  color: #fff;
  display: flex; flex-direction: column; gap: 2px;
  transform: translateY(0);
  transition: transform var(--dur) var(--ease);
}
.ph:hover figcaption { transform: translateY(-3px); }
.ph figcaption b { font-weight: 500; font-size: 0.8125rem; line-height: 1.35; }
.ph figcaption span { font-family: var(--font-mono); font-size: 0.6875rem; letter-spacing: 0.08em; opacity: .82; }

/* Bento: the two upright frames (a tall portrait and a square overhead shot)
   get 2-row slots so they are not letterboxed; landscapes fill the 1-row cells. */
.ph--a { grid-column: 1 / 3; grid-row: 1 / 3; }
.ph--b { grid-column: 3 / 4; grid-row: 1 / 3; }
.ph--c { grid-column: 1 / 2; grid-row: 3 / 5; }
.ph--d { grid-column: 2 / 3; grid-row: 3 / 4; }
.ph--e { grid-column: 3 / 4; grid-row: 3 / 4; }
.ph--f { grid-column: 2 / 3; grid-row: 4 / 5; }

.ph-cta {
  grid-column: 3 / 4; grid-row: 4 / 5;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 12px;
  padding: 20px 0 6px;
  border-top: 1px solid var(--ink);
  /* Back off the bled edge. Margin and not padding, so the rule that closes
     the cell stops on the text margin with the words. */
  margin-inline-end: var(--page-x);
}
.ph-cta p { color: var(--muted); font-size: var(--text-sm); }

@media (max-width: 900px) {
  .photos { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: clamp(140px, 25vw, 230px); }
  /* Full width from here down, so it needs the margin on both sides. */
  .ph-cta { margin-inline: var(--page-x); }
  .ph--a { grid-column: 1 / 3; grid-row: 1 / 3; }
  .ph--b { grid-column: 1 / 2; grid-row: 3 / 5; }
  .ph--c { grid-column: 2 / 3; grid-row: 3 / 5; }
  .ph--d { grid-column: 1 / 2; grid-row: 5 / 6; }
  .ph--e { grid-column: 2 / 3; grid-row: 5 / 6; }
  .ph--f { grid-column: 1 / 3; grid-row: 6 / 7; }
  .ph-cta { grid-column: 1 / 3; grid-row: 7 / 8; border-top: 1px solid var(--ink); }
}
@media (max-width: 560px) {
  /* Two-column mosaic: a full-width lead, then the upright pair, then pairs. */
  .photos { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: clamp(104px, 29vw, 148px); gap: 12px; }
  .ph--a { grid-column: 1 / 3; grid-row: 1 / 3; }
  .ph--b { grid-column: 1 / 2; grid-row: 3 / 5; }
  .ph--c { grid-column: 2 / 3; grid-row: 3 / 5; }
  .ph--d { grid-column: 1 / 2; grid-row: 5 / 6; }
  .ph--e { grid-column: 2 / 3; grid-row: 5 / 6; }
  .ph--f { grid-column: 1 / 3; grid-row: 6 / 8; }
  .ph-cta { grid-column: 1 / 3; grid-row: 8 / 9; padding-top: 18px; }
  .ph figcaption { padding: 24px 11px 10px; }
  .ph figcaption b { font-size: 0.75rem; }
  .ph figcaption span { font-size: 0.625rem; }
}

/* ==========================================================================
   06 / Video

   The card is the research card's structure with the picture moved to the top:
   a 16/9 still, then a hairline that takes the accent when the card is under
   the pointer, the channel and the date in mono on it, and the title in the
   display serif below. The one new element is the play control — a mono
   lozenge in the corner of the still, the same pill the lightbox buttons are,
   which fills with the accent on hover the way "live" does everywhere else on
   this site. It is not YouTube's mark and it does not want to be: the section
   is the laboratory's, and the video happens to live on YouTube.
   ========================================================================== */
.vids {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(26px, 2.8vw, 44px);
}

.vid { min-width: 0; }
.vid__link { display: flex; flex-direction: column; height: 100%; }

/* The box is reserved by its ratio, so a record with no stored size, a still
   that is still arriving and a still that never arrives all hold the same
   ground and nothing under them moves. */
.vid__frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  background: var(--navy);
}
.vid__frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  transform: scale(1.001);
  transition: transform 720ms var(--ease);
}
/* No still: a hatched navy slate rather than a void, with the play control
   still standing on it. */
.vid__frame--blank {
  background-image: repeating-linear-gradient(-45deg,
    rgba(246, 245, 241, 0.055) 0 1px, transparent 1px 9px);
}

/* The same whisper of a veil the research pictures carry — enough to seat a
   bright YouTube still on the paper, and it lifts on hover. Held under the
   play control by the z-index below. */
.vid__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--ink) 34%, transparent) 0%,
    color-mix(in srgb, var(--ink) 6%, transparent) 58%,
    transparent 100%);
  transition: opacity var(--dur) var(--ease);
  pointer-events: none;
}

.vid__play {
  position: absolute;
  z-index: 2;
  inset-inline-start: 14px;
  inset-block-end: 14px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 34px;
  padding: 0 15px 0 14px;
  border: 1px solid rgba(246, 245, 241, 0.42);
  border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 58%, transparent);
  -webkit-backdrop-filter: blur(6px) saturate(140%);
  backdrop-filter: blur(6px) saturate(140%);
  color: var(--paper);
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.vid__play b { font-weight: 500; }
.vid__play svg { display: block; transition: transform var(--dur) var(--ease); }

.vid__link:hover .vid__frame img,
.vid__link:focus-visible .vid__frame img { transform: scale(1.045); }
.vid__link:hover .vid__frame::after,
.vid__link:focus-visible .vid__frame::after { opacity: 0.35; }
.vid__link:hover .vid__play,
.vid__link:focus-visible .vid__play {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
}
.vid__link:hover .vid__play svg,
.vid__link:focus-visible .vid__play svg { transform: translateX(2px); }

.vid__body {
  display: flex;
  flex-direction: column;
  padding-top: 13px;
  margin-top: clamp(14px, 1.4vw, 20px);
  border-top: 1px solid var(--rule);
  transition: border-color var(--dur) var(--ease);
}
.vid__link:hover .vid__body,
.vid__link:focus-visible .vid__body { border-top-color: var(--accent); }

.vid__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: clamp(9px, 0.9vw, 13px);
}
.vid__chan { color: var(--accent-ink); min-width: 0; }
.vid__date { color: var(--muted); white-space: nowrap; margin-inline-start: auto; }

.vid__title {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 96;
  font-weight: 400;
  font-size: clamp(1.25rem, 0.9rem + 0.78vw, 1.5rem);
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.vid__note {
  margin-top: 14px;
  color: var(--navy);
  font-size: var(--text-sm);
  line-height: 1.68;
  text-wrap: pretty;
}

@media (min-width: 768px) and (max-width: 1023px) {
  .vids { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(28px, 3.4vw, 40px); }
}

@media (max-width: 767px) {
  /* One card a screen, the still edge to edge — the same move the research
     cards make on a phone. The play control aligns with the text column
     rather than with the picture, so the card reads as one measure. */
  .vids { grid-template-columns: minmax(0, 1fr); gap: clamp(40px, 9vw, 56px); }
  .vid__frame { margin-inline: calc(var(--page-x) * -1); }
  .vid__play { inset-inline-start: var(--page-x); }
  .vid__body { margin-top: 14px; }
  .vid__title { font-size: clamp(1.375rem, 1.05rem + 1.3vw, 1.625rem); }
  .vid__note { font-size: var(--text-body); }
}

/* ---- Video panel ---------------------------------------------------------
   The photograph lightbox on photo.html, in the one shape Home needs: no
   stepping between records, and a stage that stays empty until a card is
   clicked. Its values are photo.css's, held by hand rather than shared,
   because Home does not load that stylesheet. */
.vlb {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  /* Painted on the dialog as well as the backdrop: the masthead runs a
     backdrop-filter, and a filtered surface can ghost through a ::backdrop
     alone. Both layers are in the top layer, so nothing bleeds. */
  background: rgba(6, 24, 32, 0.9);
  color: var(--paper);
  overflow: hidden;
}
.vlb::backdrop { background: rgba(6, 24, 32, 0.9); }

.vlb__panel {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 14px;
  padding: clamp(14px, 2vw, 26px) clamp(16px, 3vw, 40px) clamp(16px, 2vw, 28px);
}

.vlb__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  color: color-mix(in srgb, var(--paper) 66%, transparent);
}
.vlb__top b { color: var(--accent); font-weight: 500; }

.vlb__stage {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
}
/* The film player. It keeps 16/9 and is capped by whatever height the panel
   has left, so it never pushes the furniture off a laptop screen. The
   browser's own controls do the work an embed's chrome used to: the films are
   files this laboratory owns, so there is nothing to frame them in. */
.vlb__title { color: var(--accent); margin: 0; }
.vlb__video {
  display: block;
  width: 100%;
  max-width: min(1100px, calc((100svh - 210px) * 16 / 9));
  max-height: 100%;
  aspect-ratio: 16 / 9;
  background: rgba(246, 245, 241, 0.06);
}

.vlb__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  min-width: 44px;
  height: 44px;
  padding: 0 16px;
  border: 1px solid rgba(246, 245, 241, 0.32);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper);
  white-space: nowrap;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.vlb__btn:hover { background: var(--paper); border-color: var(--paper); color: var(--ink); }
.vlb__btn:active { background: var(--accent); border-color: var(--accent); color: var(--ink); }
.vlb__btn svg { display: block; }
.vlb :focus-visible { outline-color: var(--accent); }

/* ==========================================================================
   Home-only composition, moved here from experience.css.
   experience.css is loaded by all six pages; everything below matches markup
   that exists on index.html alone (the midnight masthead, the opening scene,
   the asymmetric introduction and the Home section furniture), so carrying it
   on the five inner pages was dead weight. Order inside this block is the
   order it had in experience.css, and home.css still loads immediately before
   experience.css, so the cascade is unchanged.
   ========================================================================== */
.stat__value { font-variant-numeric: tabular-nums; }

/* The masthead shares the opening scene, then returns to paper on scroll. */
.home-page { --shell-max: 1720px; --page-x: clamp(24px, 4.5vw, 80px); }
.home-page .announce { background: var(--midnight); }
.home-page .announce__link { justify-content: center; min-height: 32px; font-size: 10px; }
.home-page .masthead {
  background: var(--midnight); color: var(--paper);
  border-bottom: 1px solid #ffffff1c; backdrop-filter: none;
  transition: background 500ms, color 500ms;
}
.home-page .masthead__inner { min-height: 90px; }
.home-page .wordmark__name { font-size: 34px; font-weight: 450; letter-spacing: -.045em; }
.home-page .wordmark__full { color: #b4ead8; font-size: 11px; letter-spacing: .14em; font-weight: 600; }
.home-page .nav { gap: clamp(18px, 2.1vw, 36px); }
.home-page .nav__link { color: #b9c8cb; font-size: 12px; font-weight: 400; }
.home-page .nav__link:hover, .home-page .nav__link[aria-current] { color: #fff; }
.home-page .nav__link[aria-current]::after { background: var(--mint); }
.home-page .pill { border-color: #b8c8c75e; font-size: 10px; padding: 12px 20px; }
.home-page .pill::after { content: '↗'; font-size: 16px; margin-left: 16px; }
.home-page .pill:hover { background: var(--mint); color: var(--midnight); }
.home-page .burger { border-color: #ffffff60; }
.home-page .burger span, .home-page .burger span::before, .home-page .burger span::after { background: var(--paper); }
.home-page .burger[aria-expanded=true] span { background: transparent; }
.home-page .masthead.is-compact, .home-page .masthead.menu-active {
  background: color-mix(in srgb, var(--paper) 95%, transparent);
  color: var(--ink); border-color: var(--rule); backdrop-filter: blur(16px);
}
.home-page .masthead.is-compact .masthead__inner { min-height: var(--nav-h-compact); }
.home-page .masthead:is(.is-compact, .menu-active) .nav__link { color: var(--navy); }
.home-page .masthead:is(.is-compact, .menu-active) .nav__link[aria-current] { color: var(--ink); }
.home-page .masthead:is(.is-compact, .menu-active) .wordmark__full { color: var(--muted); }
.home-page .masthead:is(.is-compact, .menu-active) .pill,
.home-page .masthead:is(.is-compact, .menu-active) .burger { border-color: var(--rule); }
.home-page .masthead:is(.is-compact, .menu-active) .burger span::before,
.home-page .masthead:is(.is-compact, .menu-active) .burger span::after,
.home-page .masthead:is(.is-compact, .menu-active) .burger:not([aria-expanded=true]) span { background: var(--ink); }

/* One full-bleed scene, with real type above a separately moving image. */
.hero-cinema {
  position: relative; isolation: isolate; color: var(--paper); background: var(--midnight);
  min-height: max(690px, calc(100svh - 122px)); overflow: clip;
}
.hero-cinema__scene { position: absolute; inset: 0; z-index: -2; overflow: hidden; }
.hero-cinema__art {
  position: absolute; inset: -5%; display: block;
  transform: translate3d(var(--scene-x, 0px), var(--scene-y, 0px), 0) scale(var(--scene-scale, 1));
  will-change: transform;
}
/* The art is 21:9 and the box is nearer 2:1, so roughly a fifth of the frame's
   width is cut and object-position decides from which side. The microsphere
   fills the right of the render and the left two fifths are empty midnight, so
   the cut is taken off the right: that keeps the whole void under the headline
   block and lets the sphere read as cropped by the frame rather than centred
   in it. Anchoring right instead would slide the lit lattice under the type. */
.hero-cinema__art > picture { display: block; width: 100%; height: 100%; }
.hero-cinema__art img { width: 100%; height: 100%; object-fit: cover; object-position: left center; }

/* The film lies in the still's own box and is cropped by the same rule, so the
   two are the same picture and the change between them is only that one of
   them moves. It arrives over a second: the still is what the page was drawn
   with, and a cut would announce a video where the design wants a frame that
   was always alive. The mask feathers the two edges the ground is nearest —
   the left, where the headline block stands, and the foot, where the closing
   rule is — so the film has no edge of its own to give away. The filter takes
   the render's contrast back to the still's, which was graded for this page. */
.hero-cinema__video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: left center;
  opacity: 0; transition: opacity 1.2s var(--motion-ease);
  filter: saturate(.95) brightness(.92);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%), linear-gradient(0deg, transparent, #000 14%);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(90deg, transparent, #000 12%), linear-gradient(0deg, transparent, #000 14%);
  mask-composite: intersect;
}
.hero-cinema__art.is-live .hero-cinema__video { opacity: 1; }
/* The particles are the still's motion. Once the film is running they are a
   second animation over a first, so they leave — faster than the film arrives,
   which is what keeps the exchange from reading as a crossfade. */
.hero-cinema__art.is-live ~ .hero-cinema__particles { opacity: 0; transition: opacity 600ms var(--motion-ease); }
/* Two bands, both earning their keep. The left-hand scrim this replaced was
   built for a subject that sat near the centre-left; against this render it
   covered nothing but empty midnight, and because #061820 is lighter than the
   render's own void it actually lifted the blacks it was meant to protect.
   What is left: a short band under the masthead, measured in pixels because
   the topline row it protects is a fixed 40px tall at every hero height, and
   the standing bottom band under the closing rule. The band is gone by 128px,
   which is above the sphere's brightest lattice, so the highlights survive. */
.hero-cinema__scene::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, #061820 0px, #061820b3 58px, transparent 128px),
    linear-gradient(0deg, #061820 0%, #06182088 13%, transparent 38%);
}
.hero-cinema__particles { position: absolute; inset: 0; width: 100%; height: 100%; opacity: .7; }
.hero-cinema__inner { position: relative; min-height: inherit; display: flex; flex-direction: column; }
.hero-cinema__topline { display: flex; justify-content: space-between; padding-top: 27px; color: #b4c6c9; font-size: 9px; letter-spacing: .09em; }
.hero-cinema__topline p { display: flex; align-items: center; gap: 9px; }
.status-dot { display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--mint); flex: none; }
.hero-cinema__school { margin-left: 7px; }
.hero-cinema__copy { padding-top: clamp(55px, 8vh, 105px); padding-bottom: 75px; }
.hero-cinema__eyebrow { color: var(--mint); font-size: 9px; letter-spacing: .13em; margin-bottom: 25px; }
.hero-cinema__title {
  font-size: clamp(78px, 7.7vw, 146px); font-weight: 320;
  font-variation-settings: 'opsz' 144, 'SOFT' 0, 'WONK' 0, 'wght' 320;
  line-height: .99; letter-spacing: -.055em; max-width: 10ch; text-wrap: initial;
}
.hero-line { display: block; white-space: nowrap; overflow: clip; padding-bottom: .11em; margin-bottom: -.11em; }
.hero-line > span { display: inline-block; }
.hero-line--italic { color: var(--mint); font-style: italic; padding-right: .08em; }
.hero-cinema__description { max-width: 330px; margin-top: 29px; font-size: 14px; line-height: 1.8; color: #c1cfd0; }
.hero-cinema__actions { display: flex; align-items: center; gap: 33px; margin-top: 27px; }
.explore-link { display: inline-flex; gap: 23px; align-items: center; font-size: 12px; }
.explore-link > span:first-child { border-bottom: 1px solid #a7c7c56b; padding-block: 5px; }
.explore-link__arrow {
  display: grid; place-items: center; width: 48px; height: 48px; border-radius: 50%;
  background: var(--mint); color: var(--midnight); font-size: 24px;
  transition: transform 600ms var(--motion-ease), background 600ms;
}
.explore-link:hover .explore-link__arrow { transform: rotate(45deg); background: #e1fff3; }
.hero-cinema__publication { font-size: 11px; color: #c1cfd0; display: inline-flex; align-items: center; gap: 16px; padding: 14px 0; }
.hero-cinema__publication span { transition: transform 500ms var(--motion-ease); }
.hero-cinema__publication:hover span { transform: translate(3px, -3px); }
/* The annotation used to hang at the right, two thirds down, where the old
   art had nothing. On this render that is the sphere's lit edge — measured at
   108, 132 and 66 mean luminance across the three desktop widths, so the line
   was unreadable. Of the three places it could go (right above the closing
   rule, right under the masthead, left under the call to action) the left one
   is the darkest by a wide margin — 17.5, 17.9 and 18.5 — and it is the only
   one that never crosses the sphere. Anchored to the bottom rather than to a
   percentage so the gap above the closing rule holds at any hero height. */
.hero-cinema__annotation { position: absolute; left: var(--page-x); bottom: 96px; display: flex; gap: 14px; align-items: start; font-size: 9px; color: #edf4f1; letter-spacing: .08em; }
.hero-cinema__annotation span span { display: inline-block; margin-top: 6px; font-size: 8px; color: #b0c3c4; }
.hero-cinema__cross { line-height: 1; font-size: 25px; font-weight: 400; color: var(--mint); }
.hero-cinema__bottom { margin-top: auto; display: flex; justify-content: space-between; align-items: center; gap: 24px; border-top: 1px solid #b7d8d52b; min-height: 77px; color: #a4babc; font-size: 8px; letter-spacing: .08em; }
.scroll-cue { display: flex; align-items: center; gap: 15px; color: #e2eee9; min-height: 44px; }
.scroll-cue__line { position: relative; width: 1px; height: 26px; background: #aacbc33a; overflow: hidden; }
.scroll-cue__line::after { content: ''; position: absolute; inset: -100% 0 100%; background: var(--mint); animation: scroll-line 2.8s ease-in-out infinite; }
@keyframes scroll-line { to { transform: translateY(200%); } }
.hero-cinema__motion { display: flex; align-items: center; gap: 20px; }
.hero-cinema.is-entering .hero-line > span { animation: hero-type 1450ms var(--motion-ease) both; }
.hero-cinema.is-entering .hero-line:nth-child(2) > span { animation-delay: 120ms; }
.hero-cinema.is-entering .hero-line:nth-child(3) > span { animation-delay: 240ms; }
.hero-cinema.is-entering .hero-cinema__description,
.hero-cinema.is-entering .hero-cinema__actions { animation: hero-support 1300ms var(--motion-ease) 420ms both; }
.hero-cinema.is-entering .hero-cinema__scene { animation: scene-arrive 2200ms var(--motion-ease) both; }
@keyframes hero-type { from { transform: translateY(115%) rotate(3deg); } to { transform: none; } }
@keyframes hero-support { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@keyframes scene-arrive { from { opacity: .2; transform: scale(1.1); } to { opacity: 1; transform: none; } }

/* A quiet, deliberately asymmetric introduction after the opening scene. */
.lab-intro { padding-block: clamp(64px, 6vw, 112px) clamp(40px, 4vw, 64px); }
.lab-intro__heading { display: grid; grid-template-columns: 1fr 2.05fr; gap: var(--gutter); align-items: start; }
.lab-intro__label { display: flex; align-items: center; gap: 10px; font-size: 9px; color: var(--muted); padding-top: 12px; }
.lab-intro__label .status-dot { background: var(--accent-ink); }
/* Two lines, broken where the <br> is: the second line now carries "begins
   with a different kind of material." whole, so the size is set to what that
   line can hold in the heading's right column from 1280 to 1600. */
.lab-intro__statement { font-size: clamp(36px, 4vw, 66px); line-height: 1.1; letter-spacing: -.04em; font-weight: 320; }
.lab-intro__statement em { color: var(--accent-ink); font-weight: inherit; }
/* The statement lights word by word as it is scrolled into view, but the
   resting words are still text a reader has to be able to read. At the old
   .22 the sentence measured 1.58:1 against the paper — 1.36:1 for the one
   word set in --accent-ink — so the line was effectively invisible until the
   animation reached it. .72 blends to 6.40:1 for the --ink words and 3.05:1
   for the accent one, which clears AA for text this size (70px display, so
   the large-text threshold applies to the accent word). The accent cannot go
   higher by opacity alone: --accent-ink is 5.04:1 on paper even fully opaque.
   The lit state is unchanged, so the reveal still reads as a reveal. */
.word-reveal__word { opacity: .72; transition: opacity 650ms var(--motion-ease); }
.word-reveal__word.is-lit { opacity: 1; }
.lab-intro__body { display: grid; grid-template-columns: 1fr 2.05fr; gap: var(--gutter); margin-top: clamp(32px, 3.5vw, 56px); align-items: end; }
.lab-intro__text { padding-right: clamp(0px, 3vw, 55px); display: flex; flex-direction: column; align-items: start; gap: 30px; }
.lab-intro__asterisk { font-family: Georgia, serif; font-size: 70px; line-height: 1; color: var(--accent-ink); }
.lab-intro__text p { font-size: 14px; color: var(--navy); line-height: 1.85; }
.lab-intro__text a { font-size: 9px; margin-bottom: 43px; }
.lab-intro__figure { overflow: hidden; }
.lab-intro__figure img { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; object-position: center 42%; transition: transform 1400ms var(--motion-ease); }
.lab-intro__figure:hover img { transform: scale(1.025); }
.lab-intro__figure .hero__cap { padding-top: 14px; margin-top: 14px; font-size: 9px; }
.home-page .section-head--major .section-head__word { font-size: clamp(45px, 5.7vw, 92px); font-weight: 320; }
.home-page .section-head--major { padding-top: 25px; }
.home-page .section-head--major .section-head__n { padding-top: 9px; font-size: 10px; }
.home-page .section-head__aside { font-size: 10px; }
.home-page .research { padding-top: clamp(48px, 5vw, 88px); }
.home-page .research__fig { overflow: hidden; }
.home-page .research__card:hover .research__fig img { transform: scale(1.04); }
.home-page .research__title { font-weight: 340; }
.home-page .research__n { font-size: 10px; letter-spacing: .13em; }
.home-page .news__row { transition: background 500ms, padding 600ms var(--motion-ease); }
.home-page .news__row:hover { background: #0a766b08; }
.home-page .footer { position: relative; padding-top: clamp(40px, 5vw, 80px); overflow: hidden; }
.home-page .footer::before { content: 'LIMM.'; display: block; max-width: 1720px; margin: 0 auto 40px; padding-inline: var(--page-x); color: var(--ink); font-family: var(--font-display); font-size: clamp(64px, 9vw, 160px); font-weight: 320; line-height: .8; letter-spacing: -.075em; }

@media (min-width: 1024px) {
  .home-page .research[data-seq] .research__title { font-size: clamp(36px, 3.4vw, 60px); line-height: 1.04; }
  .home-page .research[data-seq] .research__desc { font-size: 15px; max-width: 36ch; line-height: 1.85; }
  .home-page .research[data-seq] .research__n { font-size: 12px; }
}
@media (min-width: 1800px) {
  .hero-cinema__copy { padding-top: 100px; }
  .hero-cinema__description { font-size: 16px; max-width: 430px; }
  .hero-cinema__topline, .hero-cinema__eyebrow { font-size: 10px; }
}
@media (max-width: 1100px) {
  .home-page .wordmark__full { font-size: 9px; }
  .home-page .nav { gap: 19px; }
  .home-page .nav__link { font-size: 11px; }
  .hero-cinema__annotation { max-width: 220px; }
}
@media (max-width: 1023px) {
  .home-page .masthead__inner { min-height: 78px; }
  .hero-cinema { min-height: max(730px, calc(100svh - 110px)); }
  .hero-cinema__title { font-size: clamp(83px, 10.8vw, 110px); }
  .hero-cinema__art { inset: -2% -24% -2% -4%; }
  .hero-cinema__art img, .hero-cinema__video { object-position: 45% center; }
  .hero-cinema__scene::after { background: linear-gradient(90deg, #061820c9, #06182044 65%, transparent), linear-gradient(0deg, #061820, transparent 65%); }
  .hero-cinema__copy { padding-top: 80px; }
  .hero-cinema__location, .hero-cinema__annotation { display: none; }
  .lab-intro__heading, .lab-intro__body { grid-template-columns: 1fr 1.8fr; }
  .lab-intro__statement { font-size: clamp(42px, 5.7vw, 65px); }
  .lab-intro__label { max-width: 180px; }
  .lab-intro__text { padding-right: 0; }
  .lab-intro__text p { font-size: 13px; }
}
@media (max-width: 700px) {
  .home-page { --page-x: 24px; }
  .home-page .announce__link { font-size: 8px; min-height: 30px; }
  .home-page .masthead__inner { min-height: 70px; }
  .home-page .wordmark__name { font-size: 31px; }
  .hero-cinema { min-height: max(765px, calc(100svh - 100px)); }
  .hero-cinema__topline { padding-top: 23px; font-size: 8px; }
  .hero-cinema__school { display: none; }
  .hero-cinema__copy { padding-top: 49px; padding-bottom: 245px; }
  .hero-cinema__eyebrow { font-size: 8px; letter-spacing: .08em; margin-bottom: 20px; }
  .hero-cinema__title { font-size: clamp(62px, 13.8vw, 96px); line-height: 1.01; letter-spacing: -.06em; }
  /* The still fills the hero from its top edge, as it does on a laptop: the
     sphere is cut by the frame under the masthead and nowhere else. The phone
     file is a tall cut of the same render — the sphere's lower-left arc across
     the upper half, the bubble stream below — at roughly the hero's own
     proportion, so cover trims a few percent off the sides and nothing off
     the top. The box this replaces began 22% down and ran half a screen off
     each side: the lattice ended in a hard edge behind the headline and most
     of the sphere lay outside the viewport. */
  .hero-cinema__art { inset: 0; }
  .hero-cinema__art img { object-fit: cover; object-position: 50% 0; }
  /* The film keeps the footprint the old art box had — the same width, the
     same overhangs, the same foot — with its top pulled down to a band 71%
     tall, so the still carries the masthead region above it. Its phone file
     is the wide strip at 960, roughly 3:1; stretched over a hero nearer 1:2 it
     would be a six-times zoom of a few lattice threads, and in the band it is
     a middle strip at under twice, anchored right of centre where the
     microsphere is. The still now stands under it, so the band's top is
     feathered as well as its left and foot; those two fades fall outside the
     viewport, as before, and the scene scrim does the rest. Off by data
     (cinema.film) since the professor chose the still. */
  .hero-cinema__video {
    /* A replaced element's auto width comes from its own ratio, not from
       left and right, so the band's width is stated: the art's, plus the
       two overhangs. */
    inset: auto -35% -7% -50%; width: 185%; height: 71%; object-position: 55% center;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%), linear-gradient(0deg, transparent, #000 14%), linear-gradient(180deg, transparent, #000 24%);
    mask-image: linear-gradient(90deg, transparent, #000 12%), linear-gradient(0deg, transparent, #000 14%), linear-gradient(180deg, transparent, #000 24%);
  }
  /* The headline block has no void to stand on here, so the scrim follows it:
     dark at the top-left where the type is, thinning toward the bottom-right
     where the sphere's arc and the brightest bubbles are. The masthead band
     and the foot band are the laptop's own. */
  .hero-cinema__scene::after {
    background:
      linear-gradient(112deg, #061820 20%, #061820d9 38%, #06182088 54%, #06182022 68%, transparent 80%),
      linear-gradient(180deg, #061820 0px, #061820b3 58px, transparent 128px),
      linear-gradient(0deg, #061820 0%, #06182088 12%, transparent 34%);
  }
  /* The description and the Publications link cross the lit arc where the
     scrim has thinned; a dark halo keeps them readable there. The arrow
     button is ink on mint and gets none (below, with its sizing). */
  .hero-cinema__copy { text-shadow: 0 1px 16px #061820d9; }
  .hero-cinema__description { margin-top: 23px; font-size: 12px; line-height: 1.8; max-width: 280px; }
  .hero-cinema__actions { gap: 28px; margin-top: 20px; }
  .explore-link { font-size: 11px; gap: 15px; }
  .explore-link__arrow { width: 42px; height: 42px; font-size: 22px; text-shadow: none; }
  .hero-cinema__publication { font-size: 10px; gap: 10px; }
  .hero-cinema__bottom { min-height: 66px; gap: 8px; font-size: 7px; }
  .hero-cinema__artnote { display: none; }
  .hero-cinema__motion { gap: 0; }
  .lab-intro { padding-top: 70px; }
  .lab-intro__heading, .lab-intro__body { display: flex; flex-direction: column; align-items: stretch; gap: 30px; }
  .lab-intro__label { max-width: none; font-size: 8px; padding-top: 0; }
  .lab-intro__statement { font-size: clamp(35px, 7.9vw, 54px); line-height: 1.14; }
  .lab-intro__body { margin-top: 35px; }
  .lab-intro__text { display: grid; grid-template-columns: 40px 1fr; gap: 22px; }
  .lab-intro__asterisk { font-size: 42px; }
  .lab-intro__text p { font-size: 12px; }
  .lab-intro__text a { grid-column: 2; margin-bottom: 10px; font-size: 8px; }
  .lab-intro__figure { margin-inline: calc(var(--page-x) * -1); }
  .lab-intro__figure img { aspect-ratio: 4 / 3; }
  .lab-intro__figure .hero__cap { margin-inline: var(--page-x); font-size: 8px; }
  .home-page .section-head--major .section-head__word { font-size: 47px; }
  .home-page .section-head__aside { font-size: 9px; }
  .home-page .footer::before { margin-bottom: 40px; }
}
@media (max-width: 359px) {
  .home-page { --page-x: 20px; }
  .hero-cinema__title { font-size: 56px; }
  .hero-cinema__actions { gap: 20px; }
  .explore-link { gap: 10px; }
  .lab-intro__statement { font-size: 31px; }
}

/* The Home half of the motion overrides in experience.css. */
.motion-paused .hero-cinema__art { transform: none; will-change: auto; }
.motion-paused .word-reveal__word { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .hero-cinema__art { transform: none !important; will-change: auto; }
  .hero-cinema__particles { display: none; }
  .word-reveal__word { opacity: 1; }
  .lab-intro__figure:hover img { transform: none; }
}
