/* ==========================================================================
   Cilson Bookkeeping, main.css
   Built from the brand tokens in cilson-site-project-brief-v2.md
   Plain CSS, no build step, no framework. Portable to any static host.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Brand palette (brief §Brand tokens) */
  --trust-blue: #1F4E79;
  --profit-green: #4BAF65;
  --cta-green: #38834A;   /* Button face ONLY. White on --profit-green is 2.76 against a 4.5 requirement; on this it is 4.65. Split out rather than darkening --profit-green because that token has seventeen other uses, all non-text (borders, bullets, dots) plus one on the dark footer where darkening would reduce contrast. */
  --fresh-mint: #D8F3E4;
  --slate-gray: #2F2F2F;
  --cloud-gray: #F5F7FA;
  --guidance-gold: #FFC857;

  /* Derived tints/shades, all kept within the brand family */
  --trust-blue-dark: #163A5A;
  --trust-blue-light: #2E6FA8;
  --profit-green-dark: #2E7340;
  --mint-deep: #B6E6CE;
  --white: #FFFFFF;
  --ink: var(--slate-gray);
  --ink-muted: #5A5F66;
  --rule: #DFE5EC;

  /* Typography */
  --font-brand: "Poppins", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-body: "Inter", "Open Sans", "Segoe UI", system-ui, -apple-system, sans-serif;

  --step--1: 0.875rem;
  --step-0: 1.0625rem;
  --step-1: 1.25rem;
  --step-2: 1.5rem;
  --step-3: 1.875rem;
  --step-4: 2.375rem;
  --step-5: 4.125rem;   /* 66px, was 3rem/48px. Design bundle. H1 only: .hero__title and .hub-header h1. Both drop to --step-4 on mobile. */

  /* Space */
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2.5rem;
  --space-2xl: 4rem;
  --space-3xl: 6rem;

  /* Shape */
  --radius: 6px;
  --radius-lg: 10px;
  --shadow-sm: 0 1px 3px rgba(31, 78, 121, 0.05);
  --shadow-md: 0 2px 6px rgba(31, 78, 121, 0.16);
  --shadow-lg: 0 6px 20px rgba(31, 78, 121, 0.1);

  /* Layout */
  --wrap: 1180px;
  --wrap-narrow: 720px;
  --header-h: 76px;
}

/* --------------------------------------------------------------------------
   1a. Self-hosted webfonts
   Placed above `body`, the first rule that applies either family.

   These three files are byte-identical to what Google served (MD5 verified
   against the live gstatic responses), so nothing about the rendering of
   Poppins changes. Self-hosting removes two cross-origin round trips before
   first paint: the DNS/TLS/HTTP to fonts.googleapis.com for the stylesheet,
   then a second connection to fonts.gstatic.com for the files it names.

   src uses format("woff2"), NOT format("woff2-variations"). Both parse in
   current Chrome, verified before choosing, but woff2-variations is a
   transitional syntax that was dropped from the spec, and a browser that
   does not recognise a format string SKIPS that src entry entirely, which
   would leave no font at all rather than a degraded one. Google serves this
   exact file as plain woff2, which is the broadest-support precedent there
   is. `font-weight: 100 900` is what exposes the variable axis; the format
   string is not what makes it variable.

   CONSEQUENCE, INTENDED: Inter 600 and 700 previously had no face and were
   drawn as synthetic bold off the 500. They now get real instructed weights,
   so .founder-card__takeaway, .founder-card__arrow and article <strong> will
   render slightly differently. That is the point of shipping the axis.

   Inter italic is deliberately NOT shipped and stays synthetic: one element
   on one page (.founder__tagline) does not justify 51,832 bytes.

   Poppins 400 and 500 are deliberately NOT shipped. Both already resolve to
   Poppins 600 today (measured: pixel-identical), so omitting them preserves
   current rendering rather than changing it.

   unicode-range is reproduced exactly as both Google and Fontsource declare
   it. It excludes U+2190, U+2192 and U+25BE. DO NOT WIDEN THE RANGE TO ADMIT
   THEM. That instruction is the durable part of this note and it still stands.

   The rest of what stood here was true on 28 July 2026 and is now false, so it
   is corrected rather than left to mislead. It read that those three codepoints
   "fall through to a system font" and "already do that today". None of the
   three is in the codebase any more. The arrows went the next morning, in
   PR #40 and PR #47, which swapped them for angle quotes the subset carries.
   The nav caret went in PR #81, which replaced it with a clipped triangle.
   Nothing now depends on those codepoints, which is why the range does not need
   widening: the correct fix was always to stop using them, and that is done.
   See audit-findings H-17 for why a drawn mark was chosen over a character.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/fonts/inter-latin-wght-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/poppins-latin-600-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/poppins-latin-700-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   2. Reset / base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
  -webkit-text-size-adjust: 100%;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.7;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
}

/* 404 only: the page fills the viewport and the footer grows to meet its bottom edge, so no white shows below the footer and no content element moves with viewport height. The footer is display block and paints its own opaque background, so the added height appends below its content in the same colour. Scoped to .page-404; no other page carries the class, and .site-footer's own rules are untouched. */
.page-404 {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}
.page-404 .site-footer {
  flex: 1 0 auto;
}

img, svg, video { max-width: 100%; height: auto; display: block; }

h1, h2, h3, h4 {
  font-family: var(--font-brand);
  font-weight: 700;
  line-height: 1.2;
  /* Tighter heading tracking, from the Design bundle. NOTE: this is an ADD,
     not a change. Headings carried no letter-spacing before, so the jump is
     from 0 to -0.03em, not from -0.025em. It applies to h3 and h4 as well,
     which are 24px and 20px; the bundle only ever tracks large display
     headings this tight. Watch the small headings when judging this. */
  letter-spacing: -0.03em;
  color: var(--trust-blue);
  margin: 0 0 var(--space-md);
  text-wrap: balance;
}

h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); font-weight: 600; }
h4 { font-size: var(--step-1); font-weight: 600; }

p { margin: 0 0 var(--space-md); }
p:last-child { margin-bottom: 0; }

a { color: var(--trust-blue); text-decoration: underline; text-underline-offset: 2px; }
a:hover { color: var(--trust-blue-light); }

ul, ol { margin: 0 0 var(--space-md); padding-left: 1.25rem; }
li { margin-bottom: var(--space-xs); }

strong { font-weight: 600; }

hr { border: 0; border-top: 1px solid var(--rule); margin: var(--space-xl) 0; }

/* Focus ring, split by surface. DECIDED 17 August 2026, see audit-protocol.md,
   Other open items. Trust Blue is the BASE because light surfaces are the unnamed
   default and dark bands are named: four selectors below against thirteen light
   painting containers. Gold measured 1.54 on white, 1.43 on cloud gray and 1.31
   on mint, against a 3:1 target; Trust Blue measures 8.66, 8.07 and 7.37 there.
   Gold is kept on the dark bands, where it measures 5.63 and 7.64 and Trust Blue
   would measure 1.00. A NEW DARK BAND MUST BE ADDED TO THE LIST BELOW or it takes
   the light ring and the ring disappears on it. That failure is visible on sight,
   which is why the default sits this way round rather than the other. */
:focus-visible {
  outline: 3px solid var(--trust-blue);
  outline-offset: 2px;
  border-radius: 2px;
}

.section--blue :focus-visible,
.cta-band :focus-visible,
.site-footer :focus-visible,
.hub-header :focus-visible {
  outline-color: var(--guidance-gold);
}

/* --------------------------------------------------------------------------
   3. Layout helpers
   -------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--space-lg);
}

.wrap--narrow { max-width: var(--wrap-narrow); }

/* Two-column section shape, shared by the homepage client-pain section and by
   the problem section on /monthly-bookkeeping. EXTRACTED 26 August 2026 from
   .pain-split in section 6b, which had carried it alone since 27 July 2026.
   That block asked for exactly this on recurrence; the recurrence has happened.

   A MODIFIER ON .wrap, because that is the element it sits on and .wrap--narrow
   is already the one modifier that element has. Not .section-split, which is one
   hyphen from .section--split and would read as a section modifier, when every
   section--* in this file goes on the section element itself. Not a bare .split:
   .cta-split is NOT a two-column grid, it is text-align center, left over from a
   geometry that was removed, so a bare .split would imply a family that is not
   there.

   The default is an EVEN split. .pain-split overrides the tracks, the alignment
   and the collapse width, and wins on source order alone at equal specificity,
   being later in this file. */
.wrap--split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2xl);
  align-items: center;
}

@media (max-width: 860px) {
  /* 860 is this file's convention for a section-level collapse, and it is what
     the hero, the featured block, the founder header and all three About splits
     use. .pain-split's 900 is the documented exception and is unaffected. */
  .wrap--split { grid-template-columns: 1fr; gap: var(--space-xl); }
}

/* Vertical separation for a wrap that stacks its blocks instead of splitting
   them. The band on /bookkeeping-setup-migration reads heading, label, cards
   in one flow, and nothing else supplies a gap between the two: .wrap carries
   only horizontal margin, .section pads the band not its children, and
   p:last-child zeroes the lead's bottom margin. Measured 2 September 2026:
   without this the lead and the label sit 0.00 apart.

   The two values are the ones .wrap--split and .pain-split already use at
   these widths, so the stacked band separates exactly as the split one did. */
.wrap--stack > * + * { margin-top: var(--space-2xl); }

@media (max-width: 900px) {
  .wrap--stack > * + * { margin-top: var(--space-xl); }
}

/* The photograph in a split section. RADIUS IS A LITERAL 8px, and it is the
   THIRD instance of that literal rather than a new token. The other two are
   .hero__portrait::before and .hero__portrait img, both in section 6a, and
   between them they are ONE component: a portrait and the mint panel behind
   it. A token would assert that all three always move together, and with two
   of the three being the same component that claim is untested.

   A FOURTH PHOTOGRAPH IS THE MOMENT TO NAME A TOKEN. That is the whole reason
   this stays a literal, and the count is written here so a later reader can
   see it without going looking. Section 6a already names 8px as the candidate.

   SCOPED TO A DIRECT img CHILD, so it reaches a photograph and not a logo or a
   badge that a split section might later hold. Measured 26 August 2026 across
   the six built pages: this selector matches ONE element, and it matches ZERO
   on the homepage, which carries .wrap--split and three images of its own. */
.wrap--split > img { border-radius: 8px; }

/* A split section whose second column is a PHOTOGRAPH, and whose text has to
   break apart on mobile so the image can sit between the heading and the body.

   ONE SOURCE ORDER CANNOT EXPRESS BOTH ARRANGEMENTS. Desktop wants the text
   contiguous in the left column beside the image. Mobile wants the image
   BETWEEN the heading and the paragraphs. The elements to be separated are
   siblings inside a child rather than children of the grid, so no single
   ordering of the markup produces both, and the four pieces are placed by line
   instead. That is what section 12's .story already does, and for the same
   reason: the arrangement lives here so the markup stays in reading order.

   ROW-GAP IS 0 DELIBERATELY, and the spacing comes from the elements' own
   margins. .wrap--split sets gap for both axes, so flattening one text child
   into three would add two row gaps and MEASURED 128px to the desktop section,
   pushing the heading down 64. With row-gap 0 the desktop rendering is
   identical to the unflattened version on every element.

   THIS IS NOT DRIFT FROM THE 21 AUGUST REMOVAL OF `order`. That removal was the
   better answer to a case where source order COULD express the result, so the
   property had nothing left to do. Here it cannot, and nothing below uses
   `order` either: these are line placements, not a reordering of a flow. */
.media-split { row-gap: 0; }

.media-split > .eyebrow { grid-column: 1; grid-row: 1; }
.media-split > h2 { grid-column: 1; grid-row: 2; }
.media-split > .media-split__body { grid-column: 1; grid-row: 3; }
.media-split > img { grid-column: 2; grid-row: 1 / 4; align-self: center; }

@media (max-width: 860px) {
  /* One column, so the image takes its own row between the heading and the
     body, and carries the breathing room the row gap used to give it.
     These overrides are NOT redundant with source order: without them the
     image keeps `grid-column: 2` and creates an implicit second column. */
  .media-split > img { grid-column: 1; grid-row: 3; margin-block: var(--space-xl); }
  .media-split > .media-split__body { grid-column: 1; grid-row: 4; }
}

/* A service page built from this shape with NO photograph collapses to one
   full-width column instead of stranding its text at half width in an empty
   two-column grid.

   THE SCOPING TO .media-split IS THE WHOLE POINT AND MUST NOT BE SIMPLIFIED.
   Written against .wrap--split instead, this rule COLLAPSES THE HOMEPAGE,
   measured: that section's second column is a div of cards rather than an img,
   so it matches :not(:has(> img)) and loses its 0.85fr 1.15fr tracks. */
.media-split:not(:has(> img)) { grid-template-columns: 1fr; }

/* With no image beside it the body is a single full-width column of
   multi-paragraph running prose, so it needs a measure of its own. .prose is the
   site's other running-prose block and takes the same token; the value is the
   token rather than a number so the two cannot drift apart. */
.media-split:not(:has(> img)) > .media-split__body { max-width: var(--wrap-narrow); }


.section { padding-block: var(--space-2xl); }
.section--tight { padding-block: var(--space-xl); }
.section--mint { background: var(--fresh-mint); }
.section--cloud { background: var(--cloud-gray); }

/* DARK BANDS: ONE CONDITION, SEVERAL TREATMENTS, AND THEY MOVE TOGETHER.
   ADDED 23 August 2026, immediately above the rule that MAKES a band dark,
   because that is where someone adding a dark-band treatment will be looking.

   A band is dark when it paints Trust Blue or Trust Blue Dark. The classes
   that do that are .section--blue, .cta-band, .hub-header and .site-footer.
   .cta-split paints nothing of its own: it is a layout modifier that only
   ever appears on a section already carrying .section--blue, so it inherits
   the condition rather than creating it.

   WHAT DEPENDS ON THE CONDITION, named by what each does rather than by line
   number, because a line number goes stale silently:

     the focus outline turns gold instead of Trust Blue
     headings turn white
     the section lead turns near-white
     the eyebrow turns gold
     the button note drops to 80% white
     the subordinate text link turns white with a half-alpha underline
     the booking control takes a white ghost face instead of green,
       which is CLAUDE.md rule 5g

   A NEW DARK-BAND TREATMENT IS ADDED TO ALL OF THEM OR TO NONE. Adding one
   to a single class is what produced every defect this comment exists to
   prevent.

   THE DIFFERING SELECTOR LISTS FOLLOW FROM WHERE THE ELEMENTS SIT, and they
   are not a divergence waiting to be reconciled. MEASURED 23 August 2026:
   every treatment above reaches every dark band its element actually occurs
   in, with one exception, and that exception is described here rather than
   left implied.

   THE EXCEPTION IS .eyebrow. It occurs on .section--blue and on .hub-header,
   and its dark-band rule names .section--blue only. On .hub-header what
   reaches it is .hub-header p, which wins on specificity, so the eyebrow
   there takes THE PARAGRAPH'S size and colour rather than its own: 20px
   where its own rule sets 14px, and 88% white where its own rule sets
   --profit-green-dark. The gold override never reaches that band, because it
   names .section--blue. What the eyebrow does keep is its tracking, casing,
   family and weight, and its 0.08em against the won 20px is what makes the
   1.60px tracking there rather than the 1.12px it has on .section--blue.
   IT RENDERS CORRECTLY: 7.14 on the Trust Blue end of that gradient and 9.46
   on the Trust Blue Dark end.

   THE DEPENDENCY IS THE PART TO KNOW BEFORE TOUCHING THAT MARKUP. The
   coverage holds only while the element is a p. If it ever becomes a span or
   a div, only .eyebrow matches and the colour falls to --profit-green-dark,
   which computes 1.50 on Trust Blue, WORSE THAN THE 1.86 THAT PROMPTED THIS
   WHOLE COMMENT. The resources-hub template carries the same shape, so hub
   pages built later inherit it.

   RULED BY CHRISTOPHER 23 August 2026: NOT FIXED HERE, AND NOT AN OPEN ITEM.
   The rendering is correct, no page is affected, and adding a rule for it
   inside a branch about button treatment would put a styling change outside
   that branch's scope. THIS PARAGRAPH IS THE RECORD, which is why it carries
   the mechanism and not just the name of the exception.

   WHY THIS COMMENT EXISTS, and the obvious lesson is the wrong one. It is
   not that nobody wrote the reasoning down. The .cta-split hover override
   DID carry its reasoning, in full, in its own block comment. That reasoning
   was written as a fact about THAT BLOCK rather than about the condition, so
   nobody reading it ever asked whether anything else on a dark band needed
   the same thing. A comment explaining one rule and a comment naming a
   condition and its dependants are different artifacts, and only the second
   prevents the next gap. Three defects surfaced from that shape in a single
   session on 23 August 2026: the text link had no dark-band form at all, the
   booking control was green on two dark bands, and the rule covering for the
   second of those was scoped to one class.

   .site-footer IS A DELIBERATE EXCLUSION FOR BUTTON TREATMENT, and it is
   written down because AN EXCLUSION NOT WRITTEN DOWN GETS CLOSED BY THE NEXT
   PERSON WHO NOTICES THE FOOTER IS DARK. It sits on --trust-blue-dark rather
   than --trust-blue. Its control is .btn--outline-light, and CLAUDE.md rule
   5g rejected .btn--ghost-light there on purpose: a white face reads as MORE
   prominent than green rather than less, and the footer control is
   subordinate. MEASURED 23 August 2026 across public/ and _templates/: the
   footer carries zero .btn--primary, zero .btn-link, zero .btn-note and zero
   .btn--ghost-light, each zero controlled in the same run by the same matcher
   returning the .btn--outline-light controls the footer does carry. The other
   treatments above still reach it; only the button treatment differs.
   -------------------------------------------------------------------------- */
.section--blue { background: var(--trust-blue); color: var(--white); }
.section--blue h2,
.section--blue h3 { color: var(--white); }

.section__lead {
  font-size: var(--step-1);
  color: var(--ink-muted);
  max-width: 60ch;
}

.section--blue .section__lead { color: rgba(255, 255, 255, 0.88); }

.eyebrow {
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--profit-green-dark);
  margin: 0 0 var(--space-xs);
}

.section--blue .eyebrow { color: var(--guidance-gold); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--space-md);
  top: -100px;
  z-index: 200;
  background: var(--trust-blue);
  color: var(--white);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius);
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus { top: var(--space-md); color: var(--white); }

