/* ============================================================
   Pyronyx — waste to synthesis gas
   White ground, hairline grid, one warm accent taken from the
   logo. The drawing does the talking; the type stays quiet.
   ============================================================ */

@property --p { syntax:'<number>'; initial-value:0; inherits:true; }

:root{
  /* taken from the Pyronyx logo file: #f65225 orange, #f9f7f7 off-white */
  --paper:    #FFFFFF;
  --tint:     #FAF8F7;  /* the logo off-white, deepened enough to band a section */
  --ink:      #101012;  /* 18:1 on paper  */
  --ink-2:    #5F646B;  /*  6.0:1 — secondary text, clears AA at small sizes */
  --ink-3:    #9A9EA6;  /*  2.6:1 — hairline rules and marks ONLY, never text */
  --rule:     #E9E6E3;
  --rule-2:   #DAD6D2;
  --amber:    #F65225;  /* brand orange — fills and marks, never text on white */
  --amber-2:  #C93D17;  /* darkened brand orange — drawing strokes */
  --amber-dk: #C33A12;  /*  5.3:1 — the text-safe version of the same hue */
  --flame:    #FF5A22;  /* heat only, inside the plume */

  /* the process section inverts. Ratios are against --night, so they are the
     dark-ground equivalents of --ink, --ink-2 and --rule above. Amber clears
     5.8:1 here, which is why it can carry text on this ground and not on paper.
     The old brand orange cleared 9.5:1 on this ground; this one clears AA and
     not AAA, so do not lighten the night ground under it. */
  --night:    #0A0B0C;
  --on-1:     #FFFFFF;                  /* 19:1  */
  --on-2:     rgba(255,255,255,.64);    /*  7.9:1 */
  --on-3:     rgba(255,255,255,.48);    /*  5.0:1 — smallest text allowed on night */
  --on-rule:  rgba(255,255,255,.13);    /* rules and marks ONLY, never text */

  --display:'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --body:   'IBM Plex Sans', system-ui, -apple-system, sans-serif;
  --mono:   'IBM Plex Mono', ui-monospace, Menlo, monospace;

  /* Archivo has no Cyrillic subset, so on the Russian page every heading would
     drop to Helvetica while the English page kept its display face. ru/ loads
     Golos Text, a Cyrillic-first grotesque, and this hands it over. Body and
     mono are IBM Plex, which carries Cyrillic, and do not change. */

  --edge:   clamp(1rem, 5vw, 6rem);      /* where the standing rules sit */
  --inset:  clamp(1rem, 2.2vw, 2.5rem);  /* rule to text — never let them touch */
  --gutter: calc(var(--edge) + var(--inset));
  --pad:    clamp(4.5rem, 11vh, 9rem);   /* vertical section rhythm */
  --ease:   cubic-bezier(.22,.61,.36,1);
}

:root:lang(ru){ --display:'Golos Text', 'Helvetica Neue', Arial, sans-serif; }

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

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

body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--body);
  font-weight:300;
  font-size:clamp(1rem, .96rem + .22vw, 1.1rem);
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  /* clip, not hidden: hidden makes <body> a scroll container, which puts every
     fixed and sticky layer on a more expensive path. clip does the same job
     without that. The hidden line stays as the fallback for Safari 15. */
  overflow-x:hidden;
  overflow-x:clip;
}

a{ color:inherit; }

.col{
  width:100%;
  max-width:80rem;
  margin-inline:auto;
  padding-inline:var(--gutter);
}

.skip{
  position:absolute; left:-9999px;
  background:var(--ink); color:var(--paper);
  padding:.75rem 1rem; z-index:99; min-height:44px;
  display:inline-flex; align-items:center;
  font-family:var(--mono); font-size:.8rem;
}
.skip:focus{ left:1rem; top:1rem; }

:focus-visible{ outline:2px solid var(--amber-dk); outline-offset:3px; }

/* ── the standing grid: two hairlines the whole page hangs from ─────── */

/* fixed, so section backgrounds never paint over them and the frame reads
   as one continuous structure the whole page hangs from */
.grid-rules{
  position:fixed;
  top:0; bottom:0; left:0; right:0;
  margin-inline:auto;
  max-width:80rem;
  pointer-events:none;
  z-index:2;
}
.grid-rules i{
  position:absolute; top:0; bottom:0;
  width:1px; background:var(--rule);
}
.grid-rules i:first-child{ left:var(--edge); }
.grid-rules i:last-child{ right:var(--edge); }
@media (max-width:720px){ .grid-rules{ display:none; } }

