/* THE BLOG'S READING SURFACE.
 *
 * Loaded on single posts ONLY (includes/blog-typography.php gates on
 * is_singular('post')), so nothing here can reach the tracker, the health page,
 * the sources page or any layoffs CPT permalink.
 *
 * THIS FILE FIGHTS TWO STYLESHEETS THAT ARE NOT ON THE FILESYSTEM. Both are
 * held in the WordPress database and emitted inline, so no grep of either repo
 * finds them. Measured off the live post on 2026-08-15 at ver=2.20.55 by
 * walking document.styleSheets:
 *
 *   1. <style id="wp-block-library-inline-css">, i.e. somebody's
 *      wp_add_inline_style('wp-block-library', ...). It carries, among much
 *      else:
 *         .entry-content p  { font-size:1.05rem!important; line-height:1.78!important }
 *         .entry-content h2 { font-size:1.45rem!important; margin:2.2rem 0 .8rem!important }
 *         .entry-content h3 { font-size:1.15rem!important; margin:1.5rem 0 .5rem!important }
 *      The `0` in those two margin SHORTHANDS is the whole "two columns" bug.
 *      WP core centres a constrained child with
 *         .is-layout-constrained > :where(:not(.alignfull)) { margin-left:auto!important }
 *      which is specificity (0,1,0). `.entry-content h2` is (0,1,1), so the
 *      shorthand's margin-left:0 wins and EVERY heading sits flush at the
 *      container's left edge while the paragraph it introduces is centred.
 *      Measured at 1600px: H2 x=50, the paragraph under it x=470. 420px apart.
 *
 *   2. <style id="global-styles-inline-css">, i.e. Site Editor > Styles >
 *      Additional CSS (stored in the wp_global_styles post). Inside
 *      @media (max-width:781px) it carries
 *         .entry-content.alignfull, .wp-block-group.alignfull {
 *            margin-left:0!important; margin-right:0!important; max-width:100%!important }
 *      which cancels core's `.has-global-padding > .alignfull { margin-inline:
 *      -root-padding }`. With the negative margins gone the gutters STACK
 *      instead of cancelling: main 18px + group 30px + entry-content 30px =
 *      78px a side. At 375px that left the paragraph 219px wide, about 26
 *      characters per line. Same mechanism as the tracker's own 2026-08-04
 *      defect (TECHLOG); this is it seen on the blog.
 *
 * SO EVERY RULE BELOW IS SCOPED `body.single-post .entry-content ...`, which is
 * at least (0,2,2), and carries !important where it is contesting an
 * !important. That is not decoration: without both it loses silently, which is
 * exactly how the tracker's corrective rule sat dead in the tree for months.
 *
 * NEVER USE THE `margin` SHORTHAND IN THIS FILE. Writing `margin: 2rem 0 1rem`
 * on a constrained child is the bug at the top of this comment. Set
 * margin-block-start / margin-block-end and leave margin-inline alone (or set
 * it to auto explicitly).
 *
 * ------------------------------------------------------------------------
 * THE THIRD DEFECT, ADDED 2026-08-15: THE PAGE HAD EXACTLY ONE WIDTH.
 *
 * Measured at 2000px on the live post at ver=2.20.60: the paragraph sat at
 * x=677.5 and was 645px wide, correctly centred, 677.5px of air each side. So
 * was the h1. So was the featured image. So was the table of contents. So was
 * every signup box. A 6317px-tall article in which every single element is
 * clamped to the same 645px reads, zoomed out, as one narrow ribbon down the
 * middle of a very wide screen, and the owner described it exactly that way.
 *
 * Nothing was misaligned. There was simply no second width for the eye to
 * measure the first against, so the column had no scale and the page had no
 * rhythm. The answer is not a wider column - 645px at 20px is already the
 * right measure - it is to stop the page being ONE column of one width:
 *
 *   --alt-read-media   media sits WIDER than the text. The featured image and
 *                      any in-article figure run to 820px on a desktop and
 *                      1040px on a very wide screen against a 645-700px
 *                      measure. That contrast is what says "designed".
 *   the measure        grows once, modestly, at 1400px: 645 -> 700px with the
 *                      body at 21px. Characters per line is the constraint,
 *                      not pixels, so the two move together and the measure
 *                      stays inside the same reading band it always was.
 *   the contents box   stops being a bordered box. Its 1px border was one of
 *                      the vertical lines the ribbon was made of; it is now a
 *                      quiet band ruled top and bottom (horizontal lines cut
 *                      the ribbon, vertical ones draw it) and set in two
 *                      columns on a wide screen, which is a third texture.
 *
 * Widths are expressed as `min(<cap>, 100%)`, never `calc(100vw - x)`: 100vw
 * includes the classic scrollbar, so a vw-based cap overflows by ~15px on the
 * narrow-desktop widths where it binds. 100% is the constrained child's own
 * containing block, which is already the right number.
 *
 * ------------------------------------------------------------------------
 * PASS THREE, SAME DAY. THE SECOND WIDTH ARRIVED AND THE FIRST ONE DID NOT
 * MOVE WITH IT.
 *
 * The owner read the result and named four things. They are one thing seen
 * from four sides: the page above got a media step, a frame it never had and
 * a heading scale, and the READING COLUMN was left exactly where it started.
 *
 *   1. THE TEXT WAS STARVED BESIDE ITS OWN ILLUSTRATION. 700px of text against
 *      a 1040px image at 2000px, 1.49x. The measure now climbs in three steps
 *      with the type - 720/21, 780/22, 820/23 - and the media comes back down
 *      toward it, so the ratio is about 1.25x at every step instead of 1.49x
 *      at the last one. Characters per line is still the only constraint that
 *      decides this; the table under "the measure climbs" shows the working.
 *
 *   2. THE CONTENTS READ AS A PLUGIN WIDGET. Not because of the rule above it
 *      - that part of 2.20.61 was right - but because of 16px grey sans set in
 *      two columns. It is one column at every width now, in the article's own
 *      serif at three pixels under the body. The two columns existed to hide a
 *      525px slab made by a 44px tap floor applied at every width; the floor
 *      is scoped to touch and the slab is gone, so the workaround goes too.
 *
 *   3. THE PAGE READ AS ONE FLAT BLOCK. h2 was 30px over a 20px body, 1.50x,
 *      with 48px above it. Size and space are different signals and both
 *      moved: 1.70x on a desktop, and 80px of air, which is two and a half
 *      blank lines. The scale still steps - every neighbouring ratio is inside
 *      1.05-1.45 and the h1 is still under 2.25x the body.
 *
 *   4. THE ARTICLE WAS A THIN STRIP FLOATING IN WHITE. Section 2d caps the
 *      frame at 1300px AND puts it on a ground, because a cap alone is
 *      invisible on a page whose contents were already narrower than it.
 *
 * NONE OF 1-4 REACHES A PHONE. Below 1024px there is no card and no ground;
 * below 1200px the measure and the type are what they were. The phone's
 * paragraph is 18px in and 339px wide at 375, byte-identical across all three
 * passes, and that is the bar every one of these was designed under.
 */

