/* ==========================================================================
   TEMPLATE: article  —  the blog post page  (rebuild/templates/article.html)
   --------------------------------------------------------------------------
   Depends on tokens.css + base.css. The page also loads sections/nav.css and
   sections/footer.css, which this file never touches.

   This is a READING template. 39 posts, ~8,400 characters of body copy each,
   h2/h3/h4 and lists throughout. Everything below is in service of one thing:
   a column a person can read for six minutes without getting tired.

   --------------------------------------------------------------------------
   WHAT THE BODY CAN CONTAIN — counted across all 39 posts, not guessed:

       p 633   li 392   strong 361   h2 156   a 151   h3 104   ul 83
       figure 56   img 56   h4 16   em 13   ol 11   div 10   blockquote 6
       figcaption 4   details 2   summary 2   span 1

   Every one of those is styled here. The three that a prose stylesheet
   usually forgets, and that would each have shipped visibly broken:

     · h4 — 16 of them, eleven in one post (do-i-really-need-cyber-insurance),
       so the column genuinely needs four heading steps — h1 h2 h3 h4 — where
       the home page has never needed more than two.
     · ol — 11 lists, some running to seven long items.
     · div — five posts carry a bare <div><div><a>…</a></div></div> call to
       action through from the old site. Unstyled it is a naked blue-ish link
       sitting alone in a paragraph gap. See §8.
     · span — the count is 1, and the census READING IT AS ONE NUMBER is how
       it shipped broken. That single span is not an inline flourish: it is
       <p><a href="/get-started/"><span>Get Started</span></a></p>, a SIXTH
       call to action in a second shape, on cargo-insurance-vs-stock-throughput-
       policy. Enumerating shapes is what let it through, so §8 no longer
       enumerates them — the generator marks both and the CSS keys on the mark.

   A census counts elements. It does not tell you what one of them MEANS, and
   a lone element in a corpus this size is more likely to be an outlier that
   matters than a rounding error.

   --------------------------------------------------------------------------
   SHARED WITH THE HOME PAGE — taken, never restated:

     the container and its gutter ladder     .br-container
     the wider of the two measures (840px)   .br-measure-wide, on the hero
     section rhythm                          --br-section-y (2u)
     the whole flow scale                    --br-flow-*  (u/3 … 16u/3)
     every colour                            --br-text / --br-text-heading /
                                             --br-text-strong / --br-text-muted /
                                             --br-bg-wash / --br-border / --br-gold
     the button                              .br-btn + .br-btn--primary / --ghost
                                             — the foot's two pills AND, since
                                             the generator marks them, the
                                             carried-over CTAs inside the copy.
                                             This file no longer draws a pill.
     the hairline                            --br-border
     the focus ring                          base.css :focus-visible
     the chevron                             the exact data URI base.css draws
                                             on .br-select — see §8
     the inline-link idiom                   .br-link's ink + offset + hover
                                             thickness, applied to bare <a>
     the quotation-as-filled-card idiom      testimonials — see §6

   NEW, and why:

     --br-article-h2-size / -lh / -ls   one new token trio in tokens.css. The
       heading scale jumps 45.457 -> 24.629 at 1440 because the home page never
       stacks four heading levels in one column; a prose page does. Full
       argument is in the comment on the token itself.

     --br-tap-min                      44px, the WCAG minimum touch target.
       Already the number base.css argues from in prose on .br-btn; named in
       tokens.css because the article's back link is the second place it is
       load-bearing and two shared nav controls are still under it. See §10.

     A local reading measure (--_measure below). The system's two measured
     widths are 604 and 840; 604 is the reference's centred-intro width and is
     narrower than a comfortable reading column at 18px, 840 is far wider.
     Measured rather than picked — see §1.

     A local reading leading (1.55em floor on p and li, §4). The only value in
     this file that departs from its token on purpose. Argued at the rule.
   ========================================================================== */

.br-article {
  /* ----------------------------------------------------------------------
     §1. THE MEASURE.

     Set to land in the 65-75 character band, at the width the design is
     judged at. MEASURED, not estimated, and not eyeballed off a `ch` unit
     (`ch` is the width of a zero, which in Inter is wider than the average
     letter — it over-reports a column by about 12%).

     Method (`node scripts/_art-measure-chars.mjs <preview.html>`): render at
     1440, walk EVERY character of every body paragraph over 200 characters
     long with a Range, bucket by line-box top, and drop each paragraph's last
     line, which is ragged by definition. Run on two posts —
     do-i-really-need-cyber-insurance (29-33 full lines) and the longest in the
     set, automotive-business-insurance-guide-for-car-dealerships (46-59):

         column       mean         longest line
         604px    65.4 / 66.1        75 / 74     <- .br-measure. Bottom of the
                                                    band; it is the reference's
                                                    centred-intro width, not a
                                                    reading width.
         640px    70.0 / 70.3        77 / 76     <- SHIPPED. Dead centre of the
                                                    band on both posts.
         660px    72.4 / 71.7        81 / 79
         680px    75.2 / 74.6        82 / 81     <- mean at or past the ceiling
         720px    79.0 / 80.1        86 / 89     <- out
         840px    93.8 / 92.7       102 / 98     <- .br-measure-wide: right for
                                                    the hero, not for type

     40rem, not 640px, for the reason THE rem RULE at the top of tokens.css
     gives: a reader who raises their browser's default font size is asking
     for bigger type, and a px measure would hold the column still while the
     type grew inside it — 70 characters would become 35 and the rhythm this
     whole file is built on would be gone. In rem the measure grows with the
     type and the character count holds. Identical at root 16.
     ---------------------------------------------------------------------- */
  --_measure: 40rem;

  /* Rhythm inside the article. Named rather than repeated so the three big
     vertical intervals can be read (and moved) in one place. */
  --_head-gap: var(--br-flow-xs);        /* 18.769 @1440 — inside the header */
  --_block-gap: var(--br-flow-lg);       /* 56.308 @1440 — head -> hero -> body */
  --_prose-gap: var(--br-flow-sm);       /* 28.154 @1440 — paragraph to paragraph */

  padding-block: var(--br-section-y);
}

