/**
 * Medallion — page stylesheet. A FROZEN FORK of about.css.
 *
 * Retired from /about/ and preserved as a worked example. This sheet is
 * about.css as it shipped at v1.13.39 with two namespaces renamed --
 * `.about-page` -> `.medallion-page` and `--about-*` -> `--medallion-*` --
 * and nothing else changed. templates/medallion.php's own docblock carries the
 * full account of the fork and of why the helper-function rename was the one
 * that could not be skipped. Do not reconcile this file with about.css; they
 * are meant to diverge.
 *
 * The prose below is the original's, kept because it is still true of THIS
 * design. Its references to `spec-about-page` and to stories 1-5 are
 * provenance, not stale paths.
 *
 * Pairs with templates/medallion.php + medallion.js. Enqueued by the Neve child
 * theme's functions.php ONLY when the rendered post is a WordPress Page
 * (`is_page()`) whose assigned Template slug is `templates/medallion.php` — the
 * FOURTH entry in `$joetennis_page_template_assets`. That map has no default
 * fallback: a Page on any other template, this site's Home and Blog
 * included, misses the lookup and loads nothing from here.
 *
 * It is enqueued through the same `joetennis_child_enqueue_template_pair()`
 * emitter the seven project templates use, so it carries the same three base
 * dependencies — `joetennis-child-style`, `joetennis-site-fonts` and
 * `joetennis-site-interactive-states`. The third is the load-order edge the
 * whole interactive-states model rests on (functions.php:66-101): without it
 * WP_Styles is free to put this sheet AHEAD of the shared one, which would
 * silently lose every override declared here with no visible diff in either
 * file — and every token the rules below read is declared in that sheet's
 * `:root`, so the edge is not theoretical here.
 *
 * IT SHIPPED EMPTY IN STORY 1, deliberately. `checks/03-asset-contract.sh`
 * asserts the pair the map contracts for against the pair the live page
 * actually serves, so the render path was only proven once there was a
 * real file at this name. And functions.php:502-506 records the failure that
 * story most needed to rule out: a `css` key naming a file that does NOT
 * exist still registers with WP_Styles, which then drops every handle
 * depending on it outright. The symptom is "none of my CSS loaded" on a page
 * whose PHP is fine.
 *
 * TWO SHEETS IN ONE FILE, AND THE SECOND ONE IS A SINGLE `@media` BLOCK.
 * Everything above that block is STORY 2's flat tab bar: a row of text
 * labels with a rule under it, correct and readable at every width.
 * Everything inside it is STORY 3's medallion — the slate disk, the four
 * quarter-arc tabs with curved labels, the cut-out portrait and the text
 * flowing inside the circle around the figure — and it applies at wide
 * breakpoints ONLY. The flat bar was not a first draft of the ring and has
 * not been replaced by it; below the breakpoint the page still renders
 * exactly as v1.13.18 did, which is the acceptance criterion story 4 will
 * build on.
 *
 * NOT ONE GEOMETRIC NUMBER IN THIS FILE IS ITS OWN. Every radius, inset,
 * offset and size in the medallion block reads
 * `var(--medallion-*)`, and those are bound inline on
 * `.medallion-page__medallion` by joetennis_medallion() in
 * templates/medallion.php, which generates them from a handful of ratios. The
 * one exception is the breakpoint itself, because a media query cannot
 * read a custom property — see the note on the block.
 *
 * TYPE IS NOT GEOMETRY, AND THE LITERALS IN THE MEDALLION BLOCK ARE
 * DELIBERATE. `26px` / `1.15` / `-0.01em` on the pane heading, `15px` /
 * `1.6` on its body, `14px` between paragraphs and `0.3px` on the arc
 * label are all read straight off `CutOutAll.dc.html` at its drawn D of
 * 1040 — the artboard's own inline styles, not values anyone here chose.
 * They carry no ratio because the mockup FIXES type the way it fixes radii
 * and insets; what it does not fix is responsive behaviour, and the
 * medallion is pinned at 1040px, so there is nothing for a ratio to scale
 * against. Story 4 draws its own at D = 390 (13px labels there, against 17
 * here) and will have to state them again rather than derive them from
 * these. Said out loud because this file's own claim above would otherwise
 * make them read as unexplained literals.
 *
 * THE COLOUR STORY CHANGED AT STORY 3, having been "none at all" at
 * stories 1 and 2. The flat bar above still declares no colour value:
 * `--ground-ink`, `--ground-edge`, `--state-link-rest-fg` and
 * `--current-marker` are all sitewide `:root` tokens from
 * site-interactive-states.css, and `--font-heading` is site-fonts.css:8.
 * The medallion block declares four, every one of them ruled by Joe on
 * 2026-09-24 against a rendered comparison rather than a description:
 *
 *   `--color-bg-surface: #3C5158`   the disk. A COMPONENT SURFACE, the way
 *                                   flush-split-white.css:312 and three
 *                                   other templates already redeclare that
 *                                   same token on their own root. NOT
 *                                   `'ground' => 'dark'`, which would put
 *                                   `.joetennis-ground-dark` on <body> and
 *                                   repaint the whole white page around a
 *                                   disk that is only 1040px wide.
 *   `#ffffff`                       arc label at rest, 8.37:1 on the slate.
 *                                   Also the pane's ink and heading, as
 *                                   drawn.
 *   `#00e5ff`                       arc label on hover, 5.44:1, and the
 *                                   component's own focus ring.
 *   `var(--color-tab-current)`      the current arc's label. #8cc201,
 *                                   3.92:1 — SHORT OF 4.5:1 AND RULED IN,
 *                                   which is the second time this theme has
 *                                   ruled that exact cell in on that exact
 *                                   slate (flush-split-white.css:385,
 *                                   decision 7). The brighter #d3ff5b
 *                                   (7.24:1) was rendered alongside it and
 *                                   not taken. Do not darken or brighten it
 *                                   without Joe, and do not "fix" it to the
 *                                   passing lime.
 *
 * `#8cc201` DOES NOT APPEAR IN THIS FILE, and `checks/07` bans the literal
 * outright. The token it resolves from moved to `:root` in
 * site-interactive-states.css in this same story precisely so that it
 * could not be copied here: it used to live on `.flush-split`'s own root,
 * unreachable from this sheet, and `checks/06` counted declarations in
 * THAT FILE ONLY — so a copy here would have forked the value silently.
 * That check now scans every `theme/*.css`.
 *
 * A FIFTH PAINTED VALUE IS NOT IN THAT LIST, AND IT IS NOT A RULING. The
 * arc segments carry `rgba(0, 0, 0, 0.14)`, a 14% black wash over the
 * slate, taken verbatim from the artboard's own `.seg` rule. It is not a
 * colour so much as a shade of the surface it sits on — it composites
 * against `--color-bg-surface` rather than replacing it, and it forks
 * nothing. But it IS doing visible work, and it is doing MORE of it than
 * the artboard asked: the drawn design separates the arcs
 * with this wash AND a 1px cyan hairline, and the hairline is the value
 * the next paragraph explains is deliberately absent. So the wash plus the
 * 1.5-degree gaps are now the whole of the segmentation. Named here rather
 * than left in the rule, so it is looked at when the hairline question is
 * settled.
 *
 * TWO DRAWN VALUES ARE DELIBERATELY NOT CARRIED. The artboard rings the
 * disk and strokes each arc with `rgba(0, 229, 255, 0.15)`, which is
 * `--color-card-ring` — a token hosted on `.flush-split`'s root,
 * unreachable from here, and pinned by `checks/06` to one declaration in
 * that file. Restating the literal here would be exactly the fork the
 * paragraph above describes, and hoisting a second token was outside this
 * story. The 1.5-degree gaps between the arcs and the 14% black wash on
 * each segment carry the segmentation without it. Flagged for Joe rather
 * than quietly dropped.
 *
 * THE BEM PREFIX IS `medallion-page__`, not `medallion__`, and the root class
 * this sheet hangs everything off is `.medallion-page` — deliberately not the
 * bare template slug the other seven templates use. `.flush-split` and
 * `.simple-stack` are distinctive compounds; `about`, which this root was
 * renamed FROM, is a single common dictionary word, and medallion.js resolves
 * its root with `document.querySelector()`, which takes the FIRST match in
 * document order. A menu item, widget or editor block carrying
 * `class="about"` above the wrapper would have captured it silently. The
 * `-page` suffix is kept rather than reconsidered, so that this stays a pure
 * rename of a preserved design. Keep every selector added here under
 * `medallion-page`.
 *
 * THE FLAT BAR AND THE RING SAY "CURRENT" IN DIFFERENT COLOURS, AND THAT
 * IS DELIBERATE RATHER THAN A MISS. The flat bar's current tab takes
 * `--current-marker` (#8100fc), the sitewide "this is the one you are on"
 * value the nav and the breadcrumb already read — a value chosen against
 * the WHITE page it sits on, where it measures 6.29:1. The ring's current
 * arc takes `--color-tab-current` (#8cc201), a value chosen against the
 * SLATE disk, where the purple measures 1.33:1 and no purple in this
 * theme clears 4.5:1 at all (flush-split-white.css:466-471). Two grounds,
 * two answers. Do not "unify" them: one of the two would then be
 * unreadable on its own surface.
 */

/* =====================================================================
   THE TAB BAR
   ===================================================================== */

/* THE BAR IS A ROW OF TEXT LABELS WITH A RULE UNDER IT. No pill, no
   filled tab, no box at rest — the same posture flush-split.css:1000-1023
   takes for the card's bar, and for the same reason: this is deliberately
   NOT one of the four Family C round-means-clickable control categories
   site-interactive-states.css enumerates, so it must not grow a rounded
   box that would read as a claim it has not earned. A `tablist` is
   announced as one by its role; the shape does not have to say it twice.

   `flex-wrap: wrap` rather than a scroller or a breakpoint. The labels
   here are AUTHOR CONTENT — whole sentences like "Making possibilities
   tangible", not the 13px uppercase two-worders Flush Split's bar holds —
   and their combined width depends on copy nobody has written yet.
   Wrapping degrades into two rows; `flex: 1 1 0` equal columns would
   degrade into four unreadably narrow ones.

   The bottom rule uses `--ground-edge`, the sitewide alpha hairline. It
   composites against whatever ground story 3 lands on rather than
   camouflaging itself the way a fixed hex did when the case-study band
   arrived (CLAUDE.md records that failure on the header's border). */
.medallion-page__tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 24px;
	align-items: flex-end;
	margin: 0 0 32px;
	border-bottom: 1px solid var(--ground-edge);
}

