/* ==========================================================================
   WP Mojster — Single post styles (single.php article page)
   Article shell, header, breadcrumbs, reading column, article typography,
   alignwide geometry, featured image, the block families, post CTA, related
   posts. Block rules are mirrored declaration-for-declaration in
   editor-style.css. Breakpoints: 640 / 768 / 1024 only (tokens.css rule);
   the one max-width query is the breadcrumb collapse at the same 640
   boundary. Motion: the token motion set only. No author box (the omission
   is the design); no alignfull anywhere — the theme never registers it.
   ========================================================================== */

/* Article-scoped measure — 680px, fixed by measured CPL. Scoped to this
   sheet; --measure-max keeps its sitewide uses. */
:root {
	--measure-article: 680px;
}

/* ==========================================================================
   Article header — paper band; header and body share the centered 680px
   reading column so they sit on one axis
   ========================================================================== */
.ah-band {
	background: var(--color-paper);
	padding-top: 40px; /* the service-hero step — breadcrumbs arrive close to the site header */
}

.rcol {
	max-width: var(--measure-article);
	margin: 0 auto;
}

/* ---- Breadcrumbs — the service-page .crumbs pattern on Rank Math's (or
   the theme fallback's) markup. The trail is the parent path only — the
   current article never appears visibly (the H1 announces it). */
.crumbs {
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	margin-bottom: 28px;
}

/* Rank Math wraps its items in a <p>; inline keeps the collapsed-trail
   arrow (below) on the same line and changes nothing else */
.crumbs p { display: inline; }

/* 44px hit target via padding alone: vertical padding on an inline link
   grows the hit box without entering line-height; the 8px horizontal pair
   stays inside the separator's margins so crumb boxes never meet. */
.crumbs a {
	color: var(--color-primary);
	font-weight: 500;
	padding: 14px 8px;
	margin: 0 -8px;
	text-decoration: underline;
	text-underline-offset: 3px;
	text-decoration-thickness: 1.5px;
	transition: color var(--transition-fast);
}
.crumbs a:hover { color: var(--color-primary-strong); }
.crumbs a:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); border-radius: 2px; }

.crumbs .sep,
.crumbs .separator {
	margin: 0 8px;
	color: var(--color-text-muted);
}

/* Hide Rank Math's current-title item and the separator before it — the
   item stays in the DOM, BreadcrumbList schema untouched. The theme
   fallback never prints a title item. */
.crumbs .last,
.crumbs [aria-current] { display: none; }
.crumbs .separator:has(+ .last) { display: none; }

/* Below 640 the trail collapses to the single ← Blog parent link (always
   the second link, depth-independent); the ← is decorative and never
   announced. Hidden parents stay in the DOM — crawlable, unfocusable. */
@media (max-width: 639px) {
	.crumbs a:not(:nth-of-type(2)),
	.crumbs .sep,
	.crumbs .separator { display: none; }
	.crumbs::before {
		content: "←" / "";
		color: var(--color-text-muted);
		margin-right: 8px;
	}
	.crumbs a:nth-of-type(2) {
		display: inline-flex;
		align-items: center;
		padding: 16px 10px;
		margin: -16px -10px;
	}
}

/* ---- Category eyebrow — the archive .pc-cat conventions; 48px tap target
   via padding + negative-margin compensation. */
.ah-cat {
	display: inline-flex;
	align-items: center;
	font-size: 13px;
	font-weight: 600;
	color: var(--color-primary);
	text-decoration: none;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	padding: 16px 8px;
	margin: -16px -8px -4px;
	transition: color var(--transition-fast);
}
.ah-cat:hover {
	color: var(--color-primary-strong);
	text-decoration: underline;
	text-underline-offset: 3px;
}
.ah-cat:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); border-radius: 2px; }

/* ---- H1 — one per page; carries the sitewide 32×3 petrol level mark */
.ah-band h1::before {
	content: "";
	display: block;
	width: 32px;
	height: 3px;
	background: var(--color-primary);
	border-radius: 2px;
	margin: 16px 0;
}
.ah-band h1 {
	font-size: 30px; /* mobile step; 38 from 768 */
	font-weight: 700;
	margin-bottom: 14px;
}

/* ---- Deck — manual excerpt only; the node is omitted when absent.
   Body-size but muted, tighter 1.5 leading, between H1 and meta row. */
.ah-deck {
	font-size: 18px; /* mobile step; 20 from 768 */
	line-height: 1.5;
	color: var(--color-text-muted);
	margin-bottom: 18px;
}

/* ---- Meta row — published date, "Updated" only when meaningful, no
   byline. Dots are decorative. */
.ah-meta {
	font-size: 15px;
	color: var(--color-text-muted);
}
.ah-meta .dot {
	margin: 0 8px;
	color: var(--color-border);
}

/* ---- Featured image — 16:9 at the reading measure; the frame reserves
   the box (CLS 0). Missing image = no node at all — the body band's 40px
   top padding closes the seam. */
.ah-media {
	aspect-ratio: 16 / 9;
	margin-top: var(--space-8);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-card);
	overflow: hidden;
	background: var(--color-surface);
}
.ah-media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* ==========================================================================
   Reading column — paper band; content starts 40px below the featured
   image, the 64px bottom padding closes the article (last child flush)
   ========================================================================== */
.rd-band {
	background: var(--color-paper);
	padding-top: 40px;
	padding-bottom: var(--space-16);
}

.rd {
	max-width: var(--measure-article);
	margin: 0 auto;
}
.rd > :first-child { margin-top: 0; }
.rd > :last-child { margin-bottom: 0; }

/* Body: --text-md / 1.7 (article-only deviation from --leading-body 1.6).
   `overflow-wrap: anywhere` so an unlinked URL or long unbroken token never
   puts the page into horizontal scroll; mirrored on the editor `p`. Code
   and preformatted are unaffected — they are `pre`, not `p`. */
.rd p {
	font-size: 18px; /* mobile step; --text-md from 768 */
	line-height: 1.7;
	margin: 0 0 1em;
	overflow-wrap: anywhere;
}

/* Heading rhythm — generous above, tighter below. No level mark on body
   headings — the mark stays a template signature. Color/leading/tracking
   come from global.css; H4 is body-size at 600 with no tracking, so the
   sitewide heading tracking is zeroed there. H5/H6 stay unstyled — outline
   depth is capped at H4. */