/* The shell holds the reading column. The three text blocks are held to the
   measure; the hero is not, and takes its width from .br-measure-wide in the
   markup — which is why the 840 is not restated here.

   Listed by name rather than as `.br-article__shell > *`. That shorthand was
   written first and is wrong: it and .br-measure-wide both weigh (0,1,0), and
   this file loads after base.css, so the shorthand would win on source order
   and quietly cap the hero at the reading measure. Naming the three that
   want the measure leaves the hero with no competing rule at all. */
.br-article__head,
.br-article__body,
.br-article__foot {
  max-width: var(--_measure);
  margin-inline: auto;
}

.br-article__hero { margin-inline: auto; }

/* ==========================================================================
   §2. THE HEADER — back link, title, standfirst, byline.
   ========================================================================== */

/* Back to /blog/. Sized and coloured like the fine print, not like a button:
   it is orientation, and it must not compete with the title two lines below
   it. The chevron and the label share one hover, so the whole thing reads as
   a single control.

   The 14px/21.7px pair is the system's --br-small scale, floored in em so it
   survives a user font-size change — see the note on .br-article__byline,
   which explains the max() idiom and why it is byte-identical at root 16. */
.br-article__back {
  display: inline-flex;
  align-items: center;
  gap: var(--br-space-1);
  margin-bottom: var(--_head-gap);
  font-size: max(var(--br-small-size), 0.875rem);
  line-height: max(var(--br-small-lh), 1.55em);
  letter-spacing: var(--br-small-ls);
  font-weight: var(--br-weight-semibold);
  color: var(--br-text-muted);
  text-decoration: none;
  border-radius: var(--br-radius-xs);
  transition: color var(--br-dur) var(--br-ease);
}

.br-article__back-icon {
  /* Nudged left of its own box so the chevron's ink, not its bounding box,
     lines up with the title's stem below. A 16px icon on a 24 grid carries
     ~2.6px of empty margin at the tip. */
  margin-left: -2px;
}

.br-article__back:hover,
.br-article__back:focus-visible { color: var(--br-text-strong); }
.br-article__back:active { color: var(--br-ink-active); }

/* The title. The SECTION scale (--br-h2-size, 45.457 @1440), not the hero
   scale h1 defaults to in base.css (--br-h2-lg-size, 60.857) and not the
   display faces: at 60px in a 680px column a five-word title is three lines
   before the reader has read anything, and the display faces are the home
   page's voice, not an article's.

   `balance` is inherited from base.css and is right here — an article title
   is short enough for the browser's balancer to help rather than fight.

   The leading is floored the way --br-body-lh and --br-h4-lh already are in
   tokens.css. --br-h2-lh is one of the four that file flags as STILL FLAT:
   41px below 992, 57px above. At root 16 the ratio is 1.25 and the px term
   wins everywhere, so nothing moves at the default — but at 200% text the
   title would set 91px type in a 57px box and the lines would physically
   overlap. This is a reading page; that is not a defect it gets to inherit.
   1.15 is chosen to sit below the tightest default ratio in play (1.2537 at
   1440, 1.0789 at 991 — the 991 case is the binding one) so the px term
   always wins at root 16. Local, because tokens.css is explicit that the
   global fix needs measuring across all fifteen sections first. */
.br-article__title {
  font-size: var(--br-h2-size);
  line-height: max(var(--br-h2-lh), 1.15em);
  letter-spacing: var(--br-h2-ls);
  margin-bottom: var(--_head-gap);
}

/* The standfirst is the post's own meta description, so no copy is invented
   to fill it. Lead scale (20/28 @1440), body ink — the same treatment every
   section intro on the home page gets. */
.br-article__standfirst {
  font-size: var(--br-body-lg-size);
  line-height: var(--br-body-lg-lh);
  letter-spacing: var(--br-body-lg-ls);
  color: var(--br-text);
  margin-bottom: var(--_head-gap);
}

/* Empty is a real state — a post could arrive without a description — and an
   empty paragraph still occupies a full line box plus its margin. */
.br-article__standfirst:empty { display: none; }

/* Byline. One line, fine print, with the author in ink so the eye lands on
   the name and slides off the dates.

   THE max() IDIOM, used here and on the back link above: --br-small-size and
   --br-small-lh are FLAT px, which tokens.css names as a known defect — that
   type does not respond to the browser's default font size at all. 0.875rem
   is 14px and 1.55em of 14px is 21.7px, so at root 16 both max() calls return
   the token to the last decimal and the default render is untouched. Above
   root 16 the rem/em term takes over and the byline scales with the article
   instead of shrinking away from it. */