/* --------------------------------------------------------------------------
   4. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-0);
  line-height: 1.2;
  text-decoration: none;
  padding: 0.8rem 1.6rem;
  border: 2px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color 0.18s ease, border-color 0.18s ease,
              color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}

/* Primary: green background, white text, hover blue (brief §Brand tokens) */
.btn--primary {
  background: var(--cta-green);
  color: var(--white);
  box-shadow: var(--shadow-sm);
}
.btn--primary:hover,
.btn--primary:focus-visible {
  background: var(--trust-blue);
  color: var(--white);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.btn--secondary {
  background: transparent;
  color: var(--trust-blue);
  border-color: var(--trust-blue);
}
.btn--secondary:hover,
.btn--secondary:focus-visible {
  background: var(--trust-blue);
  color: var(--white);
}

.btn--ghost-light {
  background: var(--white);
  color: var(--trust-blue);
  box-shadow: var(--shadow-sm);
}
.btn--ghost-light:hover,
.btn--ghost-light:focus-visible {
  background: var(--fresh-mint);
  color: var(--trust-blue-dark);
}

/* Subordinate control on a dark band. The footer's contact link, which sits
   where the booking button used to and must not read as its equal.
   ~~.btn-link is light-background only and has no dark override;~~
   **`.btn-link` had no dark override when this was written and gained one in
   the same commit, `.cta-band .btn-link`, which is why it is not the answer
   here either: that override is Trust Blue's band, not this one. CORRECTED 17
   August 2026.** `.btn--secondary` is Trust Blue on transparent and
   disappears here; `.btn--ghost-light` is a white face and reads as MORE
   prominent than green rather than less.
   Measured 17 August 2026. See CLAUDE.md rule 5g. */
.btn--outline-light {
  background: transparent;
  color: var(--white);
  border-color: rgba(255, 255, 255, 0.4);
}

.btn--outline-light:hover,
.btn--outline-light:focus-visible {
  background: var(--white);
  color: var(--trust-blue);
  border-color: var(--white);
}

.btn--sm { padding: 0.55rem 1.1rem; font-size: var(--step--1); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  align-items: center;
}

/* A button row that follows a grid, list or card block needs breathing room.
   .post-nav is in here because the article template's "Browse all accounting
   resources" button sits directly under the prev/next cards and was otherwise
   flush against them. */
.grid + .btn-row,
.article-grid + .btn-row,
.included-list + .btn-row,
.post-nav + .btn-row { margin-top: var(--space-xl); }

.btn-note {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-top: var(--space-sm);
}

/* THE NOTE SITS BETWEEN THE BUTTON AND THE SECONDARY CONTROL, IN THE MARKUP.
   REBUILT 21 August 2026, replacing a reorder that ran from 27 July 2026.

   Every row that carries a note now stacks at every width: the note is
   flex-basis 100%, so it takes its own line and pushes the secondary control
   below it. Document order, visual order and focus order agree everywhere, and
   no order property is involved.

   Why the reorder went. The pair broke at a width set by its own two labels,
   measured 21 August: button 283.13 plus a 16px gap plus a label of 136.06,
   164.36, 175.67 or 226.61, giving four thresholds from 436 to 526. A single
   media query cannot serve four numbers, and between 861 and each row's own
   threshold the row stacked with no reorder applied, so the note fell past the
   control it describes. A per-row threshold would have to be kept in step with
   the copy it is derived from, and would fail silently in a band when it was
   not. The Flexbox specification also states that order is for visual
   reordering only, and Firefox follows visual order for tab sequence where
   other browsers follow the document, so the reorder read differently by
   browser.

   What this gives up: the button and the secondary control no longer share a
   line at any width. That is the cost of one arrangement everywhere. */
.btn-row > .btn-note {
  flex-basis: 100%;
  margin-top: calc(var(--space-sm) - var(--space-md));
}

.section--blue .btn-note { color: rgba(255, 255, 255, 0.8); }

/* --------------------------------------------------------------------------
   5. Header / navigation
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--white);
  border-bottom: 1px solid var(--rule);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: var(--header-h);
}

/* Brand lockup. Christopher's own logo, top of every page, and the largest
   brand mark on the site: the ProAdvisor badge in the footer must always read
   smaller than this (ProAdvisor Brand Use Guide). Height is fixed and width is
   auto so the file is never stretched or distorted. Source file is 400x128, so
   at a 40px display height it renders at ~3x for crispness on retina. */
.brand {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  text-decoration: none;
  border-radius: var(--radius);
}

.brand__logo {
  height: 44px;
  width: auto;
  max-width: none;
}

@media (max-width: 480px) {
  .brand__logo { height: 36px; }
  .btn--sm { padding-left: 12px; padding-right: 12px; }
}

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--trust-blue);
}

.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block;
  width: 20px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-toggle__bars { position: relative; }
.nav-toggle__bars::before,
.nav-toggle__bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle__bars::before { top: -6px; }
.nav-toggle__bars::after { top: 6px; }

.nav-toggle[aria-expanded="true"] .nav-toggle__bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after { transform: translateY(-6px) rotate(-45deg); }

.site-nav { display: flex; align-items: center; gap: var(--space-lg); }

.site-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav__link {
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--slate-gray);
  text-decoration: none;
  padding: var(--space-xs) 0;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.site-nav__link:hover { color: var(--trust-blue); }

/* Active nav link highlight (set by main.js) */
.site-nav__link.is-active {
  color: var(--trust-blue);
  border-bottom-color: var(--profit-green);
}

/* Services dropdown */
.has-submenu { position: relative; }

.submenu-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--slate-gray);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: var(--space-xs) 0;
  cursor: pointer;
}
.submenu-toggle:hover { color: var(--trust-blue); }
.submenu-toggle.is-active { color: var(--trust-blue); border-bottom-color: var(--profit-green); }
/* The caret is a CLIPPED TRIANGLE, not a character. The character it replaced
   was outside the declared unicode-range and absent from every self-hosted
   cmap, so it failed both tests in CLAUDE.md rule 11 and rendered in whatever
   system face the visitor happened to have. background is currentColor on
   purpose: the button recolours on hover and the triangle has to follow it.
   font-size stays even though the span holds no text, because the em box is
   what the width and height below are sized against. */
.submenu-toggle__caret {
  transition: transform 0.2s ease;
  font-size: 1.4em;
  flex: none;
  width: 0.37em;
  height: 0.32em;
  background: currentColor;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.submenu-toggle[aria-expanded="true"] .submenu-toggle__caret { transform: rotate(180deg); }

.submenu {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 0;
  min-width: 250px;
  list-style: none;
  margin: 0;
  padding: var(--space-xs);
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  display: none;
}
.submenu[data-open="true"] { display: block; }

.submenu li { margin: 0; }

.submenu a {
  display: block;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius);
  font-size: var(--step--1);
  color: var(--slate-gray);
  text-decoration: none;
}
.submenu a:hover { background: var(--fresh-mint); color: var(--trust-blue); }
.submenu a.is-active { background: var(--fresh-mint); color: var(--trust-blue); font-weight: 600; }

/* HOVER BRIDGE. Invisible on purpose, and NOT dead code: deleting it brings
   back a menu that closes while you are reaching for it.

   .submenu opens at top: calc(100% + 0.5rem), so a strip that tall sits between
   the bottom of the Services <li> and the top of the panel, and it belongs to
   neither. Measured, not assumed: elementFromPoint at the strip's midpoint
   returns UL.site-nav__list, which is OUTSIDE the <li>, so li:hover goes false
   and the panel closes mid-travel.

   This transparent pseudo-element extends the panel upward across that strip.
   .submenu is a DOM descendant of the <li>, so hovering the bridge keeps
   li:hover true and the panel open. It has no visual effect at any size.

   Its height MUST equal the offset in .submenu's top declaration above. That
   declaration writes the offset as a literal 0.5rem; --space-xs is the same
   0.5rem, used here so the bridge is not a second bare magic number. If the
   offset ever changes, change this with it.

   DESKTOP ONLY, and the media query is load-bearing rather than tidiness.
   Below 861px .submenu is position: static (see the block below), so there is
   no gap to bridge; an absolutely positioned ::before would then resolve
   against .has-submenu and float a transparent 8px strip over the nav item
   ABOVE, intercepting taps on it. 861px is the same threshold main.js uses for
   its hover branch, so the bridge exists exactly where hover-to-open does.
   BOTH NUMBERS MOVED FROM 901 TO 861 ON 21 August 2026, with the nav
   breakpoint, so that the nav and the hero now change at one width. The
   relationship this paragraph describes is unchanged, because main.js moved in
   the same commit. */
@media (min-width: 861px) {
  /* The header row is space-between. With the booking button as a third
     child, this is what keeps the nav and the button together on the
     right rather than spreading all three across the row. */
  .site-nav { margin-left: auto; }

  .submenu::before {
    content: "";
    position: absolute;
    top: calc(-1 * var(--space-xs));
    left: 0;
    right: 0;
    height: var(--space-xs);
  }
  /* The bare li rule sets an 8px bottom margin on every list item. On the horizontal desktop nav that margin is invisible and only inflates the list's box, so it is removed here. It is NOT removed at mobile widths, where the same margin is the visible gap between drawer rows. */
  .site-nav__list > li { margin-bottom: 0; }

  /* Desktop nav: size each list item to its own content. The Services item is a button at inline-flex, so its box sits in its list item's line box and makes that item taller; the three plain anchors are inline and leave theirs at line-height. Without this the row's align-items: center pushes the three shorter items down by about 5px. DESKTOP ONLY, MOVED INTO THIS QUERY 17 September 2026. At the top level it also reached the drawer below 861px, where it made each list item a flex row, so an open Services menu sat beside its button instead of below it: audit-findings.md H-46. */
  .site-nav__list > li {
    display: flex;
    align-items: center;
  }
}

/* Mobile nav (tested at 375px) */
@media (max-width: 860px) {
  .nav-toggle { display: inline-flex; }
  .nav-toggle { order: 1; }

  .site-nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--space-md) var(--space-lg) var(--space-xl);
    background: var(--white);
    border-bottom: 1px solid var(--rule);
    box-shadow: var(--shadow-md);
    max-height: calc(100vh - var(--header-h));
    overflow-y: auto;
  }
  .site-nav[data-open="true"] { display: flex; }

  .site-nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .site-nav__list > li { border-bottom: 1px solid var(--rule); }

  .site-nav__link,
  .submenu-toggle {
    display: flex;
    justify-content: space-between;
    width: 100%;
    padding: var(--space-md) 0;
    font-size: var(--step-0);
    border-bottom: 0;
  }
  .site-nav__link.is-active,
  .submenu-toggle.is-active { border-bottom: 0; }

  .submenu {
    position: static;
    min-width: 0;
    border: 0;
    box-shadow: none;
    padding: 0 0 var(--space-md) var(--space-md);
    border-left: 2px solid var(--fresh-mint);
    margin-bottom: var(--space-sm);
  }
}

/* --------------------------------------------------------------------------
   6. Hero: service pages
   -------------------------------------------------------------------------- */

.hero {
  background: linear-gradient(180deg, var(--cloud-gray) 0%, var(--white) 100%);
  padding-block: var(--space-2xl);
  border-bottom: 1px solid var(--rule);
}

.hero__inner {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: var(--space-2xl);
  align-items: center;
}

.hero__title { font-size: var(--step-5); margin-bottom: var(--space-md); }

.hero__standfirst {
  font-size: var(--step-1);
  color: var(--ink-muted);
  max-width: 52ch;
  margin-bottom: var(--space-lg);
}

/* Pricing chip: renders the "no published figure" default from the brief */
.hero__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
  padding: 0;
  list-style: none;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--trust-blue);
  background: var(--fresh-mint);
  border-radius: 999px;
  padding: 0.35rem 0.9rem;
  margin: 0;
}

.chip--gold { background: rgba(255, 200, 87, 0.28); color: var(--trust-blue-dark); }

.hero__aside {
  background: var(--white);
  border: 1px solid var(--rule);
  border-top: 4px solid var(--profit-green);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--shadow-md);
}

.hero__aside h2 {
  font-size: var(--step-1);
  margin-bottom: var(--space-sm);
}

/* The aside note is the only .btn-note that carries a link. Family and weight are
   taken from .hero__caption-name, which is the site's existing linked-name
   treatment; nothing here invents a value.

   NO COLOUR IS SET. The global `a` rule already paints the anchor --trust-blue,
   and the underline that rule supplies is what separates the link from the note
   text around it, which is close to it in colour.

   SCOPED TO .hero__aside DELIBERATELY, and the scope is the point rather than
   tidiness. .btn-note also occurs on dark bands, where an anchor resolves to
   --trust-blue on a --trust-blue background and disappears in every state. That
   is a live hazard this rule does not fix and must not reach. */
.hero__aside .btn-note a {
  font-family: var(--font-brand);
  font-weight: 600;
}

.hero__aside-list { list-style: none; padding: 0; margin: 0 0 var(--space-lg); }

.hero__aside-list li {
  position: relative;
  padding-left: 1.6rem;
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-bottom: var(--space-sm);
}
.hero__aside-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--profit-green);
}

@media (max-width: 860px) {
  .hero__inner { grid-template-columns: 1fr; gap: var(--space-xl); }
  .hero__title { font-size: var(--step-4); }
}

/* --------------------------------------------------------------------------
   6a. Homepage hero, Design bundle Variation A (split portrait)
   ADDED 27 July 2026. Purely additive: no rule above was modified or removed,
   and no token was touched. Everything here is a new class or modifier.

   Not adopted from Variation A, deliberately:
   - It puts the three stats inside the left column under a rule. Ours stays a
     full-width band below the hero, so hero height can be judged separately.
   - It carries a floating "Books tidy by 5 pm" card over the portrait's lower
     left. That duplicates a stat already in the strip, so it is omitted.
   - Its grid is 1.08fr/.92fr against our existing 1.15fr/.85fr. Changing that
     would mean editing .hero__inner, which is out of scope here.
   - ITS BACKGROUND. Variation A carries none, so the hero would be flat white.
     The site header is also white, so a white hero directly beneath it merges
     the two and the header stops reading as a separate bar: the same
     merged-boundary problem the FAQ and services teaser had before PR #23.
     The gradient (cloud to white, Variation B's treatment) separates header
     from hero and resolves to white before the blue client-pain band, so it
     introduces no adjacency problem of its own. DECIDED 27 July 2026: KEEP the
     gradient. Already expressed in tokens below, never raw bundle hex.
     Recorded here because a bundle comparison flagged it as an unexplained
     difference; it is a decision, not an oversight.
   -------------------------------------------------------------------------- */

/* Eyebrow as a mint pill with a green dot. A modifier, so .eyebrow keeps its
   own type treatment and every other page is untouched. */
.eyebrow--pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  background: var(--fresh-mint);
  color: var(--trust-blue);
  padding: 0.4rem 0.85rem;
  border-radius: 100px;
  letter-spacing: 0.06em;
}

.eyebrow__dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--profit-green);
}

/* Secondary hero action as a text link rather than a second box, so the two
   CTAs are not weighted equally and the row stays on one line. */
.btn-link {
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--trust-blue);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
  text-decoration: none;
  white-space: nowrap;
}

.btn-link:hover,
.btn-link:focus-visible {
  color: var(--profit-green-dark);
  border-bottom-color: var(--profit-green);
}

/* Portrait with the offset mint panel behind it.
   RADIUS IS A LITERAL 8px, matching the bundle. --radius is 6px and
   --radius-lg is 10px, so neither fits, and changing a token was out of
   scope. If this treatment spreads, 8px is the candidate for a new token. */
.hero__portrait {
  position: relative;
}

.hero__portrait::before {
  content: "";
  position: absolute;
  inset: 18px -18px -18px 18px;
  background: var(--fresh-mint);
  border-radius: 8px;
}

.hero__portrait img {
  position: relative;
  width: 100%;
  border-radius: 8px;
  border: 1px solid var(--rule);
}

@media (max-width: 860px) {
  /* Hero has stacked to one column, so the portrait is now full width. The
     panel's outward right offset is pulled in to keep it clear of the wrap
     padding and well away from the viewport edge. */
  .hero__portrait::before { inset: 14px -12px -12px 14px; }
}

@media (max-width: 560px) {
  /* No outward offset at phone widths: the panel sits behind and below only,
     so nothing can extend past the content box. */
  .hero__portrait::before { inset: 10px 0 -10px 10px; }
  .btn-link { white-space: normal; }
}

/* Stats inside the hero's left column, per Variation A, replacing the separate
   full-width band on the homepage. The .stat-strip rules earlier in this file
   are deliberately LEFT IN PLACE and simply unused, so anything still
   referencing them keeps working. */
.hero__stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-xl);
  border-top: 1px solid var(--rule);
}

.hero__stat { margin: 0; }

.hero__stat-value {
  display: block;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-2);
  line-height: 1.15;
  color: var(--trust-blue);
}

.hero__stat-label {
  display: block;
  margin-top: var(--space-2xs);
  font-size: var(--step--1);
  line-height: 1.4;
  color: var(--ink-muted);
}

/* Portrait caption. NOT from Variation A; it follows the ActiveCampaign
   landing page. It lives in the portrait column and outside .hero__portrait,
   so the offset mint panel does not sit behind it and it cannot affect the
   left column's height. */
.hero__caption {
  margin: var(--space-md) 0 0;
  text-align: center;
}

.hero__caption-name {
  display: block;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-0);
  color: var(--trust-blue);
}

.hero__caption-role {
  display: block;
  margin-top: 2px;
  font-size: var(--step--1);
  color: var(--ink-muted);
}

/* Tighter hero block padding so the primary CTA and its reassurance line clear
   a 650px content area, which is what a real laptop window gives once browser
   chrome is taken off a 720px screen. A modifier, so other heroes are
   unaffected. align-items: start stops a tall portrait column from vertically
   centring the text column and pushing the CTA back down. */
.hero--tight { padding-block: var(--space-lg) var(--space-xl); }
.hero--tight .hero__inner { align-items: start; }

@media (max-width: 560px) {
  /* One stat per row. Two of the three labels run long enough that a wrapped
     row leaves a ragged orphan, and three columns at this width is unreadable. */
  .hero__stats {
    flex-direction: column;
    gap: var(--space-md);
  }
}

/* Hero spacing corrections. Both are separate later rules rather than edits to
   the rules above, so this stays additive with zero deletions.

   CAPTION CLEARANCE. The mint panel overhangs the image by 18px at the bottom
   (inset ... -18px ...), so the caption's VISIBLE gap is its top margin minus
   that overhang. At --space-md the visible gap was about 6px and the caption
   read as part of the panel rather than as its own element. --space-xl leaves
   roughly 22px clear, which matches the hero's other vertical rhythm. The
   overhang shrinks to 12px and then 10px at the narrower breakpoints, so the
   visible gap grows slightly on smaller screens, which is the right direction. */
.hero__caption { margin-top: var(--space-xl); }

/* EVEN BLOCK PADDING. Measured at 1440x900 before changing anything: about
   28px above the eyebrow against about 41px below the stats, and the two
   columns ended within roughly 10px of each other. So the imbalance was NOT
   surplus pooling beneath a shorter portrait column, and align-items: start is
   not the cause. It was the asymmetric padding set in commit 4: --space-lg top
   against --space-xl bottom. Equalising the bottom to the top is the fix.

   This does NOT put the fold at risk. At 1280x650 the primary CTA sits around
   y=550 and the hero's bottom padding begins around y=742, outside the
   viewport entirely. Padding below the content cannot move anything above it.
   Verified by re-rendering at 1280x650 rather than assumed. */
.hero--tight { padding-bottom: var(--space-lg); }

/* Hairline rule above the stats, and the space on either side of it. The rule
   divides the reassurance line above from the stats below, so it has to belong
   to neither group.

   THE VALUES: 28px above and 28px below, set here as 1.75rem on both
   margin-top and padding-top. Both are literals because no spacing token sits
   between --space-lg (24px) and --space-xl (40px).

   HOW THEY WERE REACHED, kept because it is not recoverable from the values.
   24 above and 32 below was the first split that looked even, and it read
   wrong: the stats are large type whose line box carries leading above the cap
   height, so the space below the rule reads larger than it measures and the
   rule appeared to belong to the reassurance line above it. Shifting 4px from
   below the rule to above it fixes that. The total block contribution is
   deliberately unchanged at 57px, being 28 plus a 1px border plus 28 against
   24 plus 1 plus 32, so the left column height does not move and the 1280x650
   fold stays exactly as it is. --space-xl was tried below the rule before 32px
   and overshot: it made the left column tall enough to reopen the vertical
   imbalance under the portrait column.

   CORRECTED 21 August 2026. Two superseded declarations were removed, the
   16px padding-top in the base .hero__stats rule and the 32px intermediate
   that sat here, both of which this rule had overridden since the day they
   were written. Their two comments are merged into this one, which is why this
   block carries the derivation as well as the values. */