.rd h2 {
	font-size: var(--text-lg); /* mobile step 24; --text-xl 30 from 768 */
	font-weight: 700;
	margin: var(--space-12) 0 var(--space-3);
}
.rd h3 {
	font-size: 20px; /* mobile step; --text-lg 24 from 768 */
	font-weight: 600;
	margin: var(--space-8) 0 var(--space-3);
}
.rd h4 {
	font-size: 18px; /* mobile step; --text-md 20 from 768 */
	font-weight: 600;
	letter-spacing: 0;
	margin: var(--space-8) 0 var(--space-2);
}

/* Body links — petrol 500, always underlined; overflow-wrap so unbroken
   technical URLs never escape the column */
.rd a {
	color: var(--color-primary);
	font-weight: 500;
	text-decoration: underline;
	text-underline-offset: 3px;
	text-decoration-thickness: 1.5px;
	overflow-wrap: anywhere;
	transition: color var(--transition-fast);
}
.rd a:hover { color: var(--color-primary-strong); }
.rd a:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); border-radius: 2px; }

/* ---- Inline formats — bold is 600 + ink, not 700 (which reads as error
   inside 400 body); matches the sitewide H4/strong convention. */
.rd strong {
	font-weight: 600;
	color: var(--color-ink);
}
.rd em { font-style: italic; }

/* Inline code chip. The :not(pre code) guard keeps this off block-level
   code/preformatted, so the shared panel below needs no reset. .85em is
   relative, so the chip tracks the body step. */
.rd code:not(pre code) {
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 0.85em;
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: 4px;
	padding: 1px 6px;
	color: var(--color-primary-strong);
}

/* ---- Lists carry the body step and leading explicitly: without this, li
   falls back to the sitewide 17px/1.6 while p is 18/20 at 1.7, and the
   em-based margin would resolve against 17px. li and ::marker inherit from
   the list; adjacent margins collapse, so nothing stacks against a heading
   gap. */
.rd ul,
.rd ol {
	font-size: 18px; /* mobile step; --text-md from 768 */
	line-height: 1.7;
	margin: 0 0 1em;
	padding-left: 26px;
}
.rd li { margin-bottom: 8px; }

/* Markers carry petrol; ordered markers step to 600 so the number reads as a
   marker rather than as body text. */
.rd li::marker { color: var(--color-primary); }
.rd ol li::marker { font-weight: 600; }

/* Nested one level: no bottom margin of its own — the parent li's 8px carries
   the rhythm; deeper levels inherit. */
.rd li ul,
.rd li ol {
	margin: 8px 0 0;
	padding-left: 24px;
}

/* Must undo global.css's `ul[class] { list-style: none }` reset: core saves
   `<ul class="wp-block-list">`, so article lists would lose their markers
   while the editor canvas (no global.css) kept them. `revert` restores the
   UA's own markers, including nested levels. Scoped to .rd so the sitewide
   reset keeps every other list. */
.rd ul[class],
.rd ol[class] {
	list-style: revert;
}

/* ==========================================================================
   Image family — core/image and core/gallery.
   CASCADE: the theme dequeues `wp-block-library`, but core loads per-block
   CSS on demand and prints it AFTER this sheet — core wins every
   specificity tie, so each override is pitched to beat a real core
   selector. `:not(#individual-image)` mirrors core's own gallery
   specificity device (ID-level weight no id-free selector can outrank);
   even so, core's two-image tile-width rule (2,7,2) cannot be beaten, hence
   the single documented `width: auto !important` below.
   ========================================================================== */

/* Figure rhythm — the one 32px media gap. Longhands, never the shorthand:
   `.rd > .alignwide` supplies a wide figure's calc() side margins and a
   shorthand would silently reset them. */
.rd .wp-block-image,
.rd .wp-block-gallery {
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
}

/* Boundary spacing — flush first/last. The (0,2,0) figure rhythm above ties
   `.rd > :first/:last-child` and wins on source order, so these (0,3,0)
   rules restore the flush contract (and outrank `.rd > .alignwide` too). */
.rd > .wp-block-image:first-child,
.rd > .wp-block-gallery:first-child {
	margin-top: 0;
}
.rd > .wp-block-image:last-child,
.rd > .wp-block-gallery:last-child {
	margin-bottom: 0;
}

/* Natural ratio — deliberately NO width or aspect-ratio: intrinsic
   width/height + srcset already reserve the box (CLS 0), and forcing 16:9
   would crop the screenshots articles exist to show; 16:9 binds the
   featured image only. */
.rd .wp-block-image img {
	border: 1px solid var(--color-border);
	border-radius: var(--radius-card);
}

/* Portrait image — `is-portrait` is added server-side (inc/blog.php) to an
   unaligned, unresized image taller than wide. At the full 680 column such
   an image outgrows a desktop viewport, so it narrows to 520px and centers.
   Horizontal longhands only — the vertical figure rhythm stays intact.
   Frontend-only: the class never exists in the editor canvas. */
.rd .wp-block-image.is-portrait {
	max-width: 520px;
	margin-left: auto;
	margin-right: auto;
}

/* Caption — left aligned, 10px above, no bottom margin (the figure's 32px
   carries the gap). (0,2,2) outranks core's gallery-caption
   `text-align: center`. Per-image gallery tile captions stay unstyled —
   core's (0,3,2) tile-caption rule outranks the image selector. */
.rd .wp-block-image > figcaption,
.rd figure.wp-block-gallery > figcaption {
	font-size: 15px;
	line-height: 1.5;
	color: var(--color-text-muted);
	margin: 10px 0 0;
	text-align: left;
}

/* Linked image — the image is the affordance, nothing underlined; the focus
   ring is drawn at the image's own radius (overriding `.rd a`'s 2px). */
.rd .wp-block-image > a {
	display: block;
	text-decoration: none;
	border-radius: var(--radius-card);
}
.rd .wp-block-image > a:focus-visible {
	outline: var(--focus-ring);
	outline-offset: var(--focus-offset);
	border-radius: var(--radius-card);
}

/* ---- Gallery — a fixed 2-up grid, never a slider; 1-up below 768.
   `display: grid` is declared because core's flex layout ships in dequeued
   `global-styles` — an unstyled gallery would fall back to block flow. */
.rd .wp-block-gallery {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--space-4);
}

