/* ==========================================================================
   WPVelocity — components, in section order.
   Colour, type and space come from tokens.css. Nothing here hard-codes a hex
   except where a value is deliberately one-off and documented.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
	scroll-padding-top: calc(var(--header-h) + 16px);
}

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
}

body {
	margin: 0;
	background: var(--wpv-bg);
	color: var(--wpv-text);
	font-family: var(--wpv-b);
	font-size: var(--step-0);
	line-height: var(--lh-body);
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
}

img,
svg,
video { max-width: 100%; height: auto; display: block; }

a { color: inherit; text-decoration-color: var(--wpv-accent-line); text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--wpv-accent); }

button { font: inherit; color: inherit; }

h1, h2, h3, h4 {
	font-family: var(--wpv-h);
	font-weight: 700;
	line-height: var(--lh-head);
	letter-spacing: -.02em;
	margin: 0 0 var(--space-s);
	text-wrap: balance;
}

h1 { font-size: var(--step-5); line-height: var(--lh-tight); }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); }

p { margin: 0 0 var(--space-m); }
ul, ol { margin: 0 0 var(--space-m); padding-left: 1.15em; }

:focus-visible {
	outline: 2px solid var(--wpv-accent);
	outline-offset: 2px;
	border-radius: var(--radius-s);
}

::selection { background: var(--wpv-accent); color: #fff; }

.screen-reader-text {
	position: absolute !important;
	width: 1px; height: 1px;
	padding: 0; margin: -1px;
	overflow: hidden; clip-path: inset(50%);
	white-space: nowrap; border: 0;
}

.skip-link {
	position: absolute;
	top: -100px; left: var(--space-s);
	z-index: 200;
	padding: 12px 18px;
	background: var(--wpv-accent);
	color: #fff;
	border-radius: var(--radius-s);
	text-decoration: none;
	transition: top var(--dur-micro) var(--ease-snap);
}
.skip-link:focus { top: var(--space-s); }

/* --------------------------------------------------------------------------
   2. Layout
   -------------------------------------------------------------------------- */

.wrap {
	width: min(100% - (var(--gutter) * 2), var(--container));
	margin-inline: auto;
}

.wrap--wide { width: min(100% - (var(--gutter) * 2), var(--container-wide)); }

/* A measure column NEVER narrows the wrap itself.
   `.wrap` centres with auto margins, so shrinking that same element with
   max-width re-centres it in the viewport and the text drifts off the page's
   left rail — measured at +150px on the home hero and +259px on every inner
   page. The wrap keeps its full width and the line length is capped on its
   children instead, so every block starts on the same vertical line.
   Percentage padding on the wrap does not work here: percentages resolve
   against the parent section, not the wrap, which collapses the measure and
   wraps the headings at wide viewports. */
.wrap--prose > * { max-width: 74ch; }

/* One rule for every section: half the gap on each side. Two stacked sections
   therefore always produce exactly --section-gap between them, and where the
   background changes the boundary lands in the middle of that gap instead of
   hugging the block above it. No exceptions, no adjacent-sibling or :has()
   special cases — those were what made the band edges look off-centre. */
.section {
	position: relative;
	padding-block: var(--section-pad);
}

.section--services,
.section--product { background: var(--wpv-bg-2); }

.section__head {
	display: grid;
	gap: var(--space-m) var(--space-2xl);
	margin-bottom: var(--space-2xl);
}

@media (min-width: 900px) {
	.section__head {
		grid-template-columns: 1.1fr .9fr;
		align-items: end;
	}
	.section__lede { margin-bottom: var(--space-2xs); }
}

.section__title { margin-bottom: 0; }
.section__lede { color: var(--wpv-muted); max-width: 52ch; margin-bottom: 0; }
.section__more { margin: var(--space-xl) 0 0; }

.grid { display: grid; gap: var(--space-m); }

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

.split { display: grid; gap: var(--space-2xl); }
@media (min-width: 940px) {
	.split { grid-template-columns: 1fr .85fr; align-items: center; }
}

/* Ambient blobs + dot grid. Decorative, never interactive. */
.ambient {
	position: absolute;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
	z-index: 0;
}
.ambient span {
	position: absolute;
	border-radius: 50%;
	filter: blur(90px);
	opacity: .5;
}
.ambient__a {
	width: 46vw; height: 46vw;
	max-width: 620px; max-height: 620px;
	top: -14vw; right: -10vw;
	background: radial-gradient(circle, var(--wpv-accent-glow), transparent 68%);
}
.ambient__b {
	width: 40vw; height: 40vw;
	max-width: 540px; max-height: 540px;
	bottom: -16vw; left: -12vw;
	background: radial-gradient(circle, rgba(42, 45, 69, .85), transparent 70%);
}
.ambient--hero::after {
	content: "";
	position: absolute;
	inset: 0;
	background-image: radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1px);
	background-size: 26px 26px;
	mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, #000 20%, transparent 75%);
	opacity: .55;
}

.wrap, .section > .wrap { position: relative; z-index: 1; }

/* Hairline gradient rule between sections. */
.section--work::before,
.section--about::before,
.section--process::before,
.cta-band::before {
	content: "";
	position: absolute;
	top: 0; left: 50%;
	transform: translateX(-50%);
	width: min(100% - (var(--gutter) * 2), var(--container));
	height: 1px;
	background: linear-gradient(90deg, transparent, var(--wpv-accent-line) 50%, transparent);
}

/* --------------------------------------------------------------------------
   3. Motion primitives
   -------------------------------------------------------------------------- */

/* Reveals start visible. JS adds .js-motion to <html> and only then hides them,
   so a failed script leaves a fully readable page. */
.js-motion [data-reveal] {
	opacity: 0;
	transform: translateY(18px);
	transition: opacity var(--dur-reveal) var(--ease-out), transform var(--dur-reveal) var(--ease-out);
	transition-delay: var(--reveal-delay, 0ms);
}
.js-motion [data-reveal].is-in { opacity: 1; transform: none; }

