/**
 * Shared components — the parts of the master that recur across templates.
 *
 * Introduced with How we help, because that is the first view to use them, but
 * they belong to every later template: the display type, the lead and copy
 * roles, the two buttons, the chip, the section head and the funnel CTA.
 *
 * Measured on the rendered master at 390 / 820 / 1366 / 1600, and at the band
 * edges 651 / 1100 / 1101 / 1500 / 1501 / 1920 wherever a value turned out to
 * be fluid. One ascending media-query set at 651 / 1101 / 1501; every laptop
 * override is reset explicitly at 1501.
 */

/* ----------------------------------------------------------------- type */
/*
 * The eyebrow, one style site wide (Ben, 6 Oct 2026): the Home hero's. Inter 800, 13.2px, uppercase, 0.18em tracking, 18px above the title, at every width. It was 16.32px sentence
 * case from 2 Oct 2026 (before that Montserrat 800 uppercase, then Title Case). The text is still stored as written; the capitals are CSS only, so a screen reader reads it as written.
 * Card eyebrows are the same style (Ben, 7 Oct 2026): the small label above a card's title (`.chip`, `.client`, `.num`, the journey, motion, about and contact labels) takes
 * `--fs-eyebrow-label` and `--ls-eyebrow-label` from tokens.css, so there is one eyebrow size, 13.2px, in a title block and on a card. Links, buttons, filters, form labels, breadcrumbs, pills and
 * metadata rows are different roles and keep their own sizes. Colour stays with each surface.
 *
 * Copy is one size too (Ben, 7 Oct 2026): every paragraph, card description, lead and list text is `--fs-body`, 17.8px, at every width. Titles, quotes, buttons and labels are not copy. A new
 * copy rule takes `var(--fs-body)`, never a pixel size; `docs/verification/type-rules-check.py` fails on anything else.
 */
.eyebrow {
	display: flex;
	align-items: center;
	gap: 0;
	margin-bottom: 18px;
	font-family: var(--font-body);
	font-size: var(--fs-eyebrow-label);
	line-height: 1.1;
	font-weight: 800;
	letter-spacing: var(--ls-eyebrow-label);
	text-transform: uppercase;
	color: var(--ink);
}

/*
 * Fluid inside the tablet and laptop bands, fixed outside them. Verified at
 * 390 / 651 / 820 / 1100 / 1101 / 1200 / 1366 / 1500 / 1501 / 1600 / 1920:
 * 48 · clamp(48, 6vw, 66) · clamp(43, 3.8vw, 57) · 88. The band edges step
 * rather than run continuously, which is the master's own behaviour.
 */
.display-sm {
	margin: 0;
	font-size: 48px;
	line-height: 0.97;
	font-weight: var(--fw-title);
	letter-spacing: var(--tr-display);
}

/*
 * The orange display split (Ben, 21 Sep 2026) -- an approved Form device, and
 * now a site-wide rule rather than a handful of pages. On a light ground the
 * latter semantic half of a major display heading is orange.
 *
 * The split is always an explicit `<em>` written into the heading's own text,
 * so the wording stays controlled. Nothing here colours "the last half of a
 * string", and nothing infers where a sentence divides.
 */
h1 em,
h2 em,
.display-sm em {
	font-style: normal;
	color: var(--orange-text);
}

/*
 * ...and never on the grounds Ben excluded, where orange either disappears or
 * loses contrast: the yellow/orange/red gradient CTA, the dark testimonial and
 * feature panels, and any heading laid over imagery. No `<em>` is written into
 * those headings in the first place -- this is the second line of defence, so
 * that adding one later cannot quietly produce unreadable type.
 */
.funnel em,
.funnel-cta em,
.testimonial-carousel em,
.quote-panel em,
figure em,
[class*="hero-media"] em,
[class*="cover"] em {
	color: inherit;
}

.lead {
	max-width: 780px;
	margin: 1em 0;
	font-size: var(--fs-body);
	line-height: 1.36;
	font-weight: var(--fw-body);
	color: var(--ink);
}

.copy {
	max-width: 720px;
	margin: 1em 0;
	font-size: var(--fs-body);
	line-height: 1.7;
	color: var(--copy-ink);
}

/* -------------------------------------------------------------- buttons */
.btn-row {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	/* The master's base. Every hero and the funnel override it; the Work
	   detail hero is the one place that takes it as it stands. */
	margin-top: 30px;
}

/*
 * 28_ §6's button: 14px 20px, radius 16, and 48px tall at every width.
 *
 * `min-height` is what makes it 48, not the line box, and the 12px gap spaces
 * a label from its arrow. An earlier reading reproduced the 48 with an
 * invented `line-height: 18px`: the height matched, so it passed, but the
 * master sets no line-height at all and the button's internals were wrong
 * wherever a second child appeared. Found when the route sweep started
 * recording `gap`, `min-height` and `display`.
 */
.btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 12px;
	min-height: 48px;
	padding: 14px 20px;
	border: 1px solid var(--ink);
	border-radius: var(--r-button);
	background: transparent;
	font-size: var(--fs-button);
	/* A <button> does not inherit the document font. Every .btn so far has
	   been an anchor, so this went unnoticed until the Work index's Load more
	   rendered 13px narrower in the UA's own font at identical metrics. */
	font-family: inherit;
	font-weight: var(--fw-eyebrow);
	letter-spacing: var(--tr-button);
	color: var(--ink);
	text-decoration: none;
	transition: 0.22s;
}

.btn-dark {
	background: var(--ink);
	color: var(--white);
}

.btn:hover,
.btn:focus-visible {
	transform: translateY(-2px);
	box-shadow: var(--btn-shadow-hover);
}

/* ------------------------------------------------------ action chevron */
/*
 * The one CTA / action arrow (Ben, 29 Sep 2026; Dave's list, item 2): the
 * Homepage tile's `›`, drawn here so it is identical wherever it is used.
 * Markup comes from form_theme_chevron() / form_theme_cta_label() in
 * functions.php, which turn the `→`, `↓` and `←` that copy has always carried
 * into this. The glyph is Arial 300 by token — the brand faces' Latin subsets
 * have no chevron — in the colour of its label, and nudges 3px the way it
 * points on hover and keyboard focus of the link or button it sits in.
 *
 * It moves with `translate`, not `transform`, so a caller that positions it
 * with a transform (the Home tile centres its chevron with translateY(-50%))
 * keeps doing so. With prefers-reduced-motion the nudge is not applied.
 *
 * In a flex container the container's own gap spaces it; inline, the margin
 * does.
 */
.cta-chevron {
	display: inline-block;
	flex: none;
	vertical-align: middle;
	color: inherit;
	translate: 0 0;
	transition: translate var(--chevron-duration);
}

/*
 * Its type is stated at two classes' weight. The chevron is a <span>, and a
 * family's `.card span { font-size: 9.5px; text-transform: uppercase }` written
 * for its eyebrow would otherwise reach it and shrink it to a speck (found on
 * the challenge cards, the proof-card circle and the Contact link).
 */
.cta-chevron.cta-chevron {
	font-family: var(--chevron-font);
	font-size: var(--chevron-size);
	font-style: normal;
	font-weight: var(--chevron-weight);
	line-height: var(--chevron-line);
	letter-spacing: 0;
	text-transform: none;
}

.cta-chevron::before {
	content: "\203a";
}

.cta-chevron--right,
.cta-chevron--down {
	margin-inline-start: 0.3em;
}

.cta-chevron--left {
	margin-inline-end: 0.3em;
	rotate: 180deg;
}

