/* ─────────────────────────────────────────────────────────────
   ARIA SGAI Notebook — essay stylesheet
   Loaded after base.css, which supplies the tokens, the page
   frame, and the chrome this sheet builds on. An essay page asks
   for both, in that order:

       styles: /static/base.css, /static/essay.css

   What lives here is the long-form layout. In order, and the order
   is the index — every region carries a ── banner, and no selector
   is declared in two of them:

       Title block          Headings
       Article              Registers without a rail
       Drop cap             Marginalia
       Table of contents
                            Citations
                            Footnotes
                            References

   Everything here is reachable from a page. The playground residue
   that used to close the file — a type trial, a colour picker, a
   status box, a demo article, none of whose markup exists in this
   repository — is gone, along with the section-header vocabulary
   (.section-kicker, .section-q, .section-dek), the reference
   containers (.bibliography, .refs, .refs-list, .rl-group) and the
   two <hr> variants that markdown can't reach, since `---` carries
   no class. /styleguide/ rendered all of it once so the decision to
   drop it could be taken by looking rather than guessing.
   ───────────────────────────────────────────────────────────── */

/* ── Title block ────────────────────────────────────────────── */

/* v.13 — canonical title-block pattern, used on every page.
   Anatomy: essay-meta (small caps row) > doc-title >
   doc-byline (optional) > abstract. Drops the earlier
   grid + gap layout in favour of normal block flow with
   per-element margins, so spacing is tunable without
   touching the parent. */
.title-block {
  display: block;
  padding: 2rem 0 1.5rem;
}

/* Content-width rule: the title-block, like every other
   non-figure block, spans rail + body only — never the full
   page width. At wide widths the block sets up its own grid
   and places each child at intro-span (cols 1 → body-end). */
@media (min-width: 980px) {
.title-block {
    display: grid;
    grid-template-columns: repeat(var(--layout-grid-columns), 1fr);
    column-gap: var(--layout-grid-gap);
    padding: 2rem 0 1.5rem;
  }

.title-block > * { grid-column: 1 / span var(--layout-intro-span); }

}

h1.doc-title {
  font-family: var(--text-family-serif);
  font-weight: 400;
  font-size: clamp(2.4rem, 5.4vw, 3rem);
  line-height: 1.02;
  letter-spacing: -0.012em;
  margin: 0;
  font-feature-settings: "onum", "kern", "liga", "dlig";
}

h1.doc-title em { font-style: italic; }

/* Layout only — the voice comes from .label, which this carries alongside. It
   used to track at 0.12em where the rest of the register tracks at 0.1em; that
   was a drift nobody could see until the nine were rendered side by side. */
.essay-meta {
  margin: 0 0 1rem;
  font-variant-numeric: lining-nums;
  display: flex;
  gap: 1.2rem;
  flex-wrap: wrap;
  align-items: baseline;
}

.essay-meta .tech-dots {
  font-size: 0.6em;
  letter-spacing: 0.18em;
  color: var(--color-ink-mute);
  vertical-align: 0.28em;
  display: inline-block;
}

.doc-byline {
  font-family: var(--text-family-serif);
  font-size: var(--text-size-body);
  color: var(--color-ink);
  margin: 0.6rem 0 0;
}

.abstract {
  font-family: var(--text-family-serif);
  font-style: italic;
  font-size: var(--text-size-body);
  line-height: var(--text-line-height-base);
  color: var(--color-ink-soft);
  /* No rule above it. The title block already ends on one — the contents draws a
     hairline under its own header a few lines below — and a second one between the byline
     and the abstract cut the title block in half rather than closing it. The abstract is
     part of the title block, not a section after it; the italic and the ink-soft say so
     without a line. */
  margin: var(--space-1) 0 0;
  padding: 0;
}

/* The abstract is italic, so its opener has to say it is not. */
.abstract .label { font-style: normal; margin-right: 0.6em; }



/* ── Article ────────────────────────────────────────────────── */
/* The document is one <article> — title, contents and prose are the thing a reader
   saves or hands to a reader app — and .article-body is its prose. Both long-form
   templates build the same shape, so an essay and a note differ in their apparatus
   and not in their architecture.

   The class is the layout, not the element: default.html carries it on a plain <div>
   with no <article> around it, because a front page and a 404 are not independently
   distributable and an <article> claims they are. What they want is this column.

   **The prose is one flow, and the three bands are padding.** A twelve-column grid with
   a fixed gap has tracks that are a linear function of its width — one column and its
   gap is (100% + gap) / 12 — so the rail's width, and the spacer and band beside the
   prose, are paddings from the same tokens the grid used, and resize with the viewport
   exactly as the tracks did. Measured against the grid it replaces, at 1440 / 1280 /
   1100 / 980: every edge within 0.03px. The pieces — one column and its gap, of the page and
   of the prose column, and the rail and tail column counts — are tokens in base.css
   beside --layout-wide-extra, declared on the same list so a register that moves the
   split moves them too. What one flow gives that a grid of sections could not is margin
   collapse — a paragraph's line meets a heading's two lines and the gap is two, without
   a first-child and a last-child rule on every box to make it so.

   `position: relative` because this is the containing block a margin note is
   positioned against: `right: 0` is measured from this box's outer edge, which is the
   band's right edge (§ Marginalia). */
.article-body {
  min-width: 0;
  position: relative;
}

@media (min-width: 980px) {
  .article-body {
    padding-left: calc(var(--layout-unit-page) * var(--layout-rail));
    padding-right: calc(var(--layout-unit-page) * var(--layout-tail));
  }
}