.reveal-text .line { display: block; overflow: hidden; }
.reveal-text .line__in { display: block; }
.js-motion .reveal-text .line__in {
	transform: translateY(105%);
	transition: transform var(--dur-reveal) var(--ease-out);
	transition-delay: var(--line-delay, 0ms);
}
.js-motion .reveal-text.is-in .line__in { transform: none; }

@media (prefers-reduced-motion: reduce) {
	/* Final state immediately — not a faster animation. */
	.js-motion [data-reveal],
	.js-motion .reveal-text .line__in {
		opacity: 1 !important;
		transform: none !important;
		transition: none !important;
	}
}

/* Cursor-tracked highlight on card grids. */
[data-cursor] { position: relative; }
[data-cursor]::after {
	content: "";
	position: absolute;
	inset: -8px;
	border-radius: var(--radius-xl);
	background: radial-gradient(240px circle at var(--mx, 50%) var(--my, 50%), rgba(244, 123, 40, .10), transparent 60%);
	opacity: 0;
	transition: opacity var(--dur-hover) var(--ease-snap);
	pointer-events: none;
	z-index: 0;
}
[data-cursor].is-hovered::after { opacity: 1; }
[data-cursor] > * { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   4. Header & navigation
   -------------------------------------------------------------------------- */

.site-header {
	position: sticky;
	top: 0;
	z-index: 100;
	background: rgba(10, 14, 20, .72);
	backdrop-filter: blur(14px);
	-webkit-backdrop-filter: blur(14px);
	border-bottom: 1px solid transparent;
	transition: border-color var(--dur-hover) var(--ease-snap), background var(--dur-hover) var(--ease-snap);
}
.site-header.is-stuck { border-bottom-color: var(--wpv-line); background: rgba(10, 14, 20, .88); }

.site-header__inner {
	display: flex;
	align-items: center;
	gap: var(--space-m);
	min-height: var(--header-h);
}

/* The lockup is sized by height so the 640×200 artwork keeps its ratio and the
   header bar never grows with it. The live site's orange drop shadow is kept. */
.wordmark {
	display: inline-flex;
	align-items: center;
	text-decoration: none;
	margin-right: auto;
	min-height: 44px;
}
.wordmark__svg {
	height: 34px;
	width: auto;
	display: block;
	filter: drop-shadow(0 4px 14px rgba(244, 123, 40, .25));
}
@media (min-width: 640px) { .wordmark__svg { height: 36px; } }

.site-footer .wordmark { margin-right: 0; }
.site-footer .wordmark__svg { height: 42px; }

.nav { display: none; }
.nav__list {
	display: flex;
	align-items: center;
	gap: var(--space-m);
	list-style: none;
	margin: 0;
	padding: 0;
}
.nav__list a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding-inline: 2px;
	font-size: .96rem;
	color: var(--wpv-muted);
	text-decoration: none;
	transition: color var(--dur-micro) var(--ease-snap);
}
.nav__list a:hover,
.nav__list .current-menu-item > a { color: var(--wpv-text); }
.nav__list .current-menu-item > a { box-shadow: inset 0 -2px 0 var(--wpv-accent); }

.site-header__cta { display: none; }

.burger {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px; height: 44px;
	background: none;
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-s);
	cursor: pointer;
}

/* Declared after .burger so the hamburger is actually hidden on desktop —
   equal specificity, so source order decides. */
@media (min-width: 1080px) {
	.nav, .site-header__cta { display: block; }
	.burger { display: none; }
}
.burger__box { display: grid; gap: 5px; width: 18px; }
.burger__box span {
	height: 2px;
	background: var(--wpv-text);
	border-radius: 2px;
	transition: transform var(--dur-micro) var(--ease-snap), opacity var(--dur-micro);
}
.burger[aria-expanded="true"] .burger__box span:first-child { transform: translateY(3.5px) rotate(45deg); }
.burger[aria-expanded="true"] .burger__box span:last-child { transform: translateY(-3.5px) rotate(-45deg); }

.mobile-menu {
	position: fixed;
	inset: var(--header-h) 0 0;
	z-index: 99;
	background: var(--wpv-bg);
	overflow-y: auto;
	/* Hidden by the `hidden` attribute, so its links stay out of the tab order. */
}
.mobile-menu__inner {
	width: min(100% - (var(--gutter) * 2), var(--container));
	margin-inline: auto;
	padding-block: var(--space-xl);
}
.mobile-menu__list { list-style: none; margin: 0 0 var(--space-xl); padding: 0; }
.mobile-menu__list a {
	display: block;
	padding: 14px 0;
	font-family: var(--wpv-h);
	font-size: var(--step-2);
	text-decoration: none;
	border-bottom: 1px solid var(--wpv-line);
}
.mobile-menu__meta { margin-top: var(--space-xl); color: var(--wpv-muted); font-size: var(--step--1); }

/* --------------------------------------------------------------------------
   5. Buttons, chips, links
   -------------------------------------------------------------------------- */

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 48px;
	padding: 0 22px;
	border: 1px solid transparent;
	border-radius: var(--radius-full);
	font-weight: 600;
	font-size: .98rem;
	line-height: 1.2;
	white-space: nowrap;
	text-decoration: none;
	cursor: pointer;
	transition: transform var(--dur-hover) var(--ease-out), box-shadow var(--dur-hover) var(--ease-out),
		background var(--dur-hover) var(--ease-snap), border-color var(--dur-hover) var(--ease-snap);
	will-change: transform;
}
.btn--primary {
	background: linear-gradient(115deg, var(--wpv-accent), var(--wpv-accent-2));
	color: #fff;
	box-shadow: 0 10px 26px rgba(244, 123, 40, .28);
}
.btn--primary:hover { box-shadow: 0 16px 38px rgba(244, 123, 40, .38); }
.btn--ghost {
	border-color: var(--wpv-card-bd);
	background: var(--wpv-card);
	color: var(--wpv-text);
}
.btn--ghost:hover { border-color: var(--wpv-accent-line); background: var(--wpv-card-2); }
.btn--block { width: 100%; }
.btn__icon { width: 18px; height: 18px; transition: transform var(--dur-hover) var(--ease-out); }
.btn:hover .btn__icon { transform: translateX(3px); }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--space-s); }
@media (max-width: 600px) {
	.btn-row { display: grid; }
	.btn { width: 100%; }
}

