/* SolaSingles — public/style.css
   The one public stylesheet (Arc 1, static site v1, layout pass 4, batch 4B).
   DO-LIST: hold the one public stylesheet.
   Rules that bind this file:
   - Every color, spacing step, and type size is declared once as a token
     below and referred to by name everywhere else. No raw values in HTML.
   - Direction (locked): white-base, two modes, generous whitespace,
     hairline borders, no gradients, no shadows.

   ACCENT IS PROVISIONAL. Batch 4B (Tina's ruling, 2026-08-22, pending the
   final palette decision) moved the accent off black/white and on to a
   burgundy sampled from the logo artwork. Reverting to black — or moving to
   any other candidate — is a change to THIS ONE TOKEN PAIR and nothing else:
   --accent in :root, and --accent in the dark block. Nothing downstream
   names a burgundy; every consumer names --accent.

   Typeface: the page is one sans throughout. The masthead wordmark is the
   single exception — batch 4B replaced the logo PNG with real text, and
   matching the artwork's letterforms needs a serif (--font-wordmark). That
   stack is a stand-in for the PNG's face; naming the real font is a change
   to that one token.

   IMAGE WEIGHTS (batch 11). Every image Tina supplies arrives as a PNG of
   1.8-2.4 MB. Those originals stay in her Downloads folder untouched; what
   ships in public/ is a re-encode. The rule this batch worked to: any public/
   copy over ~500 KB is re-encoded to a 1200px long edge, and the full-bleed
   banner — which is allowed more because it covers the viewport rather than a
   272px column — is sized for that role and aimed under ~700 KB.

   All six land well inside it. Section images: 1200px long edge, JPEG q82,
   121-158 KB each (from 1.8-2.3 MB, a 92-94% reduction). Banner: no downscale
   needed, kept at its native 1774x887, JPEG q88, 358 KB from 2.4 MB.

   Two files changed EXTENSION in the process, and that is why nothing in this
   file or in the HTML still says .png for them: section-meet and
   profile_banner were PNGs, and no PNG of a photographic image lands within
   these budgets at these dimensions. Both are JPEG now and both old files are
   deleted. The only PNG left in public/ is badge-googleplay.png, which is
   artwork with real transparency and 4.9 KB.

   Layout model (pass 4): sections are full-bleed — they span the viewport
   so their hairline separators run edge to edge — while their content is
   held to --container and centred by the padding-inline rule below.

   The container is sized to EXACTLY fit the wide grid:
       --col-label + --col-gap + --measure  =  --container
       17rem      + 3.5rem    + 49rem       =  69.5rem (1112px)
   so the text column ends flush with the container's right edge and no
   dead space is left over. Changing any one of those four tokens means
   re-checking the sum.

   Unavoidable literals: media queries cannot read custom properties, so the
   breakpoints are written out. Batch 8 reduced them from two to ONE — 62rem —
   and batch 14, CHANGE 8 added a second, 41rem, for the phone masthead alone.
   The two are not independent: 41rem (656px) is the clean rem step below the
   658px one-row/two-row transition batch 8 measured, so both literals mark the
   same masthead behaviour at its two ends. See the CHANGE 8 note below.
   Batch 14, CHANGE 9 deliberately did NOT add a third — the solas scale
   through a min()/calc() pair that is capped at the size they already were,
   so they need no query of their own.

   The solas breakpoint used to be its own figure: 40rem in batch 5B, moved to
   51rem in batch 6 when halving the gaps replaced the elastic space-between
   with a fixed gap, and 51rem (816px, less the 16px gutters) was the narrowest
   viewport that still cleared the row ITSELF. That was the bug batch 8 CHANGE
   3 fixed. Clearing the row is not the same as clearing the row BESIDE THE
   WORDMARK, which is what batch 7 asked of it when the two moved onto one
   line: measured, the spread row needs 730.65px and the wordmark and its
   column-gap take another 179.78px, so the pair needs 910.43px of content
   width, not 784px. Between 816px and roughly 942px the spread form was
   therefore switched on at a width where it could not fit, and the header's
   flex-wrap dropped it to a second row — so the bar went two rows, then one
   at 690px, then BACK to two at 816px, then one again at 960px. Growing the
   viewport made the bar taller, which is the non-monotonic wrap batch 8 was
   sent to remove.

   Moving the figure to 62rem fixes it and retires the literal at the same
   time. 62rem was already the wide-layout breakpoint, so the masthead now
   changes form at exactly the width the page changes to its two-column
   layout, and 62rem (992px, less the 32px gutters that same query switches
   on) leaves 928px of content — 17.57px more than the pair needs. Below it
   the compact form stands, and the compact form is narrow enough to ride the
   wordmark's line from about 660px up. The result is monotonic: the bar goes
   from stacked to one line once and never back. The sweep is in the 62rem
   solas block below.

   Every other value in this file is a named token, except the
   letter-spacing / underline-offset em figures and the icon and badge
   geometry carried inside their SVG viewBoxes, which this file has never
   tokenised. */

@import url("theme.css");

:root {
  /* color */
  /* Sampled originally from the "Sola" letterforms of the logo artwork (modal
     interior pixel, rgb(133,1,27) = #85011b); the artwork is archived beside
     the working folder, not shipped.

     Batch 15e (Tina's ruling, 2026-08-25) — THE ACCENT LEAVES THE ARTWORK'S
     SAMPLE. "We also want to change the accent color burgundy to less purple
     and more burgundy/red. Deeper red, less pink." #85011b -> #800d06, and it
     is a hue rotation held at the same lightness: 348.0° -> 3.4°, which is the
     purple side of red swung to the other side of it. Relative luminance moves
     0.050874 -> 0.048902, a 4% change, which is why every figure below holds.

     REPLACED AT EVERY SITE. Eight occurrences of the old hex in this file, two
     of them live declarations — this one and the dark-block pin batch 14 added
     — and six inside notes recording measurements. All eight moved, and the
     six comment sites had their FIGURES re-measured with them rather than the
     hex being swapped under stale numbers. No other colour literal in this
     file changed; the inventory is identical apart from these eight.

     MEASURED, before -> after:

         white on the masthead bar            10.41:1  ->  10.62:1
         white on the sign-up button fill     10.41:1  ->  10.62:1
         white glyph on a section icon fill   10.41:1  ->  10.62:1
         logo grey #96979a on the bar          3.56:1  ->   3.63:1   (3:1 floor)
         --accent as ink on light --paper     10.41:1  ->  10.62:1

     Every one rises. The only figure that falls is --accent as ink on the
     #0f0f0f dark paper, 1.84:1 -> 1.81:1, and it is not a live figure any
     more: batch 15 moved the last two places --accent was text on dark paper
     onto --link, so every remaining consumer of this token is a FILL. It is
     recorded here so the next batch does not rediscover it as news. */
  /* --accent comes from theme.css, the one token source. */

  /* the callout wash is the page field, not a hue. The burgundy wash is gone. */
  --callout-wash: var(--field);

  /* Batch 14, CHANGE 11 (Tina's ruling, 2026-08-25) — INLINE LINK TEXT GETS ITS
     OWN TOKEN, because the accent is a fill colour and a link is not a fill.

     CHANGE 4 pinned the dark accent to this burgundy and reported what it cost:
     --accent as TEXT on the #0f0f0f dark paper measures 1.84:1, so dark-mode
     links were all but invisible. This is the first of the three levers that
     note named — give dark-mode link text its own token and leave --accent to
     the fills it now serves well. In LIGHT mode nothing moves: --link is the
     accent, 10.41:1 on --paper, exactly as it has always been. In DARK mode it
     becomes --ink, the off-white this file already sets for body text there, at
     17.12:1. No new colour is introduced in either mode; both values are
     tokens this file already declares.

     The underline is what carries the link where the colour no longer does: in
     dark mode the link is now the same value as the prose around it, so it is
     the only thing distinguishing one from the other. It was already there by
     browser default, and `a` below now states it so it cannot be lost to a
     later rule. --accent is untouched as a FILL everywhere it currently is —
     the masthead bar, the sign-up button, the section icons, ::selection. */
  /* the grey sampled from the logo artwork — "Singles", the rule, and the
     solas line all carry the same value, rgb(110,111,113). Two tokens
     because the grey does two different jobs:
       --logo-grey       a FILL (the button's hover state). Mode-independent:
                         it is the artwork's grey, not a text colour.
       --logo-grey-text  the same grey used as TEXT. It clears AA on white
                         (5.03:1) but only reaches 3.81:1 on the dark paper,
                         so the dark block lifts it to a legible value. */
  --logo-grey: #6e6f71;
  --logo-grey-text: var(--logo-grey);

  /* Batch 15b (Tina's ruling, 2026-08-25) — "change the grey in the main logo
     to a lighter grey." This is the artwork's grey lifted, and it is NOT a new
     colour: #96979a is the exact value the dark block below already declares
     for --logo-grey-text, the lift that clears AA on the dark paper. It is
     promoted to :root here so the file holds it once and two rules can name
     it; the dark block now aliases this token instead of repeating the hex.

     It has to be its own token rather than just pointing .mark-singles at
     --logo-grey-text, because that token is MODE-DEPENDENT (the artwork grey
     in light, this lift in dark) and the masthead bar is not — its ground is
     an --accent FILL, which does not invert, so the mark on it must not
     either. That is the same rule --logo-grey, --masthead-label and
     --hero-ink already follow in this file.

     "Singles" on the #800d06 bar: 2.11:1 -> 3.63:1 (re-measured in batch 15e when the bar moved; it read 2.07 -> 3.56 on the old #85011b). The wordmark is 28px at
     weight 700, which is large text and wants 3:1, so this is also the first
     time that mark has cleared its threshold — batch 8's note recorded the
     failure and left the fix as a colour ruling, and this is Tina taking it.
     For scale, --masthead-label white is 10.41:1 on the same bar; if she wants
     the grey lighter still, it is one value on this line. */
  --logo-grey-light: #96979a;

  /* Batch 11, CHANGE 2 — the hero's h1 and lede, on the banner. True black,
     and the SAME value in both modes: the ground under this text is the
     photograph, which does not invert, so the text on it must not either.
     That is the rule --logo-grey and --masthead-label already follow in this
     file — a colour whose ground is a fill or an image is mode-independent —
     and this token is deliberately absent from the dark block below.

     Pure #000000, not a near-black, and the reason is measured rather than
     preferred: against this banner every step away from black measures worse
     (2.63:1 at the median, vs 2.48:1 at #0a0a0a and 2.37:1 at #111111). The
     full table, including the white figure that beats all of them, is in the
     .home section.hero note. It governs the h1 and the lede only.

     (Batch 11 recorded here that the .badge-note under them kept --ink-soft.
     Batch 12 moved it — to --hero-caption, when the panel made its ground
     white in both modes. This token still does not reach it.) */
  --hero-ink: #000000;

  /* Batch 12, CHANGE 1 — the hero panel. Tina's ruling after batch 11
     reported that black on the bare banner reaches only 2.63:1 at the median:
     the lettering cannot win against the photograph directly, so it stops
     trying to and gets its own ground instead. Black on this is 21.00:1.

     SOLID white, no translucency. It is permitted — measured over the region
     the panel covers, white at 95% opacity still leaves black at 18.83:1 at
     the WORST pixel behind it, and even 85% leaves 15.01:1, so every value in
     that range clears AA on 100% of the pixels. It is not taken, because the
     panel exists precisely to stop the photograph reaching the text, and
     letting it back through at reduced strength reintroduces the noise this
     change was ruled to remove. The full table is in the .hero-panel note.

     Mode-independent, and deliberately absent from the dark block, for the
     same reason --hero-ink is: this is a surface laid ON the photograph, the
     photograph does not invert, and a panel that flipped to near-black in
     dark mode would put --hero-ink black on near-black. */
  --hero-panel: #ffffff;

  /* The "Apps available at launch" caption, which now sits on that panel.
     It has to leave --ink-soft behind, and this is a correction the panel
     forced rather than a colour ruling taken on its own: --ink-soft is
     mode-dependent (#6b6b6b light, #9a9a9a dark) and its ground used to be
     --paper, which moved with it. Its ground is now white in BOTH modes, and
     the dark value on white measures 2.81:1 — a clear AA failure for 14px
     normal text at the moment the panel lands.

     So the caption keeps the LIGHT-mode grey in both modes: 5.33:1 on the
     panel, past the 4.5:1 AA Normal wants, and still the muted register batch
     6 chose it for. Pinning the light value is the smallest fix available;
     the alternative was --hero-ink, which would clear AA at 21:1 but make the
     caption as loud as the headline, which is not what it is for. */
  --hero-caption: #6b6b6b;

  /* the sign-up button's label. Resting, the fill is --accent; on hover the
     fill becomes --logo-grey, which only carries a near-white label (5.03:1;
     the dark paper on that grey is 3.81:1 and fails), so the hover label is
     pinned white in both modes.

     Batch 14, CHANGE 4 — the RESTING label is now pinned to the same white,
     and this is forced by the dark accent moving to the burgundy rather than
     chosen. --btn-label was var(--paper), which is #0f0f0f in dark mode: on
     the old pink fill that measured 5.66:1, and on the burgundy it measures
     1.84:1 — a near-black label on a near-black-red fill, which is not a
     legible button. Pinning it to the white this file already pins for the
     hover state introduces NO new colour and is a no-op in light mode
     (--paper is #ffffff there, so the value does not move); the only mode it
     changes is dark, where the label goes 1.84:1 -> 10.41:1. It is an alias,
     exactly as --masthead-label below is. */
  --btn-label: var(--btn-label-hover);
  --btn-label-hover: #ffffff;

  /* Batch 7, CHANGE 1 — the masthead bar became a solid --accent fill, so
     every mark on it is now a label sitting on a fill, exactly the job
     --btn-label-hover already describes: the one white this file pins in
     BOTH modes. This is an alias, not a new value — it introduces no colour;
     it only lets the masthead rules name the white by what it does there.
     The band is gone: the header is white, so the marks take ink. */
  --masthead-label: var(--ink);

  /* Charge six — header 1.5× today's padding + wordmark line box (45.4px →
     68.1px). 1.75rem is the cap of --type-wordmark; --leading-wordmark is 1.05;
     --space-1 is 8px. The 1px hairline stays on the bar and is extra. */
  --header-today: calc(2 * var(--space-1) + 1.75rem * var(--leading-wordmark));
  --header-height: calc(1.5 * var(--header-today));
  --logo-height: calc(var(--header-height) - 2 * var(--space-1));

  /* type
     Stack order is chosen for a TRUE bold (batch 5B, EDIT A). The wordmark
     is set at 700, so every face here must ship a real bold rather than let
     the browser synthesise one. Monotype Garamond and Cormorant are both
     light-by-design — their bolds read weak at display size — so they sit
     late in the list; the old-style faces with sturdy true bolds lead it.
     On Windows this resolves to Palatino Linotype (Palatino Linotype Bold
     ships with the OS); Georgia is the universal backstop. */
  /* Batches 11-12 staged three candidate faces for the hero h1 here, behind a
     removal marker. Tina ruled in batch 13: the headline KEEPS THE FACE IT
     HAD — the body stack, inherited, which is what the real h1 wore
     throughout. The picker and all four of its sites are gone; nothing about
     the headline's type ever moved, and there is no winner's stack to install.
     --font-wordmark above is untouched and is still the masthead's alone. */

  /* the solas line's tracking. Named because the flush-right correction
     below has to cancel exactly one trailing letter-space. */
  --track-solas: 0.2em;

  /* --gap-solas is RETIRED in batch 15, CHANGE 4 and its declaration is gone.
     It held the spread form's fixed inter-sola gap — 44px in batch 6, eased to
     40px in batch 7, switched on at 62rem by batch 8. The spread form was the
     flush-right placement, and Tina's ruling moves the solas hard against the
     wordmark instead, so the compact form now carries every width and nothing
     in this file consumes that gap any more. The history is kept here rather
     than in a token with no consumer; restoring the spread means restoring the
     62rem block and this declaration together. --track-solas below is NOT
     retired — the letter-spacing within each sola is untouched by all of this.

     Batch 15, CHANGE 4 declared --gap-mark-sep here — the double space on
     either side of the masthead hyphen, 12px, derived as 2 * 0.25em of the
     wordmark's 24px phone size. Batch 15c RETIRES it with the hyphen: "get rid
     of the hyphen and move them over." There is no separator left to space
     around, so the gap stops being a derived pair and becomes an ordinary
     single gap between two things on a line — the header's column-gap now
     names --space-1 (8px) off the existing spacing scale and this token is
     gone. 12px -> 8px, and the solas sit 4px nearer the wordmark. */

  /* section-title icons (batch 5B) — sized to sit inside the h2's existing
     line box so adding one cannot raise the line. Only the front page ships
     icons; these govern the shared default. */
  --icon-size: 1rem;
  --icon-gap: var(--space-1);

  /* Batch 6, CHANGE 6 — the front page's icons only (body.home). Filled
     circles carrying a white glyph, in place of the thin-line marks. 56px
     sits inside the 56–64px range asked for; the padding insets the 24-unit
     viewBox to a 32px glyph, ~57% of the diameter, which lets every existing
     glyph shape carry over as white-on-fill without being redrawn. */
  --icon-size-home: 3.5rem;
  --icon-pad-home: 0.75rem;

  /* Batch 6, CHANGE 4 — the real store badges, sized so the DRAWN artwork
     matches at ~40px. Apple's SVG fills its 135x40 viewBox edge to edge, so
     its box height IS its drawn height. Google's PNG is 646x250 carrying 41px
     of transparent clear space on all four sides, so only 67.2% of the box is
     artwork: 3.75rem (60px) draws 40.32px — a shade taller than Apple's 40px,
     never shorter. Drawn widths land at 135.0px and 135.4px. */
  --badge-h-apple: 2.5rem;
  --badge-h-google: 3.75rem;

  /* Batch 10, CHANGE 4 — the zoom view's close control, enlarged and moved on
     to the photograph's own top-right corner. It used to take --space-4 (56px)
     for its box and --type-large (24px) for its glyph, and neither token has a
     next step that lands where an enlarged close control wants to be:
     --space-5 is 96px, which is a section's vertical rhythm rather than a
     control, and --type-hero is a clamp built for the hero headline. So the
     control gets its own pair rather than borrowing a step that means
     something else. 72px box, 36px glyph — 1.29x and 1.5x what they were. */
  --zoom-x-size: 4.5rem;
  --zoom-x-glyph: 2.25rem;

  /* Batch 15, CHANGE 3 — the hero banner's blur radius. The file had no
     `filter:` declaration at all before that batch, so the value it replaced
     was a true 0px. The pseudo-element's negative inset is written as 2 * this
     token, so the bleed that hides the blur's edge fade tracks the radius
     automatically and moving the blur is a change to this one line.

     Batch 15b (Tina's correction, 2026-08-25) — 8px -> 3px. "Too blurred, I
     didn't want it indistinct. Just *slight*." 8px was read as subtle and was
     not: it took the tiles to the edge of legibility. At 3px the faces and the
     tile edges stay clearly readable and the blur reads as a soft focus rather
     than a wash — the collage is still a collage, just off the sharp plane.

     Stated in px rather than rem on purpose, and this is the one place in the
     file where that is right: a blur radius is a paint distance, not a spacing
     step, so it has no business tracking the root font size. 0.1875rem would
     be the same number said less clearly. */
  --hero-blur: 4px;
  --hero-slide: 34s;

  /* type sizes */  /* Batch 14, CHANGE 7 — the hero headline steps down one size so it still
     breathes inside the smaller panel. Was clamp(2rem, 4.5vw, 3.5rem); every
     one of the three figures comes down by the same ~0.79, so the clamp keeps
     its shape and only its scale moves. At 1440px the headline draws 44px
     rather than 56px. This token has exactly one consumer, .hero h1, so no
     other heading on the site moves with it. */  /* Batch 6, CHANGE 1 — the masthead was compressed to roughly a third of
     its height (187.66px measured, 64.8px after), and the wordmark comes
     down with it. At 28px it is still twice the solas line and clearly the
     dominant element in the band; the old figure was sized to the cap height
     of the retired logo PNG, which no longer constrains anything. */
  /* spacing steps. --space-0 is new in batch 6: the compressed masthead
     needs a step below --space-1 to reach its height target. */
  /* layout */  --col-label: 17rem;
  --col-gap: var(--space-4);  --measure-hero: 16em;

  /* Batch 12, CHANGE 1 — the hero panel's outer width, sized TO THE CONTENT
     rather than to the viewport, which is what the brief asked for. It is
     derived, not chosen: the widest thing on the panel is the lede at
     --measure-lede, and the panel adds --space-4 of padding on each side.

         --measure-lede + 2 * --space-4  =  40rem + 7rem  =  47rem  (752px)

     so the lede fills its own measure exactly and the padding is real space
     rather than a squeeze on the text. Changing either input means redoing
     this sum, exactly as --container's note says of its own.

     Below 62rem the panel's horizontal padding drops to --space-3 and this
     cap stops binding — the panel is narrower than 47rem there anyway and
     takes the width its gutters leave it.

     Batch 14, CHANGE 7 — the panel is reduced by roughly a third. The sum is
     REDONE, not overridden, exactly as the note above requires of it: both
     inputs move, and the panel's padding is now --space-3 at EVERY width
     rather than opening to --space-4 above the breakpoint, so there is one
     figure instead of two.

         --measure-hero-lede + 2 * --space-3  =  34rem + 4rem  =  38rem (608px)

     "Roughly a third" is read as the panel's FOOTPRINT, and the reduction is
     shared between the two dimensions rather than taken out of the width
     alone — a third off the width would have left a 501px panel under a
     headline, which reads as a column, not a panel. Five levers, all of them
     one step each: the width here, the padding on .hero-panel, --type-hero
     stepped down, the hero lede stepped from --type-large to --type-base, and
     the gap under that lede stepped from --space-4 to --space-3.

     MEASURED, not derived — every figure below is a getBoundingClientRect off
     the rendered page, before and after, at ten widths:

         viewport   panel before      panel after       area
          1440px    752.0 x 581.0     608.0 x 508.8    -29.2%
           992px    752.0 x 568.5     608.0 x 499.7    -28.9%
           768px    736.0 x 557.4     608.0 x 491.2    -27.2%
           656px    624.0 x 589.4     608.0 x 491.2    -18.8%
           500px    468.0 x 659.0     468.0 x 521.6    -20.8%
           360px    328.0 x 877.2     328.0 x 722.5    -17.6%

     The wide end is the figure the brief was written against and it lands at
     roughly a third. The narrow end lands at roughly a fifth, and the reason
     is structural rather than a shortfall: below about 640px the panel has
     never taken --measure-panel at all — it takes the width the --gutter edges
     leave it, which this change does not touch — so all of the reduction down
     there is height and none of it is width. Making the phone panel narrower
     would mean insetting it from its own gutters, which is a different change
     from the one asked for and is not taken here.

     The banner still shows on all four sides at every width, and with more of
     it visible than before, so nothing about CHANGE 1's contract weakens. The
     panel is a solid #ffffff and --hero-ink is #000000, so the black-on-white
     inside it is 21.00:1 — the sRGB maximum, untouched by this change. */
  --measure-hero-lede: 34rem;
  --measure-panel: 38rem;}

