/*
 * dP components — the parts of the design that repeat.
 *
 * Transcribed from design-source/components/{PageHero,SectionHead,FilterPills,
 * PostRow,WorkCard,CtaBanner,ContactMethod}.dc.html and from the compositions
 * in dpaternina.dc.html. Each section names the file it comes from.
 *
 * Two inherited facts shape most of this file, and both come from ADR-0005.
 *
 * **The house style is global.** `:root :where(p)` and a bare `h2` reach every
 * paragraph and heading on the site, not only the ones inside a post. That is
 * what makes the editor canvas match the page, and it is the right way round —
 * but it means a hero heading and a section head, which are not body copy, have
 * to say so. Every such override is on a selector carrying an element name as
 * well as a class, so it beats both the element rule and core's layout rules on
 * specificity rather than on load order.
 *
 * **`--measure` is scoped to post content on purpose.** A paragraph inside a
 * template part or a pattern spans its own column; the ones here that want the
 * measure ask for it by name.
 *
 * No media queries: digest §5.2 puts every threshold on a component's own
 * width, so `PostRow`'s 560px is a container query against the list that holds
 * the rows.
 */
/* stylelint-disable no-descending-specificity -- the rule compares only the last
   compound selector of a pair, so it reports every two unrelated components that
   happen to end in `a`, `p`, `li` or one of core's class names. Every finding in
   this file is a pair of that kind — a pagination link against a section head's
   action, a post row's `li` against a filter pill's — and no two of them can
   select the same element, so there is no cascade between them to get wrong.
   The rule stays on in blocks.css, where a post's blocks really do nest. */


/* -------------------------------------------------------------------------
 * Document
 * ---------------------------------------------------------------------- */

/*
 * Selected text.
 *
 * The design declares this and the theme never carried it over:
 * `::selection { background: color-mix(in srgb, var(--dp-teal) 35%, transparent) }`
 * (dpaternina.dc.html L29). Selection therefore fell back to the user agent's
 * blue on every surface of a site that has no blue in it.
 *
 * Solid rather than the design's 35% wash, which is David's call and the more
 * robust of the two: a translucent highlight takes its contrast from whatever
 * it happens to sit on, and on this site that ranges from --bg-page through
 * --band to a card. The solid pair is legible on all of them.
 *
 * Here rather than in base.css, which is a verbatim copy of the design system's
 * base layer and is held to that byte for byte by
 * DP\Tests\Integration\TokenParityTest. A theme-owned global rule cannot live
 * in a file the theme is not allowed to edit.
 *
 * Not measured by the parity harness -- `::selection` has no baseline entry --
 * so there is nothing here for a re-import to disagree with. If it ever gains
 * one, this becomes a recorded divergence the way ADR-0022 records the bar
 * floor.
 */
::selection {
	background: var(--dp-teal);
	color: var(--dp-black);
}


/* -------------------------------------------------------------------------
 * Shared shells
 * ---------------------------------------------------------------------- */

/*
 * Full-bleed sections butt against each other.
 *
 * Core gives every child of a flow container a `margin-block-start` of the
 * block gap (--space-5, 24px). Between two paragraphs that is right; between
 * two full-width bands it is 24px of --bg-page showing through the join. It
 * read as a seam between the shipped band and Latest writing — both --band,
 * both black, with a lighter strip between them — and as an unexplained gap
 * above and below the closing CTA, which is the last thing before the footer.
 *
 * The design has no margins anywhere in this stack: every section carries its
 * own vertical padding and its neighbours' edges touch. Zeroed at the two
 * levels where a full-bleed section can sit — a direct child of
 * `.wp-site-blocks` (the chrome, the CTA band, `<main>` itself) and a band
 * inside `<main>`. Everything below that keeps its block gap.
 *
 * Each selector names an element or a second class deliberately: core's rule is
 * `:root :where(.is-layout-flow) > *`, which is one class, so a one-class rule
 * here would only win by loading later — true on the front end and false in the
 * block editor, which injects the global styles last.
 */
.wp-site-blocks > main,
.wp-site-blocks > .wp-block-template-part,
.wp-site-blocks > .dp-cta-band,
.wp-site-blocks main > .dp-band,
.wp-site-blocks main > .dp-cta-band,
.wp-site-blocks main > .dp-home-hero,
.wp-site-blocks main > .dp-hero,
.wp-site-blocks main > .dp-post-after,
.wp-site-blocks main > .dp-section {
	margin-block-start: 0;
}

.dp-section {
	max-width: var(--container-lg);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.dp-section-reading {
	max-width: var(--container-md);
}

.dp-band {
	border-top: var(--border-width) solid var(--border-subtle);
	background: var(--band);
}

.dp-band-closed {
	border-bottom: var(--border-width) solid var(--border-subtle);
}

.dp-band > .dp-section {
	padding-block: var(--section-y);
}

/* -------------------------------------------------------------------------
 * PageHero
 * ---------------------------------------------------------------------- */

.dp-hero {
	max-width: var(--container-lg);
	margin-inline: auto;
	padding-block: clamp(48px, 8vw, 80px) 40px;
	padding-inline: var(--gutter);
}

.dp-hero-reading {
	max-width: var(--container-md);
}

.dp-hero-tight {
	padding-bottom: 24px;
}

h1.dp-hero-title {
	max-width: 20ch;
	margin-block: 0 16px;
	font-family: var(--font-display);
	font-size: clamp(2.25rem, 5.5vw, 3.75rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
	text-wrap: balance;
}

p.dp-hero-deck {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
}

p.dp-hero-meta {
	margin-block: 24px 0;
	padding-bottom: 24px;
	border-bottom: var(--border-width) solid var(--border-subtle);
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* -------------------------------------------------------------------------
 * The homepage hero
 *
 * One gradient per view (digest §1); on the homepage it is spent on the orb
 * behind the heading, so nothing else on this page carries one.
 * ---------------------------------------------------------------------- */

.dp-home-hero {
	position: relative;
	isolation: isolate;

	/*
	 * The orb below is 120vw wide and centred, so it hangs 39px off each edge
	 * of a 390px screen and gives the document a horizontal scrollbar. `clip`
	 * rather than `hidden`: it contains the glow without turning the section
	 * into a scroll container, which `hidden` would, and which would break
	 * `position: sticky` for anything placed inside it later.
	 */
	overflow: clip;
	max-width: var(--container-lg);
	margin-inline: auto;
	padding-block: var(--section-y) 64px;
	padding-inline: var(--gutter);
}

.dp-home-hero::before {
	position: absolute;
	z-index: -1;
	top: -120px;
	left: 50%;
	width: min(760px, 120vw);
	height: 620px;
	border-radius: var(--radius-circle);
	background: radial-gradient(circle, color-mix(in srgb, var(--dp-teal) var(--glow-soft), transparent) 0%, transparent 62%);
	content: "";
	pointer-events: none;
	transform: translateX(-50%);
}

/*
 * The design opens the home page with the monogram — 40px, 32px of air under
 * it — and the theme drew nothing there. It is `core/site-logo` in the template
 * rather than a background here, because ADR-0011 settles that the mark is
 * content: David can change it in Site Identity without touching the theme.
 */
.dp-brand-hero.wp-block-site-logo {
	display: flex;
	align-items: center;
	margin-block-end: 32px;
}

.dp-brand-hero.wp-block-site-logo img {
	display: block;
	width: 40px;
	height: auto;
}

h1.dp-home-hero-title {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-display);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
	text-wrap: balance;
}

.dp-accent {
	color: var(--accent-text);
}

p.dp-home-hero-lead {
	max-width: var(--measure);
	margin-block: 40px 0;
	color: var(--text-primary);
	font-size: var(--fs-lg);
	line-height: var(--lh-relaxed);
}

p.dp-home-hero-note {
	max-width: var(--measure);
	margin-block: 24px 0;
	color: var(--text-secondary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
}

.dp-home-hero-actions.wp-block-buttons {
	margin-block-start: 40px;
	gap: 12px;
}

/* -------------------------------------------------------------------------
 * SectionHead
 *
 * `label` is either a kicker or a heading, never both, and the trailing slot is
 * either `meta` or an `action`, never both — the component's own note says so.
 * Two patterns ship those two shapes; the five tones are classes because the
 * hues are text-contrast corrections in settings.custom rather than palette
 * entries, so there is no `has-…-color` class to reach for and there should not
 * be one (ADR-0002 §2: a corrected text hue must not be offered as a fill).
 * ---------------------------------------------------------------------- */

.dp-section-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px 24px;
	flex-wrap: wrap;
	padding-bottom: 16px;
	border-bottom: var(--border-width) solid var(--border-subtle);
}

/*
 * The head is a flex row that WordPress still treats as flow, because the block
 * markup asks for the default layout and only this stylesheet makes it flex.
 * So core hands the second child a 24px `margin-block-start`, and in a
 * baseline-aligned row that pushes the whole line down and leaves a band of
 * dead space above the kicker. The paragraphs already zero their own margins;
 * the trailing `core/buttons` — "FULL TIMELINE →", "ALL POSTS →" — did not.
 */
.dp-section-head.wp-block-group > * {
	margin-block-start: 0;
}

h2.dp-section-head-heading {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-xl);
	font-weight: var(--fw-bold);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-wrap: balance;
}

/*
 * `SectionHead.dc.html` gives the kicker, the meta and the action a font-family,
 * a size, a tracking and a colour, and no line-height at all — so all three are
 * drawn in the browser's `normal` line box, not in `--lh-normal`, which is a
 * value this theme supplied. The element name is load-bearing: these are
 * paragraphs, and `theme.json`'s `core/paragraph` style is `:root :where(p)`,
 * which outranks a bare class (ADR-0008).
 */
p.dp-section-head-kicker,
p.dp-section-head-meta,
p.dp-section-head-action {
	margin: 0;
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

p.dp-section-head-kicker {
	color: var(--hue-teal);
}

.is-tone-pink p.dp-section-head-kicker,
p.dp-section-head-kicker.is-tone-pink {
	color: var(--hue-pink);
}

.is-tone-gold p.dp-section-head-kicker,
p.dp-section-head-kicker.is-tone-gold {
	color: var(--hue-gold);
}

.is-tone-purple p.dp-section-head-kicker,
p.dp-section-head-kicker.is-tone-purple {
	color: var(--hue-purple);
}

.is-tone-muted p.dp-section-head-kicker,
p.dp-section-head-kicker.is-tone-muted {
	color: var(--text-muted);
}

p.dp-section-head-meta {
	color: var(--text-muted);
	text-wrap: balance;
}

p.dp-section-head-action a,
.dp-section-head-action .wp-block-button__link {
	color: var(--accent-text);
	transition: color var(--dur-base) var(--ease-standard);
}

p.dp-section-head-action a:hover,
.dp-section-head-action .wp-block-button__link:hover {
	color: var(--dp-teal-600);
}

.dp-section-head-action .wp-block-button__link {
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* -------------------------------------------------------------------------
 * Badge — the coloured token above a title
 *
 * Every value below is the design system's own, from the compiled `Badge`
 * component in design-source/_ds/_ds_bundle.js — the `soft` variant, which is
 * its default and the only one the pages use:
 *
 *   padding 3px 10px · radius-pill · font-mono · fs-xs · fw-medium
 *   ls-wide · line-height 1.4 · gap 5px · nowrap
 *   background  color-mix(in srgb, <tone> 18%, transparent)
 *   border      1px solid color-mix(in srgb, <tone> 32%, transparent)
 *   color       <tone>
 *
 * Two deliberate differences from that source. The line height is the literal
 * 1.4 the component sets and there is no token for it, so it is written out
 * rather than rounded to --lh-normal. And the component's `<tone>` is the raw
 * brand hue used as text, which CLAUDE.md §5 forbids; the fills keep the raw
 * hue and the text side goes through --hue-*, which resolves to the same value
 * on the dark ground the site actually ships on.
 *
 * The tone class is added at render time by DP\Theme\Chrome\PostPresentation,
 * from `dp_tone` or, failing that, from whether the post is part of a series —
 * which is the derivation the design itself uses.
 * ---------------------------------------------------------------------- */

p.dp-badge {
	display: inline-flex;
	gap: 5px;
	align-items: center;
	margin: 0;
	padding: 3px 10px;
	border: var(--border-width) solid color-mix(in srgb, var(--hue-teal) 32%, transparent);
	border-radius: var(--radius-pill);
	background: color-mix(in srgb, var(--hue-teal) 18%, transparent);
	color: var(--hue-teal);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	line-height: 1.4;
	letter-spacing: var(--ls-wide);
	white-space: nowrap;
	text-transform: uppercase;
}

p.dp-badge:empty {
	display: none;
}

p.dp-badge.is-tone-pink {
	border-color: color-mix(in srgb, var(--hue-pink) 32%, transparent);
	background: color-mix(in srgb, var(--hue-pink) 18%, transparent);
	color: var(--hue-pink);
}

p.dp-badge.is-tone-gold {
	border-color: color-mix(in srgb, var(--hue-gold) 32%, transparent);
	background: color-mix(in srgb, var(--hue-gold) 18%, transparent);
	color: var(--hue-gold);
}

p.dp-badge.is-tone-purple {
	border-color: color-mix(in srgb, var(--hue-purple) 32%, transparent);
	background: color-mix(in srgb, var(--hue-purple) 18%, transparent);
	color: var(--hue-purple);
}

/* -------------------------------------------------------------------------
 * FilterPills
 *
 * `dpaternina/filter-pills`, in its `filter` variation. Real links to real
 * archives, which is the component's own instruction and what keeps filtering
 * working with JavaScript off (CLAUDE.md §1.7). `cat-item` and `current-cat` are
 * the class names `wp_list_categories` used to write and the block keeps, so
 * every rule below and every selector in the e2e suite still says what it said.
 * ---------------------------------------------------------------------- */

ul.dp-filter-pills {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	margin: 0;
	padding: 0;
	list-style: none;
}

ul.dp-filter-pills > li {
	display: block;
	margin: 0;
	padding: 0;
}

ul.dp-filter-pills > li::before {
	content: none;
}

/*
 * `min-height: var(--target-min)` is the design's own declaration, and it only
 * produces the 36px chip the token is named for under two conditions this
 * theme was not meeting.
 *
 * **`box-sizing: border-box`.** The design system's control sizing is
 * border-box and says so in `spacing.css`: "Button, IconButton and Input all
 * resolve to --control-h-md, so they sit flush when placed side by side" — three
 * controls with different padding only sit flush if the declared height is the
 * whole box. Under `content-box` this chip was 36 + 16 padding + 2 border = 54,
 * which is taller than the 44px primary button and inverts the hierarchy the
 * same file describes: "Primary navigation and form controls meet 44; secondary
 * controls such as filter chips meet 36."
 *
 * **`line-height: normal`.** The design declares no line-height here, so its
 * chip label sits in a `normal` line box; `theme.json`'s root is `--lh-relaxed`
 * (1.65), which is 19.8px at `--fs-xs` and pushes the content box past 36 on its
 * own. `normal` is not a value this theme picked — it is the value the design
 * renders. See the note above `.dp-tl-legend` for the rest of that family.
 */
ul.dp-filter-pills a {
	display: inline-flex;
	box-sizing: border-box;
	align-items: center;
	min-height: var(--target-min);
	padding: 8px 20px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--text-secondary);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-decoration: none;
	text-transform: uppercase;
	transition:
		color var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard),
		background var(--dur-base) var(--ease-standard);
}

ul.dp-filter-pills a:hover {
	border-color: var(--text-muted);
	color: var(--text-primary);
}

/* The current pill. `current-cat` is the class the block puts on the `<li>`;
   `aria-current` is on the anchor, and the timeline's three filters — which are
   not categories at all — carry only that. Same pill, same state, two ways of
   saying which one you are looking at. */
ul.dp-filter-pills > li.current-cat a,
ul.dp-filter-pills a[aria-current="page"] {
	border-color: var(--dp-teal);
	background: color-mix(in srgb, var(--dp-teal) 14%, transparent);
	color: var(--hue-teal);
}

/* -------------------------------------------------------------------------
 * PostRow
 *
 * digest §5.2: the breakpoint is the row's own width, 560px. A container cannot
 * query itself, so the list is the container and the rows answer.
 * ---------------------------------------------------------------------- */

.dp-rows {
	container: dp-rows / inline-size;
	display: flex;
	flex-direction: column;
	margin: 0;
	padding: 0;
	list-style: none;
}

.dp-rows > li {
	display: block;
	margin: 0;
	padding: 0;
}

.dp-rows > li::before {
	content: none;
}

.dp-row {
	position: relative;
	display: grid;
	align-items: baseline;
	gap: 28px;
	grid-template-columns: 130px minmax(0, 1fr) 130px;
	padding: 18px 8px;
	border-bottom: var(--border-width) solid var(--border-subtle);

	/* `padding-left`, not `padding` — the design transitions only the axis its
	   hover moves (PostRow.logic.js, `hoverStyle = 'padding-left: 10px'`).
	   Transitioning the shorthand animated the 18px vertical padding in from
	   zero whenever the stylesheet landed late, growing every row ~11px after
	   paint: layout shift for a reader on a slow connection, and the reason the
	   pager slid out from under Playwright's cursor. */
	transition: background var(--dur-base) var(--ease-standard), padding-left var(--dur-base) var(--ease-standard);
}

.dp-rows > li:hover .dp-row {
	background: color-mix(in srgb, var(--text-primary) 3%, transparent);
}

.dp-row-compact {
	gap: 24px;
	grid-template-columns: minmax(0, 1fr) auto;
	padding: 18px 0;
	border-top: var(--border-width) solid var(--border-subtle);
	border-bottom: 0;
}

.dp-rows > li:hover .dp-row-compact {
	background: none;
	padding-left: 10px;
}

.dp-row .dp-row-date,
.dp-row .dp-row-cat {
	margin: 0;
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: var(--lh-normal);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-row-date {
	color: var(--text-muted);
}

/* "Category token is always --hue-pink; date is always --text-muted." */
.dp-row-cat,
.dp-row-cat a {
	color: var(--hue-pink);
	text-decoration: none;
}

.dp-row-body {
	min-width: 0;
}

h3.dp-row-title {
	margin-block: 0 6px;
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-wrap: pretty;
}

h3.dp-row-title a {
	color: inherit;
	text-decoration: none;
}

/*
 * `core/post-excerpt` renders a `<div>` wrapping a `<p>`, so a rule written
 * against `p.dp-row-excerpt` matched nothing at all: every list row was drawing
 * its excerpt at `--fs-base` in core's flow-layout 24px block gap, with no
 * measure on it. The design's inversion — the *compact* variant takes the
 * larger `--fs-base` and the list variant the smaller `--fs-sm`, which
 * `PostRow.logic.js` flags as "not a typo in the export" — could not be seen
 * because both were rendering at the same size.
 */
.dp-row .dp-row-excerpt {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-row .dp-row-excerpt p {
	margin: 0;
	color: inherit;
	font-size: inherit;
	line-height: inherit;
}

.dp-row-compact .dp-row-excerpt,
.dp-row-compact .dp-row-excerpt p {
	font-size: var(--fs-base);
}

/* The trailing column of the list variant is the category alone; of the compact
   variant it is the category over the date. Both are the same two blocks. */
.dp-row-aside {
	display: flex;
	flex-direction: column;
	gap: 8px;
	text-align: right;
	white-space: nowrap;
}

.dp-row:not(.dp-row-compact) .dp-row-aside .dp-row-date {
	display: none;
}

@container dp-rows (width < 560px) {

	.dp-row,
	.dp-row-compact {
		gap: 10px;
		grid-template-columns: minmax(0, 1fr);
	}

	/* Below the threshold the date and the category share one mono bar above
	   the title, which is the whole point of the switch. */
	.dp-row-aside {
		display: flex;
		align-items: baseline;
		justify-content: space-between;
		gap: 16px;
		flex-direction: row-reverse;
		order: -1;
		text-align: left;
	}

	.dp-row:not(.dp-row-compact) .dp-row-aside .dp-row-date {
		display: block;
	}

	.dp-row > .dp-row-date {
		display: none;
	}
}

/* -------------------------------------------------------------------------
 * WorkCard
 * ---------------------------------------------------------------------- */

.dp-cards {
	display: grid;
	gap: 20px;
	grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

.dp-cards > li {
	display: block;
	margin: 0;
	padding: 0;
}

.dp-cards > li::before {
	content: none;
}

.dp-card {
	position: relative;
	display: flex;
	overflow: hidden;
	box-sizing: border-box;
	height: 100%;
	flex-direction: column;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--bg-surface);
	transition: transform var(--dur-base) var(--ease-standard), border-color var(--dur-base) var(--ease-standard);
}

.dp-card:hover {
	border-color: var(--accent-text);
	transform: translateY(-3px);
}

.dp-card-shot {
	margin: 0;
	background: var(--band);
}

.dp-card-shot img {
	display: block;
	width: 100%;
	border: 0;
	border-radius: 0;
	aspect-ratio: 16 / 10;
	object-fit: cover;
}

/* The card's own parts carry the design's `gap: 10px`, so core's block gap on
   top of it made every card 48px taller than the design draws. */
.dp-card.wp-block-group > *,
.dp-card-body.wp-block-group > * {
	margin-block-start: 0;
}

.dp-card-body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: 10px;
	padding: 24px;
}

.dp-card-meta.wp-block-group {
	display: flex;
	gap: 4px 12px;
	flex-wrap: wrap;
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);

	/* The design's meta row declares no leading; see p.dp-card-year below. */
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/*
 * `.dp-card-meta` already carries the design's mono caps `--fs-xs`, and on a
 * container of `<span>`s that would be the whole story. These are paragraphs,
 * and `theme.json`'s `core/paragraph` style is `:root :where(p)` — one class,
 * which outranks a container's inherited value on every property it names.
 * It names font-size, line-height and colour, so all three are restated here
 * against the element. ADR-0008; the same shape as `p.dp-badge`.
 */
p.dp-card-year,
p.dp-card-org {
	font-size: var(--fs-xs);

	/* The design's meta row declares no line-height, so it draws in `normal`;
	   `--lh-relaxed` is the root value these paragraphs inherited, and it made
	   the row 20px tall where the design draws 16. Same family as the section
	   head's meta and the timeline's labels. */
	line-height: normal;
}

p.dp-card-year {
	color: var(--hue-gold);
}

p.dp-card-org {
	color: var(--text-muted);
}

h3.dp-card-title {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-md);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-wrap: pretty;
}

h3.dp-card-title a {
	color: inherit;
	text-decoration: none;
}

/* "Clicking a card opens the matching entry on the timeline below" — the
   design's own note on WorkCard, where the whole card is the control. Here the
   title is the link and this pseudo-element is the rest of the card, which is
   what gives a mouse the target the design draws and a keyboard exactly one tab
   stop instead of a card's worth of them. The anchor and its derived href are
   DP\Theme\Blocks\WorkCardTitle's. */
h3.dp-card-title a.dp-card-open::after {
	position: absolute;
	inset: 0;
	content: "";
}

p.dp-card-line {
	flex: 1;
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
}

/* -------------------------------------------------------------------------
 * CtaBanner and ContactMethod
 * ---------------------------------------------------------------------- */

.dp-cta-banner {
	display: flex;
	align-items: center;
	gap: 24px;
	flex-wrap: wrap;
	padding: clamp(24px, 4vw, 32px);
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: transparent;
}

.dp-cta-banner-filled {
	background: var(--bg-surface);
}

.dp-cta-banner-body {
	flex: 1 1 260px;
	min-width: 0;
}

p.dp-cta-banner-title {
	margin-block: 0 8px;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: var(--fs-base);
	letter-spacing: var(--ls-tight);
	text-wrap: pretty;
}

p.dp-cta-banner-line {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
}

.dp-contact-method .wp-block-button__link {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px 16px;
	flex-wrap: wrap;
	box-sizing: border-box;
	width: 100%;
	min-height: var(--target-comfortable);
	height: auto;
	padding: 16px 24px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--bg-surface);
	box-shadow: none;
	color: var(--text-primary);
	font-family: var(--font-body);
	font-size: var(--fs-sm);
	font-weight: var(--fw-regular);
	letter-spacing: var(--ls-normal);
	white-space: normal;
	overflow-wrap: anywhere;
	transition: border-color var(--dur-base) var(--ease-standard);
}