.cta-chevron--down {
	rotate: 90deg;
}

:is(a, button):is(:hover, :focus-visible) .cta-chevron--right {
	translate: var(--chevron-nudge) 0;
}

:is(a, button):is(:hover, :focus-visible) .cta-chevron--down {
	translate: 0 var(--chevron-nudge);
}

:is(a, button):is(:hover, :focus-visible) .cta-chevron--left {
	translate: calc(var(--chevron-nudge) * -1) 0;
}

.btn .cta-chevron,
.section-action .cta-chevron,
.journey-next-action .cta-chevron,
.mobile-nav-cta .cta-chevron {
	margin: 0;
}

/* ---------------------------------------------------------- card accent bar */
/*
 * The rollover on a card is a colour bar down its left edge (Ben, 29 Sep 2026;
 * Dave's list, item 1). It replaces the decorative ring or disc the cards had,
 * which is deleted from each family's own sheet.
 *
 * There is no card base class in this theme — every family is its own — so
 * this one grouped rule is the shared component: each card names its root
 * here, supplies `--card-accent`, and nothing else. The bar sits on whichever
 * pseudo-element that card has free, is positioned over the card's own left
 * edge (the card's radius clips it), and never takes part in layout, so a card
 * neither moves nor changes size when it appears. It is 6px wide and every
 * card's padding is wider, so it cannot reach a title, a line of copy, an arrow
 * or a picture.
 *
 * It grows from the top on hover and on keyboard focus alike. With
 * prefers-reduced-motion the state is kept and the growth is not: the bar is
 * simply there.
 *
 * Cards that are pictures first (Work index tiles, Case Study image cards,
 * proof cards, related work) have no ring to replace and a hover that is
 * already an image zoom; a bar would sit on the picture, so they are not here.
 */
.home-problem-card::after,
.route-card::after,
.journey-next-card::after,
.journey-insight-card::after,
.planner-panel .option::after,
.motion-other::after,
.motion-service-card::after,
.insight-v2-card::before,
.insight-lib-card::before,
.insight-feature::before {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	z-index: 3;
	width: var(--card-bar-width);
	background: var(--card-accent, var(--orange));
	transform: scaleY(0);
	transform-origin: 50% 0;
	transition: transform var(--card-bar-duration) var(--card-bar-ease);
	pointer-events: none;
}

.home-problem-card:is(:hover, :focus-visible, :focus-within)::after,
.route-card:is(:hover, :focus-visible, :focus-within)::after,
.journey-next-card:is(:hover, :focus-visible, :focus-within)::after,
.journey-insight-card:is(:hover, :focus-visible, :focus-within)::after,
.planner-panel .option:is(:hover, :focus-visible, .selected)::after,
.motion-other:is(:hover, :focus-visible, :focus-within)::after,
.motion-service-card:is(:hover, :focus-visible, :focus-within)::after,
.insight-v2-card:is(:hover, :focus-visible, :focus-within)::before,
.insight-lib-card:is(:hover, :focus-visible, :focus-within)::before,
.insight-feature:is(:hover, :focus-visible, :focus-within)::before {
	transform: scaleY(1);
}

/* ---------------------------------------------------------- stretched link */
/*
 * A whole-card link whose name is only its title (Ben, 7 Oct 2026). The cards that are links (a route's challenge cards, How we help's two specialist cards, the Challenge page's "Ready to
 * discuss it?" card and the Insights index cards) used to be one `<a>` around the tag, title and blurb, so a screen reader announced a 150-character link name and a crawler read the same.
 * Now the link is the title (or, on the journey card, the action line, which says where it goes) and this spreads it over the card: the overlay is the link's own `::after`, so a click or tap
 * anywhere on the card is a click on the link, `:hover` on the link is `:hover` on the card, and the card's own hover rules, accent bar and lift are unchanged.
 *
 * Nothing else in these cards is a control, so nothing is under the overlay that needs a click. The overlay sits above the card's own children (z-index 2) and its side bar (3, which takes
 * no pointer events). Keyboard focus lands on the link and the ring is drawn on the whole card, the same browser ring the old whole-card link had; the link's own outline is removed because on a
 * title alone it would be the one place the ring is not wanted.
 */
.stretched-link {
	color: inherit;
	text-decoration: none;
}

.stretched-link::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 4;
}

.stretched-link:focus-visible {
	outline: none;
}

.route-card:has(.stretched-link:focus-visible),
.specialist-card:has(.stretched-link:focus-visible),
.journey-next-card:has(.stretched-link:focus-visible),
.insight-v2-card:has(.stretched-link:focus-visible),
.insight-lib-card:has(.stretched-link:focus-visible),
.insight-feature:has(.stretched-link:focus-visible) {
	outline: auto 1px Highlight;
	outline: auto 1px -webkit-focus-ring-color;
}

/* The arrow's nudge was the link's hover, and the Insights card's arrow ("Read insight") is outside the link now: the card's hover and focus move it the same. */
.insight-v2-card:is(:hover, :has(.stretched-link:focus-visible)) .cta-chevron--right,
.insight-feature:is(:hover, :has(.stretched-link:focus-visible)) .cta-chevron--right {
	translate: var(--chevron-nudge) 0;
}

/* The Insights card's copy block is itself positioned (it is laid over the picture), so the overlay is let out of it: the card clips it to the card's own edge. */
.insight-v2-copy .stretched-link::after {
	inset: -2000px;
}

/* ----------------------------------------------------------------- chip */
/* An inline span, not a block: as a block it loses its line box and every
   project card's caption sits 9px lower than the master's. */
.chip {
	font-family: var(--font-body);
	font-size: var(--fs-eyebrow-label);
	font-weight: 800;
	letter-spacing: var(--ls-eyebrow-label);
	text-transform: uppercase;
}

/*
 * On a project card the chip is the image-card meta line, the same component
 * as the Work tile's, so it takes the same size. The chip elsewhere — How we
 * help's goal cards and its financial strip — is on paper, not over imagery,
 * and is left at the master's 9px.
 */
.project-card .chip {
	font-family: var(--font-body);
	font-size: var(--fs-eyebrow-label);
	font-weight: 800;
	letter-spacing: var(--ls-eyebrow-label);
	text-transform: uppercase;
	display: block;
	line-height: 1.35;
}

/* --------------------------------------------------------- section head */
/*
 * 32px, not 34: the master's QA layer sets it for the whole head family —
 * `.section-head`, `.route-title-row`, `.work-head`, `.faq-grid-head` — and
 * then scopes 34px to How we help's goals block alone, which help.css carries.
 * Measured on About, where the two differ.
 */
.section-head {
	margin-bottom: 32px;
}

.section-head p {
	max-width: 720px;
	margin: 18px 0 0;
	font-size: var(--fs-body);
	line-height: 1.55;
	color: var(--head-lede);
}

/* --------------------------------------------------------------- funnel */
/*
 * The shared closing CTA. The master carries a paragraph here that is
 * display:none at every width, so it is not rendered at all rather than
 * shipped as hidden text — see docs/STEP-4-CHECKLIST.md.
 */
/*
 * The approved orange closing CTA: a three-layer gradient plus two rings behind
 * the content. Missing entirely from the first build because the comparison read
 * `background-color` — which is transparent — and never `background-image`.
 */
.funnel {
	position: relative;
	/*
	 * Creates the stacking context the rings need: they sit at z-index:-1, so
	 * without isolation they paint behind this element's own background and
	 * are invisible. A single property, and the whole decoration depends on it.
	 */
	isolation: isolate;
	overflow: hidden;
	padding: 54px 0;
	background: var(--funnel-gradient);
	color: var(--ink);
}



