/* Erbgut scrollytelling landing page. Light, premium.
   Tokens: marketing/BRAND.md section 9, light column, with the darker text step
   for every accent that carries normal-size text, so all of it clears WCAG AA.
   Measured pairs are listed at the bottom of this file.

   Two layouts live here on purpose:
     - no JS (or scrolly.js failed): a plain, legible, top-to-bottom story.
     - html.js: pinned stages, copy blocks stacked and cross-faded by scrolly.js.
   Everything the shell animates is opacity and transform only. */

@font-face {
  font-family: "Erbgut Sans";
  src: url("assets/fonts/InstrumentSans-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Erbgut Mono";
  src: url("assets/fonts/JetBrainsMono-latin.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
}

:root {
  --paper: #eaf3ed;
  --surface: #ffffff;
  --surface-2: #dcede1;
  --ink: #08130b;
  --muted: #3e6b52;
  --rule: #c3d9c9;
  --rule-strong: #638f7c;   /* dots and other small controls: 3.2 : 1 on paper */

  --audit: #0b6e82;
  --cost: #c41477;
  --gain: #0a7a41;
  --tbd-ink: #6e6200;
  --tbd-bg: #eaf5da;
  --tbd-line: #a9b81c;

  --sans: "Instrument Sans", "Erbgut Sans", "Helvetica Neue", Arial, sans-serif;
  --mono: "JetBrains Mono", "Erbgut Mono", ui-monospace, Menlo, Consolas, monospace;

  --gut: 20px;
  --hdr: 60px;
  /* one shared page edge, so the header, the copy and the art all line up.
     Widened where the scene dots appear, so the art never runs under them. */
  --edge: max(var(--gut), calc((100% - 1460px) / 2));
  --tscale: 1;          /* scroll length multiplier, cut for reduced motion */

  color-scheme: light;
}

@media (prefers-reduced-motion: reduce) {
  :root { --tscale: .34; }
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  html { scroll-behavior: auto; }
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  color: var(--ink);
  background: var(--paper);
  font-family: var(--sans);
  font-size: 18px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* The page wash. A fixed decorative layer rather than a fixed background image:
   it gets its own compositor layer and never repaints while scrolling. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(120% 55% at 50% -12%, #ffffff 0%, rgba(255, 255, 255, 0) 62%),
    radial-gradient(85% 48% at 92% 104%, rgba(11, 110, 130, .06) 0%, rgba(11, 110, 130, 0) 72%);
}

/* ── type ─────────────────────────────────────────────────────────────── */

h1 { margin: 0; font-weight: 600; }

p { margin: 0; }

.line {
  font-size: clamp(25px, 3.1vw, 39px);
  line-height: 1.16;
  letter-spacing: -.022em;
  font-weight: 600;
  text-wrap: balance;
  max-width: 19ch;
}
.line--xl {
  font-size: clamp(31px, 5.3vw, 62px);
  letter-spacing: -.032em;
  line-height: 1.06;
  max-width: 16ch;
}
.line--sm {
  font-size: clamp(19px, 2.1vw, 26px);
  font-weight: 500;
  letter-spacing: -.012em;
  line-height: 1.32;
  max-width: 26ch;
}
.line--soft { color: var(--muted); font-weight: 500; }
.line + .line { margin-top: .5em; }

.hl-cost { color: var(--cost); }
.hl-audit { color: var(--audit); }

.num {
  font-family: var(--mono);
  font-weight: 600;
  font-size: clamp(46px, 7.4vw, 92px);
  line-height: .96;
  letter-spacing: -.045em;
  font-variant-numeric: tabular-nums;
  margin-bottom: .16em;
}
.num--audit { color: var(--audit); }
.num--gain  { color: var(--gain); }

.cap {
  font-size: clamp(14px, 1.1vw, 15.5px);
  line-height: 1.5;
  color: var(--muted);
  max-width: 34ch;
  margin-top: .85em;
}

.idx {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .12em;
  color: var(--muted);
  margin-bottom: 1.15em;
}
.idx::after {
  content: "";
  display: inline-block;
  width: 30px;
  height: 1px;
  background: var(--rule);
  vertical-align: middle;
  margin-left: 12px;
}

/* ── skip link, header, progress ──────────────────────────────────────── */

.skip {
  position: absolute;
  left: 0;
  top: 0;
  transform: translateY(-140%);   /* never offset sideways: no stray scrollWidth */
  z-index: 60;
  background: var(--surface);
  color: var(--ink);
  padding: 12px 18px;
  border-radius: 0 0 10px 0;
}
.skip:focus { transform: none; }

.top {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 40;
  height: var(--hdr);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-inline: var(--edge);
  background: rgba(234, 243, 237, .82);
  backdrop-filter: saturate(150%) blur(12px);
  -webkit-backdrop-filter: saturate(150%) blur(12px);
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  text-decoration: none;
  font-weight: 600;
  letter-spacing: -.03em;
  font-size: 19px;
}
.brand img { display: block; }  /* the mark has no tile to round off */
.top nav { display: flex; gap: 22px; }
.top nav a {
  color: var(--muted);
  text-decoration: none;
  font-size: 15px;
  font-weight: 500;
}
.top nav a:hover, .top nav a:focus-visible { color: var(--audit); }

.bar {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--rule);
}
.bar i {
  display: block;
  height: 100%;
  background: var(--audit);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* ── scene index dots ─────────────────────────────────────────────────── */

.dots {
  position: fixed;
  z-index: 35;
  right: 22px;
  top: 50%;
  transform: translateY(-50%);
  display: none;
  flex-direction: column;
  gap: 13px;
}
.dots a {
  display: block;
  padding: 3px;
  line-height: 0;
}
.dots i {
  display: block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--rule-strong);
  transition: background .3s ease, transform .3s ease;
}
.dots a:hover i { background: var(--muted); }
.dots a.is-on i { background: var(--audit); transform: scale(1.45); }
@media (min-width: 1100px) {
  .dots { display: flex; }
  :root { --edge: max(58px, calc((100% - 1420px) / 2)); }
}

/* ── the no-JS story (also the base every .js rule builds on) ─────────── */

.scene {
  position: relative;
  padding: 72px 0 8px;
}
.scene:first-child { padding-top: calc(var(--hdr) + 56px); }
.stage {
  display: grid;
  gap: 28px;
  padding-inline: var(--edge);
}
.art { display: none; }
.cb + .cb { margin-top: 40px; }
html:not(.js) .cb + .cb .idx { display: none; }   /* one scene number per scene */
.copy--flow .stats { display: grid; gap: 40px; margin-top: 8px; }

/* ── the scrolly layout ───────────────────────────────────────────────── */

html.js .scene {
  height: calc((1 + var(--travel, 1) * var(--tscale)) * 100vh);   /* fallback */
  height: calc((1 + var(--travel, 1) * var(--tscale)) * 100svh);
  padding: 0;
}
html.js .stage {
  position: sticky;
  top: 0;
  height: 100vh;                                                  /* fallback */
  height: 100svh;
  overflow: hidden;
  grid-template-rows: minmax(0, 1fr) auto;
  align-content: stretch;
  padding-block: calc(var(--hdr) + 14px) 34px;
}
html.js .art {
  display: block;
  position: relative;
  min-height: 0;
  border-radius: 22px;
  background: var(--surface);
  border: 1px solid var(--rule);
  box-shadow:
    0 1px 2px rgba(8, 19, 11, .04),
    0 30px 70px -40px rgba(8, 19, 11, .45);
  overflow: hidden;
}
html.js .copy {
  position: relative;
  min-height: clamp(210px, 37svh, 330px);
  pointer-events: none;
}
html.js .copy > .cb {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  opacity: 0;
  margin: 0;
}
html.js [data-at] {
  opacity: 0;
  transform: translate3d(0, var(--sy, 0px), 0);
}
html.js .cue[data-at] { transform: translate3d(-50%, var(--sy, 0px), 0); }
html.js .cb a, html.js .cb button { pointer-events: auto; }

/* centred scenes: the art fills the stage, the copy floats over it */
html.js .scene--center .stage { grid-template-rows: minmax(0, 1fr); padding-block: 0; }
html.js .scene--center .art,
html.js .scene--center .copy {
  grid-area: 1 / 1;
  min-height: 0;
  background: none;
  border: 0;
  box-shadow: none;
  border-radius: 0;
}
html.js .scene--center .copy {
  padding: calc(var(--hdr) + 20px) 0 60px;
  align-items: flex-start;
}
html.js .scene--center .copy::before {
  content: "";
  position: absolute;
  inset: -10% -20%;
  background: radial-gradient(58% 46% at 42% 50%, var(--paper) 0%, rgba(234, 243, 237, .88) 46%, rgba(234, 243, 237, 0) 78%);
  pointer-events: none;
}
/* the receipts run the full width, so their wash does too */
html.js .scene--center .copy--flow::before {
  background: radial-gradient(82% 52% at 50% 50%, var(--paper) 0%, rgba(234, 243, 237, .92) 52%, rgba(234, 243, 237, 0) 88%);
}
/* only the in-flow receipts need lifting above the wash; the stacked .cb blocks
   are already absolutely positioned and paint after it in tree order. */
html.js .scene--center .copy--flow > * { position: relative; }

@media (min-width: 900px) {
  html.js .stage {
    grid-template-columns: minmax(290px, 33%) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
    gap: 56px;
    align-items: center;
    padding-block: calc(var(--hdr) + 26px) 46px;
  }
  html.js .copy  { grid-column: 1; grid-row: 1; min-height: 0; }
  html.js .art   { grid-column: 2; grid-row: 1; height: 100%; }
  html.js .scene--center .copy,
  html.js .scene--center .art { grid-column: 1 / -1; grid-row: 1; }
}

/* ── proof: three receipts, in flow, each fading in on its own ─────────── */

html.js .copy--flow {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: calc(var(--hdr) + 24px) 60px;
}
.idx--flow { margin-bottom: 26px; }
.stats { display: grid; gap: 34px; }
.stat .num { margin-bottom: .1em; }
.stat-b {
  font-size: clamp(17px, 1.6vw, 20px);
  font-weight: 600;
  letter-spacing: -.015em;
}
.stat .cap { margin-top: .35em; max-width: 30ch; }
@media (min-width: 820px) {
  .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 42px; align-items: start; }
}

/* ── hero scroll cue ──────────────────────────────────────────────────── */

.cue {
  position: absolute;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  text-decoration: none;
  font-size: 13px;
  letter-spacing: .09em;
}
.cue svg { width: 17px; height: 17px; animation: nudge 2.4s ease-in-out infinite; }
@keyframes nudge {
  0%, 100% { transform: translateY(0); opacity: .55; }
  50%      { transform: translateY(5px); opacity: 1; }
}
html.js .cue { bottom: 26px; }
html:not(.js) .cue { display: none; }

/* ── close ────────────────────────────────────────────────────────────── */

.wordmark {
  /* the full lockup is nearly square (434x380), so it is sized by height here
     rather than by width, or it swallows the close scene */
  display: block;
  width: min(232px, 60vw);
  height: auto;
  margin-bottom: 26px;
}
.asks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  font-size: clamp(17px, 1.7vw, 21px);
  font-weight: 500;
}
.asks a {
  color: var(--audit);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 4px;
}
.asks a:hover { text-decoration-thickness: 2.5px; }
.sep { color: var(--rule); }
.tbd {
  font-family: var(--mono);
  font-size: .82em;
  color: var(--tbd-ink);
  background: var(--tbd-bg);
  border: 1px dashed var(--tbd-line);
  border-radius: 7px;
  padding: 3px 9px;
}
.credit {
  margin-top: 40px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 54ch;
}
/* The close scene draws the lockup and the links itself, so the copy layer keeps only the
   dataset credit. Centre it under the composition rather than leaving it against the edge,
   and push it to the foot of the stage so it does not sit across the artwork. */