@media (prefers-color-scheme: dark) {
  :root {
    /* the wash carried at a higher alpha, because 3.5% of anything is invisible
       on near-black. Same hue, same token, one value per mode. */
    --callout-wash: var(--field);

    /* Batch 14, CHANGE 4 (Tina's ruling, 2026-08-25) — THE DARK ACCENT IS
       PINNED TO THE LIGHT BURGUNDY. It used to be #d9677f, the light burgundy
       lifted in lightness and eased off saturation so it would not glare on
       near-black. Lifted that far it is a pink, and the brief is absolute:
       no pink anywhere on the site, either mode, any screen width.

       This is the SAME value as :root's --accent, #800d06, restated here
       rather than deleted so the "one token pair" contract in the header note
       still holds — moving the dark accent is still a change to this one line.
       No new colour is introduced and no filter or opacity is involved: there
       is not one `filter:` or `opacity:` declaration in this file, so the pink
       was never a lightened burgundy, it was a second token holding a pink.

       WHAT THIS FIXES, measured (the fill jobs — masthead bar, sign-up button,
       section icons — where --accent is a ground rather than text):

           white label on the bar / button / icon   3.39:1  ->  10.41:1
           --logo-grey "Singles" on the bar         1.48:1  ->   2.07:1

       WHAT IT COST, and how it was settled. --accent is also TEXT in two
       places — `a` (inline links) and `.zoom-x:hover` — and on the #0f0f0f
       dark paper the burgundy measures 1.84:1, down from the pink's 5.66:1.
       There is no burgundy-only fix for that: a colour dark enough not to read
       as pink cannot also carry text on near-black. This note originally left
       the three levers open as colour rulings; Tina took the first of them the
       same day, and it is CHANGE 11 below — inline link text gets its own
       token, --link, which is the accent in light mode and --ink in dark, and
       the underline is stated rather than inherited. 1.84:1 -> 17.12:1.

       STILL OPEN, and deliberately not taken here: --accent is text on the
       dark paper in two remaining places, both of them transient states rather
       than resting copy — the `a:focus-visible` outline and the `.zoom-x:hover`
       glyph. Both still measure 1.84:1 in dark mode. CHANGE 11 was ruled for
       inline links and is applied to inline links only; extending --link to a
       focus ring and a hover state is a further colour ruling and is Tina's.

       CLOSED — batch 15, CHANGE 1 is Tina taking exactly that ruling. Both of
       those two sites now name --link instead of --accent, so both go
       1.84:1 -> 17.12:1 in dark mode and neither moves in light. There is no
       remaining place where --accent is text on the dark paper. */
    /* --accent comes from theme.css, the one token source. */

    /* Batch 14, CHANGE 11 — inline link text takes the body ink here, not the
       accent. See the --link note in :root. --ink is #f2f2f2 above, so this
       introduces no value of its own; it only says which existing one a link
       wears on dark paper. 17.12:1, against the 1.84:1 the accent gave. */
    /* the artwork grey lifted to clear AA on the dark paper (6.56:1),
       keeping the original's faint cool cast. --logo-grey itself is NOT
       overridden: as a fill it stays the sampled value.

       Batch 15b — the hex moved to :root as --logo-grey-light and this is now
       an alias to it. Same value, same 6.56:1; it is declared once because the
       masthead's "Singles" took the same lift and the file should not carry
       one colour in two places. */
    --logo-grey-text: var(--logo-grey-light);
  }
}

/* base */
* {
  box-sizing: border-box;
}

html {
  background: var(--field);
}

body {
  margin: 0;
  padding: 0;
  background: var(--field);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--type-base);
  line-height: var(--leading);
}

/* Batch 14, CHANGE 4 — the selected text follows --btn-label for the same
   forced reason: this is a label sitting on an --accent FILL, and var(--paper)
   on the pinned dark burgundy is 1.84:1. No new colour, no-op in light mode. */
::selection {
  background: var(--accent);
  color: var(--btn-label-hover);
}

/* Batch 15, CHANGE 1 (Tina's ruling, 2026-08-25) — the focus ring takes --link,
   which closes the first of the two dark-mode items batch 14, CHANGE 4 left
   open. On the #0f0f0f dark paper the accent burgundy measured 1.84:1 as a
   ring; --link is --ink there, #f2f2f2, which measures 17.12:1. In LIGHT mode
   --link IS --accent, so this declaration renders the identical burgundy it
   always has and nothing on a white page moves. No new colour literal: the
   value on both sides is a token this file already declares. Inside the
   masthead bar the header override below still wins and the ring is white. */
a:focus-visible {
  outline: var(--line) solid var(--link);
  outline-offset: var(--space-1);
}

/* the container rule — full-bleed block, contained content.
   Everything that spans the viewport gets its content centred here. */
header,
footer,
section,
main > h1,
main > .lede,
main > .backlink {
  padding-inline: max(var(--gutter), (100% - var(--container)) / 2);
}