/* ------------------------------------------------------------------ scale */
body.single-post {
  /* Type. Two steps rather than a curve: the whole page changes at the same
     781px boundary the theme's own gutters change at, so there is one number
     to reason about. */
  --alt-read-body: 20px;
  --alt-read-lh: 1.62;
  --alt-read-h1: 44px;
  --alt-read-h2: 34px;
  --alt-read-h3: 26px;
  --alt-read-h4: 20px;

  /* Rhythm. One unit, everything is a multiple of it. */
  --alt-read-unit: 1rem;

  /* Space above an h2, in units. This is a token rather than a number in the
     rule because it is one of the two things that make a section begin, and
     the phone wants less of it than a 2000px screen does. 5 units is 80px
     against a 32.4px line: two and a half blank lines, which is what the eye
     reads as "a new section" from a scroll. */
  --alt-read-h2-air: 5;

  /* The SECOND width. Media is deliberately wider than the measure on any
     screen that has the room, and exactly the measure on one that does not.
     `100%` here is the constrained child's containing block, i.e. the article
     minus the theme's own 50px gutters, so the cap is the only thing that can
     bind and nothing can overflow. */
  --alt-read-media: min(820px, 100%);

  /* The FRAME and its GROUND. The article stops spreading at 1300px and sits
     on a surface that is not the page, so a 2000px screen shows a document
     rather than a strip of text in an empty room. Both are inert below
     1024px: a phone has no room for a margin around the reading column, and
     inventing one is the stacked-gutter defect wearing better clothes. */
  --alt-read-frame: 1300px;
  --alt-read-ground: #eef1f5;

  --alt-read-ink: #23272b;      /* body text */
  --alt-read-ink-strong: #14171a; /* headings */
  --alt-read-ink-soft: #55606a;   /* captions, meta, TOC */
  --alt-read-rule: #e2e6e9;
  --alt-read-serif: Vollkorn, Charter, "Bitstream Charter", "Iowan Old Style",
                    "Source Serif Pro", Georgia, "Times New Roman", serif;
  --alt-read-sans: Manrope, ui-sans-serif, system-ui, -apple-system,
                   "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --alt-read-mono: "Fira Code", ui-monospace, SFMono-Regular, Menlo, monospace;
}

@media (max-width: 781px) {
  body.single-post {
    --alt-read-body: 19px;
    --alt-read-lh: 1.6;
    /* The h2 is what pass three was asked to fix and it takes most of the
       increase, 24 -> 28px. The h1 moves only as far as it can without
       costing a line: a headline is already the largest thing on a 375px
       screen and the reference post's is 57 characters, so it wraps to three
       lines at 30, 31 and 32px and to FOUR at 33. Measured, not guessed. 37px
       was tried first and cost that line, which pushed the hero 53px down the
       opening screen to buy nothing a phone reader needed. 32px it is.

       The h2 therefore lands at 1.47x the body here against 1.70x on a
       desktop, and that difference is real rather than a shortfall. On a
       375px screen a section heading is the full width of the reader's view
       with nothing beside it; on a 2000px screen it is a short line inside a
       820px column, and it needs the extra size to be seen at all. The test
       holds the two bars separately and says so. */
    --alt-read-h1: 32px;
    --alt-read-h2: 28px;
    --alt-read-h3: 23px;
    --alt-read-h4: 19px;
    /* 4 units is 64px against a 30.4px line, so a section still announces
       itself, but a phone does not pay two and a half blank lines for every
       heading in an eleven-section article. */
    --alt-read-h2-air: 4;
    /* A phone has one width and that is correct: a hero narrower or wider
       than the 339px of text it introduces would only be a mistake. 100% is
       the article's own box, which at 375 and 414 IS the measure. Between
       about 684px and 781px the article is wider than the 645px measure and
       the hero takes the extra: an image that runs to the gutters on a tablet
       is the same idea as the desktop step, at the width that screen has. */
    --alt-read-media: 100%;
  }
}