/* The gallery-level caption spans both tracks. */
.rd figure.wp-block-gallery > figcaption {
	grid-column: 1 / -1;
}

/* Column-locked: a gallery never goes wide. (0,3,0) neutralizes the shared
   `.rd > .alignwide` (0,2,0) for galleries without touching it — image,
   embed and media & text keep their wide opt-in. */
.rd .wp-block-gallery.alignwide,
.rd .wp-block-gallery.alignleft,
.rd .wp-block-gallery.alignright,
.rd .wp-block-gallery.aligncenter {
	width: auto;
	max-width: 100%;
	margin-left: 0;
	margin-right: 0;
}

/* Tiles — the grid owns the track; the tile width is neutralized.
   `!important` is required: core's caption-less two-image variant reaches
   (2,7,2), prints after this sheet, and left to win renders quarter-width
   tiles. `display: block` drops core's flex-column tile, whose
   `flex: 1 0 0%` on the img would collapse the aspect box. */
.rd figure.wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
	display: block;
	width: auto !important;
	margin: 0;
}

/* 4:3 crop — the only crop in this file; tiles are compound parts, hence
   panel radius 8, not the photographic 12. The leading `figure` is
   load-bearing: without it this only TIES core's `.is-cropped … img`
   (1,4,2) and would lose on source order. */
.rd figure.wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) img {
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-btn);
}

/* ---- Wide blocks — native .alignwide only, max 1080px, centered on the
   column axis. A container query, not a fourth breakpoint: 100cqw is the
   container's inner width, so the block is always viewport-safe. In the
   margin calc, 100% is the 680 column — equal negative margins keep the
   overhang symmetric. No alignfull rules exist anywhere. */
.rd-band .container { container-type: inline-size; }

.rd > .alignwide {
	width: min(1080px, 100cqw);
	margin: var(--space-8) calc((100% - min(1080px, 100cqw)) / 2);
}

/* ==========================================================================
   Quotes — core/quote and core/pullquote.
   The quote IS the blockquote and carries a class; the pullquote's
   blockquote is an unclassed INTERMEDIATE inside a figure — so nothing here
   selects a bare `blockquote`. Block-level selectors are qualified with
   their real element (`blockquote.wp-block-quote`,
   `figure.wp-block-pullquote`): the extra element beats core's (0,2,0)
   style-variant layer at any source order. Core's theme.min.css quote rules
   never load (`wp-block-library-theme` is dequeued). Known limit: core
   still registers the excluded style variants; the honest fix is
   unregister_block_style() in inc/blog.php, not CSS.
   ========================================================================== */

/* ---- Quote — petrol channel, body values unchanged. Longhand vertical
   margins: a shorthand would defeat global.css's side-margin reset and the
   editor's `.wp-block` auto-centering — longhands keep the two sheets
   declaration-identical. */
.rd blockquote.wp-block-quote {
	border-left: 3px solid var(--color-primary);
	padding: 4px 0 4px 24px;
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
}

/* Column-locked: among text blocks only the pullquote may go wide — wide
   prose breaks the measure. (0,3,0) neutralizes the shared
   `.rd > .alignwide` for quotes without touching it. */
.rd .wp-block-quote.alignwide,
.rd .wp-block-quote.alignleft,
.rd .wp-block-quote.alignright,
.rd .wp-block-quote.aligncenter {
	width: auto;
	max-width: 100%;
	margin-left: 0;
	margin-right: 0;
}

/* Quoted paragraphs keep the body values; only the last is closed up.
   `:last-of-type` because `cite` is the blockquote's last child. */
.rd .wp-block-quote p:last-of-type {
	margin-bottom: 0;
}

/* Citation — small-text scale, not italic; beats core's cite rules, which
   would otherwise right-align and resize it. */
.rd blockquote.wp-block-quote cite {
	display: block;
	font-style: normal;
	font-size: 15px;
	color: var(--color-text-muted);
	margin-top: 10px;
}

/* ---- Pullquote — centered, hairline frame, display type. Longhand
   vertical margins: at (0,2,1) this overrides the shared alignwide's
   vertical 32px while leaving its width/centering geometry the single
   source of truth. Padding beats core's `padding: 4em 0`. */
.rd figure.wp-block-pullquote {
	border-top: 1px solid var(--color-border);
	border-bottom: 1px solid var(--color-border);
	padding: 36px 24px;
	margin-top: var(--space-12);
	margin-bottom: var(--space-12);
	text-align: center;
}

/* The intermediate blockquote is invisible structure — the figure owns the
   frame. Scoped inside `.wp-block-pullquote`, so core/quote is never
   reached. */
.rd .wp-block-pullquote blockquote {
	margin: 0;
	border: 0;
	padding: 0;
}

/* Display type — the article's one enlarged voice; fixed 28px at every
   viewport, so no responsive step exists. */
.rd .wp-block-pullquote p {
	font-size: 28px;
	font-weight: 600;
	line-height: 1.45;
	letter-spacing: -0.01em;
	color: var(--color-ink);
	margin: 0;
}

/* Citation — the quote's small-text scale, 12px above. Beats core's
   `:where(cite)` `color: inherit`, which would take the ink above. */
.rd figure.wp-block-pullquote cite {
	display: block;
	font-style: normal;
	font-size: 15px;
	color: var(--color-text-muted);
	margin-top: 12px;
}

/* Boundary spacing — flush first/last at (0,3,1): the (0,2,1) rhythm rules
   above would beat `.rd > :first/:last-child` (0,2,0); these restore the
   flush contract and outrank `.rd > .alignwide` too. */
.rd > blockquote.wp-block-quote:first-child,
.rd > figure.wp-block-pullquote:first-child {
	margin-top: 0;
}
.rd > blockquote.wp-block-quote:last-child,
.rd > figure.wp-block-pullquote:last-child {
	margin-bottom: 0;
}

/* ==========================================================================
   Buttons and separators — core/buttons + core/button and core/separator.
   ONE VOICE: exactly one article button — no secondary/outline/small
   variants. Only per-block core sheets reach the page (printed AFTER this
   sheet); the flex layout CSS for `.is-layout-flex` ships in dequeued
   `global-styles`, so the button is left-aligned and shrink-wrapped by
   construction. Core's `:where()` pill radius/padding are (0,0,0), and its
   `text-decoration: none` loses to `.rd a` — the declaration below is
   load-bearing. Core's 100px separator width lives in theme.css, which
   loads on neither surface. The base button rule is (0,3,0) so the voice
   beats `.rd a`'s underline, hover repaint and 2px focus radius outright.
   ========================================================================== */

