/* =====================================================================
   ABOUT -- the paired sheet for templates/about.php

   Story 2 of `_bmad-output/specs/spec-about-redesign/SPEC.md` (CAP-1
   render half, CAP-2, CAP-3). It replaces the medallion's sheet
   wholesale: the disk, the four quarter-arc tabs, the circular text flow
   and the 35mm filmstrip are preserved byte-for-byte in medallion.css and
   are not referenced from here.

   WHAT THIS SHEET IS FOR, IN ONE SENTENCE. The page behind it exists as a
   drawing -- the "NEW About" frame of `Content Pages - Flush Split White`
   (`oiFIkg88DHxhQKIW68bG2V`, frame `75:278`, 1400x4802) -- and Joe's own
   verdict on that drawing is that it is *"neither color-correct nor
   positioning-correct"*. So the drawing settles STRUCTURE and settles
   nothing about paint or geometry, and every value below was re-derived
   against the site's system rather than lifted off the artboard.

   THE NORMALISATION, RECORDED HERE BECAUSE THIS IS WHERE IT LANDED.
   `_bmad-output/specs/spec-about-redesign/drawn-values-audit.md` carries
   the measurements; this is what each one ships as.

     drawn                         ships as                  why
     ---------------------------   -----------------------   ----------
     all nine headings, in the     KEPT, as                  Joe's ruling,
       site's current-tab green      --about-heading-accent    2026-09-29
     facet labels                  --about-facet-label-ink   Joe's ruling; the
                                     (the slate, as ink)       drawn value, kept
     facet + prose fill, a light   --about-band-bg           Figma's default
       grey nothing here paints      (--ground-band)           rectangle grey
     hero + prose fill #3c5158     --color-bg-surface        already correct
     the footer's grey             not carried               site chrome, and stale
     radii 11 / 28px               KEPT, as two tokens       Joe's ruling; the
                                                              drawing has two
     gutter 63px / 81px            --band-gutter             7px and 25px off 56
     panel inset 44px              2 rhythm units at the cap
     facet inset 13px              1 rhythm unit (20px)      13 is off-grid
     facet gutter 70px             3 rhythm units (60px)
     panel gaps 26 / 48px          1 and 2 rhythm units      inconsistent as drawn
     facet labels drift 5px        aligned                   a drawing slip
     text overlaps the portrait    two clean grid columns    the fault being fixed

   THIS TABLE WAS THREE ROWS LONGER AND MORE CONFIDENT ON 2026-09-28. The
   first shipped version of this sheet normalised the drawn greens away and
   squared all nine boxes. Joe reviewed the installed page against the
   Figma on 2026-09-29 and overruled both, and the rows above now read KEPT
   where they read "ships as something else". The reasoning that was
   retired is not left standing under a new value -- each token below says
   what changed and why.

   TWO OF THE DRAWN LITERALS ARE NOT WRITTEN IN THIS FILE AT ALL, not even
   in a comment: the two retired greys. The heading green is now KEPT and
   is still not written here, because it is PINNED --
   checks/06-pane-guards.sh scans every theme/*.css and requires it to be
   declared by exactly two named roles, `--color-psr-label` and
   `--color-tab-current` -- so a literal here fails the suite rather than
   forking silently. It is read through the token, which is
   contact.css:411-414's shipped pattern for the same colour. That check
   strips comments before scanning, so quoting the value would have passed
   it and still broken the story's boundary; the audit companion
   (`drawn-values-audit.md`) is where the literals live.

   ONE DEPARTURE THAT IS VISIBLE AND IS MEANT TO BE. The facet body text
   rises from the drawn ~13px to the site's one body size, so the four
   boxes grow taller than drawn. The prose measure was the second such
   departure and Joe ruled on it directly, 2026-09-29, when offered the
   drawn full-panel width and a middle option: *"Leave the 760 cap as
   shipped."* So the empty fill to the right of each prose panel is the
   accepted look and is not a loose end.

   NO LINK RULES. legal.css:186-215 and contact.css:210-247 both carry four
   -- `nv-content-wrap` is absent on a templated Page, so an unowned anchor
   falls to Neve's own `a:hover` and tints to a palette blue nobody decided.
   Page 27's `post_content` contains no anchors today (checked 2026-09-28,
   and legal.css's own comment recorded the same count on 2026-09-25), and
   half of this page sits on the slate, where the white-ground link roles
   would be the wrong values anyway. If a prose link is ever added here,
   the fix is to promote the shared group in site-interactive-states.css by
   adding this root to its selector list -- NOT to make a third copy.
   ===================================================================== */

/* =====================================================================
   THE TOKENS -- "one knob each"

   TEN TOKENS, and two declarations underneath that spend them. Nearly
   everything this page paints or spaces resolves from them, and the
   spacing values in particular are all integer multiples of
   `--about-gap`. That is the point: Joe's *"then changing as a whole if
   needed"* is a value edit here, not a hunt through the file.

   THE CLAIM IS "NEARLY", AND THE EXCEPTIONS ARE NAMED SO THE SENTENCE
   CANNOT BE QUOTED AS THOUGH IT WERE ABSOLUTE. Four values below are
   neither a token nor a multiple of the rhythm: the prose `font-size` and
   `line-height`, the facet grid's `minmax(200px, 1fr)` floor, and the
   `760px` reading measure. Each is argued at the declaration that makes
   it, and none of them is a colour or a radius -- which is the scope CAP-2
   actually covers.

   THEY ARE BOUND ON `.about-page`, NOT ON `:root`, and the reason is what
   the value IS rather than how many `:root` blocks this theme has. (It
   has three -- site-fonts.css:7, style.css:72 and
   site-interactive-states.css:161, the last of which holds both roles
   this sheet aliases.) `--color-bg-surface` is a COMPONENT-SURFACE role:
   flush-split-white.css:311-317 and simple-stack.css both bind it scoped
   to the component that paints it, because the site's GROUNDS are white
   (`:root`) and dark (`.joetennis-ground-dark`) and the slate is neither.
   Binding it at `:root` would make it a third ground by assertion.
   Consistently with that, this template's map entry carries no `ground`
   key, and checks/05 pins that count at six.
   ===================================================================== */