/* `hidden` MUST WIN OVER THE `display` ABOVE, and this rule is the whole
   reason the fail-open story holds on the bar's side. The tablist ships
   carrying the `hidden` attribute from PHP; the UA sheet expresses that as
   `display: none` at specificity (0,0,0), so ANY class rule setting
   `display` on the same element silently un-hides it. The bar would then
   be visible to a visitor whose JS never ran — a tab bar that does
   nothing, which is precisely what shipping it hidden exists to prevent.

   The same trap applies to the panes below, where it is worse: medallion.js
   hides three of the four with `panel.hidden = true`, so a `display` rule
   on `.medallion-page__pane` would leave all four stacked on top of each
   other with the tabs apparently dead. Both guards are stated, both at a
   specificity that clears their own base rule. */
.medallion-page__tabs[hidden] {
	display: none;
}

/* COLOUR AND WEIGHT CARRY THE STATE, plus a 2px underline on the current
   tab. Rest ink is `--ground-ink`, the body colour, so a tab at rest reads
   as a heading rather than as a link — the bar is navigation WITHIN the
   page, and four things that look like links but do not leave the page is
   the more misleading of the two errors.

   `font-family: var(--font-heading)` is Noto Sans, from site-fonts.css.
   The 800 weight the archive card relies on is requested there too; this
   bar only asks for 600 and 700, both of which have been in the font
   request since v1.13.9.

   `border-bottom: 2px solid transparent` at rest, coloured when current:
   the box does not change size when the state changes, so nothing below
   the bar shifts by 2px as the visitor arrows along it. Declaring it
   `transparent` and recolouring is the cheap way to buy that; adding the
   border only on the current tab is the expensive way to lose it. */
.medallion-page__tab {
	appearance: none;
	background: none;
	border: none;
	border-bottom: 2px solid transparent;
	margin: 0;
	padding: 0 0 10px;
	font-family: var(--font-heading);
	font-size: 16px;
	font-weight: 600;
	line-height: 1.3;
	letter-spacing: normal;
	text-transform: none;
	text-align: left;
	color: var(--ground-ink);
	cursor: pointer;
	transition: color 0.2s ease, border-color 0.2s ease;
}

/* `text-transform: none` AND `letter-spacing: normal` ARE REAL
   DECLARATIONS, NOT NO-OPS, and both were measured rather than assumed.

   NEVE UPPERCASES EVERY BARE `<button>` ON THIS SITE. Injected live on
   2026-09-24, a `<button>` with no classes computes `text-transform:
   uppercase`, `letter-spacing: 0.5px`, `font-size: 13px`,
   `padding: 13px 15px`, Inter rather than Noto Sans. Flush Split's tab bar
   never noticed because it WANTS uppercase and states it. These labels are
   author-written sentences — "MAKING POSSIBILITIES TANGIBLE" is not a tab
   label, it is shouting — so this rule has to turn the inherited treatment
   off rather than merely decline to ask for it. The check that finds this
   class of leak is the one CLAUDE.md records twice already: inject the bare
   element into a live page and read the rules that match it.

   The letter-spacing half has a second source, and CLAUDE.md's typography
   entry is why. Neve's Customizer typography
   theme_mods emit an inline `<style>` binding `--h2letterspacing: -1px`
   and friends, and Neve paints `letter-spacing: var(...)` from
   low-specificity ELEMENT selectors. Nothing in this repo declares that
   property, so grepping the theme for it finds nothing and the page has
   it anyway. These labels are heading-font text at heading size; stating
   `normal` is what keeps them untracked. Measured the same way the archive
   card's two lines were (v1.13.9): against a bare element on a live page,
   not against any file here. */

/* SPECIFICITY (0,4,0) FOR A HOVER, AND NEVE IS THE ENTIRE REASON. This is
   the second bare `<button>` this theme has ever put on a page; the first
   was Flush Split's tab bar at v1.13.11, and it shipped washing blue.

   Neve paints `button:hover { background: var(--primarybtnhoverbg);
   color: var(--primarybtnhovercolor); border-color: ... }` at (0,1,1).
   `--primarybtnhoverbg` resolves to `nv-secondary-accent`, #2f5aae — a
   palette slot CLAUDE.md recorded as inert for years on the grounds that
   nothing in this theme consumed it. It was never inert; it was DORMANT,
   waiting for the first unstyled button. The base rule above holds
   `background: none` at (0,1,0), which Neve's (0,1,1) outranks, so the
   background half of this state is contested even though the colour half
   is not.

   Hence all three properties are stated here, at a specificity that clears
   Neve outright rather than tying it and depending on `$deps` order to
   break the tie. flush-split.css:1068-1072 records the same lesson from
   the live bug: state every property Neve's hover states, at a specificity
   that clears it.

   `--state-link-rest-fg` (#0b6b78) for the hover ink: the teal the site
   already uses for "you can act on this". Measured 6.20:1 on #ffffff,
   2026-09-24; rest ink #16191d measures 17.63:1. */
.medallion-page__tabs .medallion-page__tab[role="tab"]:hover {
	background: none;
	border-bottom-color: var(--ground-edge);
	color: var(--state-link-rest-fg);
}

/* SPECIFICITY (0,4,0), AND IT HAS TO BE — the same fight, on the same
   kind of element, that flush-split.css:1098-1102 records losing at
   (0,2,0) on the installed v1.13.10.

   site-interactive-states.css:1810-1815 ends its baseline ring with
   `[tabindex]:not([tabindex="-1"]):focus-visible`. `:not()` contributes
   the specificity of its ARGUMENT, so that selector is (0,3,0) — not the
   (0,1,1) its three siblings (`a`, `button`, `summary`) carry. These tabs
   hold a ROVING tabindex, so every one of them matches it, and a rule at
   (0,2,0) here would lose: the tab would take a black ring pushed 2px
   OUTWARD, which on a bar whose labels sit directly above a hairline is
   the exact collision the negative offset exists to prevent.

   That baseline's own docblock forbids raising it — "a baseline that
   outranks an enumerated rule stops being a baseline" — and it is already
   outranking one by accident via the `:not()`. Fixing that is a sitewide
   focus change on every element carrying a tabindex and is logged in
   deferred-work.md, not taken here. So this rule CLEARS (0,3,0) outright
   rather than tying it and depending on the `$deps` load order to break
   the tie. Tying is what shipped broken in v1.13.10 and had to be
   re-fixed.

   `--current-marker` (#8100fc, 6.29:1 on white) for the ring, and an
   INWARD `outline-offset` so it hugs the label instead of crossing the
   hairline under it. */
.medallion-page__tabs .medallion-page__tab[role="tab"]:focus-visible {
	outline: 2px solid var(--current-marker);
	outline-offset: -2px;
	border-radius: 3px;
}

/* THE CURRENT TAB, and the value is `--current-marker` on purpose. That is
   the sitewide "this is the one you are on" colour — site-nav's current
   item and the breadcrumb's hover both read it — so this bar says
   "current" in the same colour the rest of the site already does.

   It is NOT `--color-tab-current` / #8cc201. That value is hosted on
   flush-split.css's own template root and is unreachable from this sheet,
   and `checks/06:119-126` counts declarations in THAT FILE ONLY — so
   copying the literal here would have forked it silently with the suite
   still green. SPEC.md's constraint allows exactly two outs: resolve from
   the one declaration, or name a deliberately different value. This is the
   second, named here so the next reader does not "fix" it into agreement
   with Flush Split.

   TWO SELECTORS, AND THE SECOND ONE IS A BUG FIX, found 2026-09-24. This
   rule shipped as the bare `.medallion-page__tab[aria-selected="true"]`,
   (0,2,0) — below the hover rule above it at (0,4,0). So HOVERING THE
   CURRENT TAB ERASED "you are here": the ink went from `--current-marker`
   to `--state-link-rest-fg` and the 2px underline from the marker to
   `--ground-edge`, leaving the bar with no current tab at all for as long
   as the pointer rested on it. Nothing else on the page says which pane is
   showing, so that is the one state the hover must not be able to take.

   The `:hover` selector is (0,5,0) and clears the hover rule outright,
   rather than scoping the hover with `:not([aria-selected="true"])`. The
   `:not()` version looks tidier and is wrong for a reason worth keeping:
   Neve paints `button:hover { background: var(--primarybtnhoverbg) }` at
   (0,1,1), the base rule here holds `background: none` at only (0,1,0),
   and the hover rule above is the ONLY thing outranking Neve on that
   property. Excluding the current tab from it would hand exactly one tab —
   the one always on screen — back to Neve, and it would wash #2f5aae on
   hover. Raise the winner; do not narrow the rule that is doing the other
   job. */
.medallion-page__tabs .medallion-page__tab[role="tab"][aria-selected="true"],
.medallion-page__tabs .medallion-page__tab[role="tab"][aria-selected="true"]:hover {
	color: var(--current-marker);
	border-bottom-color: var(--current-marker);
	font-weight: 700;
}

/* =====================================================================
   THE PANES
   ===================================================================== */

/* The wrapper exists for medallion.js to find and for story 3 to hang the
   circular text flow on; it carries no layout of its own today. */
.medallion-page__panes {
	margin: 0;
}

/* SEE THE `[hidden]` NOTE ON THE TAB BAR ABOVE — this is the same guard
   and the same failure. Nothing here sets `display` on a pane, so the UA
   sheet's `display: none` for `hidden` is currently unopposed; the rule is
   stated anyway because the trap is invisible until the day someone adds
   `display: flex` to the pane for a two-column layout, and the symptom
   then is "the tabs stopped working", not "my display rule was too
   specific". */
.medallion-page__pane[hidden] {
	display: none;
}

/* A tabpanel carries `tabindex="0"` so a keyboard visitor can reach its
   content from the bar, which means it matches the (0,3,0) baseline ring
   as well. The pane is a large box and an outward 2px ring on it is
   correct and wanted — it is the tab's inward ring that is the exception —
   so nothing overrides it here. Stated so the omission reads as a decision
   rather than an oversight. */

/* THE IN-PANE HEADING IS HIDDEN ONLY WHEN THE TABS ARE LIVE. Each pane
   keeps the `<h2>` it was split on, because the heading is what makes the
   no-JS page read as four titled sections rather than four unlabelled
   slabs of prose. The moment medallion.js commits, that same text is the tab's
   label directly above, so leaving it would read twice.

   `.medallion-page--tabbed` is added in medallion.js's commit block, LAST, after
   every listener is attached — so if anything above it throws, this rule
   never matches and the headings stay. That is the same fail-open shape as
   the bar's `hidden`, expressed in the other direction: the enhanced state
   is what has to be switched ON.

   `> h2:first-of-type` rather than a class, because the heading comes from
   `post_content` and this template does not rewrite author markup — it
   splits it. It is element-based, so the whitespace the filters leave
   around it does not disqualify it.

   IT WAS `:first-child` UNTIL STORY 3 AND THAT STOPPED BEING TRUE. The
   medallion emits two `shape-outside` float boxes and a lead spacer as the
   pane's first children — a float only shapes the line boxes that FOLLOW
   it, so they cannot be moved after the heading — which left the `<h2>` as
   the fourth child and this rule matching nothing at all. The symptom would
   have been the heading rendering twice at EVERY width, the narrow one this
   story is not supposed to touch included, because those boxes are emitted
   whenever the medallion is available rather than only when it applies.
   `:first-of-type` is indifferent to what precedes it and carries the same
   (0,1,0), so nothing else in this file had to move. */