.funnel .wrap {
	position: relative;
	z-index: 2;
	display: grid;
	row-gap: 0;
	column-gap: 70px;
	align-items: center;
}

/*
 * The floor drops on the narrowest screens so the longest single word clears
 * the gutter. "interchangeable." at 40px with the approved -0.02em tracking
 * needs 356px in a 350px column and ran into the page edge; the tracking is
 * Ben's and stays, so the heading gives way instead. At 408px and wider this
 * resolves to the same 40px as before.
 */
.funnel h2 {
	max-width: 900px;
	font-size: min(40px, 9.8vw);
	line-height: 0.96;
	text-wrap: balance;
}

/* The master shrink-wraps this one and pushes it to the end of its track. */
/*
 * The master's QA layer hides the funnel's supporting paragraph outright —
 * `.funnel p, .funnel-cta p { display: none !important }` is the last word on
 * it, after four earlier rules that size and space it. So it is written, it is
 * in the document, and it is not painted. About is the only page with one.
 * Reproduced rather than dropped from the template, because the master keeps
 * the copy; whether it should be shown is Ben's call, not a build decision.
 */
.funnel p,
.funnel-cta p {
	display: none;
}

.funnel .btn {
	min-width: 155px;
	justify-content: space-between;
}

/*
 * Start, not end. Once the funnel grid stacks — below 1101 — the master puts
 * the button under the headline at the left edge, not pushed to the right:
 * `justify-self: start` measured at both 820 and 390. Width-only comparison
 * could not see this, because the button is the same size either way; it
 * surfaced the moment the sweep began recording each box's x offset.
 */
.funnel .btn-row {
	min-width: 190px;
	justify-self: start;
	margin-top: 18px;
}

/* -------------------------------------------------- tablet, 651px and up */
/* --------------------------------------------------- kicker (29_ route) */
/* Section-level heading below .display-sm. Its own role, not a display-sm
   variant: 800 rather than 900, and its own clamp. */
.kicker {
	margin: 0;
	font-size: clamp(25px, 3vw, 42px);
	line-height: 1.1;
	font-weight: var(--fw-eyebrow);
	letter-spacing: var(--tr-display);
}

/* ------------------------------------------------- approach / the system */
/*
 * Two variants. `.system` is the fuller one carried by the brand route;
 * `.system-lite` is what every other route uses. In the master the only
 * differences are the section padding and the heading size — verified at all
 * four widths — so the modifier stays that small here too.
 */
.system {
	padding: 108px 0;
	background: var(--white);
}

.system-lite {
	padding: 84px 0;
}

.system-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 70px;
}

.system-lite .display-sm {
	font-size: clamp(42px, 5vw, 73px);
}

.steps {
	border-top: 1px solid var(--line);
}

.step-row {
	display: grid;
	grid-template-columns: 75px 1fr;
	gap: 20px;
	padding: 24px 0;
	border-bottom: 1px solid var(--line);
}

.step-row > span {
	font-size: max(calc(12px * var(--type-small)), var(--type-floor));
	font-weight: var(--fw-eyebrow);
	color: var(--muted);
}

.step-row strong {
	font-size: calc(24px * var(--type-heading));
	line-height: 1.1;
	letter-spacing: -0.02em;
}

.step-row p {
	max-width: 840px;
	margin: 8px 0 0;
	font-size: var(--fs-body);
	line-height: 1.52;
	color: var(--step-text);
}

/* ------------------------------------------------------- work / proof grid */
.work-section {
	padding: 88px 0;
}

/*
 * The master's QA pass turns every one of these heads from a two-column flex
 * into a block, so the supporting paragraph sits under the heading rather than
 * beside it. Reproduced as `display:block` rather than by deleting the rule,
 * because the markup keeps the master's wrapper div.
 */
.work-head {
	display: block;
	margin-bottom: 32px;
}

/*
 * The master sizes this heading down from the display scale wherever a
 * `.work-head` carries one — About, the Work index, the Case Study index. It
 * is a shared rule there and a shared rule here; without it About's heading
 * came out at the full 88px against the master's 73px.
 *
 * It does not apply in the laptop band: there the master's QA layer makes
 * `.display-sm` itself `!important`, which beats this more specific selector,
 * so the heading follows the display scale instead. Both bands are written
 * out below, next to `.display-sm`'s own.
 */
.work-head .display-sm {
	font-size: clamp(42px, 5vw, 73px);
}

.work-head > p {
	max-width: 720px;
	margin: 18px 0 0;
	font-size: var(--fs-body);
	line-height: 1.55;
	color: var(--head-lede);
}

.work-section .display-sm {
	max-width: 900px;
	font-size: clamp(38px, 4vw, 58px);
	line-height: 0.98;
}

/*
 * A heading of two sentences, each on its own line (Ben, 29 Sep 2026; Dave's
 * list, item 8): "Similar challenge." / "Different client." on every Challenge
 * page. The break is the second sentence being a block, not a `<br>` typed into
 * the string, so an editor cannot break it and the wording stays one field. The
 * orange is the existing `h2 em` rule, #FC6600.
 *
 * On a phone the heading is set so that each sentence fits its line: the
 * standard 38px makes "Similar challenge." 420px wide inside a 350px column,
 * which wrapped it into a third and fourth line. The size scales with the
 * viewport to a ceiling of the standard 38px, so from 500px up nothing changes.
 */
.heading-split em {
	display: block;
}

@media screen and (max-width: 650px) {
	.work-section .heading-split {
		font-size: calc(clamp(24px, 7.6vw, 38px) * var(--type-heading));
	}
}

.project-grid {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: 15px;
}

/*
 * `isolation: isolate` is load-bearing, exactly as on .funnel: the scrim sits
 * at z-index 1 and the caption at 2, and without a stacking context here the
 * card's own hover transform on the image would reorder them.
 */
.project-card {
	position: relative;
	grid-column: span 12;
	min-height: 450px;
	border-radius: 25px;
	overflow: hidden;
	isolation: isolate;
	background: var(--project-card-bg);
	color: var(--ink);
	text-decoration: none;
	cursor: pointer;
}

.project-card img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center center;
	background: var(--media-placeholder);
	transition: 0.65s;
}

.project-card::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background: var(--card-scrim);
}

.project-card:hover img,
.project-card:focus-visible img {
	transform: scale(1.035);
}

.project-content {
	position: absolute;
	z-index: 2;
	left: 28px;
	right: 28px;
	bottom: 26px;
	display: flex;
	align-items: end;
	justify-content: space-between;
	gap: 20px;
	color: var(--white);
}

/*
 * Clamped to three lines. The master sets -webkit-line-clamp with the box
 * model it needs; both halves have to stay together or long headlines push the
 * round arrow out of the card.
 */
.project-content h3 {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	overflow: hidden;
	max-width: 560px;
	margin: 7px 0 0;
	font-size: calc(clamp(26px, 2.7vw, 38px) * var(--type-heading));
	line-height: 1;
	letter-spacing: var(--tr-display);
	font-weight: var(--fw-card-title);
	text-wrap: balance;
}

.project-card.feature .project-content h3 {
	max-width: 760px;
	font-size: clamp(28px, 3vw, 43px);
}

