/* ===========================================================================
   FOUNDATION ZERO - RULED LAYOUT ENGINE                  (ui/book/ruled.css)

   THE PAPER LINES ARE NOT DECORATION. THEY ARE THE TYPOGRAPHIC GRID.

   What was wrong before, stated as the number that proves it: body text had
   `line-height: 1.85` on `font-size: 12.5px` = 23.125px, while the paper drew
   a rule every 28px. Two systems, each internally consistent, disagreeing by
   4.875px per line. Line one looked deliberate. By line six the text sat dead
   centre between two rules. By line twelve it was a whole rule out of place
   and climbing. Nothing was misaligned by accident - the text and the paper
   had simply never been told they were the same object.

   SO THERE IS EXACTLY ONE VERTICAL RHYTHM HERE, AND EVERYTHING DERIVES FROM IT

       --rule-spacing: R

   The rules are drawn every R. Body line-height IS R - not 1.85, not "about
   R", the variable itself. Paragraph gaps, heading heights, list rows, code
   blocks and figure heights are all integer multiples of R.

   That last sentence is the whole anti-drift argument, and it is structural
   rather than careful: if every vertical dimension on the page is n*R for
   integer n, then the top of the k-th line box is exactly k*R from the origin.
   There is no fractional residue to accumulate, so line 1 and line 400 are
   equally aligned. Section O asks for no cumulative drift; this makes drift
   arithmetically impossible rather than merely small.

   WHERE THE RULE SITS RELATIVE TO THE LETTERS
   --------------------------------------------
   Matching line-height to rule spacing is necessary and not sufficient. Text
   is laid out inside a line BOX of height R, and the baseline sits wherever
   the font's own metrics put it - which differs per family and per size. Draw
   the rule at the bottom of the box and the letters float well above it; draw
   it at the top and it strikes through them.

   So the rule's position inside each band is a measured variable,
   `--rule-at`, set by ruled.js after measuring the ACTUAL font that ACTUALLY
   loaded. If a preferred handwriting face is missing and the stack falls back,
   the measurement changes with it and the alignment still holds. Typography
   degrading must never make the Book unreadable (Section D).

   ONE PLACE OWNS THIS
   -------------------
   No page tunes its own spacing. A page that needs different typography asks
   for a different STYLE; it does not reposition a background. Section Z: this
   is written once, and every page that exists later inherits it with nothing
   edited.
   =========================================================================== */

:root {
  /* ---- A. THE ONE RHYTHM ------------------------------------------------ */

  /* ONE INTERVAL. THE PAPER IS RULED WHERE THE WRITING STEPS.

     A two-interval version lived here briefly: --rule-draw for the paper and
     --rule-spacing at twice that for the writing, so every line of text had a
     blank ruled line beneath it. Double-spaced.

     It only worked while the rules were painted from measured baselines, and
     that painter has been removed - so the two halves stopped agreeing. The
     paper ruled every 56px while the calibrated rule position was computed for
     a 28px band, which put the line above the writing instead of under it.

     One interval. The writing steps by it and the paper is ruled at it, so a
     rule cannot be anywhere except under a line of writing. */
  --rule-spacing: 28px;


  /* Where the rule is drawn inside each band, measured from the band's top.
     ruled.js overwrites this from real font metrics; the value here is only
     a sane starting point for the instant before calibration runs. */
  --rule-at: 21px;

  /* ---- F. THE WRITING REGION -------------------------------------------- */
  --margin-rule-x: 46px;      /* the red margin line */
  --write-left: 62px;         /* text starts clear of it */
  --write-right: 34px;
  --write-top: calc(var(--rule-spacing) * 1);
  --write-bottom: calc(var(--rule-spacing) * 2);

  /* ---- G. MEASURE ------------------------------------------------------- */
  /* 55-80 characters. `ch` is the width of "0" in the current font, so this
     is a real measure rather than a guessed pixel width, and it moves with
     the font instead of having to be re-tuned when the font changes. */
  --measure: 72ch;

  /* ---- D. THE TWO VOICES ------------------------------------------------ */
  /* Narrative prose is handwriting: neat print, not cursive, not decorative.
     Every candidate is a font that ships with the OS - the Book must work
     offline, so there is no webfont and no CDN anywhere in this file. The
     stack ends in a genuinely readable humanist sans so that a machine with
     none of the handwriting faces still gets calm, legible prose rather than
     a broken page. */
  --font-prose: "Segoe Print", "Ink Free", "Bradley Hand ITC", Candara,
                "Segoe UI", system-ui, -apple-system, sans-serif;

  /* Technical material is monospace, because alignment IS meaning for a path,
     a hash or a column of numbers. */
  --font-technical: Consolas, "Cascadia Mono", ui-monospace, "SF Mono", monospace;

  --prose-size: 14.5px;
  --technical-size: 12px;
}