/* THE LEDE TAKES THE RAIL — still § Article, since it is that padding being taken back.
   No ── banner: the file's index lists regions, and a banner not in the index makes the
   index wrong.

   Prose before the first section heading reaches into the rail, so it sets at the note's
   measure and starts where the abstract and the contents start. The rail exists to carry
   a § number, and until there is a heading there is no number to carry — an essay opens
   like a note and becomes an essay at its first `##`.

   It is one negative margin, taking back exactly the padding-left above, because that
   padding *is* the rail (§ Article). Nothing is wrapped and no class is written: the run
   before the first heading is named by the selector rather than by a build pass, which is
   the whole reason it costs a rule instead of a section pass.

   **The unit is --layout-unit-body, not the --layout-unit-page the padding uses**, and
   copying the padding's own expression is the mistake to avoid. Both are (100% + gap) over
   a column count, and a percentage resolves against the box the property is *used* on: the
   padding sits on .article-body and measures its padding box, while this margin sits on a
   child and measures the content box the padding has already narrowed. Written with the
   page unit it moved the lede 57.55px where the rail is 98.66. The two units exist for
   exactly this — one column and its gap as a fraction of the page, and of the prose
   column — and they are equal lengths, since the prose is seven of the page's twelve
   columns and carries six of its gaps.

   **The two halves are mutually exclusive by construction, not by source order.** There is
   no "previous siblings" combinator, so this reaches into the rail for everything that is
   not the first heading and not something following a heading — `h2 ~ *` is every element
   after the first `##`, and an element before it follows no heading at all. Written the
   other way, as a rule for everything and a reset from the heading on, the two land at the
   same (0,1,1) and only the order in the file separates them, which is the shape CLAUDE.md
   § Invariants says not to rely on. `.essay-page` also lifts this over `.article-body p`'s
   `margin` shorthand, which sets margin-left to 0 at (0,1,1) and would otherwise win from
   further down base.css.

   **Scoped to an essay that has sections**, which is what an essay is. On a `template:
   essay` page carrying no `##` at all the first heading is the back matter's own *Notes*,
   so the prose would take the note's measure while the notes list kept the essay's — an
   inconsistency with no page behind it today, and one whose real answer is that a piece
   with no sections is a note. If such a page ever appears, that is the thing to fix.

   Three exclusions, each for its own reason. An `aside` is out of the flow and positioned
   from the article body's right edge, so a left margin is meaningless to it and `--move-up`
   owns its margins. A `wide` block already reaches over the band by a width derived from
   the essay's column, so moving its left edge would take its right edge off the band's;
   a wide block in a lede keeps the essay's wide measure, and there is none on the site
   today. Below 980px there is no rail — the padding is not applied — so there is nothing
   to reclaim and the block is bounded to match. */
@media (min-width: 980px) {
  .essay-page .article-body > :not(aside, .wide, h2, h2 ~ *) {
    margin-left: calc(var(--layout-unit-body) * var(--layout-rail) * -1);
  }
}

/* The gap between the contents and the first section is the contents' own margin,
   stated once. The first heading's two lines would otherwise collapse with it, and win. */
.article-body > h2:first-child { margin-top: 0; }


/* ── Drop cap ──────────────────────────────────────────────────── */
/* v.15 — unified dropcap on the .has-drop .drop span markup
   (mirror of editorial-system.html). Span is more reliable
   than ::first-letter — no quirks with leading quotes, nested
   elements, or smart-quote characters. Per-face
   --text-scale-dropcap multiplies the base size so visible
   cap-height stays consistent across families. */
.has-drop .drop {
  /* Sized from the number of lines it spans: the cap has to cover (lines - 1)
     whole body lines plus the first line's own cap height, and dividing by the
     face's cap ratio turns that height back into a font-size. */
  --dropcap-size: calc(var(--text-scale-dropcap)
    * ((var(--text-dropcap-lines) - 1) * var(--text-baseline) + var(--font-cap-height) * var(--text-size-body))
    / var(--font-cap-height));

  font-family: var(--text-family-serif);
  font-weight: var(--text-dropcap-weight);
  font-size: var(--dropcap-size);
  float: left;
  line-height: var(--text-dropcap-line-height);
  /* Dropped so its cap-top meets the first line's cap-top: where that cap-top
     sits below the paragraph's top edge, less the distance the initial's own
     cap-top already sits inside its float box. The second term is in em, which
     is what makes one expression right at every drop size — the letter and the
     word land equally well. */
  margin: calc(var(--text-dropcap-cap-top) - var(--text-dropcap-glyph-inset) * 1em) 0.1em 0 0;
  font-feature-settings: "kern", "liga";
}

/* An opening word set whole, because splitting it would break it: "AI is rapidly
   advancing" rather than a large A beside an orphaned I. `system/initial.ts` decides
   when.

   Two lines rather than three, because several characters at three-line height would
   run past the measure. The size follows from the same derivation as the single
   letter's, one line shallower — this rule sets the line count and the size recomputes
   from it, which is why there is no font-size here.

   **An initial is one of two sizes and nothing in between.** This used to divide the
   size by the character count, to hold a whole word to about the width one letter
   occupies. At six characters that produced a 13px "initial" against 18px body text —
   an opening word set *smaller* than the sentence it opens. Whatever a drop cap is, it
   is not that. Length is governed by refusing to set a long word at all, not by
   shrinking one until it disappears. */
.has-drop .drop-word {
  --text-dropcap-lines: 2;
  /* The gap to the word that follows is a property of the *body* text, not of the
     initial: `margin-right` in the cap's own em shrinks as the cap does, and at six
     characters `Geolog is` closed up to `Geologis`. A word space, held constant. */
  margin-right: calc(var(--text-size-body) * 0.25);
  letter-spacing: -0.02em;
}

/* A raised initial. It sits on the first baseline and rises above the text block, where
   a dropped one sits on the *n*th baseline and sinks into it. Only the first line runs
   beside it, so there is no silhouette for a wrapped line to collide with — which is the
   whole reason this register exists: **mixed case works here**, and "Mc" or "As" keep
   their lowercase instead of being squared off into capitals.

   Still a float, because a float is what holds the first line clear of it. The box is one
   line deep and starts above the paragraph, so line two clears it on its own; the size
   comes from --text-initial-raise-lines rather than from the drop cap's line derivation,
   which computes a cap that *fills* the lines it spans.

   The margin is the mirror of the dropped cap's. That one puts the initial's cap-top on
   the first line's cap-top; this one puts its *baseline* on the first line's baseline and
   lets the top go where it goes. Both are one expression from the same face metrics.

   The paragraph takes the lift back as padding — see .has-drop:has() below — so the cap
   has room above it and the text block does not move down.

   It carries a lead-in like every other initial. There is a case for exempting it — the
   shared baseline already makes the continuity a lead-in is for — but that is an argument
   rather than a measurement, and the way to settle it is to look at the page. */
.has-drop .drop-raise {
  font-size: var(--text-size-initial-raise);
  margin-top: calc(-1 * var(--text-initial-raise-lift));
  margin-right: calc(var(--text-size-body) * 0.25);
  letter-spacing: 0;
}

