/*
  Textbook: a warm, type-led document style. Syne 600 for every heading,
  Albert Sans for body copy, a cream page opening on a caramel band, with
  bottle green carrying the structural fills and rust and ochre each holding
  one job.

  From St. Clair AI, Lahe Styles (https://stclair.ai/lahe-styles.html),
  version 1.1.1. This style's page: https://stclair.ai/lahe-styles/textbook.html

  Load this file after lib/templates/system-tokens.css and
  lib/templates/document.css. It sets its own raw palette, points the base's
  colour roles at it, self-hosts its two fonts from ./fonts/, and adds rules
  only where Textbook differs; every other rule in the base resolves correctly through the
  roles and is not restated here. The layout (the chapter-opener section
  head, the margin column, the opening) is the last block in the file, so
  this one file is the whole style over the base.

  Where it came from. F Textbook is the direction Ken called "oh I really
  like this overall," with the caramel and indigo pairing praised as retro
  plus modern. P, F straightened, contributed the caramel band as an opening.
  G's mechanical fixes apply: no single-side coloured borders, straighter
  figures, smaller radii, and here radii go to zero, since square cards were
  a keep for this family. Syne sits at 600 for every heading, never 700 or
  800: Syne 800 read as a stretched face in review. Two other directions were
  drafted and not picked; they are kept in directions/ as history.

  Measured contrast. Every number from scripts/contrast.py, which is the only
  place these are produced, written here by scripts/sync_headers.py. Floors:
  4.5:1 for body and anything under 24px, 3.0:1 for a drawn mark or rule.

  BEGIN MEASURED
    body text on the page ground                #2a1b12 on #fff6ea    15.52:1
    secondary copy and the lede                 #5c4a3d on #fff6ea     7.84:1
    section numbers, captions, meta             #6e5b4c on #fff6ea     6.01:1
    a link in running prose                     #4413c1 on #fff6ea     9.30:1
    body text on the raised ground              #2a1b12 on #f5e6d3    13.56:1
    body text in the default panel              #2a1b12 on #e0ede9    13.81:1
    body text on the accent tint and code       #2a1b12 on #ece7fb    13.76:1
    body text on the emphasis tint              #2a1b12 on #f6e3d8    13.37:1
    the insight label on its tint               #4413c1 on #ece7fb     8.25:1
    the rust caveat label on its tint           #984413 on #f6e3d8     5.33:1
    text on the bottle green band and callout   #ffffff on #1f5c55     7.72:1
    secondary copy on the bottle green band     #dcebe6 on #1f5c55     6.27:1
    the section number on the bottle green band #c8ddd6 on #1f5c55     5.43:1
    the dot bullet on the bottle green band     #c8ddd6 on #1f5c55     5.43:1
    text on the indigo band and callout         #ffffff on #4413c1     9.96:1
    text on the rust band and callout           #ffffff on #984413     6.62:1
    the section number on the rust band         #c8ddd6 on #984413     4.66:1
    ink on the ochre callout                    #2a1b12 on #e0a53a     7.61:1
    the dark caramel dot bullet on the ground   #a85a1a on #fff6ea     4.74:1
    the dark caramel dot on the default tint    #a85a1a on #e0ede9     4.21:1
    the dark caramel all-day label, 12px text   #a85a1a on #fff6ea     4.74:1
    the dark caramel hanging rule               #a85a1a on #fff6ea     4.74:1
    the dark caramel eyebrow, step, side label  #a85a1a on #fff6ea     4.74:1
    the dark caramel large stat number          #a85a1a on #fff6ea     4.74:1
    a stat number on the default tint           #2a1b12 on #e0ede9    13.81:1
    a stat label and meta line on the ground    #6e5b4c on #fff6ea     6.01:1
    a stat label on the default tint            #6e5b4c on #e0ede9     5.35:1
    the indigo badge on its tint                #4413c1 on #ece7fb     8.25:1
    the rust badge on its tint                  #984413 on #f6e3d8     5.33:1
    white on the bottle green badge             #ffffff on #1f5c55     7.72:1
    the bottle green status icon on the ground  #1f5c55 on #fff6ea     7.21:1
    the rust emphasis icon on the ground        #984413 on #fff6ea     6.19:1
    a plate caption on the ground               #5c4a3d on #fff6ea     7.84:1
    the error panel's red label on its tint     #a8231c on #fbeceb     6.26:1
    body text in the error panel                #2a1b12 on #fbeceb    14.48:1
    the error panel's red border on the ground  #a8231c on #fff6ea     6.72:1
    masthead and hero text on the caramel band  #2a1b12 on #da8b53     6.17:1
    the lede and meta line on the caramel band  #43301f on #da8b53     4.64:1
    a link inside the caramel band              #2a1b12 on #da8b53     6.17:1
    a list dot inside the caramel band          #2a1b12 on #da8b53     6.17:1
  END MEASURED

  Pairs tried and dropped, with their numbers, are listed in scripts/contrast.py
  under REJECTED: caramel or ochre as a mark on cream, white on caramel or
  ochre, and an indigo link inside the caramel band. Each is why a rule or a
  role below is what it is.
*/