/* THE MEASURE CLIMBS IN THREE STEPS, AND THE TYPE CLIMBS WITH IT.
 *
 * 2.20.61 moved it once, 645 -> 700px, and stopped. The media kept going, to
 * 1040px, so at 2000px the page showed 700px of text beside an image half
 * again as wide and the column looked starved beside its own illustration.
 * That is the owner's first note and it was a real proportion, not a taste.
 *
 * Pixels are not the constraint; characters per line is. Every step below
 * moves the column and the body size together so the line a reader tracks
 * back along stays the same length.
 *
 * AND "CHARACTERS PER LINE" IS A FONT MEASUREMENT, NOT A PIXEL ONE, WHICH IS
 * WHAT REDDENED CI FOR FOUR COMMITS. The first cut of this table was sized
 * against the two serifs a session can see from a Mac: live Vollkorn at
 * 0.4946em average glyph width, and the Charter the offline fixture falls back
 * to at 0.4824em. The Linux CI runner has neither, falls back a third time,
 * and that face is 0.4515em - about 9% narrower, so about 9% MORE characters
 * fit on the same column. 780px at 22px read 73.5 here and 78.5 there, and
 * 78.5 is past the top of the band.
 *
 * That is not a bad runner, it is a reader we had not measured. Anyone whose
 * device carries neither Vollkorn nor Charter gets the third face, and they
 * were being served a 79-character line. So the TYPE grew rather than the band
 * moving, and every step is now sized against the NARROWEST glyph metric
 * observed, not the widest:
 *
 *   step        measure   body   0.4515em   0.4824em   0.4946em   media   ratio
 *   base        645px     20px   71.4       66.9       65.2       820px   1.27x
 *   >= 1200     720px     22px   72.5       67.8       66.1       880px   1.22x
 *   >= 1500     780px     23px   75.1       70.3       68.5       980px   1.26x
 *   >= 1800     820px     24px   75.7       70.8       69.1       1040px  1.27x
 *
 * All twelve are inside 60-78. Size the next step against the LEFT column.
 *
 * The media:text ratio is the point of the table. It was 1.49x and is now
 * about 1.25x at every step: the image still leads the column, which is what
 * gives the page two widths, but it no longer leaves it behind. Nothing
 * upscales - the source hero is 1288px wide.
 *
 * Scoped to `main` rather than `body` so the site header and footer keep the
 * content-size they were designed against.
 */
@media (min-width: 1200px) {
  body.single-post main {
    --wp--style--global--content-size: 720px;
    --alt-read-body: 22px;
    --alt-read-h1: 48px;
    --alt-read-h2: 37px;
    --alt-read-h3: 28px;
    --alt-read-h4: 22px;
    --alt-read-media: min(880px, 100%);
  }
}

@media (min-width: 1500px) {
  body.single-post main {
    --wp--style--global--content-size: 780px;
    --alt-read-body: 23px;
    --alt-read-h1: 50px;
    --alt-read-h2: 39px;
    --alt-read-h3: 29px;
    --alt-read-h4: 23px;
    --alt-read-media: min(980px, 100%);
  }
}

@media (min-width: 1800px) {
  body.single-post main {
    --wp--style--global--content-size: 820px;
    --alt-read-body: 24px;
    --alt-read-h1: 52px;
    --alt-read-h2: 41px;
    --alt-read-h3: 31px;
    --alt-read-h4: 24px;
    --alt-read-media: min(1040px, 100%);
  }
}

/* ------------------------------------------------- 1. the stacked gutters
 *
 * Neutralise the TWO INNER .alignfull wrappers only. `main` keeps its 18px on
 * purpose: it is what the post title sits on, and zeroing it too would print
 * the h1 flush against the glass. This is the shape the tracker's fix landed
 * on in 2.19.264 and it is the same shape here.
 */
