/* particle-river styles — THIS FILE IS THE SOURCE. Formerly generated from the
   prototype by scripts/build-companion.mjs, which has been removed: the scene
   lived there while the grid and block editor lived here, so the same elements
   had to be worked on in two places. The bridge classes added by
   particle-river.js make these selectors match the exported markup; the
   override block near the end reconciles the exported Tailwind styling with
   the river-owned look, and carries the layout grid and editor chrome. */

  /* SCOPED: this sheet is global in the site, so an unscoped reset would strip the
     margins off every other page. It MUST go through :where() — scoped as plain
     `#kensa-root *` the id gives the reset a higher specificity than every class
     rule in this file, so it silently zeroed the spacing that isn't marked
     important (the prologue lost 1,088px of its run and the credits 199px).
     :where() keeps it at zero specificity, exactly like the bare `*` it replaces. */
  :where(#kensa-root, #kensa-root *) { box-sizing: border-box; margin: 0; padding: 0; }

  /* Oxanium — the machine-cut display face for the particle title + section drop caps */
  @font-face { font-family: Oxanium; font-weight: 700; font-style: normal; font-display: swap; src: url(/before-the-deluge/oxanium-700.woff2) format("woff2"); }
  /* Six Caps — the towering "skyscraper" face for the section drop caps */
  @font-face { font-family: "Six Caps"; font-weight: 400; font-style: normal; font-display: swap; src: url(/before-the-deluge/six-caps.woff2) format("woff2"); }

  /* blue lives on <html> only — the true backdrop. body stays transparent so the
     z-index:-1 river canvas paints ABOVE the blue but behind all the text. */
  /* The article's own root. On the prototype the blue backdrop lived on <html> and
     the canvas sat at z-index:-1 against the root; here the site owns <html> and
     <body> and paints its own dark background, which would bury the water. So the
     root becomes its own stacking context (isolation, NOT transform — a transform
     would break the fixed-position canvas): the root's blue paints first, then the
     photographs at -2, then the canvas at -1, then all the type. The layering is
     now self-contained and independent of whatever the site does behind it. */
  #kensa-root {
    position: relative; isolation: isolate; z-index: 0;
    background: #2a1ec6;
    color: #e8e8e8; font-family: ui-sans-serif, system-ui, sans-serif; --panel-w: 224px;
    -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
    /* the base every relative size in the piece hangs off. The site's post styles
       compute 20px here, which silently enlarged the dek and the labels by a
       quarter — this is the article's own reading base, not the site's. */
    font-size: 16px;
    --serif: Signifier, Palatino, Georgia, 'Times New Roman', serif;
    /* one reading size shared by the prologue steps, the pinned promise cards,
       and the prose body — change this and all three move together */
    --body-size: 1.15rem; }
  /* The document itself, behind the site's own black shell. It shows in the places
     the shell does not reach — the rubber-band overscroll, and the strips iOS Safari
     samples for its status bar and toolbar when a page declares no theme colour
     (see the meta tag in the template, which is what actually tints those bars on
     current versions). */
  html:has(> body #kensa-root) { background: #2a1ec6; }

  /* The site's post wrapper carries 20px of bottom padding (styles/posts.scss).
     This article is exactly one viewport tall and scrolls inside itself, so that
     padding has nothing to space and shows up as a black strip along the bottom
     of every screen. */
  .post:has(> #kensa-root) { padding-bottom: 0; }

  /* Held invisible until the engine has restructured the page, so the reader never
     watches the plain exported article turn into the laid-out one. The engine adds
     the class after its first layout; the template also adds it on a timeout, so a
     script error can never leave the article blank. */
  #kensa-root:not(.kensa-ready) { visibility: hidden; }

  /* Mimics kensa's preview pane: the scroll container */
  .stage { height: 100vh; overflow-y: scroll; scroll-behavior: auto; }

  .intro, .outro {
    min-height: 60vh; display: flex; align-items: center; justify-content: center;
    padding: 4rem 2rem; max-width: 40rem; margin: 0 auto; color: #9aa; line-height: 1.7; text-align: center;
  }

  /* ── Sticky block, structured exactly like kensa ── */
  .sticky-block { position: relative; }
  .sticky-visual {
    position: sticky; top: 0; height: 100vh; overflow: hidden;
    /* transparent: the page-wide blue field shows the fixed river canvas behind */
  }
  /* The bespoke canvas: ONE fixed full-viewport stream, behind all the text */
  #canvas { position: fixed; inset: 0; z-index: -1; pointer-events: none; }

  .sticky-steps {
    position: relative; z-index: 2; margin-top: -100vh;
    /* extra bottom room so the block stays pinned through the convergence hold */
    /* bottom room sized so the TITLE starts rising while the riding final
       pinned card is in its last stretch (~300px overlap before it exits) */
    padding: 105vh 2.5rem 114vh; display: flex; flex-direction: column; gap: 62vh;
  }
  .step {
    max-width: 22rem; font-size: var(--body-size); line-height: 1.55; color: #ffffff;
    padding: 0.5rem 0.75rem; font-family: var(--serif);
  }
  .step.pos-left { align-self: flex-start; }
  .step.pos-right { align-self: flex-end; }
  .step.pos-center { align-self: center; text-align: center; }

  /* ── POC control panel (not part of the shipped effect) ── */
  .panel {
    position: fixed; top: 14px; left: 14px; z-index: 50;
    width: var(--panel-w); max-height: calc(100vh - 28px); overflow-y: auto;
    background: rgba(10,14,18,0.85); border: 1px solid #223; border-radius: 10px;
    padding: 16px 14px; font: 12px ui-monospace, Menlo, monospace; color: #aeb6bd;
    display: flex; flex-direction: column; gap: 9px;
    backdrop-filter: blur(6px);
    box-shadow: 0 10px 34px rgba(0,0,0,0.4);
  }
  .panel b { color: #7fe9ff; font-weight: 600; }
  .panel-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
  .panel-body { display: flex; flex-direction: column; gap: 9px; }
  .collapse-btn {
    background: none; border: none; color: #7fe9ff; cursor: pointer;
    font: inherit; font-size: 15px; line-height: 1; padding: 0 2px; opacity: 0.7;
  }
  .collapse-btn:hover { opacity: 1; }
  .panel.collapsed { width: auto; gap: 0; }
  .panel.collapsed .panel-body { display: none; }
  .panel label { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
  .panel input[type=range] { width: 96px; accent-color: #7fe9ff; }
  .panel .fps { font-size: 15px; color: #e8e8e8; }
  .panel button {
    background: #14202a; color: #cfe; border: 1px solid #2a3a44; border-radius: 5px;
    padding: 4px 6px; font: inherit; cursor: pointer;
  }

  /* dual-thumb (base + variation) sliders */
  .dual { display: flex; flex-direction: column; gap: 2px; }
  .dual > .cap { display: flex; justify-content: space-between; align-items: baseline; }
  .dual > .cap em { color: #e8e8e8; font-style: normal; }
  .dual-track { position: relative; height: 16px; }
  .dual-fill { position: absolute; top: 6px; height: 4px; background: #7fe9ff; border-radius: 4px; pointer-events: none; }
  .dual-track input[type=range] {
    position: absolute; left: 0; top: 0; width: 100%; height: 16px; margin: 0;
    -webkit-appearance: none; appearance: none; background: none; pointer-events: none;
  }
  .dual-track input[type=range]::-webkit-slider-runnable-track {
    height: 4px; margin-top: 6px; background: #223; border-radius: 4px;
  }
  .dual-track input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none; pointer-events: auto;
    width: 12px; height: 12px; margin-top: -4px; border-radius: 50%;
    background: #cfefff; border: 2px solid #0c1116; cursor: pointer;
  }

  .hint { position: fixed; bottom: 14px; left: 0; right: 0; text-align: center; color: #4a5560; font: 12px ui-monospace, monospace; z-index: 50; }

  /* intro scroll affordance: sits in the negative space beneath the glyph
     (positioned by JS from the glyph geometry); hidden until the intro
     stream's head has traced down to the bottom of the bowl */
  .intro { position: relative; z-index: 2; }
  .intro .scroll-cue {
    position: absolute; transform: translateX(-50%);
    font: 0.8rem ui-monospace, Menlo, monospace; letter-spacing: 0.12em; text-transform: uppercase;
    color: rgba(255,255,255,0.5);
    opacity: 0; transition: opacity 0.9s ease;
  }
  .intro .scroll-cue.on { opacity: 1; animation: cue-bounce 2.2s ease-in-out infinite; }
  /* movable in edit mode: the label is its own handle, and the bounce has to stop
     while it is being dragged or the animation fights the drag's transform */
  body.show-jigs .intro .scroll-cue { cursor: grab; }
  .intro .scroll-cue.dragging { animation: none !important; cursor: grabbing; }
  @keyframes cue-bounce {
    0%, 100% { transform: translateX(-50%) translateY(0); }
    50% { transform: translateX(-50%) translateY(6px); }
  }

  /* pinned run: anchors hold scroll space invisibly; cards rise from the
     bottom and clamp at centre (top + opacity driven by scroll in JS) */
  .step.step-blank, .step.step-pinned-anchor { visibility: hidden; height: 1.6em; height: 1lh; padding: 0; margin: 0; }
  .sticky-visual .pinned-step {
    position: absolute; z-index: 4; top: 50%; left: 50%; transform: translate(-50%, -50%);
    max-width: 24rem; margin: 0; padding: 0.5rem 0.75rem; font-family: var(--serif);
    color: #fff; font-size: var(--body-size); line-height: 1.55; text-align: center;
    opacity: 0; pointer-events: none;
  }

  /* prose after the sticky block (real article body through §2) — two columns
     the river threads between: §1a left-aligned with the title, the stream on
     its right; then the stream cuts across and §1b reads to its right */
  .prose { max-width: 40rem; margin: 0 auto; padding: 12vh 2rem 34vh; color: #fff; font-size: var(--body-size); line-height: 1.7; font-family: var(--serif); }
  .prose-1 { margin: 0 0 0 calc(8vw - 2rem); padding: 10vh 2rem 14vh; }
  .prose-2 { margin: 0 calc(8vw - 2rem) 0 auto; padding: 24vh 2rem 34vh; }
  .prose h1 { font-size: 2.5rem; line-height: 1.15; margin: 0 0 0.6rem; font-weight: 700; }
  .prose h1 .tk { color: #b9b4ff; }
  .prose .byline { color: #c9c6ff; font-style: italic; margin: 0 0 3rem; }
  .title-screen { position: relative; min-height: 110vh; }
  /* dek sits just under the title, above the byline; byline anchors the bottom-right */
  .title-dek {
    position: absolute; right: 5vw; bottom: calc(14vh + 5.2rem);
    width: min(22rem, 34vw); text-align: left; z-index: 2;
  }
  .title-byline {
    position: absolute; right: 5vw; bottom: 14vh;
    width: min(22rem, 34vw); text-align: left; z-index: 2;
  }
  .title-dek .dek {
    font-family: var(--serif); font-size: 1.15rem; line-height: 1.5;
    color: #fff; margin: 0; width: 100% !important; max-width: 100%;
  }
  .title-byline .by {
    font-family: var(--serif); font-size: 0.9rem; line-height: 1.5;
    color: #c9c6ff; margin: 0 0 0.2rem;
  }
  /* narrow screens: dek/byline can't fit in the corners — stack both BELOW the title */
  @media (max-width: 560px) {
    .title-screen { min-height: calc(55vh + 44vw + 22rem); }
    .title-dek {
      top: calc(55vh + 44vw); bottom: auto; right: auto; left: 8vw;
      transform: none; width: min(26rem, 84vw); text-align: left;
    }
    .title-byline {
      bottom: auto; top: calc(55vh + 44vw + 9rem); right: auto; left: 8vw;
      width: min(26rem, 84vw); text-align: left;
    }
  }
  .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .prose h2 { font-size: 1.7rem; margin: 3rem 0 1.1rem; font-weight: 700; }
  .prose p { margin: 0 0 1.3rem; }
  .prose p.tk { color: #b9b4ff; }
  /* section drop cap: the .dc-slot floats to reserve space for the big Six Caps
     initial the canvas draws in particles; the letter itself lives on the canvas
     (the slot text is the CSS fallback for a no-engine render) */
  .prose p.dropcap { min-height: 6rem; }
  .prose p.dropcap .dc-slot { float: left; height: 5.4rem; margin: 0.5rem 1rem 0 0;
    font-family: "Six Caps", "Oxanium", sans-serif; font-weight: 400; line-height: 1;
    font-size: var(--cap-h, 86px); color: #fff; overflow: visible; }
  /* raised initial: baseline on the first line, glyph rises above the block —
     only line 1 wraps (slot is one line tall) and margin-top clears the ascent */
  .prose p.dc-raised { min-height: 0; margin-top: 4.4rem; }
  .prose p.dc-raised .dc-slot { height: 1.95rem; margin: 0 1rem 0 0; }
  /* per-cap tuning jig: sits in the raised cap's overhang gap, right of the glyph */
  .prose p.dropcap .dc-jig {
    position: absolute; top: -3.4rem; left: 3.4rem; z-index: 20;
    display: none; gap: 9px; align-items: flex-end;
    background: rgba(8,10,22,0.72); border: 1px solid rgba(255,255,255,0.16);
    border-radius: 6px; padding: 5px 9px; backdrop-filter: blur(3px);
    font: 9px ui-monospace, "SF Mono", monospace; color: #8fe;
    letter-spacing: 0.03em; user-select: none;
  }
  /* all tuning jigs hide by default; the `p` key reveals them with the panel */
  body.show-jigs .prose p.dropcap .dc-jig { display: flex; }
  .prose p.dropcap .dc-jig label { display: flex; flex-direction: column; align-items: center; gap: 2px; text-transform: uppercase; opacity: 0.85; }
  .prose p.dropcap .dc-jig input[type=range] { width: 72px; height: 12px; accent-color: #4fd6b0; cursor: ew-resize; }
  .prose p.dropcap .dc-jig b { font-weight: 600; color: #cfe; min-width: 2ch; text-align: center; }
  /* explainer tuning jig: lives INSIDE the explainer's sticky visual, so it
     rides the lower-left corner only while the explainer scene is on screen
     and scrolls away with it — instead of sticking to the viewport all article
     long. pointer-events:auto restores interactivity (the sticky host is
     pointer-events:none). revealed by `p` */
  /* two columns: the row count has roughly doubled as controls were added, and one
     column ran off the top of a short window. The title spans both. */
  .exp-jig {
    position: absolute; left: 16px; bottom: 16px; z-index: 30; pointer-events: auto;
    display: none; grid-template-columns: 1fr 1fr; column-gap: 14px; row-gap: 7px;
    align-content: end; width: 372px;
    background: rgba(8,10,22,0.82); border: 1px solid rgba(255,255,255,0.16);
    border-radius: 8px; padding: 11px 13px; backdrop-filter: blur(4px);
    font: 10px ui-monospace, "SF Mono", monospace; color: #8fe;
    letter-spacing: 0.03em; user-select: none;
  }
  body.show-jigs .exp-jig { display: grid; }
  /* the two panels are mutually exclusive: the full one is unusable at phone width and
     the mobile one is meaningless above it, since its values are never read there */
  .exp-jig-m { width: auto; min-width: 190px; max-width: 62vw; grid-template-columns: 1fr; }
  .exp-jig-m button { margin-top: 4px; background: rgba(80,220,180,0.14); color: #8fe;
    border: 1px solid rgba(255,255,255,0.2); border-radius: 5px; padding: 4px; font: inherit; cursor: pointer; }
  body.show-jigs .exp-jig-m { display: none; }
  @media (max-width: 760px) {
    body.show-jigs .exp-jig { display: none; }
    body.show-jigs .exp-jig-m { display: grid; }
  }
  .exp-jig .exp-jig-ttl { grid-column: 1 / -1; text-transform: uppercase; letter-spacing: 0.12em; opacity: 0.6; font-size: 9px; margin-bottom: 1px; }
  .exp-jig label { display: flex; flex-direction: column; gap: 4px; }
  .exp-jig .exp-jig-lbl { display: flex; justify-content: space-between; text-transform: uppercase; opacity: 0.85; }
  .exp-jig input[type=range] { width: 100%; height: 12px; accent-color: #4fd6b0; cursor: ew-resize; margin: 0; }
  .exp-jig b { font-weight: 600; color: #cfe; }
  /* preserved opening quote on a quote-leading cap: floats just left of the
     slot so the paragraph still opens with its quotation mark */
  .prose p.dropcap .dc-quote {
    float: left; font-family: var(--serif); color: #fff;
    font-size: 1.6rem; line-height: 1; margin: 0.1rem 0.15rem 0 0;
  }
  .prose hr { border: none; height: 3.2em; height: 2lh; margin: 0; }
  .prose figure { margin: 2.5rem 0; }
  .prose figure.duo { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
  .prose figure img { width: 100%; height: auto; border-radius: 4px; display: block; background: rgba(255,255,255,0.06); min-height: 8rem; }
  /* the ford photo is drawn INTO the canvas (base under the stream, subject
     cutout over it) once its layers load — the DOM img then only holds layout */
  /* A LIVE FORD IS TWO STACKED DOM IMAGES WITH THE CANVAS BETWEEN THEM. The photograph
     drops BELOW the particle canvas (which sits at -1) and its subject cutout is layered
     back above it, so the water passes behind the person and in front of the room without
     either layer being painted into the canvas — they scroll on the compositor instead,
     in exact register with each other and with the caption. Nothing in the ancestry
     creates a stacking context, so the negative index resolves against the root. */
  img.ford-img.ford-live { position: relative; z-index: -2; }
  .kensa-ford-cut {
    position: absolute; z-index: 1; pointer-events: none;
    border-radius: 3px; display: block;
  }
  .prose figcaption { grid-column: 1 / -1; color: #c9c6ff; font-size: 0.85rem; margin-top: 0.5rem; text-align: center; }

  /* Progress rail above each explainer step: a thin track that fills as the reader
   scrolls from this step toward the next. Scaled on the X axis rather than
   animating width, so it is a compositor transform and costs nothing per frame.
   transform-origin is the LEFT edge or it would grow from the middle. */
.explainer .exp-card .exp-rail {
  display: block;
  height: 1px;
  margin: 0 0 1.1rem;
  /* no visible track: only the filled part shows, so the rail reads as a
     growing measure rather than a container being filled in */
  background: none;
  overflow: hidden;
}
.explainer .exp-card .exp-rail > i {
  display: block;
  height: 100%;
  background: #f2e7d5;
  transform: scaleX(0);
  transform-origin: left center;
}

/* ── isometric explainer stage: a full-viewport pinned scene. The step
     cards overlay the TOP RIGHT (the projected river flows toward the
     bottom-right, so top-right is the negative space). Hidden anchors in
     .exp-steps hold the scroll length; cards crossfade as each anchor
     crosses viewport CENTER (same activation rule as the pinned run). ── */
  .explainer { position: relative; }
  .exp-visual { position: sticky; top: 0; height: 100vh; z-index: 2; pointer-events: none; }
  .exp-card {
    position: absolute; top: 9vh; right: 3.5rem; max-width: 21rem;
    font-family: var(--serif); font-size: 1.08rem; line-height: 1.55; color: #fff;
    opacity: 0; transition: opacity 0.45s ease;
  }
  .exp-steps {
    position: relative; margin-top: -100vh;
    /* bottom room sized so the LAST anchor crosses viewport center well
       before the exit camera ramp starts (finale needs dwell at camT=1) */
    padding: 110vh 0 195vh; display: flex; flex-direction: column; gap: 85vh;
  }
  .exp-anchor {
    visibility: hidden; height: 1.6em; height: 1lh;
  }

/* ── overrides: exported scaffolding → river-owned look ── */
/* the river replaces the opening block's photo frames */
.sticky-block .kensa-frame { display: none !important; }
/* exported step cards flatten to the river's borderless text blocks. font-size
   uses the shared --body-size (with !important to beat Tailwind's text-base) so
   the prologue steps, the pinned promise cards, and the prose body read at one
   size — change --body-size and all three move together. */
.stage .sticky-steps .step {
  background: none !important;
  border: 0 !important;
  border-radius: 0 !important;
  padding: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  font-size: var(--body-size) !important;
}
.stage .sticky-visual .pinned-step {
  background: none !important;
  border: 0 !important;
  border-radius: 0 !important;
  padding: 0 !important;
  font-size: var(--body-size) !important;
}
/* ── 12-COLUMN GRID MODEL ──────────────────────────────────────────────────
   Each prose section is a full-width flow container; its BLOCKS — a .prose-run
   (a run of text between figures) and each <figure> — are positioned on a
   viewport-proportional 12-col grid via --col-start (1..12) + --col-span, so the
   in-page editor (jig mode) can move/resize them INDEPENDENTLY. gridMetrics() in
   the engine mirrors this vw math. */
:root {
  /* the grid stops growing past --grid-max and centres; below it the geometry is
     identical to the old pure-percentage grid (6% margins, 1.2% gutters). Any
     change here must be mirrored in gridMetrics() in the engine or drag-snapping
     drifts from the rendered columns. */
  --grid-max: 1200px;
  --grid-w: min(100vw, var(--grid-max));
  --grid-inset: calc((100vw - var(--grid-w)) / 2);
  --grid-margin: calc(var(--grid-inset) + 0.06 * var(--grid-w));
  --grid-gap: calc(0.012 * var(--grid-w));
  --grid-colw: calc((var(--grid-w) - 0.12 * var(--grid-w) - 11 * var(--grid-gap)) / 12);
  --grid-step: calc(var(--grid-colw) + var(--grid-gap));
}
.stage { overflow-x: clip !important; }
.stage .prose {
  margin: 0 !important; width: 100% !important; max-width: none !important;
  padding: 10vh 0 !important; font-size: var(--body-size) !important; line-height: 1.7 !important;
}
.stage .prose-1 { padding-top: 5vh !important; }
/* prose-4 reads as a continuation of the drop-cap section above it, not a new
   opening — no top padding, so the two run together */
.stage .prose-4 { padding-top: 0 !important; }
/* room between the end of the piece and the site footer — halved from 20vh, which with the
   closing CTA's own margin left 213px of empty blue under the box */
.stage .prose-finale { padding-bottom: 10vh !important; }
.stage .kensa-cta[data-kensa-id="cta-end"] { margin-bottom: 2.5vh !important; }
/* movable blocks: text runs, figures, the CTA boxes and the closing rule — all positioned
   on the grid, so each can be placed and resized independently of the others */
.stage .prose-run, .stage .prose figure, .stage .kensa-cta {
  margin-left: calc(var(--grid-margin) + var(--col-start, 1) * var(--grid-step) - var(--grid-step)) !important;
  margin-right: 0 !important;
  width: calc(var(--col-span, 7) * var(--grid-colw) + (var(--col-span, 7) - 1) * var(--grid-gap)) !important;
  max-width: none !important;
}
.stage .prose-run { margin-top: 3vh !important; margin-bottom: 3vh !important; }
.stage .kensa-cta { margin-top: 5vh !important; margin-bottom: 5vh !important; }
/* one extra LINE of air between the credits and the CTA that follows them — the box is a
   different kind of thing from the piece's own sign-off, so it wants a beat of its own.
   The credits block is tagged with a class when the rule moves into it, rather than found
   with a CSS :has() test — Firefox only got :has() in 121. */
.stage .prose-run.kensa-credits + .kensa-cta {
  margin-top: calc(5vh + 1.7em) !important;
}
/* a CTA lifted out of the prose into a band of its own between two sections */
.stage .kensa-ctablock { margin: 0 !important; width: 100% !important; max-width: none !important; padding: 0 !important; }
.stage .prose figure { margin-top: 6vh !important; margin-bottom: 6vh !important; }
/* column defaults come ONLY from the flank class — a base .stage .prose figure
   default would out-specify these (type selector) and pin every figure to col 1 */
.stage .kensa-flank-left { --col-start: 1; --col-span: 7; }
.stage .kensa-flank-right { --col-start: 6; --col-span: 7; }
.stage .kensa-flank-center { --col-start: 3; --col-span: 8; }
/* absolutely-positioned blocks that still snap to the grid horizontally (the
   masthead caption unit, the pinned overlay cards): translate col-start/col-span
   into left/width while their own top/bottom anchoring is left alone. */
.stage .kensa-gridabs { --col-start: 8; --col-span: 5; }
/* the masthead caption unit: dek over byline, bottom-anchored, moves as one */
.stage .title-meta {
  position: absolute; bottom: 14vh; z-index: 2; text-align: left;
}
.stage .title-meta .title-dek, .stage .title-meta .title-byline {
  position: static !important; right: auto !important; bottom: auto !important;
  width: 100% !important; max-width: 100%; text-align: left;
}
.stage .title-meta .title-dek { margin-bottom: 1.1rem; }
/* a plain full-width rail over the masthead caption: same 1px weight, colour and
   1.1rem gap as the explainer step rails, but static — nothing to fill here */
.stage .title-meta::before {
  content: ''; display: block; height: 1px;
  background: #f2e7d5; margin: 0 0 1.1rem;
}
/* ── NARROW SCREENS: no columns ──────────────────────────────────────────────
   The 12-column grid is a wide-screen device. On a phone a five-column step card
   is a sliver, so every editable block — step text, cards, figures — spans the
   full grid width, matching the prose. The custom properties are overridden with
   !important because applyLayout writes them as inline styles from the saved
   layout, and a normal declaration would lose to that. */
@media (max-width: 760px) {
  .stage .kensa-editable, .stage .kensa-gridabs,
  .stage .explainer .exp-card, .stage .title-meta {
    --col-start: 1 !important;
    --col-span: 12 !important;
  }
  /* and the cards' own !important left/width, which are calc()s of those vars */
  .stage .explainer .exp-card.kensa-gridabs,
  .stage .sticky-visual .pinned-step.kensa-gridabs {
    left: var(--grid-margin) !important;
    right: auto !important;
    width: calc(100vw - 2 * var(--grid-margin)) !important;
    max-width: none !important;
  }
  .stage .step, .stage .exp-card, .stage .pinned-step { max-width: none !important; }
  /* the explainer's step text sits at the BOTTOM of the viewport on a small screen: the
     scene needs the rest of the height, and the wide view's 9vh plus whatever vertical
     nudge that card was given puts it over the channel. Out-specifies the .kensa-gridabs
     rule later in this file, which owns the card's geometry and its transform.
     The bottom offset clears the canvas credit in that corner (BN_CREDIT.pad). */
  body .stage .explainer .exp-card, body .stage .explainer .exp-card.kensa-gridabs {
    top: auto !important;
    bottom: 2.6rem !important;
    /* one line of breathing room under the last line of text. `em` here resolves against
       the card's own font-size and 1.55 is its line-height ratio, so this is exactly one
       line whatever the size becomes. !important because the wide rule zeroes padding. */
    padding-bottom: 1.55em !important;
    /* !important because applyLayout writes --row-dy as an INLINE style from the saved
       layout, and an inline custom property beats any normal declaration */
    --row-dy: 0px !important;
  }
  /* ...and the small framing panel moves out of the way, to the top */
  .exp-jig-m { top: 16px; bottom: auto; }
  /* the prologue's narrative steps read as centred captions at phone width, on the
     same measure as the prose so the two blocks line up down the page */
  /* NOTE the .kensa-editable on the selector: the grid rule that places these blocks
     is `.stage .sticky-steps > .step.kensa-editable` with !important and sits LATER in
     this file, so a plain `.step` selector here silently loses to it. */
  body .stage .sticky-steps > .step, body .stage .sticky-steps > .step.kensa-editable {
    text-align: center !important;
    margin-left: 14vw !important; margin-right: 14vw !important;
    width: auto !important; max-width: none !important;
  }
}

/* the title words' in-place tracking control: sits just under each word's frame,
   pointer-active only in edit mode like the drop-cap jigs */
.stage .title-jig {
  position: absolute; left: 0; top: 100%; margin-top: 6px; z-index: 40;
  pointer-events: auto; display: none;
}
body.show-jigs .stage .title-jig { display: block; }

/* pinned overlay cards: gridabs supplies left/width, so drop the horizontal
   half-shift from the centring transform (vertical centring stays) */
.stage .sticky-visual .pinned-step.kensa-gridabs { transform: translateY(calc(-50% + var(--row-dy, 0px))) !important; }
.stage .kensa-gridabs {
  left: calc(var(--grid-margin) + var(--col-start, 8) * var(--grid-step) - var(--grid-step)) !important;
  right: auto !important;
  width: calc(var(--col-span, 5) * var(--grid-colw) + (var(--col-span, 5) - 1) * var(--grid-gap)) !important;
  max-width: none !important;
  /* these blocks are absolutely positioned, so the layout editor can move them
     vertically too: --row-dy is the drag offset from their anchored position.
     Higher-specificity transforms (pinned cards, exp cards) fold it in
     themselves. */
  transform: translateY(var(--row-dy, 0px));
}
/* prologue narrative steps are grid-editable: make the flex track full-width and
   left-align each step so the grid margin/width places it on the columns; the
   62vh vertical scroll spacing is untouched. */
.stage .sticky-steps { max-width: none !important; padding-left: 0 !important; padding-right: 0 !important; }
.stage .sticky-steps > .step.kensa-editable {
  align-self: flex-start !important; max-width: none !important;
  margin-left: calc(var(--grid-margin) + var(--col-start, 3) * var(--grid-step) - var(--grid-step)) !important;
  width: calc(var(--col-span, 8) * var(--grid-colw) + (var(--col-span, 8) - 1) * var(--grid-gap)) !important;
}
/* the right-flank prologue step reads ragged-left, mirroring its position — only on
   wide screens. On a phone every step is full-measure and centred, so this rule (which
   sits after the mobile block and matches at the same specificity) has to be scoped out
   of it or that one step alone reads ragged-left. */
@media (min-width: 761px) {
  .stage .sticky-steps > .step.pos-right,
  body .stage .sticky-steps > .step.pos-right.kensa-editable { text-align: right !important; }
}
/* readable measure lives on the text (hugging the flank side) */
.stage .prose-run p, .stage .prose-run h1, .stage .prose-run h2, .stage .prose-run h3 {
  width: auto !important; max-width: 40rem !important; margin-left: 0 !important; margin-right: 0 !important;
}
.stage .prose-run p { margin-bottom: 1.3rem !important; font-size: var(--body-size) !important; line-height: 1.7 !important; }
.stage .prose-run h2 { font-size: 1.7rem !important; margin: 3rem 0 1.1rem !important; }
.stage .kensa-flank-right p, .stage .kensa-flank-right h1, .stage .kensa-flank-right h2, .stage .kensa-flank-right h3 { margin-left: auto !important; }
.stage .kensa-flank-center p, .stage .kensa-flank-center h1, .stage .kensa-flank-center h2, .stage .kensa-flank-center h3 { margin-left: auto !important; margin-right: auto !important; }
/* photos: natural aspect, never cropped, NO height cap — fill the grid cell and
   let the height follow the image's own proportions */
.stage .prose figure img { width: 100% !important; height: auto !important; display: block !important; border-radius: 3px; }
/* captions: Tailwind's text-xs pins line-height to 16px on a 13.6px face — 1.18,
   too tight for two-line captions. Set here, in the override block, because the
   utility class wins against the base rule. */
.stage .prose figcaption { line-height: 1.45 !important; }
/* links in the body read as links: underlined, on the text's own colour. The exported
   Tailwind prose leaves them undecorated, which on a field this dark is indistinguishable
   from the surrounding sentence. Offset so descenders stay legible. */
.stage .prose-run a {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.16em;
  text-decoration-color: rgba(255,255,255,0.55);
}
.stage .prose-run a:hover { text-decoration-color: #fff; }

/* hyphenation: the columns are narrow enough that ragged-right gets gappy. Limits
   keep it conservative — long words only, never a stack of hyphens down the edge.
   Needs lang="en" on <html>, which the exporter emits. */
.stage .prose-run p {
  -webkit-hyphens: auto; hyphens: auto;
  -webkit-hyphenate-limit-before: 4; -webkit-hyphenate-limit-after: 3;
  -webkit-hyphenate-limit-lines: 2;
  hyphenate-limit-chars: 8 4 3;
  hyphenate-limit-lines: 2;
}
/* captions always set flush left, whichever column the figure sits in */
.stage .prose figcaption,
.stage .kensa-flank-right figcaption,
.stage .kensa-flank-center figcaption { text-align: left !important; }
/* mobile: collapse to a single readable column, no grid, no editor */
@media (max-width: 760px) {
  /* the text leaves a LANE at each edge for the stream to run down: at phone width
     the stream has no inner flank to follow, so it hugs a screen edge alongside each
     block instead, and a 5vw measure would put the water over the first letters.
     Photographs stay wide — the stream crossing an image is part of the language. */
  .stage .prose-run { margin-left: 14vw !important; margin-right: 14vw !important; width: auto !important; }
  .stage .prose figure { margin-left: 5vw !important; margin-right: 5vw !important; width: auto !important; }
  .stage .prose-run p, .stage .prose-run h1, .stage .prose-run h2, .stage .prose-run h3 { margin-left: 0 !important; margin-right: 0 !important; max-width: none !important; }
  .stage .prose figcaption { text-align: left !important; }
  #grid-overlay, .kensa-ed-frame, .kensa-ed-move, .kensa-ed-grip { display: none !important; }
}
/* ── layout-editor chrome (jig mode only; reads the same --grid-* vars) ── */
#grid-overlay, .kensa-ed-frame, .kensa-ed-move, .kensa-ed-grip { display: none; }
body.show-jigs #grid-overlay {
  display: block; position: fixed; top: 0; bottom: 0;
  left: var(--grid-margin); right: var(--grid-margin); z-index: 6; pointer-events: none;
  /* fill each of the 12 columns as a band (colw wide) with the gutter left
     clear — edge-lines every step read as "first column narrow / last wide"
     because only left edges are marked; bands show all 12 columns uniformly */
  background: repeating-linear-gradient(to right,
    rgba(150,175,255,0.11) 0, rgba(150,175,255,0.11) var(--grid-colw),
    transparent var(--grid-colw), transparent var(--grid-step));
}
body.show-jigs .kensa-editable:not(.kensa-titlemove):not(.kensa-gridabs) { position: relative; }
body.show-jigs .kensa-editable > .kensa-ed-frame {
  display: block; position: absolute; inset: -2px; z-index: 7; pointer-events: none;
  outline: 1px dashed rgba(150,175,255,0.55);
}
body.show-jigs .kensa-editable.kensa-warn > .kensa-ed-frame { outline-color: rgba(255,150,90,0.95); }
body.show-jigs .kensa-editable > .kensa-ed-move {
  display: block; position: absolute; top: 6px; left: 6px; z-index: 12; pointer-events: auto; touch-action: none;
  cursor: grab; user-select: none; padding: 3px 8px; border-radius: 5px;
  background: rgba(18,22,46,0.85); border: 1px solid rgba(150,175,255,0.7);
  color: #cfe; font: 10px ui-monospace, Menlo, monospace; letter-spacing: 0.03em; white-space: nowrap;
}
body.show-jigs .kensa-editable > .kensa-ed-move.vert { cursor: move; }   /* also draggable up/down */
/* free-floating blocks grab anywhere on the block, not just on the chip — the
   chip is a small label and a drag on the block body looked like a dead zone */
body.show-jigs .kensa-editable.kensa-gridabs { cursor: move; user-select: none; touch-action: none; }
body.show-jigs .kensa-editable > .kensa-ed-move.dragging { cursor: grabbing; background: rgba(60,80,160,0.92); }
body.show-jigs .kensa-editable > .kensa-ed-grip {
  display: block; position: absolute; top: 0; bottom: 0; width: 14px; z-index: 11; pointer-events: auto; touch-action: none;
  cursor: ew-resize; opacity: 0.35; background: rgba(150,175,255,0.5);
}
body.show-jigs .kensa-editable > .kensa-ed-grip:hover,
body.show-jigs .kensa-editable > .kensa-ed-grip.dragging { opacity: 0.9; }
body.show-jigs .kensa-editable > .kensa-ed-grip.left { left: -7px; }
body.show-jigs .kensa-editable > .kensa-ed-grip.right { right: -7px; }
/* raised section drop caps: the .stage .prose p reset above clobbers the
   cap margin-top and the floated slot — restore them so the glyph (drawn in
   particles ABOVE the slot) keeps its top clearance and the first line wraps
   around the reserved slot. Not scoped to .prose: the finale drop cap sits in
   the plain prose block after the explainer, which the companion never wraps
   as .prose. */
.stage p.dropcap { min-height: 0 !important; }
.stage p.dc-raised { margin-top: 4.4rem !important; }
.stage p.dropcap .dc-slot { float: left !important; position: relative; }
/* the drop cap's outline: real text, absolutely positioned inside the float that reserves
   its space, so it is composited with the paragraph and cannot drift against it. Size, face
   and offsets are set by buildDropCaps; the stroke is here. Transparent fill + a stroke is
   the whole effect — CAP_LINE / CAP_LINE_W in the companion mirror these two values. */
.stage p.dropcap .dc-slot .dc-ink {
  position: absolute; white-space: pre; pointer-events: none;
  line-height: 0; color: transparent;
  -webkit-text-stroke: 1px rgba(255,255,255,0.5);
  text-stroke: 1px rgba(255,255,255,0.5);
}
.stage p.dc-raised .dc-slot { height: 1.95rem !important; margin: 0 1rem 0 0 !important; }
.stage p.dropcap .dc-quote { float: left !important; margin: 0.1rem 0.15rem 0 0 !important; }
/* the tail of a word the drop cap started: a hair of extra space after it, so the fragment
   doesn't group with the word that follows ("I" + "f all this" reading as "I fall") */
.stage p.dropcap .dc-cont { margin-right: 0.14em; }
/* the title screen's own layout comes from the companion */
.stage .title-screen { max-width: none !important; padding: 0 !important; }

/* ── isometric explainer: the river engine owns this block. Placeholder
   frames hide (the watershed is the visual); the exported pinned cards
   become the top-right step cards; steps collapse to hidden anchors laid
   out on the prototype's rhythm. ── */
.explainer .kensa-frame { display: none !important; }
.explainer .exp-card {
  background: none !important; border: 0 !important; border-radius: 0 !important;
  padding: 0 !important; left: auto !important;
  transform: translateY(var(--exit-dy, 0px)) !important;
  top: 9vh !important; right: 3.5rem !important; max-width: 21rem !important;
  font-size: 1.08rem !important; line-height: 1.55 !important;
  transition: opacity 0.45s ease !important;
}
/* ...but when the card is a grid block, the grid owns its horizontal geometry
   (this rule must out-specify the left/right/max-width !importants above) */
.stage .explainer .exp-card.kensa-gridabs {
  left: calc(var(--grid-margin) + var(--col-start, 8) * var(--grid-step) - var(--grid-step)) !important;
  right: auto !important;
  width: calc(var(--col-span, 5) * var(--grid-colw) + (var(--col-span, 5) - 1) * var(--grid-gap)) !important;
  max-width: none !important;
  /* the editor's vertical nudge and the end-of-run hold compose. Neither can be an
     inline transform: the rules above are !important, so a JS-set transform loses. */
  transform: translateY(calc(var(--row-dy, 0px) + var(--exit-dy, 0px))) !important;
}
.explainer .exp-steps {
  padding: 110vh 0 195vh !important; margin: -100vh 0 0 !important;
  display: flex !important; flex-direction: column !important; gap: 85vh !important;
  max-width: none !important;
}
.explainer .exp-anchor {
  visibility: hidden; height: 1.6em; height: 1lh;
  margin: 0 !important; padding: 0 !important;
}

/* the exported page paints body dark; the river needs it transparent so the
   z-index:-1 canvas (and the blue html field) shows through */
body { background: transparent !important; }

/* ── site chrome: the real every.to header and footer components, moved into
   .stage (the scroller) by the engine. The header stays transparent over the
   article's blue field rather than the live site's black. */
/* IN FLOW, not absolute. The component positions itself absolutely because on a
   normal page it sits over a tall hero; here .stage IS the scroller, and an
   absolutely positioned child of a scroll container is laid out against its
   padding box, which does not scroll — so the bar hung over the title screen like
   a fixed element. Static keeps it at the top of the scroll, where it belongs. */
#kensa-site-header { position: relative; z-index: 40; }
#kensa-site-header > header {
  position: static;
  font-family: ui-sans-serif, system-ui, sans-serif;
  color: #fff;
}

/* The real site footer, moved into the scroller by the engine. It brings its own
   Tailwind styling; these two rules are the seam with the article: it sits above
   the fixed canvas, and the article's zero-specificity reset would otherwise
   flatten the type inside it. */
#kensa-site-footer { position: relative; z-index: 40; }
#kensa-site-footer #site-footer { font-family: ui-sans-serif, system-ui, sans-serif; }

/* ?stats readout — frame rate and dot density, legible on a phone. Named to avoid
   the panel/jig/tune words the publish transform hides. */
.river-stats {
  position: fixed; left: 8px; bottom: 8px; z-index: 100000;
  background: rgba(0,0,0,0.72); color: #8fe; padding: 6px 9px; border-radius: 6px;
  font: 12px ui-monospace, "SF Mono", monospace; letter-spacing: 0.02em;
  pointer-events: none; white-space: nowrap;
}

/* ── placeholder CTA boxes and the closing divider. The border is the drop caps' own
   hairline (1px rgba(255,255,255,0.5)) so they read as the same drawn language. Nothing is
   wired — the button and the field are inert, like the header and footer, and the every
   repo will supply the real thing. `?nocta` hides them. Sized on the grid like any other
   editable block, so the layout editor resizes them; the measure below is the fallback for
   before one has been placed. */
.stage .kensa-cta {
  border: 1px solid rgba(255,255,255,0.5);
  border-radius: 3px;
  padding: 1.6rem 1.5rem 1.7rem;
  /* vertical only: the GRID owns left/width, like any other editable block, so the box can
     be placed and resized independently of the prose columns it sits between */
  margin-top: 3.2rem; margin-bottom: 3.2rem;
  font-family: var(--serif);
  color: #fff;
}
.stage .kensa-cta .kensa-cta-hed {
  font-size: 1.5rem; font-weight: 400; line-height: 1.2; letter-spacing: -0.01em;
}
.stage .kensa-cta .kensa-cta-dek {
  font-size: 1rem; line-height: 1.45; margin-top: 0.5rem; color: rgba(255,255,255,0.78);
}
.stage .kensa-cta .kensa-cta-row { display: flex; gap: 0.6rem; margin-top: 1.2rem; }
.stage .kensa-cta .kensa-cta-email {
  flex: 1; min-width: 0;
  background: none; color: #fff; font: inherit; font-size: 0.95rem;
  border: 1px solid rgba(255,255,255,0.5); border-radius: 3px; padding: 0.7rem 0.8rem;
}
.stage .kensa-cta .kensa-cta-email::placeholder { color: rgba(255,255,255,0.45); }
.stage .kensa-cta .kensa-cta-btn {
  /* inline-block, not inline: the button is an <a> for the states that just link
     out, and as an inline box its padding overlapped the dek above it */
  display: inline-block; text-decoration: none;
  margin-top: 1.2rem; padding: 0.7rem 1.4rem;
  background: #fff; color: #2a1ec6; border: 0; border-radius: 3px;
  font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.9rem; font-weight: 500;
  cursor: pointer; white-space: nowrap;
}
.stage .kensa-cta .kensa-cta-row .kensa-cta-btn { margin-top: 0; }
/* the anon box captures an email in place with the site's real signup form, which
   brings its own styling — this just seats it in the box and drops the label's
   standalone top margin */
.stage .kensa-cta .kensa-cta-signup {
  margin-top: 1.2rem; font-family: ui-sans-serif, system-ui, sans-serif;
}
.stage .kensa-cta .kensa-cta-signup label { margin-top: 0 !important; }
/* Rendered outside the mount so the wrap into the scroller cannot sweep them up,
   the chrome and the CTAs sit here as siblings until the engine moves them into
   place. None of the article's styling reaches them there, so keep them out of
   sight rather than flashing an unstyled box at the top of the page. */
.post > #kensa-site-header,
.post > #kensa-site-footer,
.post > .kensa-cta { display: none; }
/* the divider that closes the piece, above the writer's byline */
/* the divider that closes the piece. The space beneath it is ONE LINE of the body text —
   `em` against the body size times its line-height ratio — so it reads as a single blank
   line before the credits rather than an open field. */
/* it sits INSIDE the credits block now, so the block owns its width and position */
.stage .kensa-endrule {
  border: 0; border-top: 1px solid rgba(255,255,255,0.5);
  width: 100%;
  /* HEIGHT ZERO. `.prose hr` further up gives every rule inside the article a height of
     2lh — that is the section-break rule's own spacing — and this one inherited it, so the
     gap under the line was its 62px body PLUS the margin, not the margin alone. With the
     height gone the margin is the whole space, and one line means one line. */
  height: 0;
  margin-top: 3.4rem; margin-bottom: 1.7em; font-size: var(--body-size);
}
@media (max-width: 760px) {
  /* NO margin of their own here: both are inserted INSIDE a .prose-run, which already
     carries the phone measure, so adding it again indented the box twice and left it 173px
     wide on a 393px screen. Fill the run instead. */
  /* THE BOXES SIT AT IMAGE WIDTH, not the prose measure: they are figures of a sort, and at
     the 14vw text measure they read as narrower than everything around them. Same 5vw as
     `.prose figure`. (They still must not add the margin twice — they are inside a run that
     already carries the text measure, so these override it rather than compound it.) */
  .stage .kensa-cta {
    margin-left: 5vw !important; margin-right: 5vw !important;
    width: auto !important; max-width: none !important;
    padding: 1.3rem 1.2rem 1.4rem;
  }
  /* room for the stream to cross BELOW the mid-article box: at the normal spacing the gap
     between the box and the section under it is ~44px, and a crossing spans the full measure,
     so the eased diagonal had nowhere to go but over the box's own heading and field. On the
     box itself, not on a following sibling — the section below starts a new block. */
  .stage .kensa-cta[data-kensa-id="cta-mid"] { margin-bottom: calc(5vh + 3.4em) !important; }
  .stage .kensa-cta .kensa-cta-row { flex-direction: column; }
  .stage .kensa-cta .kensa-cta-row .kensa-cta-btn { margin-top: 0.2rem; }
}


/* ── THEME PINNING ─────────────────────────────────────────────────────────────
   The piece was designed against Tailwind's STOCK scale, which it got from the
   CDN build. This site's config REPLACES that scale rather than extending it:
   text-base is 20px here and 16px there, text-sm 16 against 14, text-xs 14
   against 12, and there is no mono family at all. Dropped in unpinned, every
   caption and dek resizes — and because the stream's course, the drop-cap
   geometry and every saved block position were all measured against the
   current metrics, the whole layout reflows with them.
   These six rules make the article immune to the site's type scale, so the
   theme can change without anyone having to re-tune the water. The :where()
   wrapper is deliberate — see the note above each rule's specificity. */
:where(#kensa-root) .text-base { font-size: 1rem; }
:where(#kensa-root) .text-sm { font-size: 0.875rem; }
:where(#kensa-root) .text-xs { font-size: 0.75rem; }
:where(#kensa-root) .font-serif { font-family: var(--serif); }
:where(#kensa-root) .font-sans { font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
:where(#kensa-root) .font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; }

/* ── CASCADE ORDER ────────────────────────────────────────────────────────────
   One more consequence of the move, and the reason to measure rather than eyeball.
   On the prototype Tailwind was built at RUNTIME and appended after this sheet, so
   a utility BEAT any rule of ours at equal specificity. Here the site's stylesheet
   is loaded first and this one second, so the tie goes the other way — every
   single-class rule of ours that a utility used to override now wins instead.
   A full computed-style comparison of the two pages across eighteen element types
   and twenty-two properties turned up exactly one such conflict: the prologue
   steps, where `leading-[1.6]` used to beat the .step rule's 1.55 and stopped.
   That is the reading the piece was tuned at, so it is set here explicitly.
   Anything new that leans on a utility overriding this sheet needs the same
   treatment — the tie-break is no longer in the utility's favour. */
.stage .sticky-steps > .step { line-height: 1.6; }