/* ---------- fonts, self-hosted ----------
   A Google Fonts <link> is not enough for a style folder: a style has to be
   a folder the switcher can point at, and a folder that reaches the network
   for its type renders wrong on a plane, behind a proxy, or in a PDF export.
   One file per family, not one per weight, because Google's css2 endpoint
   returns the same latin woff2 for every weight of a variable font.

   | File                       | Family      | Range declared | Google version |
   | -------------------------- | ----------- | -------------- | -------------- |
   | syne-variable.woff2        | Syne        | 600 to 700     | v24            |
   | albert-sans-variable.woff2 | Albert Sans | 400 to 700     | v4             |

   Both are SIL Open Font License 1.1. fonts/LICENSE carries the licence and
   both copyright lines. To update: fetch the css2 URL with a browser
   User-Agent so Google returns woff2 blocks, pull the url() out of each latin
   block, copy the files over these, and update the version column above.

     curl -H "User-Agent: Mozilla/5.0 ..." \
       "https://fonts.googleapis.com/css2?family=Syne:wght@600;700&family=Albert+Sans:wght@400;500;600&display=swap"

   The fallback stacks in --font-display and --font-body stay in place, so a
   page that cannot reach these files still gets a real face. */

@font-face {
  font-family: "Syne";
  font-style: normal;
  /* Headings are 600 and never higher: Syne 800 read as a stretched face in
     review, and 700 exists only for the one bold label the day page uses. */
  font-weight: 600 700;
  font-display: swap;
  src: url("./fonts/syne-variable.woff2") format("woff2");
}

@font-face {
  font-family: "Albert Sans";
  font-style: normal;
  /* The range runs to 700 rather than stopping at 600, because a <b> in body
     prose asks the browser for 700. With no face covering it the browser
     smears the 600 into a fake bold, which is visible beside real Syne 600. */
  font-weight: 400 700;
  font-display: swap;
  src: url("./fonts/albert-sans-variable.woff2") format("woff2");
}