/* Sinkage, and only where it is needed. Fixed sinkage on every opening — reserving the
   tallest initial's space whatever the page actually uses — keeps the text block's top
   edge in one place across a whole publication, which is the better rule for a set of
   pages read side by side. Nothing here is read side by side yet, and spending the gap on
   every dropped opening to buy that is not worth it at three posts. Revisit if a page
   ever shows two openings at once. */
.has-drop:has(.drop-raise) { padding-top: var(--text-initial-raise-lift); }


/* An initial that is the *whole* opening word is set in capitals. A capital beside an
   x-height letter — "As", "Mc" — is a stepped silhouette, and the line that wraps
   against it collides with the step; two capitals are a block. It also settles a thing
   /styleguide/ had been asking for in prose and enforcing nowhere: a whole word set as
   an initial wants to be all capitals, or it reads as a headline and a lowercase
   descender hangs out of a box that is two lines deep by construction.

   Only when it is the whole word. `dropcap: Mc` on "McCarthy" sets a *prefix*, and
   capitalising a prefix spells MCCarthy — so initial.ts emits this class on the whole
   word alone. */
.has-drop .drop-caps { text-transform: uppercase; }


/* The lead-in: real capitals carrying the reader from the initial back to body size.
   The jump from a three-line letter to 18px prose is abrupt, and a run of capitals is
   the bridge — it is the oldest device in the book and it costs nothing but a span.

   **Real capitals, scaled, not `font-variant: small-caps`.** None of the three PT faces
   carries `smcp` (checked in the GSUB table, not assumed), so asking for small caps
   would get the browser scaling capitals down and calling them small caps — with no
   correction for the stems it thins in the process. This does the same scaling and then
   corrects it: see --text-leadin-scale and --text-leadin-stroke in base.css, both
   derived from metrics measured off the loaded face.

   The stroke is currentColor rather than a fixed ink, so a lead-in inside anything that
   sets its own colour stays that colour rather than reverting to black. */
.lead-in {
  font-size: var(--text-size-leadin);
  text-transform: uppercase;
  /* Capitals set solid are unreadable in a run; this is the low end of the conventional
     4–6%, because the stroke correction below already opens the colour up a little. */
  letter-spacing: 0.04em;
  -webkit-text-stroke: var(--text-leadin-stroke) currentColor;
}



/* ── Table of contents ──────────────────────────────────────── */
.toc {
  /* v.11 — top border dropped (lede above provides the break;
     the contents label sits without a redundant rule). Keep the
     bottom rule to close the index. */
  border-bottom: 0.5px solid var(--color-rule);
  padding: 0 0 1.2rem;
  margin: 2rem 0 3rem;
}

/* An <h2> that names the nav landmark, carrying .label for its voice — all this
   adds is taking the browser's heading margins back off it. */
.toc-header {
  margin: 0 0 0.4rem;
  padding-bottom: 0.4rem;
  border-bottom: 0.5px solid var(--color-rule-hair);
}

/* The rows are list items now. The list carries no marker and no indent — the
   numbers in the first column are the markers. */
.toc-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Two tracks, not three. The third reserved at least 12rem above 980px for a
   gloss {{toc}} has never emitted — 192px of nothing, which at 375px left the
   title 71px and six lines of one word each. Dropped rather than filled: the
   markdown a heading is written in has nowhere to put a gloss. */
.toc-row {
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  gap: 1rem;
  align-items: baseline;
  padding: 0.45rem 0;
  font-size: var(--text-size-small);
}

.toc-row + .toc-row { border-top: 0.5px solid var(--color-rule-hair); }

.toc-number { font-family: var(--text-family-mono); font-size: var(--text-size-label); color: var(--color-ink-mute); letter-spacing: 0.04em; }

.toc-title { font-family: var(--text-family-serif); color: var(--color-ink); font-size: var(--text-size-small); }

.toc-title a { color: inherit; text-decoration: none; border-bottom: 0.5px solid transparent; transition: border-color 0.15s; }

.toc-title a:hover { border-bottom-color: var(--color-ink); }

/* At ≥980px the TOC adopts the page grid as its outer container.
   Each TOC row sits inside the intro span (rail + body), with its
   own internal num · title · gloss layout. The aside region above
   the TOC stays empty — the index is content, not marginalia. */
@media (min-width: 980px) {
.toc {
    display: grid;
    grid-template-columns: repeat(var(--layout-grid-columns), 1fr);
    column-gap: var(--layout-grid-gap);
  }

.toc-header { grid-column: 1 / span var(--layout-intro-span); }

  /* The list takes the intro span and the rows sit inside it, rather than every
     row being placed on the page grid individually. One placement instead of N. */
.toc-list { grid-column: 1 / span var(--layout-intro-span); }

}

/* A narrower number column on a phone than the 3.5rem above, but still a *fixed* one.

   It was `auto`, to take only the width §5.1 actually needs. But each row is its own grid,
   so `auto` sizes the number track per row and not across the list: a section's title, a
   subsection's, and an unnumbered Notes row all landed at different indents — measured at
   375, three distinct x positions in one block, with Notes 15px left of the sections and
   the subsections 15px right of them. A contents is a list; its titles line up.

   2.2rem holds `§5.1` — four mono characters at --text-size-label plus tracking, about
   29px — with room to spare, and still saves 21px against the wide value. (`subgrid` on
   the rows would give the auto sizing *and* the alignment, sharing one column across the
   list. Worth it if a third level ever appears; a fixed track is one line and cannot
   fall back badly.) */
@media (max-width: 979px) {
  .toc-row {
    grid-template-columns: 2.2rem 1fr;
    gap: 0.7rem;
  }
}


/* What the essay adds to the shared contents: a subsection modifier that tints the number
   a step lighter, so the level reads at a glance. The number is the only thing that says
   which level a row is on — the rules are the same all the way down. */
.toc-row.toc-subsection .toc-number { color: var(--color-ink-dim); }

/* Descendant, not child: the contents is inside the <article> now, and there is only
   ever one of them on a page.

   The `margin-bottom: 1.5rem` this used to carry was inert and had always been. The
   first section's `margin-top: 3rem` collapsed with it in normal flow, and a collapse
   takes the larger of the two — so the gap a reader saw was 3rem, never 1.5rem.
   Placing the prose in a grid stopped the collapse and made the dead value suddenly
   real, which is how it was found: the essay came out 24px shorter. Removed rather
   than restated, so .toc's own 3rem is the one place the gap is written. Fourth rule
   in this codebase to have been quietly doing nothing. */