.hero__stats {
  margin-top: 1.75rem;
  padding-top: 1.75rem;
}

/* --------------------------------------------------------------------------
   6b. Homepage client-pain section: asymmetric split, translucent card grid
   ADDED 27 July 2026. Additive only: no rule above modified, no token touched.

   The dark band, the white headings and the gold eyebrow all come from
   .section--blue, which was already in main.css and unused by any page until
   now. So no new colour is introduced for any of them, and the gold eyebrow on
   Trust Blue was already a decided pairing rather than a new one.

   The two translucent values are white at 6% and 14% alpha, straight from the
   bundle. They are not new colours: they are the section's own white over its
   own background, so the palette is unchanged.

   Named for the section rather than generically, because the treatment was
   used once and this block asked for a rename if it ever recurred. BOTH HALVES
   OF THAT REQUEST ARE ANSWERED AND NEITHER IS STILL OWED. The generic half,
   display and gap and the collapse, became .wrap--split in section 3 on
   26 August 2026, when the problem section on /monthly-bookkeeping became the
   second two-column section on this site. THE CARD FAMILY WAS RENAMED TO
   .point-cards AND .point-card ON 30 AUGUST 2026, when it recurred on
   /bookkeeping-setup-migration. .pain-split KEPT ITS NAME, because the geometry
   it describes is a pain-section split on the homepage, which is its only
   remaining subject since /bookkeeping-setup-migration became one vertical
   flow on 2 September 2026. IT IS NOT DEAD: a class with one subject looks
   removable and this one is still in use. What stays here is only
   what is the homepage's: the asymmetric tracks, the start alignment, and the
   900 collapse below. The homepage wrap carries wrap--split alongside
   pain-split and renders identically to before the extraction.
   -------------------------------------------------------------------------- */
.pain-split {
  grid-template-columns: 0.85fr 1.15fr;
  align-items: start;
}

.point-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-md);
}

/* A label above the cards spans the grid. .point-cards is two columns, so a
   label inserted with no placement takes a card cell instead: measured
   2 September 2026, an unplaced label rendered 558px wide and grew the
   section from 565.17 to 683.16. */
.point-cards > .eyebrow { grid-column: 1 / -1; }

.point-card {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius);
  padding: var(--space-lg);
}

.point-card h3 {
  font-size: var(--step-0);
  margin-bottom: var(--space-xs);
}

.point-card p {
  margin: 0;
  font-size: var(--step--1);
  color: rgba(255, 255, 255, 0.86);
}

@media (max-width: 900px) {
  /* Columns stack, left column first, which is source order so nothing is
     reordered. 900px rather than the hero's 860px because this grid is more
     asymmetric: the narrow left column gets uncomfortable sooner. */
  .pain-split {
    grid-template-columns: 1fr;
    gap: var(--space-xl);
  }
}

@media (max-width: 640px) {
  /* Cards to a single column. 640px rather than 560px because two cards below
     that width leave roughly 200px of text measure once padding is taken off,
     and every card body wraps to four or more lines. */
  .point-cards { grid-template-columns: 1fr; }
}


/* --------------------------------------------------------------------------
   7. Content blocks: cards, grids, checklists, pull quotes
   -------------------------------------------------------------------------- */

.grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

.grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); }

/* Fixed 4-across that folds to a balanced 2x2, then 1 column.
   Used where a wrapping auto-fill grid would strand an orphan card. */
.grid--4 { grid-template-columns: repeat(4, 1fr); }

@media (max-width: 1040px) {
  .grid--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  .grid--4 { grid-template-columns: 1fr; }
}

.card {
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}

.card h3 { font-size: var(--step-1); margin-bottom: var(--space-xs); }

/* text-wrap: pretty, ADDED 27 July 2026, to stop one-word last lines in card
   descriptions (CLAUDE.md 5e). It is NOT text-wrap: balance, which is
   deliberately rejected on the CTA note further down for evening out every
   line; pretty leaves the earlier lines alone and only reworks the last few to
   avoid an orphan.

   PROGRESSIVE ENHANCEMENT, not a guarantee. Chrome/Edge 117+, Safari 26+,
   roughly 83% of traffic. FIREFOX DOES NOT SUPPORT IT at any version to date,
   and there it degrades to normal wrapping, which is exactly today's output,
   so nothing breaks and nothing regresses. That is why a case which MUST hold
   everywhere still takes a named &nbsp; instead: see the Setup & Migration
   card in the homepage services teaser. */
.card p {
  color: var(--ink-muted);
  font-size: var(--step--1);
  text-wrap: pretty;
}

.card--link:hover,
.card--link:focus-within {
  box-shadow: var(--shadow-md);
  border-color: var(--mint-deep);
  transform: translateY(-2px);
}

.card__link { text-decoration: none; }
.card__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.card--stretch { position: relative; }

.card__meta {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-bottom: var(--space-xs);
}

/* Included-items list (service template "What's included") */
.included-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.included-list > li {
  position: relative;
  padding: var(--space-md) var(--space-md) var(--space-md) 3rem;
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  margin: 0;
  box-shadow: var(--shadow-sm);
}

.included-list > li::before {
  content: "";
  position: absolute;
  left: var(--space-md);
  top: var(--space-md);
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--profit-green);
}

/* The tick is DRAWN, not typed. ::before is the green disc; ::after is a box laid
   exactly over that disc and CLIPPED TO A POLYGON, so the polygon coordinates are
   disc-local and match the measurements they came from. A rotated-border tick was
   tried first and abandoned: its two arms are necessarily perpendicular and the
   target's are not. Both content strings are empty on purpose: a character here
   would have to clear CLAUDE.md rule 11, and an empty string clears it with
   nothing to argue about. The polygon is the stroked outline of a three-point
   centreline, fitted by measurement; re-measure if the disc size ever changes. */
.included-list > li::after {
  content: "";
  position: absolute;
  left: 16px;
  top: 16px;
  width: 24px;
  height: 24px;
  background: var(--white);
  clip-path: polygon(7.422px 13.112px, 11.065px 16.877px, 16.530px 7.477px, 15.320px 6.773px, 10.785px 14.574px, 8.428px 12.138px);
}

.included-list strong {
  display: block;
  font-family: var(--font-brand);
  font-weight: 600;
  color: var(--trust-blue);
  margin-bottom: var(--space-2xs);
}

.included-list span { font-size: var(--step--1); color: var(--ink-muted); }

/* Hollow marker, on any included-list inside .additional-support. It first
   shipped on the second included-list on /monthly-bookkeeping.
   The green disc from .included-list > li::before is UNCHANGED. What changes is
   ::after, which stops being the drawn tick and becomes a smaller white circle
   laid concentrically over that disc, so what remains visible is a green ring.
   clip-path is reset to none because the base rule sets a polygon; leaving it
   would clip this circle to a tick shape. Both boxes are centred on the same
   point: disc 16px/16px at 24px square, this 20px/20px at 16px square.
   SCOPED, so the base component is untouched wherever else it is used. */
.additional-support .included-list > li::after {
  left: 20px;
  top: 20px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  clip-path: none;
}

/* THREE ACROSS THEN ONE, NEVER TWO, for included-lists inside
   .additional-support, which first shipped on /monthly-bookkeeping.
   Three items on the auto-fill base grid pass through a two-column stage on
   the way down, and three items in two columns always strand one. This is the
   shape .steps--cards uses in section 6c for exactly that reason, and 860 is
   its breakpoint rather than a number chosen here.
   WHAT DOES NOT CARRY FROM 6c: its gap, its margin-top, its card fill, border,
   radius and padding, and its counter marker. .included-list already supplies
   the equivalents, and the marker here is the ring above. Only the column
   behaviour is borrowed.
   ONE FIGURE IS WORSE HERE THAN IT IS THERE, and it is recorded rather than
   worked around: at the breakpoint three columns leave about 260px per item,
   where .steps treats 230px as workable but .included-list's own base declares
   minmax(300px, 1fr). See the measurement in this branch's report.
   SCOPED to .additional-support. The base .included-list is untouched, and so
   is the .included-core span layout above. */
.additional-support .included-list {
  grid-template-columns: repeat(3, 1fr);
}

@media (max-width: 860px) {
  .additional-support .included-list { grid-template-columns: 1fr; }
}

/* The line closing the second included-list. .included-list sets margin 0, so
   without this the sentence sits flush against the last item. The three values
   are taken from .founder__close, which is the site's only other line of text
   plus a .btn-link following a list. This is NOT that component: .founder__close
   is a flex row with a gap, and this is one sentence carrying an inline link. */
.included-list + .included-close {
  margin-top: var(--space-xl);
  font-size: var(--step--1);
  color: var(--ink-muted);
}

/* SPAN LAYOUT FOR THE MONTHLY LIST, /monthly-bookkeeping only.
   Eight items on the auto-fill base grid render 3, 3, 2 above 980 and 2, 2, 2, 2
   below it, so the last row hangs short at desktop. This is the technique
   .benefits-grid uses in section 6e for the same reason: a SIX column grid
   rather than three, items spanning 2 to give three across, and the trailing
   pair spanning 3 so it fills its row at wider cards instead of leaving a third
   of the row empty. THE COLLAPSE IS AT 860, NOT 6e's 900, AND THAT IS DELIBERATE.
   860 is this file's convention for a section-level collapse, and it is what
   section two on this page uses, so both lists change at the same width rather
   than disagreeing across a 40px band. DO NOT "CORRECT" THIS BACK TO 900 on the
   strength of 6e. The 600 below IS 6e's and is unchanged.
   Counted 24 August 2026, and recorded as a dated measurement rather than a live
   claim: of 36 media blocks in this file, 860 carried 11 and 900 carried 4, and
   of those four only .pain-split states a reason, which is specific to an
   asymmetric two-column grid and does not reach a card grid.
   The trailing pair is 7 and 8 here because eight items divide as 3 + 3 + 2.

   RENAMED FROM .monthly-core AND EXTENDED TO THE TEMPLATE, 27 August 2026. The
   scope now sits on TWO elements, /monthly-bookkeeping and _template-service.html,
   so one rule serves the page and everything built from it. The old name said which
   page rather than which role, which is what made it unusable in the template.
   CORRECTED WITH IT: this comment used to say the base .included-list was untouched
   BECAUSE it serves the template. Half of that is still true and half is not. The
   base is still untouched, and the second list on this page still relies on it,
   because .additional-support does not carry this class. What is no longer true is
   the reason: the template now carries .included-core and takes the span layout.

   THE TRAILING PAIR IS EXPLICIT nth-child, NOT POSITION FROM THE END, so this rule
   is tuned to EIGHT and degrades away from it. MEASURED 27 August 2026 in a browser
   at 1180, 985 and 885, three across, reporting the last row only:
     3 items   full row
     4 items   one card at a third width, alone
     5 items   two cards at a third width, a third of the row empty
     6 items   full row
     7 items   ONE CARD AT HALF WIDTH, alone. Odd rather than broken: it is wider
               than its neighbours, so it reads as deliberate rather than stranded.
     8 items   two cards at half width, full row. This is what the rule is for.
     9 items   one card at a third width, alone, after a row of two half-width
    10 items   two cards at a third width, a third of the row empty
   SIX AND EIGHT ARE THE CLEAN COUNTS. "Even counts pair cleanly" is FALSE as a
   general claim: ten leaves the same short row that eight was written to fix.
   Below 861 the span rules flatten to two across and none of this applies, and
   below 601 to one across. */
.included-core .included-list {
  grid-template-columns: repeat(6, 1fr);
}

.included-core .included-list > li { grid-column: span 2; }

.included-core .included-list > li:nth-child(7),
.included-core .included-list > li:nth-child(8) { grid-column: span 3; }

@media (max-width: 860px) {
  /* Two across. Eight is even, so the last row pairs without help; 6e needs a
     full-width override here only because it carries an odd fifth card. The
     nth-child rule above already resolves to span 3, so it is not restated. */
  .included-core .included-list > li { grid-column: span 3; }
}

@media (max-width: 600px) {
  /* One across. The nth-child selector outranks the child selector, so both are
     restated rather than relied on, exactly as 6e records at its own 600 block. */
  .included-core .included-list > li,
  .included-core .included-list > li:nth-child(7),
  .included-core .included-list > li:nth-child(8) { grid-column: span 6; }
}

/* THE FLAT-RATE CARD'S TEXT LINK DOES NOT BREAK MID-PHRASE, ABOVE THE BOUND.
   Measured 26 August 2026 at five layout widths, through fresh same-origin
   iframes created one per width. The link sets on ONE line at 1180, 985, 885
   and 585, and at 390 it split into a 38.52px fragment carrying "What",
   trailing the end of the sentence above it, and a 189.72px fragment on the
   next line. Those four widths were already correct. Only 390 was wrong.
   IT FITS AT 390 AND ONLY NEEDED TO STOP BREAKING. Held on one line the link
   measures 231.56px, and its right edge lands 61.44px inside the card's border
   box at 390, so nothing is being forced into a space it does not have.
   WHAT THIS INTERACTS WITH is the .btn-link { white-space: normal; }
   declaration in the 560 block earlier in this file, which is the reason the
   link wraps here rather than overflowing. This selector is 0,3,0 against that
   declaration's 0,1,0, so it wins on specificity alone. The specificity is a
   by-product of scoping to this one list and was not raised to force it, and
   the media block below is a BOUND rather than a specificity device.
   SCOPED to .included-core. CORRECTED 27 August 2026, AND THIS ONE CHANGED MEANING
   RATHER THAN JUST ITS NAME. It used to say the scope sat on ONE element so this
   rule reached nothing built from _template-service.html. THE CLASS NOW SITS ON TWO,
   and the template carries a pricing link in the same position, so THIS RULE NOW
   REACHES IT. It still reaches neither the other three .btn-link on this page nor
   the proof section's closing link, which is what the scoping was otherwise for.
   NARROWED TO ITEMS SEVEN AND EIGHT, 27 August 2026, ON CHRISTOPHER'S RULING. It
   used to read .included-core .included-list .btn-link, which reached every link in
   the list. It now names the same trailing pair the span rule above names, so the
   rule sits exactly where its 26 August measurement was taken and reaches no card
   that measurement did not cover. BOTH SEVEN AND EIGHT, not eight alone: the two are
   geometrically identical, measured 558 and 558 at 1180 and 410 and 411 at 885, so
   a link on seven would otherwise wrap where the same link on eight is held, for no
   measured reason.

   WHAT THIS RULE ACTUALLY DOES, AND IT IS LESS THAN IT LOOKS. .btn-link already
   carries white-space: nowrap in its BASE rule, so ABOVE 560 every link on the site
   is held and this rule changes nothing. The 560 block overrides that to normal.
   SO THE ONLY LIVE BAND FOR THIS RULE IS 329 TO 560, where it restores the hold for
   the trailing pair. That is precisely the band the 26 August tail measurement was
   taken in, which is why narrowing costs the page nothing.

   WHAT THIS RULE IS NOT RESPONSIBLE FOR, RECORDED BECAUSE THE PREVIOUS VERSION OF
   THIS COMMENT SAID OTHERWISE AND WAS WRONG. A six-item list built from the template
   puts its pricing link in an ORDINARY card, and a label of this page's length
   crosses that card from 861 to 920. THAT IS THE BASE RULE, NOT THIS ONE: measured
   27 August 2026 with this rule DELETED ENTIRELY, the overflow at 885 was identical
   at -12.23px. Narrowing did not fix it and was never going to. THE BAND IS STILL
   OPEN, and it is a question about .btn-link's base white-space rather than about
   this selector. The template's own pricing comment carries the budget and tells a
   builder to keep the label short or delete the anchor.

   THIS PAGE IS UNAFFECTED THROUGHOUT. Its link sits in the wide item, measured held
   on one line at 1180, 985, 885 and 390 with 277.44, 35.64, 129.94 and 46.44px of
   slack, before and after this narrowing, identically.
   THE TAIL BELOW THE BOUND WAS MEASURED, RENDERED AND RULED ON, 26 August 2026.
   Unbounded, the held line crossed the card's right border below 329, by 8.56px
   at 320. Christopher rendered 320 and 390 side by side, looked at it, and
   ruled it worth one line to remove: a flaw nobody in the modal device set
   meets is still a flaw, and "no device is 320" is an assumption that ages
   badly. An earlier version of this comment recorded that tail as accepted.
   That is superseded by his ruling rather than by a later measurement.
   THE BOUND IS THE MEASURED CROSSOVER, and it is NOT a breakpoint this file
   already uses. Every existing value is 480 or higher, and bounding at the
   nearest of them would restore the wrap at 390, 393, 402, 412, 430 and 440,
   which is the whole modal device set. Measured at 1px resolution, reproducing
   a prediction at five widths around it: the line clears the card border at 329
   with 0.44px to spare and crosses it at 328 by 0.56px. At 328 and below this
   rule is absent and the 560 block's normal governs, which is the ordinary wrap
   and is correct there.
   THE BOUND IS IN innerWidth UNITS AND THE CARD GEOMETRY IS IN LAYOUT UNITS,
   so where a classic scrollbar exists the two differ by its allowance and this
   bound fires early in layout terms. Measured both ways rather than assumed: in
   a 900px-tall frame carrying a 15px scrollbar the query flipped at layout 314,
   and under device emulation, and in a frame tall enough not to scroll, the
   allowance was 0 and it flipped at 329. On overlay scrollbars, which is every
   phone, the two are equal and the bound is 329 exactly. The general form of
   this trap, and the instruction to state the allowance a boundary was measured
   under, live in audit-protocol.md, Measurement traps, at the entry beginning
   A MEDIA QUERY TESTS innerWidth. */
@media (min-width: 329px) {
  .included-core .included-list > li:nth-child(7) .btn-link,
  .included-core .included-list > li:nth-child(8) .btn-link { white-space: nowrap; }
}

/* Numbered process steps */
.steps {
  list-style: none;
  counter-reset: step;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}

.steps > li {
  counter-increment: step;
  position: relative;
  padding-top: var(--space-xl);
  margin: 0;
}

.steps > li::before {
  content: counter(step);
  position: absolute;
  top: 0;
  left: 0;
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--trust-blue);
  color: var(--white);
  font-family: var(--font-brand);
  font-weight: 700;
}

.steps h3 { font-size: var(--step-1); margin-bottom: var(--space-2xs); }
.steps p { font-size: var(--step--1); color: var(--ink-muted); }
/* --------------------------------------------------------------------------
   6c. Homepage solution section: numbered step cards
   ADDED 27 July 2026. Additive only: no rule above modified, no token touched.

   A MODIFIER on the existing .steps, not a new component. .steps already gives
   an ordered list with CSS counters and is in use on /monthly-bookkeeping and
   the service template, so it is deliberately left alone; everything below
   only adds. Reusing it keeps the markup a real <ol> and the numbering
   automatic rather than typed into the HTML.

   The bundle's treatment differs from plain .steps in two ways: the marker is
   a rounded square sitting in the flow rather than an absolutely positioned
   circle, and each step is a Cloud Gray card with a hairline border.
   -------------------------------------------------------------------------- */
.steps--cards {
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
  margin-top: var(--space-xl);
}

.steps--cards > li {
  background: var(--cloud-gray);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--space-lg);
}

.steps--cards > li::before {
  /* .steps places this absolutely at the card's top left corner. In a padded
     card it belongs in the flow instead, above the title. */
  position: static;
  width: 30px;
  height: 30px;
  border-radius: var(--radius);
  font-size: var(--step--1);
  margin-bottom: var(--space-md);
}

