/* ============================================================================
   ADAPTYV x NIMMERSATT: sections built around the protein graphics.
   Thread (fixed engine stage), specimen, interlude A (#protein, stop-motion Raupe),
   Kapitel "Nächster Gang." (#next: Struktur und Bewegung), danach Kontakt.
   Zusätzliche lokale Punktwolken begleiten diese drei Abschnitte.
   Tokens come from style.css (:root). One family for all text. The cyan
   protein tones live inside the engine's drawings only.
   ========================================================================== */

/* --- Thread -------------------------------------------------------------------
   One fixed, full-viewport engine canvas behind the whole page (z-index -1, so it sits in the root
   stacking context: sections and multiply-blended plates composite over it). sections.js moves it
   from section to section. It is decoration: no pointer events, hidden from assistive tech.
   No CSS blend here: the engine multiplies its own dots (paper: null, blend: "multiply"), and behind the
   thread there is only the white page, so a second multiply would change nothing and cost a compositing pass. */
.thread {
  position: fixed;
  inset: 0;
  z-index: -1;
  height: 100vh;
  height: 100lvh;
  margin: 0;
  pointer-events: none;
}
.thread canvas { width: 100%; height: 100%; }
.thread.is-off { visibility: hidden; }
html.is-static .thread, html:not(.js) .thread { display: none; }
/* The small hero specimen is only the fallback for static mode or a missing engine. */
html.has-thread .hero__mark { display: none; }
html.has-thread .hero__sub { grid-column: 1 / span 7; }
html.has-thread .bite__fig { display: none; }

/* --- Specimen ---------------------------------------------------------------
   A square holding the poster image and the live canvas (css/protein.css). */