.link-arrow {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-height: 44px;
	font-weight: 600;
	color: var(--wpv-accent);
	text-decoration: none;
}
.link-arrow svg { transition: transform var(--dur-hover) var(--ease-out); }
.link-arrow:hover svg { transform: translateX(4px); }

.chip {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 8px 14px;
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-full);
	background: var(--wpv-card);
	font-size: var(--step--1);
	color: var(--wpv-text);
}
.chip--sm { padding: 4px 10px; font-size: var(--step--2); color: var(--wpv-muted); }

.pill {
	display: inline-flex;
	align-items: center;
	padding: 5px 10px;
	border-radius: var(--radius-full);
	font-family: var(--wpv-m);
	font-size: var(--step--2);
	letter-spacing: .08em;
	text-transform: uppercase;
}
.pill--status {
	position: absolute;
	top: 12px; left: 12px;
	background: rgba(10, 14, 20, .82);
	border: 1px solid var(--wpv-accent-line);
	color: var(--wpv-accent);
	backdrop-filter: blur(6px);
}

.eyebrow {
	font-family: var(--wpv-m);
	font-size: var(--step--2);
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--wpv-accent);
	margin-bottom: var(--space-s);
}
.eyebrow span { opacity: .6; }

.icon { width: 22px; height: 22px; flex: none; }
.icon--xs { width: 16px; height: 16px; }

.ticks { list-style: none; padding: 0; display: grid; gap: 12px; }
.ticks li { display: grid; grid-template-columns: 20px 1fr; gap: 12px; align-items: start; }
.ticks svg { color: var(--wpv-accent); margin-top: 5px; }

/* --------------------------------------------------------------------------
   6. Hero
   -------------------------------------------------------------------------- */

.hero {
	position: relative;
	padding-block: calc(var(--section-y) * 1.2) var(--section-pad);
	background: linear-gradient(180deg, var(--wpv-bg) 0%, var(--wpv-bg-2) 100%);
}
/* Same rule as .wrap--prose: cap the line length, keep the left rail. */
.hero__inner > * { max-width: 900px; }

.hero__kicker {
	font-family: var(--wpv-m);
	font-size: var(--step--1);
	color: var(--wpv-muted);
	margin-bottom: var(--space-s);
}
.hero__eyebrow { margin-bottom: var(--space-m); }
.hero__title { margin-bottom: var(--space-s); }

.hi {
	background: linear-gradient(100deg, var(--wpv-accent), var(--wpv-accent-2));
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}
/* Forced-colours mode drops the gradient entirely — keep the words visible. */
@media (forced-colors: active) {
	.hi { color: CanvasText; background: none; }
}

.hero__type {
	font-family: var(--wpv-m);
	font-size: var(--step-1);
	color: var(--wpv-accent);
	margin-bottom: var(--space-m);
	min-height: 1.6em;
}
.type::after {
	content: "";
	display: inline-block;
	width: 2px; height: 1em;
	margin-left: 2px;
	background: currentColor;
	vertical-align: -.12em;
	animation: caret 1.05s steps(2, start) infinite;
}
@keyframes caret { to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
	.type::after { animation: none; }
}

.hero__sub {
	font-size: var(--step-1);
	color: var(--wpv-muted);
	max-width: 62ch;
	margin-bottom: var(--space-xl);
}

/* --------------------------------------------------------------------------
   7. Trust strip & marquee
   -------------------------------------------------------------------------- */

/* The chips now live in the hero; this band carries only the marquee strip, so
   it adds no padding of its own — the hero above and the section below each
   contribute --section-pad, keeping the strip centred in the gap. */
.trust { background: var(--wpv-bg-2); }
/* The chip row is a full-width band of its own, not body copy — it is exempt
   from the hero's 900px measure so the four chips stay on one line. */
.hero__inner > .trust__chips { max-width: none; margin-top: var(--space-xl); }
.trust__chips {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-xs);
	list-style: none;
	margin: 0;
	padding: 0;
}

.marquee {
	margin-top: 0;
	overflow: hidden;
	mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
	border-block: 1px solid var(--wpv-line);
	padding-block: var(--space-s);
}
.marquee__track {
	display: flex;
	gap: var(--space-2xl);
	width: max-content;
	animation: marquee 38s linear infinite;
}
.marquee__item {
	font-family: var(--wpv-m);
	font-size: var(--step--1);
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--wpv-muted);
	white-space: nowrap;
}
/* The track is rendered twice, so -50% is a seamless loop. */
@keyframes marquee { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
	.marquee__track { animation: none; }
}

/* --------------------------------------------------------------------------
   8. Cards
   -------------------------------------------------------------------------- */