@media (max-width: 860px) {
  /* Three to ONE, not three to two. Three cards in a two-column grid always
     strand one on a second row, which the first-build review already flagged
     as a defect elsewhere on the site. 860px matches the hero's breakpoint,
     and at that width three columns still leave about 254px per card, which
     is above the 230px minimum the existing .steps grid treats as workable. */
  .steps--cards { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   6d. Homepage founder section
   ADDED 27 July 2026. Additive only: no rule above modified, no token touched.

   Placed AFTER .grid, .grid--3 and .card so the overrides below actually win.
   The previous section learned that the hard way: equal specificity means
   source order decides, and a block placed too early is silently ignored.

   NOT TAKEN FROM THE BUNDLE, deliberately: its four-stop dated timeline rail,
   reading 1998-2013 / 2013-2018 / 2018-2023 / 2023-today. Every one of those
   dates is wrong, it is the retired four-node arc, and CLAUDE.md rule 5 item 7
   bars any page from implying the Federal Reserve years run up to the founding
   of Cilson Bookkeeping. A left-to-right dated rail asserts exactly that. So:
   no rail, no dots, no connecting line, no dates. The bundle's fourth card, a
   Financial Analyst chapter, is also not taken; the homepage carries three.
   -------------------------------------------------------------------------- */
.founder {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.founder__header {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2xl);
  align-items: end;
  margin-bottom: var(--space-xl);
}

/* Quote moves up beside the headline, replacing the mint .pull-quote box that
   used to sit below the cards. The .pull-quote rule is deliberately left in
   main.css and simply unused here, since the article template still needs it. */
.founder__quote {
  margin: 0;
  border-left: 3px solid var(--guidance-gold);
  padding-left: var(--space-lg);
}

.founder__quote p {
  margin: 0;
  font-family: var(--font-brand);
  font-weight: 500;
  font-size: var(--step-2);
  line-height: 1.45;
  color: var(--ink);
}

.founder__quote cite {
  display: block;
  margin-top: var(--space-sm);
  font-style: normal;
  font-weight: 500;
  font-size: var(--step--1);
  color: var(--ink-muted);
}

/* Specificity note: .card p sets the muted colour and small size, so the
   takeaway is scoped as .card .founder-card__takeaway to outrank it. */
.card .founder-card__takeaway {
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px dashed var(--rule);
  font-size: var(--step-0);
  font-weight: 600;
  color: var(--ink);
}

.founder-card__arrow {
  color: var(--profit-green);
  font-weight: 700;
  margin-right: var(--space-2xs);
}

.founder__close {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm) var(--space-lg);
  margin-top: var(--space-xl);
  font-size: var(--step--1);
  color: var(--ink-muted);
}

.founder__tagline { font-style: italic; }

@media (max-width: 860px) {
  /* Header stacks headline first, quote second, which is source order.
     align-items returns to start so the quote does not float oddly once the
     columns are no longer side by side.
     A .founder__cards declaration sat here until 27 July 2026. It became
     unreachable when the four-card revision added later blocks at 1000px and
     560px, and was deleted rather than left as dead weight. Card behaviour is
     defined entirely in the section below. */
  .founder__header {
    grid-template-columns: 1fr;
    gap: var(--space-lg);
    align-items: start;
  }
}

/* Founder section, revised 27 July 2026: a fourth chapter card.
   Later overrides rather than edits to the rules above, which is how the
   27 July revision was written. CORRECTED 21 August 2026: the clause here
   claiming this file is additive with zero deletions was true when written and
   is not now. The three-column declaration those overrides had made unreachable
   was removed on that date, together with five others elsewhere in the file. */

/* Kicker above each card title. These are load-bearing, not decoration: they
   frame the four cards as facets rather than stages, which is what stops four
   cards reading as a career timeline. See the brief's founder-story bullet.
   Monospace and letterspaced per the bundle. Colour is --ink-muted rather than
   the bundle's #9aa4b0, so no non-brand grey enters the palette. */
.founder-card__kicker {
  margin: 0 0 var(--space-xs);
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* FOUR cards. Four divides evenly into two columns, so unlike the three-card
   rows elsewhere on this page it can take an intermediate two-column step
   without stranding a card alone on a second row. Hence four to two to one
   here, against three to one for the solution section. */
.founder__cards { grid-template-columns: repeat(4, 1fr); }

/* Italic line and the About link sit adjacent rather than at opposite ends.
   This is the only justify-content on .founder__close: the space-between it
   overrode was removed from the base rule on 21 August 2026. */
.founder__close { justify-content: flex-start; }

@media (max-width: 1000px) {
  /* Four columns leave about 220px per card below this width, which is under
     the 230px the rest of the file treats as the workable minimum.
     Effective behaviour: 4 columns above 1000px, 2 columns from 561 to 1000,
     1 column at 560 and below. The 860px block above no longer touches
     .founder__cards; its declaration there was unreachable and was removed on
     27 July 2026, so card widths are defined only here. */
  .founder__cards { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .founder__cards { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   6e. Homepage benefits section: numbered tiles on the mint band
   ADDED 27 July 2026. Additive only, no token declared or changed.
   Placed after .card so the border override below wins.

   FIVE cards in a 3 + 2 layout, done on a SIX column grid rather than three.
   Three columns would leave the last two cards at one third width each,
   stranded against a third of empty space. On six columns the first three
   span 2 and the last two span 3, so the pair fills its row at wider cards
   instead of hanging. The bundle does the same thing through a per-card span
   value; this expresses it in CSS so the markup stays plain.
   -------------------------------------------------------------------------- */
.benefits-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--space-md);
  margin-top: var(--space-xl);
}

.benefits-grid > * { grid-column: span 2; }

.benefits-grid > :nth-child(4),
.benefits-grid > :nth-child(5) { grid-column: span 3; }

/* Mint-tinted border rather than the default grey rule: on a mint band a
   neutral border reads as a seam. --mint-deep is an existing token. */
.benefits-grid .card { border-color: var(--mint-deep); }

/* Numbered tile: mint square, Trust Blue numeral, matching the bundle. */
.benefit-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin-bottom: var(--space-md);
  border-radius: var(--radius);
  background: var(--fresh-mint);
  font-family: var(--font-brand);
  font-weight: 700;
  font-size: var(--step--1);
  color: var(--trust-blue);
}

@media (max-width: 900px) {
  /* Two per row, and the fifth spans the full width rather than sitting at
     half width beside an empty half. */
  .benefits-grid > * { grid-column: span 3; }
  .benefits-grid > :nth-child(5) { grid-column: span 6; }
}

@media (max-width: 600px) {
  /* One per row. The nth-child rules above outrank the universal child
     selector, so both are restated here rather than relying on it. */
  .benefits-grid > *,
  .benefits-grid > :nth-child(4),
  .benefits-grid > :nth-child(5) { grid-column: span 6; }
}


/* --------------------------------------------------------------------------
   6f. Homepage FAQ section: intro above a native disclosure list
   ADDED 27 July 2026. Additive only, no token declared or changed.

   CORRECTED 24 August 2026, and the old wording is kept in this paragraph
   rather than deleted. This heading read "split intro", and the paragraph
   below it said the layout was the design bundle's, intro column left and
   questions right at the bundle's .8fr / 1.2fr ratio, and that the ratio was
   the point of the split because the questions needed the wider column. Every
   word of that was true while the container was a grid. IT IS NOT NOW. The
   intro and the list stack, and the ratio, the two columns and the 860px
   stacking point are all gone. NARROWED LATER THE SAME DAY: they stack inside
   one measure rather than at full width, see the block comment on .faq below.
   What is unchanged is
   the rest of the sentence: the bundle's hand-tuned px type and hex colours
   are NOT carried over, and those still come from the tokens above.

   Expand/collapse is native <details>, no JavaScript. It is keyboard
   accessible and screen-reader announced with no ARIA, it works with JS
   disabled, and every answer stays in the HTML.

   The +/x marker is a CSS pseudo-element rather than a <span> in the markup,
   so the summary carries only the question text and a screen reader reads the
   question rather than a stray plus sign. It rotates 45 degrees on open,
   turning + into x, which shows the state change without swapping in a dash
   character (house style, CLAUDE.md 5e) and without a script.

   BACKGROUND: WHITE, and there is deliberately no background declared below.
   Changed 27 July 2026 to match the design bundle, where the FAQ sits on
   white and the SERVICES section beneath it carries the grey band. The port
   had these two inverted. Nothing is needed here to make it white: .section
   declares no background and body is already --white, so the fix was removing
   section--cloud from the markup rather than overriding it here. Do not
   re-add section--cloud to this section, and do not add a background rule
   below; either would put the grey back.

   OPEN, flagged 27 July 2026, NOT fixed here: the services teaser below is
   still white, so with the FAQ now also white the two run together with no
   visible boundary. The bundle avoids this by making services grey with a
   1px top rule. Fixing that means editing the services section, which was
   out of scope for this change. See the PR discussion.
   -------------------------------------------------------------------------- */
/* SINGLE COLUMN since 24 August 2026. The column geometry is gone: display,
   grid-template-columns and align-items are deleted, not overridden, because
   no other page uses this selector. What survives is the separation the grid
   supplied as gap, re-homed onto the child boundary at the same value, so the
   space above .faq__list's border-top is deliberate rather than incidental.

   ONE MEASURE FOR THE WHOLE BLOCK, added 24 August 2026 on the same branch,
   after the single column made a defect visible that the old 655px column had
   hidden. .faq__a carries max-width 44em, which computed to 748px against a
   1132px content box, so the answers stopped 384px short of the rules above
   them and the block read ragged. The measure goes on the CONTAINER rather
   than the cap coming off the answers: long-form line length is what that cap
   is for and it stays.

   THE VALUE IS THE TOKEN, NOT THE CAP. Measured at layout width 1280: the
   answer cap computes to 748px and --wrap-narrow is 720px, so the cap is 3.89%
   wider. Inside 5% a token beats a bespoke number, so this is --wrap-narrow.

   IT WINS OVER .wrap BY SOURCE ORDER, NOT BY SPECIFICITY, and that is the one
   fragile thing here. Both selectors are a single class, so they tie, and .faq
   wins only because it is declared later in this file. Moving either rule past
   the other silently restores the 1180px measure.

   A CONSEQUENCE WORTH KNOWING BEFORE EDITING .faq__a: at 720px the container's
   content box is 672px, so the 748px cap on the answers can no longer bind at
   any viewport. It is redundant rather than wrong, and it is deliberately NOT
   deleted here. */
.faq { max-width: var(--wrap-narrow); }

/* CORRECTED 24 August 2026, same day it was written. This comment said .faq
   has exactly two children, the intro and the list, so the rule was the
   intro-to-list spacing and nothing else. THE NOTE AND ITS CONTACT LINK CAME
   OUT OF .faq__list on the same branch, because inside the list they inherited
   its row rhythm and read as a sixth question. There are now FOUR children:
   the intro, the list, the note, and the link. This rule spaces every adjacent
   pair, and what it produces between the note and the link is measured on the
   preview rather than assumed, because .btn-link declares no display and is
   inline wherever its parent is not a flex container. */
.faq > * + * { margin-top: var(--space-xl); }

.faq__intro h2 { margin-top: 0; }

.faq__list { border-top: 1px solid var(--rule); }

.faq__item {
  border-bottom: 1px solid var(--rule);
  padding-block: var(--space-md);
}

.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  cursor: pointer;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-1);
  line-height: 1.3;
  color: var(--trust-blue);
  list-style: none;
}

/* Without both of these the native triangle sits beside the custom marker. */
.faq__q::-webkit-details-marker { display: none; }

.faq__q::after {
  content: "+";
  flex: none;
  font-weight: 700;
  font-size: var(--step-2);
  line-height: 1;
  color: var(--profit-green-dark);
  transition: transform 0.2s ease;
}

.faq__item[open] .faq__q::after { transform: rotate(45deg); }

/* The summary IS the control, so its focus ring has to survive. */
.faq__q:focus-visible {
  outline: 2px solid var(--trust-blue);
  outline-offset: 3px;
}

.faq__a {
  margin: var(--space-sm) 0 var(--space-2xs);
  max-width: 44em;
  color: var(--ink-muted);
  line-height: 1.7;
}

/* The contact link matches the proof section link, 5 September 2026. It set
   14px directly beneath a 20px lead and 20px summaries, which read as the
   smallest thing in the block rather than as its closing action. Christopher
   ruled that both FAQ links take the proof link treatment. The selector has
   exactly two subjects, the homepage and the catch-up and cleanup page.
   CORRECTED 16 September 2026: it reaches those two pages and three links. The
   third is the pricing link inside the catch-up and cleanup page's first
   answer, which takes the same size as the answer text around it. */
.faq .btn-link { font-size: var(--step-0); }


/* --------------------------------------------------------------------------
   6g. Section head: heading left, link right, on one row
   ADDED 27 July 2026. Additive only, no token declared or changed.

   The bundle's services band puts the H2 and an "All services" link on a
   single row, rather than the heading on top and a button below the cards.

   ONLY that header row is new. The cards themselves are the existing .card /
   .card--link / .card--stretch component and .grid--4, which already folds
   four across to a 2x2 and then to one column. The bundle's card is that same
   card (white, 1px rule, small radius, whole-card click), so rebuilding it
   here would have duplicated a component that already matched. The bundle's
   separate "Details" link is deliberately not built: .card__link::after
   already stretches the title's hit area over the whole card, so a second
   link would be two links to one destination and one extra tab stop per card.

   align-items: end sets the link on the heading's baseline rather than its box
   centre, which is what stops it floating high beside a two-line heading.
   flex-wrap lets it drop below the heading on narrow screens instead of
   crushing the heading, so no media query is needed.
   -------------------------------------------------------------------------- */
.section-head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: var(--space-lg);
  flex-wrap: wrap;
  margin-bottom: var(--space-xl);
}

.section-head h2 { margin: 0; }

.section-head__link {
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-0);
  color: var(--trust-blue);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
  white-space: nowrap;
  transition: color 0.18s ease, border-color 0.18s ease;
}

.section-head__link:hover,
.section-head__link:focus-visible {
  color: var(--profit-green-dark);
  border-bottom-color: var(--profit-green);
}


/* --------------------------------------------------------------------------
   6h. Homepage resources teaser: four cards, eyebrow over a linked title
   ADDED 27 July 2026. Additive only, no token declared or changed.

   Almost nothing is new. The header row is .section-head from 6g, which
   already puts a heading left and a link right; the only addition is a
   wrapper so the heading can carry a sub-line beneath it and still sit on the
   link's baseline. The cards are the existing .article-card, whose border,
   radius, shadow, hover and stretched-link ::after already match the bundle's
   card exactly, and the eyebrow is the existing .eyebrow.

   THE BUNDLE'S IMAGE SLOT IS DELIBERATELY NOT BUILT, so no CSS for it exists
   here. Resource articles default to NO image (brief, 26 July 2026). The
   bundle's card leads with a 130px image band; building it would mean either
   a placeholder, which the image rule exists to prevent, or an empty box.
   The hub's featured lead slot is the one place an image is required, and
   that is why the news post leads there rather than appearing here.

   Four across rather than the bundle's three: these cards carry a title and
   an eyebrow only, no description or image, so they are much shorter than the
   bundle's and three would leave the row looking under-filled. Folds to 2x2
   then one column on the same breakpoints as .grid--4, so the two card grids
   on the page fold together rather than at different widths.
   -------------------------------------------------------------------------- */
.section-head__group { min-width: 0; }

/* The sub-line sits tight under the heading, not at full paragraph spacing. */
.section-head__group .section__lead { margin: var(--space-xs) 0 0; }

/* Doubled class on purpose, do not simplify to .article-grid--4. The base
   .article-grid rule lives in section 9 and sets an auto-fill track list.
   That is LATER in the file than this block, so at equal specificity it wins
   and the grid silently falls back to three columns. Measured, not guessed.
   Two classes outrank one, so this holds regardless of source order. */
.article-grid.article-grid--4 { grid-template-columns: repeat(4, 1fr); }

@media (max-width: 1040px) {
  .article-grid.article-grid--4 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .article-grid.article-grid--4 { grid-template-columns: 1fr; }
}

/* Teaser cards end at the heading. Without this the h3's trailing margin
   leaves slack under the last line, which reads as a missing description. */
.article-card h3:last-child { margin-bottom: 0; }

/* EVERY card eyebrow reserves two lines, whether it needs them or not.
   "Monthly Bookkeeping & Reporting" wraps to two lines while the other three
   categories fit on one, so that card ran taller, grid stretch matched the
   rest to it, and the three short cards carried 25px of dead space below
   their titles. Reserving the taller case starts every title at the same
   vertical position, which is what actually makes the row look even.

   Measured, not eyeballed: 14px type at the inherited 1.7 line-height is a
   23.8px line box, so two lines is 47.6px.

   The calc() is the fallback and hard-codes that 1.7. The lh unit means "two
   line boxes of this element" whatever the line-height is, so it stays
   correct if the body line-height ever changes; it is behind @supports
   because a browser without lh must not be handed an invalid value and drop
   the reservation entirely. */
.article-card .eyebrow { min-height: calc(2 * 1.7em); }

@supports (min-height: 2lh) {
  .article-card .eyebrow { min-height: 2lh; }
}


/* --------------------------------------------------------------------------
   8. Article template
   -------------------------------------------------------------------------- */

.article-header {
  background: var(--cloud-gray);
  padding-block: var(--space-xl) var(--space-2xl);
  border-bottom: 1px solid var(--rule);
}

.breadcrumb {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-bottom: var(--space-md);
}
.breadcrumb ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  padding: 0;
  margin: 0;
}
.breadcrumb li { margin: 0; }
.breadcrumb li + li::before { content: "/"; margin-right: var(--space-xs); color: var(--rule); }
.breadcrumb a { color: var(--trust-blue); text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }

.article-header__title { font-size: var(--step-4); margin-bottom: var(--space-md); }

.standfirst {
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--ink-muted);
  max-width: 62ch;
  margin-bottom: var(--space-lg);
}

/* Byline: published date only, never "updated" (brief §Resource-article notes) */
.byline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--step--1);
  color: var(--ink-muted);
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule);
}

.byline__author {
  font-family: var(--font-brand);
  font-weight: 600;
  color: var(--trust-blue);
}

.byline__sep { color: var(--rule); }

.article-figure { margin: 0 0 var(--space-xl); }
.article-figure img { border-radius: var(--radius-lg); width: 100%; }
.article-figure figcaption {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-top: var(--space-sm);
}

.prose { max-width: var(--wrap-narrow); text-wrap: pretty; }

/* Proof section: one client outcome on the mint band. FOUR RULES, and the two
   that carry width and quote treatment are here because nothing existing fits.

   .pull-quote WAS THE OBVIOUS REUSE AND IT DOES NOT FIT, measured rather than
   assumed: it fills with var(--fresh-mint), which is the same value
   .section--mint paints, so its box would be invisible on this band and only
   its border would read. Worse, it would reappear as a box the moment anyone
   moved this section to a white band.

   .proof__quote MIRRORS .founder__quote's approach, a rule rather than a fill,
   because that is the treatment that survives a tinted band. IT IS A SECOND
   HOME FOR THAT IDEA AND IS NAMED FOR ITS SECTION ON PURPOSE, which is this
   file's practice on first use, the same way .pain-split was. IF A THIRD
   QUOTE-ON-A-TINTED-BAND APPEARS, extract the shared half the way .wrap--split
   was extracted from .pain-split rather than adding a third copy.

   The green rule on mint is not a new pairing: .pull-quote already ships that
   exact border colour on that exact fill in the article template. */
.proof__body { max-width: var(--wrap-narrow); }