/* ---- Buttons block — the 32px action rhythm. Longhand margins so the
   shared `.rd > .alignwide` calc() geometry stays intact. */
.rd .wp-block-buttons {
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
}

/* Column-locked: buttons never go wide; (0,3,0) neutralizes the shared
   alignwide (`wide` is the only alignment core/buttons supports). */
.rd .wp-block-buttons.alignwide {
	width: auto;
	max-width: 100%;
	margin-left: 0;
	margin-right: 0;
}

/* ---- Button — the sitewide voice as a block-level action. 15px/600 is
   deliberate (not the global `.btn`'s 17px). `line-height: 1.2` makes the
   48px minimum deterministic across surfaces; `overflow-wrap: normal`
   removes a frontend/canvas asymmetry (`.rd a` sets `anywhere` here, the
   editor's link rule never reaches the button there). */
.rd .wp-block-button .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 48px;
	padding: 12px 24px;
	background: var(--color-primary);
	color: #fff;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.2;
	border-radius: var(--radius-btn);
	text-decoration: none;
	overflow-wrap: normal;
	transition: background-color var(--transition-fast);
}
.rd .wp-block-button .wp-block-button__link:hover {
	background: var(--color-primary-strong);
}
.rd .wp-block-button .wp-block-button__link:focus-visible {
	outline: var(--focus-ring);
	outline-offset: var(--focus-offset);
}

/* ---- Separator — a pause, not a layout line: 96px hairline at the 48px
   rhythm. `border: 0` then `border-top` replaces core's 2px rule. The
   `auto` side margins also column-lock the block: this rule's (0,2,1)
   beats the shared alignwide (0,2,0), so no separate lock is needed. */
.rd hr.wp-block-separator {
	width: 96px;
	border: 0;
	border-top: 1px solid var(--color-border);
	margin-top: var(--space-12);
	margin-bottom: var(--space-12);
	margin-left: auto;
	margin-right: auto;
}

/* Boundary spacing — flush first/last at (0,3,0)/(0,3,1), outranking the
   rhythm rules above and the shared alignwide. */
.rd > .wp-block-buttons:first-child,
.rd > hr.wp-block-separator:first-child {
	margin-top: 0;
}
.rd > .wp-block-buttons:last-child,
.rd > hr.wp-block-separator:last-child {
	margin-bottom: 0;
}

/* ==========================================================================
   Table — core/table. The block wrapper figure IS the scroll container
   (`overflow-x` is restated so scroll and radius travel together). Core's
   per-block CSS prints AFTER this sheet, so every rule below outranks its
   counterpart instead of relying on source order. The easy miss: under
   `border-collapse: collapse` the WIDER border wins regardless of
   specificity, so core's 3px thead/tfoot rules must be zeroed or the header
   renders a heavy rule instead of the 1px hairline. Editor-control issues
   (stripes style, hasFixedLayout default, alignment offers) are deliberately
   not fought from CSS.
   ========================================================================== */

/* Scroll container — 32px rhythm, hairline frame, panel radius 8.
   THE GRID IS LOAD-BEARING: a single-column grid makes table and caption
   share ONE track, so the caption spans exactly the table's used width at
   every scroll position. `minmax(min-content, 1fr)`: base `min-content`
   captures the real width a wide table needs; `1fr` lets a narrow table
   fill the reading column. The 560 floor rides on the `min-width: 560px`
   of table and caption below (clamping their min-content contributions).
   Measured rejects: `minmax(560px, max-content)` never grows (a minmax
   track needs FREE SPACE, and a scroll container has none); bare
   `max-content` blows up to 500000px against core's default
   `has-fixed-layout`. Do not reintroduce either. */
.rd .wp-block-table {
	display: grid;
	grid-template-columns: minmax(min-content, 1fr);
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-btn);
	overflow-x: auto;
}

/* Focus ring — reachable by real keyboard Tab in every engine:
   inc/blog.php adds tabindex/role at render time and names the region from
   the figcaption. Latent in the editor canvas, which never runs render. */
.rd .wp-block-table:focus-visible {
	outline: var(--focus-ring);
	outline-offset: var(--focus-offset);
}

/* Column-locked: a table never goes wide — it scrolls. (0,3,0) neutralizes
   the shared alignwide. The display reset is load-bearing: core's aligned
   variants set `display: table; width: auto`, which would shrink-wrap the
   figure to its 560px table and push the PAGE sideways at 375/320. Reset to
   `grid`, NOT `block`, to keep the shared caption track. */
.rd .wp-block-table.alignwide,
.rd .wp-block-table.alignleft,
.rd .wp-block-table.alignright,
.rd .wp-block-table.aligncenter {
	display: grid;
	width: auto;
	max-width: 100%;
	margin-left: 0;
	margin-right: 0;
}

/* Boundary spacing — flush first/last at (0,3,0); the (0,2,0) rhythm rule
   above would tie `.rd > :first/:last-child` and win on source order. */
.rd > .wp-block-table:first-child {
	margin-top: 0;
}
.rd > .wp-block-table:last-child {
	margin-bottom: 0;
}

/* The table — 16/1.5, one step below body (tabular content is scanned, not
   read). `min-width: 560px` converts a width problem into a scroll: the
   columns keep their proportions instead of crushing. `table-layout` is
   deliberately NOT declared. */
.rd .wp-block-table table {
	border-collapse: collapse;
	width: 100%;
	min-width: 560px;
	font-size: 16px;
	line-height: 1.5;
	background: var(--color-paper);
}

/* Kill core's 3px section rules — see the header note on collapsed borders. */
.rd .wp-block-table thead,
.rd .wp-block-table tfoot {
	border: 0;
}

/* Header cells — 16/600 ink on surface, explicitly NOT the uppercase label
   treatment; `text-align: left` overrides the UA `center`. `border: 0` then
   the bottom longhand reduces core's four-sided border to one hairline. */
.rd .wp-block-table th {
	background: var(--color-surface);
	padding: 12px 16px;
	font-size: 16px;
	font-weight: 600;
	line-height: 1.5;
	color: var(--color-ink);
	text-align: left;
	text-transform: none;
	letter-spacing: 0;
	border: 0;
	border-bottom: 1px solid var(--color-border);
}