/* masthead — the wordmark as real text, far left, linking home, and the
   solas line beneath it. Batch 4B replaced the two logo PNGs, which no
   longer ship with the site; the originals are archived beside the working
   folder. Batch 5B set the wordmark bold and justified the solas across the
   container from 40rem up.

   Batch 6, CHANGE 1 + CHANGE 2 — the band was compressed to roughly a third
   of its height. Measured at a 1250px viewport, before and after:

       padding-top          32.00  ->   4.00   (--space-3 -> --space-0)
       wordmark line box    59.39  ->  29.40   (54px/1.1 -> 28px/1.05)
       solas margin-top     16.00  ->   4.00   (--space-2 -> --space-0)
       solas line box       22.39  ->  22.39   (unchanged — typography locked)
       ornament margin-top  16.00  ->      —   (removed, CHANGE 2)
       ornament box          8.88  ->      —   (removed, CHANGE 2)
       padding-bottom       32.00  ->   4.00   (--space-3 -> --space-0)
       border-bottom         1.00  ->   1.00
                           ------      -----
                           187.66  ->  64.80   (34.5% of the original)

   The ornament's 24.88px is counted once, here, as CHANGE 2's contribution
   to this target. Its rules are gone from this file and its markup from all
   four pages.

   Batch 7, CHANGE 1 + CHANGE 3 — the band becomes a solid --accent fill
   carrying white marks, and the wordmark and the solas move onto ONE line.

   CHANGE 1. The fill is --accent, so it follows the mode exactly as every
   other consumer of that token does; nothing here names a burgundy. All four
   marks on the bar — both halves of the wordmark, the solas, and the pipes —
   take --masthead-label. Two of them (.mark-sola and .solas .sep) carried
   --accent before and would now be invisible on their own ground, so this is
   not only Tina's ruling, it is forced. Measured on the composited bar:
   10.41:1 light, 3.39:1 dark. See the dark-mode note under .solas.

   The bottom hairline is REMOVED. It was there to part the band from the
   paper below it; a solid fill already draws that edge harder than a 1px
   --hairline can, and in dark mode a #2a2a2a line under a light-pink bar
   reads as a grey smudge rather than a separator. Nothing replaces it.

   CHANGE 3. The band is a flex row: wordmark left, solas taking the rest of
   the width. align-items:center centres the two against each other, and the
   symmetric padding-block centres the pair in the bar. Height, measured at a
   1250px viewport:

       padding-top          4.00  ->   8.00   (--space-0 -> --space-1)
       tallest child       29.40  ->  29.40   (the wordmark, 28px/1.05;
                                               the solas' 22.39px box now
                                               sits inside this, not below)
       solas margin-top     4.00  ->      —   (removed — same line now)
       solas line box      22.39  ->      —   (absorbed above)
       padding-bottom       4.00  ->   8.00   (--space-0 -> --space-1)
       border-bottom        1.00  ->      —   (removed, see CHANGE 1)
                           -----      -----
                           64.79  ->  45.40

   The padding goes UP a step on purpose: --space-0 was sized for a band
   whose two stacked rows already filled it, and 4px of air above a single
   line inside a coloured bar reads as a crop. --space-1 gives the line room
   without giving back the height CHANGE 3 just saved.

   flex-wrap:wrap is the narrow-viewport contract: below the width where the
   pair fits, the solas takes its own row rather than clipping or forcing the
   document to scroll sideways. See the sweep note in the 62rem solas block.

   Batch 15, CHANGE 4 (Tina's ruling, 2026-08-25) — THE SOLAS MOVE OVER NEXT TO
   THE LOGO, with a hyphen between. "We want to move the solas over next to the
   logo with a hyphen between the logo and the solas, maybe double space on
   either side of the hyphen."

   The line itself does not change — the same nine spans, the same string, the
   same tracking, the same --masthead-label white. What changes is where it
   sits: it used to take all the width the wordmark left it (flex:1 1 auto) and
   push its content to the far right edge, mirroring the wordmark across the
   band. It now shrink-wraps its own text and sits immediately after the
   wordmark, so the pair reads as one continuous masthead line from the left
   gutter. That flush-right placement is what the ruling calls the old one, and
   it is REMOVED rather than overridden: .solas loses its flex-grow here and
   the whole 62rem spread block is gone from this file.

   column-gap drops --space-3 (32px) for --gap-mark-sep (12px) — the gap is now
   the double space around the hyphen rather than the space between two things
   holding opposite ends of a bar. See that token for where 12px comes from.

   THE HYPHEN LIVED INSIDE .solas, as its first child, so it always travelled
   with the line it introduced rather than being stranded at the end of row one.

   BATCH 15c — THE HYPHEN IS GONE, and the gap closes with it. "Get rid of the
   hyphen and move them over." The span is out of all four pages, its rule and
   its token are out of this file, and the display:none that hid it below the
   breakpoint is out of the query. column-gap goes 12px -> --space-1 (8px): the
   12px was half of a derived double space around a separator, and with no
   separator there is nothing to space around — 8px is one gap between two
   things on a line, off the spacing scale this file already keeps, and the
   solas sit 4px nearer the wordmark than they did.

   WIDTHS, re-bisected to the pixel on all four pages with the hyphen gone. The
   compact line draws 400.0px at its --type-micro cap and the wordmark measures
   126.7px at its 24px phone size, so the pair needs 534.7px plus the two 16px
   gutters:

       <= 566px   two rows   wordmark centred, solas centred beneath
       >= 567px   one row    the pair together, flush left in the gutter

   ONE boundary now, where batch 15 had two — the second one existed only to
   decide whether the hyphen showed, and there is no hyphen. Every earlier
   figure for the record: batch 15 read 652 / 653-672 / 673, batch 15b read
   570 / 571-592 / 593, and this batch reads 566 / 567.

   The 35rem query below draws it. 35rem is 560px, the clean rem step under the
   566px seam and wholly inside two-row territory — the same derivation batch
   14 wrote and the third value this one literal has carried.

   The two-row form is the SAME flex-wrap contract batch 7 wrote, not a
   placement any of this adds back: there is no width at which a 400px line and
   a 127px wordmark both fit inside a 500px phone, and the type is already
   scaling by the min() rather than wrapping to a further row.

   Zero overflow and zero horizontal document scroll at every width. */
header {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  column-gap: var(--space-1);
  min-height: calc(var(--header-height) + var(--line));
  padding-block: var(--space-1);
  background: var(--paper);
  border-bottom: var(--line) solid var(--hairline);
}

/* the focus ring is --accent site-wide, which on an --accent fill is no ring
   at all. Inside the bar it takes the same white the marks take. */
header a:focus-visible {
  outline-color: var(--masthead-label);
}

.mark {
  display: flex;
  align-items: center;
  min-width: 0;
  flex: 1 1 auto;
  font-family: var(--font-wordmark);
  text-decoration: none;
}

.mark img {
  display: block;
  height: var(--logo-height);
  width: auto;
  max-width: 100%;
  object-fit: contain;
  object-position: left center;
}

/* Batch 15g, CHANGE 1 — THE WORDMARK LINKS HOME, AND NOW BEHAVES LIKE A LINK.
   The anchor itself is not new: `<a class="mark" href="index.html">` has been
   the markup on every page since batch 4B, index.html included, and it is
   deliberately NOT special-cased there — the home page's wordmark points at
   home like every other, which is what a masthead wordmark does everywhere on
   the web and is one less conditional in nine files.

   What was missing was the LINK TREATMENT. .mark declares text-decoration:none
   and had no hover or focus state at all, so the one link on the bar that
   every visitor recognises as clickable gave no sign it was. This rule is a
   copy of .mast-link:hover/:focus-visible above — same underline, same 0.2em
   offset, same --line thickness — so the wordmark and the nav answer the
   pointer identically and nothing new is invented for it.

   RESTING STATE IS UNTOUCHED, which is the ruling: no underline at rest, no
   colour change. (As written in batch 15g that read "Sola" still
   --masthead-label white and "Singles" still --logo-grey-light; batch 21 took
   "Singles" to --masthead-label too, so both halves are now white. The point
   of the sentence is unchanged — this rule still adds nothing but hover and
   focus — but the file should not describe a split it no longer has.)

   text-decoration-color is stated rather than left to inherit, and that is the
   one declaration here with no counterpart on .mast-link. It was pinned when
   the wordmark was the only link on the site whose two halves were DIFFERENT
   COLOURS; batch 21 made both halves white, so the underline would now inherit
   the right colour by accident. It stays pinned anyway. An underline set on
   .mark is painted in .mark's own colour, which .mark never declares — it
   would come from whatever header happens to inherit, which is not a thing
   this file should be deciding by luck, and it is still not, whether or not
   the mark is two-tone this week. Pinning it to --masthead-label draws ONE
   white rule under both halves. It is the same white both halves now carry, so
   no new token and no new literal.

   The focus RING needs nothing here: `header a:focus-visible` above already
   re-points --link to --masthead-label for every anchor inside the bar, and
   the wordmark is one. White on the #800d06 bar is 10.62:1, and because batch
   14 pinned the dark --accent to the light burgundy that figure is the same in
   BOTH modes.

   Batch 20 — THE HOVER UNDERLINE COMES OFF, per Tina's ruling, and :hover is
   gone from the selector below. The wordmark is the one link on the bar that
   nobody needs taught is clickable, and the rule above got that wrong.

   :focus-visible STAYS, and the distinction is not a technicality. Hover is a
   convenience for a pointer that is already on the target; focus is how a
   keyboard user knows where they are, and it is the only thing standing
   between them and an invisible position on the bar. Removing hover costs a
   mouse user nothing. Removing focus would strand a keyboard user, so it is
   left exactly as it was — same white underline, same offset, same thickness.
   .mast-link and .mast-menu-link are NOT touched; they keep hover and focus
   both, which is the "other nav links keep whatever they do now" of the ruling. */
/* Charge six: the wordmark is an image. Focus is the header outline, not an
   underline under letterforms. */

/* Batch 7, CHANGE 1 — the artwork's burgundy/grey split is retired ON THE
   BAR by Tina's ruling: both halves take --masthead-label. The two classes
   are kept rather than merged, so restoring the split is a change to these
   two declarations and nothing structural.

   Batch 8, CHANGE 5 (Tina's ruling, 2026-08-22) — half of that split comes
   back. "Sola" keeps --masthead-label; "Singles" returns to --logo-grey, the
   artwork's grey. Batch 7 kept the two classes apart for exactly this, so the
   change is the one declaration below and nothing structural. The solas line
   is untouched and stays --masthead-label.

   --logo-grey, not --logo-grey-text, is the correct token: this is the grey
   sitting on a coloured FILL, which is the job this file already documents
   --logo-grey as doing. It is mode-independent by design, so the wordmark's
   second half renders rgb(110,111,113) in both modes while the bar under it
   moves with --accent.

   CONTRAST, stated plainly rather than worked around. Batch 14, CHANGE 4
   pinned the dark --accent to the light burgundy, so there is now ONE bar
   colour rather than two and this note has one figure rather than a pair:
   #6e6f71 on #800d06 measures 2.11:1, in both modes (2.07:1 on the old #85011b bar; batch 15e moved the ground, not the grey). The wordmark is 28px at
   weight 700, which is large text and wants 3:1, so it still does not clear —
   the grey is only a little darker than the bar, so it sits close to its
   ground. What changed is the dark mode, which was 1.48:1 on the old pink bar
   and is now 2.07:1 like the light one. For comparison, the --masthead-label
   white this replaces measures 10.41:1, also now in both modes (it was 3.39:1
   in dark).

   Nothing here is silently adjusted to make those numbers pass. The ruling is
   Tina's, it was issued with these figures owed back to her, and her eyes
   decide. Moving the result means moving --logo-grey (which is the artwork's
   own sampled value), lifting the wordmark's grey the way --logo-grey-text
   already lifts the body grey, or putting this declaration back to
   --masthead-label. All three are colour rulings, so none is taken here.

   BATCH 15b — TINA TOOK THE SECOND OF THOSE THREE. "Change the grey in the
   main logo to a lighter grey." So "Singles" moves off the artwork's sampled
   --logo-grey and onto --logo-grey-light, the lift this file already carried
   for dark-mode body text, promoted to :root so the value is declared once.
   The figure the paragraph above owed her: 2.11:1 -> 3.63:1 on the #800d06
   bar, so the 28px/700 wordmark now clears the 3:1 that large text wants, for
   the first time since the bar became a fill in batch 7. (Both are batch 15e
   re-measurements against the new bar; on the #85011b they replaced they read
   2.07:1 -> 3.56:1, and the ruling was taken on those.)

   Mode-independent, and it stays that way — see the token note for why this is
   --logo-grey-light and not --logo-grey-text. --logo-grey itself is untouched
   and is still the artwork's value wherever it is a FILL (the button's hover
   state). "Sola" is untouched and still --masthead-label white; the split the
   two classes exist to hold is intact, and it is now a white/light-grey pair
   rather than a white/mid-grey one.

   BATCH 21 — THE LOGO GOES ENTIRELY WHITE, all parts, everywhere it appears.
   That is the THIRD of the three moves the batch-8 note above listed and left
   for Tina ("or putting this declaration back to --masthead-label"), so this
   is not a new idea in the file, it is the option that was already written
   down waiting for a ruling.

   Both halves now take --masthead-label, which resolves to --btn-label-hover,
   #ffffff. No new token and no new literal: this is the same white "Sola" has
   carried since batch 7 and the same white the solas line and .mast-link take,
   so the whole bar is now one ink. The wordmark is the only place the logo
   appears — one `<a class="mark">` in the header of each of the twelve pages,
   and there is no SVG or image logo anywhere in public/ — so "everywhere it
   appears" is these two declarations and nothing else. (.sec-icon's
   --logo-grey fills on index.html are section icons, not the logo, and are
   deliberately untouched.)

   CONTRAST: 3.63:1 -> 10.41:1 on the #800d06 bar, in both modes, matching
   "Sola" exactly. The 28px/700 wordmark wanted 3:1 and already cleared it;
   this is not a fix, it is a ruling, and it happens to raise the figure.

   THE TWO CLASSES ARE STILL KEPT APART, for the same reason batch 7 kept them:
   restoring any split is a change to one declaration and nothing structural.
   --logo-grey-light is NOT removed — it is still live at --logo-grey-text in
   the dark-mode block, so the token keeps a job. */
/* Charge six: .mark-s, .mark-sola and .mark-singles went with the text
   wordmark. The PNG is the mark; nothing else used those three rules. */

/* the solas banner — the artwork's solas line, set as text.
   DEFAULT (narrow): the compact centred form, allowed to wrap. The spread
   form is switched on at the 62rem breakpoint below, where there is room for
   it BESIDE THE WORDMARK; see the header note on the one breakpoint literal
   and on why batch 8 moved this figure.

   Batch 8, CHANGE 3 — the compact form now carries every width below 62rem,
   including the 816px-to-992px band the spread form used to claim and could
   not fit. On that band the compact line rides the wordmark's line and is
   centred in the width the wordmark leaves, rather than sitting flush right
   on a second row. That is the visible trade the fix makes: one row instead
   of two, centred instead of flush right, between roughly 660px and 992px.
   Nothing about the typography moved — same size, same tracking, same wrap.

   Batch 7, CHANGE 3 — the top margin goes: this is a flex item on the
   wordmark's line now, not a block stacked under it, and align-items on the
   header does the vertical centring that margin used to fight.

   flex:1 1 auto is what makes the two placements agree. Sharing the line, it
   takes the width the wordmark leaves and pushes its content to the far end
   of it (justify-content in the 62rem block); wrapped to its own row, it
   takes the whole header width and pushes to the same edge. The solas
   therefore sit flush right against the container's right edge in both
   states, mirroring the wordmark on the left — the same edge-to-edge
   discipline the wide layout already uses for its flipped section labels.
   Centring them in the leftover width was the alternative and was rejected:
   it lands the row a little right of the page's optical centre, which reads
   as a near-miss rather than a placement.

   Batch 7, CHANGE 1 — the line and its pipes take --masthead-label. The
   pipes lose their --accent, which on an --accent bar was invisible.

   DARK-MODE CONTRAST — THIS ONE IS FIXED. It used to read: white on the dark
   --accent (#d9677f) measures 3.39:1, so this 14px line cleared AA Large but
   NOT AA Normal in dark mode, and the note closed by saying the fix was to
   move the dark --accent, which was a colour ruling and so was not taken.
   Batch 14, CHANGE 4 is Tina taking it — for the pink, not for this — and the
   contrast came with it. White on #800d06 is 10.62:1, now in both modes, so
   the line clears AA Normal everywhere and the exception this note recorded
   is gone. */