.card {
	position: relative;
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-l);
	background: var(--wpv-card);
	overflow: hidden;
	transition: transform var(--dur-hover) var(--ease-out), border-color var(--dur-hover) var(--ease-snap),
		box-shadow var(--dur-hover) var(--ease-out);
}
.card:hover {
	transform: translateY(-4px);
	border-color: var(--wpv-accent-line);
	box-shadow: var(--shadow-glow);
}
.card__link {
	display: grid;
	gap: 0;
	height: 100%;
	text-decoration: none;
	color: inherit;
}
.card__frame {
	position: relative;
	display: block;
	aspect-ratio: 4 / 5;
	overflow: hidden;
	background: var(--wpv-bg-3);
}
.card__frame--wide { aspect-ratio: 16 / 9; }
.card__media { width: 100%; height: 100%; object-fit: cover; object-position: top center; transition: transform 600ms var(--ease-out); }
.card:hover .card__media { transform: scale(1.03); }
.card__media--fallback {
	display: grid;
	place-items: center;
	width: 100%; height: 100%;
	background: radial-gradient(120% 120% at 20% 0%, rgba(244, 123, 40, .18), transparent 60%), var(--wpv-bg-3);
	font-family: var(--wpv-h);
	font-size: 3rem;
	color: var(--wpv-accent);
}
.card__body { display: grid; gap: 10px; padding: var(--space-m); align-content: start; }
.card__meta {
	display: flex;
	justify-content: space-between;
	gap: var(--space-s);
	font-family: var(--wpv-m);
	font-size: var(--step--2);
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--wpv-muted);
}
.card__cat { color: var(--wpv-accent); }
.card__title { margin: 0; font-size: var(--step-2); }
.card__excerpt { color: var(--wpv-muted); font-size: var(--step--1); max-width: var(--measure-card); }
.card__stack { display: flex; flex-wrap: wrap; gap: 6px; }
.card__cta {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	margin-top: 6px;
	font-weight: 600;
	font-size: var(--step--1);
	color: var(--wpv-accent);
}
.card__cta svg { transition: transform var(--dur-hover) var(--ease-out); }
.card:hover .card__cta svg { transform: translateX(4px); }

.card--service .card__link,
.card--feature { padding: var(--space-l) var(--space-m); }
.card--service .card__link { gap: 12px; align-content: start; }
.card--feature { display: grid; gap: 12px; align-content: start; }
.card--feature .card__excerpt,
.card--feature .card__title { margin: 0; }

.card__icon {
	display: grid;
	place-items: center;
	width: 44px; height: 44px;
	border-radius: var(--radius-m);
	background: var(--wpv-accent-soft);
	color: var(--wpv-accent);
	margin-bottom: 4px;
}

/* --------------------------------------------------------------------------
   9. Steps
   -------------------------------------------------------------------------- */

.steps {
	display: grid;
	gap: var(--space-m);
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 700px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1060px) { .steps { grid-template-columns: repeat(4, 1fr); } }

.step {
	padding: var(--space-l) var(--space-m);
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-l);
	background: var(--wpv-card);
}
.step__no {
	display: block;
	font-family: var(--wpv-m);
	font-size: var(--step--1);
	color: var(--wpv-accent);
	margin-bottom: var(--space-s);
}
.step__title { font-size: var(--step-2); margin-bottom: 8px; }
.step p { color: var(--wpv-muted); font-size: var(--step--1); margin: 0; }

/* --------------------------------------------------------------------------
   10. Code frame (about teaser)
   -------------------------------------------------------------------------- */

.codeframe {
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-l);
	background: var(--wpv-bg-3);
	overflow: hidden;
	box-shadow: var(--shadow-m);
}
.codeframe__bar {
	display: flex;
	gap: 6px;
	padding: 12px var(--space-s);
	border-bottom: 1px solid var(--wpv-line);
	background: rgba(255, 255, 255, .03);
}
.codeframe__bar i { width: 10px; height: 10px; border-radius: 50%; background: var(--wpv-line); }
.codeframe__bar i:first-child { background: var(--wpv-accent); }
.codeframe__code {
	margin: 0;
	padding: var(--space-m);
	overflow-x: auto;
	font-family: var(--wpv-m);
	font-size: .82rem;
	line-height: 1.75;
	color: var(--wpv-muted);
}
.codeframe__code b { color: var(--wpv-accent); font-weight: 600; }
.codeframe__note { margin-top: var(--space-s); font-size: var(--step--1); color: var(--wpv-muted); }

/* --------------------------------------------------------------------------
   11. Contact
   -------------------------------------------------------------------------- */

.contact {
	display: grid;
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-xl);
	overflow: hidden;
	background: var(--wpv-bg-2);
}
@media (min-width: 960px) { .contact { grid-template-columns: .9fr 1.1fr; } }

.contact__info { padding: clamp(24px, 4vw, 48px); }
.contact__title { font-size: var(--step-3); }
.contact__lede { color: var(--wpv-muted); max-width: 46ch; }
.contact__list { list-style: none; padding: 0; margin: var(--space-xl) 0 0; display: grid; gap: var(--space-m); }
.contact__list li { display: grid; grid-template-columns: 22px 1fr; gap: var(--space-s); align-items: start; }
.contact__list svg { color: var(--wpv-accent); margin-top: 3px; }
.contact__list strong { display: block; font-size: var(--step--2); letter-spacing: .1em; text-transform: uppercase; color: var(--wpv-muted); font-weight: 500; }
.contact__list a { text-decoration: none; }
.contact__list a:hover { color: var(--wpv-accent); }
.contact__note { margin: var(--space-xl) 0 0; font-size: var(--step--1); color: var(--wpv-muted); }

/* The form panel sits on the site's own dark ground, raised one step off the
   details panel and split from it by a hairline. Orange is contrast-safe here
   (7.10:1 on the dark surface), so --wpv-accent-deep is not needed — it stays
   in the tokens for any genuinely light surface added later. */
.contact__panel {
	padding: clamp(24px, 4vw, 44px);
	background: var(--wpv-bg-3);
	color: var(--wpv-text);
	border-top: 1px solid var(--wpv-card-bd);
}
@media (min-width: 960px) {
	.contact__panel { border-top: 0; border-left: 1px solid var(--wpv-card-bd); }
}

.form__row { display: grid; gap: var(--space-s); }
@media (min-width: 560px) { .form__row { grid-template-columns: 1fr 1fr; } }

.field { display: grid; gap: 6px; margin-bottom: var(--space-s); }
.field label { font-size: var(--step--1); font-weight: 600; color: var(--wpv-text); }
.req { color: var(--wpv-accent); }