.br-article__byline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--br-space-2);
  font-size: max(var(--br-small-size), 0.875rem);
  line-height: max(var(--br-small-lh), 1.55em);
  letter-spacing: var(--br-small-ls);
  color: var(--br-text-muted);
  padding-top: var(--br-flow-xs);
  border-top: 1px solid var(--br-border);
}

.br-article__author {
  font-weight: var(--br-weight-semibold);
  color: var(--br-text-strong);
}

.br-article__byline-dot { color: var(--br-neutral-500); }

/* "Updated <date>" is dropped whole when the generator passes an empty
   {{modified_display}}. :has() rather than :empty because the span always
   holds the literal word "Updated" plus a <time> — it is the TIME that goes
   empty, and an "Updated" with nothing after it is worse than no line at all.
   Browsers without :has() show "Updated" alone in that one case, which is the
   pre-existing behaviour and not a new fault. */
.br-article__updated:has(time:empty) { display: none; }

/* ==========================================================================
   §3. THE HERO

   Steps out to 840px (.br-measure-wide, in the markup) so the picture opens
   the article instead of reading as one more block in the column.

   A fixed 16:9 box with object-fit: cover, for two reasons. It reserves the
   space before the image arrives, so the title does not jump when a 2000px
   photograph lands — and the 39 heroes are all different shapes, so without
   it the page's opening rhythm changes post to post for no editorial reason.
   16:9 is the home page banner's own ratio (1920x1080).
   ========================================================================== */

/* The gap BELOW the hero lives on the body, not on the hero. Caught on
   market-misconceptions, which is one of the five posts whose hero is
   suppressed: a display: none element has no margins, so with the 2u sitting
   on the hero's own margin-bottom the byline and the body's first figure came
   out 8px apart — the header and the article ran together. Splitting it this
   way means the interval survives the hero being absent, and is identical
   when it is present (0 + 2u collapse to 2u). The other half of this pair is
   the margin-top on .br-article__body in §4. */
.br-article__hero {
  margin-top: var(--_block-gap);
  margin-bottom: 0;
}

.br-article__hero-img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--br-radius-lg);
  background: var(--br-bg-band);   /* the plate under a photo that has not
                                      arrived — the same neutral band the
                                      page already uses, so a slow image
                                      degrades to a quiet block, not a hole */
}

/* Empty {{hero}} suppresses the whole figure. An empty src is not fetched
   (HTML spec: the image data update aborts on an empty src), so this costs
   nothing on the five posts whose body already opens on the same picture. The
   full count, and the rule for deciding, are in the placeholder notes at the
   top of article.html. */
.br-article__hero:has(> img[src=""]) { display: none; }

/* ==========================================================================
   §4. THE BODY — shared prose rhythm

   The whole of §4-§8 is written against BARE ELEMENTS, scoped to
   .br-article__body. That is deliberate: {{body}} is Warren's markup injected
   verbatim, so there are no classes to hook and adding any would mean the
   generator rewriting his HTML.

   Margin collapsing does the rhythm arithmetic. .br-article__body is a plain
   block box, so a paragraph's 1u bottom margin and a following h2's 2u top
   margin collapse to 2u rather than stacking to 3u — which is exactly the
   rule an editorial page wants: the space before a heading is set by the
   heading, not by whatever happened to precede it.
   ========================================================================== */

.br-article__body {
  /* The interval above the copy — see the note on .br-article__hero in §3.
     It is carried here so it survives the hero being suppressed. */
  margin-top: var(--_block-gap);
  color: var(--br-text);
}

/* First and last child margins would otherwise collapse THROUGH the wrapper
   and move the whole block. Six of the 39 posts open on a <figure>, whose top
   margin is 2u — without this the gap above the copy would be 4u on those and
   2u on the rest. */
.br-article__body > :first-child { margin-top: 0; }
.br-article__body > :last-child { margin-bottom: 0; }

/* THE READING LEADING — the one number in this file that is NOT the token.
   --br-body-lh is max(26px, 1.35em): 26px on 18px copy, a ratio of 1.444. That
   is right for the home page, where the longest paragraph is four lines in a
   604px intro. It is at the tight end for a 40rem column running 70 characters
   a line for 8,400 characters — long-form practice sits at 1.5-1.6 (Medium
   1.58, NYT ~1.5), and the whole argument of §1 is that this column is judged
   as something a person reads for six minutes, not glances at.

   1.55em, floored with max() the same way .br-article__title, h3 and the
   byline already floor theirs. Two things it buys:

     · 27.9px against the token's 26px at root 16 — +1.9px a line, ~1.9px x 46
       lines on the longest post, deliberate and local to the reading template.
       The home page's --br-body-lh is untouched.
     · The em term now BINDS. As shipped, max(26px, 1.35em) crosses over at
       root 19.3 and above that the leading gets proportionally TIGHTER (1.35)
       exactly where a reader who has enlarged their type needs it looser. At
       1.55em the em term wins from root 16 up, so the ratio holds at every
       text size instead of degrading at the one that asked for help.

   On li as well as p: 392 list items is the second largest body of running
   copy in the corpus and it would otherwise sit on a different leading from
   the paragraphs either side of it. The blockquote keeps --br-body-lg-lh —
   .br-article__body blockquote p is (0,1,2) and outranks this. */