:root{
  /* ---------- type ----------
     Syne 600 for every heading, Albert Sans for everything else. There is no
     mono file in this folder, so the mono token is the platform stack: a
     document gets a real monospace rather than a download it cannot make. */
  --font-display:"Syne","Albert Sans",system-ui,sans-serif;
  --font-body:"Albert Sans",system-ui,sans-serif;
  --font-mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
  --w-display:600;
  --w-bold:700;

  /* ---------- the palette, in Textbook's own names ----------
     Warm: cream ground, caramel for the opening, a darkened caramel for marks,
     rust for emphasis, ochre for the one light fill. Cool: indigo for the
     accent and links, bottle green for the structural fills. The base's raw
     names (cobalt, sage, purple) are not touched; the roles below are. */
  --cream:#fff6ea;
  --cream-tint:#f5e6d3;
  --caramel:#da8b53;
  /* A darkened caramel, for marks that have to read on a light ground. #da8b53
     measures 2.52:1 on cream, which cannot carry a 7px dot or a 2px rule. */
  --caramel-mark:#a85a1a;
  --rust:#984413;
  --rust-deep:#7a370f;
  --rust-tint:#f6e3d8;
  --ochre:#e0a53a;           /* a fill only: under the 3:1 mark floor on cream (REJECTED in contrast.py), so never a mark or text */
  --indigo:#4413c1;
  --indigo-deep:#35109a;
  --indigo-soft:#ece7fb;
  --bottle-green:#1f5c55;
  --bottle-tint:#e0ede9;     /* the default panel, green */
  --bottle-mist:#dcebe6;     /* secondary copy on bottle green */
  --bottle-haze:#c8ddd6;     /* section numbers and dots on bottle green */
  --warm-ink:#2a1b12;
  --warm-ink-soft:#5c4a3d;
  --warm-ink-faint:#6e5b4c;
  --umber:#43301f;           /* soft text on the caramel band */
  --sand:#e8d9c7;            /* hairlines */

  /* Families for the specimen's palette view; the format is explained where
     system-tokens.css declares the house version. Sand sits with cream as its
     darker hairline shade, and umber with the inks as a warm ink. */
  --palette-families:"cream cream-tint sand: The page ground, its raised ground and hairlines \
    | warm-ink warm-ink-soft warm-ink-faint umber: Body text, secondary copy and meta \
    | caramel caramel-mark: The opening band, and in its dark shade the dots and hanging rule \
    | indigo indigo-deep indigo-soft: The accent: links, the insight label, code \
    | rust rust-deep rust-tint: Emphasis: the caveat, the emphasis band and callout \
    | bottle-green bottle-tint bottle-mist bottle-haze: The structural band and callout, and the default panel \
    | ochre: The one light fill, carrying ink";

  /* ---------- roles ---------- */
  --surface:var(--cream);
  --surface-raised:var(--cream-tint);
  --on-surface:var(--warm-ink);
  --on-surface-soft:var(--warm-ink-soft);
  --on-surface-faint:var(--warm-ink-faint);
  --border:var(--sand);

  --accent:var(--indigo);              /* links, the insight label, facts icons */
  --accent-hover:var(--indigo-deep);
  --accent-tint:var(--indigo-soft);    /* code and the insight panel */
  --link:var(--indigo);
  --focus:var(--indigo);

  --mark:var(--caramel-mark);          /* dots, accent numerals, the hanging rule */
  --fill:var(--ochre);                 /* the light fill, which carries ink */
  --on-fill:var(--warm-ink);
  --tint:var(--bottle-tint);             /* the default panel, green */
  --emphasis:var(--rust);              /* the caveat, the emphasis band and callout */
  --emphasis-hover:var(--rust-deep);
  --emphasis-tint:var(--rust-tint);

  --structure:var(--bottle-green);     /* the band, the default callout, the icon circle */
  --on-structure:#ffffff;
  --on-structure-soft:var(--bottle-mist);
  --on-structure-faint:var(--bottle-haze);

  /* ---------- lines and radius ----------
     The hanging rule is the dark caramel, which is the one place this
     direction spends its warm accent on structure. Zero radius throughout:
     square cards were the keep for this family, and a rounded corner on a
     cream ground reads softer than the type wants. */
  --divider:2px solid var(--caramel-mark);
  --line:1px solid var(--sand);
  --line-ink:1px solid var(--warm-ink);
  --r:0px;
  --r-lg:0px;
}

/* ---------- the opening band ----------
   A caramel band behind the masthead and the hero. It is done without a
   wrapper element, because a style never gets its own HTML: the two elements
   drop the column's max-width and take the same inset back as padding, so the
   background runs edge to edge while the text stays on the column. The inset
   is the base's --layout-bleed, which repeats the page rule's arithmetic and
   holds at the plain gutter once the viewport is narrower than the column.

   No vw unit here on purpose: 100vw includes the scrollbar, and the guide says
   the page never scrolls sideways at any width.

   The selector mirrors the page rule's own shape so the two carry the same
   specificity and this file, loading second, wins. A plain .masthead loses to
   the page rule and the band silently stays column width. */