.proof__quote {
  margin: var(--space-lg) 0 var(--space-xl);
  border-left: 3px solid var(--profit-green);
  padding-left: var(--space-lg);
}

.proof__quote p {
  margin: 0;
  font-family: var(--font-brand);
  font-weight: 500;
  font-size: var(--step-1);
  line-height: 1.45;
  color: var(--trust-blue);
}

.proof__quote cite {
  display: block;
  margin-top: var(--space-sm);
  font-style: normal;
  font-weight: 500;
  font-size: var(--step--1);
  color: var(--ink-muted);
}

/* THE CLOSING LINK, and this override exists because of the BAND rather than
   because of the section. .btn-link is tuned for white and cloud: its underline
   is var(--rule), a cool grey that reads against those two and very nearly
   vanishes on mint. THIS IS THE SITE'S FIRST .btn-link ON A TINTED BAND, so the
   question had not come up before. Every other one sits on white, on cloud, or
   on a dark band where .cta-band .btn-link already overrides it.

   THE SIZE. .btn-link is --step--1, which made the closing element the smallest
   type in a section whose body is --step-0. It takes --step-0 here so the link
   matches the paragraphs it follows.

   THE COLOUR IS A LITERAL AND THERE IS NO TOKEN FOR IT. Half-strength Trust
   Blue composites on mint to roughly rgb(124,161,175), which measures 2.36
   against the band. It is DECORATION rather than text, which rule 8 permits,
   and it is deliberately the same weight as the quote's own green rule at 2.34
   so the two read as a pair. The link TEXT is unaffected and stays at 7.37.

   THE SEPARATION ABOVE IT LIVES ON THE QUOTE, NOT ON THIS LINK, and that is the
   part worth knowing before anyone changes it. The gap was 24px because the
   blockquote's own margin-bottom is 24px while the paragraph's margin-top is 0.
   Putting a margin-top on the paragraph therefore does NOTHING: there is no
   margin to win a collapse against, the blockquote's is simply the only one
   there. The quote's margin-bottom is what moved, to var(--space-xl).

   THE HOVER PAIR BELOW EXISTS BECAUSE THE RULE ABOVE OVERRODE THE CLASS'S
   OWN HOVER. .btn-link:hover and .btn-link:focus-visible both set the border
   colour, and both carry the same 0,2,0 specificity as .proof .btn-link, so
   at equal weight SOURCE ORDER decides and this block, being later in the
   file, silently won. Read from the CSSOM rather than from the cascade: on
   this one link the underline colour changed on neither hover nor keyboard
   focus. The text colour and the focus outline were never affected, so it
   was lost polish rather than an accessibility failure, but it is the kind
   of loss nothing downstream reports.

   --profit-green IS DELIBERATELY NOT THE HOVER COLOUR HERE, and it is what
   the class uses everywhere else. On mint it measures 2.34 against a resting
   rule of 2.36, so hover would make the underline very slightly FAINTER than
   at rest. --profit-green-dark measures 4.90 on the same band, so the change
   reads as a strengthening, which is what a hover affordance is for. */
.proof .btn-link {
  font-size: var(--step-0);
  border-bottom-color: rgba(31, 78, 121, 0.5);
}

.proof .btn-link:hover,
.proof .btn-link:focus-visible {
  border-bottom-color: var(--profit-green-dark);
}
.prose > h2 { margin-top: var(--space-xl); }
.prose > h3 { margin-top: var(--space-lg); font-size: var(--step-1); }
.prose > ul, .prose > ol { margin-bottom: var(--space-lg); }
.prose li { margin-bottom: var(--space-sm); }

.pull-quote {
  margin: var(--space-xl) 0;
  padding: var(--space-lg) var(--space-lg) var(--space-lg) var(--space-xl);
  border-left: 4px solid var(--profit-green);
  background: var(--fresh-mint);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
}

.pull-quote p {
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-2);
  line-height: 1.35;
  color: var(--trust-blue);
  margin: 0;
}

.pull-quote cite {
  display: block;
  margin-top: var(--space-sm);
  font-family: var(--font-body);
  font-style: normal;
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--ink-muted);
}

/* Optional checklist module (omit for pure-narrative articles) */
.checklist {
  margin: var(--space-xl) 0;
  padding: var(--space-lg);
  background: var(--white);
  border: 1px solid var(--mint-deep);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.checklist h2, .checklist h3 {
  font-size: var(--step-1);
  margin-top: 0;
  margin-bottom: var(--space-md);
}

.checklist ul { list-style: none; padding: 0; margin: 0; }

.checklist li {
  position: relative;
  padding-left: 2rem;
  margin-bottom: var(--space-sm);
}

.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.35em;
  width: 1.05rem;
  height: 1.05rem;
  border: 2px solid var(--profit-green);
  border-radius: 3px;
  background: var(--white);
}

.checklist li::after {
  content: "";
  position: absolute;
  left: 0.32rem;
  top: 0.5em;
  width: 0.35rem;
  height: 0.65rem;
  border-right: 2px solid var(--profit-green);
  border-bottom: 2px solid var(--profit-green);
  transform: rotate(45deg);
}

/* Related services: per-article, not a fixed default pair */
.related-services { margin-top: var(--space-2xl); }

.related-services h2 { font-size: var(--step-2); }

/* Prev/next article nav */
.post-nav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg);
  margin-top: var(--space-2xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--rule);
}

.post-nav__item {
  display: block;
  padding: var(--space-md);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  text-decoration: none;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.post-nav__item:hover { border-color: var(--mint-deep); background: var(--cloud-gray); }

.post-nav__item--next { text-align: right; }

.post-nav__label {
  display: block;
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-bottom: var(--space-2xs);
}

.post-nav__title {
  font-family: var(--font-brand);
  font-weight: 600;
  color: var(--trust-blue);
  line-height: 1.3;
}

@media (max-width: 640px) {
  .post-nav { grid-template-columns: 1fr; }
  .post-nav__item--next { text-align: left; }
}

/* Contents control, resource articles (main.js section 9). It sits at the end
   of the article-template section because it is a control over the article,
   which is the same reasoning that puts .filter-return with the filter
   components rather than at the end of the file.
   THE SECOND position: fixed RULE IN THIS STYLESHEET. It shares the
   precedent's z-index band: 150 sits above the sticky header at 100 and below
   the skip link at 200, so a keyboard user who tabs to the skip link still
   sees it over this.
   TOP 101px IS DERIVED, NOT CHOSEN. The header renders 77px and is sticky, so
   it never moves. A 24px offset was measured and rejected: every candidate
   size overlapped the header by 53px at every scroll position, and at z-index
   150 the control covers the header rather than tucking under it. 101 is 77
   plus the same 24px gap the right offset uses.
   NO TRANSITION AND NO TRANSFORM, so this rule needs no prefers-reduced-motion
   branch of its own. That is the precedent's reasoning, and the mark below
   carries no transition either.
   The hidden state is display: none, which also takes the control out of the
   tab order instead of leaving a focusable invisible box. */
.contents-toggle {
  position: fixed;
  top: 101px;
  right: 24px;
  z-index: 150;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: var(--trust-blue);
  border: 1px solid var(--trust-blue);
  border-radius: var(--radius);
  color: var(--white);
  cursor: pointer;
  box-shadow: var(--shadow-md);
}

.contents-toggle:hover { box-shadow: var(--shadow-lg); }

.contents-toggle.is-hidden { display: none; }

/* The mark on the contents control. It is DRAWN, not a character, and nothing
   here puts a codepoint into rendered text, so CLAUDE.md rule 11 is not
   engaged. That is the route both prior characters took: U+2713 and U+25BE
   were each put to Christopher under rule 11 and each was replaced with a
   drawn shape rather than approved.

   IT IS BULLETS AND LINES RATHER THAN THREE EQUAL BARS, ruled by Christopher
   on 27 August 2026 after comparing six rendered candidates. Three bars is the
   nav toggle's mark and read as a menu; a list mark is what tells the two
   apart at a glance, which matters because at mobile widths both controls are
   on screen at once.

   THE TECHNIQUE IS SIX BACKGROUND LAYERS ON ONE ELEMENT, not the element plus
   two pseudo-elements this rule used before. THE REASON IS A COUNT: bullets
   and lines needs SIX shapes and an element plus its two pseudo-elements
   offers THREE. Child elements would give six, and they are unavailable here:
   the span is built by main.js and this branch's scope is the stylesheet and
   the one article, so the markup cannot grow a child. Six layers on the one
   box is the only form that fits, and it is the technique .media-placeholder
   already uses to draw with a gradient rather than an image.

   EVERY LAYER IS currentColor, so the mark follows the button's own colour and
   no value is hard-coded. Dots are radial gradients with a hard stop, which is
   what makes them round; lines are the idiomatic solid linear-gradient.
   GEOMETRY LIVES IN THE TWO LISTS BELOW rather than in six rules, so the
   values Christopher approved are legible in one place: 3px dots, a 4px gap,
   14px lines 2px high, and 4px between rows. */
.contents-toggle__bars {
  display: block;
  width: 21px;
  height: 17px;
  background-repeat: no-repeat;
  background-image:
    radial-gradient(circle closest-side, currentColor 100%, transparent 100%),
    radial-gradient(circle closest-side, currentColor 100%, transparent 100%),
    radial-gradient(circle closest-side, currentColor 100%, transparent 100%),
    linear-gradient(currentColor 0 0),
    linear-gradient(currentColor 0 0),
    linear-gradient(currentColor 0 0);
  background-size:
    3px 3px, 3px 3px, 3px 3px,
    14px 2px, 14px 2px, 14px 2px;
  background-position:
    0 0, 0 7px, 0 14px,
    7px 0.5px, 7px 7.5px, 7px 14.5px;
}


/* The contents panel. Fixed rather than absolute, because the control it
   belongs to is fixed and the two have to travel together.
   TOP 153px IS THE CONTROL'S OWN GEOMETRY: 101 plus the control's 44 plus an
   8px gap. WIDTH IS 300px BY MEASUREMENT: at 260 three of this article's seven
   entries wrap to three lines, and at 340 the panel exceeds the width
   available at layout 360, which is 312.
   THE max-height AND overflow PAIR IS THE MOBILE NAV'S, which is the one
   existing panel on this site that can outgrow its viewport. It is sized
   against this panel's own top offset rather than against the header, because
   that is where this panel starts. Seven entries do not reach it; an article
   with more headings would.
   THE ENTRIES ARE TRUST BLUE AT WEIGHT 500 WITH A HAIRLINE RULE BETWEEN THEM,
   ruled by Christopher on 27 August 2026 after comparing six treatments
   rendered side by side on this page.
   THE COLOUR IS WHAT MAKES THEM READ AS LINKS rather than as prose. At body
   ink and weight 400 seven two-line entries set as a paragraph, which is the
   defect this fixes; the font is unchanged.
   THE RULE WAS CHOSEN OVER VERTICAL SPACE, and the reason is the height. A
   rule adds 6px to the panel and 6px of space between items adds 36, against a
   max-height this panel can actually reach: seven entries do not, and an
   article with more headings would, so the cheaper separator is the one that
   keeps that headroom. */
.contents-panel {
  position: fixed;
  top: 153px;
  right: 24px;
  z-index: 150;
  width: 300px;
  max-width: calc(100vw - 48px);
  max-height: calc(100vh - 177px);
  overflow-y: auto;
  padding: var(--space-xs);
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  display: none;
}
.contents-panel[data-open="true"] { display: block; }

.contents-panel__list { list-style: none; margin: 0; padding: 0; }
.contents-panel__list > li { margin: 0; }
.contents-panel__list > li + li { border-top: 1px solid var(--rule); }

.contents-panel__list a {
  display: block;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--trust-blue);
  text-decoration: none;
}
.contents-panel__list a:hover,
.contents-panel__list a:focus-visible {
  background: var(--fresh-mint);
  color: var(--trust-blue);
}


/* --------------------------------------------------------------------------
   8a. Feature article template
   ADDED 15 September 2026, for pages built from
   _templates/_template-feature-article.html. The brief's "Featured articles:
   two tracks" owns what that template carries. Its bands, header, FAQ, cards
   and closing band reuse the rules above and in sections 3, 6f, 7 and 10;
   only the Key insight callout and the author section are new.
   CORRECTED 15 September 2026, on PR 259: the band and closing band spacing
   rules below are new as well.
   -------------------------------------------------------------------------- */

/* Key insight: one statement, centred on the mint band, labelled with the
   site's eyebrow. A callout rather than a section, so it has no heading. */
.insight { text-align: center; }

.insight__text {
  max-width: var(--wrap-narrow);
  margin: 0 auto;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-2);
  line-height: 1.4;
  color: var(--trust-blue);
  text-wrap: pretty;
}

/* The FAQ intro also carries .prose, so its heading joins the contents panel.
   .prose > h2 would add a section's top margin inside the intro; this restores
   the intro's own zero margin. */
.faq__intro.prose > h2 { margin-top: 0; }

/* BAND SPACING. .prose > h2 gives a heading a section's top margin, which is
   right where an article's headings share one column. Here each heading opens
   its own band, so that margin stacked on the band's padding. .prose--band
   sits only on this template's band wrappers and puts the opening heading at
   the band's own padding, as every other built page places a band's first
   heading. It outranks .prose > h2 on specificity (CLAUDE.md rule 13). */
.prose--band > h2:first-child { margin-top: 0; }

/* THE SAME AT THE BOTTOM, ADDED 15 September 2026. p:last-child already
   closes a band that ends on a paragraph at the band's padding. A band that
   ends on a list or the optional figure kept that element's bottom margin
   inside the padding, and a list's last item carries a margin of its own that
   passes through the list's edge, so both are taken away. Same scope and
   specificity reasoning as the rule above (CLAUDE.md rule 13). */
.prose--band > :last-child,
.prose--band > :last-child > li:last-child { margin-bottom: 0; }

/* THE CLOSING BAND. The closing block is the resource template's, unchanged,
   and carries its own .section--tight. This template also wraps it in a full
   .section, so the two top paddings stacked. .article-closing sits only on
   that wrapper and gives up its top padding, so the tight section alone sets
   the card's distance from the band above, as a tight closing band does
   elsewhere. The bottom padding is untouched. Doubled so it outranks .section
   on specificity (CLAUDE.md rule 13). */
.section.article-closing { padding-top: 0; }

/* THE COMPARISON VENN, for /bookkeeping-accounting-whats-the-difference.
   Two overlapping circles behind two columns of text, which is what the live
   page draws with the page builder's shapes. Drawn here instead: no asset, no
   SVG file, and the same technique the site already uses for its caret, tick
   and card arrow.

   THE FILLS ARE THE SITE'S OWN COLOURS at the live page's alpha. The live blue
   IS --trust-blue and the live green is --profit-green to within one value per
   channel, so the tokens are used rather than the live literals. A translucent
   fill is a tile background, which CLAUDE.md rule 8 permits; that rule bars
   green TEXT.

   GEOMETRY. Each circle is 62% of the container wide, pinned to its own side,
   so they overlap by 24%. Each text column is 46% wide and sits strictly
   inside its own circle's span. The container's aspect-ratio makes it as tall
   as a circle, and taller if the text needs it, so nothing is clipped.

   THE CIRCLES STOP AT 1000px, where the columns stack, and the band then reads
   as two labelled lists, which is what the live page gives phones. MEASURED 15
   September 2026 on the demo, at sixteen widths: with the heading inside the
   circles, as the live page has it, the content stops fitting below a layout
   width of about 1000, so the circles stop there. 1000 is a breakpoint this
   stylesheet already uses, and it keeps the circles on a 1024px laptop. The
   live page changes its shapes at a different width from its text, which is
   why a part-width desktop window there shows text outside the circle.

   The circles are pseudo-elements, so they are decoration and reach no
   accessibility tree. */
.venn {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto auto;
  align-content: center;
  justify-items: center;
  row-gap: var(--space-lg);
  column-gap: 6%;
  padding-inline: 16%;
  aspect-ratio: 100 / 62;
}

/* The heading sits on the circles, as the live page has it, spanning both. */
.venn__heading { grid-column: 1 / -1; text-align: center; }
.venn__heading h2 { margin-bottom: 0; }

.venn::before,
.venn::after {
  content: "";
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  width: 64%;
  aspect-ratio: 1;
  border-radius: 50%;
}

.venn::before { left: 0; z-index: 0; background: color-mix(in srgb, var(--trust-blue) 33%, transparent); }
.venn::after { right: 0; z-index: 0; background: color-mix(in srgb, var(--profit-green) 47%, transparent); }

/* The text sits above the circles. The green circle is the container's
   ::after, so it is painted AFTER the columns in DOM order and dimmed the
   right column until both were given a stacking order. MEASURED 15 September
   2026 on the demo. */
.venn > * { position: relative; z-index: 1; }

.venn__col > h3 {
  margin-top: 0;
  text-align: center;
  text-decoration: underline;
  text-underline-offset: 6px;
}

.venn__col p { margin-bottom: var(--space-md); }
.venn__col p:last-child { margin-bottom: 0; }
.venn__label { display: block; font-weight: 600; }

/* THE BREAKPOINT IS 1100, AND THE MEASURED BOUNDARY IS 1052. The columns set
   at body size, as the live page does, and the widest they can be while still
   sitting inside their circles is what fixes this number. Swept at 1px on the
   branch demo: the columns first fit at layout width 1052, with 0.34px to
   spare, and the clearance grows about 0.24px per pixel of width. 0.34px is
   not a margin, so the breakpoint is set at 1100 for 11.11px of it. Below
   that the circles come off and the columns stack. */
@media (max-width: 1099px) {
  /* One column, no circles: see the note above. */
  .venn {
    grid-template-columns: 1fr;
    aspect-ratio: auto;
    gap: var(--space-xl);
  }

  .venn::before,
  .venn::after { content: none; }
}

/* Author section: in the article's reading column, photo beside the text,
   stacking under 640px. */
.author__inner {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-xl);
  align-items: start;
}

.author__photo {
  width: 160px;
  height: 160px;
  border-radius: 50%;
  object-fit: cover;
}

.author__bio {
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--ink);
}

.author__facts-heading { margin-top: var(--space-xl); }

.author__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.author__list > li {
  position: relative;
  padding-left: var(--space-lg);
  margin-bottom: var(--space-md);
}

.author__list > li:last-child { margin-bottom: 0; }

/* Marker dot. An accent, never type, so it takes the bright token (rule 8). */
.author__list > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6em;
  width: var(--space-xs);
  height: var(--space-xs);
  border-radius: 50%;
  background: var(--profit-green);
}

.author .btn-row { margin-top: var(--space-xl); }

@media (max-width: 640px) {
  .author__inner { grid-template-columns: 1fr; gap: var(--space-lg); }
  .author__photo { width: 120px; height: 120px; }
}


/* --------------------------------------------------------------------------
   9. Resources hub
   -------------------------------------------------------------------------- */

.hub-header {
  background: linear-gradient(180deg, var(--trust-blue) 0%, var(--trust-blue-dark) 100%);
  color: var(--white);
  padding-block: var(--space-2xl);
}

.hub-header h1 { color: var(--white); font-size: var(--step-5); max-width: 20ch; }

.hub-header p {
  font-size: var(--step-1);
  color: rgba(255, 255, 255, 0.88);
  max-width: 60ch;
}

p.hub-header__byline {
  margin-top: var(--space-md);
  font-size: var(--step--1);
  color: rgba(255, 255, 255, 0.65);
}

.hub-header__byline a {
  font-family: var(--font-brand);
  font-weight: 600;
  color: inherit;
  text-decoration: underline;
}

@media (max-width: 640px) {
  .hub-header h1 { font-size: var(--step-4); }
}