/* ===========================================================================
   THE WRITING REGION - the box that carries BOTH the rules and the text.

   This is the load-bearing structural decision. The rules used to be painted
   on `.page`, whose padding then pushed the text down by an amount that had
   nothing to do with R - so the grid and the prose started from different
   origins before a single line was laid out.

   Here one element owns both. The background's origin and the first line
   box's origin are the same point by construction, so alignment cannot be
   knocked out by changing padding somewhere else.
   =========================================================================== */
.ruled-writing {
  font-family: var(--font-prose);
  font-size: var(--prose-size);
  line-height: var(--rule-spacing);      /* A: the text IS on the grid */
  color: var(--paper-ink);

  max-width: var(--measure);             /* G: a comfortable measure */
  padding: 0;
  position: relative;

  /* The rules are painted on the SHEET, not on this box - see .ruled-host
     below. Ruled paper is ruled all the way across; stopping the lines at the
     measure would draw the text column rather than the paper. Alignment
     survives the move because the writing region sits exactly one rule row
     below the top of the sheet, so both share an origin that is a whole
     multiple of R. */

  /* S: ordinary prose never scrolls sideways. Anything that genuinely needs
     width says so for itself, locally, further down this file. */
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;                          /* H: no arbitrary word chopping */
}

/* The paper under a ruled page carries no rules of its own any more - it is
   just the sheet. Ownership of the grid moved into .ruled-writing above, and
   leaving a second set of rules on the parent would put two grids on one page
   at two different origins, which is the original bug wearing a new hat. */
/* KEYED ON THE ENGINE'S OWN MARKER, NOT ON A LEGACY PAPER CLASS.

   This selector was .page.paper-ruled.ruled-host, and it silently did nothing
   on the fault pages - because a Fault renders as LAB paper, which is also a
   ruled style. The engine had correctly mounted the writing region and the
   text was on the grid; there were simply no rules drawn under it.

   .ruled-host is added by the engine to every page whose STYLE is ruled. So
   the paper follows the decision the registry already made, and a style
   registered next year gets its rules by being ruled rather than by being
   named here. */
.page.ruled-host {
  /* ONE REPEATING RULE EVERY R, AND NOTHING ELSE DRAWS RULES.

     Deriving the rules from the measured baselines was tried instead of this.
     It removed strikethroughs by construction, which was the point - but it
     also had to fill the blank regions to keep the sheet looking like paper,
     and a fill running at R alongside rules already placed per line put extra
     lines between the written ones. The page came out double spaced.

     One repeating grid cannot do that: R is R everywhere, so the sheet has
     exactly the rhythm the text has. The strikethrough class is handled by the
     snapping and baseline work above rather than by moving the paper.

     The padding is a whole number of rule rows on purpose - any other value
     would offset the text from the very grid it is drawn onto. */
  /* TWO LAYERS, CLIPPED DIFFERENTLY - AND THAT IS THE WHOLE POINT.

     THE RULE STARTS WHERE THE WRITING STARTS. Clipping it to the content box
     stops it running out to the edge of the sheet and through the red margin:
     a ruled line belongs under the writing, and the writing begins at the left
     margin, so the rule begins there too.

     But background-clip applies to EVERY layer, so clipping the whole
     background clipped the paper colour with it and the margins went dark -
     the sheet stopped being a sheet. The colour is therefore its own layer,
     painted across the border box, while only the rules are held to the
     content box. */
  background:
    repeating-linear-gradient(
      180deg,
      transparent 0 var(--rule-at),
      var(--rule) var(--rule-at) calc(var(--rule-at) + 1px),
      transparent calc(var(--rule-at) + 1px) var(--rule-spacing)
    ) 0 0 / auto repeat-y content-box content-box,
    linear-gradient(var(--paper), var(--paper))
      0 0 / auto no-repeat border-box border-box;
  padding: var(--write-top) var(--write-right) var(--write-bottom) var(--write-left);
}
.page.ruled-host::before {
  /* F: the red margin. A boundary, not decoration - prose starts to its right
     and never crosses it. */
  content: ""; position: absolute;
  left: var(--margin-rule-x); top: 0; bottom: 0;
  width: 1px; background: var(--margin-red); opacity: 0.5;
}