/*
 * The circular arrow (Ben, 1 Oct 2026, Dave's list): the circle is 10% smaller (50px to 45px) and the chevron in it 10% larger (27px to 29.7px), and the chevron is centred by
 * its ink, not by its box. The box was off-centre because the shared chevron carries a 0.3em margin for the space before a label (4px of push to the right in a box that has
 * no label) and because the glyph's ink does not sit in the middle of its own advance box; the offset below is the measured remainder
 * (docs/verification/chevron-centering.py), in em so it stays right if the size changes. The circle is decoration inside a link that is the whole card, so a smaller circle
 * costs no click or touch area (the testimonial buttons, whose circle IS the control, are not touched: they use arrows, set their own size and keep 42px).
 */
.round-arrow {
	display: grid;
	place-items: center;
	width: 45px;
	height: 45px;
	min-width: 45px;
	border-radius: var(--r-round);
	background: var(--white);
	color: var(--ink);
	--chevron-size: 29.7px;
	--chevron-optical-x: 0.012em;
	--chevron-optical-y: -0.0616em;
}

.round-arrow .cta-chevron.cta-chevron {
	margin: 0;
	position: relative;
	inset-inline-start: var(--chevron-optical-x);
	inset-block-start: var(--chevron-optical-y);
}

.work-footer-actions {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	justify-content: flex-end;
	gap: 8px;
	margin-top: 18px;
}

.section-action {
	display: flex;
	width: 100%;
	align-items: center;
	justify-content: space-between;
	gap: 28px;
	min-width: 178px;
	/* 44px tall at the very least (Ben, 8 Oct 2026: "See all work" and "Case studies" were 41); the padding is unchanged, so only the short ones grow. */
	min-height: 44px;
	padding: 13px 14px 13px 16px;
	border: 1px solid var(--line);
	border-radius: var(--r-button);
	background: var(--white);
	color: var(--ink);
	font-size: max(calc(10px * var(--type-small)), var(--type-floor));
	font-weight: var(--fw-eyebrow);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	text-decoration: none;
	transition: 0.22s ease;
}

.section-action:hover,
.section-action:focus-visible {
	background: var(--ink);
	border-color: var(--ink);
	color: var(--white);
	transform: translateY(-2px);
}

.work-footer-actions .section-action {
	min-width: 170px;
}

/*
 * The one-line way back up to a Route (Ben, 5 Oct 2026): under a Case Study's "What this work helps with" and at the foot of a Challenge's "Keep exploring". The same
 * text-link as "Browse all work", sized to its words rather than the full row, so it reads as a pointer and not a button.
 */
.journey-helps-route,
.journey-next-route {
	display: inline-flex;
	width: auto;
	max-width: 100%;
	margin-top: 18px;
}

/* -------------------------------------------------- testimonial carousel */
/*
 * Shared. Built for the Work detail and now also carried by both Case Study
 * surfaces, so it belongs here rather than in one family's stylesheet. Each
 * surface sets its own colours; this is the mechanism only.
 */
/* Clipped, because a slide is translated before it fades in. */
/*
 * The component's own ground: dark, white type, 88px of air, tightening to
 * 68px in the laptop band. Every surface that uses it so far restates all of
 * it — the Work detail, the Case Study detail, the Case Studies strip — so the
 * defaults were never reached and never written. About is the first surface
 * that takes them as they come.
 */
.testimonial-carousel {
	padding: 88px 0;
	background: var(--ink);
	color: var(--white);
	overflow: hidden;
}

.testimonial-slides {
	position: relative;
	min-height: 320px;
}

/*
 * The slides are stacked and cross-faded, so all but the active one are taken
 * out of the flow and out of the tab order. The active one goes back into the
 * flow, which is what gives the section its height.
 */
.testimonial-slide {
	position: absolute;
	inset: 0;
	opacity: 0;
	transform: translateY(12px);
	pointer-events: none;
	transition: opacity 0.32s ease, transform 0.32s ease;
}

.testimonial-slide.active {
	position: relative;
	opacity: 1;
	transform: none;
	pointer-events: auto;
}

.testimonial-slide blockquote {
	max-width: 1180px;
	margin: 0;
	font-size: clamp(34px, 4.6vw, 68px);
	line-height: 1.04;
	letter-spacing: var(--tr-display);
	font-weight: var(--fw-eyebrow);
	color: var(--white);
}

/* Presentational labels that used to be <strong> (Score Recovery B, Ben, 8 Oct 2026): a name, a figure or a label drawn bold is not emphasis in the text, so these are spans. `bolder` is
   what the browser gave <strong>; at specificity 0 every rule that sets a weight still wins, as it did over the browser's. */
:where(.work-detail-fact-value, .work-detail-proof-value, .testimonial-name, .qa-answer, .case-metric-label, .case-image-stat, .cap-label) {
	font-weight: bolder;
}

.testimonial-slide cite {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin-top: 28px;
	font-style: normal;
}

.testimonial-slide cite .testimonial-name {
	font-size: calc(20px * var(--type-heading));
	font-weight: var(--fw-eyebrow);
	color: var(--white);
}

.testimonial-slide cite span:not(.testimonial-name) {
	font-size: var(--fs-body);
	color: var(--work-quote-meta);
}

.testimonial-carousel-nav {
	display: flex;
	gap: 16px;
	margin-top: 34px;
	align-items: center;
}
/*
 * Manual controls beside the dots (Ben, 21 Sep 2026). The arrow is the theme's
 * own `.round-arrow`, scaled down so it sits with the dots rather than over
 * them, and it inverts on hover and on keyboard focus like everything else.
 */
.testimonial-dots {
	display: flex;
	align-items: center;
	gap: 7px;
}

.testimonial-arrow {
	width: 42px;
	height: 42px;
	min-width: 42px;
	padding: 0;
	/*
	 * `currentColor` rather than a token: this component sits on the dark Work
	 * and Case Study panels and on the light Case Studies strip, and the
	 * surface already sets `color`. One rule, right on all three.
	 */
	border: 1px solid currentColor;
	background: transparent;
	color: inherit;
	font-size: calc(17px * var(--type-reading));
	opacity: 0.55;
	cursor: pointer;
	transition: 0.2s;
}

.testimonial-arrow:hover,
.testimonial-arrow:focus-visible {
	opacity: 1;
	transform: translateY(-2px);
}


.testimonial-dot {
	width: 28px;
	height: 3px;
	padding: 0;
	border: 0;
	border-radius: 2px;
	background: var(--work-dot);
	cursor: pointer;
	transition: 0.2s;
}

.testimonial-dot.active {
	width: 48px;
	background: var(--yellow);
}

/* ---------------------------------------------------------------- FAQs */
.faq-section {
	padding: 62px 0;
	background: var(--white);
}

.faq-grid-head {
	display: block;
	margin-bottom: 32px;
}

.faq-grid-head > p {
	max-width: 720px;
	margin: 18px 0 0;
	font-size: var(--fs-body);
	line-height: 1.55;
	color: var(--head-lede);
}

.faq-grid-head .display-sm {
	font-size: clamp(36px, 4.3vw, 62px);
}

.faq-list {
	border-top: 1px solid var(--line);
}

.faq-list details {
	border-bottom: 1px solid var(--line);
}

.faq-list summary {
	position: relative;
	list-style: none;
	cursor: pointer;
	padding: 17px 34px 17px 0;
	font-size: calc(18.5px * var(--type-reading));
	font-weight: var(--fw-eyebrow);
	letter-spacing: var(--tr-display);
}

.faq-list summary::-webkit-details-marker {
	display: none;
}

.faq-list summary::after {
	content: "+";
	position: absolute;
	top: 19px;
	right: 0;
	font-size: calc(25px * var(--type-reading));
	font-weight: 500;
}

