/* glass-flow — the columns, the spreads, and what is allowed to break out.
 *
 * The type, the measure, the pull quote and the drop cap are
 * glass-reading.css; this sheet is the geometry a page is poured into, and it
 * is the CSS half of two modules: glass-flow.js decides what spans a column,
 * glass-spread.js decides where one screen ends and the next begins. It was
 * split out of glass-reading.css when the spread rule took that file past 800
 * lines — split by responsibility, which is where the seam already was: a
 * reader asking "how wide is a line" and a reader asking "why does the page
 * break here" are asking two different questions.
 *
 * Dark is the surface's law; there is no light branch here or anywhere in the
 * kit (the decision, with its contrast numbers, is in glass-reading.css's own
 * head comment and in docs/design/glass-reading.md).
 */
/* ── THE FLOW ─────────────────────────────────────────────────────────
 * Dan, on a 2000px screenshot of #cassini: "note all the empty space on
 * these pages — it would be better in columns."
 *
 * Three shapes, in this order, each fixing the one before:
 *
 *  1. COLUMNS PER SECTION. A three-line section, or a list, ends its own
 *     little two-column block and the rest of the row is dead space. That is
 *     the empty space Dan pointed at.
 *  2. ONE FLOW PER PAGE. No seams — and no screens either: at 1600px
 *     #cassini ran §1–§12 down a 4191px left column and §13–§24 down the
 *     right, so reading column one to its end meant scrolling four screens
 *     down and four back up.
 *  3. ONE FLOW PER SPREAD, and a spread is one screen. The columns live on
 *     `.spread`; glass-spread.js packs the sections into screens from their
 *     measured heights. Left→right inside a spread, then down.
 *
 * How many columns: `columns: 23rem N` sets N as the CEILING and 23rem as
 * the minimum, so the flow takes as many as the width holds and never one
 * more than fits. The ceiling steps at 1200 / 1800 / 2400px, and each step
 * carries a max-width chosen so that a column can never grow past ~75
 * characters in the middle of its band — 2 columns at 1600px would be 615px
 * (82 characters) without it. That cap is the only place the paper does not
 * take the full width, and what it leaves is a page margin, never a
 * column-wide hole. Measured per width in the report: 56–74 characters
 * across 1200 / 1300 / 1600 / 1800 / 2000 / 2400.
 *
 * `column-fill: balance` levels the last row so a page never ends in one
 * long column beside two empty ones.
 *
 * The masthead and the deck SPAN, because they are the page's opening and
 * not part of its body flow; the flow then balances under them, which is
 * the shape of a spread. */