.br-article__body p,
.br-article__body li {
  line-height: max(var(--br-body-lh), 1.55em);
}

.br-article__body p {
  margin-block: 0 var(--_prose-gap);
}

/* <em> gets no rule beyond the browser's italic — Inter's true italic is
   loaded and the emphasis is doing its own job. Declared rather than left
   implicit so nobody adds a colour to it later thinking it was an oversight. */
.br-article__body em { font-style: italic; }

/* ==========================================================================
   §5. HEADINGS

   The ladder, @1440:

       h1  45.457   the page title  (--br-h2-size, the section scale)
       h2  32.000   --br-article-h2-size, the one new token   <- see tokens.css
       h3  24.629   --br-h3-size, the card-title scale
       h4  20.000   --br-h4-size, the lead / h4 scale
       p   18.000   --br-body-size

   Ratios 1.42 / 1.30 / 1.23 / 1.11. Tightening as it descends is what makes
   four levels legible in one column: the top of the ladder needs the gap to
   separate from the title, the bottom needs only to separate from the copy.

   Space above each heading is roughly three times the space below it, so a
   heading always belongs to what follows rather than floating between two
   blocks. Measured in flow units, never in px:

       h2   2u   above   (56.308)   2u/3  below  (18.769)
       h3   4u/3 above   (37.538)   u/2   below  (14.077)
       h4   1u   above   (28.154)   u/3   below  ( 9.385)
   ========================================================================== */

.br-article__body h2 {
  font-size: var(--br-article-h2-size);
  line-height: var(--br-article-h2-lh);
  letter-spacing: var(--br-article-h2-ls);
  margin-block: var(--br-flow-lg) var(--br-flow-xs);
}

/* --br-h3-lh and --br-h3-ls are flat px/em in tokens.css and that file names
   the leading as one of the four it has not yet floored. Same treatment, same
   reasoning, same local scope as .br-article__title above: 1.2 sits below the
   tightest default ratio this token produces (1.28 at 992, 1.30 at 1440), so
   the px term wins at root 16 and nothing moves at the default. */
.br-article__body h3 {
  line-height: max(var(--br-h3-lh), 1.2em);
  margin-block: var(--br-flow-md) var(--br-flow-2xs);
}

.br-article__body h4 {
  margin-block: var(--br-flow-sm) var(--br-flow-3xs);
}

/* A heading immediately under a heading is a pair, not two blocks. Six such
   pairs across five posts — stock-throughput-insurance has two of them — and
   left alone the second heading takes its own full top margin, so the two
   drift apart into unrelated things. */
.br-article__body h2 + h3,
.br-article__body h2 + h4,
.br-article__body h3 + h4 {
  margin-top: var(--br-flow-xs);
}

/* Section headings sit on Inter 600 through the whole system — they must not
   inherit the semibold-plus-underline an inline link gets in §6. */
.br-article__body :is(h2, h3, h4) a {
  font-weight: inherit;
  color: inherit;
}

/* ==========================================================================
   §6. INLINE ELEMENTS — links, quotations
   ========================================================================== */

/* Bare <a> in the copy takes .br-link's treatment: ink rather than a
   competing colour, semibold so it reads at a glance, and the underline the
   system already uses at the offset it already uses. The declarations are
   written out rather than reached for via the class because there is no class
   on the markup to reach for — keep them in step with .br-link in base.css.

   Gold is deliberately NOT used here. 151 links across the 39 posts; gold on
   every one of them would turn the accent into decoration, which is the one
   thing this palette is disciplined about.

   :where(:not(.br-btn)) on all four states. The generator now marks the
   carried-over calls to action as real buttons (see §8), and a bare
   `.br-article__body a` is (0,1,1) against .br-btn's (0,1,0) in base.css —
   with this file loading second it would repaint every pill in body ink and
   put a 1px underline across it. The exclusion is what lets §8 be four lines
   of layout instead of a hand-copied button.

   :where(), not a bare :not(), because :not() would carry its argument's
   weight: `a:not(.br-btn)` is (0,2,1), which would outrank both
   `.br-article__body :is(h2,h3,h4) a` and `.br-article__body figure a` — each
   (0,1,2) — and hand the underline back to every heading link and every linked
   photograph. :where() contributes nothing, so this stays byte-for-byte the
   (0,1,1) it has always been and only the matching changes. :where() has been
   supported longer than the :has() this file already depends on, so there is
   no browser that drops this rule and keeps the rest of the sheet. */
.br-article__body a:where(:not(.br-btn)) {
  color: var(--br-text-strong);
  font-weight: var(--br-weight-semibold);
  text-decoration: underline;
  text-underline-offset: 0.3em;
  text-decoration-thickness: 1px;
  border-radius: var(--br-radius-xs);
  transition: color var(--br-dur) var(--br-ease),
              text-decoration-thickness var(--br-dur) var(--br-ease);
}

.br-article__body a:where(:not(.br-btn)):hover {
  color: var(--br-ink-hover);
  text-decoration-thickness: 2px;
}

.br-article__body a:where(:not(.br-btn)):focus-visible { color: var(--br-ink-hover); }
.br-article__body a:where(:not(.br-btn)):active { color: var(--br-ink-active); }