/* Featured block: one large + three secondary */
.featured {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--space-lg);
}

.featured__secondary {
  display: grid;
  gap: var(--space-lg);
  grid-template-rows: repeat(3, 1fr);
}

.featured-card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}
.featured-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--mint-deep);
  transform: translateY(-2px);
}

.featured-card--lead {
  background: var(--fresh-mint);
  border-color: var(--mint-deep);
  min-height: 300px;
}

/* Featured image slot.
   Renders an on-brand marked placeholder until a real image is supplied.
   To ship a real image, replace the .media-placeholder div with an <img>. */
.featured-card__media {
  margin: 0 0 var(--space-lg);
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 16 / 9;
  background: var(--white);
}

.featured-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.media-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  text-align: center;
  padding: var(--space-md);
  color: var(--trust-blue);
  background:
    repeating-linear-gradient(
      -45deg,
      rgba(31, 78, 121, 0.05) 0 12px,
      rgba(31, 78, 121, 0.10) 12px 24px
    );
  border: 1px dashed var(--trust-blue-light);
  border-radius: var(--radius);
}

.media-placeholder__label {
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
}

.media-placeholder__hint {
  font-size: 0.78rem;
  color: var(--ink-muted);
  max-width: 30ch;
}

.featured-card--lead h3 { font-size: var(--step-3); }

.featured-card h3 { font-size: var(--step-1); margin-bottom: var(--space-xs); }

.featured-card p { font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 0; }

.featured-card a { text-decoration: none; color: inherit; }
.featured-card a::after { content: ""; position: absolute; inset: 0; }

.tag {
  display: inline-block;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--trust-blue);
  background: rgba(255, 255, 255, 0.75);
  border: 1px solid var(--mint-deep);
  border-radius: 999px;
  padding: 0.2rem 0.7rem;
  margin-bottom: var(--space-sm);
  align-self: flex-start;
}

.tag--news { background: rgba(255, 200, 87, 0.3); border-color: var(--guidance-gold); }

@media (max-width: 860px) {
  .featured { grid-template-columns: 1fr; }
  .featured__secondary { grid-template-rows: none; }
}

/* Category filter chips: CLIENT-SIDE ONLY.
   All 30 article links stay in the DOM at all times; JS toggles visibility.
   Never URL-based (brief §Resources-hub, HARD BUILD REQUIREMENT). */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  align-items: center;
  margin-bottom: var(--space-lg);
  padding-bottom: var(--space-lg);
  border-bottom: 1px solid var(--rule);
}

.filter-chip {
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--trust-blue);
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.45rem 1.05rem;
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease, color 0.16s ease;
}
.filter-chip:hover { background: var(--fresh-mint); border-color: var(--mint-deep); }

.filter-chip[aria-pressed="true"] {
  background: var(--trust-blue);
  border-color: var(--trust-blue);
  color: var(--white);
}

.filter-chip__count {
  font-weight: 400;
  opacity: 0.7;
  margin-left: 0.35rem;
}

.filter-status {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-left: auto;
}

/* Return-to-filters control (main.js §8). It sits with the filter components
   rather than at the end of the file because it is a control over the filter.
   ONE OF TWO position: fixed RULES IN THIS STYLESHEET. The other is
   .contents-toggle, added 27 August 2026 with the article contents control,
   which shares this z-index band. z-index 150 sits above the
   sticky header at 100 and below the skip link at 200, so a keyboard user who
   tabs to the skip link still sees it over this.
   The face matches .filter-chip[aria-pressed="true"] above, deliberately, so
   the control reads as the active chip come to find the reader.
   NO TRANSITION AND NO TRANSFORM, so this rule needs no prefers-reduced-motion
   branch of its own. The hidden state is display: none, which also takes the
   control out of the tab order instead of leaving a focusable invisible box. */
.filter-return {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 150;
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--white);
  background: var(--trust-blue);
  border: 1px solid var(--trust-blue);
  border-radius: 999px;
  padding: 0.45rem 1.05rem;
  text-decoration: none;
  box-shadow: var(--shadow-md);
}

.filter-return:hover { box-shadow: var(--shadow-lg); }

.filter-return.is-hidden { display: none; }

/* The upward mark on the return control. It is a CLIPPED TRIANGLE, not a
   character, which is the technique .submenu-toggle__caret already uses and for
   the same reason: an arrow codepoint would have to clear CLAUDE.md rule 11,
   whose cmap test no committed tool can run, and U+25BE came out of that caret
   on exactly that ground with the approval branch declined. Nothing here puts a
   new codepoint into rendered text.
   The polygon is the caret's, reoriented to point UP.
   Sized in em so it tracks the control's own font-size, and painted with
   currentColor so it follows the label. NO TRANSITION, so the rule set above
   still needs no prefers-reduced-motion branch of its own.
   THE SIZE IS CHOSEN, NOT DEFAULTED. Christopher compared three sizes on the
   branch preview, 21 August 2026, and took this one: the smaller read as not
   large enough and the larger as slightly heavy against the label. There is
   one size and no modifier class. */
.filter-return__mark {
  flex: none;
  width: 0.75em;
  height: 0.65em;
  background: currentColor;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

.article-grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  list-style: none;
  padding: 0;
  margin: 0;
}

.article-grid > li { margin: 0; }

.article-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}
.article-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--mint-deep);
  transform: translateY(-2px);
}

.article-card h3 { font-size: var(--step-1); margin-bottom: var(--space-xs); }

.article-card h3 a { color: inherit; text-decoration: none; }
.article-card h3 a::after { content: ""; position: absolute; inset: 0; }

.article-card p {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-bottom: var(--space-md);
}

.article-card__foot {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  align-items: center;
  font-size: var(--step--1);
  color: var(--ink-muted);
}

.article-card__cat {
  font-family: var(--font-brand);
  font-weight: 600;
  color: var(--profit-green-dark);
}

/* Hidden by the client-side filter. Links remain in the HTML. */
.is-filtered-out { display: none; }

/* --------------------------------------------------------------------------
   10. CTA band
   -------------------------------------------------------------------------- */

.cta-band {
  background: var(--trust-blue);
  color: var(--white);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  text-align: center;
}

.cta-band h2 { color: var(--white); font-size: var(--step-3); }

.cta-band p {
  color: rgba(255, 255, 255, 0.9);
  max-width: 58ch;
  margin-inline: auto;
  margin-bottom: var(--space-lg);
}

.cta-band .btn-row { justify-content: center; }
.cta-band .btn-note { color: rgba(255, 255, 255, 0.8); }
/* .btn-link is Trust Blue and .cta-band is Trust Blue, so the contact link the
   templates carry would render at 1.00 with a floating underline. This is the
   dark-band half of the same class rather than a second class: one treatment
   carries the contact link everywhere in the body, light band or dark.
   Measured 17 August 2026. See CLAUDE.md rule 5g.
   The alpha is 0.5 rather than 0.4 because this band sets color: white on
   itself, so the link is white text among white text and the underline is
   not merely reinforcement here. 0.4 measured 2.77; the 29 July ruling on
   sub-3:1 borders covers a light background where colour carries the
   distinction, which is not this case.
   .section--blue ADDED 23 August 2026, on About's closing block: that band sets
   color: white on itself exactly as this one does, so the reasoning above always
   covered it and only the selector did not. */
.cta-band .btn-link,
.section--blue .btn-link {
  color: var(--white);
  border-bottom-color: rgba(255, 255, 255, 0.5);
}


/* Caption measure, made deliberate. ADDED 27 July 2026, no visual change.
   CORRECTED 21 August 2026: this block introduced a max-width on the item, and
   that declaration was superseded on 20 August by the rule below and is removed
   here. The 26rem measure it derives is still the live figure; it is held by the
   padding in that rule rather than by a cap on the item.

   Before this the note had no width rule of its own and inherited
   .cta-band p { max-width: 58ch }, a READING MEASURE meant for paragraphs,
   which rendered it at 512px. It sat on one line, but by accident: nothing
   said a caption should be 58 characters wide, and any future change to the
   paragraph measure would have silently moved the caption with it.

   26rem is 416px. Measured rather than guessed: the string sets at 394px in
   Inter, the loaded body font, which is the WIDEST case. Every fallback in
   the stack (Open Sans, Segoe UI, system-ui, Arial) sets it at 366 to 367px,
   so a font that fails to load makes the line narrower, never wider. 416px
   leaves 22px of headroom over the worst case and stays well inside the band.

   CORRECTED 24 August 2026. This paragraph read THE TWO CTA CONTEXTS DIFFER ON
   PURPOSE, do not harmonise them, and said that .cta-split was the homepage only
   and pinned its button in a narrow right column, that a caption wider than the
   button there read as detached from it, and that it was therefore capped tight
   at 17rem and deliberately wrapped to two lines. THAT CONTEXT NO LONGER EXISTS.
   The homepage CTA is a single centred column, there is no narrow right column
   and no 17rem cap, and the rule that carried them is deleted.

   WHAT SURVIVES IS THE HALF THAT WAS ONLY EVER ABOUT .cta-band, which is
   symmetric and centred, is the pattern in every template, and so governs
   the other 40 pages. Its caption holds the 26rem measure derived above and its
   text sets on one line. */

/* A cta-band note is a full-width row member at EVERY width, and its 26rem
   measure is held by padding on that full-width box rather than by a max-width
   on the item. ADDED 20 August 2026, replacing a min-width 861px version added
   earlier the same day.

   Why the item cannot carry the cap: a max-width participates in how flex sizes
   an item, so a capped note is narrow enough to share a flex line with a
   control. Measured across 501 viewport widths on 20 August: at 751 to 858 the
   note sat beside the LINK with the button alone above, and at 859 to 860 it sat
   beside the BUTTON with the link pushed below. Both vanish when the note is full
   width, which is why the homepage hero, whose note has never carried a cap, is
   clean at all 503 widths measured.

   The breakpoint version fixed only the band above 861 and left 110 pixels of
   viewport wrong, so it is replaced rather than extended. Padding does not
   participate in flex sizing, so the box spans the row while the text stays
   where it was. The clamp holds the padding at zero on rows narrower than the
   measure, which is every phone width. */
.cta-band .btn-note {
  max-width: none;
  padding-inline: max(0px, calc((100% - 26rem) / 2));
}

/* Homepage CTA: full-width band, single centred column.
   ADDED 27 July 2026. Additive only: .cta-band above is untouched and still
   serves every other page that uses it.

   The band itself is .section--blue, so the background, the white heading and
   the near-white .section__lead come from rules that already existed.

   CORRECTED 24 August 2026, and the old wording is kept in this paragraph
   rather than deleted. This block used to say the only new thing here was the
   two-column geometry, and it then declared .cta-split__inner as a
   space-between flex row, capped its first child at 30em, gave the button its
   own .cta-split__action column, capped that column's note at 17rem, and
   stacked the pair below 860px. ALL OF THAT IS DELETED. The block is now a
   single centred column and its arrangement is taken from .about-cta, which
   does the same job on the About page. The three rules below are that
   arrangement with the selector changed and nothing else; see .about-cta in
   section 12f for the reasoning, which is not restated here.

   THE CLASS NAME NO LONGER DESCRIBES A SPLIT. It is kept because the block
   still needs a non-generic hook for the centring, and renaming it was not
   authorised. Recorded so the next reader does not take the name as evidence
   of a layout that is gone.

   CORRECTED 23 August 2026, and the old wording is kept in this paragraph
   rather than deleted. This block used to say the only new things here were
   the two-column geometry AND ONE HOVER STATE, and then explained that hover
   state: that .btn--primary hovers to Trust Blue, which is this section's own
   background, so on a blue band the button would dissolve on hover, and that
   it inverted to white on Trust Blue instead. Every word of that was true
   while the button was green. It is not true now. The booking control in this
   band is .btn--ghost-light under CLAUDE.md rule 5g, and the override that
   carried the inversion is REMOVED, because it had no subject left.

   THIS PARAGRAPH IS WHY THE DARK BANDS COMMENT EXISTS. It carried real
   reasoning and still produced a defect, because it was written as a fact
   about THIS BLOCK rather than about the condition every dark band shares.
   -------------------------------------------------------------------------- */
.cta-split h2 { margin-bottom: var(--space-sm); }


/* The centring, taken from .about-cta in section 12f. Same three declarations,
   selector changed, nothing else. No media override: a single centred column
   needs no stacking point, which is why the 860px block that used to sit here
   is deleted rather than rewritten. */
.cta-split { text-align: center; }

.cta-split .section__lead {
  max-width: var(--wrap-narrow);
  margin-inline: auto;
  margin-bottom: var(--space-xl);
}

.cta-split .btn-row { justify-content: center; }

/* --------------------------------------------------------------------------
   11. Trust strip
   -------------------------------------------------------------------------- */

.trust-strip {
  background: var(--fresh-mint);
  padding-block: var(--space-lg);
}

.trust-strip__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-lg);
  align-items: center;
  justify-content: center;
  text-align: center;
}

.trust-strip__item {
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--trust-blue-dark);
}

/* Stat strip: the homepage's compressed three-stat band beneath the hero.
   ADDED 27 July 2026. Nothing above was modified.

   Deliberately NOT .trust-strip, which it would otherwise closely resemble.
   .trust-strip is for credential and trust badges, and CLAUDE.md rule 2 bars a
   ProAdvisor trust strip from the homepage, so a compliance sweep reading class
   names must not find "trust-strip" on that page. Same band, honest name.

   Each item is a figure with its label below: value large, label small. The
   flex row wraps and centres, so it degrades to a stack on narrow screens with
   no separate mobile rule. */
.stat-strip {
  background: var(--fresh-mint);
  padding-block: var(--space-lg);
  border-bottom: 1px solid var(--rule);
}

.stat-strip__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-lg) var(--space-2xl);
  align-items: flex-start;
  justify-content: center;
  text-align: center;
}

.stat-strip__item {
  margin: 0;
  font-family: var(--font-brand);
  font-weight: 700;
  font-size: var(--step-2);
  line-height: 1.15;
  color: var(--trust-blue-dark);
}

.stat-strip__label {
  display: block;
  margin-top: var(--space-2xs);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step--1);
  line-height: 1.4;
  color: var(--ink-muted);
}

/* --------------------------------------------------------------------------
   12. Footer
   -------------------------------------------------------------------------- */

.site-footer {
  background: var(--trust-blue-dark);
  color: rgba(255, 255, 255, 0.82);
  padding-block: var(--space-2xl) var(--space-lg);
  font-size: var(--step--1);
  /* NO margin above the footer. This carried 96px until 27 July 2026, then
     64px, and both were wrong for the same reason: the margin STACKED on top
     of the last section's own bottom padding. That made the footer boundary
     the sum of two values while every section-to-section boundary is set by
     padding alone, so the last edge on the page was always the largest one
     and the closing section always read bottom-heavy.

     At 0 the footer boundary is simply the last section's bottom padding,
     which is the same rule every other boundary on the page follows. A page
     ending on a normal section closes at 64px; one ending on section--tight
     closes at 40px unless a page rule overrides that band's bottom padding.
     The spacing follows the section, not a separate constant that has to be
     kept in sync with it.

     This also matches the design bundle, whose footer carries only background
     and color and specifies no gap above itself at all. Checked before
     changing, because this is global to all 41 pages. */
  margin-top: 0;
}

.site-footer__grid {
  display: grid;
  gap: var(--space-xl);
  grid-template-columns: 1.3fr 1fr 1fr 1.2fr;
  padding-bottom: var(--space-xl);
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}

.site-footer h2 {
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--guidance-gold);
  margin-bottom: var(--space-md);
}

.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin-bottom: var(--space-sm); }

.site-footer a { color: rgba(255, 255, 255, 0.85); text-decoration: none; }
.site-footer a:hover { color: var(--white); text-decoration: underline; }

/* The softened link colour above also lands on the footer's primary button,
   because .site-footer a (0,1,1) outranks .btn--primary (0,1,0). At 85% white
   over the button's --cta-green face that composites to #e1ece4 and measures
   3.85 against a 4.5 requirement.

   Restoring solid white on the BUTTON ONLY. .site-footer .btn is (0,2,0),
   which beats (0,1,1) on class count, so this wins the cascade without
   !important and the rule above is left intact. The other sixteen footer
   links keep their 85%: on the dark band they measure 8.96, they pass
   comfortably, and the softening is deliberate. */
.site-footer .btn { color: var(--white); }
/* The hover version of the collision the comment above describes, exposed by the
   first footer control that hovers to a LIGHT background. .site-footer a:hover
   is (0,2,1) and .site-footer .btn is (0,2,0); both hold the label white, which
   on .btn--outline-light's white hover face measures 1.00. This is (0,3,0) and
   beats both without !important, leaving the generic modifier intact for use on
   any other dark band. Measured 17 August 2026. See CLAUDE.md rule 5g. */
.site-footer .btn--outline-light:hover,
.site-footer .btn--outline-light:focus-visible {
  color: var(--trust-blue);
}


/* Footer brand block. The SQUARE logo here, not the header's horizontal one:
   the stacked lockup fills a narrow footer column better than a wide strip.
   Source is a transparent 300x300 PNG, so the gold sits straight on Trust Blue
   Dark with no plate. 96px keeps the "BOOKKEEPING" subline legible (it goes
   muddy below about 80px) while staying clearly larger than the ProAdvisor
   badge lower down, as the Brand Use Guide requires. 156px reads as a
   deliberate brand anchor rather than an afterthought; the 300x300 source still
   has ~1.9x of pixels at that size, so it stays crisp on retina. */
/* display:block, not inline-block: an inline-block wrapper sits on the line
   box baseline and leaves a descender-sized gap under the image, which read as
   a stray ~10px between the logo and the blurb. */
.site-footer__brand {
  display: block;
  width: fit-content;
  margin-bottom: var(--space-md);
  text-decoration: none;
}

.site-footer__logo {
  height: 156px;
  width: auto;
  max-width: none;
}

@media (max-width: 560px) {
  .site-footer__logo { height: 124px; }
}

.site-footer__blurb { color: rgba(255, 255, 255, 0.75); max-width: 34ch; }

/* NAP block. Address (Littleton, CO 80125) and service area are distinct
   lines and must never be collapsed into each other. */
.site-footer__nap {
  font-style: normal;
  display: block;
  margin: var(--space-md) 0 var(--space-sm);
}

.site-footer__addr {
  display: block;
  font-family: var(--font-brand);
  font-weight: 600;
  color: var(--white);
}

.site-footer__area,
.site-footer__reply {
  display: block;
  color: rgba(255, 255, 255, 0.75);
}

.site-footer__reply { margin: 0 0 var(--space-md); }

/* Social row. Brand-palette monochrome, never native platform colors.
   Base tone is the footer's light neutral for contrast on the dark ground;
   hover is Profit Green per the brand spec. */
.social-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  list-style: none;
  padding: 0;
  margin: 0;
}

.social-row li { margin: 0; }

/* The contact control needs clear separation from the 40px social
   targets sitting directly above it, so the two never read as one control
   cluster and neither gets mis-tapped on mobile. The booking button that
   used to sit here was replaced on 17 August 2026; the spacing reason is
   unchanged and only the control's name moved. */
.site-footer__contact { margin: var(--space-lg) 0 0; }

.social-link {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius);
  color: rgba(255, 255, 255, 0.85);
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.16);
  transition: color 0.16s ease, background 0.16s ease, border-color 0.16s ease;
}

.social-link svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}

.social-link:hover,
.social-link:focus-visible {
  color: var(--profit-green);
  background: rgba(75, 175, 101, 0.14);
  border-color: var(--profit-green);
}

