/*
 * The stage: layout for the pinned scenes, and the initial states that make a
 * reveal possible without hiding anything from a reader who has no JavaScript.
 *
 * The rule that governs the whole file: NOTHING is hidden except under
 * `html.scenes`. That class is added before first paint by
 * web/public/js/scene-ready.js, and only when the page is going to animate - so
 * with no JavaScript, or under prefers-reduced-motion, every selector below is
 * inert and the page is a long static document with each scene at its end state.
 *
 * Read web/public/js/scene-ready.js before changing anything here.
 */

/* ===================================================================== stage */

/* ScrollTrigger and `scroll-behavior: smooth` fight: a smooth programmatic
 * scroll animates the position out from under the trigger's own sync, and anchor
 * jumps land in the middle of a pin. Instant jumps are also simply more
 * predictable on a page where an anchor can be three viewports inside a scene. */
html.scenes { scroll-behavior: auto; }

/* ---- how far a block travels as it arrives -----------------------------
 * One number, here, because two places need it and they must agree: the CSS
 * rest states below, and mountReveals, which reads this property rather than
 * carrying a constant of its own.
 *
 * IT HAS TO BE SMALLER THAN THE GAPS IT MOVES ACROSS. A block that rises 20px
 * between neighbours 8px apart is drawn through the one above it for the whole
 * of its entrance - and the gaps on a phone are clamped small precisely because
 * the stage is short, so the fixed 20px that reads well on a desktop was
 * guaranteed to collide there. Reported as the cadence rows sitting on the
 * lede, and as the value diagram over its own headline. */
:root { --pickle-rise: 20px; }

@media (max-width: 1023px) {
  :root { --pickle-rise: 6px; }
}

/*
 * THE PIN IS OPT-IN, exactly like the hiding.
 *
 * This was the first version's bug and it is worth stating, because it looked
 * right: the pin was the default and a reduced-motion query unpinned it, scoped
 * to `html.scenes`. But the class is absent precisely when nothing will animate -
 * so with no JavaScript the query never matched, and the scene became three
 * viewports of track that refused to scroll while nothing moved. Measured, not
 * guessed: `position: sticky` and a 2700px track with the class off.
 *
 * One class now governs both halves of the register. No class means no hiding AND
 * no pinning: the scene is an ordinary section at its end state, which is the same
 * page a crawler sees and the same page a reduced-motion reader gets.
 */
.scene { position: relative; }

.scene-stage {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: var(--pickle-space-5);
  padding: var(--pickle-space-7) var(--pickle-gutter);
}

html.scenes .scene {
  /* The track. Its height IS the scene's duration - the one knob that decides
   * whether a scene feels brisk or interminable. Per-scene overrides go on the
   * scene's own class, never here. */
  min-height: var(--pickle-scene-track);
}

html.scenes .scene-stage {
  position: sticky;
  top: 0;
  /* svh, not vh: on mobile `vh` includes the browser chrome that is about to
   * collapse, so a 100vh stage is taller than the screen it is meant to fill. */
  height: 100svh;
  padding: calc(var(--pickle-nav-h) + var(--pickle-space-4)) var(--pickle-gutter) var(--pickle-space-5);

  /* `minmax(0, 1fr)` on the body row, and `min-height: 0` on everything inside
   * it, is what stops a scene clipping.
   *
   * Measured before this: four of the five scenes overflowed a 100svh stage and
   * `overflow: hidden` silently cut them - a headline sheared off at the top, the
   * counters gone from the bottom, and worse the shorter the viewport. The cause
   * was sizing each figure as a fraction of the viewport and hoping the total
   * fit. Now the figure takes the row that is LEFT, so it shrinks instead of
   * overflowing and there is no budget to get wrong. */
  grid-template-rows: auto minmax(0, 1fr);
  overflow: hidden;
}

html.scenes .scene-stage > * { min-height: 0; }

/* ------------------------------------------------------------------ scene head
 * The kicker stays in frame for the whole scene, and for a Designed mechanism
 * that is a content requirement rather than a layout choice: an animation of a
 * system that does not exist yet must say so while it plays.
 */
