/* ============================================================
   PLATES — the sections of this case study as a stack of sheets

   Loaded LAST on /raf, after wide.css. Everything here is additive: delete
   the file and the page renders as it did before, full-bleed bands and all.
   Nothing below is required for the content to be readable.

   THE IDEA
   The page already had one moment of this language — .cs-context.is-wide, the
   cream panel whose rounded bottom corners cut into the dark band beneath it,
   one sheet lifting off another. This file makes that the page's grammar
   instead of a one-off: most top-level blocks become sheets, inset from the
   viewport by one gutter, rounded, with a ground of their own.

   THE RULE FOR GROUNDS, so this stays a system rather than a mood board:
     * the two set-pieces of the argument — the $12.7M band and the 86% / 9%
       statement — are DARK (.is-dark, chart-glass.css)
     * everything else ALTERNATES between the page's own paper (--bg-cream)
       and one step of recess (--surface), so no two adjacent sheets share a
       ground and every seam has an edge you can see without a rule being
       drawn there.

   THREE SHEETS OPT OUT OF THE INSET, each for a stated reason:
     * #pv-ch1 is not a sheet at all any more — it keeps the full frame it
       had, edge to edge, and does not carry .cs-plate. See CH1 below.
     * #pv-ch2 is a sheet with no edge (.is-edgeless): ground only, no
       hairline and no shadow.
     * #pv-ch3 runs the full width (.is-bleed) with a capped top and a square
       foot, and swallows the four-step band so the two read as one panel.

   NO ROTATION, no stacked drop shadows. "A pile of papers" is read here as
   depth and separation, not as literal scatter: one hairline, one contact
   shadow, one ambient shadow, and a ground that changes at every boundary.
   This is an editorial case study; the sheets are the frame, not the subject.

   THE ARITHMETIC, once, because every rule below is a variant of it.
   .cs-body is a 1060px measure centred in the viewport. A percentage in a
   child's margin or padding resolves against THAT measure, so:
       margin-inline: calc(50% - 50vw + var(--plate-edge))
   pushes a block's edges out to --plate-edge short of the viewport, and
       padding-inline: max(gutter, calc(50vw - 50% - var(--plate-edge)))
   puts back exactly what the bleed took, landing the content on the measure.
   Change the measure and both follow it; there is no magic number. max()
   keeps a floor so a narrow viewport never asks for negative padding.
   --plate-edge is --plate-gutter for an inset sheet and 0px for a bleeding
   one, which is the whole of what .is-bleed does.
   ============================================================ */

body[data-case-study] {
  /* THE GUTTER. The only spacing value in this file, used on all four sides
     of every inset sheet and between them, so the stack reads as one inset
     rather than as a set of decisions. Small on purpose: at 8-20px the sheets
     are laid ON the page, which is the effect; at 40px they become cards
     floating in a container, which is a different and much cheaper page. It
     matches the .cs-context-film gutter (clamp(8px, 0.9vw, 16px)) that was
     already here, one step wider so the film still reads as the widest thing. */
  --plate-gutter: clamp(8px, 1.2vw, 20px);

  /* THE RADIUS. Sized against the two radii already on the page — the context
     panel's clamp(18px, 2vw, 34px) and the film's clamp(12px, 1.1vw, 18px) —
     and set between them, because these sheets are bigger than the film and
     smaller in the hierarchy than the panel that opens the page. */
  --plate-radius: clamp(14px, 1.7vw, 28px);

  /* Vertical air INSIDE a prose sheet. This replaces --cs-chapter-gap (300px
     of page ground between chapters); a sheet earns its separation from its
     own padding and its edge, not from a canyon. --cs-chapter-gap is still
     live and still means what it meant — the arrow rule below depends on the
     difference between the two, so do not delete it. */
  --plate-pad-block: clamp(60px, 6.5vw, 112px);

  /* THE FOUR-STEP BAND'S COLUMN. Was a hard-coded 1240px in three places in
     styles-sidebar.css; narrowed on request and named here because
     #pv-ch4's --split-w is matched to it and the two must not drift. At
     1100px each of the four phone frames is about 254px wide, which is still
     a legible screen; much below this and the band stops being readable. */
  --band-w: 1100px;

  /* THE COLUMN, resolved, for a FULL-BLEED container. --band-w capped by the
     room actually available across the whole viewport. One user left after
     rev 6: the white card inside #pv-ch3, which sits on a bleeding panel. The
     band and #pv-ch4's split moved to --band-col below when the band moved
     into an inset sheet; see the note there. */
  --panel-w: min(var(--band-w), calc(100vw - 2 * var(--wrap-gutter, 24px)));

  /* THE SHEET'S OWN INSET. Was a literal clamp inside the mosaic's padding
     rule; named because --band-col now has to subtract it. It is the hairline
     of air that keeps the outer plates off the sheet's rounded corner. */
  --sheet-pad: clamp(6px, 0.7vw, 12px);

  /* THE COLUMN, resolved, for a container INSIDE THE INSET SHEET.
     The four-step band moved out of #pv-ch3 (full bleed, 100vw) and into the
     mosaic's sheet, which is inset by one --plate-gutter a side and padded by
     one --sheet-pad a side. So the room it has is not 100vw any more:
         --stack-w = 100vw - 2*gutter - 2*sheet-pad
     and the band's column is --band-w capped by what is left of that after
     the gutter floor:
         --band-col = min(--band-w, --stack-w - 2*--wrap-gutter)
     This is exactly the width .cs-shortcut-row's CONTENT box resolves to
     under its own `padding-inline: max(wrap-gutter, (100% - band-w)/2)` in
     both branches of that max(), which is why #pv-ch4's --split-w can read it
     and stay equal to the pixel. Above about 1150px viewport the cap does not
     bind and this is simply --band-w, so the desktop composition is unmoved.
     --wrap-gutter resolves at this scope (40px wide, 24px narrow); verified,
     because taking the 24px fallback here would silently break the match. */
  --stack-w: calc(100vw - 2 * var(--plate-gutter) - 2 * var(--sheet-pad));
  --band-col: min(var(--band-w), calc(var(--stack-w) - 2 * var(--wrap-gutter, 24px)));

  /* THE BAND CONTAINER'S INSET. The same clamp .cs-panel-card uses for its
     own horizontal padding, because the band is now the same object in the
     same relationship: a cream card on a recessed sheet. Named because the
     container's WIDTH is derived from it — see the band rule below. */
  --band-pad: clamp(24px, 3.2vw, 56px);
}

/* ---------- the primitive ---------- */