.solas {
  margin: 0;
  /* Batch 15, CHANGE 4 — flex-grow goes. 1 1 auto made this box take all the
     width the wordmark left and pushed the text to the far end of it; 0 1 auto
     shrink-wraps the text so the line begins where the hyphen puts it, right
     next to the wordmark. flex-shrink stays 1 so a narrow row still hands the
     box to flex-wrap rather than overflowing it. text-align:center below is now
     reached only when the line is on its own row and its text wraps inside it. */
  flex: 0 1 auto;
  max-width: none;
  text-align: center;
  /* Batch 14, CHANGE 9 — THE SOLAS SCALE WITH THE VIEWPORT INSTEAD OF WRAPPING.
     Batch 8's width sweep recorded the fallback this replaces: from 360px to
     499px the compact line could not be made to fit one row at 14px, so it
     wrapped to two and the masthead stood three rows tall. The brief rules
     that out — the five solas fit one line on a phone — and the lever it names
     is the type, not the wrap.

     The figure is measured, not guessed. The line's width is exactly linear in
     its font-size (rendered at 14/13/12/11/10px it draws 466.58 / 433.30 /
     399.91 / 366.64 / 333.38px), which is 33.327px of width per 1px of type.
     Below the one-row breakpoint the line has its own row and the whole
     content width to use, so what it has to fit is 100vw less the two
     --gutter (1rem) edges:

         fit  =  14px * (100vw - 32px) / 466.58px  =  3.0006vw - 0.9602px

     and the declared 2.96vw - 0.95px sits just inside it, leaving 4-6px clear
     at every phone width rather than landing flush on the gutter.

     min() caps it at --type-small, which is the size this line has always been,
     so NOTHING ABOVE 505px MOVES: the cap binds from 505px up, the line needs
     498.6px there, and every measurement in the batch 8 sweep from 500px up
     still holds. That is why this needs no breakpoint of its own and does not
     add a third literal to the file. The trade the brief accepts is at the
     bottom: 9.71px at 360px and 8.52px at 320px, small type rather than a
     wrapped row. There is deliberately NO floor — a floor would put the wrap
     back the moment the viewport went under it.

     Batch 15b, CHANGE 3 — THE CAP STEPS DOWN, --type-small -> --type-micro.
     "I think we should shrink the solas." One step down the type scale this
     file already declares, so no new size literal appears: 14px -> 12px, and
     against the wordmark's 28px ceiling the line goes from half its size to
     three sevenths of it. The vw term is UNTOUCHED — it is the phone fit
     formula and it still governs below the cap, so the narrow end of the range
     is exactly what batch 14, CHANGE 9 measured it to be (9.71px at 360px,
     8.52px at 320px, one line, no wrap). What moves is where the cap starts
     binding: 12px needs 400.0px of row, so it binds from 432px up rather than
     505px, and every width above that draws 12px. */
  font-size: min(var(--type-micro), calc(2.96vw - 0.95px));
  /* Batch 15c — 1.6 -> 1.2. This is a HEIGHT fix, not an alignment one: the
     baselines were already level and the ink already sat on the wordmark's
     base line. 1.6 left 2.19px of half-leading hanging under the line, which
     was the only thing in the bar reaching below the wordmark's own box and
     the whole of the 0.80px the bar had grown. See the align-items note on
     the header rule for the full measurement table. */
  line-height: 1.2;
  letter-spacing: var(--track-solas);
  color: var(--masthead-label);
}

.solas .sep {
  color: var(--masthead-label);
}

/* each sola stays on one line, so "DEO GLORIA" can never break in half */
.solas .sola {
  white-space: nowrap;
}

/* Batch 15's .solas .mark-sep rule stood here — the hyphen between the logo
   and the solas, inheriting the line's colour and size and carrying the right
   half of the double space. Batch 15c removes it: "get rid of the hyphen and
   move them over." The span is gone from all four pages, this rule is gone,
   --gap-mark-sep is gone, and the display:none the 37rem query used to hold for
   it is gone with the rest. The whole hyphen apparatus was three declarations
   and one media-query line, and all four are removed together rather than left
   inert — a rule with no markup to match is the kind of thing that gets read as
   live two batches later.

   Batch 15c, CHANGE 3 — THE MASTHEAD NAVIGATION, right-hand end of the bar.
   "In the right side do a fake (but authentic) Articles  Register Login."

   FAKE, AND MARKED AS SUCH. Every href is "#": there is no articles page, no
   sign-in and no registration, and nothing behind any of the three exists.
   Each anchor carries its own PREVIEW-ONLY FAKE marker in the HTML — three
   markers for three separately-fake links, which is the only reason this
   batch's count moves 2 -> 5. The nav ships on the front page ALONE; the three
   inner pages keep the bare masthead, which is what holds the count at 5.

   WORDING, and it is DC's call under Tina's delegation, reversible on her
   word. She wrote "Articles Register Login"; this ships Articles, Sign in,
   Register. Two changes, both about voice rather than function. "Sign in"
   because the site's copy is plain English throughout and "Login" is the noun
   used as a verb. And Register LAST, so it reads as the pair every product
   masthead sets — the way back in, then the way to start — rather than sitting
   second where it would compete with the hero's own sign-up button for the one
   thing the page is actually asking a visitor to do.

   TYPE — no new tokens. It takes exactly what the solas take: --type-micro,
   --masthead-label white, the same 0.2em tracking, uppercased. That is what
   "matching the bar's existing type treatment" means here, and it is why the
   nav reads as part of the bar rather than as something laid on it.

   margin-inline-start:auto is the whole of the positioning: the wordmark and
   the solas keep their gap at the left gutter, and the nav takes the rest of
   the line and sits against the right one. It is a flex item on the same
   baseline as the other two, so all three sit on one line rather than the nav
   floating in the middle of the bar's height. (Three FLEX ITEMS — wordmark,
   solas, nav — which is a different three from the three links, and unaffected
   by the count of links changing below.)

   TWO LATER RULINGS AMEND THE PARAGRAPHS ABOVE without changing a declaration
   in this block. Batch 15e gave Articles a real page, so that anchor stopped
   being fake and lost its marker. Batch 15f takes Register out altogether —
   Tina, on the delegated wording and reversing it: "What about just Articles
   and Sign In?" The nav is now TWO items, Articles then Sign in, in that order
   and with that treatment, so the whole case for Register's position above is
   moot because there is no Register. Nothing in this file was ever Register's
   alone — .mast-nav and .mast-link are written for the items generally,
   whatever their number — so nothing here comes out with it, and no rule is
   left standing with no markup to match. */
.mast-nav {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  margin-inline-start: auto;
  column-gap: var(--space-2);
  row-gap: var(--space-0);
  flex-shrink: 0;
}

.mast-register {
  padding: var(--space-1) var(--space-2);
  letter-spacing: var(--track-caps);
  flex-shrink: 0;
}

.mast-fold {
  position: relative;
}

.mast-fold-summary {
  list-style: none;
  cursor: pointer;
}

.mast-fold-summary::-webkit-details-marker {
  display: none;
}

.mast-fold-bars {
  display: block;
  width: var(--space-2);
  height: var(--line);
  background: var(--ink);
  box-shadow: 0 var(--space-0) 0 var(--ink), 0 var(--space-1) 0 var(--ink);
}

.hero-solas {
  margin: 0 0 var(--space-2);
  letter-spacing: var(--track-caps);
}

@media (min-width: 41rem) {
  .mast-fold-summary {
    display: none;
  }

  .mast-fold-panel {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--space-2);
  }
}

@media (max-width: 40.98rem) {
  .mast-fold:not([open]) .mast-fold-panel {
    display: none;
  }

  .mast-fold[open] .mast-fold-panel {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    position: absolute;
    inset-inline-end: 0;
    top: calc(100% + var(--space-1));
    z-index: 2;
    min-width: var(--measure-photo);
    padding: var(--space-1);
    background: var(--paper);
    border: var(--line) solid var(--hairline);
  }
}

/* The links themselves. text-decoration:none is what makes these read as
   navigation rather than as prose links — the `a` rule states an underline for
   inline links, and this is the fourth place in the file that opts out of it,
   alongside .mark, .notify and .zoom-x. The underline comes back on hover and
   focus, so the affordance is not lost, it is just not the resting state.

   The focus ring is NOT restated here on purpose. `a:focus-visible` gives it
   --link, and `header a:focus-visible` above already re-points that to
   --masthead-label for everything inside the bar — which is required, not
   decorative: --link is --accent in light mode, and an --accent ring on an
   --accent bar is 1.00:1, no ring at all. White on #800d06 is 10.62:1 in BOTH
   modes, so these tab stops are ringed identically light and dark. (Two of
   them since batch 15f, three when this was written; the ratio is a property
   of the colours, not of how many links carry them.) */
.mast-link {
  color: var(--masthead-label);
  font-size: var(--type-micro);
  /* 1.2, and it is the SAME fix and the same reason as the solas' line-height
     above: a 12px mark inheriting the body's 1.65 hangs 5.80px below the
     baseline against the wordmark's 4.39px, and on a baseline-aligned flex row
     that grows the bar. Measured, adding the nav at 1.65 took the bar from
     46.19px to 46.80px; at 1.2 it is 45.39px, which is what it was before
     batch 15b touched the alignment at all. Every mark on this bar now carries
     leading that fits inside the wordmark's own line box. */
  line-height: 1.2;
  letter-spacing: var(--track-solas);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}

.mast-link:hover,
.mast-link:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: var(--line);
}

/* ==========================================================================
   Batch 15g, CHANGE 3 — ARTICLES IS A DROPDOWN. CSS ONLY.
   "I envisioned Articles being a dropdown menu, not a page." — Tina

   NO JAVASCRIPT. The site has none by design and gains none here. The charge
   said to stop and report rather than add a script if keyboard opening could
   not be done without one. It can, and this is how: the menu is hidden with
   VISIBILITY, not with display or opacity, and it is revealed by :focus-within
   on the wrapper as well as by :hover.

   Why visibility is the load-bearing choice. A visibility:hidden subtree is
   not focusable and takes no tab stops, so on a keyboard the closed menu does
   not exist. Tabbing onto the Articles trigger puts focus inside .mast-item,
   :focus-within matches, the subtree flips to visible, and its links become
   tab stops the very next Tab — the browser recomputes tab order live. Tab off
   the last item and focus leaves the wrapper, :focus-within stops matching and
   the menu closes itself. Opacity alone would leave seven invisible tab stops
   on the bar at all times; display:none cannot be transitioned INTO visibility
   by a parent state without the same problem in reverse. This is the whole
   trick and it needs no script.

   NO role="menu", DELIBERATELY. ARIA's menu pattern is a promise that arrow
   keys move between items, Home/End jump, and Escape closes. None of that is
   possible without JavaScript. Claiming the role and not honouring it is worse
   for a screen-reader user than not claiming it, because it advertises keys
   that do nothing. What this is, honestly described, is a group of links that
   appears — so it stays a plain set of anchors inside the labelled <nav>, and
   Tab is the documented way through it, which is what Tab already does.

   THE TRIGGER IS STILL A REAL LINK to articles.html, not a dead button. That
   is what makes the no-hover case below a straight degradation rather than a
   dead end, and it is why articles.html stays exactly as built and is not
   deleted: it is the fallback target, and on a phone it is the ONLY target. */
/* The trigger's wrapper. position:relative is what the panel is positioned
   against, and it is the reason the wrapper exists at all.

   display:flex IS NOT DECORATION — IT IS A BAR-HEIGHT FIX, and it is the same
   fix, for the same reason, that .mast-link's line-height:1.2 note above
   records. A block wrapper establishes an inline formatting context, and that
   context's STRUT takes the wrapper's own inherited font (16px at the body's
   1.65 = 26.4px of leading), not the 12px/1.2 = 14.4px of the link inside it.
   The strut is invisible and it is taller than its contents, so simply wrapping
   the Articles link in a <div> grew the masthead: measured, 45.39px -> 50.64px
   at desktop widths and 73.80px -> 94.05px at phone widths, where the bar wraps
   and the extra leading is paid on every row.

   A flex container generates no line box for its children, so there is no strut
   to be taller than the link. Measured back to 45.39px and 73.80px — the exact
   figures the bar carried before this batch, unchanged to the hundredth. Every
   mark on this bar still carries leading that fits inside the wordmark's own
   line box, which is the invariant batch 15c set and this rule keeps. */
.mast-item {
  position: relative;
  display: flex;
}

/* THE DEFAULT IS NO MENU. This is the phone case and it is the base state, not
   an override — mobile-first, so a device that never matches the query below
   never gets a rule it has to undo. display:none removes the seven items from
   the document entirely: no tab stops, no screen-reader output, no hover
   target, nothing to open by accident with a fingertip. Articles is then just
   a link to articles.html, which is exactly what it was before this batch. */
.mast-menu {
  display: none;
}

/* THE HOVER-CAPABILITY QUERY, and it is a capability test rather than a width
   test on purpose. A width breakpoint answers "is the screen big?", which is
   the wrong question: a 1280px touch monitor has no hover and would get a menu
   that can only be opened by a tap that also navigates, and a 700px window on
   a laptop has a mouse and would lose a menu it can use perfectly well. The
   media feature asks the question we actually mean.

   BOTH CONDITIONS ARE REQUIRED. (hover: hover) alone admits devices that
   EMULATE hover with a coarse pointer — some smart TVs and set-top remotes
   report it — where a hover-opened menu is a trap rather than an affordance.
   (pointer: fine) alone admits a stylus, which is fine-grained and cannot
   hover. Together they describe a mouse or a trackpad, which is the only input
   for which "point at it and it opens" is a real thing a person can do. A
   keyboard user is on a machine that has one, so the :focus-within path lives
   inside this query without being narrowed by it. */