:is(body, .band) > :is(.masthead, .hero){
  background:var(--caramel);
  max-width:none;
  padding-inline:var(--layout-bleed);
  /* The roles are repointed for the band's own ground rather than each rule
     being rewritten, so .masthead .meta and .hero .lede keep resolving through
     the base. Indigo measures 3.70:1 on caramel, under the floor, so a link
     inside the band is ink and keeps the base's underline as its affordance. */
  --on-surface-soft:var(--umber);
  --on-surface-faint:var(--umber);
  --link:var(--on-surface);
}

/* A hero on its own ground carries the full section gap inside that ground.
   The base keeps a bare hero's bottom padding small because the next section
   brings its own top space; a coloured band ends at its own edge, so the room
   under the lede has to live inside the band or the opening reads tight. */
:is(body, .band) > .hero{padding-block-end:var(--section-gap)}

/* ---------- the opening band in rendered Markdown ----------
   Lahe and build_styled_doc.py set a Markdown document inside <main>, which
   already sits in the column, so the hero is not a body child and the band
   above never reached it: in 1.1 a Markdown document under Textbook opened
   on plain cream (the 1.1.1 audit).

   Inside main the band cannot drop the column's width, so the caramel is
   painted past the box's sides instead: a border-image whose fill is one
   flat colour (the gradient has a single stop, so nothing blends) and whose
   outset reaches the window's edges. An outset is paint, not layout, so it
   never makes the page scroll sideways, which is what kept vw out of the
   rule above.

   What the band holds depends on the hero. A hero that is only the title
   and a lede is banded whole, as in the skeleton. A hero that also holds
   body copy (a list, a table, more paragraphs) bands the title alone, and
   the body copy reads on cream below it; a long table on caramel would turn
   the opening into one orange block. */
main > .hero:not(:has(> :not(h1,.eyebrow,.lede,h1 + p:last-child))),
main > .hero:has(> :not(h1,.eyebrow,.lede,h1 + p:last-child)) > :is(h1,.eyebrow){
  background:var(--caramel);
  border-image:linear-gradient(var(--caramel) 0 0) fill 0 / 0 / 0 100vmax;
  --on-surface-soft:var(--umber);
  --on-surface-faint:var(--umber);
  --link:var(--on-surface);
  --mark:var(--on-surface);
}
main > .hero:not(:has(> :not(h1,.eyebrow,.lede,h1 + p:last-child))){padding-block:var(--section-gap)}
main > .hero:has(> :not(h1,.eyebrow,.lede,h1 + p:last-child)){padding-block-start:0}
main > .hero:has(> :not(h1,.eyebrow,.lede,h1 + p:last-child)) > h1{
  margin:0 0 var(--section-gap-narrow);
  padding-block:var(--section-gap) var(--section-gap-narrow);
}
main > .hero:has(> :not(h1,.eyebrow,.lede,h1 + p:last-child)) > h1:not(:first-child){padding-block-start:var(--s2)}
main > .hero:has(> :not(h1,.eyebrow,.lede,h1 + p:last-child)) > .eyebrow:first-child{margin:0;padding-block-start:var(--section-gap)}

/* ---------- list markers inside a band ----------
   The base leaves a dot at --mark and a numeral at --on-surface even inside a
   band, so both land in a page-ground colour on a saturated one. Both take
   the band's own faint colour instead; the measured pair is above. */
.band ul li::before{background:var(--on-structure-faint)}
.band ol li::before{color:var(--on-structure-faint)}

/* ---------- the components added in 1.1 ----------
   Most of them resolve through the roles above with no rule here: the badge
   fills, the checklist's check, the option card, the image frames, the icon
   colours, and the error panel, which keeps the base's own danger red and
   tint. What follows is only where Textbook's voice differs. */