/* Body cells — row hairlines only; same reset-then-longhand reason as `th`. */
.rd .wp-block-table td {
	padding: 12px 16px;
	font-size: 16px;
	line-height: 1.5;
	color: var(--color-text);
	border: 0;
	border-bottom: 1px solid var(--color-border);
}

/* Final row borderless — the container border closes the table. Qualified
   on `td` (not `tr`) so the header hairline survives a single-header-row
   table. */
.rd .wp-block-table tr:last-child td {
	border-bottom: 0;
}

/* Caption — rendered INSIDE the scroll container, so it must move and
   measure WITH the table. What aligns it is the shared grid track above,
   not a width here (an own-width approach was measured and FAILED — 180px
   short at max scroll). Its `min-width: 560px` is half of the 560 floor
   mechanism, not a caption width. `box-sizing: border-box` is required for
   the editor mirror: the canvas never loads global.css's universal reset,
   and without it 560px would mean content width and push the track to 592.
   The top hairline pairs with the borderless last row above — one line
   between the last row and the caption, not two. */
.rd .wp-block-table > figcaption {
	box-sizing: border-box;
	min-width: 560px;
	margin: 0;
	padding: 10px 16px;
	background: var(--color-paper);
	border-top: 1px solid var(--color-border);
	font-size: 15px;
	line-height: 1.5;
	color: var(--color-text-muted);
	text-align: left;
}

/* ==========================================================================
   Code and preformatted — core/code + core/preformatted, ONE shared
   monospace panel (both render a root `pre`). Keyboard semantics are added
   at render time by inc/blog.php; this sheet owns appearance only.
   THE WRAP RULES ARE THE TRAP: long lines must scroll, never wrap, and core
   ships `white-space: pre-wrap` at two levels — on the `pre` for
   preformatted (answered by the panel rule) and on the INNER `code` for
   core/code, so the inner-code rule below is what makes no-wrap real;
   core's paired `overflow-wrap: break-word` is reset with it. The inline
   chip is `code:not(pre code)`, so block code needs no reset here. The
   class-based lock below keeps the shared alignwide off the panel.
   ========================================================================== */

/* Scroll panel — 32px rhythm, hairline frame, panel radius 8; padding 16/20
   mobile stepping to 20/24 at 768. (0,2,0) beats core's `pre-wrap` on
   `.wp-block-preformatted`. */
.rd .wp-block-code,
.rd .wp-block-preformatted {
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 15px;
	line-height: 1.65;
	color: var(--color-text);
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-btn);
	padding: 16px 20px;
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
	overflow-x: auto;
	white-space: pre;
}

/* core/code's text lives in the inner `code`, where core wraps it —
   (0,2,1) unwraps it so the panel scrolls instead. */
.rd .wp-block-code code {
	white-space: pre;
	overflow-wrap: normal;
}

/* Focus ring — not latent: inc/blog.php gives both panels an explicit
   tabindex="0" at render time, so the ring is reachable in every engine. */
.rd .wp-block-code:focus-visible,
.rd .wp-block-preformatted:focus-visible {
	outline: var(--focus-ring);
	outline-offset: var(--focus-offset);
}

/* Column lock — the panel never goes wide; it scrolls. Zeroing the
   horizontal margins undoes the shared alignwide's centring calc; vertical
   rhythm untouched. No `display` reset — no caption track to preserve. */
.rd .wp-block-code.alignwide,
.rd .wp-block-preformatted.alignwide {
	width: auto;
	max-width: 100%;
	margin-left: 0;
	margin-right: 0;
}

/* Boundary spacing — flush first/last at (0,3,0); the (0,2,0) panel rule
   above would tie `.rd > :first/:last-child` and win on source order. */
.rd > .wp-block-code:first-child,
.rd > .wp-block-preformatted:first-child {
	margin-top: 0;
}
.rd > .wp-block-code:last-child,
.rd > .wp-block-preformatted:last-child {
	margin-bottom: 0;
}

/* ==========================================================================
   Embeds — core/embed, the video case. `.wp-block-embed__wrapper` is the
   ratio box. RESERVED GEOMETRY IS CORE'S: the theme supports
   responsive-embeds, so `wp-embed-responsive` + core's per-block CSS
   reserve the box before the iframe loads (CLS 0). Adding `aspect-ratio`
   here would DOUBLE that treatment, and a legacy 4:3/1:1 embed keeps core's
   own padding. Chrome, rhythm and caption only.
   ========================================================================== */

/* Figure rhythm — 32px in longhands; embed is a wide OPT-IN, so the shared
   `.rd > .alignwide` governs the wide case (no per-block width rule). */
.rd .wp-block-embed {
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
}

/* Wrapper chrome — radius 12, hairline, clipped so the iframe corners
   follow the radius. No `position`/`aspect-ratio` — core owns both. */
.rd .wp-block-embed__wrapper {
	border: 1px solid var(--color-border);
	border-radius: var(--radius-card);
	overflow: hidden;
}

/* Caption — declaration-identical to the image/gallery caption, kept a
   SEPARATE rule so those stay untouched. Beats core's caption margins. */
.rd .wp-block-embed > figcaption {
	font-size: 15px;
	line-height: 1.5;
	color: var(--color-text-muted);
	margin: 10px 0 0;
	text-align: left;
}

/* Boundary spacing — flush first/last at (0,3,0), so a standard OR wide
   embed sits flush at either article boundary. */
.rd > .wp-block-embed:first-child {
	margin-top: 0;
}
.rd > .wp-block-embed:last-child {
	margin-bottom: 0;
}

/* ==========================================================================
   Structural blocks — core/details, core/group (the editorial-callout class
   only), core/columns + core/column, core/media-text. Only per-block core
   sheets reach the page, printed AFTER this sheet. Columns and media-text
   are converted to / re-declared as CSS grid so the stack happens at the
   binding <768 step, not core's 781/600: `display: grid` (0,2,0) beats
   columns' flex (0,1,0), and under grid core's flex rules are inert. A
   saved custom column width (inline flex-basis) or custom media width
   (inline grid-template-columns) are the only authoring escapes left.
   ========================================================================== */