/* QUOTATION.

   The system already has an answer for "a quote", and it is not a hairline in
   the left margin: the home page sets its four testimonials as filled cards,
   gold or ink, with the quote at lead size and semibold. This is that idiom
   at the quietest setting the palette offers — the cream wash the difference
   cards put behind their illustrations — because a blockquote inside running
   copy is a change of voice, not a change of section, and a full gold panel
   four paragraphs into an 8,000-character article would stop the reader dead.

   Six blockquotes exist across the 39 posts, so this is a rare element and it
   is worth it being unmistakable when it appears.

   Ink is --br-ink-on-gold (#3a341c), the warm ink the system uses on its warm
   surfaces, rather than the cooler body grey. Measured 11.62:1 on the wash
   (node scripts/_art-audit.mjs <preview.html>), against 9.37:1 for the body
   copy it interrupts — the quote is the darker of the two, which is the right
   direction for a passage the design is deliberately raising its voice on. */
.br-article__body blockquote {
  margin-block: var(--br-flow-lg);
  padding: var(--br-card-py) var(--br-card-px);
  background: var(--br-bg-wash);
  border-radius: var(--br-radius-lg);
  color: var(--br-ink-on-gold);
}

.br-article__body blockquote p {
  font-size: var(--br-body-lg-size);
  line-height: var(--br-body-lg-lh);
  letter-spacing: var(--br-body-lg-ls);
  font-weight: var(--br-weight-semibold);
  margin-bottom: var(--_prose-gap);
}

.br-article__body blockquote > :last-child { margin-bottom: 0; }

/* strong inside a quotation would take --br-text-strong (#1f1902) and read
   colder than the quote around it. */
.br-article__body blockquote strong { color: inherit; }

/* ==========================================================================
   §7. LISTS

   392 list items — the second most common element in the whole corpus after
   the paragraph, and the one a prose stylesheet that only styles paragraphs
   gets visibly wrong. Most items open with a bolded label ("<strong>Phishing:
   </strong> Malicious emails …"), so the marker has to hold its own against
   a semibold run starting one space to its right.

   The marker takes --br-ink-olive (#4a3c0a) — the warm ink the difference
   cards already use for their filled proof-point badge. Not gold: 392 gold
   dots is decoration. Not body grey: the marker would disappear under the
   bold label beside it.
   ========================================================================== */

/* Two indents, not one. An outside marker is laid out to the left of the item,
   so the padding has to hold it: a bullet needs about a third of what a
   two-digit numeral does, and giving both the wider value pushes every bullet
   list a visible step further from the column edge than the paragraphs around
   it for no reason. 2u/3 and 1u. */
.br-article__body ul {
  margin-block: 0 var(--_prose-gap);
  padding-left: var(--br-flow-xs);
}

.br-article__body ol {
  margin-block: 0 var(--_prose-gap);
  padding-left: var(--br-flow-sm);
}

.br-article__body li {
  /* The gap between two items has to be smaller than the gap between two
     paragraphs, or the list stops reading as one thing: u/2 against 1u. */
  margin-bottom: var(--br-flow-2xs);
  /* And a third of a unit between the marker and the label it introduces —
     without it a bullet sits hard against a semibold run and the two read as
     one mark. */
  padding-left: var(--br-flow-3xs);
}

.br-article__body li:last-child { margin-bottom: 0; }

.br-article__body li::marker {
  color: var(--br-ink-olive);
}

.br-article__body ol > li::marker {
  font-weight: var(--br-weight-semibold);
}

/* Nested lists exist in none of the 39 posts today. Handled anyway: without
   this a nested list inherits the 1u bottom margin meant to separate a list
   from the next paragraph, and opens a hole inside its own parent item. */
.br-article__body li > :is(ul, ol) {
  margin-block: var(--br-flow-2xs) 0;
}

/* ==========================================================================
   §8. FIGURES, ACCORDIONS, AND THE CARRIED-OVER CTA
   ========================================================================== */

/* FIGURES. 56 of them, and their sources are not one size: 29 are 350px wide,
   22 are 740px, the rest in between. Stretching a 350px source across the
   640px column would put a visibly soft photograph in the middle of an otherwise
   crisp page, so images render at their intrinsic width, capped at the column
   and centred. A 350px photo centred with a caption under it reads as a
   deliberate inset; the same photo blown up reads as a mistake.

   text-align: center centres both the image (which base.css sets to
   display: block, so it needs margin-inline: auto too) and its caption. */
.br-article__body figure {
  margin-block: var(--br-flow-lg);
  text-align: center;
}

.br-article__body figure img {
  margin-inline: auto;
  border-radius: var(--br-radius-md);
}

/* Three of the 56 figures wrap their image in a link. It must not take the
   underline the copy links take, and it needs a visible affordance of its
   own — the system's inferred hover lift. */
.br-article__body figure a {
  display: inline-block;
  text-decoration: none;
  border-radius: var(--br-radius-md);
  transition: box-shadow var(--br-dur) var(--br-ease);
}

.br-article__body figure a:hover { box-shadow: var(--br-shadow-sm); }
/* base.css draws the global ring with a 2px radius, which would cut the
   corners off a 16px-rounded photograph. Restate the picture's own radius so
   the ring follows its edge. */
.br-article__body figure a:focus-visible { border-radius: var(--br-radius-md); }