.medallion-page--tabbed .medallion-page__pane > h2:first-of-type {
	display: none;
}

/* =====================================================================
   THE MEDALLION
   ===================================================================== */

/* THE DISK'S SURFACE, DECLARED ON THIS TEMPLATE'S OWN ROOT. `#3C5158` is
   the slate the flip cards and the Flush Split hero media already use —
   deliberately NOT a palette slot, because it is a component surface
   rather than a site-wide ground role (CLAUDE.md records that reasoning on
   the palette itself). Four templates already redeclare
   `--color-bg-surface` on their own root exactly like this:
   flush-split-white.css:312, simple-stack.css:99, editorial-split.css:25
   and archive-project.css:105. This is the fifth.

   IT IS DECLARED OUTSIDE THE MEDIA BLOCK ON PURPOSE, next to nothing that
   reads it below the breakpoint. A custom property declaration paints
   nothing, so the narrow page is untouched, and the token then sits where
   the other four templates put theirs — on the root, one line, easy to
   find — rather than buried in a media query where a reader looking for
   "what colour is this template" would not think to check. */
.medallion-page {
	--color-bg-surface: #3C5158;
}

/* EVERY MEDALLION LAYER SHIPS OFF AND IS SWITCHED ON BY THE MEDIA BLOCK,
   never the other way round. The disk, the portrait, the curved arc
   labels and the text-flow float boxes are all emitted into the markup
   whenever the medallion is AVAILABLE — which is a pane-count decision
   PHP makes and has nothing to do with viewport width — so something has
   to keep them off the narrow page, and the safe direction is off by
   default.

   The other direction would have been one `display: none` per layer
   inside a `max-width` query, and it fails open in the wrong sense: a
   typo in the query, or a sheet that loads without it, puts a 1040px
   slate disk and four full-width invisible buttons on a phone. This way
   the same failure yields story 2's flat bar, which is a correct page. */
.medallion-page__disk,
.medallion-page__portrait,
.medallion-page__tab-art,
.medallion-page__flow,
.medallion-page__flow-lead {
	display: none;
}

/* =====================================================================
   THE MEDALLION — WIDE ONLY
   =====================================================================

   1280px, AND IT IS THE ONE NUMBER IN THIS FILE THAT IS NOT GENERATED. A
   media query cannot read a custom property, so the breakpoint cannot come
   from `--medallion-size` the way everything else in this block
   does. The contract it has to satisfy is arithmetic rather than
   aesthetic: the medallion is a FIXED 1040px box (see the next paragraph
   for why it cannot be fluid), so the breakpoint has to guarantee at least
   1040px of content width or the page grows a horizontal scrollbar.

   FROZEN FORK, SO THE PARAGRAPH BELOW IS LEFT AS IT SHIPPED AT v1.13.39 and
   only this note is added. v1.14.0 took the desktop container 1600 -> 1400
   and made the sitewide gutter ramp to 56px. Neither reaches this page:
   `.medallion-page` declares no width, max-width or padding of its own, so
   it runs at full `main` width and the 1040px box still has ~1265px of
   content at a 1280px viewport. Read "1600px" below as "1400px today"; the
   arithmetic it supports still clears.

   Neve's
   desktop container is 1600px with the row's own padding inside it, which
   leaves roughly 1230px of content at a 1280px viewport — 190px of
   headroom. IF `$d` IN templates/medallion.php EVER GROWS, THIS NUMBER HAS TO
   GROW WITH IT.

   WHY THE MEDALLION IS FIXED-SIZE RATHER THAN FLUID, which is a real
   constraint and not a shortcut. Each tab's hit area is
   `clip-path: path('…')`, and `path()` takes USER PIXELS: unlike the SVG
   beside it, which has a viewBox and scales, a clip path does not resize
   with its box. A fluid medallion would paint arcs at one size and accept
   clicks at another, with the divergence invisible until someone tried to
   press a tab near its edge. Pinning the box to the size the paths were
   generated for makes that impossible by construction. The mockup fixes
   relationships, not responsive behaviour; fixed-vs-fluid is the
   template's call, and this is the call.

   BELOW THIS WIDTH NOTHING IN HERE APPLIES and the page is byte-for-byte
   the v1.13.18 render. Story 4 owns the small breakpoint. */