.field input,
.field select,
.field textarea {
	width: 100%;
	min-height: 46px;
	padding: 11px 14px;
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-m);
	background: var(--wpv-card);
	color: var(--wpv-text);
	font: inherit;
	font-size: var(--step--1);
	transition: border-color var(--dur-micro) var(--ease-snap), background var(--dur-micro) var(--ease-snap);
}
.field input::placeholder,
.field textarea::placeholder { color: var(--wpv-muted); }
.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--wpv-accent-line); }
.field textarea { min-height: 120px; resize: vertical; }
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
	outline: 2px solid var(--wpv-accent);
	outline-offset: 1px;
	background: var(--wpv-card-2);
}
/* The native select arrow renders black-on-black in a dark field on Chrome,
   so the control draws its own. */
.field select {
	appearance: none;
	background-image: linear-gradient(45deg, transparent 50%, var(--wpv-muted) 50%), linear-gradient(135deg, var(--wpv-muted) 50%, transparent 50%);
	background-position: right 18px center, right 12px center;
	background-size: 6px 6px, 6px 6px;
	background-repeat: no-repeat;
	padding-right: 38px;
}
.field select option { background: var(--wpv-bg-2); color: var(--wpv-text); }

.form__hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form__actions { margin: var(--space-m) 0 0; display: grid; gap: 10px; }
.form__fine { font-size: var(--step--2); color: var(--wpv-muted); }

.notice {
	padding: 14px 16px;
	border-radius: var(--radius-m);
	margin-bottom: var(--space-m);
	font-size: var(--step--1);
	border: 1px solid;
}
/* Measured on --wpv-bg-3: #6EE7A8 is 9.9:1, #FFAE7A is 8.4:1. */
.notice--ok { background: rgba(52, 199, 123, .10); border-color: rgba(52, 199, 123, .38); color: #6EE7A8; }
.notice--err { background: rgba(244, 123, 40, .10); border-color: var(--wpv-accent-line); color: #FFAE7A; }

/* Short viewports tighten the fields instead of clipping the form — a 1080p
   screen at 125% scaling is ~758 CSS px, and the Send button must stay visible. */
@media (min-width: 960px) {
	.contact--sticky .contact__panel { position: sticky; top: calc(var(--header-h) + 8px); align-self: start; }
}
@media (min-width: 960px) and (max-height: 820px) {
	.contact--sticky .field { margin-bottom: 10px; }
	.contact--sticky .field input,
	.contact--sticky .contact--sticky .field select { min-height: 42px; padding-block: 9px; }
	.contact--sticky .field textarea { min-height: 92px; }
}

/* --------------------------------------------------------------------------
   12. CTA band & footer
   -------------------------------------------------------------------------- */

.cta-band { text-align: center; }
.cta-band__inner { max-width: 44ch; margin-inline: auto; }
.cta-band__title { font-size: var(--step-3); }
.cta-band__sub { color: var(--wpv-muted); margin-bottom: var(--space-xl); }
.cta-band .btn-row { justify-content: center; }

.site-footer {
	background: var(--wpv-bg-2);
	border-top: 1px solid var(--wpv-line);
	padding-top: var(--space-3xl);
}
.site-footer__grid { display: grid; gap: var(--space-xl); }
@media (min-width: 760px) { .site-footer__grid { grid-template-columns: 1.4fr 1fr 1fr; } }
@media (min-width: 1060px) { .site-footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1.1fr; } }

.site-footer__blurb { margin-top: var(--space-s); color: var(--wpv-muted); font-size: var(--step--1); max-width: 42ch; }
.site-footer__badge {
	font-family: var(--wpv-m);
	font-size: var(--step--2);
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--wpv-accent);
	margin: 0;
}
.site-footer__title {
	font-size: var(--step--2);
	font-family: var(--wpv-m);
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--wpv-muted);
	margin-bottom: var(--space-s);
}
.site-footer__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.site-footer__list a,
.site-footer__list li.is-plain {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-height: 44px;
	font-size: var(--step--1);
	color: var(--wpv-muted);
	text-decoration: none;
}
.site-footer__list a:hover { color: var(--wpv-text); }
.site-footer__list svg { color: var(--wpv-accent); }

.site-footer__bar {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: center;
	gap: var(--space-s);
	margin-top: var(--space-2xl);
	padding-block: var(--space-m);
	border-top: 1px solid var(--wpv-line);
	font-size: var(--step--1);
	color: var(--wpv-muted);
}
.site-footer__bar p { margin: 0; }
.site-footer__legal { display: flex; gap: var(--space-m); list-style: none; margin: 0; padding: 0; }
.site-footer__legal a { text-decoration: none; }
.site-footer__legal a:hover { color: var(--wpv-text); }

/* --------------------------------------------------------------------------
   13. Page furniture: breadcrumbs, page hero, prose
   -------------------------------------------------------------------------- */

.crumbs { border-bottom: 1px solid var(--wpv-line); background: var(--wpv-bg-2); }
.crumbs ol {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	list-style: none;
	margin: 0;
	padding: 10px 0;
	font-size: var(--step--2);
	color: var(--wpv-muted);
}
.crumbs li + li::before { content: "/"; margin-right: 8px; opacity: .5; }
.crumbs a { text-decoration: none; }
.crumbs a:hover { color: var(--wpv-accent); }

.page-hero {
	position: relative;
	padding-block: var(--section-y) var(--section-pad);
	background: linear-gradient(180deg, var(--wpv-bg) 0%, var(--wpv-bg-2) 100%);
}
.page-hero__inner > * { max-width: 62ch; }
.page-hero__lede { font-size: var(--step-1); color: var(--wpv-muted); margin-bottom: 0; }