.about-page {
	/* THE TWO RADII, AND THEY ARE THE DRAWING'S. Joe ruled on 2026-09-29,
	   after reviewing the installed page against the Figma: *"Pray don't
	   stand on ceremony about rounded strictly meaning interactive."* So
	   `spec-interactive-color-states` CAP-7 -- *a rounded box on this site
	   can be operated; a squared one cannot* -- is OVERRIDDEN here, by name
	   and on the record, rather than reinterpreted.

	   THE ARGUMENT THAT USED TO STAND HERE IS RETIRED, NOT RE-POINTED, and
	   saying so is the point of this paragraph. It read: the drawing gives
	   nine static boxes THREE radii between them -- ~9-10 on the facets,
	   ~25-26 on the band panels, 28 on the slate ones -- and the split
	   tracks the FILL COLOUR rather than the box's role, which is the tell
	   that they were drawn in two sittings rather than decided. **The
	   premise was a measurement error.** Re-measured at 1:1 off the
	   rendered artboard on 2026-09-29: all four facet boxes are 11px and
	   all six large panels are 28px (28/29 across them, the +/-1 being
	   antialiasing). Two values, applied by BOX SIZE, which is a decision
	   and a legible one. `drawn-values-audit.md` section 2 carried the same
	   wrong claim and is corrected with it.

	   SO THERE ARE TWO TOKENS RATHER THAN ONE, and the split is the
	   drawing's own: the small boxes take the small radius. The nine boxes
	   are recorded in the standing-exception registry at
	   site-interactive-states.css:583-606, beside `simple-stack`'s two, as
	   static-but-rounded. That registry is the honest scope of CAP-7 and
	   this entry is what keeps it honest.

	   They are still ONE LINE EACH to change, which was the whole reason
	   the first version was a token rather than nine literals -- and the
	   reason a reversal cost a value edit instead of a redesign. */
	--about-panel-radius: 28px;

	/* The facet boxes, at the drawing's smaller value. `simple-stack.css`
	   keeps its own provisional radius the same way, at one use site. */
	--about-facet-radius: 11px;

	/* THE RHYTHM UNIT. Every gap, inset and page margin below is this
	   value or an integer multiple of it. The drawing has 26px between
	   prose panels, 48px between the facet row and the first one, 13px
	   inside a facet, 44px inside a panel and four facet labels drifting
	   5px out of line with each other -- five numbers for what is one
	   decision. 20px is this site's rhythm unit and is already
	   `--band-gutter`'s floor. */
	--about-gap: 20px;

	/* THE PANEL INSET -- 2 rhythm units at the cap, against the drawn 44.
	   Fluid rather than fixed because a 40px inset on a 390px phone is a
	   quarter of the screen; `3vw` reaches 40 at 1334, so it is flat
	   across the whole desktop range and only gives way below it. The
	   floor is one rhythm unit. */
	--about-panel-inset: clamp(20px, 3vw, 40px);

	/* THE BAND FILL, ALIASED AND NOT RESTATED. The literal lives once, at
	   `--ground-band` in site-interactive-states.css:243, where the
	   Portfolio archive's page area and every Flush Split White case-study
	   band already read it. flush-split-white.css:318-320 is the precedent
	   for keeping a template-local NAME over an upstream value: the name
	   is the role the grey plays here and is the one line a deliberate
	   divergence would be written on, while the value still has exactly
	   one home. Retunes the grey everywhere at once, which is the
	   consequence Joe asked for when he named it. */
	--about-band-bg: var(--ground-band);

	/* THE INK ON WHITE AND ON THE BAND -- one role, because it is one
	   value in both places. 17.63:1 on the white page ground and 13.87:1
	   on `--about-band-bg`. Aliased from `--ground-ink`
	   (site-interactive-states.css:217) for the same reason as above.

	   THESE TWO ALIASES CARRY NO FALLBACK, WHICH LOOKS LIKE AN OVERSIGHT
	   NEXT TO THE BAND BLOCK BELOW AND IS THE OPPOSITE OF ONE. A fallback
	   is a second copy of the value, and the whole argument for aliasing
	   is that the value has ONE home -- `--about-ink: var(--ground-ink,
	   #16191d)` would put the ink in two places and quietly keep painting
	   the old one after the role moved. The band block's fallbacks are a
	   different case and it explains why: an unresolved property inside a
	   `padding` shorthand or a `min()` invalidates the WHOLE declaration,
	   so the failure is geometric and total. An unresolved colour just
	   leaves the element inheriting, which on this page means readable
	   text on an unpainted panel. Degrade, do not duplicate. */
	--about-ink: var(--ground-ink);

	/* THE SLATE. The bench's third ground and already the drawing's own
	   value, so this is the one colour the artboard got right. It is a
	   component SURFACE rather than a site ground -- see the block comment
	   above -- and the role name is the shared one the other two
	   surface-painting sheets use, so the value reads the same here as it
	   does there. */
	--color-bg-surface: #3C5158;

	/* THE INK ON THE SLATE. 8.37:1, the same pairing flush-split-white.css
	   ships. The drawing puts the current-tab green here instead, which
	   measures 3.92:1 on the slate -- a figure CLAUDE.md records being
	   accepted once, for one small current-tab LABEL, and which is a
	   different proposition on a page's headlines. */
	--about-surface-ink: #FFFFFF;

	/* THE HEADING ACCENT -- the drawing's green, KEPT. Joe ruled it on
	   2026-09-29 after seeing the normalised page: the `<h1>`, the hero's
	   greeting and all four prose-section `<h2>`s take it, on the white
	   ground and on both panel fills.

	   IT IS A REFERENCE, NEVER A LITERAL, and that is what keeps the suite
	   green rather than a stylistic preference. `--color-tab-current` is
	   declared exactly once, at site-interactive-states.css:507, and
	   `checks/06-pane-guards.sh` fails on a second declaration of that
	   value anywhere in theme/*.css. contact.css:411-414 does the same
	   thing through `--state-statement-fg` and states the same reason. A
	   local alias gives the role a name this sheet's prose can use while
	   the value keeps one home.

	   THE CONTRAST FIGURES, RECORDED BECAUSE THE DECISION SHOULD STAY
	   LEGIBLE AND NOT BECAUSE IT IS IN DOUBT. Measured against the three
	   grounds this page paints:

	     on the white page ground    2.14:1
	     on --about-band-bg          1.68:1
	     on --color-bg-surface       3.92:1

	   WCAG's floor is 3:1 for large text and 4.5:1 for body, so the
	   headline and the two band panels are under it and the slate panels
	   sit at the figure CLAUDE.md already records this site accepting for
	   the medallion's current-tab label. **Joe was given all three numbers
	   and asked for the green anyway, 2026-09-29.** That is a ruling, not
	   an oversight, and it is written here so that whoever finds the ratios
	   later finds the decision beside them. The one-line reversal is
	   changing this alias to `var(--about-ink)`.

	   THE ROLE IS NOT FORKED FROM `--color-tab-current`, and the
	   difference matters if the marker is ever retuned: this page follows
	   it. If About should stop following it, that is when this alias
	   becomes its own value -- and `checks/06` would then have an opinion
	   about where that value is declared. */
	--about-heading-accent: var(--color-tab-current);

	/* THE FACET LABEL INK -- the drawing's, KEPT, and it is deliberately a
	   SURFACE colour used as ink. Joe ruled it on 2026-09-29. The first
	   version of this sheet normalised it to `--about-ink` on the argument
	   that spending a surface role as ink is the kind of borrowing that
	   makes a palette stop meaning anything; that argument is overruled
	   here and the four `<h3>` labels take the slate, which is also the
	   fill of the panel directly above them.

	   IT IS THE ONE COLOUR CHANGE IN THIS RULING WITH NO CONTRAST COST:
	   8.37:1 on the white page ground, the same pairing as
	   `--about-surface-ink` on the slate, inverted. */
	--about-facet-label-ink: var(--color-bg-surface);

	font-family: var(--font-body);
	color: var(--about-ink);
}