.cs-body > .cs-plate {
  --plate-ground: var(--bg-cream);
  --plate-edge: var(--plate-gutter);

  position: relative;
  width: auto;
  margin-block: 0 var(--plate-gutter);
  margin-inline: calc(50% - 50vw + var(--plate-edge));
  border-radius: var(--plate-radius);
  background: var(--plate-ground);

  /* THREE LAYERS, and only three.
     1. an inset hairline rather than a border, so the ring costs no layout
        and cannot disturb the multi-column flow inside a .pv-chapter;
     2. a tight contact shadow, which is the one that actually reads as
        weight — a single soft shadow makes a surface float with nothing
        underneath it;
     3. a wide, very low ambient shadow with a large negative spread, so it
        is a shading under the edge and never a visible halo.
     The hairline reads --rule-soft from the element itself, which the dark
     sheets redefine to white — so one declaration covers both grounds. */
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 1px 2px rgba(0, 0, 0, 0.04),
    0 24px 48px -34px rgba(0, 0, 0, 0.24);
}

/* recess: one step down from the page's own paper. --surface is the token the
   page already uses for a sunk plane (#eeece9 light, #252524 dark), so this
   inherits the theme rather than declaring a second palette. */
.cs-body > .cs-plate.is-recessed { --plate-ground: var(--surface); }

/* the dark sheets take their ground and their ink from .is-dark in
   chart-glass.css; naming it here keeps --plate-ground truthful for the
   children that read it (the sticky head below) */