@media (min-width: 1280px) {

/* GATED ON `clip-path: path()`, AND ON THE WHOLE BLOCK RATHER THAN ON THE
   TAB RULES ALONE. Every tab is a full-medallion button stacked on three
   others; without `path()` the clip never applies, the four boxes overlap
   completely, and only the last one in the DOM is pressable — three tabs
   that look right and do nothing. Gating only the tab rules would leave the
   disk, the portrait and the circular panes painted around a broken ring,
   which is worse than not drawing the medallion at all: with the whole
   block behind this query an engine without `path()` gets story 2's flat
   page, which is correct at every width.

   The brace below is closed at the foot of the block. The rules inside are
   NOT re-indented for it, so the diff that added this gate is two lines
   rather than four hundred. */
@supports (clip-path: path('M 0 0 Z')) {

	/* THE ONE POSITIONED ANCESTOR. `min-height` rather than `height`
	   because without JS the panes flow BENEATH the disk rather than
	   inside it (see the panes section below), and the box has to grow to
	   hold them.

	   `max-width` AND `overflow` ARE A GUARD AGAINST A VALUE THAT LIVES
	   ONLY IN THE DATABASE. The 1040px box fits because Neve's desktop
	   container is 1600px — `neve_container_width` in
	   `theme_mods_neve-child`, which is in no file in this repo, which
	   CLAUDE.md records under "Site configuration that exists only in the
	   database", and which HAS ALREADY MOVED ONCE (1170 -> 1600, story 2
	   of spec-sitewide-alignment). Nothing stops it moving again from the
	   Customizer, and if it drops below ~1100 the medallion is wider than
	   its column and every page on this site grows a horizontal scrollbar
	   — a sitewide symptom from a change nobody would connect to this
	   page. The guard trades that for a clipped disk, which is local,
	   visible and obviously wrong. */
	.medallion-page__medallion {
		position: relative;
		min-height: var(--medallion-size);
		margin: 0 auto;
		overflow: hidden;

		/* THE SCALE. One number, and everything the medallion draws is
		   multiplied by it — because it is applied as a `transform` on
		   this box rather than by rebuilding the inside in relative
		   units. That distinction is the whole reason it is safe: a
		   `transform` scales the RENDERED RESULT, so the tab buttons'
		   `clip-path: path()` — which is in USER PIXELS and does not
		   scale with anything else — moves with the arcs it clips.
		   Verified live at five viewport sizes: a hit test at each of the
		   four arc labels lands on its own tab at every scale.

		   `tan(atan2(<length>, <length>))` is how a ratio of two lengths
		   becomes a NUMBER in CSS. `calc(100svh / 1040px)` computes the
		   same value and was measured working in this engine, but
		   length-by-length division landed years after the trig
		   functions, so the trig form is the portable one. If an engine
		   supports neither, the custom property holds an unparsable token
		   stream, `transform` is invalid at computed-value time and
		   resolves to `none`, and the medallion renders unscaled — which
		   is the behaviour that shipped in v1.13.19. It degrades to the
		   bug, not past it.

		   `100cqw` needs the `container-type` declared on `.medallion-page`
		   below. With no container it would resolve against the viewport,
		   which is wider than the column and therefore optimistic — the
		   declaration is what makes the width term honest. */
		/* THE HEIGHT TERM MEASURES THE SPACE THE MEDALLION HAS, NOT THE
		   SPACE THE VIEWPORT HAS, and the difference between those two
		   was a shipped bug in v1.13.19 and v1.13.20.

		   `100svh` is the whole viewport. The medallion does not start at
		   the top of it — Neve's header is above it — so the fit was
		   over-generous by exactly that header's height and the medallion
		   ran past the fold at every viewport between roughly 750px and
		   1120px tall. MEASURED on the live page, 2026-09-24: medallion
		   top 81.2px, `.medallion-page` padding-top 0, header height 81.2px.
		   The two are the same number because this page puts nothing
		   between them.

		   IT WAS REPORTED AS A FIREFOX BUG AND IS NOT ONE. Chrome and
		   Firefox place the disk within 1px of each other at every size
		   tested; Firefox's browser chrome is simply taller, so the same
		   window gives it a shorter viewport and it crosses the threshold
		   first. `tan(atan2())` resolves in both. Nothing here is
		   engine-specific and no engine test belongs in this file.

		   88px RATHER THAN 81.2px, DELIBERATELY. The allowance is a hand
		   copy of a value that lives in `theme_mods_neve-child` and in no
		   file in this repo — the same class of coupling CLAUDE.md records
		   for the footer's `--primarybtnpadding`, and the same hazard: a
		   Customizer change moves the header and leaves this behind. Being
		   a few pixels generous costs a few pixels of medallion, which
		   nobody can see. Being a few pixels short brings the bug back.
		   IF THE HEADER'S HEIGHT CHANGES, CHANGE THIS IN THE SAME COMMIT. */
		--medallion-top-allowance: 88px;
		--medallion-fit-w: tan(atan2(100cqw, var(--medallion-size)));
		--medallion-fit-h: tan(atan2(calc(100svh - var(--medallion-top-allowance)), var(--medallion-size)));
		--medallion-scale: clamp(
			var(--medallion-scale-floor),
			min(1, var(--medallion-fit-w), var(--medallion-fit-h)),
			1
		);

		/* THE FLOOR, AND WHAT HAPPENS UNDER IT.

		   0.72 puts the body copy at 10.8px and the arc labels at 12.2px,
		   which is the smallest this page stays readable at. Joe asked for
		   a floor in those terms: "the text will go wild otherwise."

		   UNDER THE FLOOR THE MEDALLION STOPS SHRINKING AND THE PAGE
		   SCROLLS. That is a decision, not a rounding — the alternatives
		   were to clip it, to let it overflow sideways, or to drop to the
		   flat stacked layout. Clipping loses content and sideways
		   overflow puts a scrollbar on every page on the site, so both are
		   out. The flat fallback is the interesting one and it was
		   rejected on arithmetic: the floor is reached at a viewport
		   height of 749px, and a very large share of laptops sit below
		   that — so a layout switch there would not be a rare edge case,
		   it would delete this design for most of the people who see it.
		   Scrolling a little is ordinary, and Joe has already accepted the
		   page running past the fold ("footer can go offscreen for sure").
		   At the 723px viewport the defect was reported on, the medallion
		   is 749px tall: 26px of scroll.

		   `max-width: 100%` IS NO LONGER THE TRAP IT WAS. It shipped
		   alongside fixed inner pixels, so the box could shrink while the
		   arcs and their clip paths did not — paint and hit area drifting
		   apart with nothing to catch it. The width term above now shrinks
		   the whole medallion by the same ratio the box would be capped
		   by, so the two agree by construction instead of by luck. */
		--medallion-scale-floor: 0.72;
	}

	/* THE CONTAINER THE WIDTH TERM MEASURES. Declared only inside this
	   block: `container-type` applies layout and style containment, and
	   there is no reason for the narrow page to carry either. */
	.medallion-page {
		container-type: inline-size;
	}

	/* THE TRANSFORM AND THE SPACE IT RESERVES.
	   A transform paints smaller without laying out smaller, so the page
	   below would still be pushed down by the full 1040px. The negative
	   bottom margin gives back exactly the height the scale removed —
	   `size * (scale - 1)` is zero at scale 1 and -291px at the floor —
	   which is what stops the footer being shoved off by an element that
	   is not there any more.

	   `transform-origin: top center` rather than `top left`, because the
	   layout box stays 1040px wide and is centred by `margin: 0 auto`;
	   scaling from its top centre keeps the painted medallion centred on
	   it at every scale.

	   GATED ON `--tabbed`, WHICH IS THE NO-JS CASE'S ANSWER. Without the
	   script the panes are not absolutely positioned — they flow beneath
	   the disk, and the medallion's height is the disk plus all four of
	   them. Scaling that is possible but the reserved height cannot be
	   computed from one constant any more, so it would leave a band of
	   dead space below. Without JS the page is a long scroll already and
	   there is nothing to fit the disk to, so it renders at 1040 exactly
	   as it did before. */
	.medallion-page--tabbed .medallion-page__medallion {
		height: var(--medallion-size);
		min-height: 0;
		transform: scale(var(--medallion-scale));
		transform-origin: top center;
		margin-bottom: calc(var(--medallion-size) * (var(--medallion-scale) - 1));
	}

	/* THE DISK IS 2 x THE OUTER RADIUS, NOT THE MEDALLION BOX, and it
	   shipped for one commit as the box. The template computed the outer
	   radius and did not pass it on, so this rule fell back to
	   `--medallion-size` and painted 1040px across where the artboard
	   draws 940 — radius 520 against 470. The ring then floated 50px inside
	   the disk's own rim instead of sitting flush to it. NOTHING COULD SEE
	   IT: the arcs were right, the panes were right, every check was green,
	   and the only symptom was a slate margin round the outside. That is
	   why `checks/07` now compares the property names this sheet READS
	   against the names the template EMITS, in both directions.

	   Explicit width and height rather than `inset: 0`, because the wrapper
	   is taller than the disk in the no-JS case and `inset: 0` would
	   stretch a circle into a very tall ellipse exactly when nobody is
	   looking. */
	.medallion-page__medallion .medallion-page__disk {
		display: block;
		position: absolute;
		left: var(--medallion-disk-offset);
		top: var(--medallion-disk-offset);
		width: var(--medallion-disk);
		height: var(--medallion-disk);
		border-radius: 50%;
		background: var(--color-bg-surface);
	}

	/* THE PORTRAIT, CLIPPED TO THE INNER CIRCLE — the "cut-out" in the
	   artboard's name. It sits in the inner circle's bounding box and is
	   clipped to the circle itself, so the parts of the figure that run
	   past the ring are removed rather than drawn under the arcs.

	   `pointer-events: none` because the tabs are absolutely positioned
	   ABOVE this layer in document order; without it the portrait would
	   swallow presses aimed at the lower arcs.

	   The `<img>`'s own left / top / width / height are INLINE, from the
	   same geometry call that produced everything else here, because they
	   depend on the featured image's aspect ratio and the sheet cannot
	   know it. `max-width: none` because Neve constrains images to their
	   container and this one is deliberately drawn wider than the circle
	   that clips it. */
	.medallion-page__medallion .medallion-page__portrait {
		display: block;
		position: absolute;
		left: var(--medallion-pane-offset);
		top: var(--medallion-pane-offset);
		width: var(--medallion-pane);
		height: var(--medallion-pane);
		clip-path: circle(var(--medallion-inner) at 50% 50%);
		pointer-events: none;
	}

	/* THE BOX IS BOUND HERE NOW, NOT INLINE, and that move is the one
	   thing in story 4 that could have broken the wide layout silently.
	   These four values used to be literal `left:…;top:…` in the <img>'s
	   style attribute, which was safe while exactly one block ever showed
	   the portrait. Two blocks show it at two different sizes now, and an
	   inline declaration applies at EVERY width — so they became inert
	   custom properties and each block spends its own pair. Leaving this
	   rule as it was would have dropped the wide portrait to its natural
	   641x1024 at the box origin, which is what it did for one render.

	   `height: auto`, AND THE HEIGHT IT REPLACED WAS A CLAMP MEANT FOR
	   SOMETHING ELSE. The generator returns `figure.height` as
	   `min( width x aspect, inner x 2 + top )` -- a ceiling that exists so
	   a very tall upload cannot make the text-wrap FLOAT taller than its
	   pane and push every line of copy below it. That ceiling is right for
	   the float and wrong for the PAINT: v1.13.21 bound it to the <img> at
	   both sizes, and at the phone size it bit -- the portrait rendered at
	   65% of its true height, squashed, stopping 9.7px short of the
	   circle's bottom edge and showing a straight horizontal edge where
	   the picture ended. The wide size escaped only by proportion: 788.4
	   natural against an 822.6 ceiling. One taller portrait and it would
	   have squashed there too.

	   The <img> has a width and an intrinsic ratio, so `auto` is the true
	   height by construction and cannot be clamped by anything. The float
	   keeps the ceiling, inline, where it was always correct. */
	.medallion-page__medallion .medallion-page__portrait img {
		display: block;
		position: absolute;
		max-width: none;
		left: var(--medallion-figure-left);
		top: var(--medallion-figure-top);
		width: var(--medallion-figure-width);
		height: auto;
	}

	/* THE RING IS THE TAB BAR, RE-LAID. Same element, same role, same
	   buttons, same roving tabindex — only the geometry moves, which is
	   what keeps story 2's ARIA and keyboard behaviour untouched.

	   NO `display` IS DECLARED HERE, and that is load-bearing. The base
	   rule above sets `display: flex`; the UA sheet expresses the `hidden`
	   attribute as `display: none` at (0,0,0) and therefore loses to it,
	   which is why `.medallion-page__tabs[hidden]` exists at all. Declaring
	   `display` again in this block would need the same guard again, so
	   the flex row is simply left in place — its children are all
	   absolutely positioned and out of flow, so it lays out nothing. The
	   guard below is restated anyway, at a specificity that clears every
	   rule in this block, because the next person to add `display: grid`
	   here will not read this paragraph first. */
	.medallion-page__medallion .medallion-page__tabs {
		position: absolute;
		left: 0;
		top: 0;
		width: var(--medallion-size);
		height: var(--medallion-size);
		margin: 0;
		gap: 0;
		border-bottom: 0;
	}

	.medallion-page__medallion .medallion-page__tabs[hidden] {
		display: none;
	}

	/* EVERY TAB IS THE WHOLE MEDALLION, CLIPPED TO ITS OWN ARC. The
	   `clip-path` is inline per button and is the SAME `d` string the
	   painted segment uses, so the hit area cannot drift from what the
	   visitor can see. Four full-size buttons stacked on each other would
	   normally mean only the last one is pressable; the clip is what makes
	   each of them pressable exactly where its arc is.

	   THE CLIP ARRIVES AS `--medallion-tab-clip` AND IS SPENT HERE, which is
	   the only reason the flat bar survives below this breakpoint. The
	   template used to emit `clip-path` directly in the button's style
	   attribute, and an inline declaration applies at EVERY width -- so at
	   narrow, where each button is a ~200x30 text label, a clip region
	   plotted in a 1040px space falls entirely outside the box and the
	   whole tab bar vanishes. No check in this repo renders CSS, so that
	   would have shipped silently against a story whose acceptance
	   criterion is "narrow is visually identical to v1.13.18".

	   `font-size: 0` IS HOW THE PLAIN LABEL IS HIDDEN, and it is hidden
	   rather than removed because it is the button's ACCESSIBLE NAME. The
	   visible label is the `<textPath>` inside an `aria-hidden` `<svg>`,
	   so the two never both reach the accessibility tree; zero-size text
	   still does. `display: none` or `visibility: hidden` would take the
	   name away and leave a control a screen-reader user hears as
	   "button" and a voice-control user cannot address at all. */
	.medallion-page__medallion .medallion-page__tab {
		position: absolute;
		left: 0;
		top: 0;
		clip-path: var(--medallion-tab-clip);
		width: var(--medallion-size);
		height: var(--medallion-size);
		padding: 0;
		border: 0;
		font-size: 0;
		line-height: 0;
		color: transparent;
	}

	/* `--lg` RATHER THAN THE BARE CLASS, because there are two <svg>
	   elements in every tab button now — one drawn in a 1040-unit space
	   and one in a 390-unit space — and a viewBox belongs to the <svg>,
	   so they cannot be the same element. Both carry
	   `.medallion-page__tab-art` and are therefore held off by the default
	   rule above; each block turns on exactly one. Dropping the modifier
	   here would show both, stacked. */
	.medallion-page__medallion .medallion-page__tab-art--lg {
		display: block;
		position: absolute;
		left: 0;
		top: 0;
		width: 100%;
		height: 100%;
		overflow: visible;
	}

	/* THE ARC ITSELF: a 14% black wash over the slate, as drawn. Alpha
	   rather than a mixed hex so it composites against whatever
	   `--color-bg-surface` holds — the same reason the card ring and the
	   header hairline are alphas, and the reason a fixed hex would have
	   bound the ring to one disk colour. */
	.medallion-page__medallion .medallion-page__tab-seg {
		fill: rgba(0, 0, 0, 0.14);
	}

	/* THE FOCUS RING IS A SECOND PATH, INSET, AND IT IS THE ONLY SHAPE
	   HERE THAT EXISTS FOR A STATE RATHER THAN FOR THE DRAWING.

	   A stroke on the segment itself would be cut in half: the segment's
	   edge IS the clip edge, so half of any stroke centred on it falls
	   outside the clip and is discarded. The generator therefore emits a
	   second wedge inset on all four sides — both radii and both radial
	   edges — so the whole stroke lands inside. The inset is derived from
	   the same ratio as the stroke width, which is why a 4px value moves
	   both together. */
	.medallion-page__medallion .medallion-page__tab-ring {
		fill: none;
		stroke: transparent;
		stroke-width: var(--medallion-focus);
	}

	/* THE CURVED LABEL. `font-size` is stated from the generator because
	   the button above sets `font-size: 0` to hide the plain-text name,
	   and SVG text inherits it. `letter-spacing` is stated because Neve's
	   Customizer typography emits `--h2letterspacing: -1px` and paints it
	   from low-specificity element selectors — nothing in this repo
	   declares that property, so grepping for it finds nothing and the
	   page has it anyway (CLAUDE.md's typography entry). The artboard
	   draws these at +0.3. */
	.medallion-page__medallion .medallion-page__tab-label {
		fill: #ffffff;
		font-family: var(--font-heading);
		font-size: var(--medallion-label);
		font-weight: 600;
		letter-spacing: 0.3px;
		transition: fill 0.2s ease;
	}

	/* SPECIFICITY (0,5,0), AND ALL THREE OF NEVE'S HOVER PROPERTIES ARE
	   STATED — the same fight flush-split.css:1068-1072 and the flat bar
	   above both record. Neve paints `button:hover { background:
	   var(--primarybtnhoverbg); color: …; border-color: … }` at (0,1,1)
	   and `--primarybtnhoverbg` resolves to `nv-secondary-accent`,
	   #2f5aae. On a 1040px button that is a full-medallion blue wash
	   clipped to one quadrant, which is not subtle. The flat bar's hover
	   rule already clears it at (0,4,0) but states `border-bottom-color`
	   rather than `border-color`, so this one restates the property Neve
	   actually sets.

	   The hover PAINT is on the label, not on the button — the arc holds
	   still and its words change colour, as drawn. */
	.medallion-page__medallion .medallion-page__tabs .medallion-page__tab[role="tab"]:hover {
		background: none;
		border-color: transparent;
		color: transparent;
	}

	.medallion-page__medallion .medallion-page__tab[role="tab"]:hover .medallion-page__tab-label {
		fill: #00e5ff;
	}

	/* THE BASELINE RING IS CLEARED BY RE-DECLARING `outline`, NOT BY
	   `outline: none`, and the value it is re-declared with is transparent
	   for a reason worth stating rather than hiding.

	   site-interactive-states.css:1810-1815 paints a 2px OUTWARD black
	   ring on `[tabindex]:not([tabindex="-1"]):focus-visible`, which is
	   (0,3,0) because `:not()` contributes its argument's specificity.
	   Every one of these tabs holds a roving tabindex, so every one of
	   them matches it — and on a 1040px square button that ring is a
	   1044px black square around the whole medallion. The flat bar above
	   clears it at (0,4,0) with an inward marker ring; this block has to
	   clear that rule too, hence (0,5,0).

	   AN OUTLINE CANNOT BE THIS COMPONENT'S RING NO MATTER WHAT COLOUR IT
	   IS GIVEN. The button's box is the whole medallion, so any outline on
	   it is a square, and `clip-path` clips the element's outline along
	   with everything else it paints — so the visible remainder would be
	   whatever slivers of a square happen to fall inside one arc. The
	   ring this component actually shows is the inset stroked path above,
	   which is inside the clip by construction. So this declaration exists
	   to WIN, and the next one exists to PAINT. */
	.medallion-page__medallion .medallion-page__tabs .medallion-page__tab[role="tab"]:focus-visible {
		outline: 2px solid transparent;
		outline-offset: 0;
		border-radius: 0;
	}

	.medallion-page__medallion .medallion-page__tab[role="tab"]:focus-visible .medallion-page__tab-ring {
		stroke: #00e5ff;
	}

	/* THE CURRENT ARC, AND IT HAS TO OUTRANK ITS OWN HOVER. The flat bar
	   above records this as a shipped bug: at (0,2,0) the selected rule
	   lost to the (0,4,0) hover rule, so hovering the current tab erased
	   "you are here" for as long as the pointer rested on it. Here the
	   hover rule is (0,5,0), so the selected pair is (0,5,0) and (0,6,0) —
	   the second selector is what survives the pointer.

	   Raising the winner rather than narrowing the hover rule with
	   `:not([aria-selected="true"])`, for the same reason story 2 gives:
	   the hover rule is also the only thing outranking Neve on
	   `background`, and excluding one tab from it would hand exactly that
	   tab back to #2f5aae. */
	.medallion-page__medallion .medallion-page__tab[role="tab"][aria-selected="true"] .medallion-page__tab-label,
	.medallion-page__medallion .medallion-page__tab[role="tab"][aria-selected="true"]:hover .medallion-page__tab-label {
		fill: var(--color-tab-current);
	}

	/* ---------------------------------------------------------------
	   THE PANES, AND THE NO-JS CASE IS THE REASON THIS IS TWO RULES
	   ---------------------------------------------------------------

	   Only ONE pane can occupy the circle; four cannot share it. So the
	   circular layout is switched on by `.medallion-page--tabbed`, which
	   medallion.js adds LAST, after every listener is attached — the same
	   fail-open shape the tab bar's `hidden` uses, in the other
	   direction.

	   WITHOUT JS the panes are pushed clear of the disk and read in
	   document order underneath it, in the flat bar's own treatment: dark
	   ink on the white page, headings intact. The disk and the portrait
	   still paint above them, because the medallion is CSS and markup
	   only and does not need a script to exist. What it needs a script
	   for is choosing which pane is in the circle, and there is no
	   scriptless answer to that question. */
	.medallion-page__medallion .medallion-page__panes {
		padding-top: var(--medallion-size);
	}

	.medallion-page--tabbed .medallion-page__medallion .medallion-page__panes {
		padding-top: 0;
	}

	/* THE PANE IS THE INNER CIRCLE'S BOUNDING BOX, AND THE `clip-path` ON
	   IT CLOSES TWO SEPARATE FAILURES THAT BOTH SHIPPED.

	   1. IT WAS SWALLOWING EVERY CLICK ON EVERY ARC LABEL. The pane is a
	      796px SQUARE, absolutely positioned, and it comes AFTER the
	      tablist in document order — so it paints later and wins the hit
	      test wherever the two overlap. Its corners reach 563px from the
	      centre, past the ring's outer 470, at exactly the four diagonals
	      where the labels are centred (`startOffset="50%"` on a path at
	      radius 434). Every tab was pressable only on the parts of its arc
	      away from its own label.

	   2. OVERFLOW LINES WERE LAYING OUT IN THE CORNERS. Copy longer than
	      the float boxes scrolls, and the floats are exactly as tall as
	      the pane — so every line past that height had no shape to wrap
	      and ran to the full square width: white text in the square
	      corners, over the ring and out onto the white page.

	   `clip-path` clips hit-testing as well as paint, so one declaration
	   answers both. It is the same circle the portrait is clipped to and
	   reads the same radius, so the two cannot drift.

	   THE SCROLLBAR IS TURNED OFF DELIBERATELY, and this is the one part
	   of the fix worth arguing with. `overflow: hidden auto` stays: the
	   copy is edited in wp-admin, so "longer than the circle" is a Tuesday
	   and silently cutting a paragraph is the worse failure. But a
	   scrollbar at the pane's right edge sits at the circle's extremity,
	   so the clip above removes all but a sliver of it — the affordance
	   was never going to be visible inside a circle. Worse, a classic
	   scrollbar NARROWS THE CONTENT BOX, and the two shape floats are
	   sized at exactly half the pane each: lose 11px and the right float
	   no longer fits beside the left one, drops below it, and the circular
	   flow collapses on precisely the long copy that caused the scrollbar.
	   `scrollbar-width: none` keeps the content box at its full width and
	   leaves wheel, trackpad and keyboard scrolling working — and the pane
	   carries `tabindex="0"`, so the keyboard route is reachable.

	   JOE'S CALL, 2026-09-24, AND IT IS PROVISIONAL: leave it, keep the
	   circle clean — the drawing has no scrollbar, the copy is meant to
	   fit, and a pane that overflows is a signal the copy is too long
	   rather than something to decorate. A fade at the lower edge was
	   offered and not taken. He reserved the right to change it once the
	   zip is installed and he has seen it on the page, so this is a
	   decision taken on the argument and not yet on the render.

	   So the accepted cost, for now, is that LONG COPY SCROLLS WITH NO
	   VISIBLE INDICATION THAT IT DOES. If that is revisited, the fade is
	   the live alternative — NOT restoring the scrollbar, which is what
	   collapses the flow two paragraphs up. */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane {
		position: absolute;
		left: var(--medallion-pane-offset);
		top: var(--medallion-pane-offset);
		width: calc(var(--medallion-pane) + var(--medallion-overhang));
		height: var(--medallion-pane);
		clip-path: circle(var(--medallion-inner) at var(--medallion-inner) 50%);
		overflow: hidden auto;
		scrollbar-width: none;
		font-size: 15px;
		line-height: 1.6;
		color: #ffffff;
	}

	/* SEE THE `[hidden]` NOTE ON THE FLAT BAR. Nothing in this block sets
	   `display` on a pane, so the UA sheet's `display: none` is currently
	   unopposed — but medallion.js hides three panes out of four with
	   `panel.hidden = true`, and a single `display` declaration added here
	   later would stack all four on top of each other inside the circle
	   with the tabs apparently dead. Restated at (0,4,0), which clears
	   every rule in this block. */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane[hidden] {
		display: none;
	}

	/* The pane carries `tabindex="0"`, so it matches the (0,3,0) baseline
	   ring as well. The flat bar leaves that ring alone deliberately — an
	   outward ring on a large box is correct there. Inside the medallion
	   it would be a 796px black square sitting on top of the arcs, so it
	   is turned inward and given the component's own colour. */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane:focus-visible {
		outline: 2px solid #00e5ff;
		outline-offset: -2px;
	}

	/* THE HEADING IS SHOWN INSIDE THE CIRCLE, as drawn, even though the
	   arc above already carries the same words. That is the artboard's
	   own choice and not an oversight: the arc label is 17px and curved,
	   which is a wayfinding mark rather than a title, and the pane still
	   needs one. The rule that hides it at narrow is (0,3,1); this is
	   (0,4,1). */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane > h2:first-of-type {
		display: block;
		margin: 0 0 14px;
		font-family: var(--font-heading);
		font-size: 26px;
		font-weight: 600;
		line-height: 1.15;
		letter-spacing: -0.01em;
		color: #ffffff;
	}

	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane p {
		margin: 0 0 14px;
	}

	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane p:last-child {
		margin-bottom: 0;
	}

	/* ---------------------------------------------------------------
	   THE CIRCULAR TEXT FLOW
	   ---------------------------------------------------------------

	   `shape-outside` APPLIES TO FLOATS AND NOTHING ELSE, and a float only
	   shapes the line boxes that come AFTER it — which is why these three
	   boxes are the pane's first children and why the heading rule above
	   had to stop using `:first-child`.

	   THE SHAPE IS A GRADIENT'S ALPHA CHANNEL, NOT A POLYGON. A
	   `radial-gradient` whose centre is transparent out to exactly the
	   inner radius and opaque beyond it describes "everywhere except the
	   circle", which is the exclusion a float needs in order to put text
	   INSIDE a circle — something no `<basic-shape>` can express, because
	   `circle()` describes the disc rather than its complement. The
	   artboard reached the same place with a 135-point polygon per pane
	   per side; this reaches it with one radius, and the radius is
	   `var(--medallion-inner)`, so it moves when the ring moves.

	   `shape-margin` THEN DOES THE INSET. Growing an
	   outside-the-circle region outward by 30px is exactly the same shape
	   as the outside of a circle 30px smaller, so the artboard's "circle
	   edge plus 30" needs no second number and no second gradient.

	   `shape-image-threshold: 0.5` rather than the 0 default, so the
	   boundary sits where the gradient's hard stop is rather than where
	   its antialiasing starts. */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__flow {
		display: block;
		width: var(--medallion-inner);
		height: var(--medallion-pane);
		shape-image-threshold: 0.5;
		shape-margin: var(--medallion-inset);
	}

	.medallion-page--tabbed .medallion-page__medallion .medallion-page__flow--left {
		float: left;
		shape-outside: radial-gradient(
			circle var(--medallion-inner) at 100% 50%,
			rgba(0, 0, 0, 0) 0 100%,
			rgba(0, 0, 0, 1) 100%
		);
	}

	.medallion-page--tabbed .medallion-page__medallion .medallion-page__flow--right {
		float: right;
		/* THE CIRCLE'S CENTRE IS NO LONGER THIS FLOAT'S LEFT EDGE. The pane
		   is wider than the circle by the portrait's overhang, and this
		   float sits at the pane's RIGHT edge — so the centre is that much
		   further left than it used to be. Read from the same value the
		   pane's own width and clip are read from, so the three cannot
		   disagree. This float only renders when there is no featured
		   image; with one, the figure float replaces it. */
		shape-outside: radial-gradient(
			circle var(--medallion-inner) at calc(-1 * var(--medallion-overhang)) 50%,
			rgba(0, 0, 0, 0) 0 100%,
			rgba(0, 0, 0, 1) 100%
		);
	}

	/* THE FIGURE FLOAT REPLACES THE RIGHT-HAND CIRCLE RATHER THAN JOINING
	   IT, AND THAT IS A FINDING RATHER THAN A SHORTCUT.

	   The artboard's right-hand polygon is the circle UNIONED with the
	   portrait's silhouette. CSS Shapes cannot express a union: a float's
	   shape is clipped to its own margin box ("a shape can only ever
	   reduce a float area, not increase it"), `shape-outside` takes a
	   single image, and two right floats cannot overlap — which they would
	   have to, because the two exclusions cover the same region. The only
	   faithful mechanism left would be compositing the circle and the
	   portrait into one image at render time, which means inlining the
	   photograph as a data URI in four places, because an SVG used as a
	   CSS image is in secure static mode and cannot load an external one.

	   SO IT WAS MEASURED INSTEAD, against the artboard's own WebP alpha
	   channel. From y=48 to y=768 of a 796px pane the FIGURE is the
	   binding edge on every sampled row; the circle only wins above y≈48
	   and below y≈790, and the generated lead spacer already keeps text
	   out of the first 117px. The approximation therefore costs nothing
	   where text actually sits — and it keeps the promise the union would
	   have broken either way, because a pasted polygon stops matching the
	   photograph the moment Joe changes it.

	   `shape-outside`, `shape-margin`, `width`, `height` and the top offset
	   are all INLINE, from the same geometry call: they depend on the
	   featured image's own pixel dimensions and the sheet cannot know
	   them. If the image fails to decode the shape falls back to the
	   float's box, which is further LEFT than the circle — the text gets
	   narrower, never wider, and never escapes the disk.

	   THERE IS NO NEGATIVE MARGIN ON THIS FLOAT ANY MORE, and there must
	   never be one again. It carried the portrait's overhang past the
	   pane's right edge, which made the float's margin box 395.9px against
	   a 493.5px content box. That is a discrepancy with no purpose — the
	   shape is resolved against the content box — and a standing trap for
	   any engine that resolves it against the margin box instead. The pane
	   is widened by `--medallion-overhang` and clipped back to the
	   circle, so the overhang is the pane's problem and this float's two
	   boxes are identical. `checks/07` asserts the margin cannot come
	   back. */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__flow--figure {
		float: right;
	}

	/* ONE GENERATED SPACER, NOT THE ARTBOARD'S FOUR. It is a plain block
	   rather than padding on the pane, because padding would push the
	   floats down with it and their shapes are keyed to the pane's own
	   box. A block box is not narrowed by floats — only line boxes are —
	   so an empty one simply occupies its height and the first line lands
	   below it. */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__flow-lead {
		display: block;
		height: var(--medallion-lead);
	}

	/* =================================================================
	   THE GREY FIELD, AND THE FILMSTRIP
	   =================================================================

	   WIDE ONLY, BOTH OF THEM. The phone keeps the ground story 4 shipped
	   -- white above the circle's equator, slate below and through the
	   stacked copy -- because that is what Phone.dc.html draws. The grey
	   field is the DESKTOP composition, from PageFull.dc.html, and it
	   would fight the phone's band rather than extend it. */

	/* THE FIELD RUNS HEADER TO FOOTER AND EDGE TO EDGE. `#e4e4e4` is the
	   sitewide grey this theme already paints -- CLAUDE.md records it as
	   the case-study band added at v1.13.3 -- so this is the site's own
	   value reaching a second place, not a new colour.

	   FULL-BLEED BY THE USUAL ESCAPE, because the field is the page's and
	   Neve's container is only the copy's. `.medallion-page` has no top
	   padding and sits directly under the header, so `top: 0` IS the
	   header's bottom edge -- the same 81px story 4 measured and the same
	   81px PageFull draws. The bottom runs to the end of the medallion's
	   reserved height, which is where the footer begins.

	   BEHIND EVERYTHING, and `z-index: 0` rather than `-1`: negative would
	   put it behind `.medallion-page`'s own background and, on any ancestor
	   that happens to establish a stacking context with a paint, behind
	   the page itself. Zero keeps it in this element's own layer, under
	   the positioned medallion and the strip, both of which paint after
	   it in document order. */
	.medallion-page::before {
		content: '';
		position: absolute;
		left: 50%;
		width: 100vw;
		transform: translateX(-50%);
		top: 0;
		bottom: 0;
		background: #e4e4e4;
		z-index: 0;
	}

	/* THE ROW. One item without a gallery, two with one, centred either
	   way -- so a page with no strip lays out exactly as it did before
	   this story, which is the shape the no-gallery matrix row asserts.
	   `flex-wrap` plus a full-width lede keeps prose written before the
	   first heading on its own line, where it belongs to the page rather
	   than to the ring.

	   `column-gap` ALONE. A row gap would open a space between the lede
	   and the medallion that no artboard draws. */
	.medallion-page {
		display: flex;
		flex-wrap: wrap;
		align-items: stretch;
		justify-content: center;
		column-gap: 60px;
		position: relative;
	}

	.medallion-page__lede {
		flex: 1 0 100%;
	}

	/* THE LAYOUT BOX FOLLOWS THE PAINT NOW, and it has to for the strip to
	   sit beside it. The medallion paints at `size x scale` but has always
	   LAID OUT at the full `size`, with a negative bottom margin giving the
	   height back; in a one-item centred row that was invisible. Put a
	   second item next to it and the row measures 1040 + gap + strip even
	   when the medallion is painting 970 -- which overflows the column at
	   exactly the width Joe asked the strip to appear at.

	   Scaling about `top center` of a box that is itself `size x scale`
	   wide lands the painted result exactly on the box: its centre is at
	   `size x scale / 2`, and content `size` wide scaled about that point
	   spans 0 to `size x scale`. The height mechanism is untouched -- the
	   margin still gives back what the scale removed, and the flex line's
	   cross size is the medallion's margin box, which is what the strip
	   stretches to. */
	.medallion-page__medallion {
		width: var(--medallion-size);
		max-width: 100%;
		flex: 0 0 auto;

		/* THE WIDTH COMES BACK AS NEGATIVE MARGIN, NOT AS A SMALLER BOX,
		   and the difference is one factor of the scale. Setting
		   `width: size x scale` looks right and double-scales: the content
		   inside is authored at `size`, the transform multiplies EVERYTHING
		   by `scale` including the box, so the box paints at `size x
		   scale²` while the content paints at `size x scale` and hangs out
		   of it. Measured, before the fix: layout 992, painted box 946,
		   content 992.

		   Split evenly so the box's centre does not move -- the transform
		   origin is `top center`, so a centre that moved would slide the
		   painted medallion off its own layout box. The margin box is then
		   exactly the painted width, which is what the flex row measures
		   and what keeps the strip from being pushed out of the column.
		   Same trick story 3 already uses on the height, one axis over. */
		margin-left: calc(var(--medallion-size) * (var(--medallion-scale) - 1) / 2);
		margin-right: calc(var(--medallion-size) * (var(--medallion-scale) - 1) / 2);

		/* THE AUTO MARGINS HAVE TO GO, AND THEY ARE WHY THE STRIP FIRST
		   RENDERED PINNED TO THE WINDOW EDGE. Story 3 centred this box with
		   `margin: 0 auto`, which is correct for a block in normal flow and
		   is something else entirely in a flex row: an auto margin on a flex
		   item absorbs ALL the free space on that side, so the medallion
		   took the left of the row and pushed the strip as far right as the
		   container allowed. Two centring mechanisms, and the stronger one
		   was the one nobody meant to use. `justify-content: center` on the
		   row is now the only one, and the margins above are arithmetic
		   rather than alignment. */
	}

	/* THE MEDALLION GIVES UP THE STRIP'S COLUMN. Joe's ruling, 2026-09-25:
	   one breakpoint rather than two. Without this the strip would have had
	   to wait for a second, higher threshold and leave a band of widths
	   showing neither drawn layout; with it the strip arrives at 1280 and
	   the medallion is about 7% smaller there, and no smaller at all above
	   roughly 1400 where the cap takes over.

	   The class is on this element because PHP knows whether there is a
	   gallery and the sheet does not, and the root <div> is emitted before
	   `the_post()` so it cannot carry the answer. */
	.medallion-page__medallion--film {
		--medallion-fit-w: tan(atan2(calc(100cqw - var(--medallion-film-column)), var(--medallion-size)));
	}

	/* THE STRIP. `overflow: hidden` is the gate the running track scrolls
	   behind, and `align-self: stretch` takes its height from the flex
	   line -- which is the medallion's margin box, so the strip is exactly
	   as tall as the medallion paints at every scale without either of
	   them knowing the other's number. */
	.medallion-page__film {
		flex: 0 0 auto;
		align-self: stretch;
		width: var(--medallion-film-strip);
		overflow: hidden;
		position: relative;
		z-index: 1;
		background: #141311;
	}

	/* THE TRACK CARRIES THE LIST TWICE AND TRAVELS HALF ITS LENGTH, which
	   is the whole of why the loop has no seam: at -50% the second copy is
	   sitting exactly where the first started. The duration is per-frame,
	   so a gallery of six and a gallery of sixty run at the same pace
	   rather than the same speed. */
	/* ABSOLUTE, AND THAT IS WHAT LETS THE STRIP BE THE MEDALLION'S HEIGHT.
	   `align-self: stretch` cannot shrink a flex item below its own
	   content, and the track IS the content -- twelve frames of it, 2,606px
	   measured, which made the strip the tallest thing in the row and grew
	   the whole line to match. Out of flow, the track contributes no height
	   at all, the line's cross size is the medallion's margin box again,
	   and the strip stretches to exactly that. */
	.medallion-page__film-track {
		position: absolute;
		left: 0;
		top: 0;
		display: flex;
		flex-direction: column;
		width: var(--medallion-film-strip);
		animation: medallion-film-run var(--medallion-film-dur) linear infinite;
		will-change: transform;
	}

	@keyframes medallion-film-run {
		from { transform: translateY(0); }
		to { transform: translateY(-50%); }
	}

	/* PAUSE ON HOVER, on the WRAPPER rather than the track: the track is
	   moving, so hovering it is a moving target and the pause would
	   stutter as frames slide out from under the pointer. */
	.medallion-page__film:hover .medallion-page__film-track {
		animation-play-state: paused;
	}

	.medallion-page__film-frame {
		display: flex;
		flex-direction: row;
		align-items: center;
		flex-shrink: 0;
		width: var(--medallion-film-strip);
		height: var(--medallion-film-row);
	}

	/* THE PERFORATIONS ARE THE GENERATED TILE, REPEATED. One perforation
	   pitch tall, so it lines up with the frame by construction -- eight
	   of them to a frame, because that is what 35mm is. Nothing is
	   uploaded and nothing can drift. */
	.medallion-page__film-edge {
		flex: 0 0 auto;
		width: var(--medallion-film-band);
		height: var(--medallion-film-row);
		background-image: var(--medallion-film-perf);
		background-repeat: repeat-y;
		background-position: center top;
		writing-mode: vertical-rl;
		font-family: 'Courier New', Courier, monospace;
		font-size: 8px;
		letter-spacing: 0.12em;
		line-height: 1;
		color: #c9c4b8;
		white-space: nowrap;
		overflow: hidden;
	}

	.medallion-page__film-edge--num {
		transform: rotate(180deg);
	}

	/* THE WINDOW IS THE FRAME: 24mm across the film by 36mm along it. */
	.medallion-page__film-window {
		flex: 0 0 auto;
		position: relative;
		display: block;
		width: var(--medallion-film-window);
		height: var(--medallion-film-shot);
		overflow: hidden;
		border-radius: 2px;
	}

	/* THE PHOTOGRAPH, AND IT IS A BACKGROUND RATHER THAN AN <img> ON
	   PURPOSE. A hidden <img> is still fetched, so markup switched off
	   below the breakpoint would have every phone download the whole
	   gallery to show none of it. `--medallion-film-src` rides on the element
	   inert; THIS declaration, inside this block, is the only thing that
	   ever spends it. Below 1280 it does not exist and nothing is
	   requested. */
	.medallion-page__film-shot {
		position: absolute;
		inset: 0;
		display: block;
		background-image: var(--medallion-film-src);
		background-size: cover;
		background-position: center;
	}

	/* THE RETINA ALTERNATE, BEHIND `@supports` RATHER THAN BEHIND A SECOND
	   DECLARATION, AND THE DIFFERENCE IS NOT COSMETIC.

	   The usual fallback idiom is to state the property twice and let an
	   engine that cannot parse the second keep the first --
	   `shape-outside` in this same template does exactly that. IT DOES NOT
	   WORK THROUGH A CUSTOM PROPERTY. A var() whose substituted value the
	   engine cannot parse is invalid AT COMPUTED-VALUE TIME, and the spec
	   says that resolves to `unset` -- it does NOT fall back to the earlier
	   declaration. So the two-line idiom here would have left engines
	   without `image-set()` showing no photograph at all, which is the
	   opposite of a fallback and would have looked like an empty strip.

	   `@supports` tests a LITERAL, so it is decided before any
	   substitution happens, and the plain `medium` url above simply stands
	   where the test fails. */
	@supports (background-image: image-set(url("a.jpg") 1x)) {
		.medallion-page__film-shot {
			background-image: var(--medallion-film-src-hd);
		}
	}

	/* A LANDSCAPE PHOTOGRAPH TURNED INTO AN UPRIGHT FRAME -- which is what
	   a 35mm strip does, because the frame is landscape along the film and
	   the strip runs vertically. Sized to the frame's OTHER axis before
	   rotating, so after the quarter turn it fills the window exactly.
	   Whether a given photograph gets this comes from its real dimensions,
	   not from a list anyone has to maintain. */
	.medallion-page__film-shot--turned {
		inset: auto;
		left: 50%;
		top: 50%;
		width: var(--medallion-film-shot);
		height: var(--medallion-film-window);
		transform: translate(-50%, -50%) rotate(90deg);
	}

}
}