/* =====================================================================
   THE BAND -- CAP-3, satisfied by structure

   THREE DECLARATIONS, AND THEY ARE THE SAME THREE NEVE'S OWN `.container`
   TAKES. `--container` is Neve's, fed by the `neve_container_width`
   theme_mod ({mobile:748, tablet:992, desktop:1400}, read live
   2026-09-28); `--band-gutter` is theme/style.css:72-74, and since
   v1.14.0 `body .container` reads OUR value for it. So the header above
   this page and every panel inside it resolve their edges from one pair
   of numbers, which is the 0.00px agreement `spec-sitewide-alignment-band`
   exists for.

   EVERY PANEL IS A CHILD OF THIS ELEMENT AND IS THEREFORE FULL-BAND BY
   CONSTRUCTION. That is CAP-3's six-rectangle assertion satisfied by
   structure rather than by six matching declarations, any one of which
   could drift. The drawing's own 63px/81px pair is exactly that failure
   already happened: five bands at one inset and the facet row at another,
   18px apart, with nothing in the drawing explaining it.

   THE GUTTER IS STATED HERE AND NOWHERE INSIDE. Every measure below this
   point is relative to a box that already sits inside it, so subtracting
   `--band-gutter` a second time deeper in the page would inset content
   from an edge it is not next to. That mistake shipped in this file's
   first draft -- see the prose measure block for what it cost.

   THE FALLBACKS ARE NOT DECORATION. An unresolved custom property inside
   a `padding` shorthand or a `min()` is invalid at computed-value time and
   drops the whole declaration -- which for `padding-inline` means the page
   going flush to the viewport edge. `15px` is what shipped through
   v1.13.41, so a missing style.css degrades to that rather than to
   nothing. (The colour aliases above deliberately carry none; the token
   block says why.)

   A FLEX COLUMN RATHER THAN BLOCK FLOW, so the rhythm between panels is
   `gap` and cannot be collapsed away by a margin on a block that came
   out of `post_content`. `align-items` is left at `stretch`, which is
   what makes every child full-band without saying so six times.
   ===================================================================== */
.about-page__band {
	display: flex;
	flex-direction: column;
	gap: var(--about-gap);
	box-sizing: border-box;
	max-width: var(--container, 1400px);
	margin-inline: auto;
	padding-inline: var(--band-gutter, 15px);
	padding-block: calc(var(--about-gap) * 3) calc(var(--about-gap) * 5);
}