main{ position:relative; }
section{ position:relative; z-index:1; }
/* the top bar is sticky, so a deep link would otherwise land with its first
   line underneath it */
section[id]{ scroll-margin-top:5rem; }

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

.eyebrow{
  font-family:var(--mono);
  font-size:.74rem;
  font-weight:500;
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--ink-2);
  margin:0 0 2rem;
}

/* Increase's device: the claim in black, the elaboration in grey,
   one continuous sentence rather than a headline plus a deck. */
.h2, .hero-title{
  font-family:var(--display);
  font-weight:600;
  letter-spacing:-.028em;
  line-height:1.04;
  margin:0 0 2.5rem;
  text-wrap:balance;
}
.h2{ font-size:clamp(1.9rem, 1.15rem + 3vw, 3.4rem); }
.hero-title{ font-size:clamp(2.3rem, 1.1rem + 4.6vw, 5rem); }

.h2 span, .hero-title span{
  color:var(--ink-2);
  font-weight:400;
  letter-spacing:-.015em;
  display:block;
  margin-top:.55em;
  max-width:46ch;
  font-size:.46em;
  line-height:1.38;
}
.hero-title span{ font-size:.34em; max-width:44ch; }

.note{
  font-family:var(--mono);
  font-size:.76rem;
  line-height:1.7;
  color:var(--ink-2);
  max-width:52ch;
  margin:3rem 0 0;
}

.prose p{ margin:0 0 1.3rem; max-width:46ch; color:var(--ink-2); }
.prose p:last-child{ margin-bottom:0; }
.prose b{ color:var(--ink); font-weight:500; }

/* ── buttons ────────────────────────────────────────────────────────── */