.scene-head {
  width: min(100%, var(--pickle-content));
  margin-inline: auto;
}

.scene-kicker {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pickle-space-3);
  align-items: center;
  color: var(--pickle-subtle);
  font-family: var(--pickle-font-mono);
  font-size: var(--pickle-text-2xs);
  letter-spacing: var(--pickle-tracking-caps);
  text-transform: uppercase;
}

.scene-kicker .n {
  color: var(--pickle-accent-text);
  /* A rule after the number, drawn rather than typed, so it holds its width
   * whatever the numeral. */
  padding-right: var(--pickle-space-3);
  border-right: 1px solid var(--pickle-line);
}

/* --------------------------------------------------------------- scene canvas
 * Where a scene's own elements live. Grid rather than flow: a stage positions
 * things against each other, not one after another.
 */
.scene-body {
  display: grid;
  align-content: center;
  width: min(100%, var(--pickle-content));
  min-height: 0;
  margin-inline: auto;
}

.scene-body > * { min-height: 0; }

/* Display type on a stage scales with the SMALLER of width and height.
 *
 * A text column only has to worry about its measure; a stage headline also has to
 * leave room for a figure below it in a frame whose height is fixed. Sized on vw
 * alone, the same headline that fits at 1440x900 eats the figure at 1440x720. */
.scene h2 {
  font-size: clamp(1.5rem, min(4.2vw, 5.4svh), 3.4rem);
  letter-spacing: var(--pickle-tracking-display);
  line-height: var(--pickle-leading-display);
  text-wrap: balance;
}

.scene .scene-lede {
  max-width: 62ch;
  margin-top: var(--pickle-space-3);
  color: var(--pickle-muted);
  font-size: clamp(.92rem, 2.1svh, 1.12rem);
  line-height: 1.5;
}

/* ============================================================ initial states
 * Everything below is scoped to html.scenes and nothing below is scoped to
 * anything else. This is the boundary the whole design rests on.
 */

/* A reveal: present in the markup, transparent until its scene reaches it. */
html.scenes [data-reveal] {
  opacity: 0;
  /* will-change on a handful of elements is a hint; on every element it is a
   * memory leak. Only the reveal targets get it, and only while a scene is the
   * thing being scrolled. */
  will-change: opacity, transform;
}

/* A figure that counts. It starts at its own start value in the markup, so with
 * no JavaScript the reader sees a real number rather than a zero. */
html.scenes [data-count] { font-variant-numeric: tabular-nums; }

/* ============================================================ reduced motion
 * Belt and braces. scene-ready.js already declines to add the class, so none of
 * the initial states above apply - but a reader can turn reduced motion on after
 * load, and then the class is already on the element.
 */
@media (prefers-reduced-motion: reduce) {
  /* The reveals only need resetting if the class somehow IS on - a reader can
   * turn reduced motion on after load, and scene-ready.js has already run. */
  html.scenes [data-reveal] { opacity: 1; will-change: auto; }

  /* Unpin unconditionally, NOT scoped to html.scenes. The pin is opt-in above,
   * so normally there is nothing to undo; this is the after-load case, where the
   * class is present and the animation will not run. */
  .scene { min-height: 0; }

  .scene-stage {
    position: static;
    height: auto;
    padding: var(--pickle-space-7) var(--pickle-gutter);
    overflow: visible;
  }
}