/* =====================================================================
   HEADINGS -- what this sheet decides about type, and what it does not

   NO `font-size` AND NO `letter-spacing` ON ANY HEADING. Those two are
   load-bearing omissions: Neve's typography theme_mods emit an inline
   <style> binding `--h1fontsize`, `--h2letterspacing` and friends --
   values that live only in the database and that CLAUDE.md records every
   heading on this site as having inherited without anyone deciding it.
   Measuring a heading inside a template and writing the number down here
   is how this repo has got it wrong before. legal.css and contact.css
   leave both alone; so does this.

   `font-weight` LEFT THAT SET ON 2026-09-29, AND THE SENTENCE ABOVE USED
   TO NAME THREE PROPERTIES. Joe: *"the headline should be the same weight
   as 'Hi. I'm Joe.' and all the other headers."* So it is stated, once,
   on the one heading that was not already 800 -- see the `<h1>` rule
   below.

   THE DISTINCTION THE RULE IS ACTUALLY DRAWING, now that it has an
   exception, is between a value NOBODY HAS DECIDED and a value SOMEBODY
   HAS. Size and tracking are still the former here: nothing in this
   repo has ever chosen an `h1` size for this page, so reading one off a
   rendered page and freezing it would be inventing a decision. Weight is
   now the latter -- Joe decided it, against the rest of the page --
   which makes leaving it to `neve_h1_typeface_general.fontWeight` (400)
   the riskier option, not the safer one, because that dial can move in
   the Customizer and take the headline with it.

   THE PROSE `font-size` BELOW IS THE EXCEPTION, AND IT NEEDS SAYING
   BECAUSE THE PARAGRAPH ABOVE ARGUES AGAINST IT. `--bodyfontsize` is one
   of those same theme_mods, so by the letter of the rule 18px should be
   left alone too. It is stated for one reason: 18px is already this
   theme's shipped prose size in five other sheets -- legal.css:138 and
   :158, contact.css:171, simple-stack.css:147, and both editorial-split
   sheets -- so this is a SIXTH copy of a settled decision rather than a
   new measurement taken off a rendered page. The distinction the rule is
   really drawing is between a value this theme has decided and a value it
   would be reading back out of Neve: headings are the latter, prose is
   the former. If prose size is ever tokenised, all six move together.

   `color: inherit` IS THE ONE THING ASSERTED ON HEADINGS, and it is a
   guard rather than a style. Half this page sits on the slate, so a
   heading that takes its colour from anywhere but its panel is unreadable
   on two of the four prose sections and on the hero. Neve paints headings
   from the palette at low specificity; inheriting from the panel is what
   makes one rule per panel enough. Paragraphs and list items are in the
   list for the same reason.
   ===================================================================== */
.about-page :is(h1, h2, h3, h4, h5, h6) {
	font-family: var(--font-heading);
	color: inherit;
}

/* THE THREE HEADING RULES THAT OVERRIDE THE `inherit` GUARD ABOVE, and
   the doubled class on two of them is specificity arithmetic rather than
   style. The guard is `.about-page :is(...)` at (0,1,1); a bare
   `.about-page__title > *` would be (0,1,0) and lose. Repeating the root
   raises these to (0,2,0) and (0,2,1), so the cascade is decided by the
   numbers and not by which rule happens to come last in the file.

   WHAT IS NOT IN THE LIST IS AS DELIBERATE AS WHAT IS. Joe's ruling names
   the `<h1>`, the hero's greeting and the four prose `<h2>`s -- every
   heading the template puts in a `.about-page__title` wrapper -- and the
   four facet `<h3>`s. A SUB-heading (h4-h6) inside a prose section is
   none of those: it keeps `inherit`, so it reads as body ink on whichever
   fill it lands on, which is the only thing that works on both. */
.about-page .about-page__band > h1,
.about-page .about-page__title > * {
	color: var(--about-heading-accent);
}

.about-page :is(p, li) {
	color: inherit;
	font-size: 18px;
	line-height: 1.6;
}

/* THE ACCESSIBLE-NAME WRAPPER, and it exists for the `id` rather than for
   anything visual. A `<section>` is exposed as a region only if it has an
   accessible name, and the headings here come out of `render_block()`, so
   the template cannot put an id on the heading element itself. It wraps
   one instead -- contact.php:318-337's shipped pattern. The only thing
   this rule does is stop the wrapper and the heading inside it from
   stacking two margins: the WRAPPER takes the rhythm (from the panel's own
   `> *` rule) and the heading takes none. */
.about-page__title > * {
	margin: 0;
}

/* =====================================================================
   THE HEADLINE, AND EVERYTHING ELSE THAT LANDS AT BAND LEVEL

   The headline sits on the white page ground, above and OUTSIDE the hero
   panel, spanning the full band -- the drawing's arrangement, and the
   drawing is authoritative on arrangement. It is emitted as a direct child
   of the band with no wrapper of its own, so it is addressed by tag.

   NO MEASURE CAP ON THE `<h1>`, unlike everything else here. At 1400 it
   wraps to two lines across the full 1288 band, which is what the drawing
   shows and what a display line wants; the cap exists for reading columns,
   and a headline is not one.

   EVERYTHING ELSE AT THIS LEVEL IS CAPPED, THOUGH, and the second rule is
   not hypothetical tidying. Anything an editor puts above the first
   heading -- or under the headline and before the greeting -- lands here
   as a direct child of the band, and without this it would set 18px prose
   across 1288px: roughly 190 characters a line, and the exact composition
   the prose panels below are capped to avoid. The selector lists the three
   region wrappers and the `<h1>` as the exclusions rather than naming what
   it catches, because what it catches is "whatever wp-admin produced",
   which cannot be enumerated.
   ===================================================================== */