@media (max-width: 781px) {
  body.single-post .wp-site-blocks main .wp-block-group.alignfull.has-global-padding,
  body.single-post .entry-content.alignfull.has-global-padding {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
}

/* ------------------------------------------------ 2. the measure, and only
 * the measure. The theme's content-size is 645px, which at 20px Vollkorn is
 * about 67 characters: the right answer, and it is what this ships below
 * 1400px. Above 1400px the token block above moves it to 700px at 21px, which
 * is the same number of characters. What this section restores is the
 * CENTRING that the heading rules broke, for every block-level child of the
 * article.
 */
body.single-post .entry-content
  > :where(:not(.alignfull):not(.alignleft):not(.alignright):not(.alignwide)) {
  margin-left: auto !important;
  margin-right: auto !important;
}

/* Headings specifically, because the rule they are losing to is (0,1,1)
 * !important and names them one by one. `text-align` is set because the site
 * also ships an unscoped `.wp-block-heading { text-align:center }`, but an
 * author who deliberately centred or right-aligned a block keeps it. */
body.single-post .entry-content h1,
body.single-post .entry-content h2,
body.single-post .entry-content h3,
body.single-post .entry-content h4,
body.single-post .entry-content h5,
body.single-post .entry-content h6 {
  margin-left: auto !important;
  margin-right: auto !important;
  max-width: var(--wp--style--global--content-size, 645px) !important;
}

body.single-post .entry-content h1:not(.has-text-align-center):not(.has-text-align-right),
body.single-post .entry-content h2:not(.has-text-align-center):not(.has-text-align-right),
body.single-post .entry-content h3:not(.has-text-align-center):not(.has-text-align-right),
body.single-post .entry-content h4:not(.has-text-align-center):not(.has-text-align-right),
body.single-post .entry-content h5:not(.has-text-align-center):not(.has-text-align-right),
body.single-post .entry-content h6:not(.has-text-align-center):not(.has-text-align-right) {
  text-align: start !important;
}

/* Every block in the column is measured by its BORDER box. Without this a
 * blockquote's 3px rule and 1.1em padding, or a list's marker indent, are
 * added OUTSIDE the 645px max-width and the block sits wider than the text it
 * belongs to. The site only sets box-sizing globally under 1024px, so on a
 * desktop nothing else does it. */
body.single-post .entry-content,
body.single-post .entry-content > *,
body.single-post .entry-content ul,
body.single-post .entry-content ol,
body.single-post .entry-content blockquote,
body.single-post .entry-content pre,
body.single-post .entry-content table {
  box-sizing: border-box;
}

/* -------------------------------------------- 2b. media steps OUTSIDE the
 * measure. This is the single change that stops the page reading as one
 * ribbon: the featured image and any in-article figure are noticeably wider
 * than the paragraphs, so the column has something to be measured against.
 *
 * The featured image is NOT inside .entry-content - it is a sibling of the
 * post title inside the article group - so it needs its own selector and gets
 * no help from the entry-content rules above. Both selectors are scoped
 * `body.single-post` and carry !important because the WPCode database sheet
 * pins `max-width:100% !important` on several of these containers below
 * 782px; 100% is the right answer there anyway, and the token says so.
 */
body.single-post .wp-block-post-featured-image,
body.single-post .entry-content > figure,
body.single-post .entry-content > .wp-block-image,
body.single-post .entry-content > .wp-block-embed,
body.single-post .entry-content > .wp-block-video {
  max-width: var(--alt-read-media) !important;
  margin-left: auto !important;
  margin-right: auto !important;
}

/* A table is media by width and text by content: it may exceed the measure
   when it needs to, but it still scrolls inside its own box rather than the
   document (section 5 sets that). */
body.single-post .entry-content > .wp-block-table,
body.single-post .entry-content > figure.wp-block-table {
  max-width: var(--alt-read-media) !important;
  margin-left: auto !important;
  margin-right: auto !important;
}

body.single-post .wp-block-post-featured-image img,
body.single-post .entry-content > figure img {
  width: 100%;
  height: auto;
}

/* The hero earns a little more air under it than a mid-article figure, and
   the theme's own 32px is not enough once the image is 1040px wide. */
body.single-post .wp-block-post-featured-image {
  margin-block-end: calc(var(--alt-read-unit) * 2.6) !important;
}

/* ----------------------------------------- 2c. the gap above the headline
 * Measured live at 2000px: the site header ended at y=89 and the h1 began at
 * y=229. 140px of nothing, from `main`'s 70px margin plus the article group's
 * 70px padding, on a page whose first job is to start reading. 28 + 28 puts
 * the headline 56px under the header, which still reads as a clear break.
 * Below 782px the same two numbers are already 0 and 30, so this leaves the
 * phone exactly as it is.
 *
 * The 28px on `main` is PADDING rather than margin, which is not cosmetic:
 * section 2d paints `main` as the page's ground, and a margin would leave a
 * 28px band of the old white between the site header and the ground. Padding
 * and margin measure the same gap; only one of them is inside the paint.
 */
@media (min-width: 782px) {
  body.single-post .wp-site-blocks > main {
    margin-block-start: 0 !important;
    padding-block-start: calc(var(--alt-read-unit) * 1.75) !important;
  }
  body.single-post .wp-site-blocks > main
    > .wp-block-group.alignfull.has-global-padding {
    padding-block-start: calc(var(--alt-read-unit) * 1.75) !important;
  }
}

/* ------------------------------- 2d. the frame, and the ground under it
 *
 * The owner's fourth note: "at 2000px the article is a thin strip floating in
 * white". Two answers were on the table - cap the frame, or give the column a
 * ground - and this commits to both halves of one answer, because a cap on its
 * own changes nothing a reader can see. Everything on the page was ALREADY
 * narrower than the frame, so clamping the frame to 1300px moves no pixel; it
 * only becomes visible once the frame is a surface with an edge.
 *
 * So: `main` is the ground, the article group is a white card on it, capped at
 * 1300px and centred. At 2000px that is a 1300px document with 350px of ground
 * each side, instead of 820px of text with 590px of nothing each side. The
 * card is the thing the eye now measures the column against, which is the same
 * job the wide media does horizontally, done by the page frame.
 *
 * BELOW 1024px NONE OF THIS APPLIES. A phone has no screen to spend on a
 * margin around the reading column, and a card there would be the stacked
 * gutter defect with better manners. The page stays one edge-to-edge white
 * surface exactly as it shipped, and a test asserts that at 375, 414 and 768.
 *
 * The card takes `margin-left/right: auto`, which also cancels core's
 * `.has-global-padding > .alignfull { margin-inline: -50px }`. That is
 * deliberate: the negative margin exists to let a full-bleed child escape the
 * parent's gutter, and a capped card is the opposite intent. `.entry-content`
 * inside it still carries its own -50/+50 pair, so the text column is
 * unaffected and only the card's own edges move.
 */
@media (min-width: 1024px) {
  body.single-post .wp-site-blocks > main {
    background-color: var(--alt-read-ground) !important;
    padding-block-end: calc(var(--alt-read-unit) * 3) !important;
  }
  body.single-post .wp-site-blocks > main
    > .wp-block-group.alignfull.has-global-padding {
    max-width: var(--alt-read-frame) !important;
    /* The group carries 50px of global padding and the site does not set
       box-sizing above 1024px, so a content-box cap of 1300px renders a
       1400px card. The cap has to mean the card, not the card's contents. */
    box-sizing: border-box !important;
    margin-left: auto !important;
    margin-right: auto !important;
    background-color: #ffffff !important;
    border-radius: 14px !important;
    /* Two shadows, both nearly invisible: a 1px seat so the card has an edge
       even where the ground is pale, and a wide soft one so it reads as
       resting on the page rather than cut out of it. No image, no asset. */
    box-shadow: 0 1px 2px rgba(20, 23, 26, 0.05),
                0 10px 34px rgba(20, 23, 26, 0.055) !important;
  }
}

/* ------------------------------------------------------------- 3. the body */
body.single-post .entry-content {
  font-family: var(--alt-read-serif);
  color: var(--alt-read-ink);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  /* Lining figures, not Vollkorn's default oldstyle: this blog publishes
     counts and years inside sentences, and oldstyle digits read as lowercase
     letters when a number is the point of the sentence. */
  font-variant-numeric: lining-nums proportional-nums;
}

body.single-post .entry-content p,
body.single-post .wp-block-post-content p {
  font-family: var(--alt-read-serif) !important;
  font-size: var(--alt-read-body) !important;
  line-height: var(--alt-read-lh) !important;
  font-weight: 400 !important;
  letter-spacing: 0 !important;
  color: var(--alt-read-ink) !important;
  margin-block-start: 0 !important;
  /* In `em`, so the gap between paragraphs scales with the type instead of
     being pinned to the root. At 20px this is 29px against a 32.4px line,
     which reads as just under one blank line: visible separation without the
     text breaking into slabs. A rem-based gap measured 23.2px against the
     same line, which is 0.76 of it and too tight to work as a paragraph
     break. */
  margin-block-end: 1.45em !important;
  /* A serif at 20px hyphenates badly in a 339px column if left to chance and
     looks ragged if not allowed to break at all. Break long words only. */
  overflow-wrap: break-word;
}

body.single-post .entry-content > p:first-child {
  margin-block-start: 0 !important;
}

/* The standfirst: the first paragraph of the article carries the reader in. */
body.single-post .entry-content > p:first-of-type {
  font-size: calc(var(--alt-read-body) + 2px) !important;
  line-height: 1.55 !important;
  color: var(--alt-read-ink-strong) !important;
}

/* ---------------------------------------------------------- 4. the headings
 * A scale that steps: 44 / 34 / 26 / 20 over a 20px body on desktop, which is
 * 2.20x, 1.70x, 1.30x and 1.00x. The site shipped 47px over a 16.8px body with
 * the next stop at 23.2px, so h1 was 2.8x the text and 2.0x the section
 * heading under it. That gap is most of why it did not read as long-form.
 *
 * PASS THREE RAISED THE MIDDLE OF THE SCALE, NOT THE TOP. 2.20.61 shipped
 * 40 / 30 / 24, and the owner's third note was that the page read as one flat
 * block. h2 at 30px over a 20px body is 1.50x - large enough to be a heading
 * and not large enough to be a SECTION - so the step from h2 down to the text
 * is what moved: 1.50x to 1.70x, while every neighbouring ratio stays inside
 * the 1.05-1.45 band the test holds. The h1 came up with it only enough to
 * keep leading the h2; it is still under 2.25x the body, which is the ceiling
 * that stops a headline detaching from the article under it.
 *
 * SIZE IS HALF OF IT AND SPACE IS THE OTHER HALF, and they are not
 * interchangeable. Size says "this is a heading". Space says "a section
 * begins here", and that is the one a reader gets from a scroll without
 * reading a word. The room above an h2 goes from 48px to 80px, expressed as
 * --alt-read-h2-air units so the phone can take four instead of five.
 */
body.single-post .wp-block-post-title,
body.single-post h1.wp-block-post-title {
  font-family: var(--alt-read-sans) !important;
  font-size: var(--alt-read-h1) !important;
  line-height: 1.16 !important;
  font-weight: 700 !important;
  letter-spacing: -0.021em !important;
  color: var(--alt-read-ink-strong) !important;
  margin-block-start: 0 !important;
  margin-block-end: calc(var(--alt-read-unit) * 1.4) !important;
  text-wrap: balance;
}

body.single-post .entry-content h2,
body.single-post .wp-block-post-content h2 {
  font-family: var(--alt-read-sans) !important;
  font-size: var(--alt-read-h2) !important;
  line-height: 1.24 !important;
  font-weight: 700 !important;
  letter-spacing: -0.018em !important;
  color: var(--alt-read-ink-strong) !important;
  margin-block-start: calc(var(--alt-read-unit) * var(--alt-read-h2-air)) !important;
  margin-block-end: calc(var(--alt-read-unit) * 0.9) !important;
  /* The site draws a 2px #eef3ee rule under every h2. At this scale the space
     above already says "new section" and the rule only adds noise. */
  border-bottom: 0 !important;
  padding-bottom: 0 !important;
  text-wrap: balance;
}

body.single-post .entry-content h3,
body.single-post .wp-block-post-content h3 {
  font-family: var(--alt-read-sans) !important;
  font-size: var(--alt-read-h3) !important;
  line-height: 1.3 !important;
  font-weight: 700 !important;
  letter-spacing: -0.014em !important;
  color: var(--alt-read-ink-strong) !important;
  margin-block-start: calc(var(--alt-read-unit) * 2.9) !important;
  margin-block-end: calc(var(--alt-read-unit) * 0.55) !important;
  text-wrap: balance;
}

body.single-post .entry-content h4,
body.single-post .wp-block-post-content h4 {
  font-family: var(--alt-read-sans) !important;
  font-size: var(--alt-read-h4) !important;
  line-height: 1.35 !important;
  font-weight: 700 !important;
  color: var(--alt-read-ink-strong) !important;
  margin-block-start: calc(var(--alt-read-unit) * 1.8) !important;
  margin-block-end: calc(var(--alt-read-unit) * 0.45) !important;
}

/* A heading immediately after another heading gets its space back. */
body.single-post .entry-content h2 + h3,
body.single-post .entry-content h3 + h4 {
  margin-block-start: calc(var(--alt-read-unit) * 1.1) !important;
}

/* ------------------------------------------------------------- 5. the rest
 * Lists, quotes, figures, rules and tables, so vertical rhythm holds across
 * everything an article actually contains rather than only paragraphs.
 */
body.single-post .entry-content ul,
body.single-post .entry-content ol {
  font-family: var(--alt-read-serif) !important;
  font-size: var(--alt-read-body) !important;
  line-height: var(--alt-read-lh) !important;
  color: var(--alt-read-ink) !important;
  margin-block-start: calc(var(--alt-read-unit) * 1.3) !important;
  margin-block-end: calc(var(--alt-read-unit) * 1.45) !important;
  padding-inline-start: 1.35em !important;
}

body.single-post .entry-content li {
  margin-block-end: 0.5em;
}

body.single-post .entry-content li > ul,
body.single-post .entry-content li > ol {
  margin-block-start: 0.5em !important;
  margin-block-end: 0.5em !important;
}

body.single-post .entry-content li::marker {
  color: var(--alt-read-ink-soft);
}

body.single-post .entry-content blockquote,
body.single-post .entry-content .wp-block-quote {
  font-family: var(--alt-read-serif) !important;
  font-size: calc(var(--alt-read-body) + 1px) !important;
  line-height: 1.5 !important;
  color: var(--alt-read-ink-strong) !important;
  margin-block-start: calc(var(--alt-read-unit) * 2.2) !important;
  margin-block-end: calc(var(--alt-read-unit) * 2.2) !important;
  border-inline-start: 3px solid var(--alt-read-rule) !important;
  border-left: 3px solid var(--alt-read-rule) !important;
  padding-inline-start: 1.1em !important;
  padding-left: 1.1em !important;
  font-style: normal !important;
  /* The WPCode snippet pins `blockquote { max-width:100% !important }` under
     782px, which beats WP core's un-important 645px. Left alone, a pull quote
     is the ONE block on a tablet that spans the full container while every
     paragraph around it is 645px. */
  max-width: var(--wp--style--global--content-size, 645px) !important;
}

body.single-post .entry-content blockquote p {
  font-size: inherit !important;
  color: inherit !important;
}

body.single-post .entry-content blockquote > :last-child {
  margin-block-end: 0 !important;
}

body.single-post .entry-content figure,
body.single-post .entry-content .wp-block-image,
body.single-post .entry-content .wp-block-embed,
body.single-post .entry-content .wp-block-table {
  margin-block-start: calc(var(--alt-read-unit) * 2.4) !important;
  margin-block-end: calc(var(--alt-read-unit) * 2.4) !important;
}

body.single-post .entry-content img {
  max-width: 100%;
  height: auto;
  border-radius: 6px;
}

body.single-post .entry-content hr,
body.single-post .entry-content .wp-block-separator {
  margin-block-start: calc(var(--alt-read-unit) * 3) !important;
  margin-block-end: calc(var(--alt-read-unit) * 3) !important;
  border-top: 1px solid var(--alt-read-rule) !important;
  border-bottom: 0 !important;
}

/* A table wider than the measure scrolls INSIDE its own box. The document
   never scrolls sideways (CLAUDE.md hard bar). */
body.single-post .entry-content .wp-block-table,
body.single-post .entry-content figure.wp-block-table {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

body.single-post .entry-content table {
  font-family: var(--alt-read-sans) !important;
  font-size: calc(var(--alt-read-body) - 3px) !important;
  line-height: 1.45 !important;
  border-collapse: collapse;
}

body.single-post .entry-content th,
body.single-post .entry-content td {
  padding: 0.55em 0.7em;
  border-bottom: 1px solid var(--alt-read-rule);
  text-align: start;
}

body.single-post .entry-content code,
body.single-post .entry-content kbd,
body.single-post .entry-content pre {
  font-family: var(--alt-read-mono) !important;
  font-size: 0.88em !important;
}

body.single-post .entry-content pre {
  overflow-x: auto;
  padding: 1em 1.1em;
  border-radius: 6px;
  background: #f6f7f8;
  margin-block-start: calc(var(--alt-read-unit) * 1.8) !important;
  margin-block-end: calc(var(--alt-read-unit) * 1.8) !important;
}

/* ------------------------------------------------------------- 6. the links
 * Colour is deliberately inherited. The site sets
 * `a:where(:not(.wp-element-button)) { color: currentColor }` and the rendered
 * contrast audit is a live gate in this repo; introducing a new link colour
 * here would be a contrast claim this change has not measured. What changes is
 * the underline, which is what makes a link legible in a serif at 20px.
 */
body.single-post .entry-content a:where(:not(.wp-element-button)) {
  text-decoration: underline;
  text-decoration-thickness: 0.06em;
  text-underline-offset: 0.16em;
  text-decoration-color: rgba(35, 39, 43, 0.42);
  transition: text-decoration-color 0.15s ease;
}

body.single-post .entry-content a:where(:not(.wp-element-button)):hover,
body.single-post .entry-content a:where(:not(.wp-element-button)):focus-visible {
  text-decoration-color: currentColor;
}

/* An anchor inside a sentence cannot be 44px tall without opening a 44px hole
 * in the line around it, and WCAG 2.5.5/2.5.8 both carry the "in a sentence"
 * exception for exactly that. Inline vertical padding hit-tests without
 * entering the line box, so the tap area grows and the paragraph does not
 * move. Same rule the tracker's own tap-target work landed on. */
@media (max-width: 781px) {
  body.single-post .entry-content p a,
  body.single-post .entry-content li a {
    padding-block: 7px;
  }
}

/* -------------------------------------------- 7. the table of contents
 * easy-table-of-contents ships its own screen.min.css. This does not fight it,
 * it dresses it.
 *
 * PASS THREE: IT READ AS A PLUGIN WIDGET, AND TWO OF THE THREE REASONS WERE
 * SHIPPED BY 2.20.61. The owner's second note. What made it a widget was not
 * the rule above it, it was the TYPE and the COLUMNS: 16px grey sans under a
 * 21px serif is four sizes down from the prose it indexes, and setting it in
 * two columns is a layout no article uses and every sidebar module does.
 *
 * Both are reversed here, and the two-column decision is reversed at its
 * cause rather than overruled. Two columns existed to halve a 525px slab, and
 * the slab existed because every link carried a 44px tap floor at every width.
 * A desktop pointer does not need a 44px row. The floor is now scoped to
 * 781px and below, where it is a real requirement, and above it the links set
 * on their own leading - one column, about 380px for eleven entries, which is
 * shorter than the two-column block it replaces was tall plus the gap it left.
 *
 * The links are set in the ARTICLE'S OWN SERIF at three pixels under the body
 * size. That is the whole "article-shaped" instruction taken literally: these
 * strings are the article's headings quoted back to the reader, so they are
 * set in the face the article is set in. The label above them stays sans,
 * because it is the one string on the block that is not the article's words.
 *
 * IT IS NO LONGER A BOX, AND THAT IS THE POINT. It shipped with a 1px border
 * on all four sides, and on a 2000px screen those two vertical edges are
 * literally two of the lines that made the page read as a ribbon - one of the
 * few elements on the page tall enough to draw a long one. It stays INLINE and
 * it stays on the reading column, because a phone reader needs it in the flow
 * and a floated aside would be a second column this layout has no room for.
 * What changes is what it is made of: horizontal rules top and bottom, which
 * cut across the column instead of running down it, no fill, and no side
 * padding, so its links sit on the article's own left edge instead of 17.6px
 * inside it.
 */
body.single-post .entry-content div[class*="ez-toc-v"],
body.single-post .entry-content #ez-toc-container {
  font-family: var(--alt-read-sans) !important;
  max-width: var(--wp--style--global--content-size, 645px) !important;
  margin-left: auto !important;
  margin-right: auto !important;
  margin-block-start: calc(var(--alt-read-unit) * 2.4) !important;
  margin-block-end: calc(var(--alt-read-unit) * 2.8) !important;
  background: transparent !important;
  border: 0 !important;
  border-top: 1px solid var(--alt-read-rule) !important;
  border-bottom: 1px solid var(--alt-read-rule) !important;
  border-radius: 0 !important;
  padding-block: 1rem !important;
  padding-inline: 0 !important;
  box-shadow: none !important;
  /* easy-table-of-contents renders its container as a shrink-to-fit box, so
     `width: auto` alone leaves it narrower than the article and centred inside
     it: measured live at 1280 as 508px sitting at x=386 while the text ran
     317-962. A box that neither matches the column nor clearly sits outside it
     reads as a mistake. display:block makes it take the measure. */
  display: block !important;
  width: auto !important;
}

body.single-post .entry-content .ez-toc-title-container,
body.single-post .entry-content .ez-toc-title {
  font-family: var(--alt-read-sans) !important;
  /* 15px, sentence case, no tracking. 13px uppercase letterspaced grey is the
     house style of every plugin module ever shipped, and it was announcing
     this block as one. */
  font-size: 15px !important;
  font-weight: 700 !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
  color: var(--alt-read-ink-strong) !important;
}

body.single-post .entry-content .ez-toc-list,
body.single-post .entry-content nav.ez-toc-nav ul,
body.single-post .entry-content #ez-toc-container ul {
  font-family: var(--alt-read-serif) !important;
  font-size: calc(var(--alt-read-body) - 3px) !important;
  line-height: 1.5 !important;
  margin-block-start: 0.55rem !important;
  margin-block-end: 0 !important;
  padding-inline-start: 0 !important;
  /* No markers. Two reasons, and the second is a bug rather than a taste: a
     bulleted list of the article's own headings is the widget look this pass
     was asked to remove, AND with padding-inline-start at 0 the markers
     render OUTSIDE the reading column - measured 20px left of the link text,
     which is 20px left of every paragraph on the page. */
  list-style: none !important;
}