.faq-list details[open] summary::after {
	content: "\2013";
}

.faq-list details p {
	max-width: 740px;
	margin: 0 0 24px;
	font-size: var(--fs-faq-answer);
	line-height: 1.62;
	color: var(--faq-answer);
}

@media screen and (min-width: 651px) {
	.testimonial-slides {
		min-height: 240px;
	}

	.display-sm {
		font-size: clamp(48px, 6vw, 66px);
	}

	.lead {
		font-size: var(--fs-body);
	}

	.funnel {
		padding: 66px 0;
	}

	.funnel h2 {
		font-size: clamp(40px, 4.2vw, 46.2px);
	}

	.step-row p {
		font-size: var(--fs-body);
	}

	/* The master's ≤1000 rule: both cards drop to full width and 520 tall, and
	   only below 650 do they shorten again to 450. */
	.project-card,
	.project-card.feature {
		min-height: 520px;
	}

	.work-footer-actions {
		flex-direction: row;
		align-items: normal;
		justify-content: flex-end;
	}

	.section-action {
		width: auto;
	}
}

/* --------------------------------------------- laptop band, 1101px and up */
@media screen and (min-width: 1101px) {

	.testimonial-carousel {
		padding: 68px 0;
	}


	/* `.display-sm` is `!important` in the master's laptop band and wins. */
	.work-head .display-sm {
		font-size: clamp(43px, 3.8vw, 57px);
	}

	.testimonial-slide blockquote {
		font-size: clamp(30px, 3.5vw, 50px);
	}

	.display-sm {
		font-size: clamp(43px, 3.8vw, 57px);
	}

	.lead {
		font-size: var(--fs-body);
		line-height: 1.5;
	}

	.copy {
		font-size: var(--fs-body);
		line-height: 1.62;
	}

	.funnel {
		padding: 50px 0;
	}

	/*
	 * The master keeps the eyebrow and headline in the first column and pulls
	 * the button alongside them, rather than letting auto-placement drop the
	 * headline into column two. Placed explicitly so the DOM stays the
	 * master's, with no wrapper element added.
	 */
	.funnel .wrap {
		grid-template-columns: 1fr auto;
	}

	.funnel .eyebrow,
	.funnel h2 {
		grid-column: 1;
	}

	.funnel .btn-row {
		grid-column: 2;
		grid-row: 1 / 3;
		justify-self: end;
	}

	.funnel h2 {
		max-width: 820px;
		font-size: clamp(38px, 3.5vw, 50px);
	}

	.funnel .btn-row {
		margin-top: 0;
	}

	.kicker {
		font-size: calc(clamp(26px, 2.4vw, 34px) * var(--type-heading));
		line-height: 1.08;
	}

	.system,
	.system-lite {
		padding: 72px 0;
	}

	.system-grid {
		grid-template-columns: 0.72fr 1.28fr;
	}

	/*
	 * The master's laptop `.display-sm` is `!important`, so it outranks the
	 * scoped `.system-lite` / `.faq-grid-head` rules that win at every other
	 * width. Reproduced by restating the laptop value at the same specificity
	 * instead, since theme CSS carries no `!important` (28_ §7).
	 */
	.system-lite .display-sm,
	.faq-grid-head .display-sm {
		font-size: clamp(43px, 3.8vw, 57px);
	}

	.step-row {
		padding: 18px 0;
	}

	.step-row strong {
		font-size: calc(20px * var(--type-heading));
	}

	.step-row p {
		font-size: var(--fs-body);   /* was 14px in this band (Ben, 1 Oct 2026: card descriptions no smaller than 14.5px) */
	}

	.work-section {
		padding: 70px 0;
	}

	.work-section .display-sm {
		font-size: 46px;
	}

	.project-card {
		grid-column: span 6;
		min-height: 540px;
	}

	.project-card.feature {
		grid-column: span 12;
		min-height: 640px;
	}

	.faq-section {
		padding: 50px 0;
	}

	.faq-list summary {
		padding: 14px 32px 14px 0;
		font-size: calc(16px * var(--type-reading));
	}

}

/* ---------------------------------------------- large desktop, 1501px and up */
@media screen and (min-width: 1501px) {

	.testimonial-carousel {
		padding: 88px 0;
	}


	/* The laptop `!important` stops applying, so this takes over again. */
	.work-head .display-sm {
		font-size: clamp(42px, 5vw, 73px);
	}

	.testimonial-slide blockquote {
		font-size: clamp(34px, 4.6vw, 68px);
	}

	.display-sm {
		font-size: 88px;
	}

	.lead {
		font-size: var(--fs-body);
		line-height: 1.36;
	}

	.copy {
		font-size: var(--fs-body);
		line-height: 1.7;
	}

	.funnel {
		padding: 66px 0;
	}

	.funnel h2 {
		max-width: 1080px;
		font-size: 62px;
	}

	.kicker {
		font-size: clamp(25px, 3vw, 42px);
		line-height: 1.1;
	}

	.system {
		padding: 108px 0;
	}

	.system-lite {
		padding: 84px 0;
	}

	.system-lite .display-sm {
		font-size: clamp(42px, 5vw, 73px);
	}

	.faq-grid-head .display-sm {
		font-size: clamp(36px, 4.3vw, 62px);
	}

	.step-row {
		padding: 24px 0;
	}

	.step-row strong {
		font-size: calc(24px * var(--type-heading));
	}

	.step-row p {
		font-size: var(--fs-body);
	}

	.work-section {
		padding: 88px 0;
	}

	.work-section .display-sm {
		font-size: clamp(38px, 4vw, 58px);
	}

	.faq-section {
		padding: 62px 0;
	}

	.faq-list summary {
		padding: 17px 34px 17px 0;
		font-size: calc(18.5px * var(--type-reading));
	}

}


/* ------------------------------------------------- reduced motion */
/*
 * The master carries its own `prefers-reduced-motion` block, covering the logo
 * marquee among others, so this matches it rather than inventing a rule. Hover
 * states still change — only the movement is removed.
 */
@media (prefers-reduced-motion: reduce) {
	.testimonial-slide,
	.testimonial-dot,
	.testimonial-arrow {
		transition: none;
		transform: none;
	}
	/* The arrows lifted 2px on hover and the hover rule outranked the block above, so the lift stayed under a request for less motion (found by motion-parity.mjs). */
	.testimonial-arrow:hover,
	.testimonial-arrow:focus-visible {
		transform: none;
	}

	.btn,
	.cap-row,
	.route-card,
	.project-card img,
	.section-action {
		transition: none;
	}

	.btn:hover,
	.btn:focus-visible,
	.route-card:hover,
	.route-card:focus-visible,
.route-card:has(.stretched-link:focus-visible),
	.project-card:hover img,
	.project-card:focus-visible img,
	.section-action:hover,
	.section-action:focus-visible {
		transform: none;
	}

	/* The action chevron keeps its place: its nudge is the movement, and it goes. */
	.cta-chevron {
		transition: none;
	}

	:is(a, button):is(:hover, :focus-visible) .cta-chevron {
		translate: 0 0;
	}

	.insight-v2-card:is(:hover, :has(.stretched-link:focus-visible)) .cta-chevron,
	.insight-feature:is(:hover, :has(.stretched-link:focus-visible)) .cta-chevron,
	.journey-helps-link:is(:hover, :has(.stretched-link:focus-visible)) .cta-chevron {
		translate: 0 0;
	}

	/* The card side bar: the state stays, the growth goes. */
	.home-problem-card::after,
	.route-card::after,
	.journey-next-card::after,
	.journey-insight-card::after,
	.planner-panel .option::after,
	.motion-other::after,
	.motion-service-card::after,
	.insight-v2-card::before,
	.insight-lib-card::before,
	.insight-feature::before {
		transition: none;
	}
}