@media (min-width: 901px) {
  /* THE FLOW IS BROKEN INTO SPREADS, AND A SPREAD IS ONE SCREEN.
   * Lead ruling, 2026-09-07, on the live page at 1600px: "one continuous flow
   * per page" is wrong on a page that SCROLLS. Measured before this rule:
   * #cassini ran §1–§12 down the left column and §13–§24 down the right, each
   * column 4191px — 4.19 viewports — so reading column one to its end meant
   * scrolling down four screens and back up four to start column two.
   * `column-fill: balance` was doing exactly what it is for; the unit it was
   * balancing was the whole page.
   *
   * So the columns move to a `.spread`, one screen's worth of sections packed
   * by glass-spread.js from measured heights, and #doc becomes the stack of
   * screens. Reading order is left→right inside a spread, then down to the
   * next one. The declarations are written once for both boxes, because a
   * spread is the flow — it is the flow of one screen. */
  /* 30rem, NOT 26. A column may be as narrow as this number, and 26rem is
   * 416px — 48 characters, four under the 55-character floor §1 set. So a
   * second column appeared at a 1202px viewport holding prose narrower than
   * the measure it was there to hold. 30rem is 480px and 56 characters, and
   * it is the prose measure's own floor. Measured on /cassini with the graph
   * seated: the 2-column step moves 1202px -> 1330px and the 3-column step
   * 1800px -> 1860px, and no column at any width is under 56 characters. */
  .paper[data-presentation="gloss"] #doc,
  .paper[data-presentation="gloss"] #doc > .spread {
    columns: 30rem var(--rd-cols, 2);
    column-gap: 2.6rem;
    column-rule: 1px solid var(--line);
    column-fill: balance;
    orphans: 2;
    widows: 2;
  }
  .paper[data-presentation="gloss"] #doc { max-width: 1160px; }
  /* …and a spread SPANS #doc's own columns rather than sitting in one, or it
   * would be a nested multicol inside a column. `column-span: all` is how the
   * mast and the media block have always done it.
   *
   * The alternative — taking the columns off #doc with `columns: auto` — was
   * the first shape and it cost the page its own geometry. glass-flow.js asks
   * a box how many columns the width holds; with #doc set to `auto` it has to
   * ask a spread instead, and once a SHORT spread carries `--rd-cols: 1` the
   * answer for the whole page is "one column", so the next pass unpacked it.
   * Measured on /glsl-3d-fractals-mandelbulb at 2000px: 5 spreads to 0, the
   * page back to one 2.79-viewport column. #doc keeps its columns declared,
   * nothing flows in them, and the geometry stays readable. */
  .paper[data-presentation="gloss"] #doc:has(> .spread) { column-rule: none; }
  .paper[data-presentation="gloss"] #doc > .spread { column-span: all; }
  .paper[data-presentation="gloss"] #doc > .spread + .spread { margin-top: 1.7rem; }

  /* MEASURING. A section laid out in a live multicol reports the union of its
   * fragments — the live #cassini's §12 reported 798..4989, which is both
   * columns and the whole page — so glass-spread.js reads the heights with
   * the columns off and every section at the real column width, for exactly
   * one layout read, before anything is painted. */
  .paper[data-presentation="gloss"] #doc.rd-measure,
  .paper[data-presentation="gloss"] #doc.rd-measure > .spread { columns: auto; column-rule: none; }
  .paper[data-presentation="gloss"] #doc.rd-measure .sec { max-width: var(--rd-col, 26rem); }
  .paper[data-presentation="gloss"] #doc > .mast,
  .paper[data-presentation="gloss"] #doc > #av { column-span: all; }

  /* THE DECK DOES NOT SPAN. Spanning the whole opening section was the
   * largest hole on the site — on #moon that section is 753px tall and its
   * lead paragraph is 631px of a 1160px band, so the right half of the page
   * was empty for 694px at 1200, 1600 and 2000, with no spanner anywhere
   * near it. Spanning only the lead PARAGRAPH and centring it moved the
   * hole rather than closing it: at three columns a 631px measure sits
   * inside the middle column and leaves the outer two empty for its whole
   * height (measured 787px on #moon at 2000px). A measure cannot fill a
   * spread, so the deck keeps its lead type and takes its place in the
   * flow like every other paragraph. */

  /* AN INSTRUMENT SPANS; A THING WITH A WIDTH IS MEASURED.
   *
   * Dan's rule is "figures, code blocks, tables, display math and shaders
   * span all columns WHEN THEY ARE WIDER THAN A COLUMN and otherwise sit in
   * the flow". The first shape of this round spanned them unconditionally,
   * and each span closes the columns above it and restarts the flow below —
   * `column-fill: balance` cannot balance across one. Measured before the
   * fix: 67 column-wide holes over fifteen frames, the worst 991px tall,
   * most of them above a 186px formula spanning a 1160px band.
   *
   * So the list here is only the things that have NO width of their own: a
   * shader canvas, the graph, the transcript lattice, a control strip, and
   * the mast. They are as wide as they are given, and a shader in a 415px
   * column is a thumbnail of a thing you were meant to read.
   *
   * Everything else earns `.span-all` from glass-flow.js, which measures it
   * against the real column width on every render and every resize. */
  .paper[data-presentation="gloss"] #doc canvas,
  .paper[data-presentation="gloss"] #doc .viz,
  .paper[data-presentation="gloss"] #doc .lattice,
  .paper[data-presentation="gloss"] #doc .shader-controls,
  .paper[data-presentation="gloss"] #doc .span-all { column-span: all; }
  /* An instrument is capped at its own working size (`.viz` and
   * `canvas.shader` are 860px in glass.html), so on a 1672px spread it does
   * not fill the row it spans. Centred, what is left is two margins; left
   * aligned it was 530px of dead ground down one side. */
  .paper[data-presentation="gloss"] #doc .viz,
  .paper[data-presentation="gloss"] #doc .lattice { margin-inline: auto; }

  /* …and everything that does NOT span is kept whole instead: a formula, a
   * fence or a picture that fits a column is never sliced by a column
   * break. This is the half of the rule the unconditional span hid. */
  .paper[data-presentation="gloss"] #doc div.math,
  .paper[data-presentation="gloss"] #doc blockquote.callout,
  .paper[data-presentation="gloss"] #doc hr.mdhr { break-inside: avoid; }

  /* In the flow the COLUMN is normally the measure — but the column count
   * is what the width HOLDS, so any flow between one column and two falls
   * back to a single column the full width of the paper. Measured: #moon
   * at 1200px with the graph seated is an 872px flow, one column, 115
   * characters to the line. So the measure stays, at 35rem — wider than
   * the widest column this sheet can produce (559px at the 1160px cap), so
   * it never strands a paragraph inside a real column, and it catches the
   * single-column fallback at 74 characters. `margin-inline: auto` makes
   * what is left over two gutters rather than dead space on one side —
   * Dan's own ruling for the single-column case.
   *
   * `:not(.span-all)` is not decoration. The measure applied to EVERY child
   * of a body, and a spanner is a child of a body: a table or a formula that
   * glass-flow.js had correctly judged wider than a column got
   * `column-span: all` and then had its box capped at 35rem and CENTRED
   * inside the full-width row — a narrow thing alone in a spread with the
   * columns either side of it empty for its whole height. Measured on
   * /markov-chains-…: `column-span` computed `all`, the containing block was
   * 1160px, and the box stayed 560px. The measure is for prose; a spanner is
   * not prose. */
  .paper[data-presentation="gloss"] #doc .sec:not(.lead) .body > *:not(.span-all),
  .paper[data-presentation="gloss"] #doc .sec:not(.lead) .body > .fold-body > *:not(.span-all) { max-width: 35rem; margin-inline: auto; }
  /* A HEADING TAKES THE MEASURE TOO. Lead ruling, 2026-09-07: "the single-
   * column fallback's headings hanging ~155px left of the prose is a mistake:
   * headings align with the prose measure in every mode." The body's blocks
   * were capped and centred and the heading was not, so wherever a column is
   * wider than the measure — the 872px single-column fallback at 1200px, and
   * now any column of a SHORT spread, which takes fewer and wider columns —
   * the section had two left edges: the head's and its own prose's. Measured
   * on /cassini at 1200px before this: 156px apart. The head is capped and
   * centred by the same numbers, so the two edges are one edge at every
   * width. Below 35rem the cap does nothing and nothing moves. */
  .paper[data-presentation="gloss"] #doc .sec > h2 { max-width: 35rem; margin-inline: auto; }
  /* …but the DECK spans the flow, so it is the one paragraph that still
   * needs a measure of its own: unchecked it ran 145 characters across a
   * 1600px page. `ch` scales with the font, and the deck is set larger than
   * the body, so 50ch of lead type is ~66 real characters — a little wider
   * than the flow beneath it, which is what a deck is for. */
  .paper[data-presentation="gloss"] #doc .sec.lead .body > p { max-width: 50ch; margin-inline: auto; }

  /* A heading never ends a column with nothing under it, and a picture is
   * never sliced by a column break. */
  .paper[data-presentation="gloss"] #doc .sec > h2,
  .paper[data-presentation="gloss"] #doc h4.mdh,
  .paper[data-presentation="gloss"] #doc h5.mdh { break-after: avoid; }
  .paper[data-presentation="gloss"] #doc figure,
  .paper[data-presentation="gloss"] #doc .tbl-wrap,
  .paper[data-presentation="gloss"] #doc .viz { break-inside: avoid; }
  /* A FENCE IS NOT A PICTURE. `break-inside: avoid` on `pre` was in that
   * list, and a fence taller than the room left in a column cannot honour
   * it: it jumps whole to the next column and strands everything it left
   * behind. Measured on /glsl-3d-fractals-sierpinski-tetrahedron at 1600px,
   * a 40-line fence left 567px of empty first column beside the prose it
   * jumped over. Code is lines; a column break between two of them is what
   * a printed listing has always done, and it is `orphans`/`widows` that
   * keeps it from leaving one line behind. */
  .paper[data-presentation="gloss"] #doc pre { break-inside: auto; orphans: 3; widows: 3; }
  /* A SECTION IS NOT KEPT WHOLE. `break-inside: avoid-column` on short
   * sections was the second hole-maker: a 690px section that does not fit
   * the rest of a column jumps to the next one and leaves everything it
   * left behind empty — measured 694px of nothing beside prose on #moon at
   * 1600px, with no spanner anywhere near it. A section that runs over a
   * column boundary is what a magazine does; the heading rules above are
   * what keep it from stranding a title. */
}
/* one more column, and the room to hold it. The count is a custom property so
 * the step is stated once and INHERITS into every spread — the spreads are the
 * boxes with the columns on them, and a per-step rule for each would be the
 * same number written twice. */
@media (min-width: 1800px) {
  .paper[data-presentation="gloss"] #doc { --rd-cols: 3; max-width: 1740px; }
}
@media (min-width: 2400px) {
  .paper[data-presentation="gloss"] #doc { --rd-cols: 4; max-width: 2320px; }
}
/* NOTHING CAN ESCAPE A TRANSFORM. `.sec` carries the staggered secIn
 * animation (glass.html:279), whose keyframes set `transform`, and an
 * ancestor with a transform is a containing block AND a stacking context.
 * Two things this surface needs are swallowed by it:
 *
 *  - every `column-span: all` inside a section, while the animation runs —
 *    a spread is one page, not a stack of cards arriving one by one;
 *  - the locator's popover, which is positioned against its heading. Trapped
 *    in the section's own stacking context, `z-index: 60` cannot lift it
 *    above the NEXT section, which paints later. Measured on a phone at
 *    375x812: the popover's own text and the following section's paragraph
 *    drawn over each other, both legible, neither readable.
 *
 * The second one is why this rule is no longer inside a 901px media query:
 * the phone has no spread, but it has the popover. This is the "no
 * decorative motion" law paying for itself twice. */
.paper[data-presentation="gloss"] .sec { animation: none; }