.btn{
  display:inline-flex; align-items:center;
  font-family:var(--body);
  font-weight:400;
  font-size:.92rem;
  line-height:1;
  min-height:44px;
  padding:.75rem 1.2rem;
  border:1px solid var(--ink);
  background:var(--ink);
  color:var(--paper);
  text-decoration:none;
  border-radius:3px;
  transition:background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.btn:hover{ background:transparent; color:var(--ink); }
.btn-quiet{
  background:transparent; color:var(--ink); border-color:var(--rule-2);
}
.btn-quiet:hover{ border-color:var(--ink); background:transparent; }
.btn-sm{ padding:.5rem .85rem; font-size:.84rem; border-radius:3px; }

/* ── top bar ────────────────────────────────────────────────────────── */

/* No backdrop-filter here. A blurred backdrop has to be re-sampled and
   re-blurred across the full width of the bar on every scroll frame, and at
   this opacity it bought almost nothing visually. The bar is near-opaque
   instead, which composites for free. */
.topbar{
  position:sticky; top:0; z-index:30;
  background:color-mix(in srgb, var(--paper) 96%, transparent);
  border-bottom:1px solid var(--rule);
  transition:background .3s var(--ease), border-color .3s var(--ease);
}

/* main.js sets .on-night while the dark process section is under the bar.
   The lockup goes to a flat white version of itself rather than sitting in a
   pale slab over black. */
.on-night .topbar{
  background:color-mix(in srgb, var(--night) 96%, transparent);
  border-bottom-color:var(--on-rule);
}
.on-night .topnav > a:not(.btn){ color:var(--on-2); }
.on-night .topnav > a:not(.btn):hover{ color:var(--on-1); }
.on-night .btn{ background:var(--on-1); border-color:var(--on-1); color:var(--night); }
.on-night .btn:hover{ background:transparent; color:var(--on-1); }
.topbar-in{
  max-width:80rem; margin-inline:auto;
  padding:1rem var(--gutter);
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
}
.brand{
  display:inline-flex; align-items:center; text-decoration:none;
  min-height:44px;
}
/* The Pyronyx lockup is 5.2:1, twice as wide as the mark-plus-acronym it
   replaced, so the narrow-screen height is set explicitly rather than left to
   the 30px: at 320px the bar has to hold the lockup, the language switch and
   the Contact button on one line. */
.brand-logo{ display:block; height:30px; width:auto; }
/* one lockup or the other, never both. These have to sit after .brand-logo:
   .brand-night is no more specific than it, so order is what decides. */
.brand-night{ display:none; }
.on-night .brand-day{ display:none; }
.on-night .brand-night{ display:block; }
.topnav{ display:flex; align-items:center; gap:clamp(.9rem,2.2vw,1.9rem); }
/* padding, not font-size, gets these to a 44px tap target */
.topnav > a:not(.btn):not(.lang){
  font-size:.9rem; text-decoration:none; color:var(--ink-2);
  display:inline-flex; align-items:center;
  min-height:44px; padding-inline:.15rem;
  transition:color .18s var(--ease);
}
.topnav > a:not(.btn):not(.lang):hover{ color:var(--ink); }
@media (max-width:640px){ .topnav > a:not(.btn):not(.lang){ display:none; } }

/* ── language switch ────────────────────────────────────────────────────
   One link, not a pair: it names the language you would be going to rather
   than the one you are already reading, so it costs the same width at 320px
   as it does at 1600px and never needs hiding.
   ─────────────────────────────────────────────────────────────────────── */

.lang{
  font-family:var(--mono);
  font-size:.74rem; font-weight:500;
  letter-spacing:.1em;
  text-decoration:none;
  color:var(--ink-2);
  border:1px solid var(--rule-2);
  border-radius:3px;
  display:inline-flex; align-items:center; justify-content:center;
  min-width:2.6rem; min-height:44px;
  flex:none;
  transition:color .18s var(--ease), border-color .18s var(--ease);
}
.lang:hover{ color:var(--ink); border-color:var(--ink); }
.on-night .lang{ color:var(--on-2); border-color:var(--on-rule); }
.on-night .lang:hover{ color:var(--on-1); border-color:var(--on-1); }

/* The top bar is the tightest row on the site and the only one that can run out
   of width. At 320px it has to hold the lockup, the switch and the call to
   action on one line, and the Russian labels are longer than the English ones:
   "Связаться" is half again the width of "Contact". Measured at 320px, the row
   comes to 245 of the 256px the content box allows, in Russian, which is the
   worse of the two. If you lengthen either label, measure it again.
   Padding-inline is deliberately left alone here, so the lockup keeps landing
   on the same line as the text below it. */
@media (max-width:560px){
  .brand-logo{ height:20px; }
  .topbar-in{ gap:.5rem; }
  .topnav{ gap:.5rem; }
  .topnav .btn-sm{ padding-inline:.7rem; }
  .lang{ min-width:2.2rem; }
}

/* ── hero ───────────────────────────────────────────────────────────── */

.hero{ padding-block:clamp(4rem,13vh,9rem) clamp(3.5rem,9vh,6rem); }
.hero-actions{ display:flex; flex-wrap:wrap; gap:.7rem; margin-bottom:3rem; }

.stamp{
  display:inline-flex; align-items:center; gap:.55rem;
  font-family:var(--mono); font-size:.74rem;
  letter-spacing:.1em; text-transform:uppercase;
  color:var(--ink-2); margin:0;
}
.pip{
  width:7px; height:7px; border-radius:50%;
  background:var(--amber);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--amber) 22%, transparent);
}

/* ══════════════════════════════════════════════════════════════════════
   THE PROCESS — one run of the unit, played by scroll on a pinned dark stage.

   main.js turns scroll position through the section into a single number,
   --p, from 0 to 1, and draws the frame sequence into a canvas at that
   position. Everything else in the stage reads the same --p in CSS: the four
   step segments on the left rail, the step copy handing over, the charge
   falling into the mouth, and the heat pooling under the machine.

   Step k owns --p from k/--steps to (k+1)/--steps. Nothing is hardcoded to
   five: change --steps on the section and add list items.
   ══════════════════════════════════════════════════════════════════════ */

.flow{
  --p:0;
  --steps:5;
  --seg:calc(1 / var(--steps));      /* how much of --p one step is worth */
  /* How much of --p a hand-over takes. Because captions never overlap, this
     is also how long nothing is lit between two of them, so it is kept short:
     about a seventh of a step, which reads as a swap rather than as a gap. */
  --fade:.03;

  position:relative;
  /* above the page-wide hairlines, which are light and would burn through */
  z-index:3;
  /* 105vh per step rather than 115: at five steps the old figure asked for
     nearly six screens of scroll to get through one section. */
  min-height:calc(var(--steps) * 105vh);
  background:var(--night);
  color:var(--on-1);
}