/* =====================================================================
   THE SMALL-BREAKPOINT GREETING

   `templates/medallion.php` emits one `<h2>Hi. I'm Joe</h2>` immediately
   above the medallion. Joe asked for it on 2026-09-29 so the page opens
   the way /about/ does when the disk has stacked; at desktop the circular
   flow already carries that voice and a heading above it would be a
   second start to the same page.

   HIDDEN BY DEFAULT AND SHOWN IN THE SMALL BLOCK, rather than the other
   way round. The wide layout is this file's subject and the one it must
   not regress -- a rule that fails to apply should leave the desktop
   medallion exactly as it was, and `display: none` here is what
   guarantees that. The template's own comment records why the string is
   in PHP rather than in page 3598's `post_content`: that field is split
   on top-level `<h2>` to build the panes, so a fifth heading there would
   become a fifth tab.
   ===================================================================== */
.medallion-page__greeting {
	display: none;
}

/* =====================================================================
   THE MEDALLION — SMALL
   =====================================================================

   THE PHONE ARTBOARD, AND IT IS A DIFFERENT DRAWING RATHER THAN A
   SMALLER ONE. `Phone.dc.html` agrees with the wide board on the outer
   radius as a fraction of D, on the label radius being the ring's
   midline, on the 1.5 degree gaps and on the sweep-flag rule — and
   disagrees on the ring's thickness (41% thicker here as a fraction of
   its diameter), on the label's size, and on all three of the figure's
   placement ratios. So this block spends a SECOND set of generated
   values, `--medallion-sm-*`, and the template emits both.

   THE COPY IS OUTSIDE THE CIRCLE HERE, which is why this block is so
   much shorter than the one above. No `shape-outside`, no figure float,
   no lead spacer, no overhang: the hardest machinery in this file exists
   to flow text around a silhouette INSIDE the disk, and the phone stacks
   it underneath instead. Three of the emitted wide values — `inset`,
   `lead` and `overhang` — have no consumer here, which is why the
   template's small map omits them rather than emitting names nothing
   reads.

   1279.98px, NOT 1279px, so there is no gap at a fractional viewport
   width. Its partner above is `min-width: 1280px`; between them every
   width is covered exactly once, which is what the "never two, never
   none" acceptance criterion needs.

   GATED ON `clip-path: path()` FOR THE REASON THE WIDE BLOCK GIVES: the
   four tab buttons are full-size boxes stacked on each other, so without
   the clip only the last is pressable. An engine without it gets story
   2's flat bar, which is correct at every width. */