@media (hover: hover) and (pointer: fine) {
  .mast-menu {
    display: block;
    /* hidden, but PRESENT — see the visibility note above */
    visibility: hidden;
    position: absolute;
    /* --space-2 below the trigger's line box, which puts the panel clear of
       the bar's bottom edge rather than starting inside the burgundy. */
    top: calc(100% + var(--space-2));
    /* RIGHT-ALIGNED, and this is what keeps the panel on the page. The nav
       sits at the right-hand end of the bar, so a left-aligned panel would
       grow toward the viewport edge and hang off it at narrow desktop widths.
       Anchored by its END edge the panel grows LEFTWARD into the empty middle
       of the bar, where there is always room. */
    inset-inline-end: 0;
    /* above the page content below the bar. The only other positioned thing on
       any page is .home section.hero at z-index:-1, which paints behind, so
       there is nothing here to fight. */
    z-index: 2;
    min-width: 15rem;
    padding-block: var(--space-1);
    background: var(--paper);
    border: var(--line) solid var(--hairline);
    border-radius: var(--space-1);
    /* the bar's type is uppercase and tracked; the panel's is prose. Both are
       reset here rather than on each item so a future item inherits correctly. */
    text-align: start;
    text-transform: none;
    letter-spacing: normal;
  }

  /* THE HOVER BRIDGE. The panel sits --space-2 below the trigger, and a gap
     between the two is a dead zone: the pointer leaves Articles, hovers
     nothing, and the menu shuts before it arrives. This pseudo-element fills
     that gap with an invisible strip so the hover surface is continuous. It
     exists only while the panel is visible, which is exactly when it is
     needed, and it is the reason the offset above can be a real visual gap
     rather than a compromise. */
  .mast-menu::before {
    content: "";
    position: absolute;
    inset-inline: 0;
    top: calc(-1 * var(--space-2));
    height: var(--space-2);
  }

  .mast-item:hover > .mast-menu,
  .mast-item:focus-within > .mast-menu {
    visibility: visible;
  }

  /* the items. Real prose type, not the bar's micro-caps: this is a reading
     list, and it is on --paper rather than on the accent fill. */
  .mast-menu-link {
    display: block;
    padding: var(--space-1) var(--space-2);
    color: var(--ink);
    font-size: var(--type-small);
    line-height: 1.4;
    text-decoration: none;
    white-space: nowrap;
  }

  .mast-menu-link:hover {
    text-decoration: underline;
    text-underline-offset: 0.2em;
    text-decoration-thickness: var(--line);
  }

  /* THE FOCUS RING INSIDE THE PANEL, and this rule is REQUIRED, not polish.
     `header a:focus-visible` re-points the ring to --masthead-label for
     everything in the bar, which is right on burgundy and catastrophic here:
     the panel is --paper, so a white ring on white paper is 1.00:1 in light
     mode — no ring at all for a keyboard user, on the one control that only a
     keyboard user reaches this way. Putting the ring back to --link fixes both
     modes at once, because --link is already the token that means "ink that
     contrasts with paper": #800d06 on #ffffff is 10.62:1 light, #f2f2f2 on
     #0f0f0f is 17.12:1 dark.

     Specificity is why this wins: (0,2,0) here against (0,1,2) for
     `header a:focus-visible` — two classes beat one class and two elements.

     The offset is pulled INSIDE the item. The global a:focus-visible offsets
     the ring by --space-1, which on a menu row would draw it outside the
     panel's own border. --space-0 negative keeps it on the row it marks. */
  .mast-menu-link:focus-visible {
    outline: var(--line) solid var(--link);
    outline-offset: calc(-1 * var(--space-0));
  }

  /* the entry that is not a destination. Batch 15h retired the second one when
     Why SolaSingles? was built, and batch 19 relabelled the one that remains —
     Position Papers now reads "Members Only" rather than announcing itself as
     unwritten. Tina's ruling is that it is PLAIN TEXT, not a link — so it is a
     span, it goes nowhere, and the label says what it is. It keeps its
     PREVIEW-ONLY FAKE marker in the HTML.

     The class name still says is-unwritten. It is left alone deliberately: it
     is the selector the nav markup carries on all ten pages, and renaming it
     would touch a block whose byte-identity across those pages is the thing
     the masthead comment tells you to check first. The name is now a fossil,
     not a description.

     tabindex="0" on a span is normally a thing to avoid, and it is deliberate
     here for one reason: the charge requires every item in this menu to be a
     real tab stop with a visible ring. A keyboard user tabbing the menu should
     meet the same nine entries a mouse user sees, including the one that is
     gated — skipping it would silently show a keyboard user a shorter menu.
     It is not interactive, so there is nothing to activate and Enter does
     nothing, which is the honest behaviour for text that is not a link.

     At launch this becomes an anchor and the tabindex comes off with the
     marker. */
  .mast-menu-link.is-unwritten {
    color: var(--ink-soft);
    cursor: default;
  }

  /* the dividers between the groups — the same split, in the same order, that
     articles.html already renders with an <hr>. Batch 23 made it FOUR groups
     and therefore three dividers: documents, then the MacArthur and Sproul
     piece on its own, then the Why pieces, then the What pieces. The
     global hr rule
     carries --space-3 of margin, which is a page rhythm and far too much for a
     menu row, so it is overridden to the panel's own scale. */
  .mast-menu-div {
    margin: var(--space-1) 0;
  }
}

/* Batch 14, CHANGE 8 — ON PHONE WIDTHS THE WORDMARK IS CENTRED.

   This is the file's SECOND breakpoint literal and the header note's "there is
   now ONE" no longer holds; it is added deliberately and it is derived rather
   than picked. Batch 8's sweep bisected the masthead's one-row/two-row
   transition to the pixel: from 658px up the wordmark and the solas share a
   line, and below it they are two stacked rows. Centring a wordmark that is
   SHARING its line with the solas would be centring it against them, which is
   not what the brief asks for, so this query has to stop below that figure.
   41rem is 656px — the nearest clean rem step under the transition, and the
   whole of it is two-row territory. (The seam is the single pixel at 657px,
   which is two-row with a left wordmark; there is no rem step that lands
   exactly on 658px, and a raw 41.125rem literal to buy back one pixel is worse
   than the pixel.)

   justify-content on the header is the whole change — one declaration, and it
   reaches the wordmark alone. The solas are the second flex line and carry
   flex-grow:1, so they already fill that row and their own text-align:center
   already centres them inside it; justify-content cannot move what has nothing
   left to distribute. Nothing about the type, the fill, or the padding moves,
   and above 656px the masthead is exactly what it was.

   (Batch 15, CHANGE 4 amends two facts in the paragraph above without changing
   what it does. The solas no longer carry flex-grow:1, so on the two-row form
   justify-content now centres a shrink-wrapped box rather than being unable to
   move a full-width one — same result, different route. And the literal moved
   to 42rem, then to 37rem in batch 15b when the solas shrank, then to 35rem in
   batch 15c when the hyphen came out, so "above 656px" now reads "above 560px".
   See below.) */
/* Batch 15, CHANGE 4 — THIS LITERAL MOVES, 41rem -> 42rem, and it moves
   because the thing it was derived from moved. Batch 14 bisected the
   one-row/two-row seam to 658px and took the nearest clean rem step under it.
   The new masthead fits differently — the header's column-gap dropped from 32px
   to 12px, and the hyphen with its right margin adds 17.6px back — so the seam
   was re-bisected rather than assumed. Batch 15 put it at 673px and took 42rem
   (672px), the clean rem step immediately under, which is exactly batch 14's
   own derivation applied to the figure that replaced its own. The file still
   carries exactly TWO breakpoint literals — this one and 62rem — because a
   value changed rather than a query being added.

   BATCH 15b — 42rem -> 37rem, for the third time and for the same reason: the
   seam moved again, because the solas shrank a step. The compact line draws
   400.0px at --type-micro rather than 466.6px at --type-small, so the whole
   masthead fits one row 82px earlier than it did. Left at 42rem this query
   would have gone on hiding the hyphen across a hundred pixels of viewport
   where the line sits happily on one row — dropping the joiner out of a
   masthead that has nothing to join across, which is the opposite of what the
   query is for. The literal follows the measurement; it is not a new rule.

   Batch 15b re-bisected to 570 / 571-592 / 593, keeping batch 15's three-state
   shape with the hyphen hidden across the middle band.

   BATCH 15c — 37rem -> 35rem, the literal's FOURTH value, and this time the
   query gets simpler rather than only moving. The hyphen is gone, so the
   middle band it created is gone with it and the display:none that ran this
   query's second declaration is deleted rather than re-tuned. What is left is
   one seam: the pair needs 534.7px, the gutters take 32px, so they share a row
   from 567px up and stack below it. 35rem is 560px, the clean rem step under
   566 and wholly inside two-row territory — batch 14's derivation, unchanged,
   on the fourth figure it has been handed.

       <= 566px   two rows   wordmark centred, solas centred beneath
       >= 567px   one row    the pair together, flush left in the gutter

   The file still carries exactly TWO breakpoint literals — this one and 62rem.
   Four batches have moved this value and none has added a query beside it.

   Charge six removed this query. The header is one row at every width: the
   PNG scales down rather than wrapping or centering, and the nav stays at
   the right-hand end. */

/* The rule-and-dots ornament (batch 5B, EDIT C) was removed in batch 6,
   CHANGE 2 — markup gone from all four pages, rules gone from here. Its
   24.88px of height is accounted for in the masthead table above. */

/* page head — inner pages */
main > h1 {
  padding-block: var(--space-6) 0;
}

/* Batch 5B addendum, EDIT F: page headings carry the logo grey rather than
   the ink. The grey TEXT token is the one that lifts on dark paper, so both
   modes clear AA for large text (5.03:1 light, 6.56:1 dark; 3:1 required). */
h1 {
  margin: 0 0 var(--space-3);
  font-size: var(--type-title);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.05;
  color: var(--logo-grey-text);
}

main > .lede {
  /* content-box so --measure-lede caps the text, not the text plus the
     container padding this element carries from the full-bleed rule */
  box-sizing: content-box;
  padding-block: 0 var(--space-5);
}

.lede {
  margin: 0;
  max-width: var(--measure-lede);
  font-size: var(--type-large);
  line-height: 1.45;
  color: var(--ink-soft);
  font-weight: 400;
}

hr {
  border: none;
  border-top: var(--line) solid var(--hairline);
  margin: var(--space-3) 0;
}

/* Batch 15g, CHANGE 4 — "Back to Articles", on the five long pages.

   AT THE BOTTOM, on all five, and the placement is the same on every one so
   there is one habit to learn rather than five. Three reasons it is the foot
   and not the head. The masthead now carries the Articles dropdown on every
   page, so a back link at the TOP would be a second route to the same place
   sitting a few pixels under the first — the top of these pages is already
   solved. These are long documents, and the reader who needs the link is the
   one who has just finished and is looking for what to read next; that reader
   is at the bottom, and putting it there saves a scroll back. And on
   statement.html the last thing on the page is the affirmation line, which
   batch 15f was told not to touch — a foot link is added AFTER it and leaves
   the document's own ending intact, where a head link would push in above the
   lede.

   NO NEW LINK TREATMENT. It is a bare <a> in a <p>, so it takes the site's
   inline link rule exactly as prose links do: --link colour, underline at
   rest, 0.2em offset, --line thickness, and the standard a:focus-visible ring
   in --link. That ring is 10.62:1 on light paper and 17.12:1 on dark. The only
   thing this rule does is space it; the .backlink selector is also added to
   the container rule above so it takes the same gutter as everything else in
   main rather than running full-bleed.

   The rhythm: --space-4 above separates it from the last section's text, and
   --space-5 below matches the padding every section already ends on, so the
   distance from the link down to the footer hairline is the page's own. */
.backlink {
  margin: 0;
  padding-block: var(--space-4) var(--space-5);
}

/* sections — even vertical rhythm: every section is --space-5 top and
   bottom, so section-to-section spacing is uniform down the page. */
section {
  margin: 0;
  padding-block: var(--space-5);
  border-top: var(--line) solid var(--hairline);
}

/* the hero band — full width, extra air, no rule above it */
section.hero {
  border-top: none;
  padding-block: var(--space-6);
}

.hero h1 {
  max-width: var(--measure-hero);
  font-size: var(--type-hero);
  line-height: 1.1;
}

/* Batch 14, CHANGE 7 — the gap under the hero lede comes down one step with
   the type above it. --space-4 was sized against a 24px lede; the lede is
   21px now, and the step keeps the gap in the same proportion to its text
   rather than leaving a hole the smaller panel cannot afford. */
.hero .lede {
  margin: 0 0 var(--space-3);
}