.specimen { width: 100%; }
button.specimen {
  appearance: none;
  -webkit-appearance: none;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
button.specimen:focus-visible { outline: 3px solid var(--ink); outline-offset: 6px; }
.hero__mark { aspect-ratio: 1 / 1; }
.hero__mark canvas { cursor: pointer; }

/* --- Interlude A: #protein ---------------------------------------------------
   One big live protein. The section is a tall runway, the stage sticks to the top of the screen (no GSAP pin,
   so Lenis and the scroll stay native). Scrolling draws the protein as points, grid, ascii and steps the Raupe
   from bite to bite. Static mode and a missing engine fall back to a plain, flowing stage with its own figure. */
.bite { position: relative; }
/* Rest space, desktop: with "About" at rest, the first line of "Hungry for" stays below the fold. */
@media (min-width: 761px) {
  main > section.bite { margin-top: calc(var(--gap-section) + 120px); }
}
.bite__stage {
  position: relative;
  overflow: hidden;
  /* Transparent: the thread (fixed, behind the page) is the picture here. The fallback figure draws its own. */
}
.bite__title {
  position: relative;
  z-index: 3;
  margin: 0;
  padding: var(--s8) var(--gutter) var(--s4);
  font-size: var(--fs-display);
  line-height: 0.86;
  pointer-events: none;
}
/* Without JavaScript the poster stays in the stage. */
html:not(.js) .bite__view { margin-top: 0; }
html:not(.js) .bite__fig { inset: 0; }
/* The mask keeps the descenders of "Hungry" and "protein": it reaches further below the line. */
.bite__title .sh__line { padding-bottom: 0.24em; margin-bottom: -0.24em; }
.bite__stage.is-eaten .bite__title .sh__arw { visibility: hidden; }
.bite__view {
  position: relative;
  aspect-ratio: 1 / 1;
  max-height: 78vh;
  max-height: 78svh;
  width: 100%;
  margin-top: -6vw;
}
/* Keyboard focus on the view (fallback figure, engine keyTarget): a two-tone ring drawn inside the stage edge. */
.bite__view[data-protein-key]:focus-visible {
  outline: 3px solid #fff;
  outline-offset: -6px;
  box-shadow: inset 0 0 0 9px var(--ink);
}
.bite__fig {
  position: absolute;
  inset: -8% -20vw;
  z-index: 1;
  width: auto;
  height: auto;
}

/* The XL Raupe. Its height is the unit. It never glides: sections.js sets one transform per step (snap), and a pose
   (stretch, squash, normal) per step through --sx, --sy, --sk. Layers: .bite__raupe (position, tilt, jitter)
   > .raupe__flip (facing, pose) > .raupe__body (chew, steps()). */
.bite__raupe {
  --rh: clamp(104px, 17svh, 190px);
  --face: 1;
  --sx: 1;
  --sy: 1;
  --sk: 0deg;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 4;
  font-size: calc(var(--rh) / 0.72);
  will-change: transform;
}
.bite__raupe .raupe__flip {
  transform: scaleX(var(--face)) scale(var(--sx), var(--sy)) skewX(var(--sk));
  transform-origin: 50% 100%;
}
/* Without svh (old engines): the same Raupe size in vh. Modern browsers never read this block. */
@supports not (height: 100svh) {
  .bite__raupe { --rh: clamp(104px, 17vh, 190px); }
}
/* Until a step (or the parked state) is placed the Raupe waits out of sight. */
.bite__raupe:not(.is-placed) { visibility: hidden; }
/* Chew: four held poses at 8 frames a second (scale 0.93 to 1.07, 7 percent each way), only while it sits still and only while motion is on. */
@keyframes chew {
  0%   { transform: translate(0, 0) scale(1, 1); }
  25%  { transform: translate(1.2%, 0) scale(0.93, 1.07); }
  50%  { transform: translate(0, 0.6%) scale(1.07, 0.93); }
  75%  { transform: translate(-1%, 0.8%) scale(0.94, 1.06); }
  100% { transform: translate(0, 0) scale(1, 1); }
}
.bite__raupe .raupe__body { transform-origin: 99.6% 73%; }
.bite__raupe.is-chewing .raupe__body { animation: chew 0.5s steps(1, end) infinite; }
html.is-paused .bite__raupe .raupe__body { animation: none !important; }
/* The pause switch mirrors the motion state visually; main.js updates its accessible state. */
html.is-paused .nav__motion { background: var(--ink); color: var(--paper); }
/* Reduced motion cuts every transition to 0.01 s, so the menu links (which inherit visibility) were still hidden when focus moved into them on open. */
@media (prefers-reduced-motion: reduce) { .nav__links, .nav__links * { transition: none !important; } }
/* Fallback without the bites API: the old fluid crawl from left to right. */
.bite__raupe.is-fluid { --rh: clamp(76px, 25vw, 124px); top: 50%; margin-top: calc(var(--rh) / -2); }
.bite__raupe.is-parked {
  left: var(--gutter);
  top: auto;
  bottom: var(--s3);
  margin: 0;
  transform: none !important;
}

/* Live layout: runway plus sticky stage. */
html.js:not(.is-static) .bite {
  --run: max(2300px, 290svh);                  /* about 70 px of scroll per bite: one step of the Raupe, held, then the next */
  height: calc(100vh + var(--run));
  height: calc(100svh + var(--run));
  /* Once the protein is eaten the lower half of the stage is empty: the next chapter starts under it, so the headline
     hands over to "Next course." without a blank screen in between. */
  margin-bottom: -50vh;
  margin-bottom: -50svh;
}
html.js:not(.is-static) .bite__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  min-height: 480px;
}
html.js:not(.is-static) .bite__title {
  position: absolute;
  left: 0;
  top: 0;
  padding: 80px var(--gutter) 0;
}
html.js:not(.is-static) .bite__view {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  max-height: none;
  width: auto;
  margin: 0;
}
html.js:not(.is-static) .bite__fig { inset: -4svh 0 -12svh 14vw; }

@media (max-width: 760px) {
  html.js:not(.is-static) .bite { --run: max(2200px, 290svh); }
  html.js:not(.is-static) .bite__title { padding-top: 72px; }
  .bite__raupe { --rh: clamp(64px, 19vw, 96px); }
  .bite__raupe.is-fluid { --rh: clamp(64px, 19vw, 96px); }
}
@media (min-width: 761px) {
  html.js:not(.is-static) .bite__title { padding-top: 96px; font-size: min(12.4vw, 20vh, 140px); font-size: min(12.4vw, 20svh, 140px); }
  .bite__fig { inset: -4svh 0 -12svh 26vw; }          /* right of "protein.", like the thread in the live layout */
  .bite__raupe.is-fluid { --rh: clamp(120px, 27svh, 290px); top: 63%; }
  @supports not (height: 100svh) {
    .bite__raupe.is-fluid { --rh: clamp(120px, 27vh, 290px); }
  }
}
/* Static mode on a desktop window: a framed stage like before. */
@media (min-width: 761px) and (min-height: 560px) {
  html.is-static .bite__stage { height: 100vh; max-height: 1100px; min-height: 560px; }
  @supports (height: 100svh) { html.is-static .bite__stage { height: 100svh; } }
  html.is-static .bite__title { position: absolute; left: 0; top: 0; padding: 96px var(--gutter) 0; font-size: min(12.4vw, 20vh, 140px); font-size: min(12.4vw, 20svh, 140px); }
  html.is-static .bite__view { position: absolute; inset: 64px 0 0 0; aspect-ratio: auto; max-height: none; width: auto; margin-top: 0; }
}