.br-article__body figcaption {
  max-width: 46ch;
  margin: var(--br-flow-2xs) auto 0;
  font-size: var(--br-body-sm-size);
  line-height: var(--br-body-sm-lh);
  letter-spacing: var(--br-body-sm-ls);
  color: var(--br-text-muted);
}

/* ACCORDIONS. Native <details>, so they open, close and take keyboard focus
   with no script — the same decision the header's language menu and mobile
   sheet already made. Enter and Space both operate a <summary>; the focus
   ring comes from base.css, which already lists `summary` in its
   :focus-visible set.

   Consecutive <details> read as one bank: a rule above the first, a rule
   under each, no rule doubled between two. Only two exist today (both in
   stock-throughput-insurance) but the FAQ page type will bring many more, and
   a bank that only looks right at n=2 is not a bank. */
.br-article__body details {
  border-bottom: 1px solid var(--br-border);
}

.br-article__body details:not(details + details) {
  margin-top: var(--br-flow-lg);
  border-top: 1px solid var(--br-border);
}

/* A bank that opens directly under its own heading takes the heading's
   interval, not the block interval — otherwise "FAQs" sits 2u above the first
   question and reads as floating between two things rather than labelling the
   one below it. Same rule the h2 + h3 pair follows in §5.

   THE :not() HERE IS A WEIGHT, NOT A FILTER. It changes nothing about which
   elements match — a <details> that follows a heading cannot also follow a
   <details> — and it is here because without it this rule LOSES to the bank
   rule six lines above and the interval it is supposed to prevent is the one
   that shipped.

       .br-article__body :is(h2,h3,h4) + details          (0,1,2)
       .br-article__body details:not(details + details)   (0,1,3)   <- wins

   :not() takes the specificity of its most specific argument, and
   `details + details` is (0,0,2), so the bank rule outweighs this one by a
   whole element. Specificity beats source order, so "FAQs" sat 56.31px above
   its own bank at 1440 and 32.15px at 390 — measured — instead of 18.77 /
   16.06. Matching the guard puts this rule at (0,1,4) and it wins.

   No copy of this in §10. The bank rule has a narrow override at flow-md and
   this one now outweighs it at every width, and --br-flow-xs is fluid — it
   resolves to 18.77 at 1440 and 16.06 at 390 on its own, which are the two
   intended numbers. A second rule restating the same token would be noise. */
.br-article__body :is(h2, h3, h4) + details:not(details + details) {
  margin-top: var(--br-flow-xs);
}

.br-article__body details:not(:has(+ details)) {
  margin-bottom: var(--br-flow-lg);
}

.br-article__body summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--br-flow-xs);
  padding-block: var(--br-flow-xs);
  /* The whole row is the target — a 20px question is not something to make
     someone aim at. */
  cursor: pointer;
  font-size: var(--br-h4-size);
  line-height: var(--br-h4-lh);
  letter-spacing: var(--br-h4-ls);
  font-weight: var(--br-heading-weight);
  color: var(--br-text-heading);
  /* Firefox draws the disclosure triangle from list-style; Chrome and Safari
     from ::-webkit-details-marker. display: flex removes Chrome's, this
     removes Firefox's, and the rule below covers older WebKit. */
  list-style: none;
}

.br-article__body summary::-webkit-details-marker { display: none; }

/* Both of the summaries in the content wrap their text in <strong>, which
   would otherwise take the button ink (#1f1902) while the summary itself is
   on heading ink (#0e0f0c) — two near-blacks in the same row. */
.br-article__body summary strong {
  font-weight: inherit;
  color: inherit;
}

/* THE chevron — byte-for-byte the data URI base.css draws on .br-select, at
   the same 14px, so the accordion and the form control in the hero are
   demonstrably the same mark. If that path, weight or size is ever
   re-measured (see the long note above .br-select), change it here too. */
.br-article__body summary::after {
  content: "";
  flex: 0 0 auto;
  width: var(--br-icon);
  height: var(--br-h4-lh);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231f1902' stroke-width='1.85' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.62 8.82 12 14.67 17.38 8.82'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 14px 14px;
  transition: transform var(--br-dur) var(--br-ease);
}

.br-article__body details[open] > summary::after { transform: rotate(180deg); }

.br-article__body details > :not(summary) {
  margin-bottom: var(--br-flow-xs);
}

.br-article__body details > summary + * {
  margin-top: 0;
}

/* THE CARRIED-OVER CTA.

   SIX posts end a section with a button from the old site that survived
   extraction as bare markup, in TWO shapes — which is the whole story of this
   block:

     A  <div><div><a href="/contact">GET STARTED</a></div></div>       x5
     B  <p><a href="/get-started/"><span>Get Started</span></a></p>    x1
        (cargo-insurance-vs-stock-throughput-policy)

   This file used to draw the pill here by hand, matching shape A only. Shape B
   fell through to §6 and shipped as a 21px underlined text link alone in a
   paragraph, ~60px above the foot's real gold pill saying the same words — the
   exact failure this block exists to prevent, on the one post it did not
   enumerate. The element census at the top of this file counts `span 1` and
   never asked what it was.

   FIXED WHERE IT BELONGS: the generator marks both shapes
   class="br-btn br-btn--primary" before injection (markCtaButtons() in
   scripts/lib/normalise-body.mjs), so the pill is now THE button — base.css's,
   the same object the foot and the header use — rather than a copy of it. The
   copy had already drifted three declarations from its original
   (font-family, white-space, cursor), and a mirror that drifts is a second
   design system waiting to happen. Deleting it removes the drift and the
   shape-by-shape enumeration in one go: any new CTA shape the generator learns
   to recognise is styled the moment it is recognised.

   What is LEFT here is layout, and only layout — a CTA is a block-level pause
   in the column, centred, with the block interval around it. Keyed on the
   button class rather than on the wrapper's tag so both shapes and any third
   one take the same treatment; `> :has(...)` cannot reach past the two wrapper
   depths the content actually uses.

   Where :has() is unsupported the anchor is STILL a fully drawn .br-btn — it
   simply sits left in the column at the paragraph interval. That is a strictly
   better degradation than the old block's, which lost the button entirely. */