body.single-post .entry-content #ez-toc-container li,
body.single-post .entry-content .ez-toc-list li {
  margin-block-end: 0 !important;
}

body.single-post .entry-content #ez-toc-container a,
body.single-post .entry-content .ez-toc-list a {
  font-family: var(--alt-read-serif) !important;
  font-size: calc(var(--alt-read-body) - 3px) !important;
  line-height: 1.5 !important;
  color: var(--alt-read-ink) !important;
  text-decoration: none !important;
  display: block;
  padding-block: 0.2em;
}

/* A nav list is not a sentence, so the 44px floor applies to it in full - on
 * a touch screen. It is scoped here rather than applied at every width
 * because that floor, applied to eleven links on a desktop, is what made the
 * block 525px tall and sent 2.20.61 looking for a second column to hide the
 * height in. Fix the cause, delete the workaround. */
@media (max-width: 781px) {
  body.single-post .entry-content #ez-toc-container a,
  body.single-post .entry-content .ez-toc-list a {
    min-height: 44px;
    display: flex;
    align-items: center;
  }
}

body.single-post .entry-content #ez-toc-container a:hover,
body.single-post .entry-content .ez-toc-list a:hover {
  text-decoration: underline !important;
}

/* ONE COLUMN AT EVERY WIDTH. 2.20.61 set this in two above 1100px to halve a
   525px slab; the slab is gone now that the 44px floor is scoped to touch, and
   what two columns bought instead was the single most module-looking thing on
   the page. The declaration is written out rather than deleted because the
   easy-table-of-contents stylesheet is not ours and a future version of it
   could arrive with columns of its own. */