/* ---- Details (core/details) — the sitewide FAQ disclosure grammar.
   BODY INSET IS THE DETAILS' OWN PADDING, not per-child padding: body
   blocks are DIRECT children of <details> (no body wrapper) and can be any
   block — per-child padding would REPLACE each nested block's own padding
   (list indents, code-panel padding) instead of adding an inset. The
   summary is pulled back over the side padding with equal negative margins
   so its hover surface and rail run edge to edge. */
.rd .wp-block-details {
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-btn);
	overflow: hidden;
	padding-left: 16px; /* body side inset — mobile; 20 from 768 */
	padding-right: 16px;
}

/* Summary — the 48px disclosure target. Negative side margins cancel the
   details' body padding so the summary spans full width; its own padding
   sets the text inset so summary and body sit on one axis. */
.rd .wp-block-details summary {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: var(--space-4);
	min-height: 48px;
	margin-left: -16px; /* cancel the body side padding — mobile; -20 from 768 */
	margin-right: -16px;
	padding: 12px 16px; /* mobile; 14 20 from 768 */
	font-size: 16px; /* mobile; 18 from 768 */
	font-weight: 600;
	color: var(--color-ink);
	list-style: none;
	cursor: pointer;
	transition: background-color var(--transition-fast), color var(--transition-fast);
}
.rd .wp-block-details summary::-webkit-details-marker { display: none; }

/* Petrol chevron — must stay byte-identical to `.faq-item summary::after`
   in tokens.css; petrol borders, never inherited text colour. */
.rd .wp-block-details summary::after {
	content: "";
	flex: none;
	width: 9px;
	height: 9px;
	border-right: 2px solid var(--color-primary);
	border-bottom: 2px solid var(--color-primary);
	transform: rotate(45deg);
}
.rd .wp-block-details summary:hover { background: var(--color-surface); }
.rd .wp-block-details summary:focus-visible {
	outline: var(--focus-ring);
	outline-offset: -2px;
	border-radius: var(--radius-btn);
}

/* Open state — petrol-strong summary, rotated chevron, continuous 3px inset
   rail. THE RAIL IS ON THE CONTAINER (no body wrapper exists), painted as
   an inset box-shadow; the summary carries its own identical rail so its
   hover surface never covers it. The body top/bottom insets live on the
   open state only, so a closed panel keeps no trailing gap: 2px via the
   summary's margin-bottom (collapses with the first block), 14/16 via the
   details' own padding-bottom. */
.rd .wp-block-details[open] {
	box-shadow: inset 3px 0 0 var(--color-primary);
	padding-bottom: 14px; /* body bottom inset — mobile; 16 from 768 */
}
.rd .wp-block-details[open] summary {
	color: var(--color-primary-strong);
	box-shadow: inset 3px 0 0 var(--color-primary);
	margin-bottom: 2px; /* body top inset (collapses with the first body block) */
}
.rd .wp-block-details[open] summary::after {
	transform: rotate(-135deg);
}

/* Last inner block flush — the only rule touching a body child; sets margin
   (never padding), so nested lists/code keep their own padding and markers. */
.rd .wp-block-details > :last-child {
	margin-bottom: 0;
}

/* Body text — auxiliary, one step below body, fixed 18/1.7 at EVERY width (it
   deliberately does NOT step to 20 at 768 like the main reading column). */
.rd .wp-block-details p {
	font-size: 18px;
	line-height: 1.7;
}

/* Column-locked — details is not a wide opt-in; (0,3,0) neutralizes the
   shared alignwide without touching it. */
.rd .wp-block-details.alignwide {
	width: auto;
	max-width: 100%;
	margin-left: 0;
	margin-right: 0;
}

/* Boundary spacing — flush first/last (0,3,0), outranking the rhythm and the
   shared alignwide, exactly as the other block families do. */
.rd > .wp-block-details:first-child { margin-top: 0; }
.rd > .wp-block-details:last-child { margin-bottom: 0; }

/* ---- Group (core/group) + the editorial-callout class. A PLAIN group is
   invisible structure — nothing targets `.wp-block-group`; only `.callout`
   is styled. Core wraps group children in `__inner-container`. */
.rd .callout {
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-btn);
	padding: 24px 20px; /* mobile; 28 from 768 */
	margin-top: var(--space-12);
	margin-bottom: var(--space-12);
}

/* Eyebrow label — the sitewide label voice; (0,3,0) beats `.rd .callout p`
   so a label paragraph reads as label, not body. */
.rd .callout .co-label {
	font-size: 13px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--color-primary);
	margin-bottom: 10px;
}

/* Panel heading — tight to label and body. H3 is the pattern level; H4
   stays matched so published callouts keep their styling. */
.rd .callout :is(h3, h4) {
	/* Pinned to the H4 step so promoting the heading to H3 does not resize it. */
	font-size: 18px; /* mobile step; --text-md 20 from 768 */
	letter-spacing: 0;
	margin: 0 0 var(--space-2);
}

/* Body — one step below body, fixed 18/1.65. */
.rd .callout p {
	font-size: 18px;
	line-height: 1.65;
	margin-bottom: 1em;
}

/* Last child flush — covers both the `__inner-container` structure and a
   flat one. */
.rd .callout > :last-child,
.rd .callout .wp-block-group__inner-container > :last-child {
	margin-bottom: 0;
}

/* Column-locked — the callout is a column-width content panel; (0,3,0)
   neutralizes the shared alignwide. */
.rd .callout.alignwide {
	width: auto;
	max-width: 100%;
	margin-left: 0;
	margin-right: 0;
}

/* Boundary spacing — flush first/last (0,3,0). */
.rd > .callout:first-child { margin-top: 0; }
.rd > .callout:last-child { margin-bottom: 0; }

/* ---- Columns (core/columns + core/column) — 2 equal from 768, one column
   below. Converted to grid: `display:grid` (0,2,0) beats core's flex and
   makes its flex breakpoints inert; equal `1fr 1fr` renders a saved custom
   column width (inline flex-basis) as equal. Longhand 32px rhythm. */
.rd .wp-block-columns {
	display: grid;
	grid-template-columns: 1fr;
	gap: 20px; /* mobile; 32 from 768 */
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
}

/* Inner content — narrow parallel content one step below body, fixed 18/1.65
   (it does not step to 20 at 768). */
.rd .wp-block-columns p,
.rd .wp-block-columns li {
	font-size: 18px;
	line-height: 1.65;
}