.prose { max-width: var(--measure); }
.prose > * + * { margin-top: var(--space-m); }
.prose h2 { font-size: var(--step-3); margin-top: var(--space-2xl); }
.prose h3 { font-size: var(--step-2); margin-top: var(--space-xl); }
.prose ul, .prose ol { color: var(--wpv-muted); }
.prose li + li { margin-top: 8px; }
.prose p { color: var(--wpv-muted); }
.prose strong { color: var(--wpv-text); }
.prose a { color: var(--wpv-accent); }
.prose blockquote {
	margin: var(--space-xl) 0;
	padding: var(--space-m) var(--space-l);
	border-left: 2px solid var(--wpv-accent);
	background: var(--wpv-card);
	border-radius: 0 var(--radius-m) var(--radius-m) 0;
	font-size: var(--step-1);
}
.prose code {
	font-family: var(--wpv-m);
	font-size: .86em;
	padding: 2px 6px;
	border-radius: var(--radius-s);
	background: var(--wpv-card-2);
	color: var(--wpv-accent-2);
}
.prose pre {
	overflow-x: auto;
	padding: var(--space-m);
	border-radius: var(--radius-m);
	background: var(--wpv-bg-3);
	border: 1px solid var(--wpv-card-bd);
}
.prose pre code { background: none; padding: 0; color: var(--wpv-muted); }
.prose img { border-radius: var(--radius-m); }
.prose table { width: 100%; border-collapse: collapse; font-size: var(--step--1); }
.prose th, .prose td { padding: 10px 12px; border-bottom: 1px solid var(--wpv-line); text-align: left; }

/* --------------------------------------------------------------------------
   14. Work archive & filter
   -------------------------------------------------------------------------- */

.filters {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-bottom: var(--space-xl);
	padding: 0;
	list-style: none;
}
.filter {
	min-height: 44px;
	padding: 8px 18px;
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-full);
	background: var(--wpv-card);
	color: var(--wpv-muted);
	font-size: var(--step--1);
	cursor: pointer;
	transition: color var(--dur-micro), border-color var(--dur-micro), background var(--dur-micro);
}
.filter:hover { color: var(--wpv-text); border-color: var(--wpv-accent-line); }
.filter[aria-pressed="true"] {
	background: var(--wpv-accent-soft);
	border-color: var(--wpv-accent);
	color: var(--wpv-accent);
}
.card.is-filtering { opacity: 0; transform: translateY(10px); }
.card { transition-property: transform, border-color, box-shadow, opacity; }

.load-more { display: grid; justify-content: center; margin-top: var(--space-2xl); }
.archive-count { font-size: var(--step--1); color: var(--wpv-muted); margin-bottom: var(--space-s); }

/* --------------------------------------------------------------------------
   15. Case study
   -------------------------------------------------------------------------- */

.case-hero { position: relative; padding-block: var(--section-y) var(--section-pad); }
.case-hero__plate {
	margin-top: var(--space-xl);
	border-radius: var(--radius-xl);
	overflow: hidden;
	border: 1px solid var(--wpv-card-bd);
	box-shadow: var(--shadow-l);
}
.case-hero__plate img { width: 100%; }

.facts {
	display: grid;
	gap: var(--space-m);
	margin: var(--space-2xl) 0 0;
	padding: var(--space-l) 0;
	border-block: 1px solid var(--wpv-line);
}
@media (min-width: 720px) { .facts { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1040px) { .facts { grid-template-columns: repeat(5, 1fr); } }
.fact dt {
	font-family: var(--wpv-m);
	font-size: var(--step--2);
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--wpv-muted);
	margin-bottom: 6px;
}
.fact dd { margin: 0; font-size: var(--step--1); }
.fact a { display: inline-flex; align-items: center; gap: 6px; color: var(--wpv-accent); text-decoration: none; }

.case-note {
	margin-top: var(--space-m);
	font-family: var(--wpv-m);
	font-size: var(--step--1);
	color: var(--wpv-muted);
}

.case-body { display: grid; gap: var(--space-2xl); margin-top: var(--space-2xl); }
@media (min-width: 1000px) { .case-body { grid-template-columns: 1fr .8fr; align-items: start; } }
/* No gallery, no second column — a lone text column in a two-column grid reads
   as a missing element rather than as space. */
.case-body--solo { grid-template-columns: 1fr !important; }
.case-body--solo .case-body__text { max-width: var(--measure); }
.next-project__title { display: block; }
.next-project a > span { display: grid; gap: 4px; }
.case-body__media { position: sticky; top: calc(var(--header-h) + 24px); display: grid; gap: var(--space-m); }
.case-body__media img { border-radius: var(--radius-l); border: 1px solid var(--wpv-card-bd); }

.metrics { display: grid; gap: var(--space-m); margin: var(--space-xl) 0; padding: 0; list-style: none; }
@media (min-width: 620px) { .metrics { grid-template-columns: repeat(3, 1fr); } }
.metric { text-align: center; padding: var(--space-m); border: 1px solid var(--wpv-card-bd); border-radius: var(--radius-l); background: var(--wpv-card); }
.metric__value {
	display: block;
	font-family: var(--wpv-h);
	font-size: var(--step-3);
	color: var(--wpv-accent);
	font-variant-numeric: tabular-nums;
}
.metric__label { display: block; min-height: 2.6em; font-size: var(--step--1); color: var(--wpv-muted); margin-top: 6px; }

.next-project { border-top: 1px solid var(--wpv-line); margin-top: var(--section-y); padding-top: var(--space-2xl); }
.next-project__label { font-family: var(--wpv-m); font-size: var(--step--2); letter-spacing: .14em; text-transform: uppercase; color: var(--wpv-muted); }
.next-project__title { font-size: var(--step-3); margin: 8px 0 0; }
.next-project a { text-decoration: none; display: inline-flex; align-items: center; gap: 12px; }
.next-project a:hover .next-project__title { color: var(--wpv-accent); }

/* --------------------------------------------------------------------------
   16. Service page
   -------------------------------------------------------------------------- */

.deliverables { display: grid; gap: var(--space-s); list-style: none; padding: 0; }
@media (min-width: 760px) { .deliverables { grid-template-columns: repeat(2, 1fr); } }
.deliverables li {
	display: grid;
	grid-template-columns: 20px 1fr;
	gap: 12px;
	padding: var(--space-s) var(--space-m);
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-m);
	background: var(--wpv-card);
	font-size: var(--step--1);
}
.deliverables svg { color: var(--wpv-accent); margin-top: 4px; }