.cs-body > .cs-plate.is-dark { --plate-ground: #120a07; }

/* Dark mode: a cast shadow on a near-black page is invisible, and pushing it
   until it is visible produces the shadow soup this file exists to avoid. The
   hairline carries the whole edge instead. */
.dark-mode .cs-body > .cs-plate {
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 20px 44px -36px rgba(0, 0, 0, 0.9);
}

/* PADDED sheets — the prose chapters. .is-bare opts out: the twenty-one step
   sheet and the statement manage their own insets, and giving them a second
   one would break the compositions measured against their own boxes. */
.cs-body > .cs-plate:not(.is-bare) {
  padding-block: var(--plate-pad-block);
  padding-inline: max(var(--wrap-gutter), calc(50vw - 50% - var(--plate-edge)));
}

/* ---------- .is-edgeless: a sheet with a ground and no edge ----------
   No hairline, no shadow. Used where the sheet should hold its own ground
   without drawing a boundary around the writing. On a sheet whose ground is
   the page's own paper this makes the container invisible, which is the
   point of the modifier — the sheet is still there for its padding and its
   place in the stack, it just stops announcing itself. */
.cs-body > .cs-plate.is-edgeless,
.dark-mode .cs-body > .cs-plate.is-edgeless { box-shadow: none; }

/* ---------- .is-bleed: a sheet that keeps the full width ----------
   One token. Everything else — the padding that returns the content to the
   measure, the grounds, the shadow — is unchanged, so a bleeding sheet is
   the same object at a different width rather than a second system. */
.cs-body > .cs-plate.is-bleed {
  --plate-edge: 0px;
  border-radius: 0;
}

/* ---------- .is-tucked: a sheet laid ON the one above it ----------
   The page's oldest gesture, generalised. .cs-context.is-wide overlaps the
   band beneath it by one radius so its corners have something to cut into —
   a rounded corner only reveals what is behind it, so two sheets that merely
   abut show nothing at all. This pulls a sheet up over its predecessor by
   --plate-tuck (one radius by default) and takes the gutter out from between
   them, so the corners land on the sheet above rather than on page ground.

   The predecessor is found with :has(), so adding or removing .is-tucked is
   one class and the gutter follows on its own. */
.cs-body > .cs-plate.is-tucked {
  margin-top: calc(-1 * var(--plate-tuck, var(--plate-radius)));
}

.cs-body > .cs-plate:has(+ .cs-plate.is-tucked),
.cs-body > .pv-chapter.is-banded:has(+ .cs-plate.is-tucked) { margin-bottom: 0; }

/* .is-capped: rounded at the head, square at the foot. A square foot only
   reads as a join rather than as a cut if something arrives on it, so the
   bottom gutter goes with it and the next sheet sits directly on this edge —
   which puts that sheet's own top corners against this ground, where they
   read. Nothing carries this at the moment; it is kept because it is the
   other half of .is-tucked and one line to reach for. */
.cs-body > .cs-plate.is-capped {
  border-radius: var(--plate-radius) var(--plate-radius) 0 0;
  margin-bottom: 0;
}

/* The last sheet closes on the same gutter it opened with, so the stack ends
   on the page rather than on the next-case navigation. .cs-body's top padding
   is already removed by the :has() rule in chart-glass.css. */
.cs-body { padding-bottom: var(--plate-gutter); }
.cs-body > .cs-plate:last-child { margin-bottom: 0; }

/* ---------- CH1: not a sheet ----------
   The $12.7M band keeps the full frame, edge to edge, with no radius and no
   gutter — it is the page's opening statement and the sheets start under it.
   It carries no .cs-plate at all, so every rule in chart-glass.css applies
   untouched and the .cs-context panel above it still overlaps it by one
   --ctx-radius with both boxes at full width: the panel's two corner notches
   land on this band, and the seam stays closed. The only thing it takes from
   this file is the gutter that opens the stack beneath it — and #pv-ch2 then
   takes that gutter back by tucking, so in practice the two meet. */
.cs-body > .pv-chapter.is-banded { margin-bottom: var(--plate-gutter); }

/* THE LINE ACROSS THE TOP OF CH2, removed.

   chart-glass.css ends with a seam block written before this file existed:
   the band got `margin-bottom: 0` and the chapter after it got a ::before —
   a FULL-VIEWPORT-WIDTH cream box, pulled up one --ctx-radius (28.8px at
   1440) over the band, with two rounded top corners. It was ch2's plate
   before ch2 had a real one.

   With a real inset sheet in front of it that pseudo-plate is not redundant,
   it is damaging. Measured at 1440: it covered the band's last 11.5px across
   the whole viewport, so the dark ground survived only in the two corner
   notches at x < 20 and x > 1420 — which reads exactly as the user described
   it, a hairline running the full width of the screen with the sheet's
   corners bitten out of it.

   `content: none` takes the pseudo-element away entirely. The gesture it was
   making is not lost: ch2 carries .is-tucked, so the REAL sheet now overlaps
   the band by one radius, and because that sheet is inset the dark ground
   runs continuously around and beside its corners instead of being reduced
   to a sliver. Same seam, one box instead of two.

   Left in chart-glass.css rather than deleted from it: without this file ch2
   has no plate of its own and that block is doing useful work again.

   SPECIFICITY, because it is not what it looks like. `.cs-body
   .pv-chapter.is-banded + .pv-chapter` reads like three classes and is FOUR
   — .pv-chapter.is-banded is two on one element, and the sibling .pv-chapter
   is a fourth. It therefore beats the generic .is-tucked rule (0,3,0)
   whatever the file order, which is exactly what it did on the first pass:
   ch2's tuck measured 0 and its top padding came out at 75px rather than
   --plate-pad-block. Both are restated here at 0,5,0. */
.cs-body .pv-chapter.is-banded + .pv-chapter::before { content: none; }

.cs-body .pv-chapter.is-banded + .pv-chapter.cs-plate {
  margin-top: calc(-1 * var(--plate-tuck, var(--plate-radius)));
  padding-top: var(--plate-pad-block);
}

/* ---------- figures inside a sheet ----------
   .cs-body .pv-fig bleeds to one --wrap-gutter of the VIEWPORT, which on an
   inset sheet is past the sheet it is sitting on. Re-anchored to the sheet: a
   figure stops one wrap-gutter inside the sheet's edge, so it reads as
   printed on the sheet rather than as running off it. On a bleeding sheet
   --plate-edge is 0 and this resolves to exactly the page's original rule.
   Named figures rather than a blanket .pv-fig, because .cs-chart
   (margin-inline: 0 inside the band) and .cs-sheet (its own rule below) must
   keep the widths they already have. */
.cs-body > .cs-plate .pv-fig.cs-trio,
.cs-body > .cs-plate .pv-fig.cs-live,
.cs-body > .cs-plate .pv-fig.cs-offer {
  margin-inline: max(
    calc(-1 * var(--cs-bleed, 90px)),
    calc(50% - 50vw + var(--plate-edge) + var(--wrap-gutter))
  );
}

/* ---------- the twenty-one step sheet ----------
   Still the widest thing in the article, now bounded by the gutter instead of
   the viewport. The SAFETY CONTRACT in steps.css is untouched: nothing here
   is scroll-driven, nothing here is a @property, and no rule below can make a
   plate invisible. overflow is deliberately NOT set — .pv-flow already runs
   `overflow-x: clip` (chosen over `hidden` precisely so the sticky head keeps
   the page as its scrollport), and adding a clip here would risk that.

   margin-bottom: 0 and the z-index are the tuck: the statement below slides
   up under this sheet, so this one has to paint on top of it.

   REV 6: THE PLATE IS THE WRAPPER, NOT THE FIGURE. The sheet now holds two
   things — the mosaic figure and the four-step band that answers it — so the
   .cs-plate classes moved to a .cs-sheet-stack div around the pair and the
   <figure> went back to being a plain figure with its own caption.

   The margin-inline restatement that used to be here is gone with it, and
   that is worth a line because it was not redundant: steps.css sets
   `.cs-body .pv-fig.cs-sheet { margin-inline: calc(50% - 50vw) }` at (0,3,0),
   which out-specifies the primitive's (0,2,0) whatever the file order, so the
   figure-as-plate had to restate its own margin at (0,4,0) to win. A div is
   not a .pv-fig, so nothing reaches the wrapper but the primitive and this.

   THE FOOT. This was 0 for one revision, on the reasoning that the sheet
   closed on the band and the band brought its own bottom padding. That was
   wrong twice over. The band's padding is INSIDE its card — it is the space
   under the caption, not the space under the card — so with 0 here the card
   ran flush into the sheet's bottom edge and the sheet read as a crop rather
   than as a frame. And the figcaption that used to bring a foot of its own
   has since been deleted, so nothing was left holding the bottom open.

   THE VALUE IS THE ONE ALREADY IN PLAY. clamp(48px, 5vw, 88px) is the
   breathing room between the mosaic and the band — the gap ABOVE the card —
   so using it again below makes the card sit centred in its own air rather
   than pushed against the foot. One token, both sides, and the number is
   the page's rather than a new one. 80px at 1600, 48px at a phone.

   It does not stack with the card's own bottom padding: that padding is
   between the band's caption and the card's edge, and this is between the
   card's edge and the sheet's. Measured, the reader sees one measure of air
   below the card, not two summed. */
.cs-body > .cs-sheet-stack.cs-plate {
  margin-block: 0 0;
  z-index: 1;
  /* the mosaic runs to the sheet's edge less a hairline of air, so the outer
     plates are not touching the rounded corner */
  padding: 0 var(--sheet-pad) clamp(48px, 5vw, 88px);
}

/* THE FIGURE, back inside a box. steps.css bleeds it to the viewport
   (`margin-inline: calc(50% - 50vw)`) and gives it 72-150px of block margin,
   both written for a figure that was a direct child of the 1060 measure.
   Inside the wrapper that 50% is half the SHEET, so the bleed would push the
   mosaic out past the sheet it is printed on. (0,4,0) against steps.css's
   (0,3,0). */
.cs-body > .cs-sheet-stack > .pv-fig.cs-sheet {
  margin-inline: 0;
  margin-block: 0;
}

/* THE ARROW'S ROOM.

   The ink arrow is absolutely positioned off .cs-trio-row with a fixed
   height of `--cs-chapter-gap + 314px`, and preserveAspectRatio="none", so
   its whole geometry is that box. It was drawn to land its head about 6px
   above the first line of the sentence below, and it crossed a
   --cs-chapter-gap (300px) of page ground to get there.

   The stack replaced that 300px with --plate-pad-block + --plate-gutter,
   which is 111px at 1440 — measured, the head overshot the sentence by
   183px and finished level with plate 01. Rather than shorten the arrow to
   fit (which loses the long descent that is the whole gesture), the room it
   lost is given back HERE, above the sentence and inside the sheet, so the
   total run from the row to the first line is exactly what it was and the
   arrow's own rule in before.css needs no change at all.

   Reads --cs-chapter-gap on purpose: the deficit IS the difference between
   the old chapter gap and the new sheet padding, so this stays correct if
   either number is retuned. Below 761px the arrow is display: none
   (before.css), so there is no room to reserve. */
/* SPECIFICITY, and it bit again in rev 6. before.css sets this element's
   padding with `.cs-body .pv-fig.cs-sheet .cs-sheet-lead`, which reads like
   three classes and is FOUR — .pv-fig.cs-sheet is two on one element. The
   original override here was `.cs-body > .pv-fig.cs-sheet.cs-plate
   .cs-sheet-lead`, five classes, and it won. Rev 6 moved .cs-plate on to the
   wrapper and re-pointed this at `.cs-body > .cs-sheet-stack .cs-sheet-lead`,
   which is THREE, so before.css's flat 194px started winning instead.

   Measured with that: the room came out at 194px against the 370.8px the
   arrow is drawn for, and the arrowhead finished 169.29px BELOW the top of
   the sentence at 1600 — through the line rather than above it. Mobile was
   never affected, because arrow.js measures the run and sizes the box to
   whatever it finds; only the desktop branch, which trusts the CSS, broke.

   The path back through the figure restores the count to five. */
@media (min-width: 761px) {
  .cs-body > .cs-sheet-stack > .pv-fig.cs-sheet .cs-sheet-lead {
    padding-top: calc(
      194px + var(--cs-chapter-gap, 300px)
      - var(--plate-pad-block) - var(--plate-gutter)
    );
  }
}

/* THE LINE THE ARROW POINTS AT.

   "Twenty-one steps later." is the claim the whole section exists to make,
   so it takes a line of its own and a step of presence. A block-level
   <strong> rather than <br> soup: the emphasis stays semantic, and the break
   and the air around it have something to hang on.

   The step is deliberately small — about 1.28x the sentence it sits in, in
   the SAME family and weight range, with the leading pulled in a touch and
   a third of an em of air above and below. It is a sentence that got louder,
   not a pull quote: no colour of its own, no rule, no second typeface. The
   surrounding lines are unchanged.

   No opacity and no transition here. The whole paragraph fades in together
   when arrow.js adds .is-in, and anything of its own on this child would
   arrive on a different clock or, worse, strand it at opacity 0.

   text-wrap: balance on the paragraph so the closing sentence splits evenly
   instead of dropping a one-word orphan; a block-level child makes the text
   either side of it its own anonymous block, and each balances separately. */
.cs-body .pv-fig.cs-sheet .cs-sheet-lead { text-wrap: balance; }

.cs-body .pv-fig.cs-sheet .cs-sheet-hit {
  display: block;
  font-size: clamp(20px, 1.75vw, 26px);
  font-weight: 700;
  line-height: 1.24;
  letter-spacing: -0.012em;
  margin: 0.34em 0 0.3em;
}

/* THE STICKY HEAD'S OPAQUE GROUND, narrowed to where a sticky head can
   actually be.

   This was `.cs-body > .cs-plate .cs-shortcut-head`, which paints any head
   inside any sheet with that sheet's ground. Rev 6's note called it a no-op,
   and it was one for exactly as long as the band sat on the recessed sheet
   and --plate-ground happened to be the colour underneath it. Rev 7 put the
   band on a cream card, and the rule immediately became visible: a grey
   --surface rectangle painted behind the title, stopping at the column's
   edge, on top of the card. Caught in a screenshot, not in a number.

   The reason the declaration exists is raf/motion/steps.css, which sticks a
   .cs-shortcut-head at top: 0 when one sits inside .pv-fig.cs-sheet — and a
   sticky head there needs to be opaque, because the mosaic's plates pass
   under it. So it is scoped to that case and only that case. The band's head
   is not sticky and is not over anything; it takes the card it is printed on.

   Nothing carries this today (the mosaic has a .cs-sheet-lead, not a head).
   It is kept rather than deleted because it is half of a working pair with
   steps.css, and it can no longer reach anything it should not. */
.cs-body > .cs-sheet-stack > .pv-fig.cs-sheet .cs-shortcut-head {
  background: var(--plate-ground);
}

/* ---------- the statement ----------
   A dark sheet with no field: no .cs-scene, and none of the ::before core
   glow or ::after grain, which stay scoped to .is-banded.

   Three moves, all requested. NARROWER: 10% taken off each side, so the
   sheet is 80% of the width the stack uses. It is centred, which is the only
   alignment that reads under a full-width sheet — flushing it to one side
   would make it look like a mistake rather than a smaller sheet.
   TUCKED: pulled up past its own top corners so the sheet above covers them,
   and put below that sheet in paint order. It reads as a second sheet slid
   out from under the first rather than as an independent card.
   SHORTER: it was a full 100svh. Now it sizes to its content over a 62svh
   floor, so the finding still owns most of a screen but the reader is not
   scrolling past a third of a viewport of empty ground to leave it. */
.cs-body > .cs-statement.cs-plate {
  --quote-side: calc(0.1 * (100vw - 2 * var(--plate-gutter)));
  --plate-tuck: calc(var(--plate-radius) + var(--plate-gutter));
  margin-inline: calc(50% - 50vw + var(--plate-edge) + var(--quote-side));
  z-index: 0;
  height: auto;
}

.cs-body > .cs-statement.cs-plate .cs-statement-pin {
  background: transparent;
  border-radius: inherit;
}

/* Above the phone breakpoint only. Under 800px styles-sidebar.css already
   sizes this section to its content and switches the pin to display: block —
   a considered decision about 204px of type floating in an 812px box — and
   nothing here should reopen it. A phone also has no width to give away, so
   the 20% inset above is left to apply on its own. */
@media (min-width: 801px) {
  .cs-body > .cs-statement.cs-plate .cs-statement-pin {
    height: auto;
    min-height: 62svh;
    /* the pin's own `max(gutter, 50vw - 46vw)` was measured against a
       full-width section; inside a sheet that is already 20% narrower it
       would take another 115px off the line. The inset is the measure now. */
    padding: clamp(56px, 7vh, 96px) var(--wrap-gutter);
  }
}

/* ============================================================
   CH3 — A PANEL WITH A CARD ON IT

   The chapter is a full-width recessed panel that sits ON the statement
   above it, and its reading matter lives in a card on that panel. The
   four-step band stays on the panel itself, below the card, so the section
   reads as one thing with a footer rather than as two stacked cards — which
   is what "the band belongs to ch3" has to mean once ch3 has an inside.

   THE TUCK IS TWO RADII, not one. Every other tuck on this page joins two
   sheets of the same width, where one radius is enough to hide the corners.
   This one lands a full-width panel on a sheet 20% narrower than the stack,
   so the overlap has to read as depth from a long way inside the edge as
   well as at the corners. With the hairline below it, the panel reads as
   laid over the statement rather than as the next thing along.

   z-index 2: above the statement (0) and the twenty-one step sheet (1),
   below chapter two (4), which is lifted so its ink arrow can cross into
   the sheet. */
.cs-body > .cs-plate.is-panel {
  --plate-tuck: calc(2 * var(--plate-radius));
  z-index: 2;
  /* the card and the band both set their own width against --panel-w, so
     the panel is a container and not a column */
  columns: auto;
  column-count: auto;
}

/* THE CARD.

   "White" is --bg-cream, the page's own paper, not #fff. Two reasons. It is
   the ground every other piece of reading matter on this page sits on, so
   the card reads as the article surfacing through the panel rather than as a
   fourth colour; and it is the only choice that survives the theme, because
   --bg-cream is #151514 in dark mode while a literal #fff would stay white
   and become the brightest object on a near-black page.

   In dark mode the card is therefore DARKER than the panel it sits on, which
   is the wrong direction for "raised" on its own — so it carries the same
   three-layer edge the sheets do. The hairline and the contact shadow are
   what say raised; the tone is free to go either way underneath them.

   Width is --panel-w: the same column as the band below it and as ch4's
   split. Percentage margins resolve against the panel's content box, so
   setting the width and splitting the remainder centres it whatever that
   box turns out to be — the move .cs-split already makes. */
.cs-panel-card {
  column-span: all;
  width: var(--panel-w);
  margin-inline: calc((100% - var(--panel-w)) / 2);
  padding: clamp(40px, 4.5vw, 76px) clamp(24px, 3.2vw, 56px);
  border-radius: var(--plate-radius);
  background: var(--bg-cream);
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 1px 2px rgba(0, 0, 0, 0.04),
    0 24px 48px -34px rgba(0, 0, 0, 0.24);

  /* the reading column moves in here with the prose; same two-column
     newsprint measure the chapter used to hold itself */
  columns: 2 380px;
  column-gap: 64px;
}

.dark-mode .cs-panel-card {
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 20px 44px -36px rgba(0, 0, 0, 0.9);
}

.cs-panel-card > h2,
.cs-panel-card > .pv-fig { column-span: all; }

/* the live embed is measured by the card now, not by the viewport: the
   named-figure bleed above would run it off both sides of the card */
.cs-body > .cs-plate .cs-panel-card .pv-fig.cs-live {
  width: auto;
  margin-inline: 0;
}

/* ---------- the four-step band ----------
   REV 6: it lives in the mosaic's sheet, directly under the twenty-one plates
   it answers. The section's argument is that twenty-one steps is too many and
   this is the payoff; two sections apart they were two claims, adjacent they
   are one before-and-after.

   THREE THINGS THE OLD RULE NEEDED AND THIS ONE DOES NOT.
   `column-span: all` was there because #pv-ch3 is a multi-column container
   and the band had to leave the column flow. A .cs-sheet-stack div is not a
   column container, so there is no flow to leave.
   `width: 100vw` and `margin-inline: calc(50% - 50vw)` were the full-bleed
   pair. The band is CONTAINED now — it is as wide as the sheet's content box
   and no wider — so `width: auto` and a zero inline margin are the whole of
   it. The collapse those two guarded against cannot happen here: the sheet's
   width does not depend on this element's parent measure.

   No border-block and no ground of its own: it is part of the sheet it sits
   on, and a rule across it would divide the sheet from its own payoff.

   THE BREATHING ROOM the brief asks for is the top margin, and it is not a
   new number: clamp(48px, 5vw, 88px) is the value the band already carried
   above itself inside #pv-ch3, and the same clamp the coda used for the same
   job. 80px at 1600, on top of the band's own clamp(40px, 4vw, 64px) of head
   padding, so the caption under the mosaic and the band's title stand about
   144px apart at 1600 and 112px at 620.

   (0,3,0) — .cs-body, .cs-sheet-stack, .cs-shortcut — which beats the base
   .cs-shortcut (0,1,0) without relying on the file order that used to carry
   it. (An earlier version of this note said 0,4,0 and miscounted; the
   conclusion was right and the arithmetic was not.)

   REV 7: IT IS A CARD NOW, on the page's own paper.
   The band sat directly on the sheet, which is .is-recessed. It is asked to
   have a container of its own in the page background, and the page already
   has exactly that object one section later: .cs-panel-card, --bg-cream on a
   recessed panel, one --plate-radius, and the primitive's three-layer edge.
   This takes that surface rather than inventing a second one, so the page has
   one pattern for "reading matter lifted off the ground it sits on".

   THE WIDTH IS DERIVED, not chosen, and this is the part that keeps the
   DELTA = 0 lock intact. The container is the column plus its own padding:
       width = --band-col + 2 * --band-pad
   so its CONTENT box is exactly --band-col, the row inside still takes
   --band-col as its width, and .cs-split still takes the same token. Nothing
   about the row's box changes; a card was drawn around it.

   min(100%, ...) is a floor, not a design decision: below about 700px
   --band-col is already --stack-w less two --wrap-gutters and --band-pad is
   at its 24px floor, so the two expressions meet and the container lands
   flush inside the sheet's own padding. That is the right answer on a phone
   in any case — a second inset inside the first is what mobile.css spent the
   coda's --nest-pad note arguing against — and the min() guarantees it can
   never resolve wider than the sheet. */
.cs-body > .cs-sheet-stack > .cs-shortcut {
  width: min(100%, calc(var(--band-col) + 2 * var(--band-pad)));
  margin: clamp(48px, 5vw, 88px) auto 0;
  /* the band's own vertical rhythm, the card's horizontal inset */
  padding: clamp(40px, 4vw, 64px) var(--band-pad) clamp(32px, 3.2vw, 48px);
  border-block: 0;
  border-radius: var(--plate-radius);
  background: var(--bg-cream);
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 1px 2px rgba(0, 0, 0, 0.04),
    0 24px 48px -34px rgba(0, 0, 0, 0.24);
}

/* Same reason the sheets have a dark-mode edge of their own: a cast shadow on
   a near-black page is invisible, and the hairline carries the boundary. This
   is .cs-panel-card's dark rule, for the same object. */
.dark-mode .cs-body > .cs-sheet-stack > .cs-shortcut {
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 20px 44px -36px rgba(0, 0, 0, 0.9);
}

/* THE BAND'S COLUMN, from the token rather than from a percentage.

   This was `padding-inline: max(wrap-gutter, (100% - band-w)/2)`, which
   centres the column and floors it on the gutter — and which is exactly what
   --band-col says in a token. Both forms give the same column, but only one
   of them gives it to #pv-ch4's split as the SAME LENGTH.

   Measured with the percentage form: the row's content came out 779.406 at
   900 against the split's 779.398, a 0.008px disagreement. It is smaller than
   one Chrome LayoutUnit (1/64px) and could never render, but it is real, and
   it comes from --sheet-pad's vw term being rounded per side inside the used
   padding on one path and carried unrounded through the calc() on the other.
   Two derivations of one number will always find a way to disagree.

   So the row takes --band-col as its width and centres itself, and .cs-split
   takes the same token. One length, resolved once, used twice: the match is
   exact by construction rather than by arithmetic that happens to agree.
   The gutter floor is not lost — it is inside --band-col's min(). */
.cs-body .cs-shortcut .cs-shortcut-head,
.cs-body .cs-shortcut .cs-shortcut-row {
  width: var(--band-col);
  margin-inline: auto;
  padding-inline: 0;
}

/* THE CAPTION'S MEASURE, off the viewport and on to its own box.

   styles-sidebar.css pads this from the VIEWPORT (`50vw - 620px`) and the
   previous version of this rule kept that form at the new column width. That
   was correct while the band was full bleed: its box started at viewport x=0,
   so a viewport-measured inset and the row's own percentage inset landed on
   the same pixel. The band is inset by one --plate-gutter plus one
   --sheet-pad now, so the two forms differ by exactly that — 30.4px at 1600 —
   and the caption sat that far right of the row above it.

   Written from --band-col instead, the same token the row and the split take,
   so all three describe one column and cannot drift again. The caption keeps
   a PADDING rather than a width because it has to start on the column's left
   edge and wrap at its own 70ch, not be a centred 70ch box.

   The content-box patch in styles-sidebar.css stays and stays correct: it
   measures the 70ch on the text and lets the centring padding sit outside it.
   It is also no longer load-bearing against overflow: 2 * padding is exactly
   (box - --band-col), so the element resolves to 70ch + box - band-col, which
   is narrower than the box at every viewport. The viewport form did not have
   that property, which is what the patch was there to survive. */
.cs-body .cs-shortcut .cs-shortcut-cap {
  padding: 0 calc((100% - var(--band-col)) / 2);
}

/* THE BAND'S TYPE — the restatement is GONE, and that is the point.

   Four values (the 56px display title, the 11px caps sub, the 13px caption,
   and the right alignment) had to be copied out of styles-sidebar.css here at
   (0,4,0), because moving the band into #pv-ch3 put its three <p> elements
   under `.cs-body .pv-chapter p` (0,3,0), which outranks every
   `.cs-shortcut-*` rule (0,1,0) and rendered all three at 18px in --text-body.

   The band is in a <div> inside the article now, not in a .pv-chapter, so
   that selector does not reach it and the base rules govern again on their
   own. The duplication is deleted rather than left as a no-op: it was the one
   duplication in this file and it only ever existed to pay for the nesting.
   Measured after the move — title 60px, sub 11px, count 34px, caption 13px,
   step label 11px — which is what styles-sidebar.css asks for.

   Nothing in raf/motion/steps.css reaches it either. That file styles
   `.cs-body .pv-fig.cs-sheet .cs-shortcut-head/-title/-count` for an older
   arrangement in which the band lived INSIDE this figure, and those rules
   would have taken the title to a 32px cap and made the head sticky. The band
   is the figure's SIBLING inside the wrapper, not its descendant, so they
   stay dormant exactly as they were before this change. */

/* ---------- ch4: the split ----------
   --split-w is matched to .cs-shortcut-row's content width and the two must
   stay equal to the pixel. That was --panel-w while the band was a full-bleed
   box inside #pv-ch3; the band is inside the mosaic's inset sheet now, so the
   room it insets its --band-w column into is --stack-w rather than 100vw and
   the matching number is --band-col. Above about 1150px the two are the same
   value (--band-w, uncapped) and nothing moves; below it they diverge, and
   this follows the band rather than the viewport.

   NOT a change to --band-w, which is untouched at 1100px. */
.cs-body > .cs-plate .cs-split { --split-w: var(--band-col); }

/* ---------- the "Try it" tag ----------
   It used to sit inside .pv-fig-num, which was display: block and put the tag
   on a line of its own above the caption. The number is gone; the tag keeps
   that line. width: max-content because an inline-block cannot take a line to
   itself, and a bare display: block would stretch the pill to the caption's
   full measure. `margin-inline: auto` because the only caption that carries
   this tag is centred (.cs-offer .pv-fig-cap, offer.css) — a shrink-wrapped
   block ignores text-align and would otherwise sit hard left under centred
   prose. */
.pv-fig-cap > .cs-offer-tag {
  display: block;
  width: max-content;
  margin: 0 auto 7px;
}

/* ---------- phones ----------
   No breakpoint of its own beyond the two above. The gutter is at its 8px
   floor and the radius at its 14px floor below about 670px, which is the
   right proportion for a 390px screen — any more and the sheets read as
   chiclets — and every other value here is already a clamp. */

/* ============================================================
   REV 4 — four sheets change width, two lose their edge, and the
   coda moves inside the chapter it belongs to.
   ============================================================ */

/* ---------- CH2 goes full width ----------
   One class in the markup: .is-bleed. No rule is needed here, and that is
   the point of the modifier — --plate-edge drops to 0 and the padding
   formula puts the same 1060 measure back under the writing.

   THE SEAM, restated, because the reason for the tuck has changed twice.
   .is-tucked was on ch2 so that its ROUNDED CORNERS had dark band beside
   them to cut into; that was the fix for the hairline-across-the-screen
   defect. Rev 4's .is-bleed set border-radius: 0 and removed the corners,
   leaving the tuck a smaller, duller job: ch1's bottom and ch2's top are
   both fractional pixel positions (at 1440 the band ends on .48 of a
   pixel), and two full-width boxes that merely ABUT can leave a half-pixel
   of page ground showing between them — which would be a new version of
   exactly the defect this seam already had once. A 27.2px overlap makes
   that impossible. It costs the last 27.2px of the band's bottom padding,
   which is padding and not content.

   REV 5 gives the corners back (.is-crowned), so the tuck is doing both
   jobs again. The important part is that the overlap is one radius and the
   radius is one radius, so the quarter-circle at each end closes ON the
   dark band and nowhere else: the notches reveal ch1, not page ground. The
   original defect was a full-viewport CREAM box painted over the band's
   last 11.5px with its corners bitten out; here the top of the sheet IS the
   sheet, there is no second box, and above its top edge there is dark band
   at every x. Verified by pixel scan across the seam in both themes.

   Nothing else is required: the tucked sibling rule already takes ch1's
   bottom gutter away, so dark meets cream on one straight horizontal edge
   with no gap and no third colour. */

/* ---------- CH3's corners ----------
   The diagnosis first, because the previous note in this file claimed the
   corners were already round and they were not. Measured at 1600, all four
   of ch3's radii computed to 0px. .is-capped had indeed been dropped, but
   ch3 also carries .is-bleed, and .is-bleed's whole job is
   `--plate-edge: 0; border-radius: 0` — a full-bleed sheet has square
   corners by definition in this file. Removing .is-capped could not
   possibly have rounded anything.

   So the radius is put back explicitly, at (0,4,0) so it beats .is-bleed's
   (0,3,0) whatever the order ends up being. The width is untouched: ch3
   stays edge to edge.

   AND THE CORNERS ARE VISIBLE, which is the part that does not follow
   automatically. A rounded corner shows only what is behind it, and ch3's
   corners sit ON the viewport edge, so the thing behind them is page
   ground. Both ends check out:
     * the head is tucked two radii over the statement, but the statement is
       20% narrower than the stack, so at x < 175 (1600) there is page ground
       above ch3's top edge, not statement;
     * the foot is followed by ch4, which is inset and NOT tucked, so ch3's
       bottom gutter is page ground too.
   The tonal step is small on purpose — the recessed ground is one step off
   the paper — so the curve is carried mostly by the inset hairline, which
   follows the radius and is the reason ch3 keeps its edge while ch4 and ch6
   lose theirs. */
.cs-body > .cs-plate.is-bleed.is-panel { border-radius: var(--plate-radius); }

/* ---------- CH4 and CH6 lose the stroke ----------
   .is-edgeless in the markup, which is already declared for both themes
   above. They keep their grounds and their place in the alternation, so the
   seams either side of them still read from the change of tone. Both are
   cream sheets against recessed neighbours (ch3 and ch5 respectively), so
   there is a tonal edge at every boundary without a line drawn on it. */

/* ---------- CH5 goes full width ----------
   .is-bleed again. The one thing to check was the figure rule above: the
   named-figure re-anchor resolves --plate-edge to 0px here, so
   `50% - 50vw + 0 + wrap-gutter` is -246px at 1600, the max() floor of
   -90px wins, and .cs-offer lands on exactly the -90px bleed the page gave
   it before plates.css existed. Nothing to change. */

/* ============================================================
   REV 5 — three sheets get their corners and lose their cast
   shadow, and the coda comes back out to the foot of the page.
   ============================================================ */

/* ---------- .is-crowned: a bleeding sheet with a rounded head ----------
   .is-bleed's contract is `--plate-edge: 0; border-radius: 0`, so a
   full-width sheet has square corners by definition in this file. This puts
   the radius back on the TOP two only.

   NOT .is-capped, which says the same thing about the head and then couples
   it to `margin-bottom: 0` — a square foot only reads as a join if the next
   sheet arrives on it. This modifier is radius and nothing else: it touches
   no margin, so adding or removing it cannot move anything.

   0,4,0 against .is-bleed's 0,3,0, so file order is not load-bearing. */
.cs-body > .cs-plate.is-bleed.is-crowned {
  border-radius: var(--plate-radius) var(--plate-radius) 0 0;
}

/* ---------- .is-rounded: a bleeding sheet with all four ----------
   The same restore on every corner, for a bleeding sheet with page ground
   above and below it. Same 0,4,0. .is-panel already carries its own copy of
   this because it is doing more than a radius; this is the plain version. */
.cs-body > .cs-plate.is-bleed.is-rounded { border-radius: var(--plate-radius); }

/* ---------- .is-flat: the ring without the cast ----------
   The primitive paints three layers — an inset hairline, a tight contact
   shadow and a wide ambient one. .is-edgeless removes all three, which on a
   sheet whose ground differs from the page leaves nothing describing the
   edge at all. This removes only the two CAST layers and keeps the hairline,
   so the sheet still has a drawn boundary and simply stops floating.

   Both themes are restated because the dark rule is a separate declaration
   at 0,3,0; this is 0,4,0 there and 0,3,0 in light, so it wins either way.
   Declared after .is-edgeless so that, on the impossible day something
   carries both, "no edge" is the answer. */
.cs-body > .cs-plate.is-flat,
.dark-mode .cs-body > .cs-plate.is-flat {
  box-shadow: inset 0 0 0 1px var(--rule-soft);
}

/* ============================================================
   CH7 — the coda, back out of CH6 and on to the foot of the page

   Rev 4 put "What we didn't fix" INSIDE #pv-ch6 as an inset block on the
   reasoning that it is the judgement on that chapter rather than a section
   beside it. That is reversed here on request: it is a top-level sheet
   again, full width, and it is the last thing in the article so that it
   meets the prev/next navigation with nothing between them.

   .is-nested and its two rules are GONE rather than left dead — nothing
   carries the class any more and --nest-pad had exactly one user. The
   companion --nest-pad override in mobile.css goes with it.

   .is-flush is what makes "directly on top of" true. nav.cs-next is a
   sibling of .cs-body inside main with no margin of its own, so the only
   things between the two are this sheet's bottom margin and the closing
   gutter .cs-body pads itself with. Both are taken away here, together,
   because either one alone leaves a stripe of page ground at the seam.

   :has() rather than a second class on .cs-body: the article should not
   have to be told what its own last child is doing.

   The foot stays SQUARE (no .is-rounded). A rounded corner reveals what is
   behind it, and behind this one is the navigation's #0a0a08 — rounding it
   would cut two notches of near-black into the sheet, which is the corner
   defect this file spent rev 3 removing from the ch1/ch2 seam. Square is
   the honest join when two full-width blocks abut.
   ============================================================ */
.cs-body > .cs-plate.is-flush { margin-bottom: 0; }
.cs-body:has(> .cs-plate.is-flush:last-child) { padding-bottom: 0; }

/* ============================================================
   REV 8 — THE PROTOTYPE, IN A PHONE

   The elevated experience is a working mobile prototype, and it is embedded
   in #pv-ch6 immediately after the paragraph that names the idea it shows.

   WHY A DEVICE AND NOT A BROWSER. #pv-ch3's .cs-live wraps its embed in
   browser chrome with a URL bar, because what it frames is a web page. This
   one is a phone experience, so it gets a phone. The geometry is an iPhone
   15 Pro — a 393 x 852 screen inside a 12px bezel, 417 x 876 overall — which
   is the frame the prototype was designed against.

   THE CHROME IS REBUILT IN THE PAGE'S OWN TERMS rather than pasted in from
   the prototype's old wrapper page: the bezel takes --rule and --surface
   instead of that file's hard-coded #1c1c1e greys, so the device sits in the
   theme like everything else and inverts with it. Only the proportions are
   borrowed, because those are the device's and not a taste call.

   COLUMN FLOW. #pv-ch6 is `columns: 2 380px`, so this needs column-span: all
   or it is flowed into one 498px column with prose continuing beside it. The
   same move .pv-slots and .cs-shortcut already make.
   ============================================================ */
.cs-body > .cs-plate .pv-fig.cs-proto {
  --proto-screen-w: 393px;
  /* A REAL PHONE SCREEN. iPhone 15 Pro, which is the frame the prototype was
     designed against.

     An earlier pass set this to 1120px — the height of the home document —
     so the iframe had no scroller and could not swallow the wheel. That
     bought a clean scroll-past and cost the thing the embed is for: the
     prototype is a flow, its later steps and its bottom sheets are taller
     than one screen, and a reader who cannot scroll sees one screen of it.
     The wheel is handled properly in raf/proto.js instead, which is where
     that problem belongs. */
  --proto-screen-h: 852px;
  --proto-bezel: 12px;
  --proto-outer-w: calc(var(--proto-screen-w) + 2 * var(--proto-bezel));
  --proto-outer-h: calc(var(--proto-screen-h) + 2 * var(--proto-bezel));
  --proto-scale: 1;

  column-span: all;
  /* not the named-figure bleed above: a phone is not a wide figure, it is a
     centred object, and the caption below it keeps the reading measure */
  width: auto;
  margin-inline: 0;
  margin-block: clamp(56px, 6vw, 96px);
}

/* THE DEVICE, IN TWO BOXES, and it has to be two.

   A transform never affects layout, so a 417px device scaled to 0.58 still
   occupies 417px of a 314px figure — measured, that put document.scrollWidth
   at 458 against a 390 viewport. And .cs-proto-device cannot simply carry the
   transform anyway: styles-sidebar.css runs
   `.cs-fx .cs-body .pv-fig > :first-child { transform: translateY(...) }`
   for the page's figure parallax, at (0,3,0) with an element, and the device
   WAS that first child — so the parallax overwrote scale() outright and the
   device rendered full size. This is the same --plx rule arrow.js documents.

   So the two jobs are given two elements. .cs-proto-stage is the layout box:
   it is the scaled size, it centres, and being the figure's first child it
   takes the page's parallax like every other figure. .cs-proto-device inside
   it is the true 417 x 876 device and carries only the scale, from its top
   left so the two boxes share an origin. Neither fights the other. */
/* THE DEVICE.
   transform, not width, is what shrinks it below. The iframe has to keep its
   393 CSS pixels whatever the page is doing, because that is the width the
   prototype's own media queries are written against — sizing the frame down
   would re-lay-out the design rather than show it smaller. A transform can
   never affect layout, so the height the box occupies is restated here in the
   same scale and the page closes around it correctly. */
.cs-proto-stage {
  width: calc(var(--proto-outer-w) * var(--proto-scale));
  height: calc(var(--proto-outer-h) * var(--proto-scale));
  margin: 0 auto;
}

.cs-proto-device {
  position: relative;
  width: var(--proto-outer-w);
  height: var(--proto-outer-h);
  padding: var(--proto-bezel);
  border-radius: 55px;
  background: var(--surface);
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 1px 2px rgba(0, 0, 0, 0.04),
    0 30px 60px -34px rgba(0, 0, 0, 0.34);
  transform: scale(var(--proto-scale));
  transform-origin: top left;
}

.dark-mode .cs-proto-device {
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 24px 52px -36px rgba(0, 0, 0, 0.9);
}

.cs-proto-screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 44px;
  overflow: hidden;
  background: #000;
}