h2 {
  margin: 0 0 var(--space-3);
  font-size: var(--type-micro);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* section-title icons (batch 5B, EDIT D) — thin-line marks beside each
   home-page h2. Sized at --icon-size, which is smaller than the h2's line
   box (12px type on a 1.65 line-height), so the icon sits inside the line
   the heading already occupies and cannot raise it. The stroke is set on
   the group here rather than on each path, so all eleven share one weight. */
.sec-icon {
  display: inline-block;
  width: var(--icon-size);
  height: var(--icon-size);
  margin-inline-end: var(--icon-gap);
  vertical-align: -0.22em;
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

p {
  margin: 0 0 var(--space-3);
  max-width: var(--measure);
}

p:last-child {
  margin-bottom: 0;
}

strong {
  font-weight: 600;
}

/* Batch 15e — THE TWO ELEMENTS THE ARTICLE PAGES BRING WITH THEM.

   The charge is to add no styling unless a source document requires an element
   the site has never used. Two do, and this is the whole of what was added:
   blockquote (three of them, in the Code of Conduct, quoting the Westminster
   divines) and ul/li (two lists in the Code of Conduct, one in Why Sola?).
   Before this batch there was not one <ul>, <ol>, <li> or <blockquote> in any
   page in public/, and not one rule for them in this file.

   Both are ELEMENT selectors — no new class is introduced anywhere in this
   batch — and both are built entirely from tokens this file already declares:
   --space-3, --space-1, --line, --hairline, --measure. No new colour, no new
   size, no new spacing step.

   WHY THEY CANNOT JUST BE LEFT TO THE BROWSER. The `p` rule above caps prose at
   --measure (49rem); a bare <li> or a bare <blockquote> answers to nothing but
   the container, which is --container (69.5rem). Left alone, the lists and the
   quotations would set 20rem wider than the paragraphs either side of them and
   the measure discipline would break exactly where the page is hardest to read.
   The max-width on each is the point of these rules; the rest is rhythm.

   The blockquote takes NO colour of its own — it inherits --ink, the same value
   as the prose around it. A quieter grey was available (--ink-soft, as .lede
   takes) and is not used: at --type-base the quote is 21px, which is under the
   24px WCAG counts as large, so it would want 4.5:1 rather than 3:1 and the
   question would need measuring. An indent and a hairline say "quotation"
   without raising it. */
blockquote {
  margin: 0 0 var(--space-3);
  max-width: var(--measure);
  padding-inline-start: var(--space-3);
  border-inline-start: var(--line) solid var(--hairline);
}

ul {
  margin: 0 0 var(--space-3);
  max-width: var(--measure);
  padding-inline-start: var(--space-3);
}

li {
  margin-bottom: var(--space-1);
}

li:last-child {
  margin-bottom: 0;
}

/* THE CALLOUT BOX - the standing summary line at the foot of a section.

   Promoted here from a scratch page that set the same sentence three ways
   (pull-quote, card, burgundy band); the card was the one chosen, so its rules
   move into the stylesheet as a reusable class and the scratch page is gone.

   It is ONE element, a <p>, so the box and the sentence inside it are the same
   box: there is no wrapper and no label. That is why the type size sits on this
   rule rather than on a child.

   NO NEW COLOUR AND NO NEW SIZE. The border is --hairline, the wash is
   --callout-wash (--accent at low alpha, see :root), the text is --ink, and
   --type-large is the size the lede already uses. The measure is --measure, the
   same cap the p rule applies, so a callout sets to the width of the prose
   above it rather than to the container.

   SPACING. Only the top margin is stated. The box is always the last element in
   its section, so `p:last-child` above already zeroes the bottom margin and
   wins on specificity; stating a bottom margin here would fight that rule for
   no gain. */
.callout {
  margin-block-start: var(--space-3);
  max-width: var(--measure);
  padding: var(--space-3);
  border: var(--line) solid var(--hairline);
  background: var(--callout-wash);
  font-size: var(--type-large);
  line-height: 1.35;
  color: var(--ink);
}

/* Batch 14, CHANGE 11 — the colour comes from --link, which is the accent in
   light mode and the body ink in dark. text-decoration-line is stated rather
   than left to the browser default: in dark mode the underline is the only
   thing separating a link from the prose it sits in, so it is not something
   this file should be inheriting by luck.

   Reach: this rule governs INLINE links only. Everything else that is an <a>
   already overrides it — .mark and .notify carry their own colour and
   text-decoration:none, footer a takes --ink-soft, and .home .zoom-x its own
   pair. The two .sec-photo-zoom anchors and the two aria-hidden .zoom-close
   backdrops inherit the token but wrap an image or nothing at all, so no ink
   changes there either. Seven links on three pages are what actually move. */
a {
  color: var(--link);
  text-decoration-line: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: var(--line);
}

/* the one call to action — solid accent fill, resting; the artwork's grey
   on hover (batch 4B, Lex's design). The label colour changes with the
   fill: see the --btn-label note above. */
.notify {
  display: inline-block;
  /* narrow default so the label can wrap inside a phone-width column
     instead of overflowing it; roomier padding at the wide breakpoint */
  padding: var(--space-2) var(--space-3);
  max-width: 100%;
  text-align: center;
  background: var(--accent);
  border: var(--line) solid var(--accent);
  border-radius: var(--radius);
  color: var(--btn-label);
  font-size: var(--type-small);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
}

.notify:hover,
.notify:focus-visible {
  background: var(--logo-grey);
  border-color: var(--logo-grey);
  color: var(--btn-label-hover);
}

/* PREVIEW-ONLY app-store badges. Batch 5B (EDIT E) hand-drew these; batch 6
   (CHANGE 4) replaced the drawings with the real artwork from Apple and
   Google, shipped as badge-appstore.svg and badge-googleplay.png.

   They remain PREVIEW-ONLY and deliberately INERT: plain <img>, no <a>
   wrapper and no href to either store, because this is a locked draft and
   neither app exists yet. Each is tagged in the HTML with its own removal
   marker. The marker phrase is deliberately NOT repeated here, so a
   pre-launch search for it returns exactly the two badges and nothing else.

   The two heights differ on purpose — see the --badge-h-* notes above. The
   boxes are 40px and 60px; the artwork inside them draws at 40.00px and
   40.32px, which is what the eye actually measures. align-items:center then
   lines those two drawn marks up on a shared centre, because Google's clear
   space is symmetrical (41px top and bottom). */
.appbadges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
  max-width: none;
}

/* flex:none so a narrow viewport wraps the pair instead of shrinking either
   image off its aspect ratio; width:auto overrides the intrinsic-size
   attributes carried in the HTML for layout stability */
.badge {
  display: block;
  flex: none;
  width: auto;
}

.badge-apple {
  height: var(--badge-h-apple);
}

.badge-google {
  height: var(--badge-h-google);
}

/* Batch 7, CHANGE 4 — the line under the badge pair. Quiet type from the
   pairing this file already uses for exactly that job: --type-small on
   --ink-soft is the footer's voice, so the caption speaks in a register the
   page has already established rather than a new one. --space-1 tucks it to
   the badges instead of letting the default paragraph rhythm push it away,
   and max-width:none lets it centre on the hero's full measure like the
   badge row above it rather than on the --measure prose column.

   This is NOT a removal marker. The two PREVIEW-ONLY markers stay in the
   HTML, unchanged and still exactly two, and the marker phrase is still
   absent from this file on purpose — see the .appbadges note above.

   Contrast, stated — and this paragraph has been overtaken twice, so it is
   given as the history it is rather than left reading as current fact:

     batch 6/7  over the 0.80 scrim, 3.48:1 at its worst point in light mode.
                At 14px that is normal text, so it cleared AA Large but not
                the 4.5:1 AA Normal wants.
     batch 10   the scrim was removed, so that figure stopped describing
                anything: the caption sat on the bare banner, where NO pixel
                reached 4.5:1 against either light-mode token.
     batch 12   the hero panel landed under it. Its ground is now solid white
                in both modes, and the rule below no longer governs it — see
                .home .hero .badge-note and --hero-caption, which pin the
                light-mode grey in both modes and measure 5.33:1. It clears
                AA Normal for the first time since batch 6.

   The rule below therefore still says --ink-soft and is still correct for any
   .badge-note that is NOT in the hero. There is only one on the site today,
   and it is in the hero. */
.badge-note {
  margin: var(--space-1) 0 0;
  max-width: none;
  font-size: var(--type-small);
  color: var(--ink-soft);
}

/* the promises list */
.promise {
  padding: var(--space-2) 0;
  border-bottom: var(--line) solid var(--hairline);
  max-width: var(--measure);
}

.promise:last-of-type {
  border-bottom: none;
}

.promise strong {
  display: block;
  margin-bottom: var(--space-1);
}

.promise p {
  margin: 0;
  color: var(--ink-soft);
}

/* Batch 10, CHANGE 3 — the three cases that get a personal read, in the
   APPLICATION REVIEW section. The brief asked for the lead-ins to render in the
   same register as the promises list's, so the lead-in rule below is the
   promise's lead-in rule exactly: a block <strong> tucked to its paragraph by
   --space-1.

   What it deliberately does NOT borrow is the rest of the promises' costume.
   No hairline between the items, because the hairlines are what make that
   block read as a five-item LIST of commitments, and these three are body copy
   inside a section — the paragraph before and the paragraph after them are
   ordinary prose and the cases have to sit in the same body of text, not
   interrupt it as a boxed list. For the same reason the paragraph keeps the
   section's normal --ink rather than the promises' --ink-soft: a promise's
   description is a gloss under a headline, while these paragraphs ARE the
   section's argument.

   --space-3 below each item is the paragraph rhythm this page already uses, so
   the cases stack against the surrounding prose at the same spacing every
   other paragraph does. */
.case {
  margin-bottom: var(--space-3);
  max-width: var(--measure);
}

.case strong {
  display: block;
  margin-bottom: var(--space-1);
}

.case p {
  margin: 0;
}

/* footer — centred, four stacked rows, hairline above, quiet type.
   Identical on every page. */
footer {
  padding-block: var(--space-5);
  border-top: var(--line) solid var(--hairline);
  font-size: var(--type-small);
  color: var(--ink-soft);
  text-align: center;
}

footer p {
  margin: 0 0 var(--space-1);
  max-width: none;
}

footer p:last-child {
  margin-top: var(--space-2);
  margin-bottom: 0;
}

footer a {
  color: var(--ink-soft);
}

/* ==========================================================================
   FRONT PAGE ONLY — everything below the body.home scope is batch 6 and
   applies to index.html alone. The inner pages carry the same masthead,
   footer and prose rules as before and are deliberately untouched by this
   block: they have h2s but have never had icons, no hero, and no
   alternating layout. index.html is the only page with class="home".
   ========================================================================== */

/* CHANGE 6 — the section-title icons become filled circles with a white
   glyph. Nothing here is a new colour: the fills are the two existing theme
   tokens, --accent (the burgundy) and --logo-grey (the artwork grey, which
   this file already documents as a mode-independent FILL), and the glyph is
   --btn-label-hover, the white this file already pins for a label sitting on
   a --logo-grey fill. No path data changed — every glyph is the batch 5B
   shape, scaled up and restroked in white. box-sizing:border-box is global,
   so the padding insets the 24-unit viewBox inside the circle. */
.home .sec-icon {
  width: var(--icon-size-home);
  height: var(--icon-size-home);
  padding: var(--icon-pad-home);
  margin-inline-end: 0;
  border-radius: 50%;
  background: var(--accent);
  stroke: var(--btn-label-hover);
  vertical-align: baseline;
  flex: none;
}

/* Charge four: every section icon takes the accent; the grey alternation ends. */
.home section.flip .sec-icon {
  background: var(--accent);
}

/* a 56px circle cannot sit inside a 12px line box, so the h2 becomes a
   two-row stack — icon, then title — instead of icon-inline-with-title. The
   title is an anonymous flex item (a bare text node) and inherits the h2's
   type as before. Front page only: the inner pages' h2s stay inline.

   Batch 10, CHANGE 5 — the queued accessibility fix. The section photographs
   used to be the THIRD row of this stack, sitting inside the h2 itself, which
   put their alt text inside the heading's accessible name: a screen reader
   announced "Built for protection, Three people in conversation around an open
   Bible, heading level 2". The alt text is right and the heading is right; they
   simply are not the same thing and must not be spoken as one string.

   The fix is a wrapper. .sec-label is now the label element — it carries the
   stack, the column width, the float and the alternation — and the h2 inside it
   holds ONLY the icon and the title. The photograph is the wrapper's second
   child instead of the h2's third, so the heading's accessible name is its
   title text alone while the image keeps its own alt in the tree beside it.

   NO VISIBLE CHANGE, and the geometry is why: the wrapper reproduces the stack
   the h2 used to be (same flex column, same --space-2 gap, same alignment) and
   the h2 inside it is zeroed to margin 0, so icon-to-title and title-to-photo
   are the same 16px they were and the wrapper's own margin-bottom is the
   --space-3 the h2 used to carry. Every section gets the wrapper, including the
   ten without a photograph, so one set of rules governs all fourteen labels
   rather than two.

   The h2 keeps a flex column of its own: the icon is still 56px and still
   cannot sit inside a 12px line box. It just has one fewer row in it now. */
.home .sec-label,
.home .sec-label h2 {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.home .sec-label {
  margin-bottom: var(--space-3);
}

.home .sec-label h2 {
  margin: 0;
}

/* Batch 8, CHANGE 1 + CHANGE 2 — the two photographs Tina supplied, ONE rule
   serving both. Each is the third row of the label stack the rule above
   defines: icon, then title, then photo. Sitting inside that stack is what
   puts them in the LABEL column without a single placement declaration —
   they follow the label wherever it goes: the narrow left column in the
   default sections, the narrow RIGHT column in the flipped ones (.flip
   already moves the whole h2 and right-aligns it), and stacked under the
   title in the single-column layout below the wide breakpoint. No rule here
   names a section, a side, or an image file.

   width:100% fills the label column exactly — --col-label, 272px, in the wide
   layout. max-width pins it to that same 272px below the breakpoint, where
   there is no label column and the h2 spans the whole container; the photo
   would otherwise draw a metre wide on a tablet. height:auto together with
   the intrinsic width/height attributes carried in the HTML preserves the
   1448x1086 (4:3) source ratio and reserves the box before the image decodes,
   the same reason the store badges carry theirs.

   ROUNDING: --space-1, 8px. This file has never had a radius token and
   inventing one for two images would be a larger change than the change
   itself, so the radius is an existing spacing step used as a radius —
   --space-1 is the quietest one that still reads as deliberate at 272px wide.
   Nothing else is added: no border, no shadow, no frame. The locked direction
   is hairlines and no shadows, and a rounded corner is as far as it goes.

   Batch 10, CHANGE 2 — four more images join the two, one of them REPLACING
   the protection photograph, and this rule serves all five without gaining a
   declaration. Two of the four are portrait (900x1200) rather than the
   landscape the note above was written against; height:auto and the intrinsic
   attributes carry whichever ratio the file has, so a portrait image draws
   272x363 in the label column and a landscape one 272x204. Nothing here names
   a ratio, a section or a file.

   The --col-label cap moved ON to this rule in batch 10. It used to sit on
   .sec-photo-zoom, the anchor, which was correct while every photograph was a
   zoom trigger; now only the two screenshots are, and the rest are bare <img>
   that need the cap themselves. The anchor keeps its own copy, so an image is
   capped at 272px whether it is wrapped or not.

   Batch 11, CHANGE 1 — the set this rule serves changed under it and the rule
   did not have to. Six images were replaced or added from Tina's folder, five
   of them landing in a label column: the room render turned landscape 4:3
   (1200x900), filtering held 3:4 (900x1200), protection and membership BOTH
   turned from landscape to portrait (960x1200), and a sixth image joined the
   doctrinal-statement section at 2:3 (800x1200) where there was none before.
   Nothing here moved, because nothing here names a ratio: height:auto plus the
   intrinsic width/height attributes in the HTML carry whatever ratio each file
   has. A 960x1200 file draws 272x340 in the column; an 800x1200 one, 272x408.

   Batch 14, CHANGE 6 replaced the doctrinal-statement image again, and again
   this rule did not have to move. The new file is SQUARE — 1254x1254 from
   Tina's folder, shipped 1200x1200 — so that slot has now been 2:3 and 1:1
   and drawn correctly at both: 1:1 draws 272x272 in the label column. It is
   the third ratio this one rule has carried without gaining a declaration.

   These carry no figure, no caption and no label of any kind. The alt text in
   the HTML is the whole of what any of them says, and none of it states or
   implies that a pictured person is a member. That wall held through the batch
   11 rewrite: the two screenshots say "Concept preview" and "sample profiles",
   and the three photographs describe a room and what is on a table. */
.home .sec-photo {
  display: block;
  width: 100%;
  max-width: var(--col-label);
  height: auto;
  border-radius: var(--space-1);
}

/* Batch 8 follow-up (Tina's ruling, 2026-08-22) — the photographs click to
   zoom. CSS only: each photo is a link to one of the two zoom views at the end
   of index.html, and those stay display:none until the URL fragment selects
   one. This site ships no JavaScript and this change does not start.

   Batch 10, CHANGE 4 — click-to-zoom is now SCREENSHOTS ONLY. The test is
   whether the image's content is a user interface, where the reader's reason
   to enlarge is to READ something the 272px label column is too narrow to
   show. A photograph has nothing in it to read, so enlarging it was offering a
   control that answered no question.

   Batch 11, CHANGE 4 — that rule is unchanged and the page now has two images
   that pass it, so click-to-zoom lands on EXACTLY TWO and no others. The
   tablet room render kept its trigger; the matching screen gained one, using
   this same block and the same X control, because it is the other screenshot
   and its criteria table is the densest thing on the page to read. The three
   photographs have none, and the doctrinal-statement image added in the same
   batch explicitly has none — it is a photograph of a printed sheet, and the
   sheet's own text is not what that section asks anyone to read; the statement
   itself is a link, one paragraph away, on its own page.

   The count is deliberate and it is two. Adding a third is a trigger in the
   HTML and a view at the end of it, not a rule here.

   The trigger carries the sizing that used to sit on the image itself. An
   anchor wrapping the image becomes the flex item in the label stack, so the
   image now sizes to the anchor and the anchor sizes to the column. The label
   figures are unchanged by this: 272px wide, 4:3, 8px radius, and the same
   --col-label cap below the wide breakpoint.

   Stated plainly, because it was stated when the mechanism was chosen: the
   Escape key does NOT close a :target view. Closing is a click anywhere on the
   backdrop, or Enter on the close link, which is the first thing Tab reaches
   once the view is open. Each opening also adds one history entry, so Back
   closes it too. Buying Escape means buying JavaScript, which is a ruling
   nobody has made.

   Closing points at the photo's own section (#meet, #filtering) rather than a
   bare "#". Both clear the view; a bare "#" would also throw the reader back
   to the top of the page, and naming the section puts them back where they
   were standing when they opened it. The filtering section had no id before
   batch 11 and took one for this.

   The ground is a solid --paper, so the view follows whichever mode is in
   force, and no shadow and no gradient is introduced. (It used to be described
   here as following the mode "as the hero's scrim does"; batch 10 removed that
   scrim, so the comparison is gone and the behaviour is stated on its own.) */
.home .sec-photo-zoom {
  display: block;
  width: 100%;
  max-width: var(--col-label);
  text-decoration: none;
  cursor: zoom-in;
}

.home .zoom {
  display: none;
}

.home .zoom:target {
  display: grid;
  place-items: center;
  position: fixed;
  inset: 0;
  z-index: 1;
  padding: var(--space-3);
  background: var(--paper);
}

/* the backdrop closes on a click anywhere, but it is no longer the control
   that ANNOUNCES itself: batch 9, CHANGE 3 gave the view a visible X, and that
   X now carries the accessible name and the tab stop. The backdrop is left as
   a mouse convenience only — tabindex -1 and aria-hidden keep it from being a
   second, invisible "Close" that keyboard and screen-reader users would hit
   first and see no focus ring on. It is not keyboard-focusable, which is what
   makes aria-hidden legitimate here rather than a trap. */
.home .zoom-close {
  position: absolute;
  inset: 0;
  cursor: zoom-out;
}

/* Batch 10, CHANGE 4 — the frame. The X used to be positioned against the
   .zoom overlay, which is the whole viewport, so "top right" meant the top
   right of the WINDOW: on a portrait image in a wide window the control floated
   out in the empty ground, a long way from the thing it closed. This wrapper
   shrink-wraps the enlarged image — it is the grid item, and place-items:center
   on the overlay sizes it to its content — so the X can be positioned against
   the photograph's own box instead.

   max-width/max-height repeat the image's own caps so the frame can never
   report a size the image does not fill; the image's max-height is the figure
   that actually decides both. */
.home .zoom-frame {
  position: relative;
  max-width: 100%;
  max-height: 100%;
}

/* Batch 9, CHANGE 3 — the visible close control.

   Batch 10, CHANGE 4 — larger, and tight into the photograph's own top-right
   corner. Both were asked for, and the frame above is what makes the second one
   mean anything.

   POSITION. top and right are now 0 against .zoom-frame, so the chip's corner
   sits on the image's corner and shares its --space-1 radius, nesting into it
   rather than floating near it. Batch 9's --space-2 inset was there to keep the
   focus ring off the VIEWPORT edge, and that reason is retired rather than
   ignored: the overlay carries --space-3 of padding, so the image's corner is
   at least 32px inside the window and the ring — drawn at --space-1, 8px — has
   four times the room it needs. Nothing about the ring is clipped.

   SIZE. --zoom-x-size, 72px, up from --space-4's 56px; the glyph goes to
   --zoom-x-glyph, 36px, up from --type-large's 24px. See the token note. The
   hit area was already past the 44px minimum and is now well past it.

   The multiplication sign is still used rather than a drawn mark: it needs no
   new artwork, and the file's SVG icon vocabulary is for section titles, not
   for chrome.

   --paper behind it was never decoration, and at top:0 it stops being optional:
   the chip now ALWAYS sits on the photograph rather than only reaching it on
   some window shapes, so the ground is what keeps the glyph legible over
   whatever is in that corner of the image, in both modes.

   Resting --ink-soft, --accent on hover and focus: the same quiet-to-accent
   move the rest of the page already makes. This is the single announced close —
   the backdrop above is aria-hidden and not focusable, so the tab stop and the
   accessible name are both here and there is exactly one of each. */
.home .zoom-x {
  position: absolute;
  top: 0;
  right: 0;
  display: grid;
  place-items: center;
  width: var(--zoom-x-size);
  height: var(--zoom-x-size);
  background: var(--paper);
  border-radius: var(--space-1);
  color: var(--ink-soft);
  font-size: var(--zoom-x-glyph);
  line-height: 1;
  text-decoration: none;
}

/* Batch 15, CHANGE 1 — the hover/focus glyph takes --link, the second of the
   two dark-mode items batch 14, CHANGE 4 left open. Its ground is the chip's
   --paper, so in dark mode the accent burgundy sat on #0f0f0f at 1.84:1; --link
   is --ink there and measures 17.12:1. Light mode is untouched — --link is
   --accent on white paper, 10.41:1, exactly the value this rule already drew.
   The resting state keeps --ink-soft; only the transient state moves. */
.home .zoom-x:hover,
.home .zoom-x:focus-visible {
  color: var(--link);
}

/* max-height is written against the viewport rather than as a percentage: the
   row this image sits in is auto-sized BY the image, so a percentage height
   would be resolving against itself and a tall image would overflow a short
   window instead of fitting it. */
.home .zoom img {
  display: block;
  position: relative;
  max-width: 100%;
  max-height: calc(100vh - 2 * var(--space-3));
  width: auto;
  height: auto;
  border-radius: var(--space-1);
}

/* CHANGE 7 — the hero carries the banner edge to edge. The section is
   already full-bleed, so the background spans the viewport while the content
   stays held to --container.

   Batch 10, CHANGE 1 — THE SCRIM IS REMOVED. The banner displays exactly as
   the file is: no wash, no tint, nothing between the image and the reader. The
   ::before that carried it is gone from this file entirely, and the image file
   itself is not edited. That still holds in batch 11.

   Batch 11, CHANGE 1 + CHANGE 2 — the banner is REPLACED and the lettering on
   it goes BLACK. Both were on the brief; the second is the third of the three
   levers batch 7 named for the legibility the removed wash used to carry
   ("the hero's text tokens stop being the muted greys"). Taking it is Tina's
   call, and she took it.

   The file is profile_banner.jpg, from Tina's BurgundyBanner.png — a
   burgundy-toned collage of portrait tiles on a cream ground, 1774x887,
   357,789 bytes, no downscale (the source was already under the full-width
   budget once encoded; see the header note on this batch's image weights).
   The .png it replaces is deleted, and the extension changed with it: the
   source is a photographic collage, and no PNG of it lands anywhere near the
   ~700 KB a full-bleed background may spend.

   Re-measured over the NEW file (1774x887, every other pixel sampled, WCAG
   relative luminance, contrast taken the symmetric way — lighter over darker,
   whichever the text turns out to be). Despite reading as a light image, it is
   NOT one through the middle: min 0.0026, p05 0.0160, median 0.0817,
   p95 0.5018, max 1.0000. The cream gaps between the tiles carry the top of
   that range; the burgundy wash over the photographs carries the bottom, and
   there is a great deal more photograph than gap.

   Against the median, black beside the four greys it replaces, and white for
   the ceiling (whole image / text region):

       token                        at median        share >= 3:1
       h1 + lede BLACK #000000    2.63 / 2.82:1    43.0% / 47.2%
       (was) h1   light  #6e6f71  1.59 / 1.48:1    13.4% / 14.0%
       (was) lede light  #6b6b6b  1.50 / 1.40:1     9.7% / 10.5%
       (was) h1   dark   #96979a  2.73 / 2.55:1    44.6% / 42.0%
       (was) lede dark   #9a9a9a  2.83 / 2.65:1    46.8% / 43.9%
       [not taken]  WHITE #ffffff 7.97 / 7.45:1    89.0% / 83.7%

   "Text region" is the middle 60% of the width and middle 55% of the height —
   where a centred h1 and a 40rem lede land under background-size:cover. Every
   token, black included, falls to about 1.00:1 somewhere in the frame: with an
   unwashed photographic ground there are always pixels at the text's own
   luminance.

   Reported, not designed around, exactly as batch 10 reported its figures, and
   two of these numbers are worth stating plainly rather than burying:

     - Black is a large gain on LIGHT mode, which is what it was asked to fix:
       1.59:1 -> 2.63:1 at the median, and 13.4% -> 43.0% of the image at 3:1.
       It lands level with what DARK mode already had (2.73/2.83:1), and it now
       delivers that same figure in both modes rather than only one.
     - Black is NOT the maximum-contrast choice here, and this file should not
       be read as claiming it is. White measures 7.97:1 at the median and
       clears 3:1 over 89% of the image — three times black's share. Black
       still does not reach 3:1 at the median, and no darkening of it would;
       #0a0a0a and #111111 both measure WORSE (2.48:1, 2.37:1), which is why
       the token is pure #000000 rather than a near-black.

   Black is Tina's ruling and it stands; the white figure is owed to her as a
   measurement, not offered as a correction. Nothing here compensates for the
   gap: no text-shadow was added, no partial scrim was substituted, the image
   was not edited, and no other colour moved. The levers, if she wants one,
   remain the wash returning as one ::before, a lighter banner, or white
   lettering. All three are her call.

   ONE TOKEN, BOTH MODES — --hero-ink is deliberately NOT overridden in the
   dark block. The backdrop here is the photograph in either mode; it does not
   invert, so the lettering on it must not either. This is the same reasoning
   --logo-grey and --masthead-label already carry in this file: a colour whose
   ground is a fill or an image is mode-independent. It governs the h1 and the
   lede only; batch 12 gave the .badge-note its own mode-independent token for
   the same reason, once the panel became its ground.

   Batch 12, CHANGE 1 — THE TEXT NO LONGER SITS ON THIS IMAGE. Everything
   above is still true of the banner and is kept because it is the measurement
   that produced the panel ruling, but it now describes the ground BEHIND the
   panel rather than behind the lettering. The 2.63:1 figure is history, not a
   live number: black measures 21.00:1 on the panel it actually sits on. This
   rule is unchanged — the banner is still declared here, still uncropped,
   still unedited, and still visible on all four sides of the panel.

   Batch 15, CHANGE 3 (Tina's ruling, 2026-08-25) — THE BANNER IS BLURRED.
   "The banner needs to be more blurred, like Match", then narrowed the same
   day: "I wouldn't say shapes indistinct — I would say a subtle blur." So this
   is a softening, not a wash, and BEFORE it there was no blur at all — no
   `filter:` declaration existed anywhere in this file, so the banner drew at
   full sharpness.

   Batch 15b — 8px WAS TOO MUCH and is corrected to 3px. "Too blurred, I didn't
   want it indistinct. Just *slight*." At 8px the tile edges and the cream gaps
   went soft enough that the collage started reading as one burgundy field; at
   3px the faces and the tile edges stay clearly legible and the image reads as
   a photograph slightly off the sharp plane. The full history is 0px -> 8px ->
   3px in one day, and the live figure is the last of them. Moving it again is
   one value on --hero-blur; nothing else in this rule depends on the number.

   THE IMAGE FILE IS NOT TOUCHED. profile_banner.jpg ships byte-identical; the
   blur is a paint-time filter, so the same file is still what the header note
   weighs and what every measurement above was taken over.

   WHY A ::before RATHER THAN A FILTER ON THE SECTION. `filter` on .hero would
   blur the section's CONTENTS as well — the panel, the headline, the badges —
   which is not what was asked for and would destroy the 21.00:1 the panel
   exists to deliver. So the photograph moves to its own pseudo-element, which
   carries the filter alone. The one previous ::before on this section was the
   scrim batch 10 removed; this is not that scrim returning. It tints nothing,
   adds no colour and changes no contrast figure on this page — the panel is
   opaque white over it, and every token above still measures exactly what it
   measured, since none of them sits on the banner any more.

   The pseudo is inset NEGATIVELY by twice the blur radius. A Gaussian blur
   samples past its own edge, so a pseudo drawn flush to the section's box
   would fade to transparent along all four sides and draw a pale halo around
   the band. Bleeding it 16px out on every side puts that fade outside the box,
   and overflow:hidden on the section clips it. isolation:isolate keeps the
   z-index:-1 inside this section's own stacking context, so the pseudo paints
   behind the hero's content and nothing else on the page is reordered. */
.home section.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  text-align: center;
}

.home .hero-faces {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  pointer-events: none;
}

.home .hero-faces__row {
  flex: 1 1 0;
  overflow: hidden;
  filter: blur(var(--hero-blur));
}

.home .hero-faces__track {
  display: flex;
  width: max-content;
  height: 100%;
  animation-name: hero-slide-left;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: var(--hero-slide);
}

.home .hero-faces__row--right .hero-faces__track {
  animation-name: hero-slide-right;
}

.home .hero-faces__track img {
  display: block;
  height: 100%;
  width: auto;
  max-width: none;
}

.home .hero-wash {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--paper) 88%, transparent),
    color-mix(in srgb, var(--paper) 50%, transparent) 45%,
    color-mix(in srgb, var(--paper) 18%, transparent)
  );
}