/* Syne's tabular figures are set wide and its default 1 and 2 sit low, so a
   stat or a step number read as a row of odd shapes. These numbers stand
   alone rather than in a column, so they take lining, proportional figures;
   the data table keeps the base's tabular numerals in Albert Sans. */
.stat-value,ol.sequence > li::before{font-variant-numeric:lining-nums proportional-nums}

/* The single large stat is drawn like a chapter numeral: dark caramel, the
   one warm mark in its section. The tiles keep their numbers in ink. */
.stat--large .stat-value{color:var(--mark)}

/* Step numbers are the chapter numeral in miniature, in the dark caramel
   rather than the base's accent, so a sequence reads as part of the chapter
   it sits in. Indigo stays for links and the insight label. */
ol.sequence > li::before{
  font-size:var(--text-h3);
  line-height:1;
  color:var(--mark);
}
ol.sequence--rows > li::before{font-size:var(--text-h4);line-height:var(--lh-heading)}

/* The eyebrow is the side head's voice: dark caramel Syne at meta size. */
.eyebrow{color:var(--mark)}

/* The ruled pull quote takes the chapter rule, 2px dark caramel, above and
   below, so the one loud quote is ruled the way a chapter title is. */
blockquote.pullquote.pullquote--ruled{border-block:var(--divider)}

/* The status icon. The base paints it in the mark colour, which here is the
   dark caramel, and beside the rust emphasis icon the two read as one
   colour. Status takes bottle green instead, the structural colour, so the
   four icon colours stay four. */
:is(.icon,svg,i).icon--sage{color:var(--structure)}

/* =========================================================================
   LAYOUT: the chapter opener.

   Everything above sets colour, type and lines. Everything below changes
   structure only, on the same markup every style shares: where the section
   head's rule and number sit, the page grid, alignment, the opening, and
   rhythm. It reads the base's layout tokens (--layout-margin,
   --layout-numeral, --layout-bleed) rather than inventing its own.

     1. Each section opens like a textbook chapter. A large caramel numeral
        stands above the heading and the rule moves below the heading, so the
        rule closes the title instead of hanging over it (the house
        signature this style moves away from).
     2. A left margin column carries marginal notes: an h4 that is a direct
        child of a section becomes a side head set right-aligned against the
        text, and a figure's caption sits in the margin beside its image.
     3. The opening band stays, and the title inside it starts on the main
        column's edge, so the whole page hangs from one vertical line.
     4. Under 820px the margin folds away: notes and captions return inline,
        and the numeral drops to title size.
   ========================================================================= */

:root{
  --layout-margin:12rem;                          /* the margin column for notes and captions */
  --layout-numeral:calc(var(--text-h1) * 2.6);    /* the chapter numeral */
  /* How far from the column's inner edge prose may run, margin column
     included. The house prose ends 68ch in (about 40.4rem); this allows 7rem
     more, so the margin does not cost the text its measure, and still ends
     prose before a Lahe review rail docked at the right of a 1280 or wider
     window (the rail covers the right 390px). Tables, code and images keep
     the full column, as in the house. */
  --layout-prose-edge:47.5rem;
}

/* The numeral is counted, not read, because the markup carries "Section N"
   as one run of text. Only a head that has a number counts, so the check-in
   block (a timestamp, no number) does not shift the count. The counter lives
   on html so it cannot collide with a style's own body counter. */
html{counter-reset:chapter}
.sheet-head:not(.sheet-head--medium):has(.n),.fold-head:has(.n){counter-increment:chapter}

/* ---------- the chapter head ----------
   Numeral, then heading, then the rule under both. The "Section N" text stays
   in the page for a screen reader and for copy-paste; only its glyphs are
   sized to nothing, and the numeral is drawn in their place. */