.faq { display: grid; gap: 8px; }
.faq__item { border: 1px solid var(--wpv-card-bd); border-radius: var(--radius-m); background: var(--wpv-card); overflow: hidden; }
.faq__q {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: var(--space-s);
	width: 100%;
	min-height: 56px;
	padding: var(--space-s) var(--space-m);
	background: none;
	border: 0;
	text-align: left;
	font-family: var(--wpv-h);
	font-size: var(--step-1);
	cursor: pointer;
}
.faq__q::after { content: "+"; font-family: var(--wpv-m); color: var(--wpv-accent); font-size: 1.3em; line-height: 1; }
.faq__q[aria-expanded="true"]::after { content: "–"; }
.faq__a { padding: 0 var(--space-m) var(--space-m); color: var(--wpv-muted); font-size: var(--step--1); }

/* --------------------------------------------------------------------------
   17. Process rail
   -------------------------------------------------------------------------- */

.rail { display: grid; gap: var(--space-2xl); }
@media (min-width: 1000px) { .rail { grid-template-columns: 260px 1fr; align-items: start; } }
.rail__nav { position: sticky; top: calc(var(--header-h) + 24px); display: grid; gap: 4px; }
.rail__nav a {
	display: flex;
	gap: 12px;
	align-items: baseline;
	min-height: 44px;
	padding: 8px 12px;
	border-left: 2px solid var(--wpv-line);
	color: var(--wpv-muted);
	text-decoration: none;
	font-size: var(--step--1);
	transition: color var(--dur-micro), border-color var(--dur-micro);
}
.rail__nav a.is-active { color: var(--wpv-text); border-left-color: var(--wpv-accent); }
.rail__nav i { font-family: var(--wpv-m); font-style: normal; color: var(--wpv-accent); }
.rail__step + .rail__step { margin-top: var(--space-2xl); padding-top: var(--space-2xl); border-top: 1px solid var(--wpv-line); }
.rail__step h2 { font-size: var(--step-3); }

/* --------------------------------------------------------------------------
   18. Blog, search, 404
   -------------------------------------------------------------------------- */

.post-hero { padding-block: var(--section-y) var(--section-pad); }
.post-hero__meta { display: flex; flex-wrap: wrap; gap: var(--space-s); font-family: var(--wpv-m); font-size: var(--step--2); letter-spacing: .1em; text-transform: uppercase; color: var(--wpv-muted); }
.post-hero__image { margin-top: var(--space-xl); border-radius: var(--radius-l); overflow: hidden; border: 1px solid var(--wpv-card-bd); }

.empty { padding: var(--space-2xl) 0; color: var(--wpv-muted); }

.search-form { display: flex; gap: 8px; max-width: 460px; }
.search-form input[type="search"] {
	flex: 1;
	min-height: 48px;
	padding: 0 16px;
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-full);
	background: var(--wpv-card);
	color: var(--wpv-text);
	font: inherit;
}

.pagination { display: flex; flex-wrap: wrap; gap: 8px; margin-top: var(--space-2xl); }
.pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 44px; min-height: 44px;
	padding: 0 12px;
	border: 1px solid var(--wpv-card-bd);
	border-radius: var(--radius-s);
	text-decoration: none;
	color: var(--wpv-muted);
}
.pagination .page-numbers.current,
.pagination .page-numbers:hover { color: var(--wpv-accent); border-color: var(--wpv-accent-line); }

/* --------------------------------------------------------------------------
   19. QA corrections (measured pass, 390 / 768 / 1440 / 1536×758)
   -------------------------------------------------------------------------- */

/* Inner page headings run one step below the home hero. At hero scale the
   authored two-line headings wrapped to three lines from 390px right up to
   1536px — measured, not guessed. */
.page-hero h1,
.case-hero h1,
.post-hero h1 { font-size: var(--step-4); }

/* Breadcrumb and legal links were 32×14 and 40×16 — under the 44px target.
   The bar grows to fit rather than the text shrinking. */
.crumbs ol { padding: 0; }
.crumbs li { display: flex; align-items: center; }
.crumbs a,
.crumbs span[aria-current] {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding-inline: 2px;
}
.site-footer__legal a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
}

/* --------------------------------------------------------------------------
   20. Uploaded logo
   The real wpvelocity.dev mark, recoloured for a dark ground (the navy
   wordmark is invisible on --wpv-bg). Sized by height so the aspect ratio
   holds and the header bar never grows.
   -------------------------------------------------------------------------- */

.custom-logo-link {
	display: inline-flex;
	align-items: center;
	margin-right: auto;
	min-height: 44px;
}
.custom-logo {
	height: 34px;
	width: auto;
	max-width: min(58vw, 240px);
	object-fit: contain;
}
@media (min-width: 640px) { .custom-logo { height: 38px; } }

.site-footer .custom-logo { height: 42px; }
.site-footer .custom-logo-link { margin-right: 0; }

/* --------------------------------------------------------------------------
   21. Rhythm corrections (measured content-box gaps, 1440px)
   -------------------------------------------------------------------------- */

/* Layout lists are components, not prose: the inherited `ul/ol` bottom margin
   was leaking into the gap below their section and turning a 129px join into
   153px. */
.deliverables,
.steps,
.metrics,
.ticks,
.filters,
.faq,
.trust__chips,
.contact__list,
.site-footer__list,
.site-footer__legal,
.mobile-menu__list,
.crumbs ol,
.pagination { margin-bottom: 0; }

/* Nothing should push past the end of its own section. */
.prose > *:last-child,
.section__head > * > *:last-child,
.case-body__text > *:last-child { margin-bottom: 0; }

/* The next-project block already sits in its own padded section — the extra
   section-sized margin on top of that made its join measure 221px. The rule
   and the space under it stay; the duplicated gap goes. */
.next-project { margin-top: 0; }

/* The first element in a block never carries a top margin — `.prose h2` adds
   --space-2xl between headings, and on a block that opens with a heading that
   margin was pushing the whole column 64px down inside its section. */