/* Short viewports (landscape phones, 520 px high or less): the stage is the viewport. The 480 px floor used to hang the
   bottom 90 px below the fold, with the caterpillar in it. The runway (--run) is unchanged. */
@media (max-height: 520px) {
  .bite__raupe { --rh: clamp(38px, 11svh, 58px); }
  html.js:not(.is-static) .bite__stage { min-height: 0; }
}
/* Desktop-width landscape (844 by 390): the headline steps down. Phones keep their own headline size. */
@media (min-width: 761px) and (max-height: 520px) {
  html.js:not(.is-static) .bite__title { padding-top: 72px; font-size: min(12.4vw, 16vh, 220px); font-size: min(12.4vw, 16svh, 220px); }
}

/* Static phones (?reveal=1, reduced motion): the protein square has empty space under its drawing (about the lowest fifth),
   so the stage ends at the drawing and the normal chapter gap follows. A share of the width: it scales with the square. */
@media (max-width: 760px) {
  html.is-static .bite__view { margin-bottom: -16%; }
}

/* --- Chapter: #next "Next course." -----------------------------------------------
   The second line of the page. The clothes come first, these are the ideas served on the side: a wider break
   before it (--gap-chapter), the title one step lighter, and the opening to itself, one focus per screen. */
.next__intro {
  display: grid;
  align-content: center;
  min-height: calc(100vh - 64px);
  min-height: calc(100svh - 64px);
}
.next__intro .sh { margin-bottom: var(--gap-title-lead); }
.next__lead { max-width: 34ch; margin: 0; }
.next__intro + .dir { margin-top: var(--gap-section); }
/* Direction changes use the shared section gap, including Structure to Motion. */

/* Direction head: the word, then the idea in one or two lines. */
.dir__line {
  max-width: 30ch;
  margin: 0;
  font-size: var(--fs-statement);
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}

/* The deck is the evidence: smaller than the demonstration, with an editorial note for the selected clip. Space is reserved before
   refs.json arrives (deck, controls, note and selection rows), so anchors below never move. */
.dir__mount {
  --deck-h: clamp(132px, 15.5vw, 232px);
  --refnote-h: 148px;
  min-height: calc(var(--deck-h) + 8px + 40px + var(--refnote-h) + 16px + var(--rows, 1) * 36px);
}
.refs__noscript { margin: var(--s4) 0 0; color: var(--muted); font-weight: 600; }
.refs__fallback { max-width: 60ch; margin-top: var(--s7); }
.refs__fallback h3 { margin: var(--s6) 0 var(--s3); font-size: 28px; line-height: 1.1; letter-spacing: -0.03em; }
.refs__fallback ul { margin: 0; padding: 0; list-style: none; }
.refs__fallback li { margin: 0 0 var(--s2); line-height: 1.4; }
.refs__fallback a { font-weight: 700; text-underline-offset: 3px; }

.dir .conceptstack { margin-bottom: 0; }
.dir .conceptstack__deck { height: var(--deck-h, clamp(132px, 15.5vw, 232px)); }
.dir .conceptstack__bar { margin-top: var(--s2); }
.dir .stackbtn { min-height: 40px; }

.next__foot { margin-top: var(--gap-block); }
/* The note under the chapter lead (credits for the decks below). Same grey and size as before. */
.next__credit { margin: var(--s3) 0 0; font-size: var(--fs-small); font-weight: 600; line-height: 1.4; color: var(--muted); }

/* --- Motion: live figure -------------------------------------------------------
   js/sections.js inserts <div class="motion-demo"><canvas></canvas></div> between the Motion statement and its
   deck. Ohne Figur nimmt sie keinen Platz ein. Desktop: bis 1180 px, zentriert, 4:3.
   Mobil: volle Breite, quadratisch. Das größere Höhenmaß vergrößert das Protein. */
.motion-demo {
  display: block;
  width: min(calc(100% - 2 * var(--gutter)), 1180px);
  aspect-ratio: 4 / 3;
  margin: var(--gap-lead-content) auto var(--gap-block);
  background: var(--paper);
  overflow: hidden;
}
.motion-demo:empty, .motion-demo[hidden] { display: none; }
.motion-demo canvas { width: 100%; height: 100%; display: block; }
@media (max-width: 760px) {
  .motion-demo { width: 100%; aspect-ratio: 1; margin: var(--gap-lead-content) 0 var(--gap-block); }
}

.stackhit {
  position: absolute;
  inset: 0;
  z-index: 2;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}
/* One editorial line gives the selected film a reason for being here. It stays below the
   moving media, so keyboard, hover and touch have the same readable state. */