.sheet-head,.checkin .checkin-head{
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  border-top:0;
  padding-top:0;
  border-bottom:var(--divider);
  padding-bottom:var(--s3);
}
.sheet-head:not(.sheet-head--medium) .n{
  order:-1;
  font-size:0;
  line-height:1;
  margin-bottom:var(--s2);
}
/* A chapter title is set a step larger than a plain section heading. */
.sheet-head h2{font-size:calc(var(--text-h2) * 1.3);line-height:1.05}
.sheet-head:not(.sheet-head--medium) .n::before{
  content:counter(chapter);
  display:block;
  font-size:var(--layout-numeral);
  line-height:.85;
  letter-spacing:-.02em;
  color:var(--mark);
}
.band .sheet-head{border-bottom-color:var(--on-structure-faint)}
.band .sheet-head .n::before{color:var(--on-structure-faint)}

/* The two smaller heads keep the chapter head's order (heading, then the
   rule under it) and drop one thing each, as in the base: the medium head's
   rule is 1px ink and its label is a side head in the dark caramel; the
   small head's rule is a hairline and it has no label. Neither draws a
   numeral or counts as a chapter. */
.sheet-head--medium{border-bottom:var(--line-ink);padding-bottom:var(--s2)}
.sheet-head--small{border-bottom:var(--line);padding-bottom:var(--s2)}
.sheet-head--medium .n{
  order:-1;
  font-family:var(--font-display);
  font-size:var(--text-meta);
  line-height:1.3;
  color:var(--mark);
}

/* The folded section keeps its chevron beside the heading; the numeral takes
   its own line above them, and the rule moves under the row. */
details.fold summary.fold-head{
  border-top:0;
  padding-top:0;
  border-bottom:var(--divider);
  padding-bottom:var(--s3);
}
details.fold summary.fold-head .n{
  order:-1;
  flex:1 0 100%;
  font-size:0;
  line-height:1;
}
details.fold summary.fold-head .n::before{
  content:counter(chapter);
  font-size:var(--layout-numeral);
  line-height:.85;
  color:var(--mark);
}