/* ProAdvisor credential block.
   Footer only. Deliberately placed away from the Cilson logo and
   directly beside the trademark attribution. Certification-contingent:
   delete .footer-cert's badge link and nothing else breaks. */
.footer-cert {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md);
  padding-bottom: var(--space-md);
}

.footer-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-md);
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius);
  text-decoration: none;
  font-family: var(--font-brand);
  font-weight: 600;
  /* Kept far below the header logo: the Cilson mark must read larger
     than any Intuit mark anywhere on the site. */
  font-size: var(--step--1);
  line-height: 1.3;
  color: var(--white);
  flex: 0 0 auto;
}
.footer-badge:hover { background: rgba(255, 255, 255, 0.14); text-decoration: none; }

.site-footer__bottom {
  padding-top: var(--space-lg);
  display: grid;
  gap: var(--space-md);
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.8rem;
}

.site-footer__legal { max-width: 90ch; margin: 0; flex: 1 1 320px; }

@media (max-width: 900px) {
  .site-footer__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  .site-footer__grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   12a. Orphan control: text-wrap: pretty across every description component
   ADDED 27 July 2026. Additive only, no token declared or changed.

   ONE grouped rule rather than the property repeated in sixteen places. This
   is the authoritative list of what gets orphan control, so adding a new
   description component means adding one selector here rather than
   remembering a property exists. Placed last so it wins regardless of where
   each component's own rule sits.

   .card p already carries it (block 6f area) and is deliberately not repeated
   here. .founder-card__takeaway inherits through .card p and needs nothing.

   Headings are NOT in this list. h1 to h4 take text-wrap: balance near the top
   of the file, which is the right default for display type. Balance evens out
   every line and is the wrong call for body copy.

   THAT REASONING USED TO LIVE ON .cta-split__action AND THIS PARAGRAPH POINTED
   AT IT. That rule was deleted on 24 August 2026 when the homepage CTA became a
   single centred column, so the reasoning is carried here rather than lost:
   balance was tried on a button note and deliberately not used, because it
   evens every line rather than reworking only the last one, so it trades the
   taper the slot wants for a short line stacked on a long one, and it can break
   the phrase the sentence turns on.

   THE TEMPLATE COMPONENTS ARE THE POINT. .article-card, .featured-card,
   .hub-header, .cta-band and .pull-quote serve the resource articles, the
   resources hub and the service pages. Setting this on the components means
   every page built from them inherits orphan control instead of each one
   rediscovering the problem on review. CORRECTED 15 September 2026: this said
   those pages were not built yet, which went false as they were built, so it
   no longer says which pages exist.

   BEST EFFORT, NOT A GUARANTEE. See CLAUDE.md 5e. Chrome declines to reflow
   when pulling a word down would leave the previous line too short, so narrow
   columns still orphan with this set and computing as "pretty". Measured, not
   assumed: the founder takeaways compute pretty and still orphan at 215px.
   Firefox has no support at any version and falls back to normal wrapping,
   which is the current output, so this improves most traffic and regresses
   nothing. A case that must hold at desktop width takes a named &nbsp; in the
   markup as well.
   -------------------------------------------------------------------------- */
.steps p,
.point-card p,
.section__lead,
.hero__standfirst,
.hero__stat,
.hero__caption,
.founder__quote p,
.faq__a,
.btn-note,
.eyebrow,
.article-card p,
.featured-card p,
.hub-header p,
.cta-band p,
.pull-quote p {
  text-wrap: pretty;
}


/* --------------------------------------------------------------------------
   12b. About page: the Why band and the story spine

   Design bundle "About Page Explorations", Option 1a, "The spine". Every value
   below resolves to a token declared in section 1. Nothing here introduces a
   colour, a size, a radius, a shadow or a spacing constant of its own, and
   :root is untouched. Where the bundle used a value with no token the nearest
   token was taken; those substitutions are listed in the pull request that
   added this block, not restated here (rule 9).

   Scoped throughout to classes that appear only on
   /bookkeeper-christopher-wilson. There is no bare element selector and no
   descendant selector that starts anywhere but one of these classes, so no rule
   here can reach another section or another page.
   -------------------------------------------------------------------------- */

/* --- Why I do what I do ---------------------------------------------------
   The bundle centres the whole band and leads with a short green bar over a
   small uppercase label. */

.why__inner { text-align: center; }

/* Accent bar. Bright green carries no type here, it IS the mark, so this is the
   bright token rather than the dark one (rule 8). The radius token is 6px
   against a 4px box, which CSS clamps to a pill; that is the bundle's shape. */
.why__rule {
  display: block;
  width: var(--space-xl);
  height: var(--space-2xs);
  margin: 0 auto var(--space-lg);
  border-radius: var(--radius);
  background: var(--profit-green);
}

/* The bundle drew this line as a plain div. Keeping it an h2 preserves the
   document outline, so the element stays and the TREATMENT is what moves: size,
   weight, colour, tracking and margin all override the shared h1-h4 rule.
   Tracking is 0.08em to match .eyebrow rather than the bundle's 0.12em, so the
   site keeps one uppercase-label treatment instead of two. */
.why__eyebrow {
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--profit-green-dark);
  margin: 0 0 var(--space-sm);
}

/* .section__lead already supplies --step-1 and text-wrap: pretty. Only the
   measure, the centring and the darker ink differ from it here. */
.why__lead {
  max-width: var(--wrap-narrow);
  margin-inline: auto;
  color: var(--ink);
}

.why__emphasis { color: var(--trust-blue); }

/* --- My story in three short chapters -------------------------------------
   Cards alternate either side of a centred spine, one node disc per chapter
   sitting on the line. */

/* The band tint. This is the fill treatment Christopher approved on 1 August
   2026: a cloud band carrying white cards, rather than the bundle's white band
   carrying white cards. It is declared here rather than through section--cloud
   in the markup, because the pairing is band AND card and section--cloud carries
   no card rule with it. The Why band above gives up the tint in exchange, which
   is markup only: it simply drops section--cloud. */
.story-spine { background: var(--cloud-gray); }

.story-spine__title {
  text-align: center;
  margin-bottom: var(--space-xl);
}

.story-spine__track { position: relative; }

/* The spine. A hairline, so it takes --rule like every other hairline on the
   site. Centred with a transform rather than a negative margin so the 2px width
   is the only length involved. */
.story-spine__track::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 2px;
  background: var(--rule);
}

/* Card, node channel, card. Rows are ordinary grid rows in normal flow: there
   is no negative pull and no fixed row height anywhere in this block, so
   nothing here has to be retuned when the chapter copy changes length.
   z-index lifts the row over the spine so the node's fill can mask the line;
   the card never reaches the centre channel, so the spine still shows between
   the rows. */
.story {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr var(--space-3xl) 1fr;
  align-items: center;
}

.story + .story { margin-top: var(--space-xl); }

/* The white fill is now the FIGURE against the tinted band above, not a card
   that happens to match its background. It was already --white before that
   pairing was chosen, so nothing here changed when it was; the declaration is
   load-bearing now in a way it was not then. The border stays: measured on the
   preview, --white on --cloud-gray is 1.07 and the --rule hairline on
   --cloud-gray is 1.18, so the edge is mostly the shadow either way. */
.story__card {
  grid-row: 1;
  grid-column: 1;
  padding: var(--space-lg);
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

/* The alternation is here rather than in the markup, so the articles stay in
   reading order and no card carries a side in its class list. Reordering the
   articles reorders the sides, which is the intended behaviour. */
.story:nth-child(even) .story__card { grid-column: 3; }

/* Node disc. Bright green is the BORDER and the disc holds no type, so the
   invariant in rule 8 is not engaged. */
.story__node {
  grid-row: 1;
  grid-column: 2;
  justify-self: center;
  width: var(--space-2xl);
  height: var(--space-2xl);
  border-radius: 50%;
  background: var(--white);
  border: 2px solid var(--profit-green);
}

.story__card h3 { margin: 0 0 var(--space-2xs); }

.story__org {
  margin: 0;
  color: var(--ink-muted);
}

.story__dates {
  margin: var(--space-2xs) 0 var(--space-md);
  font-size: var(--step--1);
  color: var(--ink-muted);
}

.story__lines {
  border-top: 1px solid var(--rule);
  padding-top: var(--space-md);
}

/* Label above its sentence rather than inline with it. No font-family: the
   bundle leaves these in the body face, and Inter carries a real 700 on its
   variable axis where Poppins would have had to synthesise one. */
.story__label {
  display: block;
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: var(--space-2xs);
}

/* The second line is the benefit: its sentence sets heavier and its label turns
   green. Selected by adjacency so neither paragraph needs a modifier class.
   Green on type is always the dark token (rule 8). */
.story__line + .story__line { font-weight: 600; }
.story__line + .story__line .story__label { color: var(--profit-green-dark); }

@media (max-width: 860px) {
  /* Spine moves to the middle of a left-hand node channel and every card sits
     to its right. Odd and even rows get the same geometry, so the alternation
     stops rather than being fought. */
  .story { grid-template-columns: var(--space-3xl) 1fr; }
  .story__card,
  .story:nth-child(even) .story__card { grid-column: 2; }
  .story__node { grid-column: 1; }
  .story-spine__track::before { left: calc(var(--space-3xl) / 2); }
}

@media (max-width: 560px) {
  /* Narrower channel and a smaller disc, so the card keeps a usable measure at
     phone widths. Both lengths are still tokens and the spine follows them. */
  .story { grid-template-columns: var(--space-2xl) 1fr; }
  .story__node {
    width: var(--space-xl);
    height: var(--space-xl);
  }
  .story-spine__track::before { left: calc(var(--space-2xl) / 2); }
}


/* --------------------------------------------------------------------------
   12c. About page: the node numerals

   Two things only: the type inside the disc, and the phone-width geometry that
   the numerals forced. Both sit here rather than in 12b because both arrived
   after it and neither needed 12b edited.

   A comparison harness lived in this block for one commit, carrying two fill
   treatments and a pair of gold labels. Christopher chose the tinted band on
   1 August 2026 and all of it is gone: the winning fill folded into 12b, the
   losing rules and the labels deleted. No variant vocabulary survives anywhere
   in this file or in the page.
   -------------------------------------------------------------------------- */

/* --- The node numerals ----------------------------------------------------
   2011, 2006 and 2000, approved as copy 1 August 2026. Green type, so it is the
   dark token; rule 8's invariant means bright green never carries a glyph. The
   disc border stays bright, which is the accent role that token is for. */

.story-spine .story__node {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-brand);
  font-weight: 700;
  font-size: var(--step-1);
  line-height: 1;
  color: var(--profit-green-dark);
}

/* 12b shrank the disc to --space-xl below 560px, which was correct while the
   discs were empty and is not now: a four-digit numeral at --step-1 does not fit
   a 40px disc. Restored to the same geometry the 860px rule uses rather than
   editing 12b, which stays additive. The channel widens with it so the disc
   still has clearance, and the spine follows the channel. */
@media (max-width: 560px) {
  .story-spine .story { grid-template-columns: var(--space-3xl) 1fr; }
  .story-spine .story__node {
    width: var(--space-2xl);
    height: var(--space-2xl);
  }
  .story-spine .story-spine__track::before { left: calc(var(--space-3xl) / 2); }
}



/* --- The line below the chapters -------------------------------------------
   Links the chapters to the news post that continues the story, added 18
   September 2026. Centred to match the section title above it. */

.story-spine__more {
  text-align: center;
  margin: var(--space-xl) 0 0;
}



/* --------------------------------------------------------------------------
   12d. About page: fast facts and credentials

   Design bundle "About Page Explorations", Option 1a, its "credentials, mint"
   region. Two columns, facts left, badge card right.

   THIS BLOCK IS COMPLIANCE-BEARING. The badge geometry below is contractual,
   not stylistic: the size is a ceiling derived from the source raster and the
   clear space is a proportion of it. Read CLAUDE.md rule 2 before changing any
   length in .credentials__badge-link. Everything else here is ordinary layout
   and resolves to tokens from section 1.

   Scoped throughout to .credentials classes, which appear on one section of one
   page. No bare element selector and no descendant selector starting anywhere
   but a .credentials class.
   -------------------------------------------------------------------------- */

.credentials__inner {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-2xl);
  align-items: center;
}

.credentials__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.credentials__list > li {
  position: relative;
  padding-left: var(--space-lg);
  margin-bottom: var(--space-md);
}

.credentials__list > li:last-child { margin-bottom: 0; }

/* Marker dot. An accent, never type, so it takes the bright token (rule 8). */
.credentials__list > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6em;
  width: var(--space-xs);
  height: var(--space-xs);
  border-radius: 50%;
  background: var(--profit-green);
}

/* The badge's immediate background. A plain white fill, which is what the
   Agreement's design requirements ask for: no photo, no illustration, no
   pattern, and 12.99:1 against the shield's navy edge. The mint band still
   shows around it, and that pairing measures 11.05:1, so either surface would
   have satisfied the requirement; the card is the bundle's composition. */
.credentials__card {
  /* Declared on the CARD rather than on the link so the name and the legend can
     size themselves from the same number. See the geometry note below.
     THIS IS THE ONLY PLACE THE BADGE SIZE IS WRITTEN. It was 200px until
     2 August 2026; see the geometry note for why it came down. */
  --badge-size: 175px;
  background: var(--white);
  border: 1px solid var(--mint-deep);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: var(--space-lg);
  text-align: center;
}

/* CONTRACTUAL GEOMETRY. --badge-size is a LOCAL property on this component, not
   a design token, and it is deliberately not in :root: it is a licence ceiling
   rather than a scale step, and nothing else on the site may reuse it.

   THE CEILING AND THE FLOOR ARE DIFFERENT CONSTRAINTS, and only one of them set
   this number. 200px is the largest size that never upscales the 600px raster on
   a 3x display, and the Agreement permits raster forms at 100% or smaller only,
   so 200px remains the ceiling. 175px is well inside it.

   WHAT ACTUALLY SET 175px is the Brand Use Guide's size requirement, not the
   raster ceiling: Cilson's own name or logo must appear larger than any Intuit
   brand or logo. Measured, the Cilson square at 350px in the Why band is larger
   than this badge on width, height, area and diagonal, with height the binding
   measure. The two sizes are COUPLED. Changing either one without re-measuring
   the other can put the page back out of compliance. CLAUDE.md rule 2 carries
   the figures.

   The margin is the 15% clear space expressed AS A PROPORTION of that size, so
   the two cannot drift apart. Change the size and the clear space follows, which
   is what rule 2 requires of it. Nothing may touch the badge: the card's own
   padding sits outside this margin, so the border is 50.25px from the artwork at
   the shipped size. */
.credentials__badge-link {
  display: block;
  width: var(--badge-size);
  margin: calc(var(--badge-size) * 0.15);
}

.credentials__badge {
  display: block;
  width: 100%;
  height: auto;
}

/* The name. The Agreement requires the material to carry Christopher's own name
   at least as large as the Certified Logo's own lettering. Measured from the
   artwork at the shipped 175px: the badge's largest line has a 10.21px cap height
   and its smallest, CERTIFIED, has 6.71px. Poppins 600 at --step-0 gives 12px,
   which clears both, and by a wider margin than it did at 200px. Do not reduce
   this below --step-0 without re-measuring. */
/* Both text blocks are capped at the badge's own footprint, badge plus its two
   clear-space margins, which is 1 + 2 x 0.15 of the size. Without this the card
   sits in an auto grid track sized to the LEGEND's max-content, and the legend is
   one long unbroken sentence: measured before the cap, the card came out about
   770px wide and squeezed the facts column. Capping here rather than putting a
   width on the card keeps every length derived from --badge-size. */
.credentials__name,
.credentials__legend {
  max-width: calc(var(--badge-size) * 1.3);
  margin-inline: auto;
}

.credentials__name {
  margin: 0 auto;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-0);
  line-height: 1.3;
  color: var(--trust-blue);
}

/* The Agreement's Required Legend. Small, and outside the clear space because it
   sits below the badge's own bottom margin. Verbatim and unedited: it takes no
   registration symbol, because inserting one would modify a string the
   Agreement quotes. */
.credentials__legend {
  margin: var(--space-md) 0 0;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-muted);
}

@media (max-width: 860px) {
  /* One column, card below the facts and centred under them. The badge geometry
     is untouched at every width: the clear space is contractual, so it does not
     shrink with the viewport. */
  .credentials__inner { grid-template-columns: 1fr; }
  .credentials__card { justify-self: center; }
}


/* --------------------------------------------------------------------------
   12e. About page: the Why band as two columns, Cilson mark left

   WHY THIS EXISTS, and it is a compliance change wearing a layout change's
   clothes. The Brand Use Guide requires Cilson's own business name or logo to
   appear LARGER than any Intuit brand or logo. Before this block the largest
   Cilson mark on the page was the footer logo at 156px and the ProAdvisor badge
   was 200px, so the page failed that requirement on every measure. Putting the
   square mark here at 350px is the half of the remedy that adds Cilson presence;
   bringing the badge to 175px in 12b is the other half. CLAUDE.md rule 2 carries
   the measured comparison. THE TWO SIZES ARE COUPLED: do not change one without
   re-measuring against the other.

   --mark-size is a LOCAL property, the same pattern --badge-size uses in 12b and
   for the same reason: it is a size this one component owns, not a scale step,
   so it does not belong in :root.

   Additive to 12b. Nothing there is edited by this block; the two rules that
   re-range the rule bar and the lead are scoped through .why__body, which did
   not exist before, rather than restating 12b's own selectors.
   -------------------------------------------------------------------------- */

.why__inner {
  --mark-size: 350px;
  display: grid;
  /* The token is the text column's declared width, not a leftover from a
     subtraction. minmax lets it give up the two pixels that 1132 of usable width
     cannot supply, rather than overflowing the wrap by that much. Measured on the
     preview: 718px against the token's 720px. */
  grid-template-columns: var(--mark-size) minmax(0, var(--wrap-narrow));
  gap: var(--space-2xl);
  align-items: center;
}

.why__mark {
  width: var(--mark-size);
  height: auto;
}

/* Ranged left. Centred prose beside a mark reads unresolved, and the centring in
   12b was written for a band that had nothing beside it. This overrides the
   INHERITED centre on the child rather than editing 12b's declaration. */
.why__body { text-align: left; }

/* The rule bar and the lead both centre themselves with auto inline margins in
   12b. Scoped here so the two-column layout owns the change. */
.why__body .why__rule { margin-inline: 0; }
.why__body .why__lead { margin-inline: 0; }

@media (max-width: 860px) {
  /* One column at this page's existing breakpoint. The mark must NOT stay at
     350px here: on a 400px viewport that is most of the screen. min() caps it at
     half the viewport, so it is 350px wherever there is room and shrinks with the
     screen below that, reaching 200px at 400px wide and 187px at 375px. */
  .why__inner { grid-template-columns: 1fr; }
  .why__mark { width: min(var(--mark-size), 50vw); }
}


/* --------------------------------------------------------------------------
   12f. About page: values with fun facts, and the closing CTA

   The last two bespoke blocks on this page. The services teaser below them needs
   no CSS at all: the brief specifies the site's existing section, grid, card and
   btn-row classes, and every one of them already exists.

   From the Design bundle's "values + fun facts" and "CTA" regions. Scoped to
   .values, .funfacts and .about-cta, which appear on one page.
   -------------------------------------------------------------------------- */

/* --- My values, with fun facts alongside ---------------------------------- */

.values__inner {
  display: grid;
  grid-template-columns: 7fr 5fr;
  gap: var(--space-2xl);
}

.values__list,
.funfacts {
  list-style: none;
  margin: 0;
  padding: 0;
}