/* the standing rules continue through the dark section in its own contrast */
.flow::before, .flow::after{
  content:"";
  position:absolute; top:0; bottom:0;
  width:1px; background:var(--on-rule);
  pointer-events:none;
}
.flow::before{ left:max(var(--edge), calc((100% - 80rem) / 2 + var(--edge))); }
.flow::after{ right:max(var(--edge), calc((100% - 80rem) / 2 + var(--edge))); }
@media (max-width:720px){ .flow::before, .flow::after{ display:none; } }

.flow-stage{
  position:sticky; top:0;
  height:100vh;
  display:grid;
  grid-template-rows:auto 1fr;
  gap:clamp(1.25rem, 3.5vh, 2.75rem);
  /* the top bar is sticky and translucent; keep the heading clear of it */
  padding-block:clamp(4.5rem, 11vh, 7rem) clamp(1.5rem, 5vh, 3rem);
  overflow:hidden;
  /* nothing inside paints or lays out beyond the stage, and it already clips,
     so let the browser skip anything that would fall outside */
  contain:layout paint;
}

/* ── the heading, pinned and constant while the steps play under it ──── */

.flow-head{ text-align:center; }
.flow-head .eyebrow{ color:var(--amber); margin-bottom:1.15rem; }
.flow-title{
  font-family:var(--display);
  font-weight:600;
  font-size:clamp(1.7rem, 1.1rem + 2.4vw, 3rem);
  letter-spacing:-.035em;
  line-height:1.05;
  margin:0;
  text-wrap:balance;
}
.flow-sub{
  margin:.85rem auto 0;
  max-width:44ch;
  color:var(--on-2);
  font-size:clamp(.95rem, .9rem + .2vw, 1.08rem);
}

/* ── the left rail: one segment per step, filling as that step plays ─── */

.rail{
  position:absolute;
  inset-inline:0; top:50%;
  margin:0 auto;
  max-width:80rem;
  padding:0 calc(var(--edge) + 1rem);
  transform:translateY(-50%);
  display:flex; flex-direction:column; gap:.5rem;
  list-style:none;
  pointer-events:none;
  z-index:4;
}
.rail li{
  position:relative;
  width:3px;
  height:clamp(2.25rem, 6.5vh, 4.25rem);
  border-radius:999px;
  background:var(--on-rule);
  overflow:hidden;
}
.rail i{
  position:absolute; inset:0;
  border-radius:inherit;
  background:var(--amber);
  transform-origin:top center;
  transform:scaleY(clamp(0, calc((var(--p) - var(--i) * var(--seg)) / var(--seg)), 1));
}
/* below this the rail has nowhere to sit without crowding the machine */
@media (max-width:1080px){ .rail{ display:none; } }

/* ── the machine and the step copy, side by side ─────────────────────── */

.flow-body{
  display:grid;
  grid-template-columns:1fr;
  align-items:center;
  gap:clamp(1.5rem, 4vh, 2.5rem);
  min-height:0;
}
@media (min-width:900px){
  /* the machine and its copy read as one object. Both columns are sized to
     their content and the pair is centred, rather than each being given half
     the page and left floating at opposite rules. */
  .flow-body{
    grid-template-columns:auto minmax(0, 28rem);
    justify-content:center;
    gap:clamp(2rem, 5vw, 4.5rem);
  }
}

.rig{
  position:relative;
  justify-self:center;
  /* Sized for the stacked layout, where the captions sit under the drawing and
     need the room. The two-column rule below gives it the space back. The last
     term is what the stage has left after the heading and its padding, so a
     short window shrinks the drawing instead of cropping its feet. */
  height:min(46vh, 24rem, calc(100vh - 28rem));
  aspect-ratio:760 / 1220;
  min-height:0;

  /* Five phases cut out of --p, one per step, each finishing just inside its
     own fifth so the drawing never contradicts the caption beside it. Every
     moving part in the tower reads one of these and nothing else. The fifths
     are 0 to .2, .2 to .4, and so on; each ramp starts a little after its
     fifth opens and ends a little before it closes. */
  --feed: clamp(0, calc((var(--p) - .015) / .15), 1);
  --fire: clamp(0, calc((var(--p) - .215) / .15), 1);
  --run:  clamp(0, calc((var(--p) - .415) / .15), 1);
  --gas:  clamp(0, calc((var(--p) - .615) / .14), 1);
  --cycle:clamp(0, calc((var(--p) - .815) / .14), 1);
}