@media (max-width: 1279.98px) {

	/* THE STRIP IS OFF HERE, AND IT HAS TO BE STATED RATHER THAN INHERITED.
	   Every `.medallion-page__film*` rule in this file sits inside the wide
	   block, so below 1280 the markup is not "unstyled small" -- it is a
	   bare <div> of text: the edge codes on every frame, twice over,
	   rendering as ordinary inline runs with no writing-mode, no size and
	   no clip. The photographs are still not fetched, because the only
	   declaration that spends `--medallion-film-src` is in the wide block too.

	   OUTSIDE THE `@supports` BELOW, DELIBERATELY. The clip-path gate
	   exists for the tab buttons; an engine without it gets story 2's flat
	   bar and should still not get the edge-code artifacts. */
	.medallion-page__film {
		display: none;
	}

	/* SHOWN HERE, AND OUTSIDE THE `@supports` BELOW FOR THE SAME REASON
	   THE FILM RULE ABOVE IS: the clip-path gate exists for the tab
	   buttons, and an engine without it gets story 2's flat bar -- which
	   still wants the greeting. Plain text needs no capability.

	   CENTRED BY TEXT-ALIGN, NOT BY A MEASURE, and that is deliberate
	   rather than lazy. The obvious version was `max-width:
	   var(--medallion-sm-size); margin: 0 auto` to match the disk's own
	   box -- but that custom property is set as an INLINE STYLE on
	   `.medallion-page__medallion`, which is this element's SIBLING, so it
	   does not inherit here and the declaration would have silently
	   resolved to nothing. The disk centres itself in the column; a
	   full-width centred heading sits on the same axis with no shared
	   value to get wrong.

	   `font-size` AND `letter-spacing` ARE LEFT TO NEVE, per the rule the
	   rest of this theme follows: both are typography theme_mods that live
	   only in the database. */
	.medallion-page__greeting {
		display: block;
		margin: 0 0 24px;
		font-family: var(--font-heading);
		text-align: center;
	}

@supports (clip-path: path('M 0 0 Z')) {

	/* THE CIRCLE IS DRAWN AT 390 AND SCALES DOWN, NEVER UP. Below 390px
	   of container there is no choice — a fixed box would put a
	   horizontal scrollbar on the page, which this site rules out
	   everywhere. Above it there is a choice and the answer is to leave
	   the drawing alone: scaling a 390px artboard up to fill a 1279px
	   tablet would render 13px labels at 43px, and the artboard is the
	   design rather than a minimum. So the factor is capped at 1 and the
	   circle simply centres in whatever space it has.

	   `padding-top` RESERVES THE CIRCLE AND THE PANES FLOW AFTER IT.
	   Every circle layer below is absolutely positioned, so none of them
	   contributes height; the padding is what the stacked copy starts
	   beneath. It is the SCALED height, so a narrow phone does not get a
	   band of dead space under a shrunken disk. */
	.medallion-page__medallion {
		position: relative;
		width: 100%;
		max-width: var(--medallion-sm-size);
		margin: 0 auto;
		padding-top: calc(var(--medallion-sm-size) * var(--medallion-sm-scale));

		--medallion-sm-fit: tan(atan2(100cqw, var(--medallion-sm-size)));
		--medallion-sm-scale: min(1, var(--medallion-sm-fit));
	}

	/* The container the width term measures, exactly as above. */
	.medallion-page {
		container-type: inline-size;
	}

	/* THE GROUND FLIPS AT THE CIRCLE'S EQUATOR, AS DRAWN — white above,
	   slate from there down and through the stacked copy. `Phone.dc.html`
	   paints it as `linear-gradient(#fff 0, #fff 195px, <slate> 195px)`
	   over a slate page, and 195px is exactly half of its 390px frame.

	   THIS IS THE ONE PLACE THE SMALL LAYOUT IS MORE THAN THE WIDE ONE
	   RESIZED, and it is load-bearing rather than decorative: the panes'
	   copy is white, because it sits on the disk at the wide size and on
	   this band at the small one. Without the band it is white on white.

	   FULL-BLEED, so the band reaches the window edge rather than stopping
	   at Neve's container. `left: 50%` + `100vw` + a centring translate is
	   the usual escape and it is safe here because this wrapper sets no
	   `overflow` — the wide block's `overflow: hidden` is its own rule, not
	   a shared one.

	   STILL A COMPONENT SURFACE, NOT A GROUND. It reads
	   `--color-bg-surface`, the same token the disk does, so the two cannot
	   drift apart. `'ground' => 'dark'` remains wrong for the same reason
	   story 3 gave: it would put `.joetennis-ground-dark` on <body> and
	   repaint the whole page, and checks/05 counts that key at six. */
	.medallion-page__medallion::before {
		content: '';
		position: absolute;
		left: 50%;
		width: 100vw;
		transform: translateX(-50%);
		top: calc(var(--medallion-sm-size) * var(--medallion-sm-scale) / 2);
		bottom: 0;
		background: var(--color-bg-surface);
		z-index: 0;
	}

	/* THE THREE CIRCLE LAYERS SCALE AS ONE GROUP, AND THE NEGATIVE
	   TRANSFORM-ORIGIN IS WHAT MAKES THREE SEPARATE TRANSFORMS EQUAL ONE.

	   Scaling the WRAPPER would have been simpler and is wrong here: the
	   panes are inside it, so their type would shrink with the disk, and
	   a transform does not change layout height — the page below would
	   still be spaced for the unscaled copy and leave a dead band.

	   So each layer scales itself. A layer sitting at `left: L` scales
	   about its own box by default, which leaves its left edge at L when
	   the group wants it at L*s. Setting the origin to -L fixes it
	   exactly: a point maps to `origin + s*(p - origin)`, so the element's
	   own top-left (0,0) maps to `L + origin*(1 - s)`, and `origin = -L`
	   makes that `L*s`. The tab layer is already at the wrapper's origin,
	   so its origin is `top left` and needs no compensation. */
	.medallion-page__medallion .medallion-page__disk {
		display: block;
		position: absolute;
		left: var(--medallion-sm-disk-offset);
		top: var(--medallion-sm-disk-offset);
		width: var(--medallion-sm-disk);
		height: var(--medallion-sm-disk);
		border-radius: 50%;
		background: var(--color-bg-surface);
		transform: scale(var(--medallion-sm-scale));
		transform-origin:
			calc(-1 * var(--medallion-sm-disk-offset))
			calc(-1 * var(--medallion-sm-disk-offset));
	}

	.medallion-page__medallion .medallion-page__portrait {
		display: block;
		position: absolute;
		left: var(--medallion-sm-pane-offset);
		top: var(--medallion-sm-pane-offset);
		width: var(--medallion-sm-pane);
		height: var(--medallion-sm-pane);
		clip-path: circle(var(--medallion-sm-inner) at 50% 50%);
		pointer-events: none;
		transform: scale(var(--medallion-sm-scale));
		transform-origin:
			calc(-1 * var(--medallion-sm-pane-offset))
			calc(-1 * var(--medallion-sm-pane-offset));
	}

	/* THE SECOND FIGURE BOX. The phone's portrait is proportionally much
	   larger than the wide one — 0.653 of D against 0.475 — and centred
	   rather than held to the right half, because there is no column of
	   text beside it to leave room for. Both boxes ship as inert custom
	   properties on the <img> and each block spends its own pair. */
	.medallion-page__medallion .medallion-page__portrait img {
		display: block;
		position: absolute;
		max-width: none;
		left: var(--medallion-figure-sm-left);
		top: var(--medallion-figure-sm-top);
		width: var(--medallion-figure-sm-width);
		height: auto;
	}

	.medallion-page__medallion .medallion-page__tabs {
		position: absolute;
		left: 0;
		top: 0;
		width: var(--medallion-sm-size);
		height: var(--medallion-sm-size);
		margin: 0;
		gap: 0;
		border-bottom: 0;
		transform: scale(var(--medallion-sm-scale));
		transform-origin: top left;
	}

	/* RESTATED, for the reason the wide block gives: the UA sheet
	   expresses `hidden` as `display: none` at (0,0,0) and the base rule
	   sets `display: flex`, so the guard has to out-score every rule in
	   this block. Nothing here declares `display` on the bar — its
	   children are all out of flow — but the next person to add one will
	   not have read that sentence. */
	.medallion-page__medallion .medallion-page__tabs[hidden] {
		display: none;
	}

	.medallion-page__medallion .medallion-page__tab {
		position: absolute;
		left: 0;
		top: 0;
		width: var(--medallion-sm-size);
		height: var(--medallion-sm-size);
		padding: 0;
		margin: 0;
		border: 0;
		border-radius: 0;
		background: transparent;
		font-size: 0;
		clip-path: var(--medallion-tab-sm-clip);
	}

	.medallion-page__medallion .medallion-page__tab-art--sm {
		display: block;
		position: absolute;
		left: 0;
		top: 0;
		width: 100%;
		height: 100%;
		overflow: visible;
	}

	/* THE PAINT IS RESTATED RATHER THAN SHARED, AND checks/07 IS WHAT
	   STOPS THE TWO DRIFTING. These four colour values are identical to
	   the wide block's and could have been hoisted above both; that would
	   have meant moving rules story 3 wrote and re-proving its
	   specificity assertions for no behaviour change. Duplicating them
	   instead is only safe if something notices when one copy moves, so
	   the check now compares the two blocks' arc colours and fails if
	   they disagree. */
	.medallion-page__medallion .medallion-page__tab-seg {
		fill: rgba(0, 0, 0, 0.14);
	}

	.medallion-page__medallion .medallion-page__tab-ring {
		fill: none;
		stroke: transparent;
		stroke-width: var(--medallion-sm-focus);
	}

	.medallion-page__medallion .medallion-page__tab-label {
		fill: #ffffff;
		font-family: var(--font-heading);
		font-size: var(--medallion-sm-label);
		font-weight: 600;
		letter-spacing: 0.3px;
		transition: fill 0.2s ease;
	}

	/* (0,5,0) AND ALL THREE OF NEVE'S HOVER PROPERTIES, the same fight
	   the wide block and flush-split.css:1068-1072 both record: Neve
	   paints `button:hover { background: var(--primarybtnhoverbg) }` at
	   (0,1,1), which resolves to nv-secondary-accent #2f5aae, and on a
	   full-medallion button that is a blue wash clipped to one quadrant. */
	.medallion-page__medallion .medallion-page__tabs .medallion-page__tab[role="tab"]:hover {
		background: transparent;
		color: inherit;
		border-color: transparent;
	}

	.medallion-page__medallion .medallion-page__tab[role="tab"]:hover .medallion-page__tab-label {
		fill: #00e5ff;
	}

	/* THE FOCUS RING IS THE INSET PATH, NOT AN OUTLINE. An outline cannot
	   serve: the button's box is the whole medallion and `clip-path`
	   clips outlines too, so it would be cut to the arc's edge and half
	   of it discarded. The `outline` declaration below exists to WIN
	   against the (0,3,0) baseline in site-interactive-states.css, and
	   the stroke beside it exists to PAINT — both are stated so the
	   transparent outline does not read as `outline: none` in disguise. */
	.medallion-page__medallion .medallion-page__tabs .medallion-page__tab[role="tab"]:focus-visible {
		outline: 2px solid transparent;
		outline-offset: 0;
	}

	.medallion-page__medallion .medallion-page__tab[role="tab"]:focus-visible .medallion-page__tab-ring {
		stroke: #00e5ff;
	}

	.medallion-page__medallion .medallion-page__tab[role="tab"][aria-selected="true"] .medallion-page__tab-label,
	.medallion-page__medallion .medallion-page__tab[role="tab"][aria-selected="true"]:hover .medallion-page__tab-label {
		fill: var(--color-tab-current);
	}

	/* THE PANES STACK, AND THEY ARE THE ONE THING HERE THAT IS NOT THE
	   WIDE LAYOUT WITH DIFFERENT NUMBERS. Above the breakpoint they are
	   absolutely positioned inside the circle and shaped by two floats;
	   here they are ordinary flow beneath it, which is the whole of
	   CAP-4. Nothing is hidden from a small screen that a wide one shows. */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__panes {
		position: relative;
		z-index: 1;
		margin: 0;
	}

	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane {
		position: relative;
		z-index: 1;
		display: flex;
		flex-direction: column;
		gap: 14px;
		width: auto;
		height: auto;
		padding: 28px 24px 40px;
		overflow: visible;
		color: #ffffff;
	}

	/* RESTATED BECAUSE THE RULE ABOVE SETS `display`, which un-hides a
	   pane the UA sheet had hidden at (0,0,0). This is the guard
	   checks/06's docblock names as the likeliest future break. */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane[hidden] {
		display: none;
	}

	/* THE HEADING IS SHOWN, as the artboard draws it and as the wide
	   block already decides for the same reason: the arc above carries
	   the same words at 13px and curved, which is a wayfinding mark
	   rather than a title. Story 2 hides it under `--tabbed`; this puts
	   it back at (0,4,1). */
	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane > h2:first-of-type {
		display: block;
		margin: 0;
		font-family: var(--font-heading);
		font-size: 24px;
		font-weight: 600;
		line-height: 1.2;
		letter-spacing: -0.01em;
		color: #ffffff;
	}

	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane p {
		font-size: 15px;
		line-height: 1.6;
		margin: 0 0 14px;
	}

	.medallion-page--tabbed .medallion-page__medallion .medallion-page__pane p:last-child {
		margin-bottom: 0;
	}
}
}

/* The arc label is the only thing in the medallion that animates, and a
   colour transition on a word is exactly the kind of movement this query
   exists for. Matches the artboard's own reduced-motion block. */
@media (prefers-reduced-motion: reduce) {
	.medallion-page__medallion .medallion-page__tab-label {
		transition: none;
	}
}