/* The base pill turns its label `--accent-contrast` on hover, which is the near
   black that reads against a filled teal button and against nothing else. This
   row is a surface, not a pill, so it has to say the resting colour again —
   without this line the address goes black on `--bg-surface` and disappears. */
.dp-contact-method .wp-block-button__link:hover {
	border-color: var(--accent-text);
	background: var(--bg-surface);
	box-shadow: none;
	color: var(--text-primary);
}

/* -------------------------------------------------------------------------
 * The closing CTA band
 *
 * One gradient per view is already spent on most pages, so this orb is a glow
 * rather than a gradient fill: two radial stops of one hue, which the design's
 * own tokens (--glow-core, --glow-mid, --glow-ring) exist for.
 * ---------------------------------------------------------------------- */

.dp-cta-band {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	max-width: none;
	padding-block: clamp(96px, 14vw, 160px) clamp(104px, 15vw, 168px);
	padding-inline: var(--gutter);
	text-align: center;
}

.dp-cta-band::before,
.dp-cta-band::after {
	position: absolute;
	z-index: -1;
	top: 52%;
	left: 50%;
	border-radius: var(--radius-circle);
	content: "";
	pointer-events: none;
	transform: translate(-50%, -50%);
}

.dp-cta-band::before {
	width: min(760px, 120vw);
	aspect-ratio: 1;
	background: radial-gradient(
		circle at 50% 50%,
		color-mix(in srgb, var(--dp-teal) var(--glow-core), transparent) 0%,
		color-mix(in srgb, var(--dp-teal) var(--glow-mid), transparent) 45%,
		transparent 70%
	);
	filter: blur(30px);
	animation: dp-orb 14s var(--ease-standard) infinite;
}

.dp-cta-band::after {
	width: min(300px, 62vw);
	aspect-ratio: 1;
	border: var(--border-width) solid color-mix(in srgb, var(--dp-teal) var(--glow-ring), transparent);
}

@keyframes dp-orb {

	0%,
	100% {
		transform: translate(-50%, -50%) scale(1);
	}

	50% {
		transform: translate(-50%, -50%) scale(1.08);
	}
}

/* The design's row is `gap: 12px`; core's block gap makes it 24 in both
   contexts, and would show the moment a second button is added. */
.dp-cta-band .wp-block-buttons {
	gap: 12px;
}