/* ===========================================================================
   J / K. BLOCKS. Every vertical dimension below is n * R.

   There is not one `margin: 13px` in this file. That is the rule that keeps
   the grid intact forty lines down, and it is why these values look blunt.
   =========================================================================== */

/* Paragraphs: one blank ruled row between them. */
.ruled-writing p {
  margin: 0 0 var(--rule-spacing) 0;
  max-width: var(--measure);
}
.ruled-writing p:last-child { margin-bottom: 0; }

/* K: headings reserve whole rows, so a bigger face cannot shove the prose
   below it off the rules. The font grows; the box does not. */
/* A TALL BLOCK KEEPS A ONE-ROW LINE BOX AND GAINS ITS EXTRA ROWS AS PADDING.

   The obvious way to make a heading two rows tall is `line-height: 2R`. It is
   wrong, and measurably so: a line box of 56px CENTRES its text, putting the
   baseline at ~33px - thirteen pixels below the nearest rule, floating in the
   middle of the gap. The block occupies the right number of rows and the
   letters sit on nothing.

   Keeping line-height at exactly R and buying the extra height with padding
   above puts the baseline back on a rule and still reserves the space the
   larger face needs (Section K). */
.ruled-writing h1, .ruled-writing .rl-h1 {
  font-size: 21px;
  line-height: var(--rule-spacing);
  padding-top: var(--rule-spacing);
  margin: 0 0 var(--rule-spacing) 0;
  font-weight: 600; letter-spacing: 0.01em;
}
.ruled-writing h2, .ruled-writing .rl-h2 {
  font-size: 16.5px;
  line-height: var(--rule-spacing);
  height: var(--rule-spacing);
  margin: var(--rule-spacing) 0 0 0;
  font-weight: 600;
}
.ruled-writing h3, .ruled-writing .rl-h3 {
  font-size: 14.5px;
  line-height: var(--rule-spacing);
  height: var(--rule-spacing);
  margin: var(--rule-spacing) 0 0 0;
  font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  font-family: var(--font-technical); font-size: 10.5px;
  color: var(--paper-dim);
}

/* L: lists. A wrapped line lines up under the ITEM'S TEXT, not under the
   bullet - which is what "neatly written" means on paper and what a hanging
   indent does. */
.ruled-writing ul, .ruled-writing ol {
  margin: 0 0 var(--rule-spacing) 0;
  padding: 0; list-style: none;
}
.ruled-writing li {
  position: relative;
  padding-left: 1.6em;                    /* the hanging indent */
  margin: 0;
  line-height: var(--rule-spacing);
  max-width: var(--measure);
}
.ruled-writing li::before {
  content: "\2022";
  position: absolute; left: 0.35em; top: 0;
  line-height: var(--rule-spacing);
  color: var(--paper-dim);
}
.ruled-writing ol { counter-reset: rl; }
.ruled-writing ol > li::before {
  counter-increment: rl; content: counter(rl) ".";
  left: 0; font-family: var(--font-technical); font-size: 11px;
}

/* ===========================================================================
   E / I. TECHNICAL MATERIAL GETS ITS OWN TYPOGRAPHY.

   A path is not a sentence. Setting `/etc/caddy/Caddyfile` in handwriting and
   wrapping it like prose makes it both ugly and harder to read, and letting it
   wrap at spaces it does not contain is what forced three-word lines in the
   screenshot.
   =========================================================================== */

/* An inline chip: an id or a path inside a sentence. It stays on the grid
   because its line-height is still R - only the family and size change. */