body.single-post .entry-content #ez-toc-container > nav > ul,
body.single-post .entry-content nav.ez-toc-nav > ul.ez-toc-list,
body.single-post .entry-content #ez-toc-container ul.ez-toc-list {
  columns: auto !important;
  column-count: auto !important;
}

/* --------------------------------------------- 8. the inline email capture
 * .atr-capture is the site's own signup block, styled by a 531-byte inline
 * <style> held in the database. It is not this plugin's markup and this does
 * not change it; it makes it match the new scale and gives its two controls
 * the 44px floor a phone needs. 16px on the input is not a type choice: any
 * smaller and iOS Safari zooms the page on focus.
 */
body.single-post .entry-content .atr-capture {
  font-family: var(--alt-read-sans) !important;
  max-width: var(--wp--style--global--content-size, 645px) !important;
  margin-left: auto !important;
  margin-right: auto !important;
  margin-block-start: calc(var(--alt-read-unit) * 2.2) !important;
  margin-block-end: calc(var(--alt-read-unit) * 2.2) !important;
  border: 1px solid var(--alt-read-rule) !important;
  border-radius: 8px !important;
  background: #fafbfb !important;
  padding: 1.1rem 1.15rem !important;
}

body.single-post .entry-content .atr-capture-title {
  font-family: var(--alt-read-sans) !important;
  font-size: 17px !important;
  line-height: 1.35 !important;
  font-weight: 700 !important;
  color: var(--alt-read-ink-strong) !important;
  margin-block-end: 0.6rem !important;
}