/* ------------------------------------------------------------ form field */
/*
 * A labelled control. Shared: the planner's details step and the Contact form
 * are the same field in the master, written once against `.field`, so it lives
 * here rather than in either page's stylesheet — the same move the testimonial
 * carousel made. `.form-grid` and `.consent` stay with the planner, which is
 * the only thing that has them.
 */
/*
 * Component spacing (Ben, 21 Sep 2026). Restrained, and only where the type
 * in this brief grew: a 16px label needs more than the 7px that sat under a
 * 10px one, and the rows need to read as separate questions.
 */
.field {
	display: flex;
	flex-direction: column;
	gap: 9px;
}

.field.full {
	grid-column: 1/-1;
}

.field label {
	font-size: var(--fs-field-label);
	text-transform: uppercase;
	letter-spacing: .06em;
	font-weight: 800;
}

.field input {
	width: 100%;
	border: 1px solid var(--line);
	background: var(--paper);
	border-radius: 11px;
	padding: 14px;
	outline: none;
}

.field textarea {
	width: 100%;
	border: 1px solid var(--line);
	background: var(--paper);
	border-radius: 11px;
	padding: 14px;
	outline: none;
	min-height: 110px;
	resize: vertical;
}

/*
 * The master takes the native outline off every control and puts nothing back,
 * which leaves keyboard focus invisible. Same approved correction as the Work
 * index filter (Ben, 21 Sep 2026), and the same treatment the theme already
 * uses: the border goes to ink on `:focus-visible` and nothing else changes,
 * so the resting and mouse states are the master's.
 */
.field input:focus-visible,
.field textarea:focus-visible {
	border-color: var(--ink);
}

/* --------------------------------------------- enquiry form states */
/*
 * Step 6. Shared by the Contact form and the planner's final step, so the two
 * behave identically. Colour is never the only signal: an invalid field gets a
 * border it did not have, wording underneath it, and `aria-invalid`.
 */
.form-honeypot {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.field-error {
	margin: 0;
	font-size: calc(14.5px * var(--type-reading));
	line-height: 1.35;
	font-weight: 600;
	color: var(--red);
}

.field input.has-error,
.field textarea.has-error {
	border-color: var(--red);
}

.contact-form-status,
.planner-form-status {
	margin: 0;
	font-size: var(--fs-field-note);
	line-height: 1.5;
	font-weight: 600;
}

.contact-form-status.is-error,
.planner-form-status.is-error {
	color: var(--red);
}

.contact-form-status.is-success,
.planner-form-status.is-success {
	color: var(--ink);
}

/*
 * The sent state — shared by any form card, not Contact's alone.
 *
 * Ben, 25 Sep 2026: a line of text under a form that is still standing there
 * reads as system feedback. So the card is replaced rather than appended to:
 * every direct child of the form goes, and the panel takes their place. The
 * form element *is* the white card (`.contact-form` in about.css), which is
 * why this hides children rather than swapping a wrapper — the card, its
 * border, its radius and its padding are all untouched.
 *
 * `site.js` locks the card's measured height before adding `is-sent`, so the
 * page does not move under the reader when the contents change.
 */
.form-success {
	display: none;
}

form.is-sent > *:not(.form-success) {
	display: none;
}

form.is-sent .form-success {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	/*
	 * Centred in the height the card kept from the form it replaced, so the
	 * pinned footprint reads as deliberate rather than as a message stranded
	 * at the top of a tall empty box.
	 */
	justify-content: center;
	grid-column: 1 / -1;
	height: 100%;
}

form.is-sent .form-success:focus {
	outline: none;
}

.form-success-mark {
	width: 76px;
	height: 76px;
	margin-bottom: 26px;
	flex: none;
}

.form-success-eyebrow {
	font-family: var(--font-body);
	font-size: var(--fs-eyebrow-label);
	font-weight: 800;
	letter-spacing: var(--ls-eyebrow-label);
	text-transform: uppercase;
	margin: 0 0 14px;
	line-height: var(--lh-eyebrow);
	color: var(--home-eyebrow);
}

.form-success-title {
	margin: 0 0 14px;
	font-size: var(--fs-h3);
	line-height: var(--lh-h3);
	font-weight: var(--fw-title);
	letter-spacing: var(--tr-h3);
	color: var(--ink);
}

.form-success-copy {
	margin: 0;
	max-width: 32em;
	font-size: var(--fs-body);
	line-height: var(--lh-body);
	color: var(--copy-ink);
}

.form-success-more {
	margin: 24px 0 0;
	font-size: var(--fs-field-note);
	line-height: 1.5;
	color: var(--muted);
}

.form-success-more a {
	color: var(--ink);
	font-weight: 600;
	text-decoration: underline;
	text-underline-offset: 3px;
	text-decoration-thickness: 1px;
	text-decoration-color: var(--home-panel-link-rule);
}

.form-success-more a:hover,
.form-success-more a:focus-visible {
	text-decoration-color: var(--ink);
}

/* The tick draws itself once. Restrained, and only where motion is welcome. */
@media (prefers-reduced-motion: no-preference) {

	form.is-sent .form-success-mark {
		animation: form-success-pop 320ms cubic-bezier(0.2, 0.8, 0.3, 1) both;
	}

	form.is-sent .form-success-tick {
		stroke-dasharray: 52;
		stroke-dashoffset: 52;
		animation: form-success-tick 340ms 140ms ease-out both;
	}
}

@keyframes form-success-pop {

	from {
		transform: scale(0.82);
		opacity: 0;
	}

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

@keyframes form-success-tick {

	to {
		stroke-dashoffset: 0;
	}
}

@media screen and (max-width: 650px) {

	.form-success-mark {
		width: 62px;
		height: 62px;
		margin-bottom: 22px;
	}

	.form-success-more {
		margin-top: 20px;
	}
}

/*
 * Turnstile draws its own widget; all this does is give it room and keep it
 * from stretching across a two-column grid.
 */
.cf-turnstile {
	grid-column: 1/-1;
	min-height: 65px;
}

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

	.field input.has-error,
	.field textarea.has-error {
		transition: none;
	}

}

/* ------------------------------------------------ "What this work helps with"
   Audit A5 phase 2 (Ben, 23 Sep 2026). A Work or Case Study page ended the
   journey; this returns the reader to the Challenge they are actually in.

   Shared by two families, so it lives here rather than in work.css or
   case.css.

   **Refined on Ben's review (23 Sep).** The first pass was a row of 72px
   pills, which read as three related links. These are cards in the same
   language as `.route-card` — the site's existing way of putting a customer
   problem in front of someone: a real card, the destination's own words at
   card-title size, and the lift-and-shadow hover the Route grid already uses.
   Slightly shorter than a Route card, because this is a closing section rather
   than the page's main grid. The linking logic is untouched.
   -------------------------------------------------------------------------- */
.journey-helps {
	padding: 62px 0 68px;
	background: var(--white);
	border-top: 1px solid var(--line);
}

.journey-helps-title {
	margin: 0 0 26px;
	font-size: calc(28px * var(--type-heading));
	line-height: 1.08;
	letter-spacing: var(--tr-display);
	font-weight: var(--fw-title);
}

.journey-helps-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 12px;
}