.ruled-writing code, .ruled-writing .rl-tech {
  font-family: var(--font-technical);
  font-size: var(--technical-size);
  line-height: var(--rule-spacing);
  background: rgba(35,36,31,0.055);
  border: 1px solid rgba(35,36,31,0.10);
  border-radius: 2px;
  padding: 0 4px;
  /* I: break at path separators, never mid-token in an arbitrary place. */
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* A technical inset: a path or command on its own lines, off the prose grid's
   measure but still on its rhythm. */
.ruled-writing .rl-inset {
  font-family: var(--font-technical);
  font-size: var(--technical-size);
  line-height: var(--rule-spacing);
  margin: 0 0 var(--rule-spacing) 0;
  padding: 0 0 0 12px;
  border-left: 2px solid rgba(35,36,31,0.22);
  color: #3a3c33;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-width: 100%;
}

/* A code block. S: if it genuinely needs width, IT scrolls - not the page. */
.ruled-writing pre, .ruled-writing .rl-code {
  font-family: var(--font-technical);
  font-size: var(--technical-size);
  line-height: var(--rule-spacing);
  margin: 0 0 var(--rule-spacing) 0;
  padding: 0 10px;
  background: rgba(35,36,31,0.045);
  border-left: 2px solid rgba(35,36,31,0.28);
  overflow-x: auto;
  max-width: 100%;
  white-space: pre;
}

/* M: equations get their own grid-aligned block and come back onto the same
   global rhythm afterwards, because the block's height is a multiple of R. */
.ruled-writing .rl-equation {
  font-family: var(--font-technical);
  font-size: 13.5px;
  line-height: var(--rule-spacing);
  padding: calc(var(--rule-spacing) / 2) 0;
  margin: 0 0 var(--rule-spacing) 0;
  text-align: center;
  border-top: 1px solid rgba(35,36,31,0.18);
  border-bottom: 1px solid rgba(35,36,31,0.18);
  overflow-x: auto; white-space: nowrap;
}

/* N: an artifact occupies whole rows, so prose after it re-enters the grid.
   ruled.js rounds the measured height up to the next multiple of R. */
.ruled-writing .rl-figure {
  margin: 0 0 var(--rule-spacing) 0;
  overflow-x: auto;
  max-width: 100%;
}
.ruled-writing .rl-figure > * { max-width: 100%; }
.ruled-writing .rl-caption {
  font-family: var(--font-technical);
  font-size: 10.5px; line-height: var(--rule-spacing); height: var(--rule-spacing);
  color: var(--paper-dim);
}

/* A definition row - the shape most Book pages actually use. Label in
   technical type, value in prose, both on the same rhythm. */
.ruled-writing .rl-kv {
  display: grid;
  grid-template-columns: minmax(120px, 190px) 1fr;
  gap: 0 14px;
  margin: 0 0 var(--rule-spacing) 0;
  max-width: 100%;
}
.ruled-writing .rl-kv dt {
  font-family: var(--font-technical); font-size: 10px;
  letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--paper-dim);
  line-height: var(--rule-spacing);
}
.ruled-writing .rl-kv dd {
  margin: 0; line-height: var(--rule-spacing);
  overflow-wrap: break-word; min-width: 0;
}

/* ===========================================================================
   P. RESPONSIVE. The grid never changes; the writing region does.
   Narrow screens get a shorter measure, never smaller type (Section P).
   =========================================================================== */
@media (max-width: 1100px) {
  :root { --measure: 64ch; --write-left: 52px; --margin-rule-x: 38px; }
}
@media (max-width: 820px) {
  :root { --measure: 100%; --write-left: 30px; --write-right: 18px; --margin-rule-x: 18px; }
}

/* Q. Zoom and fractional DPI: rule position and line-height come from the SAME
   variable, so a browser that rounds one rounds the other identically. There
   is no second number left to disagree. */

/* A page whose content the engine could not place on the grid says so, in the
   page, rather than quietly rendering crooked. Section Z. */
.ruled-writing .rl-gap {
  font-family: var(--font-technical); font-size: 10.5px;
  line-height: var(--rule-spacing);
  color: #8a5a2a; border-left: 2px solid #c08a3e; padding-left: 8px;
  margin: 0 0 var(--rule-spacing) 0;
}