/* Each column's first/last child flush, so the halves align top and close
   cleanly. `.wp-block-column` children are direct (is-layout-flow). */
.rd .wp-block-column > :first-child { margin-top: 0; }
.rd .wp-block-column > :last-child { margin-bottom: 0; }

/* Column-locked — columns never go wide; (0,3,0) neutralizes the shared
   alignwide. */
.rd .wp-block-columns.alignwide {
	width: auto;
	max-width: 100%;
	margin-left: 0;
	margin-right: 0;
}

/* Boundary spacing — flush first/last (0,3,0). */
.rd > .wp-block-columns:first-child { margin-top: 0; }
.rd > .wp-block-columns:last-child { margin-bottom: 0; }

/* ---- Media & Text (core/media-text) — 50/50 from 768, vertically
   centered, media-first single column below. Core's grid stacks at the
   wrong step (<=600, !important) and fakes the gap with `padding: 0 8%`;
   re-declared mobile-first at the binding <768 step. Media stays a wide
   OPT-IN — no width lock, longhand rhythm so the shared alignwide keeps
   its calc side margins. `grid-template-columns` carries `!important` at
   every step (here and in the editor twin): save() emits an INLINE
   grid-template-columns for a custom mediaWidth, which no selector can
   outrank; the exception is scoped to that one property. */
.rd .wp-block-media-text {
	display: grid;
	grid-template-columns: 1fr !important;
	gap: 20px; /* mobile; 32 from 768 */
	align-items: center;
	margin-top: var(--space-8);
	margin-bottom: var(--space-8);
}

/* The media-right variant needs its own qualifier at EVERY step: core's
   `.has-media-on-the-right` template (0,2,0) ties the base rule and,
   printing later, would leave the right variant unstacked at 601–767. */
.rd .wp-block-media-text.has-media-on-the-right {
	grid-template-columns: 1fr !important;
}

/* Below 768 both halves collapse into one column; resets core's
   grid-column:2 / grid-row:1 placement. The right-variant selectors are one
   class heavier so they beat core's own placement rules. */
.rd .wp-block-media-text > .wp-block-media-text__media,
.rd .wp-block-media-text > .wp-block-media-text__content,
.rd .wp-block-media-text.has-media-on-the-right > .wp-block-media-text__media,
.rd .wp-block-media-text.has-media-on-the-right > .wp-block-media-text__content {
	grid-column: 1;
	grid-row: auto;
}

/* Core emits the media-RIGHT variant content-first, so `grid-row: auto`
   would stack text ABOVE media below 768 — both halves are placed
   explicitly (media then text); the >=768 block overrides with row 1. */
.rd .wp-block-media-text.has-media-on-the-right > .wp-block-media-text__media {
	grid-row: 1;
}
.rd .wp-block-media-text.has-media-on-the-right > .wp-block-media-text__content {
	grid-row: 2;
}

/* Strip core's `padding:0 8%` fake gap — the grid `gap` owns the spacing. */
.rd .wp-block-media-text > .wp-block-media-text__content {
	padding: 0;
}

/* Media keeps its natural ratio (no aspect-ratio); photographic radius 12 and
   the 1px hairline, on the image itself. (0,2,1) beats core's img rule (0,1,1). */
.rd .wp-block-media-text__media img {
	border: 1px solid var(--color-border);
	border-radius: var(--radius-card);
}

/* Text half — narrow content one step below body, fixed 18/1.65 (only the text
   half holds paragraphs; the media half is a figure). */
.rd .wp-block-media-text p {
	font-size: 18px;
	line-height: 1.65;
}

/* Flush first/last inside the text half. */
.rd .wp-block-media-text__content > :first-child { margin-top: 0; }
.rd .wp-block-media-text__content > :last-child { margin-bottom: 0; }

/* Boundary spacing — flush first/last (0,3,0); outranks the rhythm and the
   shared alignwide, so a standard OR wide media & text sits flush. */
.rd > .wp-block-media-text:first-child { margin-top: 0; }
.rd > .wp-block-media-text:last-child { margin-bottom: 0; }

/* ==========================================================================
   Post CTA — paper band one section step after the article (the article
   band's bottom padding is the gap); compact on-ink panel at the reading
   measure. No form, no form-plugin assets — the CTA links out.
   ========================================================================== */
.pcta-band {
	background: var(--color-paper);
	padding-bottom: var(--space-16);
}

.pcta {
	max-width: var(--measure-article);
	margin: 0 auto;
	background:
		radial-gradient(420px 280px at 82% 0%, rgba(11, 114, 133, 0.40), transparent 70%),
		var(--color-ink);
	border-radius: var(--radius-card);
	padding: 28px 24px;
	color: var(--color-text-on-ink);
}

/* Component heading, not a page section — no 32×3 mark */
.pcta h2 {
	font-size: var(--text-lg);
	font-weight: 700;
	line-height: 1.2;
	color: #fff;
	margin-bottom: 10px;
}

.pcta p {
	font-size: 16px;
	line-height: var(--leading-body);
	color: var(--color-text-muted-on-ink);
	max-width: 54ch;
}

.pcta .row {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--space-6);
	margin-top: var(--space-6);
}

/* Full-width tap target below 640; the petrol focus ring would vanish on
   ink, so it switches to the on-ink link color */
.pcta .btn { width: 100%; }
.pcta .btn:focus-visible { outline-color: var(--color-link-on-ink); }

/* Fallback link — its own 48px hit area (the row is 48 tall already from
   the button, so the visual position doesn't change) */
.pcta .alt {
	display: inline-flex;
	align-items: center;
	min-height: 48px;
	font-size: 15px;
	font-weight: 500;
	color: var(--color-link-on-ink);
	text-decoration: underline;
	text-underline-offset: 3px;
	text-decoration-thickness: 1.5px;
	transition: color var(--transition-fast);
}
.pcta .alt:hover { color: #fff; }
.pcta .alt:focus-visible {
	outline: 2px solid var(--color-link-on-ink);
	outline-offset: var(--focus-offset);
	border-radius: 2px;
}

/* ==========================================================================
   Related posts — surface band after the CTA; template-owned H2 with the
   level mark; one to three compact cards. Zero results = no band at all.
   ========================================================================== */
.rel-band {
	background: var(--color-surface);
	padding-top: var(--space-16);
	padding-bottom: var(--space-16);
}

.rel-band h2::before {
	content: "";
	display: block;
	width: 32px;
	height: 3px;
	background: var(--color-primary);
	border-radius: 2px;
	margin-bottom: var(--space-4);
}
.rel-band h2 {
	font-size: var(--text-lg);
	font-weight: 700;
	line-height: 1.2;
	margin-bottom: var(--space-6);
}

/* 1-up below 640, 2-up from 640, 3-up from 1024; a third card at tablet
   wraps to row 2 at the same width */
.rel-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--grid-gutter);
}