.journey-helps-link {
	position: relative;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	gap: 18px;
	min-height: 210px;
	padding: 26px;
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	background: var(--white);
	color: var(--ink);
	text-decoration: none;
	cursor: pointer;
	overflow: hidden;
	transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}

/* The same lift the Route grid uses, so the two read as one family. */
.journey-helps-link:hover,
.journey-helps-link:focus-visible,
.journey-helps-link:has(.stretched-link:focus-visible) {
	transform: translateY(-5px);
	box-shadow: var(--shadow-lift);
	border-color: rgba(17, 17, 17, 0.2);
	outline: 0;
}

.journey-helps-link:focus-visible,
.journey-helps-link:has(.stretched-link:focus-visible) {
	outline: 3px solid rgba(255, 106, 0, 0.35);
	outline-offset: 3px;
}

/* The destination's own words, at the size the site gives a card title. */
.journey-helps-label {
	display: block;
	max-width: 15ch;
	font-size: calc(25px * var(--type-heading));
	line-height: 1.05;
	letter-spacing: -0.012em;
	font-weight: var(--fw-card-title);
	text-wrap: balance;
}

/*
 * The Route page's own card text for the same Challenge. It is what makes the
 * card a problem someone can recognise rather than a label, and it is the
 * site's existing sentence, not a new one.
 */
.journey-helps-text {
	display: block;
	margin-top: 12px;
	max-width: 34ch;
	font-size: var(--fs-body);
	line-height: 1.5;
	font-weight: var(--fw-body, 500);
	color: var(--muted);
}

.journey-helps-go {
	display: block;
	--chevron-size: 27px;
	color: var(--muted);
	transition: translate var(--chevron-duration), color .2s ease;
}

.journey-helps-link:hover .journey-helps-go,
.journey-helps-link:focus-visible .journey-helps-go,
.journey-helps-link:has(.stretched-link:focus-visible) .journey-helps-go {
	color: var(--ink);
}

/* The arrow's nudge was the link's hover, and the card is not the link now: the card's hover and focus move it the same. */
.journey-helps-link:is(:hover, :has(.stretched-link:focus-visible)) .cta-chevron--right {
	translate: var(--chevron-nudge) 0;
}

@media screen and (min-width: 651px) {

	.journey-helps {
		padding: 84px 0 92px;
	}

	.journey-helps-title {
		font-size: calc(34px * var(--type-heading));
		margin-bottom: 30px;
	}

	.journey-helps-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: 14px;
	}

	/*
	 * A card is ~400px in a full row, and stays that width when there are
	 * fewer of them rather than stretching or leaving a gap. Capping the grid
	 * is what holds the card size steady.
	 */
	.journey-helps-grid.is-1 {
		grid-template-columns: minmax(0, 1fr);
		max-width: 420px;
	}

	.journey-helps-grid.is-2 {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		max-width: 854px;
	}

	.journey-helps-link {
		min-height: 236px;
		padding: 26px;
	}

	/*
	 * 27px is the Route card's own rendered title size at this width. The two
	 * cards then share a footprint as well as a language.
	 */
	.journey-helps-label {
		font-size: calc(27px * var(--type-heading));
	}

}

/* ---------------------------------------------------------- hero headings
 *
 * Ben, 23 Sep 2026: no desktop hero headline past four lines, and a short one
 * must not shrink to buy that.
 *
 * A hero h1 is a flat 88px whatever its length, which is right for "Make the
 * brand feel real." and wrong for "If Google does not understand what you are
 * useful for, customers probably do not either." — 88 characters, and nine
 * lines before any of this began. The width of the column was never the
 * problem; the size of the type was.
 *
 * Sized in `vw` so the ladder holds at 1600, 1366 and 820 alike rather than
 * being tuned to one width, and floored so nothing collapses. Applied from
 * 651px up: below that the hero is a single column and already reads.
 *
 * Nothing at or under 40 characters carries a class at all, so the short
 * assertive headlines are byte-identical to what they were.
 */
@media screen and (min-width: 651px) {
	/*
	 * `h1[class]` rather than a bare class, deliberately.
	 *
	 * Two ladders already exist: `.motion-title` at 96px, and Work's own
	 * `.work-detail-page .work-title-long`, which is two classes and
	 * outranks a plain `h1.hero-title-l`. Work's heading kept winning while
	 * the markup looked correct. The attribute selector costs one point of
	 * specificity, which is exactly enough.
	 *
	 * The floor is 54px and it is the whole point. An earlier pass took the
	 * longest headline to 38px to hold four lines and Ben was right that it
	 * had gone too far: it fitted, and it stopped being display typography.
	 * Five lines at 58px is the better trade.
	 *
	 * `m` was 76px and `l` was 66px until 23 Sep 2026. Measured across all 49
	 * Work pages and 5 Case Studies at 1600, that left nineteen heroes past
	 * five lines and five of them at seven, because 76px overrides Work's own
	 * `work-title-medium` of 58px and a 63-character headline cannot hold five
	 * lines at 76px in a 612px column. 66 and 60 leave two heroes at six lines
	 * and none beyond, with the smallest desktop hero at 60px — still above
	 * the floor, and still above the 58/51/45px the master itself gives Work
	 * at this width. Ben approved the step down rather than a wider column or
	 * a shorter headline.
	 */
	h1[class].hero-title-m {
		font-size: clamp(54px, 4.4vw, 66px);
	}

	h1[class].hero-title-l {
		font-size: clamp(54px, 4vw, 60px);
	}

	h1[class].hero-title-xl {
		font-size: clamp(54px, 3.8vw, 58px);
	}
}

/* The promise under the artwork rather than across it: present, but not
   competing with the picture it describes. */
.hero-media-note {
	margin-top: 14px;
	font-size: var(--fs-body);
	line-height: 1.45;
	color: var(--muted, #6e675f);
	max-width: 46ch;
}

/*
 * The video lightbox — shared, and therefore here rather than in work.css.
 *
 * `site.js` binds it to `[data-work-video]`, which is any card on any
 * template, and builds the dialog itself. The rules lived in work.css, which
 * is enqueued only for the Work archive and Work singulars, so on a Motion
 * page the modal was injected with no styling at all — an unstyled div at the
 * end of <body> renders as a block below the footer with the iframe at its
 * intrinsic size, which is the tiny bottom-left player Ben found. Every future
 * template showing a video card would have hit the same thing.
 *
 * Moved unchanged: same selectors, same declarations, same order, including
 * `.video-modal-open` (the scroll lock, which is not named after the modal and
 * so is easy to leave behind) and the 651px block, which held nothing else.
 * The ten tokens it reads are all in tokens.css, which loads everywhere, and
 * nothing else defines these selectors — so Work pages render as before.
 */
/* ----------------------------------------------------------- video modal */
/*
 * Built once and reused. It is in the page from the start rather than created
 * on first use, so focus can move into it without waiting for markup.
 */
.work-video-modal {
	position: fixed;
	inset: 0;
	z-index: 9999;
	display: none;
	place-items: center;
	padding: 28px;
}

.work-video-modal.open {
	display: grid;
}

.work-video-modal-backdrop {
	position: absolute;
	inset: 0;
	border: 0;
	background: var(--work-modal-scrim);
	backdrop-filter: blur(8px);
	cursor: pointer;
}

.work-video-modal-dialog {
	position: relative;
	z-index: 2;
	width: min(1180px, 94vw);
	overflow: hidden;
	border-radius: 24px;
	background: var(--ink);
	color: var(--white);
	box-shadow: var(--work-modal-shadow);
}

.work-video-modal-bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 20px;
	min-height: 72px;
	padding: 15px 18px 15px 24px;
	border-bottom: 1px solid var(--work-modal-line);
}