.essay-page .toc { border-bottom: 0; }

/* A rule used to be suppressed here where a subsection row returned to a top-level one —
   §5.2 to §6 on the essay — on the reasoning that it read as a doubled line. It did not;
   it read as one row in a ruled list missing its rule, and it was the only inconsistency
   in the block. Every row is separated the same way now. */

/* Tighter number column for the essay TOC. The working document
   uses wider numbers (§III.10) and keeps its 3.5rem column;
   the essay's widest is §5.1 / §5.2 (4 mono chars) so 3em is
   enough to hold the widest plus a small comfort margin. */
@media (min-width: 980px) {
.essay-page .toc-row {
    grid-template-columns: 3em 1fr;
    gap: 0.7rem;
  }

}



/* ── Headings ───────────────────────────────────────────────── */
/* A heading stays where the author wrote it, and everything a <section> used to be
   built out of is drawn here from the heading alone: the § number is a CSS counter in
   a ::before, the rule across the page is the heading's own border pulled out to the
   page's edges, and the space above a section is the heading's margin. There is no
   <section> in the markup and no pass that makes one. The contents (macros.ts) reads
   the same headings and numbers them by the same rule — an h2 counts, an h3 counts
   within it, `unnumbered` counts nothing — so the two cannot disagree without one of
   them being wrong, and article.test.ts pins the builder's side.

   **Four body lines above an essay's section, the rule in the middle; two above a
   note's.** Two lines each side of the rule, so it sits the way a break does (base.css
   `hr`): a rule with a heading under it, not a rule stuck to one. It sat two lines down
   in three at first, nearer the heading it opens, and read as crowding it. A note's
   section is the essay's with the rule and the two lines under it taken away: the
   heading stands where the rule would have been. It spent three as space for a while,
   argued from an undivided gap reading larger than a bisected one, and on a narrow
   screen three lines of nothing above a heading read as too much (status.md). The 2px
   comes out of the padding rather than adding to the total.
   The block before the heading contributes nothing: in one flow its bottom margin
   collapses into the heading's two lines and the larger wins, and the largest bottom
   margin in the column — a figure's two lines — is not larger. */
.article-body h2,
.article-body h3 {
  font-family: var(--text-family-serif);
  font-weight: 400;
  font-size: var(--text-size-h1);
  line-height: var(--text-line-height-tight);
  letter-spacing: -0.008em;
  /* Full body-column width. Stated rather than dropped, because "takes the column" is
     a decision and the absence of a rule is not. */
  max-width: none;
  margin: var(--space-2) 0 var(--space-1);
  padding-top: calc(var(--space-2) - 2px);
  border-top: 2px solid var(--color-rule);
  scroll-margin-top: var(--text-baseline);
}

.article-body :is(h2, h3) em { font-style: italic; }

/* A subsection is one step down in size at the same weight, and it is part of the
   section it sits in rather than a break from it: no rule, two lines rather than three,
   and its number a step lighter in the rail. */
.article-body h3 {
  font-size: var(--text-size-h3);
  border-top: 0;
  padding-top: 0;
  margin-top: var(--space-2);
}

/* The heading's margin is the whole gap below it. What the prose opens on brings a top
   margin of its own — none for a paragraph, two lines for a figure — and in one flow the
   larger would win, so a section opening on a figure sat two lines under its heading
   where one opening on prose sat one. Zeroing the next block's margin makes the gap the
   heading's, stated once, whatever follows. Not an aside's: its margin-top is its
   --move-up (§ Marginalia), and it takes no space in the flow, so the block after it is
   the one this rule is about. A subsection opening straight under its section is named
   as a second case, h2 + h3: the heading's line is the gap, so the two titles read as
   one stacked title rather than as a section that has nothing in it — it used to keep
   its two lines, and on a note the pair stood in 54px of air. A heading straight under
   one of its own level is left alone, since that is an empty section, or subsection,
   and should look like one; that is why the first pair still excludes headings rather
   than only h2. (0,1,2); the styleguide's example frame overrides it at (0,2,1), because
   its label is lifted above the frame by a margin. */
.article-body :is(h2, h3) + :not(aside, h2, h3),
.article-body :is(h2, h3) + aside + :not(h2, h3),
.article-body h2 + h3,
.article-body h2 + aside + h3 { margin-top: 0; }

/* The § number is a counter, not markup. An h2 advances the section count and resets
   the subsection's; an h3 advances the subsection's; a heading marked `unnumbered` —
   Notes, References — advances neither and shows nothing. An h3 before any h2 has
   nothing to count within, so it shows nothing either, as the contents lists it.

   Drawn in a ::before, which puts one character on the page that is in no page's HTML.
   The font subset is collected from the HTML, so font.ts pins § by name.

   The rail takes --color-ink-mute and a subsection number --color-ink-dim, which is
   what the colour ramp on /styleguide/ says: a § number is an address, not a word in
   the sentence. */
.article-body { counter-reset: section; }
.article-body h2:not(.unnumbered) { counter-increment: section; counter-reset: subsection; }
.article-body h3:not(.unnumbered) { counter-increment: subsection; }

.article-body h2:not(.unnumbered)::before { content: "\00a7" counter(section); }
.article-body h3:not(.unnumbered)::before { content: "\00a7" counter(section) "." counter(subsection); color: var(--color-ink-dim); }
/* (0,2,4): `:not(h2 ~ h3)` alone is (0,0,2), and the guard lost to the (0,2,2) rule above. */
.article-body > h3:not(.unnumbered):not(h2 ~ h3)::before { content: none; }

.article-body :is(h2, h3)::before {
  font-family: var(--text-family-serif);
  font-size: 1.1rem;
  line-height: 1.2;
  letter-spacing: 0;
  color: var(--color-ink-mute);
  white-space: nowrap;
}

/* Where the number sits, and where the rule ends, at each width. Two blocks bounded on
   both sides, because both place the same pseudo-element; source order must not decide it. */