.about-page__band > h1 {
	margin: 0;
	max-width: none;
	/* 800, MATCHING THE `h2`s AND CONTACT'S GREEN `h1`. Joe's ruling,
	   2026-09-29. Neve's `neve_h1_typeface_general.fontWeight` is 400,
	   which is where the thin strokes came from, and it is the only
	   heading on this page that was not already ExtraBold.

	   IT IS ALSO THE PAGE'S ONE REAL LEGIBILITY IMPROVEMENT rather than
	   only a consistency fix. `--about-heading-accent` on white is
	   2.14:1, and 2.14:1 at 400 across a 72px display line is the worst
	   pairing on the page -- thin strokes are what actually makes a
	   low-contrast colour hard to read, which is why WCAG's own floor
	   drops from 4.5:1 to 3:1 as text gets larger and heavier. At 800 it
	   reads the way the 48px/800 `h2`s in the same green already do. The
	   ratio does not move; the legibility does.

	   800 IS IN THE FONT REQUEST. functions.php asks Google Fonts for
	   Noto Sans 400;600;700;800 and `checks/05` asserts two of those
	   weights, so this is not a synthesised bold. */
	font-weight: 800;
}

.about-page__band > :not(h1, .about-page__hero, .about-page__facets, .about-page__section) {
	width: min(760px, 100%);
	margin: 0;
}

/* =====================================================================
   THE HERO

   A slate panel, ONE column, with the portrait floated right inside the
   copy and the text wrapping the figure's own silhouette.

   IT WAS A TWO-COLUMN GRID UNTIL v1.14.3 and the grid is gone rather than
   hidden. Joe, 2026-09-29: *"I'd like to wait longer on resize for it to
   stack, ideally with the text wrapping to its alpha channel. When it
   does break, having it above the paragraphs."* A float delivers all
   three from one DOM order -- it wraps, it un-floats into normal flow at
   the break, and the constraint that forced the old 1280 breakpoint is
   gone with the grid that caused it.

   THE ROW HEIGHT IS STILL THE COPY'S, which is the thing from v1.14.2
   that must not regress. A float does not extend its container UNLESS
   something establishes a block formatting context around it -- and the
   panel is a flex item of `.about-page__band`, which does exactly that.
   So the panel's height is `max(copy, float)`, and the float is sized
   (below) so that the two are near enough equal at the desktop band. The
   copy still sets it whenever the text is the taller of the two.

   THE PANEL'S INSET NOW APPLIES ON THE LEFT ONLY, not left-and-top. Joe:
   *"make the portrait just a little bigger; like in the Figma, so it just
   touches the top of the head."* The asset has NO transparent margin on
   any side -- the head is at y=0 and the figure spans the full 796x1271 --
   so "touches the top of the head" is a statement about the PANEL, not
   about the crop: the Figma bleeds the image to the panel's top edge as
   well as its right and bottom. The top inset therefore moves off the
   panel and onto the copy, where the text still needs it, and the float
   reaches y=0.

   ------------------------------------------------------------------
   THE SHAPE, AND WHY IT IS A POLYGON RATHER THAN `url()`

   medallion.css:1103-1182 is this repo's other `shape-outside` block and
   its mechanics are inherited rather than re-derived: shape-outside
   applies to FLOATS and nothing else; a float only shapes the line boxes
   that come AFTER it; `shape-margin` does the inset; and a shape can only
   ever REDUCE a float area, never increase it.

   THAT BLOCK ALSO ARGUES AGAINST PASTED POLYGONS -- *"a pasted polygon
   stops matching the photograph the moment Joe changes it"* -- and it is
   right, so the reason one is used here needs stating rather than
   glossing.

   `shape-outside: <image>` scales the image to the float's reference box
   and reads its alpha. That is exact ONLY if the float box has the
   image's own aspect ratio. This float does not: it is 796x953, the top
   75% of the file, because that is where Joe's cut lands (*"just below
   the elbows so not to show the length of the shirt"*). Feed `url()` the
   uncropped file and the shape is the WHOLE figure squashed into a
   three-quarter-height box -- the arms land 105px above where they are
   drawn, and text tucks into an elbow that is not there. Measured, not
   assumed: the arms are at y=884 of 1271, which maps to 316px in the
   squashed shape and 421px in the render.

   SO THE POLYGON IS TRACED FROM THE SAME CROP THE PAGE DRAWS, and it is
   generated rather than eyeballed. 26 samples down the left edge of rows
   0..952 of the source PNG, at alpha > 16, expressed as percentages of
   the float box:

     python3 -c "
     from PIL import Image
     im=Image.open('_bmad-output/inputs/Image-Assets/about/about-portrait-cutout.png').convert('RGBA')
     W,H=im.size; a=im.getchannel('A').load(); CROP=953; N=26
     f=lambda y:next((x for x in range(W) if a[x,y]>16), W)
     print(', '.join('%.1f%% %.1f%%'%(f(min(CROP-1,round(i*(CROP-1)/N)))/W*100,
                     min(CROP-1,round(i*(CROP-1)/N))/(CROP-1)*100) for i in range(N+1)))"

   ONLY THE LEFT EDGE IS TRACED, because the float is on the right and a
   float's shape is only consulted on the side the text wraps. The two
   `100%` corners close it.

   IT GOES STALE IF THE PHOTOGRAPH CHANGES, and so does the `aspect-ratio`
   two declarations below it -- both are facts about attachment 601. That
   is ONE thing to remember rather than two, and the featured image is the
   only way to change the portrait, so the trigger is visible: swap the
   image in wp-admin, re-run the snippet above, paste both values.

   THE WRAP IS PRONOUNCED AT THE HEAD AND SLIGHT AT THE ARMS, which is the
   effect rather than a limitation: the head spans 44.6% to 100% of the
   box at the top, while at 92.3% down the silhouette reaches the float's
   own left edge. `shape-margin` keeps text off the silhouette everywhere
   it can; where the figure reaches the edge it cannot, which is why the
   float also carries a left margin -- see there.
   ===================================================================== */
.about-page__hero {
	overflow: hidden;
	box-sizing: border-box;
	margin-top: var(--about-gap);
	padding: 0 0 0 var(--about-panel-inset);
	border-radius: var(--about-panel-radius);
	background-color: var(--color-bg-surface);
	color: var(--about-surface-ink);
}