.work-video-modal-platform {
	display: block;
	margin-bottom: 4px;
	font-size: max(calc(9px * var(--type-small)), var(--type-floor));
	font-weight: var(--fw-eyebrow);
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--work-modal-platform);
}

.work-video-modal-title {
	font-size: calc(17px * var(--type-reading));
}

.work-video-modal-close {
	width: 42px;
	height: 42px;
	border: 1px solid var(--work-modal-close);
	border-radius: var(--r-round);
	background: transparent;
	color: var(--white);
	font-size: calc(28px * var(--type-heading));
	line-height: 1;
	cursor: pointer;
}

.work-video-modal-frame {
	position: relative;
	aspect-ratio: 16 / 9;
	background: #000;
}

/*
 * An embed and an uploaded file fill the frame identically. The `video` case
 * arrived with Motion & Video (23 Sep 2026): its slots can hold either, and an
 * MP4 played in an iframe has no controls the page can style and no way to
 * carry captions.
 */
.work-video-modal-frame iframe,
.work-video-modal-frame video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
	background: #000;
}

.work-video-modal-help {
	display: flex;
	justify-content: space-between;
	gap: 18px;
	padding: 13px 22px 15px;
	color: var(--work-modal-help);
	font-size: max(calc(10px * var(--type-small)), var(--type-floor));
}

.work-video-modal-help a {
	color: var(--white);
	font-weight: var(--fw-eyebrow);
}

/* The page behind the dialog does not scroll while it is open. */
.video-modal-open {
	overflow: hidden;
}

@media screen and (min-width: 651px) {
	.work-video-modal {
		padding: 28px;
	}

	.work-video-modal-dialog {
		border-radius: 24px;
	}

	.work-video-modal-help {
		flex-direction: row;
	}
}

/*
 * An animated GIF in post content, served as video (form-core
 * inc/content-video.php). The master's own reset is `img { display: block;
 * width: 100% }`, so the picture these replace filled its column and scaled
 * with it; the video has to do exactly the same or the article reflows.
 * `height: auto` is the one addition — `width`/`height` on the element give
 * the intrinsic ratio, and without it a percentage width would fight the
 * declared pixel height.
 */
.content-video {
	display: block;
	width: 100%;
	height: auto;
}

/*
 * Reduced motion is honoured in `initContentVideo()`, not here: playback is a
 * property of the element, and no CSS can stop a video that has already been
 * told to autoplay. What CSS can do is make the controls the reader is given
 * instead look deliberate rather than like a browser default dropped into the
 * page.
 */
.content-video[controls] {
	background: var(--ink, #14110f);
}

/* --------------------------------------------------------------------------
 * Body copy — one role, site-wide (Ben, 26 Sep 2026)
 *
 * The build finished its page families at different times and each carried its
 * own paragraph size: 134 font-size declarations on paragraph roles across
 * thirteen stylesheets, ranging 13.5px to 20px for copy doing the same job.
 *
 * Ben's ruling is that the homepage hero body copy is the standard. Measured
 * off staging rather than chosen: Inter 400, 19.5/1.58 at 1600 and 820,
 * 16/1.52 through the laptop band, 17/1.58 on mobile. That curve is now
 * `--fs-body` and `--lh-body`, and the roles below inherit it instead of
 * setting their own.
 *
 * Deliberately not included: eyebrows, captions, card metadata, buttons,
 * navigation, footer microcopy and labels. Those are their own roles and Ben
 * excluded them. The line is "would a reader call this a paragraph" — a card's
 * one-line description is metadata, an article's prose is not.
 * ----------------------------------------------------------------------- */

.lead,
.copy,
.route-hero .lead,
.sub-hero .lead,
.cases-hero .lead,
.case-rich-hero .lead,
.case-rich-overview-copy p,
.case-rich-deep p,
.case-rich-deep-copy > p,
.work-library-hero .lead,
.work-detail-hero-copy .lead,
.work-detail-brief-copy > p,
.work-detail-idea-copy p,
.work-detail-motion-copy p,
.work-rich-text-copy p,
.work-rich-outcome-copy p,
.work-case-feature-copy p,
.insight-detail-copy > p,
.insights-v2-hero .lead,
.motion-reel-copy p,
.motion-value-card p,
.profile-intro-compact,
.contact-hero .lead,
.error-copy {
	font-family: var(--font-body);
	font-size: var(--fs-body);
	line-height: var(--lh-body);
	font-weight: 400;
}

/* The Start a Project form keeps the sizes it had (Ben, 7 Oct 2026): `--fs-planner-body` is the old body curve, 17 / 19.5 / 16 / 19.5, outside the 17.8px copy rule. */
.planner-page .copy,
.planner-step > p,
.planner-side > .copy {
	font-family: var(--font-body);
	font-size: var(--fs-planner-body);
	line-height: var(--lh-body);
	font-weight: 400;
}

/* -------------------------------------------------------------------------
 * The rest of the body copy is Inter (Ben, 29 Sep 2026 — Dave's list, item 3).
 *
 * The list above was drawn by asking "would a reader call this a paragraph",
 * and left a card's one-line description, a section intro, a FAQ answer, a
 * form's helper text and the footer blurb in Montserrat. Ben's ruling now is
 * that body copy is Inter everywhere and Montserrat stays for headings, card
 * titles and display roles; labels, captions, eyebrows, navigation, buttons and
 * metadata are not touched merely because they are not Inter.
 *
 * FAMILY ONLY. Size, weight and line-height stay each role's own — Dave's
 * proposal to flatten body copy to 14.5px is held for later review (Ben, same
 * day) and nothing here resolves it. Inter ships at 400, 700 and 800, so a
 * role written at 500 renders at 400 and one at 600 at 700.
 *
 * Which selectors are on this list is not a matter of taste: docs/verification/
 * text-audit.mjs walks every paragraph, list item and description on every page
 * and reports what is still set in Montserrat.
 * ----------------------------------------------------------------------- */

.footer-blurb,
.mobile-nav-utility p,
.section-head p,
.step-row p,
.work-head > p,
.faq-grid-head > p,
.faq-list details p,
.form-success-copy,
.form-success-more,
.journey-helps-text,
.field-error,
.contact-form-status,
.planner-form-status,
.route-title-row p,
.route-card p,
.specialist-card p,
.journey-insights-head p,
.journey-next-head p,
.journey-next-card p,
.journey-more-head p,
.symptom-card p,
.belief p,
.person-back p,
.legal-body p,
.legal-body li,
.contact-form small,
.case-performance-head p,
.case-approach-row p,
.insights-v2-intro p,
.insight-v2-card p,
.insight-lib-card p,
.insight-feature p,
.insight-detail-standfirst,
.insight-from-work-head p,
.insight-editorial-split-copy p,
.insight-author-visual-copy p,
.insights-v2-empty,
.motion-service-text,
.motion-deliverable p,
.motion-faq-list p,
.work-video-intro p,
.qa-grid-compact .qa .qa-answer,
.option small,
.planner-panel .option small,
.brief-block-copy,
.brief-block-list li,
.summary-line,
.consent {
	font-family: var(--font-body);
}

/* A retained article's body is pasted content: paragraphs, list items, divs and the text inside them at any depth.
   Headings and whatever sits inside a heading stay as they are. */
.insight-detail-copy :is(p, li, div, a, em, strong, span):not(:is(h1, h2, h3, h4, h5, h6) *) {
	font-family: var(--font-body);
}