body.single-post .entry-content .atr-capture-input {
  font-family: var(--alt-read-sans) !important;
  font-size: 16px !important;
  min-height: 44px !important;
  padding: 10px 12px !important;
  border-radius: 6px !important;
}

body.single-post .entry-content .atr-capture-btn {
  font-family: var(--alt-read-sans) !important;
  font-size: 16px !important;
  min-height: 44px !important;
  padding: 10px 20px !important;
  border-radius: 6px !important;
}

body.single-post .entry-content .atr-capture-msg {
  font-family: var(--alt-read-sans) !important;
  font-size: 13px !important;
  color: var(--alt-read-ink-soft) !important;
}

@media (max-width: 560px) {
  /* At 339px the field plus an 8px gap plus the button does not fit on one
     line, and a wrapped row half off the box is worse than two full-width
     rows. Same lesson as the digest signup's 2.20.53 landing defect. */
  body.single-post .entry-content .atr-capture-input,
  body.single-post .entry-content .atr-capture-btn {
    flex: 1 1 100% !important;
    width: 100% !important;
  }
}

/* ------------------------------------------ 8b. OUR OWN email signup
 * .alt-digest is this plugin's markup, from includes/subscribe.php, appended
 * to the end of an article by includes/subscribe-placements.php. It arrives
 * carrying its own <style>, which is what lets it render on the talent
 * tracker and anywhere else without this stylesheet. What it cannot carry is
 * THIS page's type: the article is a 20px serif on a 645px measure, and a
 * component sized for the tracker's 14px sans reads as something pasted in.
 * These rules put it on the article's own scale and nothing else. Every one
 * is `body.single-post .entry-content .alt-digest ...` (0,3,x), which beats
 * both the component's own (0,1,x) rules and the database stylesheet's
 * `.entry-content h2` (0,1,1) !important without needing to guess at load
 * order.
 *
 * IT IS THE SECOND SIGNUP ON THE PAGE, FOR NOW. div.atr-capture sits higher up
 * the article until the owner deletes the WPCode snippet that injects it.
 * Nothing here reads, styles, hides or waits for that box: the two are sized
 * from the same tokens and the same 44px floor, so they read as two blocks of
 * one page rather than as a fight, and deleting one changes nothing about the
 * other. Section 8 above styles .atr-capture and is older than this; it is
 * left exactly as it was.
 */