/* The copy keeps the inset the panel gave up, on the two edges where text
   still needs one. `min-width: 0` and `overflow-wrap` are the same pair
   they always were: one unbreakable string -- a pasted URL, a long
   compound -- would otherwise overflow the panel it sits in. */
.about-page__hero-copy {
	min-width: 0;
	overflow-wrap: break-word;
	padding-top: var(--about-panel-inset);
	padding-bottom: var(--about-panel-inset);
}

/* THE FLOAT.

   `aspect-ratio: 796 / 953` IS THE CROP, EXPRESSED AS GEOMETRY. 953 is
   75% of the file's 1271, so the box is exactly the shape of the visible
   part and `object-fit: cover` with `object-position: top` has nothing
   left to decide. The crop is therefore FIXED at every viewport, which is
   an improvement on v1.14.2, where it drifted with the copy's line count.

   `width: 380px` IS THE DIAL, AND IT IS A FLAT PIXEL VALUE RATHER THAN A
   PERCENTAGE FOR A REASON THAT TOOK A SWEEP TO FIND.

   THE REQUIREMENT IS BOTTOM-FLUSH AT EVERY TWO-COLUMN WIDTH. Joe,
   2026-09-29: *"That image definitely has to stay flush with the
   bottom."* v1.14.3 shipped `30.4%`, which is the width at which the
   float's height and the copy's are equal at the 1288 band -- exact
   there, and drifting to a 36.8px gap by 1279 and 246.6px by 768,
   because a narrower panel makes the copy taller while the float shrinks
   with it. All of that is what this value exists to remove.

   THERE IS NO BOTTOM-ANCHORED FLOAT IN CSS. A float aligns to the top of
   the line box it appears in; there is no `align-self: end` for one, and
   the classic lead-spacer trick medallion.css:416 names needs a height of
   `copy - figure`, which is not expressible here because the panel has no
   definite height to take a percentage of. Measuring it would need
   JavaScript, and CAP-1 forbids that outright. So flush is reached from
   the other side: **make the FIGURE the taller of the two**, and the
   panel's height becomes the figure's rather than the copy's. Flush then
   holds by construction rather than by arithmetic.

   THE NUMBER IS THE SMALLEST WIDTH AT WHICH THE FIGURE OUTGROWS THE COPY,
   swept live on dev.joetennis.com. The requirement is not smooth, because
   the copy's height moves in whole 28.8px lines and a wider float takes a
   line back off the text column:

     viewport   panel   needs    text column
      1920      1248    379px      828
      1400      1248    379px      828
      1320      1175    378px      755
      1279      1138    376px      724
      1200      1068    421px      602
      1100       979    416px      516
      1024       911    484px      397

   A STRAIGHT LINE THROUGH THOSE POINTS WAS TRIED AND IT FAILED, which is
   why this is two flat values and not a `calc()`. `calc(672px - 23.33%)`
   fits the plateaus and undershoots the step between them -- a 10.1px gap
   at 1240 and nowhere else, which is exactly the "close but not flush"
   being fixed. A flat value cannot undershoot a step it sits above.

   SO: 380px from 1280 up, 430px from 1100 to 1280, and stacked below --
   see the two media blocks. Both were swept at 15-20px intervals across
   their whole range with no gap at any width.

   380px KEEPS THE FIGURE JOE ALREADY APPROVED. It renders 380x455 against
   v1.14.3's 379x454, which is the same picture. The 430px band below it
   is 430x515, and the step UP as the viewport narrows is the honest cost
   of flush: the copy is taller there, so the figure has to be.

   THE NEGATIVE TOP MARGIN IS WHAT MAKES THE FIGURE TOUCH THE PANEL'S TOP.
   The inset moved off the panel and onto the copy -- text still needs one
   -- but the float lives INSIDE the copy, so it started the inset's worth
   below the panel edge, which is the 40px gap above the head Joe asked to
   lose. Pulling it back by exactly the value the copy states means the
   two cannot drift: retune `--about-panel-inset` and both move together.
   It is the one negative margin on this page and it is doing a real job
   rather than nudging something into place.

   THE LEFT MARGIN IS THE CLEARANCE `shape-margin` CANNOT GIVE. A shape is
   clipped to the float's margin box, so where the silhouette reaches the
   float's own left edge -- the elbows, at 92% down -- `shape-margin` has
   nowhere to expand into and text would touch the figure. A margin widens
   the margin box on that side while `border-box` keeps the polygon
   measured against the image, so the strip is real clearance the shape
   cannot eat. */
/* THE SELECTOR REPEATS THE PARENT ON PURPOSE. `.about-page__hero-copy > *`
   further down sets `margin: 0 0 var(--about-gap)` on every child of this
   container, which at (0,1,0) would tie with a bare
   `.about-page__hero-figure` and win on source order -- taking the left
   margin with it and adding 20px to the float's height contribution. */
.about-page__hero-copy > .about-page__hero-figure {
	float: right;
	width: 380px;
	aspect-ratio: 796 / 953;
	margin: calc(-1 * var(--about-panel-inset)) 0 0 var(--about-panel-inset);
	overflow: hidden;
	shape-outside: polygon(100% 0%, 44.6% 0.0%, 36.6% 3.9%, 34.4% 7.7%, 31.7% 11.6%, 31.3% 15.3%, 31.8% 19.2%, 30.8% 23.1%, 31.5% 26.9%, 32.9% 30.8%, 34.9% 34.7%, 36.8% 38.4%, 38.8% 42.3%, 38.8% 46.1%, 34.0% 50.0%, 25.3% 53.9%, 16.2% 57.7%, 12.4% 61.6%, 10.7% 65.3%, 8.9% 69.2%, 7.5% 73.1%, 7.4% 76.9%, 6.0% 80.8%, 4.8% 84.7%, 2.5% 88.4%, 0.0% 92.3%, 2.1% 96.1%, 2.1% 100.0%, 100% 100%) border-box;
	shape-margin: var(--about-gap);
}