/* Side by side, so the drawing is only competing with the heading for height.
   This has to sit after the base rule: same specificity, so order decides. */
@media (min-width:900px){
  .rig{ height:min(70vh, 42rem, calc(100vh - 18rem)); }
}

.unit{
  position:absolute; inset:0;
  width:100%; height:100%;
  overflow:visible;
}
/* the parts that change on every scroll frame. Naming them lets the
   compositor keep them on their own layers instead of repainting the drawing. */
.u-hot, .u-halo, .u-warm, .u-fan{ will-change:transform, opacity; }

/* the cold drawing: one weight for the silhouette, a lighter one for fittings,
   and the lightest of all for the insulation shell, which is a wrapper rather
   than a part of the machine */
.u-line{ vector-effect:non-scaling-stroke; }
.u-key{ stroke:var(--on-2); stroke-width:4.5; }
.u-thin{ stroke:var(--on-3); stroke-width:3; }
.u-shell{ stroke:rgba(255,255,255,.2); stroke-width:3; }

/* ── feed: the charge goes in on the screw, and keeps going in ─────── */

/* The tower is fed continuously, so these arrive and stay rather than falling
   through once. Each travels its own short distance and settles, which reads
   as a moving screw at any position and still reads as a full housing at the
   end, where --p pins to 1 on a narrow screen and under reduced motion. */
.u-grain rect{
  --t:clamp(0, calc((var(--feed) - var(--d)) * 4), 1);
  opacity:var(--t);
  transform:translateX(calc(var(--t) * 66px));
}

/* ── fire: the base comes up to working temperature ────────────────── */

.u-hot, .u-halo{ opacity:var(--fire); }
.u-fan{
  fill:none;
  stroke:var(--on-2); stroke-width:4.5;
  stroke-linecap:round;
  vector-effect:non-scaling-stroke;
  transform-box:view-box;
}
.u-fan-l{ transform:rotate(calc(var(--fire) * 900deg)); transform-origin:90px 823px; }
.u-fan-r{ transform:rotate(calc(var(--cycle) * 900deg)); transform-origin:700px 823px; }

/* ── run: the column comes up to heat behind its insulation ────────── */

.u-warm{
  opacity:var(--run);
  transform:scaleY(var(--run));
  transform-origin:400px 803px;
  transform-box:view-box;
}
.u-stack{
  fill:none;
  stroke:var(--on-3); stroke-width:3;
  stroke-linecap:round; stroke-linejoin:round;
  vector-effect:non-scaling-stroke;
  opacity:var(--run);
  transform:translateY(calc((1 - var(--run)) * 22px));
  transform-box:view-box;
}

/* ── gas: it leaves the side offtake for the engine ────────────────── */

/* the only orange in the drawing that is not heat, because it is the one
   thing the tower makes and sells */
.u-gas circle{
  fill:var(--amber);
  --t:clamp(0, calc((var(--gas) - var(--d)) * 4), 1);
  opacity:var(--t);
  transform:translateX(calc(var(--t) * 44px));
}
.u-gas-tip{
  fill:none;
  stroke:var(--amber); stroke-width:4.5;
  stroke-linecap:round; stroke-linejoin:round;
  vector-effect:non-scaling-stroke;
  opacity:var(--gas);
}

/* ── recover: nothing leaves the tower unused ──────────────────────── */

.u-exh{
  fill:none;
  stroke:var(--on-2); stroke-width:4.5;
  stroke-linecap:round; stroke-linejoin:round;
  vector-effect:non-scaling-stroke;
  opacity:var(--cycle);
}
.u-ash rect{
  --t:clamp(0, calc((var(--cycle) - var(--d)) * 4), 1);
  opacity:var(--t);
  transform:translateY(calc(var(--t) * 52px));
}

/* ── the steps: stacked in one cell, handing over on fade plus drift ─── */