.values__list > li,
.funfacts > li {
  position: relative;
  padding-left: var(--space-lg);
}

.values__list > li { margin-bottom: var(--space-md); }
.values__list > li:last-child { margin-bottom: 0; }

/* Square marker, deliberately different in shape from the round one the fun
   facts take and from the round one the credentials list takes, so the three
   lists read as three kinds of thing. An accent, never type, so it is the bright
   token; rule 8. The radius is 0 rather than a token: --radius is 6px, which on
   an 8px box clamps to nearly a circle and would lose the distinction. */
.values__list > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: var(--space-xs);
  height: var(--space-xs);
  border-radius: 0;
  background: var(--profit-green);
}

/* Fun facts are ruled rather than spaced, which is the bundle's own treatment
   and what keeps the shorter right-hand column from reading as an afterthought
   beside the taller values list. */
.funfacts > li {
  padding-block: var(--space-sm);
  border-top: 1px solid var(--rule);
}

.funfacts > li:last-child { border-bottom: 1px solid var(--rule); }

/* Gold, the one place on this page that token carries a mark. These are the
   lightest content on the page and the colour says so without a label. */
.funfacts > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.25em;
  width: var(--space-xs);
  height: var(--space-xs);
  border-radius: 50%;
  background: var(--guidance-gold);
}

/* The fun-facts heading sits optically level with the values list rather than
   with the h2 beside it, which is the bundle's own offset. */
.values__inner h3 { margin-top: var(--space-xs); }

/* --- Ready to work together ----------------------------------------------- */

/* .section--blue already supplies the band, the white heading and the 88%-white
   lead. Only the centring and the measure are added here. */
.about-cta { text-align: center; }

.about-cta .section__lead {
  max-width: var(--wrap-narrow);
  margin-inline: auto;
  margin-bottom: var(--space-xl);
}

.about-cta .btn-row { justify-content: center; }

/* --- The closing boundary -------------------------------------------------
   MEASURED, not chosen by eye. The footer adds no margin of its own, so the
   page's bottom edge is whatever the last section's bottom padding is. The
   services teaser takes .section--tight from the brief's spec, which is 40px top
   and bottom. That is correct where the teaser sits mid-page on a service page,
   and wrong as the last thing on this one: every other boundary on this page is
   64px, so the page would close tighter than any seam inside it.

   The bottom alone is raised to --space-2xl. The TOP stays at --space-xl,
   because it is a seam between two sections and the brief's class is right about
   it; only the closing edge is a different kind of boundary. */
.services-teaser { padding-bottom: var(--space-2xl); }

@media (max-width: 860px) {
  /* One column. The values list leads and fun facts follow, which is source
     order, so nothing needs reordering. */
  .values__inner { grid-template-columns: 1fr; }
  .values__inner h3 { margin-top: 0; }
}


/* --------------------------------------------------------------------------
   12b. Services overview page
   ADDED 9 September 2026 for /bookkeeping-services-overview. Additive only:
   no rule above is modified and no token is touched.

   WHAT FOLLOWS IS WHAT THIS PAGE NEEDED AND NO EXISTING PAGE DID, and each
   block carries its own reason.
   -------------------------------------------------------------------------- */

/* .hero--single WAS HERE AND IS GONE, removed 9 September 2026. It centred a
   hero with no aside, for /bookkeeping-services-overview, which was the only
   page that ever carried it. That page now uses .hub-header instead, so the
   six rules had no subject left on any page and came out rather than being
   left to be puzzled over. Nothing else referenced them: measured zero
   occurrences of hero--single across public/ and _templates/ after the change.
   THE HERO ITSELF IS UNTOUCHED. .hero, .hero__inner, .hero__title and
   .hero__standfirst still serve the five service pages, the homepage and
   About, and .hero__chips the five service pages, exactly as before. */

/* A HEADING-PLUS-LEAD ROW, which the design uses for every such row on this
   page: band 4's header and both tier headers in band 5. Heading LEFT, lead
   RIGHT, the two aligned at their bottom edges by .section-head--split below.

   THE LEAD IS CAPPED AND RIGHT-ALIGNED, both from the design. The cap stops a
   long lead from crowding the heading, and the right alignment is what makes
   the two read as one row rather than as a heading with a stray paragraph.

   IT STACKS BELOW 860, the breakpoint this file already uses for a
   section-level collapse. Below it the lead returns to the left, because a
   right-aligned paragraph under a left-aligned heading reads as a mistake, and
   the cap comes off so the text uses the width it has. */
.section-head__lead {
  max-width: 420px;
  text-align: right;
  margin: 0;
  font-size: var(--step-0);
  color: var(--ink-muted);
}

@media (max-width: 860px) {
  .section-head__lead { max-width: none; text-align: left; }
}

/* THE HEAD ROWS THAT CARRY A LEAD ARE A GRID, NOT A FLEX ROW. Added
   13 September 2026. As a flex row that wraps, a row broke onto two lines
   whenever its heading and its lead stopped fitting side by side, and that
   happened by content above the breakpoint, measured at layout 925 and 875.
   A grid's tracks cannot wrap, so the row now changes shape once, at 860,
   which is this file's section-level collapse.

   THE TRACKS ARE MEASURED, NOT CHOSEN. 1.64fr 1fr gives the lead 419.703px at
   layout 1280, where the flex row gave it 420, and at layout 846 still leaves
   the longest heading its full single-line width. A first track below about
   1.638fr makes the second wider than the lead's 420px cap above, so the lead
   stops short of the row's right edge; at this value the cap never binds.

   justify-items: start keeps each cell at its content width, as the flex row
   did, which is why the stacked layout below 860 is unchanged. The column gap
   is the 24px main's .section-head gap already computes. The class is doubled
   so it outranks .section-head on specificity rather than on source order, as
   .article-grid.article-grid--4 does. A row with no lead does not carry it. */
.section-head.section-head--split {
  display: grid;
  grid-template-columns: 1.64fr 1fr;
  column-gap: var(--space-lg);
  align-items: end;
  justify-items: start;
}

@media (max-width: 860px) {
  /* One track, heading above lead, at the file's section-level collapse. The
     row gap is main's .section-head gap, the same 24px the wrapped flex row
     used. */
  .section-head.section-head--split { grid-template-columns: 1fr; }
}

/* THE GREEN PHRASE IN BAND 2's HEADING. A page-only class, as .overview-cta
   is, carrying the colour an inline style attribute held until 13 September
   2026. The page comment above band 2 records why colour stands in for
   italic there, and CLAUDE.md rule 8 governs which green token text may take. */
.overview-emphasis { color: var(--profit-green-dark); }

/* THE IMAGE ALTERNATES SIDES BETWEEN BANDS 2 AND 3, and the proportions differ
   with it. Both first shipped image-right at 1fr 1fr, which read as two
   identical rows. The design runs band 2 at 1fr 1fr with the image RIGHT, and
   band 3 at 7fr 5fr with the image LEFT, the seven being the image.

   .wrap--split's own tracks are even, so band 2 needs nothing. This modifier
   carries band 3: it names the tracks and swaps the two column assignments
   .media-split makes, which place text in column 1 and the image in column 2.

   THE MOBILE ORDER IS NOT REVERSED. Below 860 .media-split already stacks
   eyebrow, heading, image, body, and that reading order is right on both bands
   regardless of which side the image takes on desktop. */
.media-split--reverse { grid-template-columns: 7fr 5fr; }

.media-split--reverse > img { grid-column: 1; grid-row: 1 / 4; }
.media-split--reverse > .eyebrow { grid-column: 2; grid-row: 1; }
.media-split--reverse > h2 { grid-column: 2; grid-row: 2; }
.media-split--reverse > .media-split__body { grid-column: 2; grid-row: 3; }

@media (max-width: 860px) {
  /* One column, so every placement above collapses back to .media-split's own
     stacked order. Restated rather than inherited, because the selectors above
     tie with .media-split's own, 0,2,0 against 0,2,0 for the eyebrow and body
     and 0,1,1 against 0,1,1 for the image and heading, and would otherwise hold.

     THE TRACKS ARE RESET HERE TOO, AND LEAVING THEM OUT WAS A DEFECT.
     Corrected 9 September 2026. .wrap--split collapses to 1fr in this same
     block, but .media-split--reverse's own 7fr 5fr sits OUTSIDE any media query
     at equal specificity and LATER in the file, so it won on source order and
     survived the collapse. MEASURED at layout 375 before the fix: the band kept
     two tracks at 188.2px and 98.8px, every child was placed in the first of
     them by the rules below, and the content sat in 188px with 99px of empty
     column beside it while band 2 above used the full 327px. */
  .media-split--reverse { grid-template-columns: 1fr; }
  .media-split--reverse > img { grid-column: 1; grid-row: 3; }
  .media-split--reverse > .eyebrow { grid-column: 1; grid-row: 1; }
  .media-split--reverse > h2 { grid-column: 1; grid-row: 2; }
  .media-split--reverse > .media-split__body { grid-column: 1; grid-row: 4; }
}

/* TWO TIERS INSIDE ONE BAND. The separation is --space-2xl, the same value the
   band's own padding uses, so the two h2 groups read as distinct without a
   second background or a rule between them. */
.tier + .tier { margin-top: var(--space-2xl); }

/* THE DARK PROFIT GREEN RING, AND WHY IT IS A PER-TIER CLASS RATHER THAN A SECTION ONE.
   The shipped ring is two rules: .included-list > li::before paints a
   --profit-green disc, and .additional-support .included-list > li::after lays a
   smaller white circle over it, leaving a ring. On /monthly-bookkeeping
   .additional-support sits on the SECTION, because that page has one list.

   TWO TIERS SHARE ONE SECTION HERE, so a section class cannot tell them apart.
   Both tiers carry .additional-support on their own wrapper div, which gives
   both the ring geometry unchanged, and tier 3 adds .tier-outside beside it.
   ONLY THE DISC COLOUR CHANGES: one declaration, because the geometry is
   already shared. Dark Profit Green measures 5.77 against the card, which
   computes white; the shipped Profit Green measures 2.76, below the 3:1 floor
   but exempt as a decorative marker since each tier sits under its own h2.
   THE RING MOVED OFF TRUST BLUE ON 12 September 2026. Trust Blue is the colour
   of this tier's own card titles, so the marker and the title were one colour.

   THE SELECTORS ARE COMPOUNDED, .additional-support.tier-outside, so they win
   on specificity rather than on source order. Written as .tier-outside alone
   they would tie with .additional-support's own rules at 0,2,0 and depend on
   sitting later in the file, which a future reorder would silently break. */
.additional-support.tier-outside .included-list > li::before {
  background: var(--profit-green-dark);
}

/* THREE ACROSS THEN TWO WIDER CARDS, tier 3 only. Five items on
   .additional-support's repeat(3, 1fr) would strand two at a third width
   against a third of empty row. The Ladder Core section runs repeat(3,1fr)
   then repeat(2,1fr); this expresses the same thing on a six-track grid, which
   is the technique .included-core already uses for its own trailing pair.
   THE TRAILING PAIR IS EXPLICIT nth-child, NOT POSITION FROM THE END, so this
   is tuned to FIVE and degrades away from it, exactly as .included-core's is
   tuned to eight. */
.additional-support.tier-outside .included-list {
  grid-template-columns: repeat(6, 1fr);
}

.additional-support.tier-outside .included-list > li { grid-column: span 2; }

.additional-support.tier-outside .included-list > li:nth-child(4),
.additional-support.tier-outside .included-list > li:nth-child(5) { grid-column: span 3; }

@media (max-width: 860px) {
  /* ONE ACROSS, MATCHING TIER 2 ABOVE IT, AND NOT TWO. This is restated rather
     than inherited because the compounded selectors above are 0,3,0 and beat
     .additional-support's own 0,2,0 collapse on specificity, which a media
     query does not change. Without this block the six-track grid survives here.

     WHY ONE AND NOT TWO, and it was MEASURED WRONG FIRST. This block originally
     kept the six-track grid and set every item to span 3, giving two across.
     Measured on the branch preview at 601, 768 and 860: five items in two
     columns render 2+2+1, stranding the last card alone, while tier 2 directly
     above it was already at one across. So the band showed two tiers at
     different column counts AND a stranded card, which is the exact defect
     .additional-support's own comment records for three items in two columns,
     and the one .steps--cards cites at this same breakpoint.

     THE SPAN VALUES GO TO auto RATHER THAN TO span 6, because the grid is a
     single track here and an explicit span would have to be kept in step with
     it. Both nth-child selectors are restated because they outrank the child
     selector, exactly as .included-core records at its own floor. */
  .additional-support.tier-outside .included-list {
    grid-template-columns: 1fr;
  }
  .additional-support.tier-outside .included-list > li,
  .additional-support.tier-outside .included-list > li:nth-child(4),
  .additional-support.tier-outside .included-list > li:nth-child(5) { grid-column: auto; }
}

/* THE SECURITY BAND IS A WHITE CARD INSIDE THE MINT BAND, split 5fr 7fr, which
   is what the Ladder archive draws. RESTRUCTURED 9 September 2026, replacing a
   full-width stack whose four proof points sat 2x2 beneath it.

   WHAT THE EARLIER RULES DID AND WHY THEY ARE GONE. This section used .grid--2
   for the proof points, and .grid--2 is repeat(auto-fit, minmax(320px, 1fr)),
   which is a FLOOR rather than a count: inside the 1132px wrapper it resolved to
   three columns and stranded the fourth card, and its 320px floor could not fit
   a 305px layout, putting 39px of overflow on the document. Naming the tracks
   fixed both. The points now sit in .security__points, which names its tracks
   for the same reason, so neither defect can return through this route. Both
   were re-measured after the restructure.

   THE CARD'S OWN VALUES ARE THE ARCHIVE'S, matched to tokens: 40px padding is
   --space-xl, the 40px column gap likewise, the radius is --radius-lg and the
   shadow --shadow-sm, all of which .card already uses. */
.security__card {
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  box-shadow: var(--shadow-sm);
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: var(--space-xl);
  align-items: center;
}

/* The left cell: eyebrow, heading and lead. The closing line is not in it: it
   is the card's last child, placed below. */
.security__intro > * { margin-bottom: var(--space-sm); }
.security__intro > *:last-child { margin-bottom: 0; }
.security__intro .section__lead { max-width: none; }

/* The right cell: four proof points, two across, at the archive's 12px gutter. */
.security__points {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-sm);
}

/* THE POINTS LOSE THEIR BORDER AND TAKE A CLOUD FILL. They sat on mint before
   and carried a mint-tinted border so the neutral rule did not read as a seam.
   They now sit on white INSIDE a bordered white card, where keeping the border
   would read as a box in a box. The archive fills them cloud instead, which
   separates them without a second edge. --cloud-gray is an existing token and
   no colour is hard-coded. */
.security__points .card {
  background: var(--cloud-gray);
  border: 0;
  box-shadow: none;
  padding: var(--space-md);
}

.security__points .card h3 { font-size: var(--step-0); }

/* THE CLOSING LINE IS A CHILD OF THE CARD, NOT OF THE LEFT CELL, AND THE THREE
   PIECES ARE PLACED BY LINE. ONE SOURCE ORDER CANNOT EXPRESS BOTH ARRANGEMENTS:
   on desktop the line sits at the foot of the left column beside the proof
   points, and stacked it has to follow them. This is .media-split's pattern
   from section 3: the piece that moves becomes a child of the grid, every
   child is placed by line, and margins rather than the row gap carry the space.

   THREE THINGS .media-split DOES NOT SUPPLY, and each is needed here.
   ALIGN-SELF ON THE TWO COLUMN-1 PIECES. Where the proof points are the taller
   side, their extra height is shared between the two rows, and without end and
   start the intro and the line would each centre in their own row and drift
   apart. ROW-GAP 0 AGAINST THE CARD'S OWN gap SHORTHAND, which sets both axes,
   so this declaration has to come after that one. THE LINE LANDS LAST in the
   markup, because once it is out of the left cell p:last-child is what gives it
   no bottom margin. */
.security__card { row-gap: 0; }

.security__intro { grid-column: 1; grid-row: 1; align-self: end; }
.security__points { grid-column: 2; grid-row: 1 / 3; }
.security__close { grid-column: 1; grid-row: 2; align-self: start; }

@media (max-width: 860px) {
  /* One column: intro, proof points, closing line. Restated rather than left to
     source order, because the placements above would otherwise hold and open an
     implicit second column. The row gap stays 0 and the points carry the space
     above them, the way .media-split's image carries its own. */
  .security__intro { grid-column: 1; grid-row: 1; }
  .security__points { grid-column: 1; grid-row: 2; margin-top: var(--space-xl); }
  .security__close { grid-column: 1; grid-row: 3; }
}

@media (max-width: 860px) {
  /* One column. The 5fr 7fr split has no room here, and the pieces stack in
     reading order: the argument, the evidence, then the closing line. */
  .security__card { grid-template-columns: 1fr; }
  .security__intro { margin-bottom: var(--space-lg); }
}

@media (max-width: 600px) {
  /* The points go one across, and the card sheds most of its padding so the
     content is not squeezed into the middle of a narrow screen. */
  .security__points { grid-template-columns: 1fr; }
  .security__card { padding: var(--space-lg); }
}

/* THE CLOSING BAND KEEPS .section--tight AT THE TOP AND TAKES THE FULL BAND
   PADDING AT THE BOTTOM. The footer adds no space of its own, so this padding
   is the whole boundary between the last band and the footer. .overview-cta is
   a page-only class on that band, because the band's other classes are the
   ones tight bands on other pages wear. It ties with .section--tight on
   specificity and wins on source order, so it must stay below that rule. */
.overview-cta { padding-bottom: var(--space-2xl); }

/* THE CLOSING LINE. One sentence closing the security band, set in the brand
   face so it reads as a statement rather than as another card body. The
   separation is --space-xl, which is what .included-list + .included-close uses
   for the same job, a line of text closing a grid. */
.security__close {
  margin-top: var(--space-xl);
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: var(--step-1);
  color: var(--trust-blue);
}

/* THE CARD LABEL ARROW, THE FIRST ARROW ON THIS SITE WITH A SHAFT. The label
   copy specified Learn more followed by U+2192, a shafted arrow, and that
   glyph is outside every self-hosted face's cmap and declared unicode-range,
   so CLAUDE.md rule 11 kept it out of the text. A bare triangle, the shape of
   .submenu-toggle__caret and .filter-return__mark, would be a different mark
   from the one specified, so this draws a shaft and a head instead.
   IT KEEPS THE HOUSE TECHNIQUE: one box painted currentColor and cut by a
   clip-path polygon, sized in em, so it follows the label's colour in every
   state, scales with the label's font, and puts no character into the text.
   IT CENTRES WITH inline-block AND vertical-align, NOT THE HOUSE inline-flex,
   AND THAT IS DELIBERATE. The label's layout must not move: inline-flex would
   drop its underline and grow the card, so the label stays inline and only
   this box is aligned. middle was chosen by measurement, landing the arrow's
   centre on the centre of the label's x-height. */
.card > span.btn-link::after {
  content: "";
  display: inline-block;
  width: 1.12em;
  height: 0.62em;
  margin-left: 0.4em;
  vertical-align: middle;
  background: currentColor;
  clip-path: polygon(0% 37%, 52% 37%, 52% 0%, 100% 50%, 52% 100%, 52% 63%, 0% 63%);
}


/* --------------------------------------------------------------------------
   13. Print
   -------------------------------------------------------------------------- */

@media print {
  .site-header, .site-footer, .cta-band, .filter-bar, .post-nav { display: none; }
  body { color: #000; font-size: 11pt; }
  a::after { content: " (" attr(href) ")"; font-size: 9pt; }
}