.cs-proto-screen iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 44px;
}

/* The island is decoration over the prototype's own status bar. */
.cs-proto-island {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  width: 126px;
  height: 37px;
  border-radius: 20px;
  background: #000;
  z-index: 2;
  pointer-events: none;
}

/* The four hardware buttons, in the bezel's own tone one step up so they read
   as relief rather than as drawn lines. */
.cs-proto-btn {
  position: absolute;
  width: 4px;
  background: var(--rule);
  pointer-events: none;
}

.cs-proto-btn.is-power    { right: -3px; top: 180px; height: 96px; border-radius: 0 2px 2px 0; }
.cs-proto-btn.is-silent   { left: -3px;  top: 120px; height: 20px; border-radius: 2px 0 0 2px; }
.cs-proto-btn.is-vol-up   { left: -3px;  top: 160px; height: 36px; border-radius: 2px 0 0 2px; }
.cs-proto-btn.is-vol-down { left: -3px;  top: 210px; height: 36px; border-radius: 2px 0 0 2px; }

/* The caption goes back on the reading measure — the device above it is
   417px wide and the caption is prose. */
.cs-body > .cs-plate .pv-fig.cs-proto .pv-fig-cap {
  max-width: 62ch;
  margin-inline: auto;
  text-align: center;
}