@keyframes hero-slide-left {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

@keyframes hero-slide-right {
  from {
    transform: translateX(-50%);
  }
  to {
    transform: translateX(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .home .hero-faces__track {
    animation: none;
    transform: translateX(0);
  }
}

/* 7b. Centre the hero content as a group. text-align above centres the
   inline runs and the inline-block button; these rules centre the blocks
   themselves, which are capped by --measure-hero / --measure-lede /
   --measure and would otherwise sit flush left inside the container. */
.home .hero h1,
.home .hero p {
  margin-inline: auto;
}

/* Batch 11, CHANGE 2 — the hero lettering goes black. Two selectors, not one
   blanket .hero rule: the h1 and the LEDE take it, and nothing else in the
   band does. The sign-up button carries its own label token and the
   .badge-note carries --hero-caption (batch 12; it was --ink-soft when this
   note was written), so this rule cannot reach either.

   The h1 override has to be here rather than on the shared h1 rule because
   that rule sets --logo-grey-text for every page heading on the site; the
   inner pages' h1 sits on --paper, not on a photograph, and must keep it. */
.home .hero h1,
.home .hero .lede {
  color: var(--hero-ink);
}

/* Batch 12, CHANGE 1 — THE HERO PANEL.

   Batch 11 put black on the bare banner and reported what it got: 2.63:1 at
   the whole-image median, 2.82:1 over the text region, 43% of pixels at 3:1.
   That is the measurement that produced this ruling. The lettering is not
   asked to beat the photograph any more; it is given its own ground and the
   photograph is left alone underneath. Black on this panel is 21.00:1 — the
   maximum the sRGB gamut has — in both modes, at every pixel, with no
   dependence on what the banner is doing behind it.

   TRANSLUCENCY, measured and declined. Composited over the actual region the
   panel covers (1774x887 file, every other pixel, black text throughout):

       white @   worst pixel   median pixel   share >= 4.5:1
        100%       21.00:1        21.00:1          100%
         98%       20.16:1        20.34:1          100%
         95%       18.83:1        19.50:1          100%
         92%       17.68:1        18.60:1          100%
         90%       16.93:1        18.02:1          100%
         85%       15.01:1        16.65:1          100%

   Every one of those clears AA against the WORST pixel behind it, so the
   brief's condition is met at any of them and translucency was a live option.
   Solid is taken anyway: the panel was ruled because the photograph reaching
   the text is the problem, and 5% of it reaching the text is still the
   problem, smaller. If Tina wants the tie to the photograph, 95% is safe on
   these numbers and is one value on --hero-panel — rgb(255 255 255 / 0.95) —
   and nothing else moves.

   SIZED TO THE CONTENT, NOT THE VIEWPORT. --measure-panel is derived from the
   lede's measure plus its own padding (see that token); the panel is centred
   in whatever the section's gutters leave and never spans the band. The
   photograph therefore shows on ALL FOUR SIDES at every width. Measured, with
   the section's own --space-6 (128px) above and below throughout:

       viewport   panel    banner visible each side
        1920px    752px      584px
        1440px    752px      344px
        1024px    752px      136px
         768px    736px       16px
         360px    328px       16px

   The two narrow figures are the shared gutter (--gutter, 1rem below 62rem),
   not a special case: below the breakpoint the panel takes the width the
   gutters leave it and the cap above stops binding. 16px is a thin band but
   it is a real one, and the 128px above and below is untouched at every
   width, so the banner is never fully covered on any viewport.

   ROUNDED, at --space-1 (8px). This is the site's one radius: .sec-photo and
   the zoom view already carry it, so the panel is not introducing a shape
   language, it is joining the one that exists. At 752px wide the 8px reads as
   a softened corner rather than a rounded box, which is the intent — square
   corners on a white rectangle over a photograph read as a crop or a missing
   image, and the direction's "no shadows, no gradients" leaves the radius as
   the only thing available to say "this is a surface, laid on top".

   The sign-up button and the badge artwork are unchanged and simply sit here
   as they are, per the brief. Two adjacencies the panel newly creates, both
   measured and both passing, neither acted on: the accent button fill against
   the white panel is 10.41:1 light and 3.39:1 dark (a UI boundary, which
   wants 3:1), and the button's own label on its fill is 10.41:1 light and
   5.66:1 dark, exactly as it was before the panel existed. */
.home .hero-panel {
  width: 100%;
  max-width: var(--measure-panel);
  margin-inline: auto;
  /* Batch 14, CHANGE 7 — --space-3 on all four sides, at every width. It was
     --space-4 vertical / --space-3 horizontal here and --space-4 all round
     above the breakpoint; the 62rem override is gone, so the padding is one
     figure now and --measure-panel's sum has one input to answer to. */
  padding: var(--space-3);
  background: var(--hero-panel);
  border-radius: var(--space-1);
}

/* Batch 14, CHANGE 7 — the hero lede steps down with the headline, from
   --type-large to --type-base, and takes the measure --measure-panel is
   derived from. Both are scoped to the hero: the inner pages' ledes and the
   shared .lede rule are untouched. Black on white, unchanged at 21.00:1. */
.home .hero .lede {
  max-width: var(--measure-hero-lede);
  font-size: var(--type-base);
}

/* Batch 20 — the two middle ledes range LEFT; the h1, the opening line and the
   "Read What is a Man?…" line stay centred. Tina's ruling, and the reason is
   measure: those two paragraphs are the long ones, and a centred rag over four
   and six lines is the hardest thing on this panel to read. The short lines
   either side of them are one line each, where centring costs nothing.

   text-align ONLY. The block itself is still centred in the panel by
   `.home .hero h1, .home .hero p { margin-inline: auto }` above, so the column
   does not move — only the rag inside it goes from both edges ragged to one.
   Spacing is untouched: these are still .lede, so they still take
   `.hero .lede { margin: 0 0 var(--space-3) }` and the gaps between all four
   paragraphs stay the one figure they have been since batch 19.

   Scoped .home .hero so it cannot reach the inner pages' ledes, and written as
   a second class on the element rather than :nth-child so that splitting or
   reordering the paragraphs later does not silently re-align the wrong ones. */
.home .hero .lede-left {
  text-align: left;
}

/* The caption is the one thing on the panel whose colour had to move. See
   --hero-caption: --ink-soft is mode-dependent and its dark value measures
   2.81:1 on white, so the panel would have shipped an AA failure in dark mode
   if this rule did not exist. Scoped to the hero, so the token cannot reach
   any other .badge-note the site might grow. */
.home .hero .badge-note {
  color: var(--hero-caption);
}

.home .hero .appbadges {
  justify-content: center;
}

/* solas spread (batch 5B, EDIT B) — from 62rem up there is room to set the
   line as one row: five items with the pipes riding the gaps between them.
   Below that the compact centred form above stands instead, because the line
   cannot fit one row beside the wordmark without being wrapped off it. 62rem
   is this file's only breakpoint literal, shared with the wide layout below.

   Batch 6, CHANGE 3 — the row no longer justifies across the container.
   space-between stretched the eight gaps to 87.67px each; they are now a
   fixed --gap-solas (44px, half of that) and the shorter row is centred.
   The trailing letter-space of the last item is still cancelled, which also
   keeps the centring optical rather than a pixel off to the left.

   Two consequences of trading elastic gaps for fixed ones, both handled
   here. The breakpoint moved from 40rem to 51rem: space-between used to
   absorb any shortfall by shrinking, so the old figure was the width at
   which the TEXT alone fit, but the row became a fixed width and needed
   816px of viewport to clear the gutters. (Batch 8 moved it again, to 62rem,
   because "the row fits" and "the row fits BESIDE THE WORDMARK" are not the
   same question and batch 7 changed which one is being asked. See the header
   note and the sweep below.) And flex-wrap is now wrap, not nowrap — if the
   line ever does outgrow its box (a fallback font with wider caps, say), it
   breaks to a second centred row instead of silently overflowing the header
   on both sides.

   Batch 7, CHANGE 3 — justify-content becomes flex-end, so the row sits
   flush against the container's right edge instead of centred in its box;
   see the .solas note. The trailing letter-space cancellation below now does
   the job it was originally named for.

   Width sweep (Batch 8, CHANGE 3 — eighteen widths, 360 -> 1920, measured on
   ALL FOUR pages; every page returned identical figures, and the two
   boundaries were then bisected to the pixel):

       360-499    3 rows   89.97px   compact, own row, text wraps to 2 lines
       500-657    2 rows   67.58px   compact, own row, text on 1 line
       658-991    1 row    41-45px   compact, riding the wordmark's line
       992-1920   1 row    45.39px   spread, riding the wordmark's line

   The bar gets SHORTER as the viewport grows and never taller: 89.97 ->
   67.58 -> 45.39, monotonic across the whole range. There is exactly one
   one-row/two-row transition, at 658px. The 500px boundary is not a second
   one: the wordmark and the solas are on separate rows on both sides of it,
   and what changes is only whether the compact line wraps within its own row.
   Below 500px it cannot be made to fit one line without moving the type,
   which is locked, so it wraps — the fallback the compact form was designed
   for. Zero overflow, zero clipping and zero horizontal document scroll at
   every width on every page.

   One measured detail that is NOT clipping: from 992px up the .solas box
   reports scrollWidth 3px wider than clientWidth. That is the negative
   margin-inline-end below cancelling the last item's trailing letter-space —
   2.8px of empty space, no ink, overflow visible, landing inside the gutter
   and never near the viewport edge. It is batch 6/7 behaviour on an untouched
   rule; batch 8 changed only the width at which this block switches on.

   Line breaking in a wrapping flex container happens BEFORE shrinking, so the
   row can never be squeezed off its measure: it moves to the next line first,
   and if it is still too wide there (a fallback face with wider caps) its own
   flex-wrap breaks it into two rows. Nothing in either path overflows.

   BATCH 15, CHANGE 4 — THIS BLOCK IS REMOVED, and with it the whole spread
   form. Everything above is kept as the record of what it was and how its
   figures were arrived at, because it is the measurement the new placement is
   derived from; the rules themselves are gone.

   The spread form WAS the flush-right placement. Its five fixed --gap-solas
   gaps existed to lay the line across the far end of the band, mirroring the
   wordmark on the left — which is precisely the placement Tina's ruling moves
   the solas out of. Pulled hard against the wordmark, those 40px gaps would
   have pushed the line 267px wider than the text needs (733.45px against
   466.58px) and forced a wrap in the 992-1055px band, where the pair sits on
   one row today. So the compact form now carries every width, the string is
   identical in both forms, and the only thing lost is the spreading.

   The trailing letter-space cancellation went with it: --track-solas' 2.8px on
   the last item was a flush-right correction, and there is no right edge to
   land on any more. That is also the end of the 3px scrollWidth-over-
   clientWidth reading the note above records from 992px up — .solas now
   reports the two equal. --track-solas itself is untouched and still sets the
   tracking inside each sola. */

/* wide layout — section label left, text right, together filling the
   container exactly. 62rem is the second breakpoint literal: see the
   header note. Below this width everything is one centred column with
   the label stacked above its text. */
@media (min-width: 62rem) {
  :root {
    --gutter: var(--gutter-wide);
  }

  section {
    display: grid;
    grid-template-columns: var(--col-label) minmax(0, 1fr);
    column-gap: var(--col-gap);
  }

  section > * {
    grid-column: 2;
  }

  section > h2 {
    grid-column: 1;
    grid-row: 1;
    margin-bottom: 0;
  }

  section.hero {
    display: block;
  }

  /* Batch 12's panel-padding override lived here — --space-4 all round from
     the wide breakpoint up. Batch 14, CHANGE 7 removed it: the panel's padding
     is --space-3 at every width now, which is the figure --measure-panel's new
     sum is built on, so there is nothing left for this query to change. */

  /* CHANGE 5 — alternating section layout, front page only. The default
     above is label-left / prose-right; .flip mirrors the grid so the prose
     takes the wide left column and the label (icon + title) sits in the
     narrow right one. The label is right-aligned there so it hugs the
     container's right edge exactly as the unflipped label hugs the left.

     This lives inside the 62rem query on purpose: below that width every
     section is a single column with the label stacked above its prose, and
     there are no sides left to alternate between. */
  .home section.flip {
    grid-template-columns: minmax(0, 1fr) var(--col-label);
  }

  .home section.flip > * {
    grid-column: 1;
  }

  /* Batch 10, CHANGE 5 — the label selector is .sec-label, not h2. See the
     wrapper note above: the label is a wrapper now, and it is the wrapper that
     takes the column and the side. */
  .home section.flip > .sec-label {
    grid-column: 2;
    grid-row: 1;
    text-align: right;
  }

  /* align-items has to reach the h2 INSIDE the wrapper as well, because the h2
     is a flex column of its own carrying the icon and the title, and only its
     own align-items moves those two to the flipped edge. text-align above is
     inherited by the h2, so that one is set once. */
  .home section.flip > .sec-label,
  .home section.flip > .sec-label h2 {
    align-items: flex-end;
  }

  /* Batch 8 follow-up (Tina's ruling, 2026-08-22, from the deployed draft) —
     the prose in the photo sections read as separated blocks instead of one
     body of text.

     CAUSE, measured at 1280px: this section is a grid, so the label and the
     prose SHARE ROWS. The first paragraph sits in row 1 beside the label, and
     row 1 is as tall as the taller of the two. Putting a 204px photo in the
     label took that row from 91.8px to 311.8px, so the first paragraph was
     stretched to a 279.8px box and the second was pushed to the row beneath
     it — about 100px of dead space after the first paragraph's last line.
     Every gap BETWEEN paragraphs measured a normal 32px, on every section on
     the page; the hole was the shared row, not the paragraph spacing.

     align-items:start does not fix it. That stops the paragraph's box being
     stretched, but row 2 still begins where the tall row 1 ends, so the hole
     stays exactly where it was and only stops being attributable to the
     paragraph. The rows themselves have to stop being shared. In a grid a
     cell holds one element, so the choice is a wrapper around the many prose
     children or taking the one label element out of the row flow. The label
     is one element, so the label is what moves.

     FRONT PAGE ONLY, and only at this breakpoint: the label floats into its
     column and the prose goes back to normal flow, where paragraphs stack on
     their own margins and nothing is distributed or stretched. Both columns
     start at the top of the section because that is simply where a float and
     the flow beside it begin. display:flow-root makes each section contain
     its own float, so a label taller than its prose lengthens ITS OWN section
     rather than leaning into the next — the photo column ending lower than
     the prose is the expected outcome here, not a fault to correct.

     The prose margin is --col-label + --col-gap, the same 328px the grid's
     first column and column-gap occupied, so the text column still starts
     328px in, still runs to --measure, and still ends flush with the
     container's right edge. Nothing about the measure or the rhythm moved.

     The floats are physical because float has no reliable logical keyword;
     the margins beside them are logical, as everywhere else in this file. In
     this LTR document the two agree exactly.

     The inner pages keep the grid above, untouched. They carry no photos, so
     their labels never outgrow their first paragraph and the shared row never
     shows. Only body.home is rerouted here. */
  .home section:not(.hero) {
    display: flow-root;
  }

  /* Batch 9, CHANGE 1 — the label stack rides UP so the title, not the icon,
     is what lines up with the prose.

     The icon was already the top row of this stack; what moved is where the
     stack sits. Before, the h2's top met the prose's top, which put the title
     73px below the first line of text in all eleven sections — the icon held
     the line and the title floated well under it. Pulling the stack up by
     exactly the icon and its gap lands the title's line box on the first
     line's line box and leaves the icon riding in the space above, inside the
     section's own 96px top padding with 24px still clear of the hairline.

     The figure is the two tokens that create the offset, never a measured
     literal: change --icon-size-home or --space-2 and this follows them.

     Residual, stated: the title's TEXT sits ~2px above the prose's first line
     of text after this, because a 12px line and a 21px line carry different
     half-leading inside line boxes that now start together. Aligning the line
     boxes is the alignment that holds when either type size changes; matching
     the ink instead would need a raw pixel nudge, which this file does not
     allow. At 12px against 21px the 2px is not visible.

     Wide layout only. Below this breakpoint the label sits ABOVE its prose in
     one column, where there is no first line beside it to meet, and pulling it
     up would drag the icon into the section before it.

     Batch 10, CHANGE 5 — the element this rule floats is .sec-label, the
     wrapper, not the h2 inside it. The offset is unchanged and still measured
     from the icon: the icon is still the top row of the stack, it is just one
     level deeper in the markup than it was. margin-bottom:0 is what the h2 used
     to take from the `section > h2` rule above, and the wrapper takes it here
     for the same reason — a float's bottom margin lengthens the float, and in
     this layout the label's spacing to its own prose is set by the columns, not
     by a margin under it. */
  .home section:not(.hero) > .sec-label {
    float: left;
    width: var(--col-label);
    margin-top: calc(-1 * (var(--icon-size-home) + var(--space-2)));
    margin-bottom: 0;
  }

  .home section:not(.hero) > :not(.sec-label) {
    margin-inline-start: calc(var(--col-label) + var(--col-gap));
  }

  /* the flipped sections put the same label on the right. These two rules
     match the specificity of the pair above, so they stand only by sitting
     after them — do not reorder. */
  .home section.flip > .sec-label {
    float: right;
  }

  .home section.flip > :not(.sec-label) {
    margin-inline-start: 0;
    margin-inline-end: calc(var(--col-label) + var(--col-gap));
  }

  .notify {
    padding: var(--space-2) var(--space-4);
  }
}