@media (min-width: 980px) {
  /* The rule reaches the page's edges: the heading is pulled out over the rail and the
     band by its margins and padded straight back, so its text stays in the prose column
     and wraps there while its border spans what a section's did. The percentages are of
     the prose column, which is the heading's containing block. */
  .article-body h2,
  .article-body h3 {
    margin-left: calc(var(--layout-unit-body) * var(--layout-rail) * -1);
    margin-right: calc(var(--layout-unit-body) * var(--layout-tail) * -1);
    padding-left: calc(var(--layout-unit-body) * var(--layout-rail));
    padding-right: calc(var(--layout-unit-body) * var(--layout-tail));
  }
  /* The number hangs in the rail: an inline-block as wide as the rail, pulled left by its
     own width, so the heading's text starts at the prose column. Inline content shares
     the heading's line box, and that is what puts the number on the heading's baseline —
     the grid used `align-items: baseline` for the same, and this measures the same
     0.00 at every width, on the *first* line of a heading that wraps. */
  .article-body :is(h2, h3)::before {
    display: inline-block;
    width: calc(var(--layout-unit-body) * var(--layout-rail));
    margin-left: calc(var(--layout-unit-body) * var(--layout-rail) * -1);
  }
}

/* Below 980 the number sits inline beside the heading, on the same line box, so the
   narrow arrangement aligns exactly as well as the wide one. */
@media (max-width: 979px) {
  .article-body :is(h2, h3)::before { margin-right: 0.5em; }
}


/* A margin note is *inside* .article-body — an aside stays where it was written and is
   positioned from there — so this rule reaches a note's paragraphs too. § Marginalia
   resets them at (0,1,2), one more than this. Any new prose rule written here needs
   the same twin there, or it sets the margin in the body's measure. */
/* **One body line between paragraphs, and this is the rule the whole column rests on.**
   It was 1rem — 0.59 of a line — and a fractional paragraph gap is not a small
   imprecision, it is the thing that stops a baseline grid existing at all: every
   paragraph after the first sits 11px off the lines above it, and the offset accumulates
   down the page. The prose and the marginalia then agree only at the top of a section,
   where the first line aligns them, and drift apart from there.

   A whole line is the only number that reads as a paragraph break — zero needs an indent
   instead, and the publication has no indent convention. So the gap is one line, every
   block below is a whole number of them, and prose and margin stay in step all the way
   down a section rather than at its first sentence. */
.article-body p {
  font-size: var(--text-size-body);
  line-height: var(--text-line-height-base);
  margin: 0 0 var(--space-1);
}

/* Lists in essay body — no invented marker. Use the browser's
   native list marker (disc) at ink-mute and slightly smaller,
   positioned via ::marker so it sits in the line-box rather
   than absolutely above it. The system has no prose-bullet
   convention to inherit; this is the minimal "use what's
   there" rendering. */
/* A list is a run of prose, not a block set apart, so it takes a paragraph's gap around
   it — one body line, like everything else in the column. */
.article-body ul {
  list-style: disc outside;
  padding-left: 1.4rem;
  margin: 0 0 var(--space-1);
}

/* **Items set solid.** On a grid a list has two honest settings and 0.8rem is neither: a
   whole line between items makes a list read as loose paragraphs, and anything less than
   a line puts every item after the first off the grid — which is the defect the paragraph
   rule above exists to end, reintroduced inside the list. Nothing is lost by closing them
   up, because the marker is what separates one item from the next; that is its job. */
.article-body ul > li {
  margin-bottom: 0;
}

.article-body ul > li::marker {
  color: var(--color-ink-mute);
  font-size: 0.85em;
}

.article-body ul > li > p:first-child { margin-top: 0; }

/* An ordered list had no rule at all and was taking the browser default — `margin: 1em 0`,
   which is 0.67 of a body line against the 0.59 a paragraph takes, and a marker in full
   ink where a bullet is muted. Sixth instance of the same shape: a rule scoped to a class
   (`.article-body ul`) with no sibling for the element beside it.

   The `:not()` is the opt-out list, and everything in it is a *component* rather than
   prose: it sets its own margin and padding at (0,1,0), and a bare `.article-body ol`
   at (0,1,1) would out-specify it. The notes at the foot of an essay were the first;
   the front page's list of posts is the second. **Name a component here rather than
   out-specifying it from the other side** — the arithmetic is not the one it looks
   like: a `:not()` carries the specificity of its argument, so this selector is
   (0,2,1) and not (0,1,1), and a `.article-body .post-list` written to beat it comes
   out at (0,2,0) and quietly loses. Measured, not reasoned: the list kept a prose
   list's 1.4rem indent and lost its two-line top margin while `list-style: none`, which
   nothing here competes for, applied. A list in a margin note is the third, and only
   on this rule: the note's own list rule in § Marginalia is (0,1,2) and cannot beat
   this from its side, and the indent and the margin are all it wants to differ in — the
   item rules below set what the margin wants anyway. `aside ol` is a complex selector in
   the list and carries (0,0,2), so the specificity does not move. */
.article-body ol:not(.footnote-list, .post-list, aside ol) {
  padding-left: 1.4rem;
  margin: 0 0 var(--space-1);
}

.article-body ol:not(.footnote-list, .post-list) > li { margin-bottom: 0; }

.article-body ol:not(.footnote-list, .post-list) > li::marker {
  color: var(--color-ink-mute);
  font-size: 0.85em;
}

.article-body ol:not(.footnote-list, .post-list) > li > p:first-child { margin-top: 0; }

/* A nested list is the same list one level in, so it must not pay a second time.
   `.article-body ul` is a *descendant* selector, so a <ul> inside an <li> matches it
   again and takes a full body line of bottom margin — inside its own parent, where
   the rule above has deliberately set the items solid. The child combinator here is
   (0,1,2) against that rule's (0,1,1), so it wins without an !important and without
   the outer list having to be renamed.

   Seventh instance of the shape CLAUDE.md keeps recording: a rule written for one
   depth that silently applies at every depth. */
.article-body li > :is(ul, ol) { margin-bottom: 0; }


/* h4 and below take the label register.

   § Headings above takes h2 and h3, and an h4 — which stays exactly where the author
   wrote it, like every heading — had no rule at all. The styleguide's advice is that
   anything deeper than a subsection wants splitting; this makes a stray one look
   composed rather than browser-default bold, without offering it as a third level.

   It restates the label register rather than carrying `.label`, for the same reason
   `th` does (see base.css § Tables): markdown gives an author no way to put a class
   on a heading. All three levels take one rule — three visual depths would be the
   page arguing with the advice above.

   One line above, none below, so the heading binds to the paragraph it heads. */