html.js .scene--close .copy > .cb {
  justify-content: flex-end;
  align-items: center;
  padding-bottom: clamp(20px, 5vh, 56px);
}
html.js .scene--close .credit { margin-top: 0; text-align: center; }

/* ── focus ────────────────────────────────────────────────────────────── */

:focus-visible {
  outline: 2.5px solid var(--audit);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ── small screens ────────────────────────────────────────────────────── */

@media (max-width: 899px) {
  :root { --gut: 18px; --hdr: 56px; }
  html.js .scene--center .copy { padding-bottom: 96px; }
  .line { max-width: 24ch; }
  .cap { max-width: 42ch; }
}

@media (max-width: 420px) {
  body { font-size: 17px; }
  .num { font-size: clamp(42px, 13vw, 56px); }
  html.js .copy { min-height: 40svh; }
}

/* ── measured contrast of every text pair shipped here (WCAG 2.x) ───────
   ink    #08130b on paper #eaf3ed   16.73 : 1
   muted  #3e6b52 on paper            5.41 : 1
   audit  #0b6e82 on paper            5.20 : 1
   gain   #0a7a41 on paper            4.79 : 1
   cost   #c41477 on paper            4.99 : 1
   tbd    #6e6200 on tbd-bg #eaf5da   5.45 : 1
   ink on surface #ffffff            18.95 : 1
   muted on surface                   6.12 : 1
   All of the above pass AA at normal size. Non-text: an inactive scene dot
   (--rule-strong) is 3.22 : 1 on paper, hairlines (--rule) are decoration.
   Verdict colours are never used alone: cost and audit only tint a phrase that
   already says what it means, and the placeholder is dashed as well as yellow. */

/* Beat 9 is drawn twice: the proof scene counts the three receipts up, and the
   copy block below repeats them as static HTML. The copy block is the no-JS
   fallback, so it is hidden once the scene is running, the same way the close
   scene's duplicates are. Flip this one rule if the receipts should be HTML
   instead, and delete the counters in site/scenes/proof.js with it. */
html.js .copy--flow .stats { display: none; }
/* and with the receipts gone the wash has nothing left to lift, so it only
   dissolved the scene's own numbers. The scene number below it reads on paper. */
html.js .scene--center .copy--flow::before { display: none; }

/* The close scene draws its own mark, wordmark and links. The copy block below is the
   no-JS fallback, so it is hidden once the scene is running; the dataset credit is not
   drawn by the scene and stays. */
html.js .scene--close .copy .wordmark,
html.js .scene--close .copy .asks { display: none; }

/* Two things the close scene needs from the copy layer, and nothing else.
   1. No paper wash. The wash exists to lift copy off busy art, but here the only
      copy left is one credit line, and the wash's hot centre landed exactly on
      the scene's wordmark and dissolved it.
   2. The credit is a footnote under the composition, not a second column: it
      goes to the bottom of the stage, centred, instead of floating at the left
      edge halfway up. */
html.js .scene--close .copy::before { display: none; }
html.js .scene--close .copy { align-self: stretch; }
html.js .scene--close .copy > .cb {
  justify-content: flex-end;
  padding-bottom: clamp(24px, 5vh, 64px);
}
html.js .scene--close .credit {
  margin: 0 auto;
  max-width: 62ch;
  text-align: center;
}

/* The hero's second block: a short plain-language explainer that appears once the
   headline has cleared, for a reader who has no idea what DNA storage is. */
/* Sits at the foot of the hero stage, centred, so the helix keeps the upper frame. */
html.js .copy > .cb--intro { justify-content: flex-end; padding-bottom: clamp(24px, 6vh, 72px); }
.cb--intro { max-width: 46rem; margin-inline: auto; text-align: center; }
.cb--intro .intro {
  font-size: clamp(1.05rem, 1.6vw, 1.35rem); line-height: 1.55;
  color: var(--muted); margin: 0 0 1rem;
}
.cb--intro .intro--lead { color: var(--ink); font-weight: 500; margin-bottom: 0; }
@media (max-width: 720px) { .cb--intro { text-align: left; } }