.about-page__portrait {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: top;
}

/* NO FEATURED IMAGE IS A FIRST-CLASS STATE, not a broken one. The template
   guards on the RESOLVED URL rather than on `has_post_thumbnail()` -- this
   site has a post whose `_thumbnail_id` points at an attachment that no
   longer exists -- so when there is no portrait the figure is simply not
   emitted. The copy then fills the panel and the one thing it lacks is
   the inset on the right, which the float's own presence was standing in
   for.

   `:only-child` USED TO CARRY THIS AND SILENTLY STOPPED WORKING when the
   figure moved inside the copy at v1.14.3 -- the copy became the hero's
   only child in BOTH states, so the selector matched always and the
   padding would have shown up behind the float. `:has()` asks the
   question that is actually being asked. */
.about-page__hero-copy:not(:has(.about-page__hero-figure)) {
	padding-right: var(--about-panel-inset);
}

/* =====================================================================
   THE FACET ROW

   Four claims, each a label on the white ground with a filled box under
   it. A FACET'S LABEL SITS OUTSIDE ITS BOX -- that is the drawing's
   arrangement and it is the opposite of a prose section, whose heading
   sits inside its panel. The template emits them as siblings for exactly
   that reason, and emits the box only when there is copy to put in it.

   `auto-fit` RATHER THAN `repeat(4, 1fr)` AND A BREAKPOINT. CAP-4 asks
   that the row reflow rather than be hidden, and nothing about "four" is
   a decision -- it is however many h3 blocks page 27 carries. `auto-fit`
   gives 4 across the desktop band, 3, then 2, then 1 as the viewport
   narrows, with no breakpoint to keep in step with a count that can
   change in wp-admin. The 200px floor is what stops a box narrower than
   its own label.

   THE TWO AXES TAKE DIFFERENT GAPS, AND THE SPLIT IS THE WHOLE REASON
   THIS IS NOT ONE `gap`. Across, the boxes are 3 rhythm units apart
   against the drawn 70px -- and they widen regardless of that change,
   because the row now spans the full 1288 band instead of the drawing's
   1238. DOWN, there is only ever a row gap when the grid has wrapped, so
   that value is the vertical rhythm between stacked facet boxes on a
   narrow screen -- where 60px would be three times what every other pair
   of boxes on the page uses. Setting it per-axis means the stacked case
   needs no media query and cannot fall out of step with `auto-fit`, which
   has no breakpoint to hang one off anyway.

   EQUAL HEIGHTS COME FROM THE GRID, which stretches every item in a row to
   the tallest; the box then takes the leftover space under its label via
   `flex: 1`. That is exact while the labels are one line each, which the
   four shipped ones are ("Patent Holder", "Storyteller", "Innovator",
   "Teacher"). A label that wrapped would push its own box down by a line
   -- visible, cheap to see, and not worth a `subgrid` to pre-empt.

   THE DRAWN LABELS DRIFT 5px OUT OF LINE WITH EACH OTHER. That is a
   drawing slip rather than a rhythm, and a grid cannot reproduce it.
   ===================================================================== */
.about-page__facets {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	column-gap: calc(var(--about-gap) * 3);
	row-gap: var(--about-gap);
	margin-top: var(--about-gap);
	margin-bottom: var(--about-gap);
}