.steps{
  position:relative;
  display:grid;
  list-style:none;
  margin:0; padding:0;
  min-height:clamp(10rem, 21vh, 14rem);
  align-content:center;
}
.step{
  grid-area:1/1;
  align-self:center;
  will-change:transform, opacity;
  /* Lit across its own segment and nowhere else. The ramps sit inside the
     segment rather than straddling its edges, so one caption is always at zero
     before the next starts to arrive. Overlap is fine for the frames, where it
     reads as a cross-fade, and useless for text, where it reads as two
     paragraphs printed on top of each other. */
  --a:calc(var(--i) * var(--seg));
  --b:calc((var(--i) + 1) * var(--seg));
  --mid:calc((var(--i) + .5) * var(--seg));
  opacity:clamp(0, calc(min(
      (var(--p) - var(--a)) / var(--fade),
      (var(--b) - var(--p)) / var(--fade))), 1);
  /* it arrives from below and leaves upward, one continuous drift */
  transform:translateY(calc((var(--p) - var(--mid)) * -150px));
}
/* The first and last steps ramp from outside the section, so one is already
   lit the moment the stage pins and one is still lit as it leaves. Without
   this, --p of exactly 0 lights nothing, which is also what a visitor with
   scripting off would get, permanently. */
.step:first-child{ --a:calc(0 - var(--fade)); }
.step:last-child{ --b:calc(1 + var(--fade)); }

.step-label{
  display:flex; align-items:center; gap:.85rem;
  margin:0 0 .95rem;
  font-family:var(--display);
  font-weight:500;
  font-size:clamp(1.15rem, 1rem + .7vw, 1.6rem);
  letter-spacing:-.022em;
  line-height:1;
}
.step-num{ font-family:var(--mono); font-size:.72em; color:var(--on-3); }
/* drawn, not typed: the page carries no dashes in its copy */
.step-bar{ width:1.75rem; height:1px; background:var(--on-rule); flex:none; }
.step > p{
  margin:0;
  max-width:40ch;
  color:var(--on-2);
  font-size:clamp(.95rem, .92rem + .2vw, 1.06rem);
  line-height:1.6;
}

/* ── bands ──────────────────────────────────────────────────────────── */

.band{ padding-block:var(--pad); border-bottom:1px solid var(--rule); }
.band-tint{ background:var(--tint); }

.split{ display:grid; grid-template-columns:1fr; gap:clamp(2.5rem,5vw,4.5rem); }
@media (min-width:900px){ .split{ grid-template-columns:1fr 1fr; align-items:start; } }

/* ── feedstock ──────────────────────────────────────────────────────── */

/* minmax(0,1fr), not 1fr. A plain 1fr floors at min-content, so one long
   unbreakable word in a cell widens its whole track and pushes the grid past
   the page: at 320px the Russian list did exactly that. These tracks are
   allowed to shrink instead, and the cells wrap rather than overflow. */