/* ============================================== below a landscape desktop
 * THE UNPIN USED TO LIVE HERE, and it took every phone animation with it.
 *
 * The measurement behind it was real: below 1024px the two-column scene layouts
 * collapse to one column, which makes every scene taller, and a held stage is
 * exactly one viewport. The waterfall overflowed it by 106px at 768x1024.
 * Holding a view still while content is cut off the bottom is the worst of both
 * registers, so the pin became a desktop treatment - and with it, so did the
 * timelines, because a scrubbed timeline on an in-flow section is worse than
 * none. A phone reader met five finished figures.
 *
 * The fix for content that does not fit a stage is to compose it for the stage,
 * not to stop holding the view. Each scene now carries a phone composition
 * budgeted against 100svh: the type scale below steps down, and the three
 * scenes that did not fit - clocks at 1.36x, waterfall at 1.76x, roadmap at
 * 2.82x on a 390px phone - are recomposed in scene-figures.css.
 *
 * So there is no unpin at any WIDTH. What is left is one at a HEIGHT, below -
 * because a stage that does not exist cannot be held, and 360px of landscape
 * phone minus 72px of sticky nav is not a stage. That gate is
 * `(max-height: 620px)`: a height no phone has in portrait, and no tablet or
 * desktop has at all.
 *
 * tests/e2e/scenes-mobile.spec.mjs measures every pinned scene against its
 * stage from 360x800 to 1920x1080 and fails on a single pixel of overflow.
 */

/* ================================================== no stage to hold
 * The unpin, at the only threshold that still earns one.
 *
 * `min-height: 621px` in web/lib/scenes/runtime.js is the same line drawn on
 * the other side: below it no scene timeline is built, so every initial state
 * has to come back on - see the block at the end of scene-figures.css. The two
 * thresholds must stay complementary. A height both covered would leave a
 * timeline animating elements a stylesheet had already finished; a height
 * neither covered would leave a held stage with nothing to play in it.
 *
 * Unscoped, like the reduced-motion block, because these have to win whether or
 * not `html.scenes` is on.
 */
@media (max-height: 620px) {
  .scene,
  html.scenes .scene { min-height: 0; }

  .scene-stage,
  html.scenes .scene-stage {
    position: static;
    height: auto;
    padding: var(--pickle-section-sm) var(--pickle-gutter);
    overflow: visible;
  }
}

/* ============================================================ scene 01 clocks
 * Four concentric rings on the right, the four cadences as rows on the left.
 *
 * The dial is the subject and the rows are its legend, so the dial gets the
 * larger column and the rows are set small and mono. The first version of this
 * scene put four bars across the full width, which read as a chart - this reads
 * as an instrument.
 */
.clocks {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: var(--pickle-space-5) var(--pickle-space-7);
  align-content: center;
}

/* The head is capped at 46ch, so spanning both columns never widened it by a
 * pixel: it only reserved the whole first row, and the half of that row beside
 * the headline was the largest empty area on the page. The head takes its own
 * column and the dial runs the full height beside it, which is the composition
 * the scene wanted all along - copy left, instrument right.
 *
 * 46ch still holds the headline to two lines. Given the whole 592px column it
 * would set as "One transaction. Four" / "clocks.", breaking mid-sentence. */
.clocks-head { grid-column: 1; max-width: 46ch; }
.clocks-rows-col { grid-column: 1; align-self: center; }
.clocks-foot { grid-column: 1 / -1; }

/* ---- the dial ---------------------------------------------------------- */

.clocks-dial {
  display: grid;
  place-items: center;
  align-self: center;
  /* The dial is square and must never be the thing that overflows a 100svh
   * stage, so its size is bounded by the stage's height as well as its width. */
  width: 100%;
  /* Two rows of height to work with now rather than one, so the bound rises
   * with it. Both bounds still hold: never wider than its column, never taller
   * than a share of the stage it has to share with the head and the foot. */
  max-width: min(100%, 56svh);
  margin-inline: auto;
  /* The ring labels are positioned against this box. It is square (a square
   * viewBox at width:100%, height:auto), so a percentage here is a viewBox
   * coordinate over 720. */
  position: relative;
  grid-row: 1 / 3;
  grid-column: 2;
}

.clocks-dial svg { display: block; width: 100%; height: auto; overflow: visible; }

/* The spiral the transaction travels. Brighter and thicker than a track, because
 * a track is a target and this is what actually happened. */