/* ===========================================================================
   T. THE NORMALISATION LAYER - why this is generic and not a list of patches

   Everything above styles blocks the engine itself creates. But the Book is
   full of pages written before the engine existed, using their own classes
   with their own spacing: `gap: 2px 14px`, `margin-top: 14px`,
   `padding-top: 8px`, `line-height: 1.5`, row separators drawn as borders.
   None of those numbers are multiples of R, and each one knocks everything
   below it off the grid by its remainder.

   Section T forbids fixing this page by page, and it is right to: a list of
   legacy class names is a closed world that the next page breaks.

   So this normalises by STRUCTURE. Anything inside a writing region is placed
   on the grid whatever it calls itself - including markup that does not exist
   yet. A future page gets grid-aligned prose by being inside the region, not
   by being known to this file.

   THE THREE THINGS THAT KNOCK CONTENT OFF A GRID, and the answer to each:

     arbitrary line-height  -> everything inherits R unless it declares a
                               deliberate multiple of R
     arbitrary margins      -> vertical space is 0 or exactly R
     borders and padding    -> a 1px border adds 1px to a 28px row and the
                               whole page below it slides. Separators are
                               painted as a background line, which occupies
                               no height at all.
   =========================================================================== */

/* Line-height: one value, everywhere, except the blocks that deliberately
   declare a multiple of it. */
.ruled-writing *:not(pre):not(.rl-code):not(.rl-equation):not(h1):not(.rl-h1) {
  line-height: var(--rule-spacing);
}

/* Vertical space is zero or one row. There is no third option, because a
   third option is where fractional remainder comes from. */
.ruled-writing dl,
.ruled-writing ul,
.ruled-writing ol,
.ruled-writing section,
.ruled-writing blockquote,
/* THE NORMALISATION LAYER IS FOR LEGACY MARKUP, AND NATIVE BLOCKS ARE NOT
   LEGACY.

   This selector gave every direct child a bottom margin of one row, which is
   right for a legacy page and wrong for a composed one: the composer had
   already allocated the space between blocks, so each block silently added a
   second row nobody owned. Four blocks, three extra rows, and a page that
   measured 504px while its blocks measured 420px - the exact
   UNOWNED_LAYOUT_SPACE the row map was reporting.

   Specificity is why it was invisible: this selector scores (0,3,1) with its
   two :not() arguments, so it beat `.ruled-writing .blk { margin: 0 }` at
   (0,2,0) and the block's own rule never applied. Excluding native blocks HERE
   is the fix; adding a louder counter-rule elsewhere would have left two
   systems arguing about the same margin. */
.ruled-writing > div:not(.rl-figure):not(.rl-equation):not(.blk):not(.blk-gap) {
  margin-top: 0;
  margin-bottom: var(--rule-spacing);
  padding-top: 0;
  padding-bottom: 0;
  row-gap: 0;
}

/* Native blocks own no vertical space at all - the composer owns all of it. */
.ruled-writing > .blk,
.ruled-writing > .blk-gap {
  margin-top: 0; margin-bottom: 0;
  padding-top: 0; padding-bottom: 0;
}

/* Rows: no padding, no borders, exactly one rule row tall at minimum. */
.ruled-writing h1,
.ruled-writing h2,
.ruled-writing h3,
.ruled-writing dt,
.ruled-writing dd,
.ruled-writing li,
.ruled-writing tr,
.ruled-writing td,
.ruled-writing th {
  padding-top: 0;
  padding-bottom: 0;
  /* Headings are included because a legacy h3 carried a border-bottom, which
     drew a SECOND line a pixel from the rule - two grey lines under one label,
     which is what made the section headings look struck through rather than
     underlined. A separator that costs height has no place on a grid. */
  border-top: 0;
  border-bottom: 0;
  min-height: var(--rule-spacing);
}

/* A row separator that costs no height. The old one was `border-bottom: 1px`,
   which added a pixel per row - invisible on row one and a rule out of place
   by row twenty-eight. */
.ruled-writing .rowlist li,
.ruled-writing .rl-row {
  background-image: linear-gradient(to right, rgba(35,36,31,0.10), rgba(35,36,31,0.10));
  background-size: 100% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
}

/* Legacy prose classes keep their voice - smaller, dimmer, italic - but not
   their spacing. Only the things that do not move the grid survive. */