.feed{
  list-style:none; margin:0; padding:0;
  display:grid; gap:1px;
  background:var(--rule);
  border:1px solid var(--rule);
  grid-template-columns:repeat(2,minmax(0,1fr));
}
@media (min-width:700px){ .feed{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
@media (min-width:1000px){ .feed{ grid-template-columns:repeat(5,minmax(0,1fr)); } }
.feed li{
  background:var(--paper);
  padding:1.6rem 1.1rem;
  font-family:var(--mono);
  font-size:.8rem;
  line-height:1.4;
  color:var(--ink);
  display:flex; align-items:flex-end;
  min-height:6.5rem;
  overflow-wrap:break-word;
  hyphens:auto;
}
/* two columns of monospace inside a 256px content box leaves about 92px of
   text per cell at the default padding, which is under the longest word in
   either language. The cells give some of their padding back instead. */
@media (max-width:560px){
  .feed li{ padding:1.2rem .8rem; min-height:5.5rem; }
}

/* a second heading inside a band that already opened with one */
.eyebrow-mid{ margin-top:clamp(3rem,6vh,4.5rem); }

/* four outputs, not ten inputs, so the track count changes with it */
.feed-out{ grid-template-columns:repeat(2,minmax(0,1fr)); }
@media (min-width:700px){ .feed-out{ grid-template-columns:repeat(4,minmax(0,1fr)); } }

/* ── sizes ──────────────────────────────────────────────────────────── */

/* Same one-pixel-gap grid as .cards, so the three size classes land on the
   same rules as everything else in the band. */
.sizes{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:1fr; gap:1px;
  background:var(--rule); border:1px solid var(--rule);
}
@media (min-width:820px){ .sizes{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
.sizes li{ background:var(--paper); padding:clamp(1.4rem,2.4vw,2rem); }
.size-tag{
  font-family:var(--mono); font-size:.72rem;
  letter-spacing:.2em; text-transform:uppercase;
  color:var(--amber-dk); margin:0 0 1.2rem;
}
.size-out{
  font-family:var(--display); font-weight:600;
  font-size:clamp(1.3rem,1.1rem + .8vw,1.7rem);
  letter-spacing:-.025em; line-height:1;
  margin:0 0 .9rem;
}
.size-body{ margin:0; color:var(--ink-2); font-size:.94rem; }

/* ── figures ────────────────────────────────────────────────────────── */

.figures{ margin:0; border-top:1px solid var(--rule-2); }
.figures > div{
  display:grid;
  grid-template-columns:minmax(6rem,auto) 1fr;
  gap:0 clamp(1rem,3vw,2.25rem);
  align-items:baseline;
  padding:1.3rem 0;
  border-bottom:1px solid var(--rule);
}
.figures dt{
  font-family:var(--display); font-weight:600;
  font-size:clamp(1.4rem,1.05rem + 1.3vw,2rem);
  letter-spacing:-.025em; line-height:1;
  color:var(--ink);
}
.figures dd{ margin:0; font-size:.92rem; line-height:1.45; color:var(--ink-2); }
/* the source line is doing credibility work — it has to be readable */
.figures dd span{
  display:block; font-family:var(--mono); font-size:.75rem;
  color:var(--ink-2); margin-top:.45rem;
}

/* ── cards ──────────────────────────────────────────────────────────── */

.cards{
  display:grid; grid-template-columns:1fr; gap:1px;
  background:var(--rule); border:1px solid var(--rule);
}
@media (min-width:820px){ .cards{ grid-template-columns:1fr 1fr; } }
.card{ background:var(--paper); padding:clamp(1.6rem,3vw,2.6rem); }
.card-tag{
  font-family:var(--mono); font-size:.72rem;
  letter-spacing:.2em; text-transform:uppercase;
  color:var(--amber-dk); margin:0 0 1.6rem;
}
.card h3{
  font-family:var(--display); font-weight:600;
  font-size:clamp(1.35rem,1.15rem + .9vw,1.85rem);
  letter-spacing:-.022em; line-height:1.05; margin:0 0 .9rem;
}
.card p{ margin:0; color:var(--ink-2); max-width:42ch; }
.card p + p{ margin-top:1.1rem; }

/* ── applications ───────────────────────────────────────────────────── */

.apps{ list-style:none; margin:0; padding:0; border-top:1px solid var(--rule-2); }
.apps li{ padding:1.2rem 0; border-bottom:1px solid var(--rule); }
.apps span{
  display:block; font-family:var(--display); font-weight:500;
  font-size:1.05rem; letter-spacing:-.008em; margin-bottom:.25rem;
}
.apps em{
  font-style:normal; font-family:var(--mono);
  font-size:.72rem; line-height:1.6; color:var(--ink-2); display:block;
}

/* ── status ─────────────────────────────────────────────────────────── */

.facts{ list-style:none; margin:0 0 clamp(3rem,6vh,4.5rem); padding:0; }
.facts li{
  position:relative;
  padding:1.1rem 0 1.1rem 2rem;
  border-bottom:1px solid var(--rule);
  color:var(--ink-2);
  max-width:64ch;
}
.facts li:first-child{ border-top:1px solid var(--rule-2); }
.facts li::before{
  content:""; position:absolute; left:0; top:1.8em;
  width:11px; height:1px; background:var(--amber);
}
.facts b{ color:var(--ink); font-weight:500; }

.seeking{
  border-left:2px solid var(--amber);
  padding-left:clamp(1.25rem,3vw,2.25rem);
  margin-bottom:clamp(2.5rem,5vh,4rem);
}
.seeking h3{
  font-family:var(--display); font-weight:600;
  font-size:clamp(1.1rem,1rem + .5vw,1.4rem);
  letter-spacing:-.015em; margin:0 0 1.3rem;
}
.seek{
  margin:0; padding:0; list-style:none;
  counter-reset:s; display:grid; gap:1.4rem;
}
.seek li{
  counter-increment:s; color:var(--ink-2);
  max-width:58ch; position:relative; padding-left:2.3rem;
}
.seek li::before{
  content:counter(s,decimal-leading-zero);
  position:absolute; left:0; top:.2em;
  font-family:var(--mono); font-size:.7rem; color:var(--amber-dk);
}
.seek b{ color:var(--ink); font-weight:500; display:block; margin-bottom:.2rem; }

/* ── contact ────────────────────────────────────────────────────────── */

.mail{
  display:inline-block;
  font-family:var(--display); font-weight:500;
  font-size:clamp(1.15rem,.85rem + 1.5vw,2.1rem);
  letter-spacing:-.025em;
  text-decoration:none;
  border-bottom:2px solid var(--amber);
  /* padding above, not min-height: the border is the underline, and giving the
     box a floor would leave it stranded below the text. This lifts the hit area
     to 44px while the rule stays where it belongs. */
  padding-top:.6rem;
  padding-bottom:.1em;
  margin-bottom:clamp(2.5rem,5vh,3.5rem);
  word-break:break-word;
  transition:border-color .18s var(--ease);
}
.mail:hover{ border-color:var(--ink); }

.person{ border-top:1px solid var(--rule); padding-top:1.4rem; max-width:46ch; }
.person-name{ font-family:var(--display); font-weight:600; font-size:1.1rem; margin:0 0 .15rem; }
.person-role{
  font-family:var(--mono); font-size:.7rem;
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--amber-dk); margin:0 0 .9rem;
}
.person p:last-child{ margin:0; color:var(--ink-2); font-size:.94rem; }

/* ── footer ─────────────────────────────────────────────────────────── */

.foot{ padding-block:2.5rem; }
.foot-in{ display:grid; gap:.35rem; }
.foot-brand{ font-family:var(--display); font-weight:600; font-size:.9rem; margin:0; }
.foot-note{
  font-family:var(--mono); font-size:.7rem;
  color:var(--ink-2); margin:0;
}

/* ══════════════════════════════════════════════════════════════════════
   REVEAL — the one piece of motion the rest of the page gets. Blocks arrive
   from a little below as they enter the viewport, children staggered.

   The hidden state is gated behind .js, which an inline script in <head> sets
   before first paint. With scripting off nothing is ever hidden.
   ══════════════════════════════════════════════════════════════════════ */

.js [data-reveal]{
  opacity:0;
  transform:translateY(16px);
}
.js [data-reveal].is-in{
  opacity:1;
  transform:none;
  transition:
    opacity .75s var(--ease) calc(var(--d, 0) * 65ms),
    transform .75s var(--ease) calc(var(--d, 0) * 65ms);
}
/* these sit in a one-pixel grid gap that shows through as a rule, so moving
   them would flicker the rule. They fade in place instead. */
.js .feed[data-reveal], .js .cards[data-reveal]{ transform:none; }

/* ══════════════════════════════════════════════════════════════════════
   NARROW SCREENS AND REDUCED MOTION — the process stops being a scroll rig.
   --p pins to 1, the stage becomes an ordinary block, the machine shows its
   finished running state and all four steps are read at once. Nothing is
   caught mid-air on a page that is meant to hold still.
   ══════════════════════════════════════════════════════════════════════ */

@media (max-width:760px), (max-height:560px){
  .flow{ min-height:auto; --p:1; }
  .flow-stage{
    position:static; height:auto;
    display:block;
    padding-block:clamp(3rem, 8vh, 4.5rem);
  }
  .flow-head{ text-align:left; }
  .rail{ display:none; }
  .flow-body{ display:block; }

  /* the drawing holds its finished state: --p is pinned to 1, so the lid is
     shut, the furnace is lit and nothing is caught mid-air */
  .rig{
    height:auto; width:100%; max-width:17rem;
    margin:2.75rem auto;
  }

  .steps{ display:block; min-height:0; padding-inline:0; }
  .step{
    opacity:1;
    transform:none;
    padding-block:1.2rem;
    border-top:1px solid var(--on-rule);
  }
  .step:first-child{ border-top:0; }
  .step > p{ max-width:none; }
}

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{
    transition-duration:.001ms !important;
    animation-duration:.001ms !important;
  }
  .js [data-reveal]{ opacity:1; transform:none; }

  .flow{ min-height:auto; --p:1; }
  .flow-stage{
    position:static; height:auto;
    display:block;
    padding-block:clamp(3rem, 8vh, 4.5rem);
  }
  .rail{ display:none; }
  .flow-body{ display:block; }
  .rig{ height:auto; width:100%; max-width:19rem; margin:2.75rem auto; }
  .steps{ display:block; min-height:0; }
  .step{
    opacity:1; transform:none;
    padding-block:1.2rem;
    border-top:1px solid var(--on-rule);
  }
  .step:first-child{ border-top:0; }
  .step > p{ max-width:none; }
}