/* ---------- THE SCALE, and the arithmetic behind the two numbers ----------
   The device is 417px wide. The room this figure has is the viewport less
   the sheet's gutter and the chapter's own padding:
       available = 100vw - 2 * --plate-gutter - 2 * --wrap-gutter
   Below 1000px --plate-gutter is 14px (mobile.css) and --wrap-gutter is 24px
   at 800 and under, so available = 100vw - 76px. That first falls under
   417px at about 493px of viewport, which is where the device stops fitting
   and the first step begins.

   CSS cannot divide a length by a length to produce a number — scale() needs
   a <number>, and `calc(400px / 417px)` is not supported — so the ratio
   cannot be expressed as one fluid expression. Two steps instead, each set
   to the WORST case in its own band so the device can never overflow:
     401-492px : available bottoms out at 325px -> 325/417 = 0.779
     320-400px : available bottoms out at 244px -> 244/417 = 0.585
   Rounded DOWN, to 0.77 and 0.58, so the worst case in each band has a pixel
   in hand rather than landing exactly on the edge. Nothing else is needed:
   .cs-proto-stage derives its own width and height from the same token, so
   the page closes around the scaled device rather than around the full one. */
@media (max-width: 492px) {
  .cs-body > .cs-plate .pv-fig.cs-proto { --proto-scale: 0.77; }
}

@media (max-width: 400px) {
  .cs-body > .cs-plate .pv-fig.cs-proto { --proto-scale: 0.58; }
}