.clocks-trail {
  fill: none;
  stroke: var(--pickle-mint);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---- the ring labels --------------------------------------------------- */
/*
 * One label per ring, placed ON the ring it belongs to.
 *
 * Without them the dial was four unlabelled circles beside a list of four
 * cadences, with nothing at all saying which circle was which. The figure is
 * the link: each label carries the same value, in the same mono face and the
 * same mint, as the right-hand column of its row.
 *
 * HTML rather than SVG <text>, for the reason the fold's orbit labels are: text
 * inside a viewBox scales with the box, so 13px on a desktop renders at about
 * 5px on a phone. The offsets are r/720 about a centre at 360, taken at 0, 60,
 * 105 and 145 degrees clockwise from twelve. Outward and clockwise, which is
 * the direction time runs in - and it keeps every label on the dial's right
 * half, away from the rows column.
 */
.clocks-tag {
  position: absolute;
  padding: 3px 9px;
  color: var(--pickle-accent-text);
  font-family: var(--pickle-font-mono);
  font-size: clamp(.66rem, 1.9vw, .78rem);
  white-space: nowrap;
  background: var(--pickle-bg);
  border: 1px solid var(--pickle-mint-line);
  border-radius: 999px;
  transform: translate(-50%, -50%);
}

/* On the spiral, not on a ring. Each pair is the crossing point of the path
 * itself: the transaction reaches radius r at t = r / rMax, and the spiral's angle
 * there is t * TURNS * 360 - 90, which for 108, 176, 244 and 312 of 312 comes to
 * 48.5, 2.3, 316.2 and 270 degrees. Computed, not placed by eye - the closest two
 * badges are 17.7% of the box apart. */
.clocks-tag.is-exec { left: 59.95%; top: 61.23%; }
.clocks-tag.is-mini { left: 74.42%; top: 50.98%; }
.clocks-tag.is-evm { left: 74.44%; top: 26.52%; }
/* The outermost badge sits at t = 0.985 and not at 1.0. The spiral ENDS at 1.0,
 * which is exactly where the transaction settles, and a badge with an opaque
 * ground there covers the dot in the final frame - the one frame a reader
 * lingers on. A step back along the path is a long step round it at four turns,
 * which is why this pair looks unrelated to the others. */
.clocks-tag.is-l1 { left: 34.29%; top: 10.31%; }

/* The subject of the diagram, at its centre, where there was nothing. The halo
 * pulses behind it as the transaction arrives - the HTML is a later sibling
 * than the SVG, so it draws over the halo rather than under it.
 *
 * It carries the page's own ground, because the spiral now starts at radius zero
 * and its first coil passes straight through this label. The ring tags are
 * bounded the same way for the same reason. */
.clocks-centre {
  position: absolute;
  left: 50%;
  top: 50%;
  margin: 0;
  padding: 3px 9px;
  background: var(--pickle-bg);
  border-radius: 999px;
  color: var(--pickle-text);
  font-family: var(--pickle-font-mono);
  font-size: var(--pickle-text-2xs);
  letter-spacing: var(--pickle-tracking-caps);
  text-transform: uppercase;
  transform: translate(-50%, -50%);
}

/* ---- the rows ---------------------------------------------------------- */

/* Rules as borders on the rows, NOT as a container background showing through
 * 1px gaps. The gap trick is how every other grid in this design draws its
 * hairlines, and here it was a bug: a row at opacity 0 let the container's
 * line-coloured background through, so the two unrevealed rows rendered as one
 * solid grey block. `opacity` hides an element's border with it, so a border per
 * row leaves nothing behind. */
.clocks-rows {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.clocks-row {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto;
  gap: var(--pickle-space-4);
  align-items: center;
  padding: var(--pickle-space-4) var(--pickle-space-2);
}

.clocks-row + .clocks-row { border-top: 1px solid var(--pickle-line); }

/* The mark ties a row to its ring: same accent, same size as the travelling
 * dot, so the eye connects the two without a leader line. */
.clocks-row-mark {
  width: 8px;
  height: 8px;
  background: var(--pickle-mint);
  border-radius: 50%;
}

.clocks-row-text { display: grid; gap: 1px; }
.clocks-row-text b { font-size: var(--pickle-text-base); font-weight: 600; letter-spacing: -.01em; }

.clocks-row-text small {
  color: var(--pickle-subtle);
  font-family: var(--pickle-font-mono);
  font-size: var(--pickle-text-2xs);
}

.clocks-row-time {
  color: var(--pickle-accent-text);
  font-size: var(--pickle-text-sm);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* ---- the live readout -------------------------------------------------- */

.clocks-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pickle-space-3) var(--pickle-space-6);
  align-items: baseline;
  padding-top: var(--pickle-space-4);
  border-top: 1px solid var(--pickle-line);
}

.clocks-live { display: flex; gap: var(--pickle-space-3); align-items: baseline; }

.clocks-live .k {
  color: var(--pickle-subtle);
  font-family: var(--pickle-font-mono);
  font-size: var(--pickle-text-2xs);
  letter-spacing: var(--pickle-tracking-caps);
  text-transform: uppercase;
}

.clocks-live .v { color: var(--pickle-accent-text); font-size: var(--pickle-text-sm); }

.clocks-note {
  flex: 1 1 22rem;
  color: var(--pickle-subtle);
  font-family: var(--pickle-font-mono);
  font-size: var(--pickle-text-2xs);
  line-height: 1.6;
}

/* ---- initial states, under the class only ------------------------------ */

html.scenes .clocks [data-reveal] { opacity: 0; transform: translateY(var(--pickle-rise)); }
html.scenes .clocks [data-row] { opacity: 0; transform: translateX(-12px); }


/* The ring labels and the centre label. They had no rest state and so were the
 * only things on screen before the scene drew anything: four cadence values and
 * "1 tx" floating over an empty dial, which reads as a diagram that failed to
 * load rather than one that has not started. Each label now arrives with the
 * ring it belongs to. */
html.scenes .clocks [data-tag],
html.scenes .clocks [data-mid] { opacity: 0; }

/* Both halves of the dash, always - and a GAP LONGER THAN THE PATH.
 *
 * `stroke-dasharray: 1` with no offset means the dash covers the whole path, so
 * every ring was FULLY DRAWN before its timeline started and then jumped to
 * hidden when GSAP set its from-state. Declaring the offset here is what makes
 * the rest state actually undrawn. Every rule in this file that sets a dasharray
 * must set an offset with it.
 *
 * THE GAP IS 200 AND NOT 100, AND THAT IS THE WHOLE OF THE SECOND FIX.
 *
 * A dasharray of `100` is `100 100`, and the pattern REPEATS: at an offset of
 * 100 the gap covers 0-100 and the next dash begins at exactly 100, which on a
 * path whose pathLength is 100 is the path's own end. A dash that begins where
 * the path ends has zero length, and `stroke-linecap: round` paints a
 * zero-length dash as a disc of the stroke's width - so the undrawn spiral was
 * rendering a mint dot at its outer end, on screen before the scene had drawn
 * anything at all. A gap of 200 puts the next dash at 200, off the end of the
 * path, and nothing is painted. The drawing is unchanged: at every offset from
 * 100 down to 0 the first dash covers 0 to (100 - offset) either way.
 *
 * Scoped to html.scenes, so with no JavaScript there is no dash at all and a
 * reader sees four complete rings. */
/* Hidden at rest, WITHOUT depending on the path's length.
 *
 * This was `100 200` with an offset of 100, which hides a path only if
 * `pathLength="100"` is honoured - and real iOS Safari does not honour it for
 * dashes, so on a phone these paths rendered as a repeating dash pattern before
 * a single tween had run. A dash and gap larger than any path in this document
 * hides it on every engine, and neither half is zero, so no round cap paints a
 * phantom dot. The OFFSET clears the dash by one unit for the same reason: at
 * `offset === dash` the dash ends exactly at the path's start and shares a
 * single point with it, and a zero-length segment under a round cap paints as a
 * filled dot. The builders overwrite all three with measured values the moment
 * a timeline is built - see armDash in web/lib/scenes/runtime.js. */
html.scenes .clocks [data-trail] { stroke-dasharray: 100000 100000; stroke-dashoffset: 100001; }

@media (prefers-reduced-motion: reduce) {
  html.scenes .clocks [data-reveal],
  html.scenes .clocks [data-row],
  html.scenes .clocks [data-tag],
  html.scenes .clocks [data-mid] { opacity: 1; transform: none; }
  html.scenes .clocks [data-trail] { stroke-dasharray: none; stroke-dashoffset: 0; }
}

/* Below a landscape desktop the dial stacks above its rows: two columns of a
 * square diagram and a list is a desktop composition. The scene is still pinned
 * here - see the phone block below and the note where the unpin used to be. */
@media (max-width: 1023px) {
  .clocks { grid-template-columns: minmax(0, 1fr); }
  /* One column, so the row and column spans above have to be released or the
   * dial stays pinned to a column 2 that no longer exists. */
  .clocks-head,
  .clocks-rows-col { grid-column: auto; }
  .clocks-dial {
    grid-row: auto;
    grid-column: auto;
    /* Bounded by the STAGE's height as well as its width, exactly as the
     * desktop rule above is. `min(100%, 30rem)` stood here and named no height
     * at all, which is how a square figure in a one-column stack becomes the
     * thing that overflows: at 390px it took 358px of an 844px viewport. */
    max-width: min(100%, 28svh);
    order: -1;
  }
  .clocks-text { max-width: none; }
}

/* ========================================== the one-column stage, budgeted
 *
 * A pinned stage is exactly one viewport, and below 1024px it is a ONE-COLUMN
 * viewport: every scene's two-column desktop layout collapses here, which is
 * what makes the scenes taller exactly where there is least room. 844px at
 * best, 800 at worst, with 72px of sticky nav off the top before a scene owns a
 * single pixel.
 *
 * The scenes used to answer that by not being scenes here - unpinned below
 * 1024px, no timeline, every rest state undone. This block is the other answer,
 * and it is the one the pin needs: a composition sized for the frame it has to
 * hold. Nothing below removes a word or a figure. It steps the type down, takes
 * the air out of the gaps, and bounds the elements that would otherwise grow
 * without limit.
 *
 * THE BAND IS 1023px AND NOT 699px, which is the correction that made this
 * work. It was written for phones first, and phones passed while every tablet
 * stayed broken - the waterfall was still 1.55x over its stage at 700x900 and
 * 1.97x at 1023x700, because the layout that causes the problem changes at
 * 1024 and the fix for it was gated at 700. A compaction has to cover the same
 * band as the reflow it is compensating for.
 *
 * The clamps are width-driven, so the same rules read as a phone composition at
 * 360px and as a tablet one at 1023px rather than shrinking a tablet to phone
 * type. MEASURED from 360x800 to 1023x700;
 * tests/e2e/scenes-mobile.spec.mjs holds every pinned scene to its stage across
 * that range and fails on one pixel of overflow, so a headline that grows by a
 * line is caught here rather than sheared off by `overflow: hidden`.
 */
@media (max-width: 1023px) {
  html.scenes .scene-stage {
    /* The nav is sticky and its full height still has to be cleared; what comes
     * off is the breathing space under it, which a phone cannot afford. */
    padding: calc(var(--pickle-nav-h) + var(--pickle-space-2))
             var(--pickle-gutter)
             var(--pickle-space-3);
    gap: var(--pickle-space-3);
  }

  /* Display type on a stage is already sized on `min(vw, svh)`, and on a phone
   * both are small enough that the clamp's FLOOR was doing all the work - 1.5rem
   * regardless of the screen. A floor is the right mechanism and 1.5rem was the
   * wrong floor for a 360px screen; this one scales with the width it has. */
  .scene h2 { font-size: clamp(1.18rem, 5.6vw, 1.55rem); }

  .scene .scene-lede {
    margin-top: var(--pickle-space-2);
    font-size: clamp(.8rem, 3.5vw, .92rem);
    line-height: 1.45;
  }
}

/* ---- scene 01, one column ----------------------------------------------
 * The dial is bounded by svh in the layout block above; what is left is the
 * list beside it, which here is a list UNDER it: four rows at `space-4` of
 * padding each is 300px of an 844px stage for four short lines.
 */
@media (max-width: 1023px) {
  .clocks { gap: var(--pickle-space-3); }

  .clocks-row {
    gap: var(--pickle-space-3);
    padding: var(--pickle-space-2) 0;
  }

  .clocks-row-text b { font-size: var(--pickle-text-sm); }
  .clocks-row-time { font-size: var(--pickle-text-xs); }

  .clocks-foot {
    gap: var(--pickle-space-2) var(--pickle-space-4);
    padding-top: var(--pickle-space-3);
  }

  /* The badges ON the spiral: the dial is bounded by 34svh here, so a tag sized
   * for a 560px dial sits on a 280px one and four of them collide. */
  .clocks-tag { padding: 2px 6px; font-size: clamp(.58rem, 2.4vw, .68rem); }
}

/* ============================================== the stage that is SHORT
 *
 * The one-column block above is width-driven, because the thing it compensates
 * for - the collapse to one column - is a width. These are the other axis, and
 * they caught three failures nothing width-driven could:
 *
 *   1023x700, a small landscape tablet, where the one-column compaction had
 *   already applied and the scenes STILL overflowed: 700px of stage is 100px
 *   less than a 360px phone gets.
 *
 *   1440x720 and 1280x800, ordinary laptops, where the waterfall overflowed its
 *   stage by 62px and 16px - on the DESKTOP layout, silently, clipped by the
 *   stage's `overflow: hidden`. Those predate every change here. It is what a
 *   height budget checked at one viewport height looks like a year later.
 *
 * WHY THE WIDTH IS IN EVERY QUERY BELOW. These were written unscoped first, and
 * every value that a short stage wanted SMALLER than the desktop default was
 * still LARGER than the one-column default - so on a 360x800 phone, which
 * matches both, a block whose entire purpose is to trim added 36px to the
 * clocks and 14px to the waterfall. A trim that can enlarge is not a trim. Each
 * block now names the layout it is trimming, so no two of them ever meet on one
 * element.
 */

/* ---- short, on the desktop layout -------------------------------------- */
@media (min-width: 1024px) and (max-height: 820px) {
  /* The display clamp's svh term drops from 5.4 to 4.6; the vw term is
   * untouched, so the headline narrows only where height is what binds. */
  .scene h2 { font-size: clamp(1.5rem, min(4.2vw, 4.6svh), 3.4rem); }
  .scene .scene-lede { font-size: clamp(.86rem, 1.9svh, 1.05rem); }

  .clocks { gap: var(--pickle-space-4); }
  .clocks-row { padding: var(--pickle-space-3) var(--pickle-space-2); }
  .clocks-foot { padding-top: var(--pickle-space-3); }
}

/* ---- shorter still, on the desktop layout ------------------------------ */
@media (min-width: 1024px) and (max-height: 740px) {
  /* 112px of stage padding is 15% of a 720px viewport. The nav's own height
   * still has to be cleared - it is sticky - but the air under it goes. */
  html.scenes .scene-stage {
    padding: calc(var(--pickle-nav-h) + var(--pickle-space-2))
             var(--pickle-gutter)
             var(--pickle-space-3);
    gap: var(--pickle-space-3);
  }

  .clocks-dial { max-width: min(100%, 44svh); }
  .clocks-row { padding: var(--pickle-space-2) var(--pickle-space-2); }
  .clocks-foot { padding-top: var(--pickle-space-2); }
}

/* ---- short, on the one-column layout -----------------------------------
 * Small landscape tablets, and every phone below a 414: 860px catches 360x800,
 * 375x812 and 390x844, which cleared their stages by 13, 19 and 38px before
 * this block reached them. A 13px margin is not a margin - one font fallback,
 * one headline that wraps a line further, and the stage's `overflow: hidden`
 * eats the bottom of the figure silently. Every value here is smaller than its
 * one-column counterpart, which is the property the unscoped version of this
 * block lost.
 */
@media (max-width: 1023px) and (max-height: 860px) {
  /* THE DIAL HAS A LEGIBILITY FLOOR AND IT IS NOT A HEIGHT BUDGET.
   *
   * This was 24svh - 202px on a 390px phone - and the spiral drew correctly at
   * that size and read as broken anyway: the four cadence pills sit ON the
   * curve, by design, and at 202px the widest of them is a third of the dial's
   * diameter. They covered the spiral in three places, so a continuous line
   * appeared as disconnected arcs and the figure looked like concentric
   * circles rather than one journey outward.
   *
   * 40svh is 338px there, which puts a turn of the spiral 34px from the next
   * and the widest pill at a fifth of the diameter - the proportion the desktop
   * dial has. The height it costs comes off the rows and the foot below. */
  .clocks-dial { max-width: min(100%, 40svh); }
  .clocks-row { padding: 5px 0; }
  .clocks-foot { padding-top: var(--pickle-space-2); }

  html.scenes .scene-stage {
    padding: calc(var(--pickle-nav-h) + 4px) var(--pickle-gutter) var(--pickle-space-2);
    gap: var(--pickle-space-2);
  }
}

/* ---- the dial yields, because it is the only thing here that can --------
 * The clocks scene had no slack at all: the dial was pinned at 40svh while the
 * head, the four cadence rows and the foot around it are TEXT, which grows.
 * Measured, a 6% taller rendering of that text - a font fallback, a different
 * system face, a text-size setting, none of them reproducible by resizing a
 * viewport - collapsed the scene by 42px and drew the rows through the dial and
 * the lede. On the owner's iPhone it showed as "Executed" and "Preconfirmed"
 * sitting on the description.
 *
 * A budget with no give is a budget that holds only on the machine it was
 * measured on. So the dial takes the room that is LEFT rather than a fixed
 * share: its row is `minmax(0, 1fr)`, and its height is whichever is smaller of
 * 40svh and the row it has been given. The text takes what it needs first.
 *
 * The box has to stay SQUARE while it does that, because the four cadence tags
 * are positioned as percentages of it and a percentage only converts straight
 * across while the box keeps the viewBox's ratio - so `aspect-ratio: 1` with
 * `width: auto`, which makes the width follow the height rather than the other
 * way round. That is the whole trick: the height is the constrained dimension
 * here, and the width is what should follow it.
 */
@media (max-width: 1023px) {
  .clocks { grid-template-rows: minmax(0, 1fr) auto auto auto; }

  .clocks-dial {
    min-height: 0;
    aspect-ratio: 1;
    height: min(40svh, 100%);
    width: auto;
    max-width: 100%;
    margin-inline: auto;
  }

  .clocks-dial svg { width: 100%; height: 100%; }
}

/* ---- and the text around it, measured in the stage ---------------------
 * The dial yielding buys the scene one kind of slack: room when the text grows.
 * This buys the other: less text when the stage is short. Both are needed - a
 * taller font at the same svh is not the same problem as the same font on a
 * shorter stage, and neither mechanism covers the other.
 */
@media (max-width: 1023px) {
  .clocks { gap: clamp(4px, 1svh, 12px); }

  .clocks-row { padding: clamp(3px, 0.7svh, 7px) 0; }
  .clocks-row-text b { font-size: clamp(.7rem, 1.9svh, .85rem); line-height: 1.25; }
  .clocks-row-text small { font-size: clamp(.52rem, 1.3svh, .64rem); line-height: 1.3; }
  .clocks-row-time { font-size: clamp(.6rem, 1.6svh, .75rem); }

  .clocks-foot {
    gap: clamp(4px, 0.9svh, 12px) var(--pickle-space-4);
    padding-top: clamp(4px, 1svh, 12px);
  }

  .clocks-live .k { font-size: clamp(.5rem, 1.25svh, .62rem); }
  .clocks-note { font-size: clamp(.54rem, 1.3svh, .68rem); line-height: 1.45; }
}