.refnote {
  max-width: 64ch;
  margin: var(--s4) 0 0;
}
.refnote__eyebrow {
  margin: 0 0 var(--s1);
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.3;
}
.refnote__title {
  margin: 0;
  font-size: clamp(22px, 2.1vw, 32px);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.02;
}
.refnote__description {
  margin: var(--s2) 0 0;
  font-size: var(--fs-body);
  line-height: 1.5;
  text-wrap: pretty;
}

/* Reference index: visual ideas only, four columns for four clips, never wrapped. */
.refindex {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--s5);
  margin: var(--s3) 0 0;
  padding: 0;
  list-style: none;
}
.refindex button {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  min-height: 36px;
  padding: 0;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
}
.refindex__h { overflow: hidden; text-overflow: ellipsis; }
.refindex__arw { flex: 0 0 auto; font-size: 15px; }
.refindex button.is-selected { border-bottom-color: var(--ink); }
.refindex button:hover .refindex__h,
.refindex button:focus-visible .refindex__h { text-decoration: underline; text-underline-offset: 3px; }
.refindex button:hover .arw__shape { animation: jab 0.9s var(--ease-out) 1; }
.refindex button:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }

@media (max-width: 1100px) {
  .refindex { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dir__mount { --rows: 2; }
}
@media (max-width: 760px) {
  .next__intro { min-height: calc(100vh - 56px); min-height: calc(100svh - 56px); }
  /* Phones show one clip at a time at its own ratio; the first one of every deck is 16:9. */
  .dir__mount { --deck-h: calc((100vw - 2 * var(--gutter)) * 0.5625); --refnote-h: 166px; min-height: calc(var(--deck-h) + 16px + 48px + var(--refnote-h) + 16px + var(--rows, 2) * 36px); }
  .dir__line { max-width: 34ch; }
  .dir .conceptstack__bar { margin-top: var(--s3); }
  .dir .stackbtn { min-height: 48px; }
  .refindex { column-gap: var(--s4); }
  .refindex button { font-size: 13px; }
}
@media (max-width: 380px) {
  .refindex { grid-template-columns: minmax(0, 1fr); }
  .dir__mount { --rows: 4; }
}

/* Touch: the reference selectors are 44 px tall, but the 36 px rows stay (a negative block margin takes the 4 px back on each side),
   so the deck and the anchors below do not move. The taller box is the touch target. */
@media (pointer: coarse), (max-width: 760px) {
  .refindex button { min-height: 44px; margin-block: -4px; }
  .nav__motion { min-height: 44px; }
}

/* Short lines wrap evenly, no single orphan word. */
.lead, .dir__line { text-wrap: pretty; }

/* Decorative protein clouds continue the hero's spatial language through the lower page.
   Text and controls sit above them; the hosts never capture pointer or keyboard input. */
.has-ambient { position: relative; isolation: isolate; }
.has-ambient > :not(.ambient-protein) { position: relative; z-index: 1; }
.ambient-protein { position: absolute; z-index: 0; pointer-events: none; mix-blend-mode: multiply; }
.ambient-protein canvas, .ambient-protein img { display: block; position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.ambient-protein canvas { opacity: 0; }
.ambient-protein.has-engine canvas { opacity: 1; }
/* Die nächste Wolke beginnt erst nach dem Auslaufen der Raupe. */
html.js:not(.is-static) .bite:has(.bite__stage:not(.is-eaten)) + .next .ambient-protein--chapter { visibility: hidden; }
.ambient-protein--chapter { width: min(72vw, 980px); height: 100%; right: -8vw; top: 0; mask-image: linear-gradient(90deg, transparent, #000 35%); }
.ambient-protein--motion { width: min(62vw, 780px); height: 580px; right: -7vw; top: -130px; mask-image: linear-gradient(90deg, transparent 2%, #000 45%); }
.ambient-protein--contact { width: min(66vw, 860px); height: 690px; right: -10vw; top: -180px; mask-image: linear-gradient(90deg, transparent 5%, #000 50%); }
@media (max-width: 760px) {
  .ambient-protein--chapter { width: 105vw; height: 55%; right: -24vw; top: -4%; mask-image: linear-gradient(180deg, #000 45%, transparent); }
  .ambient-protein--motion { width: 105vw; height: 400px; right: -47vw; top: -84px; opacity: 0.6; mask-image: linear-gradient(90deg, transparent 4%, #000 70%); }
  .ambient-protein--contact { width: 110vw; height: 410px; right: -32vw; top: -80px; opacity: 0.55; mask-image: linear-gradient(90deg, transparent, #000 65%); }
}