.br-article__body > :is(:has(> .br-btn), :has(> div > .br-btn)) {
  margin-block: var(--br-flow-lg);
  text-align: center;
}

/* THE ONE THING A BODY CTA NEEDS THAT A UI BUTTON DOES NOT: permission to
   wrap.

   .br-btn is `white-space: nowrap`, which is right for the labels the system
   writes — "Get a quote", "All insights", two or three words chosen to fit.
   Warren's carried-over CTAs are not labels, they are sentences: "Contact Us
   to Discuss Your Insurance Needs" (42 characters) and "Call Us Today 0422 131
   401". Held to nowrap, the first sets a 371px pill in a 358px column at 390
   and pushes the whole document 13px sideways — measured on
   knockout-savings-how-we-secured-… and what-is-weather-parametric-insurance,
   and it is the reason the hand-drawn mirror this replaced quietly omitted
   `white-space` in the first place. That omission read as drift; it was load-
   bearing, and only for labels nobody had counted.

   `white-space: normal` rather than the system's .br-btn--wrap, which also
   sets text-align: left and justify-content: flex-start. Those are right for
   a wrapped button in a card or a nav row; a CTA alone in a reading column
   wants to stay centred on both axes. max-width is the belt to that brace —
   min-width: 200px from --primary must never win against a narrower column. */
.br-article__body .br-btn {
  white-space: normal;
  max-width: 100%;
}

/* Shape B wraps its label in a <span> — the `span 1` in the census at the top
   of this file. It needs NO rule, and that is worth writing down rather than
   leaving as an absence: .br-btn is inline-flex, so the span becomes its only
   flex item and is centred by the button's own justify-content; `gap` is inert
   with one item; the span inherits every type property from the button. A
   `display: contents` here would be the obvious-looking addition and it would
   only put the label at risk in the accessibility tree for no layout gain. */

/* ==========================================================================
   §9. THE FOOT

   Two things a reader wants at the end of a post: the next post, and — on an
   insurance broker's site — the thing the post has spent 8,000 characters
   arguing for. Both labels are the site's own words: "Get a quote" is the
   header pill and the hero CTA, "Insights" is what the header calls /blog/.

   No second disclaimer here. The footer below already carries the regulated
   General Advice wording, and repeating regulated text in a paraphrase is
   exactly what nobody should do.
   ========================================================================== */

.br-article__foot {
  margin-top: var(--br-flow-xl);
  padding-top: var(--br-flow-lg);
  border-top: 1px solid var(--br-border);
}

/* A POST THAT ENDS ON AN ACCORDION ALREADY HAS THIS HAIRLINE.

   §8 closes a bank with a border-bottom on its last <details>. When that bank
   is the last thing in the body — stock-throughput-insurance today — the foot
   then drew a second identical 1px rule 84.5px below it with nothing in
   between, and two hairlines a bank-row apart read as one more empty FAQ row.
   Measured on that post: body lastElementChild = DETAILS, body -> foot gap
   84.5px, both borders 1px.

   The bank's own closing rule becomes the foot's rule. margin-top goes with
   the border for the same reason the border does: keep it and the buttons sit
   140.8px under a hairline that is already drawn, which is not "the foot has
   space above it", it is a hole. Dropped, the sequence is hairline ->
   padding-top -> buttons, which is exactly the interval every other post's
   foot shows. One hairline, one interval, no special case visible to a reader.

   .br-article__body and .br-article__foot are siblings inside
   .br-article__shell (article.html), so the adjacent-sibling combinator
   reaches. Today this is 1 post of 39; §8's own note says the FAQ page type
   will bring many more banks, and a bank at the end of a page is where they
   will land. */
.br-article__body:has(> details:last-child) + .br-article__foot {
  margin-top: 0;
  border-top: 0;
}

.br-article__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--br-flow-2xs) var(--br-flow-xs);
}

/* ==========================================================================
   §10. NARROW WIDTHS

   Nothing needs to move horizontally. The column is capped by whichever is
   smaller, the measure or the container: the measure binds down to a 704px
   viewport (640 + two 32px gutters) and the gutter binds below that, and both
   are already correct.

   What does need to move is the vertical rhythm. Every interval here is a
   multiple of --br-u, which is fluid and so already shrinks with the viewport
   — but not by enough on its own: 2u of air above an h2 is right beside a
   32px heading and half a phone screen beside a 24.5px one. The intervals
   step DOWN a rung (2u -> 4u/3, 4u/3 -> 1u, 1u -> 2u/3) rather than being
   given new numbers, so the ladder keeps its proportions.
   ========================================================================== */