.about-page__facet {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

/* `h3` AND NOTHING ELSE, because h3 is the only level that can land here:
   the walk opens a facet on level 3 alone, and h4-h6 join their region's
   blocks as ordinary content. The first draft listed five levels, which
   was a selector written against a bug rather than against the template. */
.about-page .about-page__facet > h3 {
	margin: 0 0 var(--about-gap);
	color: var(--about-facet-label-ink);
}

/* A label with no box under it is a real state -- a heading typed in
   wp-admin before its paragraph is written -- and it must not trail a
   margin into the row's bottom edge. */
.about-page__facet > :last-child {
	margin-bottom: 0;
}

/* The box itself. Its label's ink is decided at
   `--about-facet-label-ink` above, which is where the reasoning for
   spending a surface colour as ink lives. */
.about-page__facet-box {
	flex: 1;
	box-sizing: border-box;
	padding: var(--about-gap);
	border-radius: var(--about-facet-radius);
	background-color: var(--about-band-bg);
	color: var(--about-ink);
}

/* =====================================================================
   THE PROSE SECTIONS

   Full-band panels, each with its `h2` INSIDE it at the top. They
   ALTERNATE, starting with the band fill: 1 band, 2 slate, 3 band, 4
   slate. The template counts prose sections rather than regions, so a
   fifth one appended in wp-admin continues the alternation instead of
   restarting it.

   ONE RHYTHM UNIT BETWEEN THEM -- the band's `gap`, with no extra margin.
   The facet row above carries the second unit as its own `margin-bottom`,
   which is the drawing's 48px normalised.

   THE MEASURE IS CAPPED, AND THIS IS THE SECOND OF THE TWO VISIBLE
   DEPARTURES. Run full width, a prose line inside a 1288 panel is ~1208px
   -- roughly 190 characters, two and a half times a comfortable measure.
   760px is this codebase's reading column, shipped by legal.css:83 and
   (at 1200) by contact.css:148.

   `min(760px, 100%)`, AND THE MISSING SECOND TERM IS THE CORRECTION THIS
   BLOCK EXISTS TO RECORD. Those two sheets write the same column as
   `min(760px, 100% - 2 * var(--band-gutter, 15px))`, and the first draft
   here copied that form. It is right THERE and wrong HERE, because the
   gutter term is what holds a PAGE-LEVEL column off the viewport edge --
   `.legal-page` is a direct child of an unpadded container and has no
   horizontal padding of its own. This column's parent is a panel that
   already states `padding: var(--about-panel-inset)` and sits inside a
   band that already states `padding-inline: var(--band-gutter)`, so the
   borrowed term subtracted the page gutter a THIRD time, from a box two
   levels inside it. At 390px the panel's content box is 310 and the rule
   resolved to 270: 40px of dead space, on the right only, with the prose
   no longer aligned to the facet text above it. `100%` is the panel's
   content box, which is the thing this measure is actually relative to.

   LEFT-ALIGNED, NOT CENTRED, unlike legal.css's own use of the same
   column. That page IS the column; here the column sits inside a painted
   panel, and centring it would leave the text's left edge disagreeing
   with the panel's -- which is the one thing this page is being rebuilt
   to stop doing.
   ===================================================================== */
.about-page__section {
	box-sizing: border-box;
	padding: var(--about-panel-inset);
	border-radius: var(--about-panel-radius);
}

.about-page__section--band {
	background-color: var(--about-band-bg);
	color: var(--about-ink);
}

.about-page__section--slate {
	background-color: var(--color-bg-surface);
	color: var(--about-surface-ink);
}

.about-page__section > * {
	width: min(760px, 100%);
	margin: 0 0 var(--about-gap);
}

.about-page__section > :last-child,
.about-page__hero-copy > :last-child,
.about-page__facet-box > :last-child {
	margin-bottom: 0;
}

/* The rhythm inside a panel, for anything `post_content` puts there that
   the rules above have not already claimed -- a pasted list, an image, a
   quote. Nothing is dropped and nothing is left touching its neighbour. */
.about-page__hero-copy > *,
.about-page__facet-box > * {
	margin: 0 0 var(--about-gap);
}

/* =====================================================================
   NARROW VIEWPORTS -- CAP-4, IN TWO STEPS

   The page stays the same page: same copy, same sections, same order,
   nothing withheld. Only the hero moves, and it now moves twice.

   THE ORDER OF CONSTRAINTS IS JOE'S, 2026-09-29, and the first two fight:
   the figure must stay flush with the panel's bottom; there is no
   JavaScript; the alpha wrap stays; and the break comes as late as those
   allow. Flush is the one he called non-negotiable, so it is what the
   other three are spent on.

   1280 IS A WIDTH STEP, NOT A LAYOUT ONE. The float goes 380px to 430px
   and nothing else changes -- still two columns, still wrapping, still
   flush. It exists because the copy gains a line somewhere between 1281
   and 1240, and a float that did not grow with it would end short. The
   figure therefore gets BIGGER as the viewport narrows across this
   boundary, which reads backwards and is deliberate: flush is worth more
   than a monotonic figure, and the alternative was a 10px gap.

   1100 IS WHERE TWO COLUMNS STOP WORKING AT ALL. Below it the copy
   outgrows any float that still leaves a readable column beside it -- the
   measured requirement is 484px at a 1024px viewport, against 397px of
   text left over. The crossing was swept at 4px intervals and sits
   between 1088 (11.2px short) and 1092 (flush); 1100 is the round number
   above it, and the ~8px of headroom is there because the line-count step
   that decides it moves if the copy is edited.

   THE BREAKPOINT WAS 768 AT v1.14.3 AND JOE ASKED FOR IT TO GO LOWER, NOT
   HIGHER. It has gone to 1100 because he then ruled flush the more
   important of the two. Recorded plainly so the reversal is not read as
   drift: 768 was reachable only by letting the figure float short of the
   bottom, which is the thing being fixed.

   THE CROP DOES NOT MOVE TO BUY ANY OF THIS. `aspect-ratio: 796 / 953` is
   untouched, so the cut stays at 75.0% at every width in both two-column
   bands and in the stacked one -- letting the figure grow into the panel
   by showing more of the figure would put the shirt back, which is the
   whole point of the crop. The `shape-outside` polygon is untouched for
   the same reason: it is traced against that aspect ratio, and the aspect
   ratio has not changed.

   STACKED, THE ORDER IS GREETING, PORTRAIT, PARAGRAPHS. Joe asked for the
   image *"above the paragraphs"* at the break, and the DOM has it before
   the greeting because a float can only reach the panel's top edge from
   there. `order` on the title is what reconciles the two.

   THE DOM AND VISUAL ORDERS THEREFORE DISAGREE BELOW 1100, and that is
   worth naming rather than leaving for someone to find. A screen reader
   follows the DOM, so the portrait's alt text is announced before the
   greeting at every width -- including above 1100, where it also renders
   first. The alternative was announcing it in a different place at
   different widths, which is worse.

   BOTTOM-FLUSH DOES NOT APPLY STACKED, and nothing here tries to make it:
   the figure sits between the greeting and the paragraphs, so the panel's
   bottom edge belongs to the last paragraph. It keeps the same 75% cut
   and right-aligns, capped at 420px so a 1099px viewport does not get a
   portrait wider than the column it sits under.
   ===================================================================== */
@media (max-width: 1279.98px) {
	.about-page__hero-copy > .about-page__hero-figure {
		width: 430px;
	}
}

@media (max-width: 1099.98px) {
	.about-page__hero-copy {
		display: flex;
		flex-direction: column;
	}

	.about-page__title {
		order: -1;
	}

	.about-page__hero-copy > .about-page__hero-figure {
		float: none;
		width: min(100%, 420px);
		margin: 0 0 var(--about-gap) auto;
		shape-outside: none;
	}
}