/* The register is stated once, for the prose and the margin alike: a margin note is
   inside .article-body, so this reaches a label above a note — which is where a
   hand-written label wants to be an h4 rather than a span doing a block element's job.
   The one thing the two want differently is the margin, and § Marginalia takes the
   note's back one step up in specificity. */
.article-body :is(h4, h5, h6) {
  font-family: var(--text-family-sans);
  font-size: var(--text-size-label);
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-ink-mute);
  margin: var(--space-1) 0 0;
}


/* ── Registers without a rail ────────────────────────────────────
   Two of them, and this is the whole of what they share: the prose takes the column
   the rail would have used, so it starts where the page starts.

   A note is an essay with the apparatus taken off. It keeps the page column, the
   margin, the type scale, the drop cap and the back matter; it drops the rail, the
   rule above each heading, the contents and the abstract.

   A page body is what the default template lays out — the front page, 404, anything
   that is not the long form. It has no rail because it has no sections to number, and
   no title block to indent from either: the first thing under the masthead is prose,
   and prose one column in with an empty rail beside it reads as a missing § number.
   Column 1 is also where the colophon at the foot of the page starts, which is the
   other half of the argument — those two are the page's only full-width furniture and
   they should agree.

   Dropping the rail is two tokens, not a second layout. Every placement on the page is
   derived from --layout-body-start and --layout-body-span — the paddings in § Article,
   a heading's reach in § Headings, a note's band, `wide` — so redefining them here
   moves all of it together, consistent by construction rather than by rules agreeing.
   With the rail at zero columns a heading's rail padding computes to nothing and the
   prose starts at the page's edge, which is the whole of the column change.

   --layout-wide-extra comes along for free, and that is the part worth knowing. It is
   declared on :root, but a custom property's value is a token stream that resolves
   where it is *used* — so the var(--layout-body-span) inside it reads this element's
   value, not the root's, and a wide figure in a note still lands exactly on the band's
   right edge. Hard-coding the note's span anywhere would have broken that silently,
   which is the 80px bug the token was written to end. */
.note,
.page-body {
  --layout-body-start: 1;
  --layout-body-span: 8;
}

/* The space a title block would have spent. An essay and a note open on 2rem of
   padding above the title; a page body opens straight on prose, which is on the
   baseline grid, so it takes two whole body lines instead of a rem value that would
   sit between two lines for the rest of the page. The first child's own margin — a
   note's, in the flow below 980 — collapses into this and the larger wins. */
.page-body { margin-top: calc(var(--text-baseline) * 2); }

/* **The note's section break is the essay's, minus the rule and the two lines under
   it.** Two lines of space, so the heading stands where the essay's rule would: the
   same two lines a subsection takes, and a section is told from a subsection by its
   size. It was three, and the argument for three is worth keeping because it was sound
   and still lost: the essay's rule sits *inside* its gap, so a reader sees two gaps with
   a line between them, and an undivided void reads larger than a bisected one of the
   same height — so three undivided read like the essay's four. True, and on a narrow
   screen three lines of nothing above a heading was still too much air. The registers
   share everything but the rule; the rule is what earns the essay its extra two. */
.note h2 {
  border-top: 0;
  padding-top: 0;
  margin-top: var(--space-2);
}

/* And no rail: the number is not drawn. `.note` on the heading's side rather than
   `.note .article-body`, because /styleguide/ puts the class on an example frame *inside*
   the article body to show a note's heading. (0,2,2), the same as the counter rules
   above, and later, which is what wins. */
.note :is(h2, h3):not(.unnumbered)::before { content: none; }


/* ── Marginalia ─────────────────────────────────────────────────
   {{aside}} and <aside move-up=N>: a note in the margin, beside the line it was
   written at. The macro emits <aside class="move-up" style="--move-up: N">.

   The aside stays in the prose, exactly where the author wrote it, and this region
   does the placing. Above the marginalia breakpoint it is taken out of the flow with
   `position: absolute` and put in the band at the height it already had; below it,
   it stays in the flow between the paragraphs it was written between. That is the
   whole mechanism, and it is the one ../website/public/static/base.css uses — a
   positioned container that reserves a band, `right: 0` inside it, and a negative
   margin for the nudge. Nothing is hoisted by the build, no column exists to be
   sized, and no script measures anything.

   What it costs is the collision guarantee a grid cell had: two notes written close
   together overlap, and --move-up is the author's tool for that. The build used to
   lift every aside into a per-section cell, which forced a short section open to the
   height of its margin and then needed a script to keep consecutive cells apart; both
   of those were the cell's problems, and a note placed where it was written has
   neither.

   Where a note lands. An absolutely positioned box with `top: auto` sits at its
   *static position* — where it would have been in the flow — and its own margin-top
   is added to that rather than collapsed with a neighbour's. Measured, not assumed:
   +4px on the margin moved the box 4px, where a collapse with the 27px above it would
   have moved it 0. So a note, which is written as a paragraph of its own above the
   one it belongs beside, sits level with the top of that paragraph. --move-up is then
   a negative margin in body lines, and --text-marginal-baseline-offset is the
   constant that puts a sans line's baseline on the serif line's beside it. */

/* The register: sans, a size down, on the marginal line, in ink-soft — the same voice
   a caption takes. Stated once, for both placements. */
aside.move-up {
  font-family: var(--text-family-sans);
  font-size: var(--text-size-small);
  line-height: var(--text-line-height-marginal);
  color: var(--color-ink-soft);
}

/* The aside is inside .article-body, so every prose rule reaches it. Most of them set
   what a note wants anyway — the list markers, the label register — and are left to.
   What a note wants differently is restated here at (0,1,2), one more than
   `.article-body p`: the paragraph's size and margin, a list's indent and margin, a
   label's margin. The ordered-list rule is the one that cannot be beaten from this
   side, being (0,2,1); it names `aside ol` in its own opt-out for the two properties
   that differ (§ Section). Add a prose rule that sets something a note should not take,
   and add its twin here. */
aside.move-up p { font-size: inherit; line-height: inherit; margin: 0; }

/* A list in the margin keeps the prose list's marker and solid items, and takes its
   own indent and margin: 1rem against prose's 1.4rem, because the band is about
   16.5rem wide and an indent that is nothing in a 40rem measure is real width here. */