body.single-post .entry-content .alt-digest {
  font-family: var(--alt-read-sans) !important;
  max-width: var(--wp--style--global--content-size, 645px) !important;
  margin-left: auto !important;
  margin-right: auto !important;
  margin-block-start: calc(var(--alt-read-unit) * 2.6) !important;
  margin-block-end: calc(var(--alt-read-unit) * 1.2) !important;
  border-color: var(--alt-read-rule) !important;
  border-radius: 8px !important;
  padding: 1.1rem 1.15rem !important;
  color: var(--alt-read-ink) !important;
}

/* The site's own `.entry-content h2` rule draws a 2px underline and reserves
 * 2.2rem above it. Inside a bordered box that underline reads as a second
 * edge, and the margin pushes the heading off its own padding. */
body.single-post .entry-content .alt-digest h2 {
  font-family: var(--alt-read-sans) !important;
  font-size: var(--alt-read-h4) !important;
  line-height: 1.3 !important;
  font-weight: 700 !important;
  color: var(--alt-read-ink-strong) !important;
  text-align: start !important;
  margin-block-start: 0 !important;
  margin-block-end: 0.5rem !important;
  padding-bottom: 0 !important;
  border-bottom: 0 !important;
}

body.single-post .entry-content .alt-digest p,
body.single-post .entry-content .alt-digest label,
body.single-post .entry-content .alt-digest legend,
body.single-post .entry-content .alt-digest summary {
  font-family: var(--alt-read-sans) !important;
  color: var(--alt-read-ink) !important;
}

/* The article's serif body rule is `.entry-content p` !important at 20px on a
 * 1.62 line. Nine lines of it is the whole phone screen, which is the defect
 * the component's own intro rule was written for on the tracker, one scale
 * up. Same fix, this page's numbers. */
body.single-post .entry-content .alt-digest p.alt-digest-intro {
  font-size: 15px !important;
  line-height: 1.5 !important;
  margin-block-start: 0 !important;
  margin-block-end: 0.75rem !important;
}

body.single-post .entry-content .alt-digest-lists label,
body.single-post .entry-content .alt-digest-freq label {
  font-size: 15px !important;
  line-height: 1.45 !important;
}

body.single-post .entry-content .alt-digest-privacy p {
  font-size: 14px !important;
  line-height: 1.5 !important;
  margin-block-start: 0.5rem !important;
  margin-block-end: 0.5rem !important;
  color: var(--alt-read-ink-soft) !important;
}

/* --------------------------------------------------------- 9. print + motion */
@media (prefers-reduced-motion: reduce) {
  body.single-post .entry-content a { transition: none; }
}