h2.dp-cta-band-title {
	margin-block: 0 16px;
	font-family: var(--font-display);
	font-size: clamp(2.25rem, 6vw, 3.75rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
}

p.dp-cta-band-line {
	max-width: var(--measure);
	margin-block: 0 36px;
	margin-inline: auto;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

/* -------------------------------------------------------------------------
 * The homepage bands
 * ---------------------------------------------------------------------- */

/*
 * The design lays the bento out on six columns and gives each tile a span —
 * 6 for the agency, 3 and 3, then 4 and 2 — collapsing to one column when the
 * strip is narrow. Six named spans need a real grid rather than `auto-fit`, and
 * the collapse is a container query on the section, so the section declares
 * itself a container and the tiles answer.
 */
.dp-right-now {
	container: dp-strip / inline-size;

	/*
	 * The design's own `padding: 24px var(--gutter) var(--section-y)`. There
	 * was none: the top read correctly only because core's 24px block gap
	 * happened to be the same number, and the bottom was simply missing, so
	 * the record strip ran straight into the border of the band below it.
	 * `.dp-section` above supplies the width and the inline padding; the
	 * block padding is per-section and belongs here.
	 */
	padding-block: 24px var(--section-y);
}

.dp-bento {
	display: grid;
	gap: 16px;
	grid-template-columns: repeat(6, minmax(0, 1fr));
}

/*
 * Two classes, and both of them earn it.
 *
 * The **margins** were the design's on the site and core's in the canvas:
 * `:root :where(.is-layout-flow) > *` sets `margin-block-end: 0` at exactly the
 * specificity of a bare `.dp-bento`, so the 40px under the bento survived on
 * the front end and vanished in the editor.
 *
 * The **tiles** are worse than that, because it is the same rule in the other
 * direction: every tile after the first got a 24px `margin-block-start` from
 * the block gap, on top of the grid's own 16px `gap`. That is a 40px row gap
 * where the design has 16 — the "cards spacing is off by default" David
 * reported, and it was wrong in both contexts rather than only one.
 */
.dp-bento.wp-block-group {
	margin-block: 24px 40px;
}

.dp-bento.wp-block-group > * {
	margin-block-start: 0;
}

.dp-bento > .dp-tile {
	grid-column: span 3;
}

.dp-bento > .dp-tile-wide {
	grid-column: span 6;
}

.dp-bento > .dp-tile-cloud {
	grid-column: span 4;
}

.dp-bento > .dp-tile-aside {
	grid-column: span 2;
}

@container dp-strip (width < 720px) {

	.dp-bento {
		gap: 12px;
		grid-template-columns: minmax(0, 1fr);
	}

	.dp-bento > .dp-tile,
	.dp-bento > .dp-tile-wide,
	.dp-bento > .dp-tile-cloud,
	.dp-bento > .dp-tile-aside {
		grid-column: auto;
	}

	.dp-bento > .dp-tile {
		padding: 20px;
	}

	.dp-bento > .dp-tile-wide {
		padding: 24px;
	}
}

.dp-tile {
	display: flex;
	flex-direction: column;
	padding: 24px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--band);
}

.dp-tile-wide {
	padding: 32px;
	background: var(--bg-surface);
	transition: transform var(--dur-base) var(--ease-standard), box-shadow var(--dur-base) var(--ease-standard);
}

.dp-tile-wide:hover {
	box-shadow: var(--shadow-lg);
	transform: translateY(-3px);
}

/*
 * Body copy in a tile, and only body copy. `.dp-tile p` used to catch the
 * mono caps labels too: at one class and one element it outranks `.dp-label`,
 * so every label in the RIGHT NOW bento came out at 16px in --text-secondary
 * instead of 12px in its own tone. The exclusions are what keep a named
 * component's own type from being overwritten by the box it happens to sit in.
 *
 * `:where()` wraps the `:not()` so the rule keeps the specificity it had. A
 * bare `:not(.a):not(.b)` counts both classes and would start outranking
 * `.dp-tile p.dp-tile-quiet` below, which is the same bug one level down.
 */
.dp-tile p:where(:not(.dp-label, .dp-badge)) {
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

.dp-tile p + p {
	margin-block-start: 12px;
}

.dp-tile p.dp-label {
	margin-block-end: 16px;
}

/* A label that closes a tile — the Fanxie Cloud stack line — sets `margin: 0`
   in the design. Without this it leaves 16px of dead space under the card. */
.dp-tile p.dp-label:last-child {
	margin-block-end: 0;
}

/* `tileOff` in the design sets 12px under its label, not the 16 the rest use. */
.dp-tile-aside p.dp-label {
	margin-block-end: 12px;
}

.dp-tile p.dp-tile-quiet {
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

h3.dp-tile-title {
	margin-block: 0 8px;
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	letter-spacing: var(--ls-tight);
}

.dp-tile-wide h3.dp-tile-title {
	font-size: var(--fs-xl);
	margin-block-end: 12px;
}

.dp-tile .wp-block-buttons {
	margin-block-start: 20px;
}

/*
 * A label's colour is its role. The design sets --text-muted for quiet
 * metadata and one of --hue-* / --accent-text where the label means something
 * — "LIVE ON THE APP STORE" is gold, "WAITLIST OPEN" is the accent, the Fanxie
 * Cloud stack line is purple — so the tone rides on the label rather than
 * standing alone. Scoping it to `.dp-label` is also what puts it at two classes,
 * which is what it takes to beat `:root :where(p) { color: … }` in the block
 * editor, where the global styles are injected after the theme's (see the
 * `.dp-label` comment in chrome.css).
 */
.dp-label.dp-tone-teal {
	color: var(--hue-teal);
}

.dp-label.dp-tone-pink {
	color: var(--hue-pink);
}

.dp-label.dp-tone-gold {
	color: var(--hue-gold);
}

.dp-label.dp-tone-purple {
	color: var(--hue-purple);
}

.dp-label.dp-tone-accent {
	color: var(--accent-text);
}

/*
 * The design puts 20px between "THE RECORD" and the strip. It cannot go on
 * `.dp-record`: the post template is the first child of the query block, and
 * core zeroes a first child's `margin-block-start` at two classes, so the rule
 * that used to be here was dead and the gap was core's 24px block gap on the
 * query instead. The query is the element with a margin, so the margin is on
 * the query — scoped, because the same block on the work page wants 32.
 */
.dp-right-now .dp-query {
	margin-block-start: 20px;
}

/*
 * `recordGridStyle`: three fixed columns, not `auto-fit`. The design says why in
 * its own comment — "6 record cells: 3 / 2 / 1 all divide evenly, so no cell is
 * ever orphaned on its own row" — and `auto-fit` cannot promise that, because
 * the column count falls out of the width rather than being chosen. With
 * `minmax(min(200px, 100%), 1fr)` and three roles the strip was drawing two
 * 518px columns and two empty tracks.
 */
.dp-record {
	display: grid;
	gap: 24px 20px;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

@container dp-strip (width < 700px) {

	.dp-record {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@container dp-strip (width < 420px) {

	.dp-record {
		grid-template-columns: minmax(0, 1fr);
	}
}

/*
 * The quiet strip of past roles. `cellStyle` hangs the divider off the cell's
 * position in its row rather than its index in the list — "so the cell at the
 * right edge of any row never dangles a border into the gutter" — which is
 * `:nth-child(3n)` here, and the column count is the same three the grid takes.
 */
.dp-record > li {
	display: block;
	margin: 0;
	padding-right: 20px;
	border-right: var(--border-width) solid var(--border-subtle);
}

.dp-record > li:nth-child(3n),
.dp-record > li:last-child {
	border-right: 0;
}

@container dp-strip (width < 700px) {

	.dp-record > li:nth-child(3n) {
		border-right: var(--border-width) solid var(--border-subtle);
	}

	.dp-record > li:nth-child(2n),
	.dp-record > li:last-child {
		border-right: 0;
	}
}

@container dp-strip (width < 420px) {

	.dp-record > li {
		border-right: 0;
	}
}

.dp-record > li::before {
	content: none;
}

h3.dp-record-org {
	margin-block: 0 8px;
	min-height: 40px;
	color: var(--text-primary);
	font-family: var(--font-body);
	font-size: var(--fs-sm);
	font-weight: var(--fw-semibold);
	line-height: normal;
	letter-spacing: var(--ls-normal);
	text-wrap: pretty;
}

/* `line-height: normal` for the reason the chart's label family has it: the
   design declares a size here and no leading, and `theme.json` declares a root
   one that every one of these inherited. */
.dp-record p {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-record p.dp-record-role {
	min-height: 17px;
}

/* Two classes and an element: `.dp-record p` above zeroes the margin at one
   class and one element, so a bare `.dp-record-years` lost its 4px to it. */
.dp-record p.dp-record-years {
	margin-block-start: 4px;
}

.dp-shipped h2 {
	margin-block: 0 16px;
	font-size: clamp(2rem, 4.5vw, 3rem);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
}

.dp-shipped > p {
	max-width: var(--measure);
	margin-block: 0 48px;
	color: var(--text-secondary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
}

/*
 * The design stacks these in a column with `gap: 0`: each item carries a
 * `border-top` and 32px of its own padding, and the borders are what separate
 * them. Core's block gap put 24px of ground between every pair, so the rules
 * floated instead of dividing.
 */
.dp-shipped.wp-block-group > .dp-shipped-item {
	margin-block-start: 0;
}

.dp-shipped-item {
	padding-block: 32px;
	border-top: var(--border-width) solid var(--border-subtle);
}

.dp-shipped-item:last-child {
	border-bottom: var(--border-width) solid var(--border-subtle);
}

.dp-shipped-item h3 {
	margin-block: 0 16px;
	font-family: var(--font-display);
	font-size: var(--fs-xl);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-display);
}

/*
 * As with `.dp-tile p` above: the badges and the trailing stack label are
 * paragraphs, they carry their own type, and a container rule at the same
 * specificity was quietly replacing it. The badges came out 16px and grey; the
 * stack line came out 16px sentence-case instead of 12px mono caps. `:where()`
 * keeps the rule at the specificity it had, for the reason given above.
 */
.dp-shipped-item p:where(:not(.dp-label, .dp-badge)) {
	max-width: var(--measure);
	margin-block: 0 16px;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

.dp-shipped-item .dp-label {
	margin-block-end: 0;
}

.dp-badges.wp-block-group {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
	margin-block-end: 16px;
}

.dp-latest.wp-block-group {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px 24px;
	flex-wrap: wrap;
	margin-block-end: 40px;
}

.dp-latest.wp-block-group > * {
	margin-block-start: 0;
}

.dp-latest h2 {
	margin: 0;
	font-size: clamp(2rem, 4.5vw, 3rem);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
}

/* -------------------------------------------------------------------------
 * The post
 * ---------------------------------------------------------------------- */

.dp-post {
	max-width: var(--container-md);
	margin-inline: auto;
	padding-block: 72px 48px;
	padding-inline: var(--gutter);
}

.dp-post-back .wp-block-button__link {
	margin-block-end: 32px;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

h1.dp-post-title {
	margin-block: 24px 16px;
	font-family: var(--font-display);
	font-size: clamp(2rem, 4.5vw, 3rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-display);
}

.dp-post-byline {
	display: flex;
	align-items: center;
	gap: 8px 16px;
	flex-wrap: wrap;
	padding-bottom: 32px;
	border-bottom: var(--border-width) solid var(--border-subtle);
	margin-block-start: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* The 26px picture the design puts in front of the name. The design drew the
   monogram there and this used to hardcode it, which made the byline of every
   post say the same thing whoever wrote it. It is `core/avatar` now: the
   author's own picture, out of their profile, changed in wp-admin rather than
   here. */
.dp-post-byline .wp-block-avatar {
	flex: none;
	line-height: 0;
}

.dp-post-byline .wp-block-avatar img {
	display: block;
	width: 26px;
	height: 26px;
	border-radius: var(--radius-circle);
	object-fit: cover;
}

.dp-post-byline p,
.dp-post-byline .wp-block-post-date,
.dp-post-byline .wp-block-post-author-name {
	margin: 0;
	color: inherit;
	font-family: inherit;
	font-size: inherit;
	line-height: inherit;
	letter-spacing: inherit;
}

/*
 * The design writes the two middle dots as their own spans, between flex items
 * with a 16px gap on each side. Generated content reproduces that exactly: the
 * dot sits inside the element that follows it, the row's gap supplies the space
 * before it, and this supplies the space after. A separator is decoration, so
 * it is content rather than markup — the same call the breadcrumb's slash makes.
 */
.dp-post-byline > *:not(:first-child)::before {
	padding-inline-end: 16px;
	content: "·";
}

/* The picture is a flex item like the rest, so the rule above would put a dot
   between it and the name. It is not one of the things the dots separate. */
.dp-post-byline .wp-block-avatar + *::before {
	padding-inline-end: 0;
	content: none;
}

/* A read time nobody computed leaves an empty paragraph, and an empty paragraph
   in this row would be a separator with nothing after it. */
.dp-post-byline p:empty {
	display: none;
}

.dp-post-byline a {
	color: inherit;
	text-decoration: none;
}

/*
 * The lead image is a figure with two children, and the second is the caption.
 * The design's inner box carries the border, the radius and the surface behind
 * a picture that has not loaded; here the `<img>` is that box, because
 * `core/post-featured-image` renders the figure and the image and nothing in
 * between. `object-fit` is what keeps the 16/9 crop honest once it has.
 */
figure.dp-post-lead-image {
	display: flex;
	flex-direction: column;
	gap: 12px;
	margin-block: 32px 0;
}

figure.dp-post-lead-image img {
	overflow: hidden;
	width: 100%;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-md);
	background: var(--bg-surface);
	aspect-ratio: 16 / 9;
	object-fit: cover;
}

figcaption.dp-post-lead-caption {
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/*
 * The standfirst, which is the post's own first paragraph.
 *
 * It used to be a bound paragraph above `core/post-content`, fed by a `dp_lead`
 * meta field with no editor control — so on every post David wrote it was an
 * empty element the rule above had to hide. The design's standfirst is simply the
 * opening paragraph set larger, so that is what this styles: position, not a
 * block style variation, which keeps `CoreStylesTest`'s promise that the house
 * blocks offer no variations and leaves David nothing to remember to apply.
 *
 * Three selectors' worth of specificity, deliberately. Core's
 * `:root :where(.is-layout-flow) > :first-child` is two units (ADR-0008), and a
 * bare `.dp-post-standfirst` would have lost to it in the canvas and won on the
 * page. `.dp-post` scopes it to a post: `core/post-content` also renders a page,
 * whose deck is a separate element above the body.
 *
 * A post that opens on a heading or an image has no first paragraph, nothing
 * matches, and nothing is styled — which is the right answer, not a gap.
 */
.dp-post .wp-block-post-content > p:first-child {
	max-width: var(--measure);
	color: var(--text-primary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
}

/*
 * Core hands every child of a flow-layout `core/group` a `margin-block-start`
 * of the global block gap, and a group this stylesheet turns into a flex row or
 * a grid keeps those margins on top of its own `gap`. Phase 7b found eighteen
 * elements spaced that way on the home page alone; these are the compositions
 * this phase adds, plus the four it inherited that already had the fault.
 *
 * The rule carries `.wp-block-group` as well as the component class so it beats
 * core's layout rule on specificity rather than on load order — the hazard
 * ADR-0008 describes, which differs between the front end and the canvas.
 */
.dp-post-after.wp-block-group > *,
.dp-post-byline.wp-block-group > *,
.dp-featured-panel.wp-block-group > *,
.dp-row.wp-block-group > *,
.dp-empty.wp-block-group > *,
.dp-blog-filter.wp-block-group > *,
.dp-crumbs.wp-block-group > *,
.dp-pagination.wp-block-dpaternina-page-state > *,
.dp-at-end.wp-block-dpaternina-page-state > *,
.dp-archive-outro.wp-block-group > *,
.dp-series-outro.wp-block-group > *,
.dp-series-footer-inner.wp-block-group > *,
.dp-series-footer-head.wp-block-group > *,
.dp-series-hero-badges.wp-block-group > *,
.dp-keep-reading.wp-block-group > *,
.dp-related-card.wp-block-group > *,
.dp-part-row.wp-block-group > *,
.dp-part-body.wp-block-group > * {
	margin-block-start: 0;
}

.dp-post-after {
	display: flex;
	flex-direction: column;
	gap: 24px;
	max-width: var(--container-md);
	margin-inline: auto;
	padding-block: 24px 120px;
	padding-inline: var(--gutter);
}

/* The series footer. One gradient per view, and on a post this is where it is
   spent. `:has()` keeps the frame off a post that is in no series: the two
   navigation links and the term list all render as nothing, and an empty
   gradient box would be the only thing left. */
.dp-series-footer {
	padding: 2px;
	border-radius: var(--radius-lg);
	background: var(--dp-gradient-warm);
}

.dp-series-footer:not(:has(.wp-block-post-terms a)) {
	display: none;
}

.dp-series-footer-inner {
	display: flex;
	flex-direction: column;
	gap: 24px;
	padding: 28px;
	border-radius: var(--radius-md);
	background: var(--bg-surface);
}

/*
 * The head of the frame: a pink kicker over the series title on the left, and
 * "ALL PARTS →" on the right. The build used to draw the term list *as* the
 * title in pink, which collapsed two of the design's three elements into one and
 * gave the title the kicker's colour.
 */
.dp-series-footer-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px 20px;
	flex-wrap: wrap;
}

p.dp-series-footer-kicker {
	margin-block: 0 8px;
	color: var(--hue-pink);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-series-footer .dp-series-footer-title {
	margin: 0;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
}

.dp-series-footer .dp-series-footer-title a {
	color: inherit;
	text-decoration: none;
}

.dp-series-footer-action .wp-block-button__link {
	color: var(--accent-text);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
	white-space: nowrap;
}

.dp-part-nav {
	display: grid;
	gap: 12px;
	grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}

.dp-part-nav .wp-block-post-navigation-link,
.dp-post-nav .wp-block-post-navigation-link {
	margin: 0;
	padding: 20px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-md);
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
	transition: border-color var(--dur-base) var(--ease-standard), transform var(--dur-base) var(--ease-standard);
}

.dp-part-nav .wp-block-post-navigation-link:hover {
	border-color: var(--dp-pink);
}

.dp-post-nav {
	display: grid;
	gap: 16px;
	grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}

.dp-post-nav .wp-block-post-navigation-link {
	padding: 24px;
	border-radius: var(--radius-lg);
}

.dp-post-nav .wp-block-post-navigation-link:hover {
	border-color: var(--accent-text);
	transform: translateY(-3px);
}

.dp-part-nav .post-navigation-link-next,
.dp-post-nav .post-navigation-link-next {
	text-align: right;
}

/*
 * Core renders the label and the title as two inline runs; the design draws them
 * as two blocks with a gap between. The gap differs by card — 10px on a series
 * part, 12px on a neighbouring post — which is the design's, not a rounding.
 *
 * `arrow: "none"` on both blocks is load-bearing. Core builds the label into its
 * `$format` and then *overwrites that format* when an arrow is asked for, so a
 * block with `label` and `arrow` and `showTitle` renders the arrow and silently
 * drops the label. The arrows are therefore part of the label text, which is
 * also where the design puts them.
 */
/* stylelint-disable selector-class-pattern -- `post-navigation-link__label` is
   core's own BEM class, written by core/post-navigation-link's render callback,
   and is no more ours to rename than `wp-block-button__link`. */
.dp-part-nav .post-navigation-link__label,
.dp-post-nav .post-navigation-link__label {
	display: block;
	margin-block-end: 10px;
}

.dp-post-nav .post-navigation-link__label {
	margin-block-end: 12px;
}
/* stylelint-enable selector-class-pattern */

.dp-part-nav a,
.dp-post-nav a {
	display: block;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: var(--fs-base);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-decoration: none;
	text-transform: none;
}

/* A part card with nothing on that side of it renders as an empty bordered box
   otherwise; the design draws the card only when there is a part to point at. */
.dp-part-nav .wp-block-post-navigation-link:not(:has(a)),
.dp-post-nav .wp-block-post-navigation-link:not(:has(a)) {
	display: none;
}

/* -------------------------------------------------------------------------
 * "Keep reading" — the three cards under a post
 *
 * The loop behind them is `dpLoop: related` in DP\Theme\Query\QueryLoops: the
 * same category first, then whatever is newest, never the post you are on,
 * which is the design's own rule stated in its own comment.
 * ---------------------------------------------------------------------- */

.dp-keep-reading {
	display: flex;
	flex-direction: column;
	gap: 20px;
	padding-block-start: 8px;
}

.dp-related {
	display: grid;
	gap: 12px;
	grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

.dp-related > li {
	display: flex;
	margin: 0;
	padding: 0;
}

.dp-related > li::before {
	content: none;
}

.dp-related-card {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	gap: 10px;
	padding: 22px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--band);
	transition:
		transform var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard);
}

.dp-related > li:hover .dp-related-card {
	border-color: var(--accent-text);
	transform: translateY(-3px);
}

p.dp-related-kicker,
p.dp-related-read {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* The read time is pushed to the bottom of the card, so three cards of
   different title lengths still line their last rows up. */
p.dp-related-read {
	margin-block-start: auto;
}

p.dp-related-title {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-base);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-wrap: pretty;
}

p.dp-related-title a {
	color: inherit;
	text-decoration: none;
}

/* -------------------------------------------------------------------------
 * Archives
 * ---------------------------------------------------------------------- */

.dp-archive-head {
	max-width: var(--container-lg);
	margin-inline: auto;
	padding-block: 72px 32px;
	padding-inline: var(--gutter);
}

h1.dp-archive-title {
	max-width: 24ch;
	margin-block: 0 20px;
	font-family: var(--font-display);
	font-size: clamp(2.25rem, 6vw, 3.75rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
	text-wrap: balance;
}

p.dp-archive-deck,
.dp-archive-head .wp-block-term-description p {
	max-width: var(--measure);
	margin-block: 0 20px;
	color: var(--text-secondary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
}

/* The breadcrumb: an accent-text link, a slash nobody reads out, and the word
   for what kind of archive this is. The slash is generated content because the
   design marks its own `aria-hidden`, and a separator that exists only to be
   looked at is exactly what generated content is for. */
.dp-crumbs {
	display: flex;
	align-items: center;
	gap: 8px 12px;
	flex-wrap: wrap;
	margin-block-end: 24px;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-crumbs .wp-block-button__link {
	color: var(--accent-text);
	font-family: inherit;
	font-size: inherit;
	line-height: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
}

p.dp-crumb-here {
	margin: 0;
	color: inherit;
	font-family: inherit;
	font-size: inherit;
	line-height: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
}

p.dp-crumb-here::before {
	padding-inline-end: 12px;
	content: "/";
}

p.dp-archive-count {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-archive {
	padding-bottom: 64px;
}

/* The archive's list is closed at the top as well as the bottom; the index's is
   not, because the pill row already rules it off. Same rows, one extra line. */
.dp-archive .dp-rows-ruled {
	border-top: var(--border-width) solid var(--border-subtle);
}

/* An archive with nothing in it is one quiet line here, not the index's panel.
   Both are the design's; they are different states of different views. */
p.dp-archive-empty {
	margin: 0;
	padding-block: 48px;
	color: var(--text-muted);
	font-size: var(--fs-base);
}

.dp-archive-outro,
.dp-series-outro {
	display: flex;
	align-items: center;
	gap: 16px 24px;
	flex-wrap: wrap;
	border-top: var(--border-width) solid var(--border-subtle);
}

.dp-archive-outro {
	justify-content: space-between;
	margin-block-start: 32px;
	padding-block-start: 24px;
}

.dp-series-outro {
	margin-block-start: 36px;
	padding-block-start: 24px;
}

p.dp-archive-outro-line {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

/* "Other categories" — the design filters the current term out of the list it
   builds. `dpaternina/filter-pills` marks the current term instead of dropping
   it, so it is taken out here. Marking and hiding rather than omitting keeps one
   render path for both variations, and keeps the mark available to the filter
   row, where the same term is the one that has to look selected. */
.dp-band.dp-category-band > .dp-section {
	padding-block: var(--section-y-sm) 120px;
}

h2.dp-category-band-title {
	margin-block: 0 20px;
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	font-weight: var(--fw-bold);
	letter-spacing: var(--ls-tight);
}

ul.dp-category-pills {
	display: flex;
	gap: 10px;
	flex-wrap: wrap;
	margin: 0;
	padding: 0;
	list-style: none;
}

ul.dp-category-pills > li {
	display: block;
	margin: 0;
	padding: 0;
}

ul.dp-category-pills > li::before {
	content: none;
}

ul.dp-category-pills > li.current-cat {
	display: none;
}

ul.dp-category-pills a {
	display: inline-flex;
	box-sizing: border-box;
	align-items: center;
	gap: 10px;
	min-height: var(--target-min);
	padding: 0 16px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-pill);
	background: var(--bg-surface);
	color: var(--text-primary);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-decoration: none;
	text-transform: uppercase;
	transition:
		color var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard),
		background var(--dur-base) var(--ease-standard);
}

ul.dp-category-pills a:hover {
	border-color: var(--text-muted);
}

/* The count is `--text-muted` where the name is `--text-primary`, which needs
   an element of its own. `core/categories` wrote it as a bare text node after
   the anchor and this theme moved it with a regular expression; the block writes
   the `<span>` in the first place. */
ul.dp-category-pills .dp-cat-count {
	color: var(--text-muted);
}

.dp-pagination {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px 24px;
	flex-wrap: wrap;
	margin-block-start: 40px;
	padding-block-start: 24px;
	border-top: var(--border-width) solid var(--border-subtle);
}

/* The mono range on the left — "1–10 OF 24 POSTS" — bound to
   DP\Theme\Query\ArchiveFacts, because a count is not a field on anything and
   no core block prints one. */
p.dp-pagination-range {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* Two classes, because `core/query-pagination`'s flex layout prints its own
   one-class rule into the head and the two would otherwise be settled by load
   order — the hazard ADR-0008 and the Phase 7.2 note both describe. */
.dp-pagination .dp-pagination-steps,
.dp-pagination .wp-block-query-pagination-numbers {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
}

/*
 * `pageStyle` and `stepStyle`, from the design's own script block. They differ
 * in three declarations — the numbered pill is square-ish and carries a current
 * state, the step is wider and carries a disabled one — so everything they share
 * is written once.
 *
 * `box-sizing` is the property the design never declares and always relies on:
 * it draws both of these as `<button>` elements, which the user-agent stylesheet
 * gives `border-box`. Rendered as anchors at `content-box` they measure 36 plus
 * padding plus border, which is what David saw on the work page's chips and
 * called huge. FilterPills.logic.js settles it in prose; this is the same fix in
 * a second place.
 */
.dp-pagination .page-numbers,
.dp-pagination .wp-block-query-pagination-previous,
.dp-pagination .wp-block-query-pagination-next {
	display: inline-flex;
	box-sizing: border-box;
	align-items: center;
	justify-content: center;
	min-height: var(--target-min);
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--text-secondary);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-decoration: none;
	text-transform: uppercase;
	transition:
		color var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard),
		background var(--dur-base) var(--ease-standard);
}

.dp-pagination .page-numbers {
	min-width: var(--target-min);
	padding: 8px 14px;
}

.dp-pagination .wp-block-query-pagination-previous,
.dp-pagination .wp-block-query-pagination-next {
	padding: 8px 18px;
}

/* The numbers block is a wrapper, not a pill. It carries neither class above. */
.dp-pagination .wp-block-query-pagination-numbers {
	min-height: 0;
	padding: 0;
	border: 0;
	border-radius: 0;
}

.dp-pagination a:hover {
	border-color: var(--text-muted);
	color: var(--text-primary);
}

.dp-pagination .page-numbers.current {
	border-color: var(--dp-teal);
	background: color-mix(in srgb, var(--dp-teal) 14%, transparent);
	color: var(--hue-teal);
}

/* A step with nowhere to go. Core drops it; the design draws it dimmed, and
   DP\Theme\Query\Pagination puts it back as a `<span>` — no href, so it is
   neither focusable nor able to reach a page that does not exist. */
.dp-pagination .dp-page-step-disabled {
	color: var(--text-muted);
	cursor: default;
	opacity: 0.45;
}

/* The panel that closes the archive, on its last page only. */
.dp-at-end {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px 24px;
	flex-wrap: wrap;
	margin-block-start: 32px;
	padding-block-start: 28px;
	border-top: var(--border-width) solid var(--border-subtle);
}

p.dp-at-end-line {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

.dp-empty {
	display: flex;
	align-items: flex-start;
	flex-direction: column;
	gap: 20px;
	padding-block: 72px;
	border-block: var(--border-width) solid var(--border-subtle);
}

/* The count line leads with the monogram at 30px and three tenths opacity. It
   is a background rather than an `<img>` for the same reason the featured
   panel's mark is: the design marks it `alt=""`, so it is decoration, and a
   decorative image that is not in the document cannot be announced, cannot be
   linked and needs nothing from the editor. The container's own `::before` is a
   flex item, which is what puts it beside the text rather than inside it. */
p.dp-empty-count {
	display: flex;
	align-items: center;
	gap: 14px;
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

p.dp-empty-count::before {
	width: 30px;
	height: 30px;
	flex: none;
	background: url(../img/dp-mark-white-128.png) no-repeat center / contain;
	content: "";
	opacity: 0.3;
}

h3.dp-empty-title {
	max-width: 28ch;
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-xl);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-wrap: balance;
}

p.dp-empty-line {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

.dp-empty-actions {
	display: flex;
	gap: 12px;
	flex-wrap: wrap;
}

/* -------------------------------------------------------------------------
 * The series archive
 * ---------------------------------------------------------------------- */

.dp-series-hero {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	max-width: var(--container-lg);
	margin-inline: auto;
	padding-block: 80px 48px;
	padding-inline: var(--gutter);
}

.dp-series-hero::before {
	position: absolute;
	z-index: -1;
	top: -160px;
	left: 12%;
	width: min(620px, 110vw);
	height: 520px;
	border-radius: var(--radius-circle);
	background: radial-gradient(circle, color-mix(in srgb, var(--dp-pink) 12%, transparent) 0%, transparent 62%);
	content: "";
	pointer-events: none;
}

.dp-series-hero-inner {
	position: relative;
	max-width: var(--container-md);
}

/* The SERIES badge and, beside it, how much of the series exists —
   "3 PARTS UP · 4 DRAFTED", counted by DP\Theme\Query\ArchiveFacts from the
   same two queries the two lists below are drawn from. */
.dp-series-hero-badges {
	display: flex;
	align-items: center;
	gap: 12px 20px;
	flex-wrap: wrap;
	margin-block-end: 28px;
}

p.dp-series-written {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

p.dp-series-written:empty {
	display: none;
}

h1.dp-series-hero-title {
	margin-block: 0 24px;
	font-family: var(--font-display);
	font-size: clamp(2.25rem, 6vw, 4rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
	text-wrap: balance;
}

/* The deck is the term's own description, bound through
   DP\Theme\Query\ArchiveFacts. The second selector styles
   `core/term-description` to match, for a hero that draws the block instead. */
p.dp-series-hero-deck,
.dp-series-hero .wp-block-term-description p {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
}

p.dp-series-hero-deck:empty {
	display: none;
}

/*
 * The two sections are not the same shape and the build used to give them one
 * class. "Start with these" is `padding: 8px var(--gutter) 48px` and its deck
 * clears 20px; "Still to come" is `0 var(--gutter) 64px` and its deck clears 8,
 * because the first row of the list below it draws its own rule.
 */
.dp-series-published {
	container: dp-parts / inline-size;
	padding-block: 8px 48px;
}

.dp-series-planned-section {
	padding-block: 0 64px;
}

h2.dp-series-section-title {
	margin-block: 0 8px;
	font-family: var(--font-display);
	font-size: var(--fs-xl);
	font-weight: var(--fw-bold);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
}

p.dp-series-section-deck {
	max-width: var(--measure);
	margin-block: 0 20px;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

p.dp-series-section-deck-tight {
	margin-block-end: 8px;
}

/* -------------------------------------------------------------------------
 * The published parts
 *
 * Three columns — the number and its date, the title and its note, and the
 * affordance — not the compact `PostRow` the build reused. The design's own
 * `seriesOut.rowStyle` is `120px minmax(0, 1fr) 110px`, and it collapses to one
 * column below 700px. The threshold is the page's width in the design tool, so
 * here it is a container query on the section, which is the page's width until
 * the page is wider than the container.
 * ---------------------------------------------------------------------- */

.dp-parts {
	display: flex;
	flex-direction: column;
	margin: 0;
	padding: 0;
	border-bottom: var(--border-width) solid var(--border-subtle);
	list-style: none;
}

.dp-parts > li {
	display: block;
	margin: 0;
	padding: 0;
}

.dp-parts > li::before {
	content: none;
}

.dp-part-row {
	display: grid;
	align-items: baseline;
	gap: 28px;
	grid-template-columns: 120px minmax(0, 1fr) 110px;
	padding-block: 26px;
	border-top: var(--border-width) solid var(--border-subtle);
}

p.dp-part-number {
	margin: 0;
	color: var(--hue-pink);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

p.dp-part-number:empty {
	display: none;
}

/* One line — "MAR 15, 2026 · 9 MIN READ" — from two blocks, because the date's
   format belongs to `core/post-date` and the read time to a meta field. Inline
   children and a generated separator put them back into the single text run the
   design writes. */
.dp-part-meta {
	margin-block-start: 6px;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-part-meta > * {
	display: inline;
	margin: 0;
	color: inherit;
	font-family: inherit;
	font-size: inherit;
	line-height: inherit;
	letter-spacing: inherit;
}

.dp-part-meta > *:not(:first-child)::before {
	content: " · ";
}

.dp-part-meta p:empty {
	display: none;
}

h3.dp-part-title {
	margin-block: 0 8px;
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-wrap: pretty;
}

h3.dp-part-title a {
	color: inherit;
	text-decoration: none;
}

.dp-part-note {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-part-note p {
	margin: 0;
	color: inherit;
	font-size: inherit;
	line-height: inherit;
}

a.dp-part-status {
	display: block;
	color: var(--accent-text);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-align: right;
	text-decoration: none;
	text-transform: uppercase;
}

@container dp-parts (width < 700px) {

	.dp-part-row {
		align-items: start;
		gap: 10px;
		grid-template-columns: minmax(0, 1fr);
		padding-block: 22px;
	}

	a.dp-part-status {
		text-align: left;
	}
}

/* Still to come — rendered by DP\Theme\Blocks\SeriesPlanned, which carries no
   post ID and so has nothing to link. */
.dp-planned-row {
	display: flex;
	align-items: baseline;
	gap: 6px 24px;
	flex-wrap: wrap;
	padding-block: 18px;
	border-top: var(--border-width) solid var(--border-subtle);
}

.dp-planned-part {
	min-width: 60px;
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-planned-body {
	flex: 1 1 280px;
}

h3.dp-planned-title {
	margin-block: 0 6px;
	color: var(--text-secondary);
	font-family: var(--font-display);
	font-size: var(--fs-base);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-wrap: pretty;
}

p.dp-planned-note {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-muted);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
}

/* -------------------------------------------------------------------------
 * The blog's featured panel
 *
 * The one gradient this view spends (digest §1). The main query holds this post
 * back — see DP\Theme\Query\QueryLoops — so the panel and the list below it
 * never show the same piece of writing twice.
 * ---------------------------------------------------------------------- */

.dp-featured {
	padding-bottom: 40px;
}

/* The list below the panel closes the view: `padding: 0 var(--gutter) 120px`. */
.dp-blog {
	padding-bottom: 120px;
}

/*
 * The pill row's own margins. The design wraps `FilterPills` in a div rather
 * than spacing the component itself, because the same component is used on the
 * work page inside different surroundings.
 *
 * Two classes and a child combinator, not one class, and the reason is worth
 * stating because it is a new corner of the hazard ADR-0008 describes. Core's
 * flow-layout rule for a *first* child is
 * `:root :where(.is-layout-flow) > :first-child`, and `:first-child` is a
 * pseudo-class — so that selector is two units of specificity, not the one its
 * `:where()` makes it look like. A one-class rule loses to it outright rather
 * than on load order, and this row is the first child of its section.
 */
.dp-section.dp-blog > .dp-blog-filter {
	margin-block: 24px 20px;
}

/*
 * The two mono affordances between the pills and the list.
 *
 * The design draws two: the pink one into the series, and a muted "BROWSE BY
 * CATEGORY →". The second is not built, because it has nowhere to go — the
 * design's own handler opens one specific category archive, which CLAUDE.md
 * §5.1 forbids the theme from naming, and there is no categories index in the
 * design or in this site to point at instead. It is a question for David rather
 * than a guess; the row's geometry is identical either way.
 */
.dp-quicklinks.wp-block-buttons {
	display: flex;
	gap: 12px 28px;
	flex-wrap: wrap;
	margin-block: 0 32px;
	padding-block-end: 24px;
	border-bottom: var(--border-width) solid var(--border-subtle);
}

.dp-quicklinks .wp-block-button__link {
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-quicklink-series .wp-block-button__link {
	color: var(--hue-pink);
}

.dp-quicklink-series .wp-block-button__link:hover {
	color: var(--dp-pink);
}

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

.dp-featured-list > li::before {
	content: none;
}

.dp-featured-panel {
	display: grid;
	overflow: hidden;
	gap: 0;
	grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--bg-surface);
	transition: transform var(--dur-base) var(--ease-standard);
}

.dp-featured-panel:hover {
	transform: translateY(-3px);
}

.dp-featured-body {
	padding: 44px;
}

h2.dp-featured-title {
	margin-block: 24px 16px;
	font-family: var(--font-display);
	font-size: var(--fs-2xl);
	font-weight: var(--fw-bold);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-display);
}

h2.dp-featured-title a {
	color: inherit;
	text-decoration: none;
}

.dp-featured-lead {
	margin-block: 0 24px;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

.dp-featured-lead p {
	margin: 0;
	color: inherit;
	font-size: inherit;
	line-height: inherit;
}

/* One line, not two elements. `featured.meta` is `p.cat + ' · ' + p.read`
   — a single string the design draws in a single div, with no link inside it
   because the whole panel is the click target. DP\Theme\Chrome\PostPresentation
   assembles it under the `card-meta` key. */
p.dp-featured-meta {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: normal;
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

p.dp-featured-meta:empty {
	display: none;
}

/*
 * The art column carries the post's own featured image, and the design's
 * gradient-and-monogram only when there is none.
 *
 * This is a deliberate divergence from `design-source/dpaternina.dc.html`,
 * which draws only the spectrum gradient with the white mark over it — David
 * overruled the design on this point (2026-08-29): the panel shows the post's
 * real Featured Image, set in the editor, and the gradient stays as the empty
 * state. The parity harness (ADR-0012) maps the work template and the front
 * page, not the blog view, so no baseline entry pins the old markup; if the
 * blog view is ever mapped, this comment is the reason the panel and the
 * design disagree.
 *
 * The layering needs no `:has()`: `core/post-featured-image` renders nothing
 * at all when the post has no featured image (the same fact
 * `.dp-about-portrait` leans on), so the figure either covers the empty state
 * completely or does not exist. The design put its `opacity: 0.9` on the
 * column itself, which would dim the photo too — so the gradient moves to a
 * `::before` layer carrying the 0.9, and the mark's `::after` keeps the same
 * rendered value it always had: the design's 0.55 inside the 0.9 container,
 * flattened.
 */
.dp-featured-art {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 300px;
}

.dp-featured-art::before {
	position: absolute;
	inset: 0;
	background: var(--dp-gradient-spectrum);
	content: "";
	opacity: 0.9;
}

.dp-featured-art::after {
	position: relative;
	width: 96px;
	height: 96px;
	background: url(../img/dp-mark-white-128.png) no-repeat center / contain;
	content: "";
	opacity: calc(0.9 * 0.55);
}

.dp-featured-art .wp-block-post-featured-image {
	position: absolute;
	z-index: 1;
	inset: 0;
	margin: 0;
}

.dp-featured-art .wp-block-post-featured-image a {
	display: block;
	height: 100%;
}

.dp-featured-art .wp-block-post-featured-image img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* -------------------------------------------------------------------------
 * TimelineChart
 *
 * design-source/components/TimelineChart.dc.html, rendered by DP\Core\Blocks\Timeline.
 *
 * The design measures its own width with a ResizeObserver and switches between
 * three modes. Its own closing note says why — "the design tool cannot express
 * media queries" — and tells the theme to use container queries and
 * `details`/`summary` instead. So this file is where the three modes actually
 * live, and the server renders one markup for all of them.
 *
 * The thresholds and the numbers are the design's, not chosen here:
 *
 *   bars    >= 700px   label column 200px, year track, no chevrons
 *   stack    < 700px   labels only, no track, chevrons        (the default)
 *   scroll   < 700px   label column 128px, sticky, 720px inner, swipe hint
 *
 * Stack is what a phone gets. Scroll is implemented, styled and reachable —
 * the block's `mobileMode` attribute picks it — but the Ledger chose stack and
 * stack is what ships.
 *
 * Four custom properties carry every number that differs between modes, so a
 * mode is a handful of declarations rather than a second copy of the component:
 *
 *   --dp-tl-label-base  the label column
 *   --dp-tl-gap         between the label column and the track
 *   --dp-tl-rail        how far a shipped thing hangs off its role's hairline
 *   --dp-tl-bleed       how far an open row's tint bleeds into the card padding
 *
 * The ship label column subtracts the rail back out, which is what keeps a ship
 * bar on the same year axis as the role bar above it. Rail minus bleed is 4px
 * in both modes (16 - 12 and 20 - 16), so an open ship's tint stops the same
 * distance from the hairline whichever mode is drawing it.
 * ---------------------------------------------------------------------- */

.dp-timeline {
	container: dp-timeline / inline-size;

	/* Stack is the default, so the stack numbers are the ones declared here. */
	--dp-tl-label-base: 100%;
	--dp-tl-rail: 16px;
	--dp-tl-bleed: 12px;

	/* The open-row tint, and the colour every bar in this subtree is drawn in.
	   A lane with its own accent overrides the colour on the row; nothing else
	   in the component names a hue.

	   Two properties, because a row's accent is used twice and CLAUDE.md §5
	   settles which token each use takes: `--dp-*` is a FILL, `--hue-*` is TEXT.
	   `--dp-tl-color` fills the bar and the legend swatch; `--dp-tl-hue` writes
	   the kind label and an open row's title. On dark they resolve to the same
	   hex — `theme.css` sets `--hue-teal: var(--dp-teal)` there — so this buys
	   nothing visible today and everything the moment a scope changes them, which
	   is exactly why the design keeps two families rather than one. */
	--dp-tl-tint: 4%;
	--dp-tl-color: var(--dp-teal);
	--dp-tl-hue: var(--hue-teal);

	/* The gap between the label column and the track. `gridStyle`, `headStyle`
	   and `detailGridStyle` all take it, and all three say 24 in every mode that
	   draws a track. Stack has one column and states its own row gaps below. */
	--dp-tl-gap: 24px;
}

/* `hidden` is how a filtered-out row is removed — see DP\Core\Blocks\TimelineRows.
   Every user agent's own stylesheet honours it, which is what makes the filter
   work with the scripts off; this rule is only here because the component's own
   display values would otherwise outrank the UA's. */
.dp-timeline [hidden] {
	display: none;
}

ul.dp-tl-pills {
	margin-bottom: 28px;
}

/* `FilterPills.dc.html`: "The trailing 'extra' button (EXPAND ALL / COLLAPSE
   ALL) uses a DASHED border", and its inline style makes it `--text-muted`
   rather than the `--text-secondary` the three filters carry. The list is named
   in the selector because `ul.dp-filter-pills a` is a class and two elements,
   which outranks a class and one element: without it this rule lost `border` and
   `color` to the general pill and the control was drawn solid, in the wrong
   grey, in both contexts. */
ul.dp-filter-pills .dp-tl-pill-extra a {
	border-style: dashed;
	color: var(--text-muted);
}

.dp-tl-card {
	padding: 4px 16px 24px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--bg-surface);
}

.dp-tl-scroller {
	min-width: 0;
	max-width: 100%;
	overflow: visible;
}

/*
 * `headStyle` is a grid on the same track list as the rows below it, not a
 * stack: the legend occupies the label column and the year axis occupies the
 * track's, which is what puts a year over the bar that starts in it. The theme
 * used to stack them and push the axis across with
 * `padding-left: calc(label + gap)` — arithmetic that was 8px short of the
 * design's, because the gap was 16 where the design says 24.
 *
 * The rule under the head is the design's too, and was simply not drawn.
 */
.dp-tl-head {
	display: grid;
	align-items: center;
	gap: 8px;
	grid-template-columns: var(--dp-tl-label-base);
	padding-block: 12px;
	border-bottom: var(--border-width) solid var(--border-subtle);
}

/*
 * The label family's line box.
 *
 * `TimelineChart.dc.html` declares a line-height on exactly three things — the
 * two paragraphs and the artifact's `<pre>`, all three `--lh-relaxed` — and on
 * nothing else. Every mono caps label in the component is therefore drawn in the
 * browser's `normal` line box. `theme.json`'s root line-height is
 * `--lh-relaxed` (1.65), so each of those labels inherited a 19.8px line box
 * where the design draws about 15.6, and the chart carried four extra pixels
 * per label row — nineteen of them between the legend and the last stat tile.
 *
 * `normal` is not a value this theme chose. It is what the design renders, and
 * restating it is the only way to say "the design declares nothing here" in a
 * document that has a root line-height. The design's own declared line-heights
 * stay exactly where they are: `p.dp-tl-prose`, `pre.dp-tl-artifact-body`, and
 * the bullet row, whose `--fs-xs` marker deliberately inherits the row's
 * `--lh-relaxed` (TimelineChart.dc.html L100-103).
 *
 * `.dp-tl-legend`, `.dp-tl-kind`, `.dp-tl-org` and `h3.dp-tl-headline` are the
 * four whose design styles are computed in the design tool rather than written
 * into the markup, so their line-height cannot be read out of the source. They
 * follow the family they belong to, and `tests/e2e/design-parity.spec.ts`
 * deliberately does not pin them (ADR-0012).
 */
.dp-tl-legend,
.dp-tl-years,
.dp-tl-role,
.dp-tl-range,
.dp-tl-kind,
.dp-tl-stack,
.dp-tl-shipped,
.dp-tl-fact-label,
.dp-tl-fact-value,
.dp-tl-artifact-head,
.dp-tl-stat-value,
.dp-tl-stat-label,
.dp-tl-swipe {
	line-height: normal;
}

.dp-tl-legend {
	display: flex;
	align-items: center;
	gap: 16px;
	flex-wrap: wrap;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-tl-key {
	display: inline-flex;
	align-items: center;
	gap: 8px;
}

.dp-tl-swatch {
	width: 8px;
	height: 8px;
	flex: none;
	border-radius: var(--radius-xs);
	background: var(--dp-tl-color);
}

.dp-tl-swatch-ship {
	width: 16px;
	background: var(--dp-gold);
}

/*
 * The axis. Hidden in stack mode, where there is no track for it to label, and
 * hidden from assistive technology always: every row prints its own range as
 * text, which is the same information said properly.
 *
 * **This is a deliberate divergence from `design-source/`, and it is the only
 * one in the chart.** The design draws this row as
 * `display: flex; justify-content: space-between`, which is a different scale
 * from the one its own bars are on. `pos(y) = ((y - start) / (end - start + 1))
 * * 100` gives a year 1/13 of the track and puts 2026 at 92.3%; spreading
 * thirteen labels between the two edges advances 1/12 and puts 2026 at 100%. By
 * the right-hand edge the two are 7.7% apart, so a role running to 2026.4 ends
 * at 95.4% — well left of its own label — and reads as having stopped a year
 * early. David saw exactly that and called it: "the lines don't go all the way
 * to the end."
 *
 * A grid of equal columns says `pos()` without repeating it. `grid-auto-flow:
 * column` gives each label a column of its own and `grid-auto-columns: minmax(0,
 * 1fr)` makes them equal, so with `n` labels the `n`th begins at `n / n_total` —
 * which *is* `pos( year )`, by construction, with no arithmetic in the
 * stylesheet and no count that has to be kept in step with the markup. The last
 * label therefore begins at 92.3% and its four mono characters (about 32px)
 * finish inside the final thirteenth at every width bars mode is drawn at, so
 * nothing overflows the track and nothing is clipped; `tests/e2e/timeline.spec.ts`
 * measures both.
 *
 * `justify-content` is gone rather than carried over: on a grid whose columns
 * already fill the container there is no free space for it to distribute, so
 * keeping it would be a declaration that reads as intent and does nothing.
 *
 * The parity harness records this as a named divergence rather than dropping the
 * entry — `chart.years` in `tests/e2e/fixtures/design-baseline.json`, generated
 * from the `divergence` field in `tests/Support/DesignBaseline.php`. The design
 * still has to keep saying what it says: if `.dp-tl-years`' declarations move in
 * Claude Design, the anchor stops matching and `composer design:check` fails.
 * See docs/adr/0014-the-year-axis-and-the-bars-share-one-scale.md.
 */
.dp-tl-years {
	display: none;
	grid-auto-flow: column;
	grid-auto-columns: minmax(0, 1fr);
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-wide);
}

.dp-tl-lanes {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding-top: 18px;
}

/*
 * `wrapStyle`: the lane, which holds a role and everything that came out of it.
 * In stack mode it carries the hairline that separates one lane from the next
 * and its two children sit flush; in bars mode there is no rule and 6px of air.
 */
.dp-tl-lane {
	display: flex;
	flex-direction: column;
	gap: 0;
	border-top: var(--border-width) solid var(--border-subtle);
}

/*
 * `rowStyle`: the padding is a function of the mode AND the open state, which is
 * four numbers per mode rather than one. The theme carried a single 8px on the
 * `<summary>` and nothing on the row, so an open row had no vertical padding of
 * its own at all — the question Phase 7d put to David as "is the open role row
 * meant to breathe more?" was the design saying yes, in a file nobody had.
 *
 * Stack is the default, so the stack numbers are the ones declared here.
 *
 * **The transition names `padding-block`, where the design names `padding`.**
 * That is a narrowing rather than a divergence: in `rowStyle` the *inline*
 * padding is constant within a mode — `16px 12px` opens to `16px 12px 18px`,
 * `6px 16px` opens to `8px 16px 14px` — so the only padding the design ever
 * animates is the block axis. Naming the whole shorthand costs nothing in a
 * design tool, which has no stylesheet to arrive late. It costs something here.
 *
 * The row bleeds into the card's padding with a negative margin and pays it back
 * with an equal padding, both read off `--dp-tl-bleed`. They cancel exactly —
 * but only while they change together. `margin-inline` is not transitioned, so
 * it snaps; `padding` was, so it crawled. Any style change that gives this
 * element its padding after it already has a computed style therefore animated
 * `padding-inline` up from `0` over `--dur-base` with the margin already at its
 * full negative value, and for those 200ms the row's content box — the grid, the
 * track, every bar on it — sat up to 16px to the left of the axis in
 * `.dp-tl-head`, which has no transition and snaps. Measured: `track.left` sweeps
 * 433 → 449 while `.dp-tl-years` stays at 449 throughout. That is the same
 * load-time repaint ADR-0013 recorded against the open rows' tint and left open,
 * seen on the axis this time, and `tests/e2e/timeline.spec.ts` caught it as an
 * 8px error on an assertion whose tolerance is half a pixel.
 *
 * `padding-block` keeps every animation the design asks for — the open row's
 * breathing is vertical — and makes the horizontal slide impossible, because the
 * two properties that have to cancel now both snap.
 */
.dp-tl-row {
	--dp-tl-label: var(--dp-tl-label-base);

	margin-inline: calc(var(--dp-tl-bleed) * -1);
	padding: 16px var(--dp-tl-bleed);
	border-radius: var(--radius-sm);
	transition:
		background var(--dur-base) var(--ease-standard),
		padding-block var(--dur-base) var(--ease-standard);
}

.dp-tl-row-role[open] {
	padding-bottom: 18px;
}


.dp-tl-row[open] {
	background: color-mix(in srgb, var(--dp-white) var(--dp-tl-tint), transparent);
}

.dp-tl-row.is-accent-teal,
.dp-tl-group-head.is-accent-teal {
	--dp-tl-color: var(--dp-teal);
	--dp-tl-hue: var(--hue-teal);
}

.dp-tl-row.is-accent-pink,
.dp-tl-group-head.is-accent-pink,
.dp-tl-swatch.is-accent-pink {
	--dp-tl-color: var(--dp-pink);
	--dp-tl-hue: var(--hue-pink);
}

.dp-tl-row.is-accent-gold,
.dp-tl-group-head.is-accent-gold,
.dp-tl-swatch.is-accent-gold {
	--dp-tl-color: var(--dp-gold);
	--dp-tl-hue: var(--hue-gold);
}

.dp-tl-row.is-accent-purple,
.dp-tl-group-head.is-accent-purple,
.dp-tl-swatch.is-accent-purple {
	--dp-tl-color: var(--dp-purple);
	--dp-tl-hue: var(--hue-purple);
}

/* `muted` is already a text token, so the fill and the text are one value. */
.dp-tl-row.is-accent-muted,
.dp-tl-group-head.is-accent-muted,
.dp-tl-swatch.is-accent-muted {
	--dp-tl-color: var(--text-muted);
	--dp-tl-hue: var(--text-muted);
}

.dp-tl-row-ship {
	--dp-tl-color: var(--dp-gold);
	--dp-tl-hue: var(--hue-gold);

	/* `rowStyle`'s ship branch: 12px all round on a phone, open or closed. */
	padding-block: 12px;
}

/* `list-style: none` removes the disclosure triangle in every engine that
   follows the current spec; the vendor pseudo-element is what older WebKit
   reads. The design draws its own chevron, and only in stack mode. */
.dp-tl-summary {
	display: block;
	cursor: pointer;
	list-style: none;
}

.dp-tl-summary::-webkit-details-marker {
	display: none;
}

.dp-tl-grid {
	display: grid;
	align-items: center;
	gap: 2px;
	grid-template-columns: var(--dp-tl-label);
}

/*
 * `labelCellStyle`. On a phone the cell is a centred column with its own 4px
 * rhythm and a 36px floor — the design's `--target-min`, which is what makes the
 * whole label a comfortable tap target — and it reserves 32px on the right for
 * the chevron. Beside a bar chart it is a plain block with no gutter at all.
 */
.dp-tl-label {
	position: relative;
	left: 0;
	display: flex;
	min-width: 0;
	min-height: var(--target-min);
	flex-direction: column;
	justify-content: center;
	gap: 4px;
	padding-right: 32px;
	text-align: left;
}

/*
 * `orgStyle`, and it is two different objects rather than one.
 *
 * A role is set in the DISPLAY face with `--ls-tight` and no weight of its own;
 * a shipped thing is set in the BODY face at 600 and one step smaller. Both are
 * a size larger on a phone than they are beside a bar chart, and a closed
 * shipped thing is `--text-secondary` where a closed role is `--text-primary`.
 * None of that was in the theme: every title was body-face, semibold,
 * `--fs-sm`, `--text-primary`, in both modes and both states.
 *
 * `line-height: normal` for the reason the label family above has it — the
 * design declares no leading here and `theme.json` declares a root one.
 */
.dp-tl-org {
	display: block;
	color: var(--text-primary);
	line-height: normal;
	transition: color var(--dur-base) var(--ease-standard);
}

.dp-tl-row-role > .dp-tl-summary .dp-tl-org {
	padding-right: 28px;
	font-family: var(--font-display);
	font-size: var(--fs-base);
	letter-spacing: var(--ls-tight);
}

.dp-tl-row-ship > .dp-tl-summary .dp-tl-org {
	color: var(--text-secondary);
	font-family: var(--font-body);
	font-size: var(--fs-sm);
	font-weight: var(--fw-semibold);
}

/*
 * An open row says which one it is in its own accent.
 *
 * `orgStyle` is one of the four chart styles the design tool computes and does
 * not export (ADR-0012), so this is not read out of the markup. What it is read
 * from is the design's own screenshot of the open MonsterInsights entry — teal
 * title on the role, gold title on the shipped thing beneath it — and from the
 * two rules in the export that make that a pattern rather than a one-off:
 *
 *   - Open state recolours to the accent. "Chevron (stack only): … colour
 *     var(--dp-teal) when open else var(--text-muted)" (LAYOUT NOTES). It is the
 *     only state-dependent colour the design writes down, and this is the same
 *     move on a different element.
 *   - The accent is per row, not per chart. "role bar lane.accent ||
 *     var(--dp-teal); ship bar var(--dp-gold)" (COLOURS). Teal on a role and
 *     gold on a shipped thing is exactly `--dp-tl-hue`, which is why this needs
 *     no second selector for the ship.
 *
 * A closed row keeps `--text-primary` above: the design's screenshot shows the
 * closed rows white, and nothing here is a licence to paint the whole column.
 *
 * The child combinator matters. A ship's `<details>` is a sibling of the role's,
 * not a descendant — see DP\Core\Blocks\TimelineRows::lane — so a descendant
 * selector would be correct today by accident. This says what it means.
 */
.dp-tl-row[open] > .dp-tl-summary .dp-tl-org {
	color: var(--dp-tl-hue);
}

.dp-tl-role {
	display: block;
	color: var(--text-muted);
	font-size: var(--fs-xs);
}

.dp-tl-range {
	display: block;
	margin-top: 4px;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-wide);
}

.dp-tl-chevron {
	position: absolute;
	top: 50%;
	right: 4px;
	display: flex;
	color: var(--text-muted);
	transform: translateY(-50%);
	transition:
		color var(--dur-base) var(--ease-standard),
		transform var(--dur-base) var(--ease-standard);
}

.dp-tl-row[open] .dp-tl-chevron {
	color: var(--hue-teal);
	transform: translateY(-50%) rotate(180deg);
}

.dp-tl-track {
	position: relative;
	display: none;
	height: 40px;
	border-radius: var(--radius-sm);
	background: color-mix(in srgb, var(--text-primary) 3%, transparent);
}

.dp-tl-row-ship .dp-tl-track {
	height: 32px;
	border-radius: var(--radius-xs);
	background: color-mix(in srgb, var(--text-primary) 2%, transparent);
}

/* left, width, max-width and min-width arrive as an inline style, because they
   are per-row data and no stylesheet can hold thirteen years of arbitrary start
   dates. DP\Core\Content\Timeline\Bar::style() writes them; everything else
   about a bar is here. */
.dp-tl-bar {
	position: absolute;
	top: 4px;
	bottom: 4px;
	overflow: hidden;
	box-sizing: border-box;
	border-radius: var(--radius-xs);
	background: color-mix(in srgb, var(--dp-tl-color) 38%, var(--bg-surface));
	transition:
		background var(--dur-base) var(--ease-standard),
		box-shadow var(--dur-base) var(--ease-standard);
}

/* `barStyle`'s `small` branch: a shipped thing's bar is inset 6px in a 32px
   track where a role's is inset 4 in a 40px one. */
.dp-tl-row-ship .dp-tl-bar {
	top: 6px;
	bottom: 6px;
}

.dp-tl-row[open] .dp-tl-bar {
	background: var(--dp-tl-color);
	box-shadow: 0 0 0 4px color-mix(in srgb, var(--dp-tl-color) 16%, transparent);
}

/* The hairline every shipped thing hangs off. The label column inside it gives
   the rail's width back, so a ship bar and its role's bar share one axis — which
   is the whole of the design's reason for it: "the ship label column subtracts
   that same padding back out so ship bars stay true to the year axis"
   (TimelineChart.dc.html, LAYOUT NOTES). It is therefore declared in the two
   modes that draw a track and in neither the base nor stack mode, where the
   label is the only column and giving 16px back to an axis that is not there
   left a dead gutter down the right of every shipped thing. */
.dp-tl-ships {
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin-block: 4px;
	padding-left: var(--dp-tl-rail);
	border-left: var(--border-width) solid var(--border-subtle);
}

/*
 * A company that ran two consecutive roles.
 *
 * `DP\Core\Content\Timeline\LaneGroup` gathers adjacent lanes naming the same
 * organisation; the header carries the company name and the run's whole span,
 * and the lanes hang off a rail beneath it — the same rail a shipped thing
 * already hangs off, drawn by the same two declarations, because it is the same
 * relationship one level up.
 *
 * **The rail is given back in the label column, exactly as it is for ships.**
 * That is the one thing in this block that is load-bearing: `padding-left`
 * moves the whole row right, so the column beside it has to shrink by the same
 * amount or the track — and every bar on it — slides off the year axis. The
 * design states the rule for ships in its LAYOUT NOTES ("the ship label column
 * subtracts that same padding back out so ship bars stay true to the year
 * axis") and this is that rule applied twice: a grouped role gives back one
 * rail, and a ship inside a grouped role, which is now two rails in, gives back
 * two. The three-class selector below is what makes the deeper one win.
 *
 * The header is `.dp-tl-group-head` and not a `<details>`. Nesting disclosures
 * would let a role row be open inside a closed parent and would break the
 * `dp-open` links that point straight at those rows, so the header opens
 * nothing: it is a row-shaped label with the run's combined bar in the track.
 * Its bar is `.dp-tl-bar-group` — thinner and quieter than a role's, because it
 * is a summary of the bars below it rather than a fourth thing on the chart.
 */
.dp-tl-group {
	display: flex;
	flex-direction: column;
	gap: 0;
	border-top: var(--border-width) solid var(--border-subtle);
}

.dp-tl-group-head {
	--dp-tl-label: var(--dp-tl-label-base);

	margin-inline: calc(var(--dp-tl-bleed) * -1);
	padding: 16px var(--dp-tl-bleed);
}

/* No chevron on a header, so no tap-target floor and no gutter to hold one. */
.dp-tl-group-head .dp-tl-label {
	min-height: auto;
	padding-right: 0;
}

/* `orgStyle`'s role branch: a company reads as a role title, because that is
   what it was before the two rows were gathered under it. */
.dp-tl-group-head .dp-tl-org {
	font-family: var(--font-display);
	font-size: var(--fs-base);
	letter-spacing: var(--ls-tight);
}

.dp-tl-group-head .dp-tl-track {
	height: 24px;
	background: transparent;
}

.dp-tl-bar-group {
	top: 10px;
	bottom: 10px;
	background: color-mix(in srgb, var(--dp-tl-color) 24%, var(--bg-surface));
}

.dp-tl-group-lanes {
	display: flex;
	flex-direction: column;
	gap: 0;
	padding-left: var(--dp-tl-rail);
	border-left: var(--border-width) solid var(--border-subtle);
}

/* The header is the separator above the first role in the run; a second
   hairline immediately under it would read as a divider between the company and
   its own first job. */
.dp-tl-group-lanes > .dp-tl-lane:first-child {
	border-top: 0;
}

.dp-tl-detail {
	display: grid;
	gap: 8px;
	grid-template-columns: minmax(0, 1fr);
	padding: 16px 0 0;
}

/*
 * ROLE — the label naming what an open role's detail is.
 *
 * `kindLabelStyle` is computed in the design tool and not exported, but the
 * shipped thing's counterpart *is*: the same element, in the same position of
 * the same disclosure, one level down, and it is written out in full —
 *
 *   font-family: var(--font-mono); font-size: var(--fs-xs);
 *   letter-spacing: var(--ls-caps); color: var(--hue-gold)
 *
 * (TimelineChart.dc.html, the `SHIPPED · {{ sh.range }}` line, which the theme
 * draws as `.dp-tl-shipped` below). Gold is the shipped thing's accent. The
 * role's label is computed for the one reason the ship's is not: a lane carries
 * its own `accent`, so the value varies per row. That makes `--dp-tl-hue` the
 * whole of the difference between the two, and `--text-muted` — which is what
 * this said for three review rounds — a colour the design never asked for.
 */
.dp-tl-kind {
	color: var(--dp-tl-hue);
	text-align: left;
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

p.dp-tl-prose {
	max-width: var(--measure);
	margin: 0 0 12px;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-tl-stack {
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* -------------------------------------------------------------------------
 * The expanded panel behind a shipped thing
 * ---------------------------------------------------------------------- */

/* The design gives the same paragraph two bottom margins: 12px under a role's
   detail (TimelineChart.dc.html L62) and 16px under a shipped thing's, where a
   bullet list follows it (L97). */
.dp-tl-panel p.dp-tl-prose {
	margin-bottom: 16px;
}

.dp-tl-panel {
	margin-block: 12px 0;
	padding: 14px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-sm);
	background: var(--band);
	animation: dp-tl-up var(--dur-slow) var(--ease-out) both;
}

@keyframes dp-tl-up {

	from {
		opacity: 0;
		transform: translateY(14px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

/* base.css already blunts every animation on the site to 0.01ms. This says the
   quieter thing as well: the panel has no entrance, rather than an entrance
   nobody can see. CLAUDE.md §1.7. */
@media (prefers-reduced-motion: reduce) {

	.dp-tl-panel {
		animation: none;
	}

	.dp-tl-row,
	.dp-tl-bar,
	.dp-tl-chevron {
		transition: none;
	}
}

.dp-tl-panel-cols {
	display: flex;
	gap: 24px;
	flex-wrap: wrap;
}

.dp-tl-panel-main {
	min-width: 0;
	flex: 1 1 320px;
}

.dp-tl-panel-aside {
	display: flex;
	min-width: 0;
	flex: 1 1 260px;
	flex-direction: column;
	gap: 12px;
}

.dp-tl-shipped {
	margin-bottom: 12px;
	color: var(--hue-gold);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* `headlineStyle`. Bold, tracked `--ls-display`, and a size larger beside a bar
   chart than on a phone — three values the theme had guessed at. */
h3.dp-tl-headline {
	margin: 0 0 12px;
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	font-weight: var(--fw-bold);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-display);
	text-wrap: pretty;
}

.dp-tl-bullets {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin: 0 0 20px;
	padding: 0;
	list-style: none;
}

.dp-tl-bullet {
	display: grid;
	align-items: baseline;
	gap: 12px;
	grid-template-columns: auto minmax(0, 1fr);
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-tl-bullet::before {
	color: var(--hue-gold);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	content: "—";
}

.dp-tl-facts {
	display: flex;
	gap: 20px 24px;
	flex-wrap: wrap;
	padding-top: 16px;
	border-top: var(--border-width) solid var(--border-subtle);
}

.dp-tl-fact-label {
	margin-bottom: 4px;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-tl-fact-value {
	font-size: var(--fs-sm);
}

.dp-tl-fact-stack {
	color: var(--hue-gold);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

a.dp-tl-writeup {
	color: var(--accent-text);
	text-decoration: none;
}

a.dp-tl-writeup:hover {
	color: var(--dp-teal-600);
}

.dp-tl-artifact {
	overflow: hidden;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-sm);
	background: var(--bg-page);
}

.dp-tl-artifact-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	flex-wrap: wrap;
	padding: 12px 16px;
	border-bottom: var(--border-width) solid var(--border-subtle);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-tl-artifact-label {
	color: var(--text-muted);
}

.dp-tl-artifact-tag {
	color: var(--hue-gold);
}

pre.dp-tl-artifact-body {
	overflow-x: auto;
	margin: 0;
	padding: 16px;
	color: var(--text-secondary);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: var(--lh-relaxed);
}

.dp-tl-stats {
	display: flex;
	gap: 12px;
	flex-wrap: wrap;
}

.dp-tl-stat {
	flex: 1 1 140px;
	padding: 16px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-sm);
	background: var(--bg-page);
}

.dp-tl-stat-value {
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	letter-spacing: var(--ls-display);
}

.dp-tl-stat-loud .dp-tl-stat-value {
	color: var(--hue-gold);
}

.dp-tl-stat-quiet .dp-tl-stat-value {
	color: var(--text-secondary);
}

.dp-tl-stat-label {
	margin-top: 4px;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* Scroll mode only, and the one instruction in the component that is addressed
   to the reader rather than to the browser. */
.dp-tl-swipe {
	display: none;
	margin-top: 4px;
	padding-top: 14px;
	border-top: var(--border-width) solid var(--border-subtle);
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* -------------------------------------------------------------------------
 * Bars — the component is at least 700px wide
 * ---------------------------------------------------------------------- */

@container dp-timeline (width >= 700px) {

	.dp-tl-card {
		padding: clamp(20px, 3vw, 32px);
	}

	.dp-tl-inner {
		--dp-tl-label-base: 200px;
		--dp-tl-rail: 20px;
		--dp-tl-bleed: 16px;
	}

	.dp-tl-head {
		gap: var(--dp-tl-gap);
		grid-template-columns: var(--dp-tl-label-base) minmax(0, 1fr);
		padding-block: 0 16px;
	}

	/* Grid, not flex: the labels sit on the bars' scale. See the base rule. */
	.dp-tl-years {
		display: grid;
	}

	.dp-tl-lane {
		gap: 6px;
		border-top: 0;
	}

	.dp-tl-ships {
		gap: 6px;
		margin-block: 6px 0;
	}

	.dp-tl-row {
		padding-block: 6px;
	}

	.dp-tl-row-role[open] {
		padding-block: 8px 14px;
	}

	.dp-tl-row-ship,
	.dp-tl-row-ship[open] {
		padding-block: 6px;
	}

	.dp-tl-row-ship[open] {
		padding-bottom: 14px;
	}

	.dp-tl-ships .dp-tl-row {
		--dp-tl-label: calc(var(--dp-tl-label-base) - var(--dp-tl-rail));
	}

	.dp-tl-group {
		gap: 6px;
		border-top: 0;
	}

	.dp-tl-group-head {
		padding-block: 6px;
	}

	.dp-tl-group-lanes {
		gap: 6px;
		margin-block: 6px 0;
	}

	/* One rail in, so one rail back — the role bars stay on the axis the
	   header's own bar and the year labels are on. */
	.dp-tl-group-lanes .dp-tl-row {
		--dp-tl-label: calc(var(--dp-tl-label-base) - var(--dp-tl-rail));
	}

	/* Two rails in: the group's, then the ships'. Three classes, so it outranks
	   both of the two-class rules above it whichever order they are written in.
	   Without this a ship inside a grouped role would give back one rail and
	   hang a rail's width to the right of every other bar on the chart. */
	.dp-tl-group-lanes .dp-tl-ships .dp-tl-row {
		--dp-tl-label: calc(var(--dp-tl-label-base) - var(--dp-tl-rail) * 2);
	}

	.dp-tl-grid {
		gap: var(--dp-tl-gap);
		grid-template-columns: var(--dp-tl-label) minmax(0, 1fr);
	}

	.dp-tl-track {
		display: block;
	}

	/*
	 * No chevron here, so no gutter for one.
	 *
	 * The base rule reserves 28px on the right of the label for the chevron the
	 * design absolutely-positions there. "Chevron (stack only)" (LAYOUT NOTES) —
	 * so in bars mode there is nothing in that space, and the 16px grid gap
	 * already separates the label from the track.
	 *
	 * It is not cosmetic. The design says the label column is 200px and that a
	 * shipped thing's column "subtracts that same padding back out", giving 180.
	 * "Natural-language queries" — the fixture's own longest ship name — measures
	 * 170.6px at `--fs-sm`/`--fw-semibold`, so it fits 180 and does not fit the
	 * 168 that 12px of gutter leaves. The design's screenshot sets it on one
	 * line; a 2.6px gutter was turning it into two. `labelCellStyle` is computed
	 * in the design tool and unexportable, so the value is not read from the
	 * source — but the reason it is zero is.
	 */
	.dp-tl-label {
		display: block;
		position: static;
		min-height: auto;
		gap: 0;
		padding-right: 0;
	}

	.dp-tl-row-role > .dp-tl-summary .dp-tl-org,
	.dp-tl-group-head .dp-tl-org {
		padding-right: 0;
		font-size: var(--fs-sm);
	}

	.dp-tl-row-ship > .dp-tl-summary .dp-tl-org {
		font-size: var(--fs-xs);
	}

	.dp-tl-chevron {
		display: none;
	}

	.dp-tl-detail {
		gap: var(--dp-tl-gap);
		grid-template-columns: var(--dp-tl-label) minmax(0, 1fr);
		padding-bottom: 4px;
	}

	.dp-tl-panel {
		padding: 24px;
	}

	.dp-tl-panel-cols {
		gap: 32px;
	}

	h3.dp-tl-headline {
		font-size: var(--fs-xl);
	}

	/* The design tints an open shipped thing more lightly than an open role,
	   because in bars mode a ship sits inside a role that may itself be open. */
	.dp-tl-row-ship {
		--dp-tl-tint: 2.5%;
	}
}

/* -------------------------------------------------------------------------
 * Scroll — the variant, not the default
 *
 * The track keeps its full 720px and the component scrolls horizontally to it.
 * Anything meant to be read is pinned to max(240px, hostWidth - 60px) and stuck
 * to the left edge, so scrolling the axis never carries the words off-screen.
 * `cqw` is the host width the design measured with JavaScript, said in CSS.
 * ---------------------------------------------------------------------- */

@container dp-timeline (width < 700px) {

	.is-mobile-scroll .dp-tl-scroller {
		overflow-x: auto;
		overscroll-behavior-x: contain;
	}

	.is-mobile-scroll .dp-tl-inner {
		--dp-tl-label-base: 128px;

		min-width: 720px;
	}

	.is-mobile-scroll .dp-tl-head {
		gap: var(--dp-tl-gap);
		grid-template-columns: var(--dp-tl-label-base) minmax(0, 1fr);
		padding-block: 0 16px;
	}

	/* `legendStyle`: the only mode where the legend is a column, so that the
	   part of the chart meant to be read stays inside the visible width. */
	.is-mobile-scroll .dp-tl-legend {
		align-items: flex-start;
		gap: 6px;
		flex-direction: column;
	}

	.is-mobile-scroll .dp-tl-years {
		display: grid;
	}

	.is-mobile-scroll .dp-tl-lane {
		gap: 6px;
		border-top: 0;
	}

	.is-mobile-scroll .dp-tl-ships {
		gap: 6px;
		margin-block: 6px 0;
	}

	.is-mobile-scroll .dp-tl-panel {
		padding: 14px;
	}

	.is-mobile-scroll .dp-tl-ships .dp-tl-row {
		--dp-tl-label: calc(var(--dp-tl-label-base) - var(--dp-tl-rail));
	}

	.is-mobile-scroll .dp-tl-group {
		gap: 6px;
		border-top: 0;
	}

	.is-mobile-scroll .dp-tl-group-lanes {
		gap: 6px;
		margin-block: 6px 0;
	}

	/* The same give-back as bars mode, for the same reason: this is the other
	   mode that draws a track, so it is the other mode where a rail that is not
	   paid back walks the bars off the axis. */
	.is-mobile-scroll .dp-tl-group-lanes .dp-tl-row {
		--dp-tl-label: calc(var(--dp-tl-label-base) - var(--dp-tl-rail));
	}

	.is-mobile-scroll .dp-tl-group-lanes .dp-tl-ships .dp-tl-row {
		--dp-tl-label: calc(var(--dp-tl-label-base) - var(--dp-tl-rail) * 2);
	}

	.is-mobile-scroll .dp-tl-grid {
		gap: var(--dp-tl-gap);
		grid-template-columns: var(--dp-tl-label) minmax(0, 1fr);
	}

	.is-mobile-scroll .dp-tl-track {
		display: block;
	}

	.is-mobile-scroll .dp-tl-label {
		position: sticky;
		left: 0;
		z-index: 1;
		display: block;
		min-height: auto;
		gap: 0;
		padding-right: 12px;
		background: var(--bg-surface);
		box-sizing: border-box;
	}

	.is-mobile-scroll .dp-tl-chevron {
		display: none;
	}

	.is-mobile-scroll .dp-tl-legend,
	.is-mobile-scroll .dp-tl-detail,
	.is-mobile-scroll .dp-tl-panel {
		position: sticky;
		left: 0;
		width: max(240px, calc(100cqw - 60px));
	}

	.is-mobile-scroll .dp-tl-swipe {
		display: block;
	}
}

/* -------------------------------------------------------------------------
 * The work page
 *
 * dpaternina.dc.html, the `isTimeline` composition: the hero, then the three
 * cards, then the whole record. The section paddings are the design's own.
 * ---------------------------------------------------------------------- */


.dp-work-featured {
	padding-block: 24px 64px;
}

.dp-work-featured .dp-section-head {
	margin-bottom: 32px;
}

/*
 * Core's block gap, on top of the head's own 32. Both sections here space
 * themselves the way the design does — the element above declares the gap
 * below it — so core's `:root :where(.is-layout-flow) > * + *` has nothing left
 * to add and its 24px is simply a second gap. Two classes, because that rule is
 * one and a tie is decided by load order, which differs between the page and
 * the editor canvas (ADR-0008, ADR-0011).
 */
.dp-work-featured .dp-query {
	margin-block-start: 0;
}

.dp-work-record {
	padding-block: 20px 120px;
}

.dp-work-record .dp-section-head {
	margin-bottom: 24px;
}

/* The lede already carries the design's 24px below it; see above. */
.dp-work-record .dp-timeline {
	margin-block-start: 0;
}

p.dp-work-lede {
	max-width: var(--measure);
	margin: 0 0 24px;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-work-outro.wp-block-group {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px 24px;
	flex-wrap: wrap;
	margin-block-start: 40px;
	padding-top: 24px;
	border-top: var(--border-width) solid var(--border-subtle);
}

.dp-work-outro.wp-block-group > * {
	margin-block-start: 0;
}

.dp-work-outro p {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
}

/* "READ THE SERIES →" is one line in the design — `white-space: nowrap` on its
   own span — and it is the flex row's second item, so wrapping it moves the
   arrow onto a line of its own rather than gaining anything. */
.dp-work-outro .wp-block-button__link {
	white-space: nowrap;
}

/* -------------------------------------------------------------------------
 * The about page
 *
 * dpaternina.dc.html, the `isAbout` composition: an auto-fit two-column
 * opening — the lede beside the portrait — then the long-form body at the
 * measure, then the two closing controls.
 *
 * The body itself is deliberately not styled here. It is `post-content`, so it
 * is the house style (blocks.css), which is what digest §1 means by posts and
 * generic pages rendering through one block kit: the design's skill rows are an
 * `h4` and a `p`, and `h4` is already mono caps in the accent colour.
 * ---------------------------------------------------------------------- */

.dp-about {
	padding-block: clamp(56px, 8vw, 80px) clamp(88px, 12vw, 120px);
}

.dp-about-intro {
	display: grid;
	align-items: start;
	gap: clamp(32px, 5vw, 48px);
	grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
}

.dp-about-lede {
	display: flex;
	flex-direction: column;
	gap: 20px;
}

h1.dp-about-title {
	margin: 0;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: clamp(2.25rem, 5.5vw, 3.75rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
	text-wrap: balance;
}

p.dp-about-deck {
	margin: 0;
	color: var(--text-primary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

p.dp-about-deck:empty {
	display: none;
}

figure.dp-about-portrait {
	display: flex;
	flex-direction: column;
	gap: 12px;
	margin: 0;
}

/*
 * No portrait, no column. `core/post-featured-image` renders nothing at all on
 * a page with no featured image, and an empty grid cell would take half the
 * opening for something that is not there. The design ships no media, so this
 * is the state the page is actually in until David uploads one.
 */
figure.dp-about-portrait:not(:has(img)) {
	display: none;
}

figure.dp-about-portrait img {
	display: block;
	width: 100%;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-md);
	background: var(--bg-surface);
	object-fit: cover;
}

.dp-about-body {
	max-width: var(--measure);
	margin-top: clamp(48px, 8vw, 72px);
}

.dp-about-actions {
	margin-top: clamp(40px, 7vw, 64px);
	padding-top: 28px;
	border-top: var(--border-width) solid var(--border-subtle);
}

/* -------------------------------------------------------------------------
 * The contact page
 *
 * dpaternina.dc.html, the `isContact` composition. The gradient edge on the
 * panel is this view's one gradient (CLAUDE.md §5): a 2px `--dp-gradient-cool`
 * ground with the card sitting on top of it, which is how the design draws a
 * gradient border without a border-image.
 * ---------------------------------------------------------------------- */

.dp-contact {
	padding-block: clamp(64px, 9vw, 88px) clamp(88px, 12vw, 120px);
}

.dp-contact-grid {
	display: grid;
	align-items: start;
	gap: clamp(40px, 6vw, 64px);
	grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
}

/*
 * The same leak `.dp-section-head` and `.dp-gear` take back: the grid is a
 * `core/group` asking for the default layout, so WordPress emits its
 * `is-layout-flow` sibling margin and the form panel — the second child, and so
 * the second *column* — started 24px below the aside it is meant to line up
 * with. `align-items: start` is what makes it visible rather than absorbed.
 */
.dp-contact-grid.wp-block-group > * {
	margin-block-start: 0;
}

h1.dp-contact-title {
	margin: 0 0 22px;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: clamp(2.25rem, 5.5vw, 3.75rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
	text-wrap: balance;
}

.dp-contact-panel {
	padding: 2px;
	border-radius: var(--radius-lg);
	background: var(--dp-gradient-cool);
}

.dp-contact-card {
	display: flex;
	flex-direction: column;
	gap: 16px;
	padding: 32px;
	border-radius: var(--radius-md);
	background: var(--bg-surface);
}

.dp-contact-card:focus-visible {
	outline: var(--border-width-strong) solid var(--focus-ring);
	outline-offset: 4px;
}

h2.dp-contact-heading {
	margin: 0;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	font-weight: var(--fw-bold);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
}

.dp-contact-form {
	display: flex;
	flex-direction: column;
	gap: 16px;
	margin: 0;
}

p.dp-field {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin: 0;
}

label.dp-field-label {
	color: var(--text-secondary);
	font-family: var(--font-body);
	font-size: var(--fs-sm);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-normal);
}

input.dp-field-input,
textarea.dp-field-input {
	box-sizing: border-box;
	width: 100%;
	min-height: var(--target-comfortable);
	padding: 14px 16px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-md);
	background: var(--bg-page);
	color: var(--text-primary);
	font-family: var(--font-body);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
	transition: border-color var(--dur-base) var(--ease-standard);
}

textarea.dp-field-textarea {
	min-height: 0;
	resize: vertical;
}

input.dp-field-input:hover,
textarea.dp-field-input:hover {
	border-color: var(--border-strong);
}

input.dp-field-input:focus-visible,
textarea.dp-field-input:focus-visible {
	border-color: var(--accent-text);
	outline: var(--border-width-strong) solid var(--focus-ring);
	outline-offset: 2px;
}

/*
 * The honeypot.
 *
 * It is `aria-hidden` and `tabindex="-1"` in the markup, which keeps it away
 * from a screen reader and from the keyboard — but a sighted person with a
 * mouse would still see a text input labelled "Leave this field empty", fill it
 * in, and be refused for it. That refusal is indistinguishable from a bug, so
 * this rule is load-bearing rather than cosmetic and has a test on it.
 *
 * Taken off screen rather than `display: none`: a field the browser cannot
 * render at all is one some form fillers skip, and the point is to be filled by
 * the things that fill everything.
 */
.dp-hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

button.dp-contact-submit {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: 100%;
	height: var(--control-h-lg);
	padding: 0 var(--control-px-lg);
	border: var(--border-width) solid transparent;
	border-radius: var(--radius-pill);
	background: var(--accent);
	box-shadow: var(--shadow-sm);
	color: var(--accent-contrast);
	font-family: var(--font-display);
	font-size: var(--fs-base);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-tight);
	cursor: pointer;
	transition: background-color var(--dur-base) var(--ease-standard);
}

button.dp-contact-submit:hover {
	background: var(--accent-hover);
}

button.dp-contact-submit:focus-visible {
	outline: var(--border-width-strong) solid var(--focus-ring);
	outline-offset: 2px;
}

button.dp-contact-submit[aria-busy="true"] {
	opacity: 0.7;
	cursor: progress;
}

p.dp-contact-note {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	line-height: var(--lh-relaxed);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* The two result panels: same shell, two marks. */

.dp-contact-result {
	align-items: flex-start;
	justify-content: center;
	gap: 18px;
	min-height: 380px;
	padding: 44px 32px;
}

.dp-contact-mark {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 56px;
	height: 56px;
	border: var(--border-width) solid currentcolor;
	border-radius: var(--radius-circle);
}

.dp-contact-mark-ok {
	background: color-mix(in srgb, var(--dp-teal) 12%, transparent);
	color: var(--dp-teal);
}

.dp-contact-mark-bad {
	background: color-mix(in srgb, var(--dp-pink) 12%, transparent);
	color: var(--dp-pink);
}

h2.dp-contact-result-title {
	margin: 0;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: var(--fs-xl);
	font-weight: var(--fw-bold);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
	text-wrap: balance;
}

p.dp-contact-result-line {
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-contact-actions {
	display: flex;
	align-items: center;
	gap: 10px;
	flex-wrap: wrap;
	margin-top: 4px;
}

.dp-contact-retry {
	margin: 0;
}

a.dp-contact-action,
button.dp-contact-action {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	min-height: var(--target-comfortable);
	padding: 0 18px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: var(--fs-sm);
	font-weight: var(--fw-semibold);
	text-decoration: none;
	cursor: pointer;
	transition: border-color var(--dur-base) var(--ease-standard);
}

a.dp-contact-action:hover,
button.dp-contact-action:hover {
	border-color: var(--accent-text);
}

button.dp-contact-action-primary {
	border-color: transparent;
	background: var(--accent);
	color: var(--accent-contrast);
}

a.dp-contact-action-ghost {
	border-color: transparent;
	color: var(--text-secondary);
}

a.dp-contact-action-mail {
	color: var(--text-secondary);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	font-weight: var(--fw-regular);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* -------------------------------------------------------------------------
 * The résumé page
 *
 * dpaternina.dc.html, the `isResume` composition. Narrower than the rest of the
 * site — `--container-md`, because a résumé is read as a document — and it is
 * one of the two views the design gives no closing CTA band.
 * ---------------------------------------------------------------------- */

.dp-resume {
	max-width: var(--container-md);
	margin-inline: auto;
	padding-block: clamp(56px, 8vw, 80px) clamp(88px, 12vw, 120px);
	padding-inline: var(--gutter);
}

.dp-resume-head {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 24px 32px;
	flex-wrap: wrap;
	padding-bottom: 32px;
	border-bottom: var(--border-width) solid var(--border-subtle);
}

/*
 * Both résumé rows are `core/group` blocks asking for the default layout, so
 * core's `is-layout-flow` sibling margin reaches the trailing child — the
 * download buttons here, the "back to the top" buttons in the outro. Neither
 * row shows it at desktop width, where `align-items` absorbs the margin into
 * the line; both show it the moment the row wraps, when the 24px lands on top
 * of the flex `row-gap` and the second line falls by 24px more than the gap
 * asked for. `gap` is the row's spacing, and this leaves it as the only spacing.
 */
.dp-resume-head.wp-block-group > *,
.dp-resume-outro.wp-block-group > * {
	margin-block-start: 0;
}

p.dp-resume-updated {
	margin: 0 0 16px;
}

p.dp-resume-updated:empty {
	display: none;
}

h1.dp-resume-name {
	margin: 0 0 12px;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: clamp(2rem, 5vw, 3.25rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
}

p.dp-resume-role {
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
}

p.dp-resume-role:empty {
	display: none;
}

.dp-resume-download .wp-block-button__link {
	white-space: nowrap;
}

.dp-resume-summary {
	max-width: var(--measure);
	padding-block: 32px 8px;
}

.dp-resume-outro {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px 24px;
	flex-wrap: wrap;
	margin-top: 32px;
	padding-top: 28px;
	border-top: var(--border-width) solid var(--border-subtle);
}

.dp-resume-outro p {
	max-width: var(--measure);
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

/* The ledger itself — `dp/resume-ledger`. */

.dp-ledger {
	margin-top: 24px;
}

.dp-ledger .dp-section-head {
	margin-bottom: 8px;
}

.dp-ledger-rows {
	display: flex;
	flex-direction: column;
	border-bottom: var(--border-width) solid var(--border-subtle);
	container-type: inline-size;
	container-name: dp-ledger;
}

.dp-ledger-row {
	display: grid;
	gap: 8px 32px;
	padding-block: 28px;
	border-top: var(--border-width) solid var(--border-subtle);
	grid-template-columns: 1fr;
}

p.dp-ledger-range {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

h3.dp-ledger-title {
	margin: 0 0 4px;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: var(--fs-lg);
	font-weight: var(--fw-bold);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
}

p.dp-ledger-org {
	margin: 0 0 12px;
	color: var(--accent-text);
	font-size: var(--fs-sm);
	font-weight: var(--fw-semibold);
}

p.dp-ledger-detail {
	max-width: var(--measure);
	margin: 0 0 12px;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

ul.dp-ledger-ships {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin: 0 0 12px;
	padding: 0 0 0 16px;
	border-left: var(--border-width) solid var(--border-subtle);
	list-style: none;
}

li.dp-ledger-ship {
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
}

.dp-ledger-ship-range {
	margin-inline-start: 6px;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

p.dp-ledger-stack {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/*
 * The range moves out beside the entry once the ledger is wide enough to hold
 * both. Digest §5.2: the threshold is the component's own width, never the
 * viewport, so this is a container query and not a breakpoint.
 */
@container dp-ledger (min-width: 560px) {

	.dp-ledger-row {
		grid-template-columns: 140px minmax(0, 1fr);
	}
}

/* -------------------------------------------------------------------------
 * 404
 *
 * dpaternina.dc.html, the `isNotFound` composition. The design renders this
 * view with **no site chrome** — `const chrome = view !== 'notfound' && view
 * !== 'offline'` — and with no closing CTA band, so the template carries
 * neither template part. The monogram at the top left is the only way back,
 * and it is the white mark rather than the gradient one, because the view's
 * single gradient is spent on the glow behind the heading.
 * ---------------------------------------------------------------------- */

.dp-404 {
	position: relative;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	max-width: none;
	min-height: 100vh;
	min-height: 100svh;
	background: var(--bg-page);
}

.dp-404::before {
	position: absolute;
	z-index: 0;
	top: 38%;
	left: 50%;
	width: min(720px, 130vw);
	aspect-ratio: 1;
	border-radius: var(--radius-circle);
	background: radial-gradient(circle, color-mix(in srgb, var(--dp-teal) 10%, transparent) 0%, transparent 64%);
	content: "";
	pointer-events: none;
	transform: translate(-50%, -50%);
}

.dp-404-mark {
	position: relative;
	z-index: 1;
	flex: none;
	padding: 20px var(--gutter);
}

.dp-brand.dp-brand-white a {
	background-image: url(../img/dp-mark-white-128.png);
}

/*
 * The design puts the body in the middle of whatever is left below the mark:
 * `flex: 1; display: flex; align-items: center`. Rolled into one element here,
 * because there is only ever one child stack to centre.
 */
.dp-404-body {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	justify-content: center;
	box-sizing: border-box;
	max-width: var(--container-md);
	width: 100%;
	margin-inline: auto;
	padding: 64px var(--gutter) 96px;
	flex: 1;
}

p.dp-404-code {
	margin: 0 0 24px;
}

h1.dp-404-title {
	margin: 0 0 24px;
	color: var(--text-primary);
	font-family: var(--font-display);
	font-size: clamp(2.25rem, 7vw, 4.5rem);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
	text-wrap: balance;
}

p.dp-404-lead {
	max-width: var(--measure);
	margin: 0 0 16px;
	color: var(--text-secondary);
	font-size: var(--fs-md);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

p.dp-404-note {
	max-width: var(--measure);
	margin: 0 0 40px;
	color: var(--text-muted);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-404-actions {
	gap: 12px;
	margin-bottom: 56px;
}

.dp-lost {
	padding-top: 28px;
	border-top: var(--border-width) solid var(--border-subtle);
}

.dp-lost .dp-label {
	margin-bottom: 20px;
}

.dp-lost-grid {
	display: grid;
	gap: 12px;
	grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
}

.dp-lost-link .wp-block-button__link {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 8px;
	box-sizing: border-box;
	width: 100%;
	height: auto;
	padding: 22px;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--band);
	box-shadow: none;
	color: var(--text-primary);
	font-family: var(--font-body);
	font-weight: var(--fw-regular);
	letter-spacing: var(--ls-normal);
	text-align: left;
	text-wrap: pretty;
	white-space: normal;
	transition:
		transform var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard);
}

/* Same correction as `.dp-contact-method`: these cards are surfaces rather than
   pills, so the base button's `--accent-contrast` hover colour would take the
   title and the note to black on `--band`. */
.dp-lost-link .wp-block-button__link:hover {
	border-color: var(--accent-text);
	background: var(--band);
	box-shadow: none;
	color: var(--text-primary);
	transform: translateY(-3px);
}

.dp-lost-title {
	font-family: var(--font-display);
	font-size: var(--fs-base);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-tight);
}

.dp-lost-note {
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
}

@media (prefers-reduced-motion: reduce) {

	.dp-lost-link .wp-block-button__link,
	.dp-lost-link .wp-block-button__link:hover {
		transform: none;
	}
}

/* -------------------------------------------------------------------------
 * Watch — the featured panel, the video grid, and the gear list
 *
 * Transcribed from the Watch view in dpaternina.dc.html (the `isWatch`
 * section, the `videoCard()` styles and the GEAR grid). The markup comes from
 * `dp/watch-featured` and `dp/video-grid` in dp-core plus the theme's own
 * `watch-gear` pattern; everything visual lives here.
 *
 * Two facts shape the card styles:
 *
 * **The glow art is the card's own look, not an error state.** The design's
 * comment says so: a thumbnail the cache has not fetched yet "fails silently"
 * onto a per-card radial glow, each tile with its own placement so a cold row
 * reads as a set of cards rather than five copies of one failure. The glow
 * cycles by `:nth-child` (the design's GLOW array), the hue by tone class —
 * `--dp-*` for the fill, per the token rule.
 *
 * **The footer link is the whole card's press.** `.dp-vg-link::after`
 * stretches the card's one anchor over the card, so the design's card-sized
 * click target needs no script and no second link; the click-to-play script
 * upgrades exactly that anchor.
 * ---------------------------------------------------------------------- */

/* The two sections the design gives vertical padding and `.dp-section` does
   not: `.dp-section` sets the gutter and the measure only, so every named
   section states its own `padding-block` the way `.dp-work-featured` and
   `.dp-work-record` do. These two were transcribed without theirs and
   collapsed to zero — the featured panel sat flush against the archive band,
   and the gear section had nothing above or below it. The values are the
   design's own: `padding: 0 var(--gutter) 56px` on the featured section and
   `padding: var(--section-y) var(--gutter) 120px` on the gear section. The
   archive band between them was already right, from `.dp-band > .dp-section`. */
.dp-watch-top {
	padding-block: 0 56px;
}

.dp-watch-gear-section {
	padding-block: var(--section-y) 120px;
}

.dp-vg-card,
.dp-watch-featured {
	--dp-vg-hue: var(--dp-teal);
	--dp-vg-text: var(--hue-teal);
}

.dp-vg-card.dp-tone-pink,
.dp-watch-featured.dp-tone-pink {
	--dp-vg-hue: var(--dp-pink);
	--dp-vg-text: var(--hue-pink);
}

.dp-vg-card.dp-tone-gold,
.dp-watch-featured.dp-tone-gold {
	--dp-vg-hue: var(--dp-gold);
	--dp-vg-text: var(--hue-gold);
}

.dp-vg-card.dp-tone-purple,
.dp-watch-featured.dp-tone-purple {
	--dp-vg-hue: var(--dp-purple);
	--dp-vg-text: var(--hue-purple);
}

/* ------------------------------------------------------ The featured panel */

.dp-watch-featured {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
	gap: 0;
	overflow: hidden;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--bg-surface);
}

.dp-watch-featured-media {
	min-height: 300px;
}

.dp-vg-media {
	position: relative;
	overflow: hidden;
}

.dp-vg-card .dp-vg-media {
	aspect-ratio: 16 / 9;
}

.dp-vg-art {
	position: absolute;
	inset: 0;
	background:
		radial-gradient(
			circle at var(--dp-vg-glow-at, 32% 28%),
			color-mix(in srgb, var(--dp-vg-hue) var(--dp-vg-glow-mix, 22%), transparent) 0%,
			transparent 66%
		),
		var(--bg-page);
}

/* The monogram in the art is the theme's own asset, drawn here so the plugin
   never has to know where a theme file lives (CLAUDE.md §2.1). */
.dp-vg-art::after {
	content: "";
	position: absolute;
	top: 18px;
	right: 18px;
	width: 22px;
	height: 22px;
	background: url(../img/dp-mark-white-128.png) center / contain no-repeat;
	opacity: 0.2;
}

.dp-vg-art-center {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 12px;
}

.dp-vg-art-center::after {
	position: static;
	width: 44px;
	height: 44px;
	opacity: 0.22;
}

.dp-vg-art-label,
.dp-vg-when {
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

/* Tile art facts: platform top left, runtime and date on the bottom line. */
.dp-vg-card .dp-vg-art {
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	padding: 18px;
}

.dp-vg-badge {
	color: var(--dp-vg-text);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-vg-tile-foot {
	display: flex;
	align-items: baseline;
	gap: 10px;

	/* Room for the top-right monogram, which is absolutely placed. */
	padding-inline-end: 30px;
}

.dp-vg-dur {
	color: color-mix(in srgb, var(--dp-vg-hue) 62%, var(--text-primary));
	font-family: var(--font-mono);
	font-size: clamp(1.5rem, 4.5vw, 2.25rem);
	letter-spacing: var(--ls-tight);
}

/* A fetched thumbnail covers the art — the design's own layering. */
.dp-vg-thumb {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.dp-vg-player {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}

.dp-vg-live-badge {
	position: absolute;
	top: 20px;
	left: 20px;
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 7px 14px;
	border: var(--border-width) solid var(--dp-pink);
	border-radius: var(--radius-pill);
	background: color-mix(in srgb, var(--bg-page) 82%, transparent);
	color: var(--hue-pink);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

.dp-vg-live-dot {
	width: 7px;
	height: 7px;
	border-radius: var(--radius-circle);
	background: var(--dp-pink);
	animation: dp-vg-pulse 2s var(--ease-standard) infinite;
}

@keyframes dp-vg-pulse {

	50% {
		opacity: 0.35;
	}
}

.dp-watch-featured-body {
	display: flex;
	flex-direction: column;
	gap: 16px;
	padding: 40px;
}

p.dp-watch-featured-kicker {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px 14px;
	margin: 0;
}

.dp-vg-meta {
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-transform: uppercase;
}

h2.dp-watch-featured-title {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-2xl);
	font-weight: var(--fw-bold);
	letter-spacing: var(--ls-display);
	line-height: var(--lh-snug);
	text-wrap: balance;
}

p.dp-watch-featured-note {
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-watch-featured-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin-block-start: auto;
	padding-block-start: 12px;
}

a.dp-watch-play,
a.dp-watch-follow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: var(--control-h-md);
	padding: 0 20px;
	border: var(--border-width) solid transparent;
	border-radius: var(--radius-pill);
	font-family: var(--font-display);
	font-size: var(--fs-base);
	font-weight: var(--fw-semibold);
	text-decoration: none;
	transition:
		transform var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard);
}

a.dp-watch-play {
	background: var(--accent);
	box-shadow: var(--shadow-sm);
	color: var(--accent-contrast);
}

a.dp-watch-follow {
	border-color: var(--border-subtle);
	background: var(--bg-surface);
	color: var(--text-primary);
}

a.dp-watch-play:hover,
a.dp-watch-follow:hover {
	transform: translateY(-1px);
}

a.dp-watch-follow:hover {
	border-color: var(--accent-text);
}

p.dp-watch-players-note {
	margin: 0;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	line-height: var(--lh-relaxed);
	text-transform: uppercase;
}

/* ---------------------------------------------------------- The archive grid */

.dp-vg {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
	gap: 20px;
}

.dp-vg-card {
	position: relative;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border: var(--border-width) solid var(--border-subtle);
	border-radius: var(--radius-lg);
	background: var(--bg-surface);
	transition:
		transform var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard);
}

.dp-vg-card:hover {
	border-color: var(--accent-text);
	transform: translateY(-3px);
}

/* The design's GLOW array: each tile its own placement, cycling by five. */
.dp-vg-card:nth-child(5n + 2) {
	--dp-vg-glow-at: 68% 38%;
	--dp-vg-glow-mix: 16%;
}

.dp-vg-card:nth-child(5n + 3) {
	--dp-vg-glow-at: 50% 62%;
	--dp-vg-glow-mix: 20%;
}

.dp-vg-card:nth-child(5n + 4) {
	--dp-vg-glow-at: 28% 66%;
	--dp-vg-glow-mix: 15%;
}

.dp-vg-card:nth-child(5n) {
	--dp-vg-glow-at: 72% 24%;
	--dp-vg-glow-mix: 21%;
}

.dp-vg-body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: 10px;
	padding: 22px;
}

h3.dp-vg-title {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-base);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-tight);
	line-height: var(--lh-snug);
	text-wrap: pretty;
}

p.dp-vg-note {
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

.dp-vg-link {
	margin-block-start: auto;
	padding-block-start: 8px;
	color: var(--text-muted);
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	letter-spacing: var(--ls-caps);
	text-decoration: none;
	text-transform: uppercase;
	transition: color var(--dur-base) var(--ease-standard);
}

a.dp-vg-link:hover {
	color: var(--accent-text);
}

/* The card-sized press: one anchor, stretched. A card that lost its link
   (no identifier yet — ADR-0008) keeps the muted label and stretches nothing. */
a.dp-vg-link::after {
	content: "";
	position: absolute;
	inset: 0;
}

/* While the player is up, the card is the player's; the stretched press would
   sit over its controls. */
.dp-vg-media.is-playing {
	z-index: 1;
}

/* ------------------------------------------------------------ The gear list */

.dp-watch-gear-section p.dp-gear-intro {
	margin: 0 0 40px;
	max-width: var(--measure);
	color: var(--text-secondary);
	font-size: var(--fs-base);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

/*
 * Four groups, two by two.
 *
 * This is a deliberate divergence from `design-source/dpaternina.dc.html`,
 * which asks for `minmax(min(300px, 100%), 1fr)` — David overruled the design
 * on this point (2026-08-29): at the design's own `--container-lg: 1120px` the
 * gear list has 1056px of content to spend, 300px tracks and a 40px gutter
 * resolve to exactly three columns, and the gear data is exactly four groups.
 * Three plus an orphan, with two empty cells beside it. He chose the two-by-two
 * block over a four-across row: four columns would need a ~230px track, and the
 * item notes wrap badly at that width.
 *
 * 400px is the smallest round value that cannot draw three columns at any
 * container this theme offers — three of them plus two gutters need 1280px of
 * content, 224px more than `--container-lg` gives — while two of them need only
 * 840px, so the pairing survives down to tablet widths. Below 400px of content
 * the `min(…, 100%)` clamp collapses the list to one column, which is the
 * design's own narrow behaviour, unchanged.
 *
 * The two `margin-block-start` rules below are the other half of what David saw.
 * Both containers are `core/group` blocks asking for the default layout, so
 * WordPress renders them `is-layout-flow` and emits its own
 * `:root :where(.is-layout-flow) > * + * { margin-block-start: 24px }`; only
 * this stylesheet makes them grid and flex. Core's rule is written for stacked
 * flow children, and inside a grid it offsets every item except the first —
 * which is why the Desk column sat 24px above Camera & light and Audio on the
 * same row — and inside the flex column it added 24px to a 4px gap between gear
 * items. Zeroed rather than absorbed into a smaller `gap`, so `gap` stays the
 * single source of spacing here. The container's class is doubled for the same
 * reason `.dp-section-head` doubles it (ADR-0011, ADR-0008): core's selector is
 * one class of specificity, so a single-class rule here would tie and be
 * settled by load order — which differs between the front end and the editor.
 */
.dp-gear {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(400px, 100%), 1fr));
	gap: 32px 40px;
}

.dp-gear.wp-block-group > * {
	margin-block-start: 0;
}

.dp-gear-group {
	display: flex;
	flex-direction: column;
	gap: 4px;
}

.dp-gear-group.wp-block-group > * {
	margin-block-start: 0;
}

/* The group label is a heading wearing the mono caps label voice; the base
   `.dp-label` rule reaches only `p` and `span`, so the voice is restated here
   and `.dp-label.dp-tone-*` colours it as it colours every other label. */
h3.dp-gear-label {
	margin: 0;
	font-family: var(--font-mono);
	font-size: var(--fs-xs);
	font-weight: var(--fw-regular);
	letter-spacing: var(--ls-caps);
	line-height: normal;
	text-transform: uppercase;
}

.dp-gear-item {
	padding: 18px 0;
	border-block-end: var(--border-width) solid var(--border-subtle);
}

p.dp-gear-name {
	margin: 0 0 6px;
	font-size: var(--fs-sm);
	font-weight: var(--fw-semibold);
	text-wrap: pretty;
}

p.dp-gear-note {
	margin: 0;
	color: var(--text-secondary);
	font-size: var(--fs-sm);
	line-height: var(--lh-relaxed);
	text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {

	.dp-vg-card,
	.dp-vg-card:hover,
	a.dp-watch-play,
	a.dp-watch-play:hover,
	a.dp-watch-follow,
	a.dp-watch-follow:hover {
		transform: none;
	}

	.dp-vg-live-dot {
		animation: none;
	}
}