/* ---- Compact post card — the archive card anatomy (blog.css never
   co-loads, so the rules live here too, scoped to the related grid);
   compact deltas: tighter padding, 17px H3 title, no excerpt. */
.rel-grid .post-card {
	position: relative;
	display: flex;
	flex-direction: column;
	background: var(--color-paper);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-card);
	overflow: hidden;
	box-shadow: var(--shadow-card);
	transition: box-shadow var(--transition-fast), border-color var(--transition-fast);
}
.rel-grid .post-card:hover,
.rel-grid .post-card:focus-within {
	border-color: var(--color-primary);
	box-shadow: var(--shadow-card-hover);
}

/* Media: flush-top variant of .media-frame — the card owns border and
   radius, so only the bottom hairline stays */
.rel-grid .pc-media {
	aspect-ratio: 16 / 9;
	background: var(--color-surface);
	border: 0;
	border-bottom: 1px solid var(--color-border);
	border-radius: 0;
}

/* Missing featured image: the quiet slate level mark — the card fallback,
   deliberately not the single-post hero rule */
.rel-grid .pc-media--none {
	display: flex;
	align-items: center;
	justify-content: center;
}
.rel-grid .pc-media--none::before {
	content: "";
	width: 32px;
	height: 3px;
	border-radius: 2px;
	background: #AEBDC4;
}

.rel-grid .pc-body {
	display: flex;
	flex-direction: column;
	flex: 1;
	padding: 16px 20px 20px;
}

/* Category link: 48px tap target via padding compensation; z-index lifts
   it above the stretch layer */
.rel-grid .pc-cat {
	position: relative;
	z-index: 2;
	align-self: flex-start;
	font-size: 13px;
	font-weight: 600;
	color: var(--color-primary);
	text-decoration: none;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	padding: 14px 8px;
	margin: -14px -8px -4px;
	transition: color var(--transition-fast);
}
.rel-grid .pc-cat:hover {
	color: var(--color-primary-strong);
	text-decoration: underline;
	text-underline-offset: 3px;
}
.rel-grid .pc-cat:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); border-radius: 2px; }

/* Compact title — H3 under the section H2 (17px, tighter than the
   archive's 20px H2 titles) */
.rel-grid .post-card h3 {
	font-size: 17px;
	font-weight: 600;
	line-height: 1.35;
	letter-spacing: -0.01em;
	margin-bottom: 12px;
}

/* Stretched primary link: its outline is suppressed and the focus ring is
   drawn on the card box instead */
.rel-grid .pc-title { color: inherit; text-decoration: none; }
.rel-grid .pc-title::after { content: ""; position: absolute; inset: 0; }
.rel-grid .post-card:hover h3 { color: var(--color-primary-strong); }
.rel-grid .pc-title:focus-visible { outline: none; }
.rel-grid .post-card:has(.pc-title:focus-visible) { outline: var(--focus-ring); outline-offset: var(--focus-offset); }

/* ==========================================================================
   Responsive steps
   ========================================================================== */
@media (min-width: 640px) {
	.rel-grid { grid-template-columns: repeat(2, 1fr); }
	.pcta { padding: 36px 40px; }
	.pcta .btn { width: auto; }
}

@media (min-width: 768px) {
	/* Desktop type step (mobile is <768) */
	.ah-band h1 { font-size: var(--text-2xl); }
	.ah-deck { font-size: var(--text-md); }
	.rd p { font-size: var(--text-md); }
	.rd ul,
	.rd ol { font-size: var(--text-md); } /* lists track the body step */
	.rd h2 { font-size: var(--text-xl); }
	.rd h3 { font-size: var(--text-lg); }
	.rd h4 { font-size: var(--text-md); }
	/* Callout heading holds the H4 step at either level (see the panel rule) */
	.rd .callout :is(h3, h4) { font-size: var(--text-md); }
	/* Gallery fixed 2-up; 1-up is the mobile base above */
	.rd .wp-block-gallery { grid-template-columns: 1fr 1fr; }
	/* code/preformatted desktop padding; 16/20 is the mobile base above */
	.rd .wp-block-code,
	.rd .wp-block-preformatted { padding: 20px 24px; }
	/* Structural blocks — desktop step. Details: body side inset and the
	   summary's cancelling margins both step 16 → 20; open bottom inset 14 → 16. */
	.rd .wp-block-details { padding-left: 20px; padding-right: 20px; }
	.rd .wp-block-details summary { margin-left: -20px; margin-right: -20px; padding: 14px 20px; font-size: 18px; }
	.rd .wp-block-details[open] { padding-bottom: 16px; }
	.rd .callout { padding: 28px; }
	.rd .wp-block-columns { grid-template-columns: 1fr 1fr; gap: var(--space-8); }
	/* Equal halves at 768+; the right variant is one class heavier so its
	   template beats core's `.has-media-on-the-right` tie. */
	.rd .wp-block-media-text,
	.rd .wp-block-media-text.has-media-on-the-right { grid-template-columns: 1fr 1fr !important; gap: var(--space-8); }
	.rd .wp-block-media-text > .wp-block-media-text__media { grid-column: 1; grid-row: 1; }
	.rd .wp-block-media-text > .wp-block-media-text__content { grid-column: 2; grid-row: 1; }
	.rd .wp-block-media-text.has-media-on-the-right > .wp-block-media-text__media { grid-column: 2; grid-row: 1; }
	.rd .wp-block-media-text.has-media-on-the-right > .wp-block-media-text__content { grid-column: 1; grid-row: 1; }
	/* Section padding steps up (sitewide rhythm) */
	.pcta-band { padding-bottom: var(--space-24); }
	.rel-band { padding-bottom: var(--space-24); }
}

@media (min-width: 1024px) {
	.rel-grid { grid-template-columns: repeat(3, 1fr); }
}