.prose > :first-child,
.case-body__text > :first-child { margin-top: 0; }

/* The case-study body column is spaced by its section, not by its own margin. */
.case-body { margin-top: 0; }
.case-note { margin-bottom: 0; }

/* The 16:9 plate only needs separating from the copy above it inside the case
   hero; on the ClientHunter page it is the first thing in its own section. */
.case-hero__plate { margin-top: 0; }
.case-hero .case-hero__plate { margin-top: var(--space-xl); }


/* A button row that follows a block of content needs the same separation the
   `View all work` link gets — without it the CTA sits flush against the card
   grid above it. The hero is excluded: its paragraph already carries the gap. */
.grid + .btn-row,
.cards + .btn-row,
.steps + .btn-row,
.deliverables + .btn-row,
.page-hero__inner .btn-row { margin-top: var(--space-xl); }

/* --------------------------------------------------------------------------
   22. Background textures
   Free Pexels stock, duotoned into the palette (build-assets/make-textures.sh)
   and credited in build-assets/IMAGE-CREDITS.md. Abstract texture and light
   only — no people, nothing that could read as client work.

   The layer paints over the section's own background but under .wrap, which is
   already z-index 1, and it is masked so it fades away from the text column.
   Opacity is per slot: enough to feel, never enough to fight the copy.
   -------------------------------------------------------------------------- */

.has-texture { position: relative; overflow: hidden; }
.has-texture::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	background-image: var(--texture);
	background-size: cover;
	background-position: var(--texture-pos, center);
	opacity: var(--texture-opacity, .26);
	pointer-events: none;
	/* Fade toward the left, where the copy lives. */
	-webkit-mask-image: linear-gradient(96deg, transparent 44%, #000 90%);
	mask-image: linear-gradient(96deg, transparent 44%, #000 90%);
}

/* Slots. Desktop art; the 800px variants are swapped in below. */
.hero { --texture: url("../img/hero-1600.webp"); --texture-opacity: .24; --texture-pos: right center; }
.archive-hero { --texture: url("../img/work-1600.webp"); --texture-opacity: .24; }
.section--services { --texture: url("../img/tech-1600.webp"); --texture-opacity: .14; }
.section--product { --texture: url("../img/about-1600.webp"); --texture-opacity: .12; }
.section--process-media { --texture: url("../img/process-1600.webp"); --texture-opacity: .16; }
.section--contact { --texture: url("../img/contact-1600.webp"); --texture-opacity: .16; }
.cta-band { --texture: url("../img/cta-1600.webp"); --texture-opacity: .26; --texture-pos: center bottom; }

@media (max-width: 700px) {
	.hero { --texture: url("../img/hero-800.webp"); }
	.archive-hero { --texture: url("../img/work-800.webp"); }
	.section--services { --texture: url("../img/tech-800.webp"); }
	.section--product { --texture: url("../img/about-800.webp"); }
	.section--process-media { --texture: url("../img/process-800.webp"); }
	.section--contact { --texture: url("../img/contact-800.webp"); }
	.cta-band { --texture: url("../img/cta-800.webp"); }
	/* On a phone the copy fills the width, so the texture reads as noise behind
	   it — pull it back and fade from the top instead of the side. */
	.has-texture::after {
		opacity: calc(var(--texture-opacity) * .6);
		-webkit-mask-image: linear-gradient(180deg, #000, transparent 70%);
		mask-image: linear-gradient(180deg, #000, transparent 70%);
	}
}

/* A visible plate, used where a section has room for one. */
.plate {
	border-radius: var(--radius-l);
	overflow: hidden;
	border: 1px solid var(--wpv-card-bd);
	box-shadow: var(--shadow-m);
	/* <figure> ships with margin-inline: 40px, which pushed the plate off the
	   left rail its sibling card sits on. */
	margin: 0 0 var(--space-m);
}
.plate img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; display: block; }

/* Cards sit on the texture, so they need a more opaque ground than the
   translucent default or the circuitry reads straight through the copy. */
.has-texture .card,
.has-texture .step,
.has-texture .contact { background: rgba(15, 20, 28, .86); }

/* On a phone the hero texture is the largest painted element, which makes it
   the LCP candidate and pushed the work archive to 2.1s. At 390px it is also
   barely visible (its opacity is already scaled to .6 there), so it is simply
   not painted — the headline becomes the LCP element again. Sections further
   down keep theirs: they are below the fold and cost nothing at first paint. */
@media (max-width: 700px) {
	.hero::after,
	.archive-hero::after { content: none; }
}

/* A portrait is a fixed-size object, not a fluid one: left to fill its column it
   rendered 728×808 once the layout collapsed to a single column, which swamped
   the copy beside it. It gets a hard ceiling and a 4:5 frame cropped from the
   top, so the face sits where it should at every width. */
.plate--portrait {
	position: relative;
	max-width: 340px;
	margin-bottom: var(--space-m);
}

/* In the two-column layout the portrait fills its column, matching the card
   below it — capped, it left a dead strip down the right of the aside. The cap
   only exists for the stacked layout, where the column is the full page width
   and an uncapped portrait swamps the copy. */
@media (min-width: 940px) {
	.plate--portrait { max-width: none; }
}
.plate--portrait img {
	aspect-ratio: 4 / 5;
	width: 100%;
	height: auto;
	object-fit: cover;
	object-position: center top;
}
.plate__caption {
	position: absolute;
	inset: auto 0 0 0;
	display: grid;
	gap: 2px;
	padding: var(--space-l) var(--space-m) var(--space-s);
	background: linear-gradient(180deg, transparent, rgba(10, 14, 20, .92) 62%);
	font-family: var(--wpv-h);
	font-weight: 700;
	font-size: var(--step-1);
	color: var(--wpv-text);
}
.plate__caption span {
	font-family: var(--wpv-m);
	font-weight: 400;
	font-size: var(--step--2);
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--wpv-muted);
}