/* ---------- the margin column ---------- */
@media (min-width:821px){
  section.sheet{
    display:grid;
    grid-template-columns:[margin] var(--layout-margin) [main] minmax(0,1fr);
    column-gap:var(--section-gap-narrow);
    align-items:start;
  }
  section.sheet > *{grid-column:main}

  /* The head spans the margin too, so the rule under the title runs the full
     width of the grid and shows where the margin column starts. */
  section.sheet > .sheet-head{
    grid-column:margin / -1;
    padding-inline-start:calc(var(--layout-margin) + var(--section-gap-narrow));
  }

  /* A side head. Auto-placement puts it on the row after the block above, in
     the margin, and the block that follows it lands beside it on that row. */
  section.sheet > h4{
    grid-column:margin;
    margin:0;
    padding-top:var(--s1);
    text-align:right;
    font-size:var(--text-small);
    line-height:1.3;
    color:var(--mark);
  }

  /* The medium head spans the margin like the chapter head, so its rule
     runs the same width; its label stands in the margin as a side head, on
     the heading's baseline. The small head stays in the main column. */
  section.sheet > .sheet-head--medium{
    display:grid;
    grid-template-columns:var(--layout-margin) minmax(0,1fr);
    column-gap:var(--section-gap-narrow);
    align-items:baseline;
    padding-inline-start:0;
  }
  .sheet-head--medium > :not(.n){grid-column:2;grid-row:1}
  .sheet-head--medium > .n{grid-column:1;grid-row:1;text-align:right}
  section.sheet > .sheet-head--small{grid-column:main;padding-inline-start:0}

  /* A figure spans both columns so its caption can sit in the margin, set
     against the image's foot the way a textbook sets a marginal caption. A
     plate keeps its own caption strip, inside its frame, so it is left out. */
  section.sheet > figure:not(.plate){
    grid-column:margin / -1;
    display:grid;
    grid-template-columns:var(--layout-margin) minmax(0,1fr);
    column-gap:var(--section-gap-narrow);
    align-items:end;
  }
  section.sheet > figure:not(.plate) > img{grid-column:2}
  section.sheet > figure:not(.plate) > figcaption{
    grid-column:1;
    grid-row:1;
    margin:0;
    text-align:right;
    padding-top:var(--s2);
    border-top:var(--divider);
  }

  /* The opening hangs from the same line as the sections: the title and the
     lede start where the main column starts. The band's own inset is the
     bleed, and the margin and its gap are added to it. */
  :is(body, .band) > :is(.masthead, .hero){
    padding-inline-start:calc(var(--layout-bleed) + var(--layout-margin) + var(--section-gap-narrow));
  }

  /* The band sections and the fold carry no grid, so their content moves to
     the main column by the same offset. The selectors match the page rule's
     specificity, which sets the plain gutter, so this rule wins by order. */
  .band > div:not(.band),:is(body, .band) > :is(details.fold, .checkin){padding-inline-start:calc(var(--gutter) + var(--layout-margin) + var(--section-gap-narrow))}

  /* In rendered Markdown the hero sits inside main, which already carries
     the column and its gutter, so only the margin and its gap are added. */
  main > .hero{padding-inline-start:calc(var(--layout-margin) + var(--section-gap-narrow))}

  /* ---------- the measure ----------
     Prose runs to --layout-prose-edge from the column's inner edge, margin
     column included, so the margin does not push the text under a Lahe
     review rail docked at the right of the window; in 1.1 it did on a 1440
     screen. A chapter title keeps to the same edge. */
  :root{--read:calc(var(--layout-prose-edge) - var(--layout-margin) - var(--section-gap-narrow))}
  .sheet-head h2{max-width:var(--read)}
  /* The title and lede, which the base lets run past the measure, keep to
     it, so the opening clears the rail too. */
  .hero h1{max-width:min(17ch, var(--read))}
  .hero .lede{max-width:var(--read)}
  /* The base caps a pull quote at half the measure. Half of this measure,
     now a length rather than 68ch of the quote's own large type, is a few
     words a line, so the quote takes the whole measure. */
  blockquote.pullquote p{max-width:var(--read)}
  /* A band carries no grid, so its head is pulled back over the margin and
     its rule runs the same width as every other chapter rule. */
  .band .sheet-head{
    margin-inline-start:calc(-1 * (var(--layout-margin) + var(--section-gap-narrow)));
    padding-inline-start:calc(var(--layout-margin) + var(--section-gap-narrow));
  }

  /* A block set straight in the body, between sections (a callout, a panel,
     a paragraph), starts on the main column's line, the line the whole page
     hangs from, rather than on the column's outer edge. The box moves, not
     only its text, so a coloured block lines up with the text above it. */
  body > :is(.callout,.panel,.insight,.stats,.stat,.badges,.options,.media,.gallery,.image-cards,.cols,.facts,p,ul,ol,blockquote,pre,.scrollx,table,figure:not(.bleed)):not(.band){
    max-width:none;
    margin-inline:calc(var(--layout-bleed) + var(--layout-margin) + var(--section-gap-narrow)) var(--layout-bleed);
  }
  /* A block with an inset of its own (a ground, a stat tile, a code block,
     a quote) keeps the page's inline padding as that inset; anything else
     starts its text right on the line. */
  body > :is(.stats,.badges,.options,.media,.gallery,.image-cards,.cols,.facts,p,ul,ol,.scrollx,table,figure:not(.bleed)):not(.band){padding-inline:0}

  /* A full-width image runs edge to edge over the margin and the main
     column alike; its caption comes back to the main column's edge, the
     line the whole page hangs from, rather than the column's outer edge. */
  .bleed > figcaption{padding-inline-start:calc(var(--gutter) + var(--layout-margin) + var(--section-gap-narrow))}
}

/* ---------- narrow screens ----------
   The margin is gone, so the numeral comes down to title size: at the full
   size it would take a third of a phone's first screen. */
@media (max-width:820px){
  :root{--layout-numeral:var(--text-h1)}
}