aside.move-up :is(ul, ol) { padding-left: 1rem; margin: 0 0 var(--text-line-height-marginal); }

/* One marginal line between any two blocks in a note. `:is()` carries the specificity
   of its most specific argument, so all-type-selectors keeps this at (0,1,3). */
aside.move-up :is(p, ul, ol) + :is(p, ul, ol) { margin-top: var(--text-line-height-marginal); }

/* A label above a note is an h4, in the register § Section gives every h4. In the
   margin it takes no margin, so it spends one line of the note's rhythm rather than a
   line plus the prose rule's one-line top margin — (0,1,2) against that rule's (0,1,1). */
aside.move-up :is(h4, h5, h6) { margin: 0; }

/* A link in a note takes .cite's underline — the two are one rule, in § Citations. */

/* In the flow, below the breakpoint: between the paragraphs it was written between,
   one body line off each, with a hairline marking where the margin material begins.
   The half-pixel comes out of the padding so the block stays a whole number of lines.
   Two notes in a row share one hairline and sit two marginal lines apart.

   Bounded by a max-width query rather than left to source order, because the block
   below places the same element: two blocks that place the same thing have to be
   mutually exclusive, or the one further down the file is the only one that applies. */
@media (max-width: 979px) {
  aside.move-up {
    margin: var(--space-1) 0;
    border-top: 0.5px solid var(--color-rule-soft);
    padding-top: calc(var(--space-1) - 0.5px);
  }

  aside.move-up + aside.move-up {
    margin-top: calc(var(--text-line-height-marginal) * 2);
    border-top: 0;
    padding-top: 0;
  }

  /* A note that opens a section sits under a heading whose own margin is already the
     one-line gap, and `.article-body` is a grid item, so nothing collapses the two: the
     hairline landed two lines down. The first-in-flow rule in § Section deliberately
     leaves an aside's margin alone, because above the breakpoint that margin is
     --move-up; here it is only a gap, and the heading has spent it. */
  aside.move-up:first-child { margin-top: 0; }
}

/* Out of the flow and into the band. The containing block is the article body —
   `position: relative` for exactly this (§ Article) — whose outer width is the page
   grid's, so the width is --layout-aside-span of its columns plus the gaps between them: the
   arithmetic the grid does, written once. One column plus its gap is (100% + gap) /
   columns; the band is aside-span of those, less the gap after the last.

   Paper too: no media type on this block, so a page box wider than the breakpoint takes
   the same arrangement a screen does. That is what the website's base.css does, and it
   keeps one layout rather than two to hold in step. Its older article.css records a
   Firefox print bug with asides positioned this way and keeps them in flow on paper; if
   that ever bites here, the fix is `screen and` on this block and nothing else. */
@media (min-width: 980px) {
  aside.move-up {
    position: absolute;
    right: 0;
    width: calc((100% + var(--layout-grid-gap)) / var(--layout-grid-columns) * var(--layout-aside-span) - var(--layout-grid-gap));
    /* Down by the baseline offset, then up by N body lines. Additive with the static
       position — see the note at the top of this region. */
    margin: calc(var(--text-marginal-baseline-offset) - var(--text-baseline) * var(--move-up, 0)) 0 0;
  }
}



/* ── Citations ─────────────────────────────────────────────────── */
/* The underline is on the label, not on the link. The brackets are inside the <a> —
   they belong to the citation and they are part of what you click — but a rule running
   under `[` and `]` underlines the punctuation rather than the reference. A border set on
   the <a> spans everything inside it and no child can cancel it, so the border moves to a
   span around the label and the anchor carries none. */
.cite {
  color: var(--color-ink);
  text-decoration: none;
  white-space: nowrap;
}

/* The anchor carries no rule of its own, and cancelling it takes two selectors rather than
   one. base.css underlines every `<a>` at (0,0,1), which a bare `.cite` beats — but
   `citations.js` wraps every citation in `<span class="preview">` at runtime, and
   base.css's `.preview > a` is **(0,1,1)**, which a bare `.cite` does not. So the
   underline went back under the brackets the moment the script ran, and the stylesheet
   looked correct while the page was wrong. A rule that only applies after JavaScript has
   rearranged the markup is the hardest kind to find by reading. */
.cite,
.preview > .cite { border-bottom: 0; padding-bottom: 0; }

/* A link in a margin note takes the same underline, so the margin and the body speak as
   one — one rule, so they cannot part company. `a`'s own base rule already gives it ink
   and no text-decoration; only the underline is the citation's. */
.cite .cite-label,
aside.move-up a { border-bottom: 0.5px dotted var(--color-ink-dim); }

.cite:hover .cite-label,
aside.move-up a:hover { border-bottom-color: var(--color-ink); border-bottom-style: solid; }


/* Citation hover preview. Reuses the §III.8 .preview-card
   styling but tuned for bibliography rows: card sits above
   the citation with the full ref entry inside. The hanging-indent
   inherited from .ref-entry is reset so the entry sits cleanly in
   the card. JS at the foot of the page wraps every <a.cite> in a
   .preview span and clones the corresponding .ref-entry into the
   card; the bibliography stays the source of truth.

   It is a margin note that happens to appear under the pointer, so
   it is set as one: the marginalia face, size, leading and colour,
   and the marginalia column's own width. That leaves one typographic
   register for everything that annotates the prose rather than two
   that nearly match. The one departure is the bottom edge — 2px of
   rule where the other three sides are hairlines, enough to read as
   a card sitting over the paragraph rather than a patch of it. */
.cite-preview { position: relative; display: inline-block; }

.cite-preview > .preview-card {
  /* 16.5rem is the marginalia band's own width at the settled
     1·7·1·3 split and a 78rem page — three of twelve columns plus the
     two gutters between them. Written as a length rather than derived,
     because the card is positioned against a citation in the prose and
     has no grid of its own to read the band off. */
  width: 16.5rem;
  max-width: min(16.5rem, 90vw);
  bottom: calc(100% + 0.5rem);
  left: 0;
  padding: 0.8rem 1rem;
  /* White, on a page that is nowhere else white — that is what lifts the card off
     the paper, rather than a tint or a shadow. The rule around it is the warm
     hairline the rest of the publication uses, doubled along the bottom edge so
     the card reads as sitting over the paragraph and not printed into it. Same
     colour on all four sides: the weight does the separating, not a darker line. */
  background: #fff;
  border: 1px solid var(--color-rule-soft);
  border-bottom-width: 2px;
  border-radius: var(--radius);
  /* Marginalia register — sans at --text-size-small on the marginal
     line, the same voice as a note in the margin. Reset the
     hanging-indent inherited from .ref-entry. */
  font-family: var(--text-family-sans);
  font-size: var(--text-size-small);
  line-height: var(--text-line-height-marginal);
  color: var(--color-ink-soft);
  text-indent: 0;
  padding-left: 1rem;
  font-style: normal;
  white-space: normal;
}