.ruled-writing .lede { font-size: var(--prose-size); margin-bottom: var(--rule-spacing); }
.ruled-writing .note { font-size: 12.5px; font-style: italic; color: #6f6a58; }
.ruled-writing .gaps {
  margin-top: var(--rule-spacing);
  margin-bottom: var(--rule-spacing);
  padding-top: 0;
  border-top: 1px dashed #c9c0aa;
}
.ruled-writing .gap-head {
  font-family: var(--font-technical); font-size: 10.5px;
  letter-spacing: 0.08em; color: #7a6f57; margin-bottom: 0;
}
.ruled-writing .gap { font-size: 12.5px; color: #6b6250; }
.ruled-writing .k { color: var(--paper-dim); }

/* E: keys, tags and ids are technical even in legacy markup. */
.ruled-writing .kv dt,
.ruled-writing .rowlist li .k,
.ruled-writing .tag {
  font-family: var(--font-technical);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--paper-dim);
}
.ruled-writing .kv dd { font-size: var(--prose-size); }
.ruled-writing .kv { grid-template-columns: minmax(120px, 190px) 1fr; gap: 0 14px; }

/* S: a table or a diagram may need width. It scrolls inside itself; the page
   never does. */
.ruled-writing table,
.ruled-writing .diagram,
.ruled-writing svg {
  max-width: 100%;
}
.ruled-writing .diagram { overflow-x: auto; }

/* ---------------------------------------------------------------------------
   CHIPS CANNOT BE ALLOWED TO GROW THE LINE BOX.

   A tag rendered as `display: inline-block` with `padding: 1px 6px` and a
   border measures 31.6px inside a 28px grid. Its row grows by 3.6px, and every
   line after it on the page is 3.6px off the rules - from a two-pixel
   decoration nobody would think to suspect.

   The fix is a property of the CSS box model rather than a tuned number:
   on an INLINE box, vertical padding and borders paint but do NOT contribute
   to line box height. So chips render inline. They keep their background,
   their border and their horizontal padding, and they lose the ability to
   push the grid around - whatever value someone sets later.
   ------------------------------------------------------------------------- */
.ruled-writing .tag,
.ruled-writing code,
.ruled-writing .rl-tech,
.ruled-writing [class*="chip"],
.ruled-writing [class*="badge"],
.ruled-writing [class*="pill"] {
  display: inline;

  /* line-height: 0 IS THE WHOLE FIX, AND IT LOOKS WRONG UNTIL YOU MEASURE IT.

     Making chips `display: inline` was not enough. A line box is the union of
     the inline boxes on it, and an inline box's extent above and below the
     baseline comes from ITS OWN font's ascent and descent - so a 12px
     monospace chip beside 14.5px handwriting reaches further below the
     baseline than the surrounding text does, even with an identical
     line-height. Measured: a plain row is 28.00px; the same row containing one
     chip is 29.60px. Grid rows inherit that, snapBlocks rounds 29.6 up to 56,
     and single-line rows acquire an empty row underneath - which is exactly
     the gaps that appeared down the page.

     An inline box with line-height 0 has zero extent, so it cannot enlarge the
     line box. The glyphs still draw, and the background and border still paint
     over the content area, because those are painted from the font metrics
     rather than from the line-height. The chip stays visible and stops
     shoving the grid around. */
  line-height: 0;

  padding-top: 0;
  padding-bottom: 0;
  vertical-align: baseline;
}

/* A technical token inside a link keeps the link's colour but loses the
   underline: at 12px a rule under a hyphen reads as a low double mark, which
   turned every hyphen in `mech:caddy-ingress-...` into visual noise. Colour
   already says it is a link. */
/* MATCHED ON ROLE, NOT ON THE TAG THAT HAPPENED TO BE USED.

   These selectors named `a` only, and the Book's links are `span.link` - so
   the underline survived every attempt to remove it, and a 14.5px underline
   drawn across 12px monospace looked exactly like a strikethrough. Whatever
   element carries a technical token, it does not get underlined. */
.ruled-writing a code,
.ruled-writing a .rl-tech,
.ruled-writing a:has(> code),
.ruled-writing .link,
.ruled-writing .link .rl-tech,
.ruled-writing .link .rl-tech-block,
.ruled-writing .rl-tech-block a,
.ruled-writing a .rl-tech-block,
.ruled-writing .rl-tech-block {
  /* An underline on a path is two horizontal lines a couple of pixels apart -
     its own, and the ruled line under it - which is what made a promoted path
     look struck through even after the rule was correctly placed. Colour is
     enough to say "link" here. */
  text-decoration: none;
}
.ruled-writing a { text-decoration-thickness: 1px; text-underline-offset: 2px; }

/* ---------------------------------------------------------------------------
   R. THE WRITING PAGE GETS THE SPACE WHEN THERE IS SPACE TO GIVE.

   The shell allocates a fixed 232px index and a fixed 268px rail, so at a
   1100px window the notebook is left 600px and prose wraps early while half
   a kilopixel sits in two side columns that are mostly empty.

   The rails are useful and are not being removed. They yield PROPORTIONALLY
   when the window is small and take their full width back when it is not, so
   the page is never cramped to keep a rail at a size nothing is using.
   ------------------------------------------------------------------------- */
@media (max-width: 1500px) {
  .spread { grid-template-columns: 210px 1fr 232px; }
}
@media (max-width: 1240px) {
  .spread { grid-template-columns: 186px 1fr 196px; }
}
@media (max-width: 1000px) {
  /* Below this the rail is costing more than it returns: the notebook keeps
     the width and the rail moves out of the way. */
  .spread { grid-template-columns: 168px 1fr; }
  .spread > .rail { display: none; }
}
@media (max-width: 760px) {
  .spread { grid-template-columns: 1fr; }
  .spread > .index { display: none; }
}

/* ===========================================================================
   WRITING LINES - THE BARRIER BETWEEN ONE SENTENCE AND THE NEXT

   Matching line-height to the rules keeps every line ON the grid, but it does
   not decide WHERE a sentence starts. Prose flowed as one continuous stream:
   a sentence began wherever the previous one happened to end, so the second
   half of one thought and the first half of the next shared a ruled row. On
   ruled paper that is not what writing looks like - a person starts a new
   thought on a new line.

   So each sentence, and each place the author pressed Enter, becomes a WRITING
   LINE: a block that starts on a rule boundary and occupies a whole number of
   rows. Its own wrapped continuation lines land on the following rows because
   its line-height is exactly R.

   The barrier is structural. A sentence cannot begin mid-row because the block
   before it ended on a row boundary, and it cannot end mid-row because its
   height is n*R. Nothing is positioned by hand and nothing accumulates.
   =========================================================================== */
.ruled-writing .rl-line {
  display: block;
  line-height: var(--rule-spacing);
  margin: 0;
  padding: 0;
  max-width: var(--measure);
  /* A sentence is a unit: it does not get split across a page break or have a
     single word left stranded on its own row where it can be avoided. */
  orphans: 2;
  widows: 2;
}

/* A run of writing lines is a paragraph. The blank row goes AFTER the run, not
   between the sentences inside it - sentences are consecutive rows, the way
   they are on paper. */
.ruled-writing .rl-para {
  display: block;
  margin: 0 0 var(--rule-spacing) 0;
  max-width: var(--measure);
}
.ruled-writing .rl-para:last-child { margin-bottom: 0; }

/* An explicit line break the author typed is a hard barrier and keeps its own
   row even when it is short. */
.ruled-writing .rl-line.rl-hard { display: block; min-height: var(--rule-spacing); }

/* A long technical token that would wrap gets its own rows rather than being
   dragged through prose. Section I: a path is not a sentence. */
.ruled-writing .rl-tech-block {
  display: block;
  font-family: var(--font-technical);
  font-size: var(--technical-size);
  line-height: var(--rule-spacing);
  margin: 0;
  padding: 0 0 0 10px;
  border-left: 2px solid rgba(35,36,31,0.20);
  overflow-wrap: anywhere;
  max-width: 100%;
  background: rgba(35,36,31,0.035);
}

/* A link with no underline still has to read as a link, so it keeps its colour
   and gains the underline on hover - which cannot collide with a rule because
   it only exists while the pointer is on it. */
.ruled-writing .link, .ruled-writing a { color: #2f5d86; cursor: pointer; }
.ruled-writing .link:hover, .ruled-writing a:hover { text-decoration: underline; }

/* THE TIMESTAMP IS A MARGIN NOTE, NOT A LINE OF WRITING.

   It is ten pixels tall and it was claiming a whole ruled row, then landing on
   the left where it collided with whatever field happened to end there. A page
   number on paper sits in the bottom margin out of the way of the writing.

   Absolutely positioned, so it consumes no row at all, and placed inside the
   bottom margin the sheet already reserves - which is why there is clear space
   under the last line for it to sit in. */
.page.ruled-host > .page-no {
  position: absolute;
  right: var(--write-right);
  bottom: calc(var(--rule-spacing) / 2);
  left: auto;
  font-family: var(--font-technical);
  font-size: 9.5px;
  line-height: 1;
  letter-spacing: 0.08em;
  color: var(--paper-dim);
  background: var(--paper);
  padding: 0 0 0 10px;
}