@media (max-width: 767.98px) {
  .br-article {
    --_block-gap: var(--br-flow-md);
    padding-block: var(--br-flow-md) var(--br-flow-lg);
  }

  .br-article__body h2 { margin-block: var(--br-flow-md) var(--br-flow-2xs); }
  .br-article__body h3 { margin-block: var(--br-flow-sm) var(--br-flow-3xs); }
  .br-article__body h4 { margin-block: var(--br-flow-xs) var(--br-flow-3xs); }

  .br-article__body :is(figure, blockquote) { margin-block: var(--br-flow-md); }
  .br-article__body details:not(details + details) { margin-top: var(--br-flow-md); }
  .br-article__body details:not(:has(+ details)) { margin-bottom: var(--br-flow-md); }
  .br-article__body > :is(:has(> .br-btn), :has(> div > .br-btn)) { margin-block: var(--br-flow-md); }

  /* The heading-adjacent bank rule in §8 is not restated here. It now weighs
     (0,1,4) against this block's (0,1,3) and wins at every width, and
     --br-flow-xs is fluid — 18.77 at 1440, 16.06 at 390, which are the two
     numbers it wants. A copy would restate the same token for no effect. */

  /* margin-top does NOT apply on a post that ends on an accordion. §9's
     collapse rule weighs (0,3,1) against this (0,1,0) and a media query adds
     none, so it wins here too and the phone gets the same single hairline. */
  .br-article__foot {
    margin-top: var(--br-flow-lg);
    padding-top: var(--br-flow-md);
  }

  /* Stacked, full width. Two 200px-min pills side by side at 358px of
     container would each be squeezed under their own minimum and the labels
     would sit off-centre. */
  .br-article__actions { flex-direction: column; align-items: stretch; }
  .br-article__actions .br-btn { width: 100%; }

  /* The blockquote keeps its card, but a card whose padding is 4u/3 at 390
     leaves a very narrow line inside it. */
  .br-article__body blockquote { padding: var(--br-card-px); }

  /* THE BACK LINK IS THE ONLY CONTROL THIS PAGE OWNS ON A PHONE.

     It measured 21.7 x 71px at 390 — the article's single navigation target,
     and the one thing on the page a reader reaches for before they have read
     anything. It passes WCAG 2.5.8 AA today only through the spacing
     exception (nothing is near it), which is a technicality, not a design.

     44px of height and one more 8px of width on the trailing edge. Nothing
     moves: min-height grows the flex box symmetrically about a label that was
     already `align-items: center`, and the padding is on the RIGHT only, so
     the chevron's ink stays exactly where .br-article__back-icon's -2px nudge
     put it, lined up with the title's stem below. Measured before and after —
     the label's left edge does not shift. margin-bottom is unchanged in value
     but now measures from the taller box, so the header's first interval
     absorbs the growth rather than the title moving down.

     This is the template holding the 44px line for its own control. Two shared
     nav controls are still under it (--br-tap-min's note in tokens.css names
     them); holding the line site-wide is a separate decision and this is not
     it. */
  .br-article__back {
    min-height: var(--br-tap-min);
    padding-right: var(--br-space-2);
  }
}

/* ==========================================================================
   §11. FORCED COLOURS

   Windows High Contrast throws away author background-color, which takes the
   blockquote's cream wash with it, and it distinguishes itself by fill alone —
   so it needs a boundary from the system palette. This section is only for the
   things drawn here that base.css has never seen.

   The carried-over CTA used to need six lines of Highlight/HighlightText here.
   It does not any more: it is a real .br-btn--primary now, and base.css's own
   forced-colors block repaints every one of those in the system accent pair.
   That is the deletion this section was owed — the copy here could drift from
   the original in high contrast as easily as it did in normal mode.
   ========================================================================== */

@media (forced-colors: active) {
  .br-article__body blockquote { border: 1px solid CanvasText; }

  /* The chevron is a background-image, which forced-colors also discards, so
     an accordion loses the only thing that says it opens. A CSS triangle
     inherits currentColor and survives. */
  .br-article__body summary::after {
    background: none;
    width: 0;
    height: 0;
    margin-top: 0.6em;
    border: 6px solid transparent;
    border-top-color: CanvasText;
  }
}

/* ==========================================================================
   §12. PRINT

   An 8,000-character article is the one page on this site people print.
   base.css already flattens the page background and drops shadows; this drops
   the chrome that has no meaning on paper and gives the column the sheet.
   ========================================================================== */

@media print {
  .br-nav,
  .br-article__back,
  .br-article__foot { display: none; }

  .br-article { padding-block: 0; }
  .br-article__head,
  .br-article__body { max-width: none; }
  .br-article__hero { display: none; }

  .br-article__body :is(h2, h3, h4) { break-after: avoid; }
  .br-article__body :is(figure, blockquote, li) { break-inside: avoid; }

  /* A closed <details> prints closed, and there is no CSS that reliably
     forces one open — the content is suppressed by the element, not by a
     display property. Nothing here pretends otherwise. The accordion keeps
     its rules on paper so a printed FAQ still reads as a list of questions;
     the answers a reader wants on paper are the ones they opened. */
}