/* Tapped open on a touch device, the card is a bottom sheet (see base.css).
   These rules only hand back what this block sets for the hover card — width,
   the bottom/left anchoring, the padding — all of which match
   `.preview-card:popover-open` on specificity and would otherwise win it on
   source order, since essay.css loads second. The register stays: same tint,
   same sans face, same size. */
.cite-preview > .preview-card:popover-open {
  width: auto;
  max-width: none;
  inset: auto 0 0 0;
  padding: 1.1rem var(--layout-page-padding) calc(1.1rem + env(safe-area-inset-bottom, 0px));
  /* The `border` shorthand above is the reason this line exists: it matches
     `.preview-card:popover-open` on specificity and wins on source order, so
     base.css's border-inline/border-bottom reset never lands and the sheet gets
     hairlines down both edges and along the bottom of the screen. A sheet is
     bounded by the viewport; only the top edge is a real edge. */
  border: 0;
  border-top: 0.5px solid var(--color-rule);
  /* Square, for the same reason the side and bottom rules go: a sheet held
     against the edges of the screen has no corners of its own to round. */
  border-radius: 0;
}

.cite-preview > .preview-card em { font-style: italic; }

.cite-preview > .preview-card a {
  color: var(--color-ink-soft);
  border-bottom: 0.5px solid var(--color-rule-hair);
  text-decoration: none;
  word-break: break-word;
}



/* ── Footnotes ──────────────────────────────────────────────────
   A marker in the prose and a numbered note at the foot of the page, collected
   under a rule. Three surfaces with three jobs, which is how the design this
   comes from puts it: marginalia sit beside the line in sans, a figcaption
   travels with its figure, and footnotes are numbered and gathered below a rule.
   These notes used to be hoisted into the marginalia column, which made the
   marginalia surface do the footnote's job and left the publication with two
   things in one place.

   The marker is `sup.footnote-ref`, which is what markdown-it-footnote emits.
   This rule used to be written as `.fn-ref` — a class nothing has ever carried —
   so the marker had been rendering as a browser default since the port, and the
   rule looked exactly like a rule that worked. */
sup.footnote-ref {
  font-family: var(--text-family-mono);
  font-size: 0.7em;
  vertical-align: super;
  line-height: 0;
  color: var(--color-ink-mute);
  font-variant-numeric: lining-nums;
}

sup.footnote-ref a {
  color: var(--color-ink-soft);
  text-decoration: none;
  border-bottom: none;
  padding: 0 0.15em;
}

sup.footnote-ref a:hover { color: var(--color-ink); }

/* Set exactly as the references are, because they are the same kind of matter:
   back matter at the foot of the page, in the body face at the body size, on a body
   line, in ink-soft. Only the marker differs — the list numbers itself, where a
   reference entry hangs its first line. They were the apparatus size for a while,
   which made the two runs of back matter read as two different registers stacked on
   top of each other. */
.footnote-list {
  margin: 0;
  padding-left: 2rem;
  font-size: var(--text-size-body);
  line-height: var(--text-line-height-base);
  color: var(--color-ink-soft);
}

/* The same gap the reference entries take between them. */
.footnote-list > li { margin-bottom: var(--text-baseline); }

/* The last item's margin collapses out of the <ol> and lands in the section break below
   it, which made the gap after a Notes section four lines where every other section break
   is three. `.article-body > :last-child` cannot reach it — the list is the last child,
   and the margin escaping is its child's. */
.footnote-list > li:last-child { margin-bottom: 0; }

.footnote-list > li::marker {
  color: var(--color-ink-mute);
  font-variant-numeric: lining-nums;
}

/* A note's prose arrives in a <p>, which would otherwise take the body's measure
   from `.article-body p` — a class *and* a type selector, so the reset has to beat
   it on specificity. Same trap as the figure caption and the marginalia column. */
.footnote-list p,
.article-body .footnote-list p { font-size: inherit; line-height: inherit; margin: 0 0 var(--text-baseline); }

.footnote-list p:last-child { margin-bottom: 0; }

/* The arrow that carries a reader home. Dimmed: a note shouldn't end on a loud glyph.
   U+2190 rather than the U+21B5 it was — PT carries no ↵ (markdown.ts § Footnotes). */
.footnote-list .footnote-back {
  font-family: var(--text-family-mono);
  color: var(--color-ink-mute);
  text-decoration: none;
  border-bottom: none;
  margin-left: 0.4em;
}

.footnote-list .footnote-back:hover { color: var(--color-ink); }



/* ── References ────────────────────────────────────────────────────
   {{references}} builds its own unnumbered section and fills it with entries.
   The .bibliography container and the .refs / .refs-list variants that used to
   sit here were the shapes this replaced, and nothing emitted them. */
.ref-entry {
  font-family: var(--text-family-serif);
  font-size: var(--text-size-small);
  line-height: var(--text-line-height-base);
  color: var(--color-ink-soft);
  margin: 0 0 var(--space-1);
  padding-left: 2rem;
  text-indent: -2rem;
  /* References sit in the article body, so the column already holds them to the
     measure. The 60ch cap this replaces was a second and narrower answer to a question
     the column had already answered. */
  max-width: none;
}

.ref-entry a {
  color: var(--color-ink-soft);
  border-bottom: 0.5px solid var(--color-rule-hair);
  text-decoration: none;
  word-break: break-word;
}

.ref-entry a:hover { color: var(--color-ink); border-bottom-color: var(--color-ink); }

.ref-entry em { font-style: italic; }

.ref-entry.is-target {
  background: var(--color-paper-tint);
  /* Extend the highlight only on the left + right edges via
     two horizontally-offset shadows (no spread, no blur, no
     radius). Text position unchanged; no vertical overlap. */
  box-shadow:
    -0.8rem 0 0 var(--color-paper-tint),
    0.8rem 0 0 var(--color-paper-tint);
}
