/*
 * Bohardien component styles, concatenated at deploy time.
 * Source of truth is css/components/*.css. Do not edit this file.
 */

/* ======================= article-card.css ======================= */
/*
 * article-card
 *
 * News teaser with an article and a press release variant: media band with a
 * type badge, byline and date, headline, one line standfirst, read cue.
 *
 * Design section 51, the news index grid.
 *
 * The hit area is a pseudo-element overlay on the headline's anchor rather than
 * an anchor wrapped around everything, so the link's accessible name is the
 * headline, not the card's whole text run. The hover state therefore lives on
 * the card. The focus indicator is left entirely to the global :focus-visible
 * rule in parent-overrides.css: it lands on the headline text, which sits
 * inside the body padding, so the card's overflow clipping never cuts it.
 * Nothing about the ring is restated here.
 *
 * Panel tokens are consumed directly. Design section 51 wraps the grid in a
 * band that remaps the page tokens to their panel equivalents (--card-line to
 * --panel-line, --well to --panel-well, --kicker to --panel-kicker-strong,
 * --link and --fg to --panel-fg, and so on). No CSS in this theme performs that
 * remap, so reading the panel tokens straight is what actually reproduces the
 * design, and it matches the treatment this file already gave --panel-card.
 * Callers need no custom-property block of their own.
 * --panel-kicker-strong is absent from tokens.css; --panel-kicker carries the
 * same #C3AFA0 the design defines for it, so the meta line uses that.
 *
 * No media queries at all: the media box is a ratio, the body is fluid, row
 * wrapping is the caller's job (.cards-3up in base.css), and the global
 * prefers-reduced-motion block in base.css neutralises the transitions.
 */

.article-card {
	/* Local scale. Token arithmetic only, so a later variant can retune the
	   card by overriding these rather than restating rules. */
	--article-card-pad: calc( var(--gap-lg) + var(--gap-2xs) ); /* 28px */
	--article-card-pad-x: var(--gap-lg);
	--article-card-gap: var(--gap-sm);
	--article-card-media-ratio: 23 / 10;

	/* Stated rather than inherited: the card carries a border and padding
	   inside a grid track, and the parent theme's box-sizing is not ours to
	   assume. Matches .case-card. */
	box-sizing: border-box;
	position: relative;
	display: flex;
	flex-direction: column;
	min-width: 0;
	background: var(--panel-card);
	border: 1px solid var(--panel-line);
	border-radius: var(--radius-xl);
	overflow: hidden;
	color: var(--panel-fg-2);
	box-shadow: var(--shadow-card);
	/* The design's two transitioned properties, no more. It changes background
	   and border colour on hover without transitioning either. */
	transition:
		transform var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease);
}

/* Hover sits on the card, since the overlay makes the whole card the target. */
.article-card:hover {
	background: var(--panel-card-hover);
	border-color: var(--panel-line-strong);
	box-shadow: var(--shadow-card-hover);
	transform: var(--card-hover-lift);
}

/* Kept in its own rule, never merged into :hover, so a keyboard user's focus
   indicator can never be painted out by a hover declaration. This surface echo
   is an addition beyond the design and is recorded as such: the design has no
   focus state. It changes no outline and no indicator, and it does not lift, so
   the ring stays exactly where the eye left it. */
.article-card:focus-within {
	background: var(--panel-card-hover);
	border-color: var(--panel-line-strong);
	box-shadow: var(--shadow-card-hover);
}

/* Media ---------------------------------------------------------------- */
.article-card__media {
	position: relative;
	flex: none;
	aspect-ratio: var(--article-card-media-ratio);
	overflow: hidden;
	background: var(--panel-well);
}

/* Only the height and the fit need overriding: max-width and display: block
   already come from the base.css img rule. */
.article-card__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Type badge. Above the image, below the overlay, so nothing is occluded, and
   transparent to the pointer so clicks reach the link. Padding, line height and
   type are the same token set as .case-card__tag: one design element, one
   rendered size. */
.article-card__type {
	position: absolute;
	z-index: 2;
	inset-block-start: var(--gap-sm);
	inset-inline-end: var(--gap-sm);
	padding: var(--gap-2xs) var(--gap-xs);
	border-radius: var(--radius-pill);
	background: var(--panel);
	color: var(--panel-fg);
	font-size: var(--fs-3xs);
	font-weight: var(--fw-medium);
	line-height: var(--lh-ui);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	pointer-events: none;
}

/* Body ----------------------------------------------------------------- */
.article-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--article-card-gap);
	/* 28px 24px 32px. The bottom is the design's 32px exactly, and it is the
	   same arithmetic .case-card__body uses off the same 28px pad. */
	padding:
		var(--article-card-pad)
		var(--article-card-pad-x)
		calc( var(--article-card-pad) + var(--gap-2xs) );
}

/* Byline and date. Type size and line height come from .small in base.css. */
.article-card__meta {
	color: var(--panel-kicker);
	font-weight: var(--fw-regular);
}

/* There is deliberately no rule for .article-card__sep. The template emits
   whitespace on both sides of the middot, which collapses to the single space
   either side of it that the design's own byline string carries. Optical
   padding on top of that would space the one glyph twice over. */

/* Headline. font-family is stated because the heading_level arg can resolve to
   p or div, which the base h1-h6 rule does not cover. */
.article-card__title {
	margin: 0;
	color: var(--panel-fg);
	/* The serif, matching the design's h1 to h4 rule. It is stated here rather
	   than inherited because heading_level can resolve to p or div, which the
	   base heading rule does not cover. Was --font-sans until 11/09/2026, when
	   the base heading face was corrected. */
	font-family: var(--font-serif);
	font-size: var(--fs-h5);
	/* The design's news card headings are h3 at weight 500, the heavier of its
	   two h3 weights. */
	font-weight: var(--fw-medium);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-heading);
	overflow-wrap: break-word;
}

.article-card__link,
.article-card__link:hover,
.article-card__link:focus,
.article-card__link:active {
	color: inherit;
	text-decoration: none;
}

/* The card-wide hit area. Its containing block is .article-card, the nearest
   positioned ancestor. Nothing focusable is allowed inside the card body, so
   the overlay cannot cover a control: the template strips interactive markup
   out of the standfirst, and the badge and the cue are both inert. */
.article-card__link::after {
	content: "";
	position: absolute;
	z-index: 3;
	inset: 0;
	border-radius: var(--radius-xl);
}

/* Standfirst. margin and max-width undo the base p rules, whose 72ch measure
   is meant for prose columns. */
.article-card__standfirst {
	margin: 0;
	flex: 1;
	max-width: none;
	color: var(--panel-fg-2);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-ui);
	overflow-wrap: break-word;
}

/* Read cue. Static text, not a control: the headline anchor carries the action,
   so nothing here is focusable or announced as a second link. The design paints
   it var(--link), which section 51 remaps to --panel-fg. That remap is not
   implemented anywhere in this theme, so the panel token is read directly. */
.article-card__cue {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	margin-block-start: auto;
	color: var(--panel-fg);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	line-height: var(--lh-ui);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
}

/* Sized in em against the cue, so the icon tracks its type instead of needing
   an icon size the token layer does not carry. 1.25em is the design's 16px
   glyph against its 13px cue. */
.article-card__cue-icon {
	flex: none;
	width: 1.25em;
	height: 1.25em;
}

/*
 * Variants. Section 51 gives the article and the press release cards identical
 * treatment: only the badge wording differs, and that comes from the template.
 * .article-card--article and .article-card--press-release are therefore
 * styling hooks with nothing to declare yet, kept so a later divergence has
 * somewhere to land without touching the markup.
 */

/* ======================= audience-grid.css ======================= */
/*
 * Component: audience-grid.
 *
 * "Who we advise": a set of audience items, a heading plus a description each.
 * Design section 12 is the cards variant, section 19 the rows variant.
 *
 * Layout is fluid throughout: no media query of its own. The cards variant
 * borrows .cards-2up, whose minmax() floor wraps 2 columns to 1 at roughly
 * 600px; the rows variant borrows .grid-split, which base.css already stacks.
 *
 * No background is declared: var(--bg) is the page ground and is set once, on
 * body in parent-overrides.css.
 *
 * Off-scale spacing is composed from two gap tokens rather than rounded to the
 * nearest one, and each such rule names the design value it reproduces.
 */

/* Section header ------------------------------------------------------- */

/* Design section 19 heads its rows with a plain block. */
.audience-grid__header {
	padding-bottom: calc(var(--gap-xl) + var(--gap-2xs)); /* design 44px */
}

/* Design section 12 sets the heading block and the optional intro on one
   baseline row. Flex, not grid: wrap handles narrow viewports without a
   breakpoint. Scoped to a modifier because section 19 never shows it. */
.audience-grid__header--inline {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: var(--gap-xl);
}

.audience-grid__header-lead {
	flex: 1 1 auto;
	min-width: 0;
}

/* The section heading takes its level from args, so the class carries the
   type. Regular weight and display leading, against base's h2 defaults. */
.audience-grid__title {
	margin: 0;
	color: var(--fg);
	font-size: var(--fs-h2);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-heading);
}

.audience-grid__intro {
	flex: 0 1 var(--measure-intro);
	min-width: 0;
	margin: 0;
}

/* Kickers -------------------------------------------------------------- */

/* Base .kicker is --fs-3xs (11.5px) and caps at --measure-label (20ch). Every
   label in this component, the section eyebrow and the 01/02/03 ordinals
   alike, is 13px in the design, and none of them is capped: at 20ch and
   --ls-kicker-wide, "Who we work with" would wrap to two lines, which the
   slice never does. */
.audience-grid__kicker {
	max-width: none;
	font-size: var(--fs-xs);
}

/* The card is a flex column, so its own gap owns the spacing: drop the
   standing margin .kicker carries for block contexts. The section header and
   the row leads are block, and there the design's 20px kicker margin is
   exactly base's. */
.audience-grid__kicker--card {
	margin-bottom: 0;
}

/* Items ---------------------------------------------------------------- */

/* Both variants render a real list, ordered when the ordinals are on. The
   markers are suppressed here, which is why the markup carries role="list". */
.audience-grid__items {
	margin: 0;
	padding-left: 0;
	list-style: none;
}

/* Cards variant, design section 12 ------------------------------------- */

/* .cards-2up supplies the auto-fit floor; the design widens its gap. */
.audience-grid__items--cards {
	gap: var(--gap-xl);
}

.audience-grid__card {
	display: flex;
	flex-direction: column;
	gap: calc(var(--gap-md) - var(--gap-2xs)); /* design 16px */
	min-width: 0;
	padding-block: calc(var(--gap-lg) + var(--gap-2xs) * 2); /* design 32px */
	border-top: 1px solid var(--line);
}

/* The design's card title is a flat 21px. --fs-lg is the static token nearest
   it, within 1px at every viewport; the fluid --fs-h5 would overshoot to 23px
   at the 1800px width the slice was drawn at and drop to 19px on a phone. */
.audience-grid__card-title {
	margin: 0;
	color: var(--kicker);
	font-size: var(--fs-lg);
	font-weight: var(--fw-regular);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-body);
}

.audience-grid__card-body {
	color: var(--fg-2);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
}

/* The card is the measure here, so drop base's 72ch prose cap. */
.audience-grid__card-body p {
	max-width: none;
}

/* Base already zeroes a trailing p. This earns its place for a trailing ul or
   ol out of wp_kses_post, which base.css does not style. */
.audience-grid__card-body > :last-child {
	margin-bottom: 0;
}

/* Rows variant, design section 19 -------------------------------------- */

/* .grid-split supplies the 0.85fr / 1.15fr columns, the 72px gap and the
   start alignment, all three verbatim from the design; the row adds its own
   rule and vertical padding. */
.audience-grid__row {
	padding-block: calc(var(--gap-2xl) - var(--gap-2xs)); /* design 52px */
	border-top: 1px solid var(--line);
}

.audience-grid__row-lead {
	min-width: 0;
}

.audience-grid__row-title {
	margin: 0;
	max-width: var(--measure-label);
	color: var(--fg);
	font-size: var(--fs-h3);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-tight);
}

/* The design wraps the description in a flex column: paragraphs are spaced by
   an 18px gap rather than by their own margin, and they fill the 1.15fr
   column, roughly 930px at the 1800px container. */
.audience-grid__row-body {
	display: flex;
	flex-direction: column;
	gap: calc(var(--gap-sm) + var(--gap-2xs)); /* design 18px */
	min-width: 0;
	color: var(--fg-2);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
}

/* max-width undoes base's 72ch measure, which is meant for prose columns and
   would wrap this description about 30% early inside its own column. */
.audience-grid__row-body p {
	margin: 0;
	max-width: none;
}

/* As on the card: a trailing list, whose own margin would otherwise add to
   the flex gap above. */
.audience-grid__row-body > :last-child {
	margin-bottom: 0;
}

/* In-copy links -------------------------------------------------------- */

/* --link against the surrounding --fg-2 body copy is 2.38:1, below the 3:1
   WCAG 1.4.1 requires where colour alone distinguishes a link, so the
   underline is not left to the user agent default. Colour, hover and the
   focus ring stay global: nothing here restates them, so the indicator
   survives hover. */
.audience-grid__card-body a,
.audience-grid__row-body a {
	text-decoration: underline;
	text-decoration-thickness: 1px;
	text-underline-offset: 0.18em;
}

/* ======================= before-after.css ======================= */
/*
 * before-after
 *
 * A comparison slider inside the "What changed." band on a case study, design
 * section 42. One per row of the `visuals` repeater, so a case study can carry
 * several, for example one per page of a rewritten contract.
 *
 * The two images are stacked, not placed side by side, and the handle wipes the
 * before image across the after one. That only works if they occupy exactly the
 * same box, which is why both are absolutely positioned into a frame whose
 * aspect ratio comes from the after image. Her pages are all 794x1123, so they
 * already agree; the frame enforces it anyway, because an editor will eventually
 * upload a pair that does not.
 *
 * The control is a real range input rather than a div with drag handlers. It is
 * reachable by keyboard, announced by a screen reader, works with touch and with
 * a mouse, and needs no JavaScript to be operable. The script only mirrors its
 * value into a custom property; without it the slider still moves, it simply
 * does not wipe, so the after image is what a visitor sees.
 */

.before-after {
	--ba-pos: 50%;
	/* Her pages are A4 portrait. Left to fill the content column they render
	   about 1170px wide and so over 1650px tall, and three of them turned this
	   page into ten thousand pixels of scrolling. Cap the HEIGHT and let the
	   aspect ratio give back the width, rather than capping the width: a
	   landscape pair then still uses the full column, and only tall portrait
	   documents are reined in.

	   calc() multiplies by a number and divides by a number, so the ratio
	   substitutes straight in: calc(min(76vh, 780px) * 794 / 1123). */
	--ba-max-height: min(76vh, 780px);
	position: relative;
	width: 100%;
	max-width: calc(var(--ba-max-height) * var(--ba-ratio, 794 / 1123));
	margin: 0 auto;
}

.before-after + .before-after {
	margin-top: var(--gap-2xl);
}

.before-after__frame {
	position: relative;
	overflow: hidden;
	aspect-ratio: var(--ba-ratio, 794 / 1123);
	background: var(--bg-2, #fff);
	border: 1px solid var(--line);
}

.before-after__img {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	/* contain, not cover: these are documents. Cropping a contract to fill a box
	   would cut the very text the comparison is about. */
	object-fit: contain;
	object-position: top center;
}

/* The before image is clipped from the left edge to the handle. inset-inline-end
   rather than width so it never rescales as the handle moves: the image keeps
   its own geometry and only the window onto it changes. */
.before-after__clip {
	position: absolute;
	inset: 0;
	inset-inline-end: calc(100% - var(--ba-pos));
	overflow: hidden;
}

.before-after__clip .before-after__img {
	/* Pinned to the frame's width, not the clip's, so the two halves line up
	   exactly under the seam. */
	width: var(--ba-frame-width, 100%);
}

/* The seam, drawn on the clip so it always sits at the cut. */
.before-after__clip::after {
	content: "";
	position: absolute;
	inset-block: 0;
	inset-inline-end: 0;
	width: 2px;
	background: var(--accent, #fff);
}

.before-after__range {
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	width: 100%;
	margin: 0;
	padding: 0;
	height: 100%;
	background: transparent;
	appearance: none;
	-webkit-appearance: none;
	cursor: ew-resize;
}

.before-after__range::-webkit-slider-thumb {
	appearance: none;
	-webkit-appearance: none;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: var(--accent, #fff);
	border: 2px solid var(--bg, #0a1e32);
	cursor: ew-resize;
}

.before-after__range::-moz-range-thumb {
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: var(--accent, #fff);
	border: 2px solid var(--bg, #0a1e32);
	cursor: ew-resize;
}

.before-after__range::-moz-range-track {
	background: transparent;
}

/* The focus ring belongs on the control, and the global :focus-visible rule in
   parent-overrides.css draws it. Nothing here removes it. */

.before-after__labels {
	display: flex;
	justify-content: space-between;
	gap: var(--gap-sm);
	margin-top: var(--gap-xs);
}

.before-after__side {
	color: var(--fg-2);
}

.before-after__caption {
	margin: var(--gap-xs) 0 0;
	color: var(--fg-2);
}

/* ======================= case-card.css ======================= */
/*
 * case-card
 *
 * Case study card: photo with a caller-set focal point, practice-area chip,
 * title, short description and a read-the-study affordance. The whole card is
 * one link.
 *
 * Design sections 3 (Selected work, the larger card) and 13 (Related case
 * studies, the .case-card--compact variant).
 *
 * The hit area is a pseudo-element overlay on the title's anchor rather than an
 * anchor wrapped around everything, so the link's accessible name is the case
 * study title, not the card's whole text run. The overlay is transparent and
 * paints nothing, so the global :focus-visible ring in parent-overrides.css is
 * the only focus indicator: this file never restates it.
 *
 * Every variant difference is carried on a custom property set on the root, so
 * no selector in this file is more than one class deep.
 *
 * No media queries: the media box is a ratio, the body is fluid, and the row
 * wrapping is the caller's job (.cards-3up from base.css). Transitions are
 * neutralised by the global prefers-reduced-motion block in base.css.
 */

.case-card {
	/* Block padding, 32px top and 36px bottom, reached by additive arithmetic
	   on the 4px rung. The gap scale holds no 28, 32 or 36, so all four rungs
	   are reported as off-scale spacing rather than presented as token hits.
	   Inline padding is --gap-lg (24px) against the design's 30px and 26px. */
	--case-card-pad-block-start: calc( var(--gap-xl) - var(--gap-2xs) * 2 ); /* 32px */
	--case-card-pad-block-end: calc( var(--gap-xl) - var(--gap-2xs) );      /* 36px */
	--case-card-pad-inline: var(--gap-lg);
	--case-card-gap: var(--gap-md);

	/* Media ratio. The design pins 280px against a card about 530px wide, but
	   that width came from a 32px grid gap; on base.css .cards-3up (24px gap)
	   inside .container the 3-up track is about 552px, so the ratios are
	   re-derived there: 2 / 1 gives 276px and 12 / 5 gives 230px exactly. */
	--case-card-media-ratio: 2 / 1;

	/* Arrow glyph. The design sizes it 15px in section 3 and 16px in section
	   13, deliberately larger than its 13px label. tokens.css has no icon
	   size, so both are reported as hardcoded. */
	--case-card-icon: 15px;

	/* Variant-driven values, so .case-card--compact retunes the card without
	   any descendant selector. */
	--case-card-chip-bg: var(--accent);
	--case-card-chip-fg: var(--accent-fg);
	--case-card-title-size: clamp( var(--fs-xl), 1.7vw, calc( var(--fs-xl) + var(--gap-2xs) ) );
	--case-card-title-weight: var(--fw-regular);
	--case-card-title-line: var(--lh-heading);
	--case-card-desc-line: var(--lh-body-sm);

	/* The design remaps these three on the section that hosts the cards. They
	   are the card's own border, well and affordance colours, so the card
	   carries the mapping itself instead of depending on section chrome:
	   --link resolves to white here, not the #508CF0 root accent. */
	--card-line: var(--panel-line);
	--well: var(--panel-well);
	--link: var(--panel-fg);

	/* Stated rather than inherited: the card carries a border and padding
	   inside a grid track, and the parent theme's box-sizing is not ours to
	   assume. block-size lets the card fill an <li> wrapper, so a caller can
	   give a row of cards real list semantics without losing equal heights. */
	box-sizing: border-box;
	position: relative;
	display: flex;
	flex-direction: column;
	block-size: 100%;
	min-width: 0;
	background: var(--panel-card);
	border: 1px solid var(--card-line);
	border-radius: var(--radius-xl);
	overflow: hidden;
	box-shadow: var(--shadow-card);
	transition:
		transform var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease);
}

/* Hover only. :focus-visible is deliberately absent from this rule so a
   keyboard user never loses the global ring to a hover repaint. */
.case-card:hover {
	background: var(--panel-card-hover);
	border-color: var(--line-strong);
	transform: var(--card-hover-lift);
	box-shadow: var(--shadow-card-hover);
}

/* Media */
.case-card__media {
	position: relative;
	aspect-ratio: var(--case-card-media-ratio);
	overflow: hidden;
	background: var(--well);
}

.case-card__img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: var(--case-card-focal, 50% 50%);
}

/* Practice-area chip */
.case-card__tag {
	position: absolute;
	inset-block-start: var(--gap-sm);
	inset-inline-end: var(--gap-sm);
	z-index: 2;
	padding: var(--gap-2xs) var(--gap-xs);
	border-radius: var(--radius-pill);
	background: var(--case-card-chip-bg);
	color: var(--case-card-chip-fg);
	font-size: var(--fs-3xs);
	font-weight: var(--fw-medium);
	line-height: var(--lh-ui);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	pointer-events: none;
}

/* Chip on a card with no photo: sits at the top of the body instead. Both
   design sections always carry a photo, so this placement is an invented
   defensive state and is reported as such. */
.case-card__tag--static {
	position: static;
	align-self: flex-start;
}

/* Body */
.case-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--case-card-gap);
	padding:
		var(--case-card-pad-block-start)
		var(--case-card-pad-inline)
		var(--case-card-pad-block-end);
}

/* Only the four declarations that base.css ramps per heading level are set
   here, because the level is a caller argument and must not change the card's
   type. Colour, family and margin come from the base heading baseline. */
.case-card__title {
	font-size: var(--case-card-title-size);
	font-weight: var(--case-card-title-weight);
	line-height: var(--case-card-title-line);
	letter-spacing: var(--ls-heading);
}

.case-card__link {
	color: inherit;
	text-decoration: none;
}

/* The card-wide hit area. Its containing block is .case-card, the nearest
   positioned ancestor. It paints nothing, so the focus ring on the anchor
   itself stays visible through it. */
.case-card__link::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 3;
	border-radius: var(--radius-xl);
}

/* Never clamped. A line clamp counts lines, so the 1.4.12 text-spacing
   overrides and a 200% zoom both push text past the cut and overflow: hidden
   destroys it. Design section 13 already runs the paragraph unclamped; the
   affordance row's auto top margin absorbs the height difference in both
   variants, so the affordances still line up across a row. */
.case-card__desc {
	margin: 0;
	max-width: none;
	font-size: var(--fs-body-sm);
	line-height: var(--case-card-desc-line);
	color: var(--fg-2);
}

.case-card__cta {
	margin-block-start: auto;
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	line-height: var(--lh-ui);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	color: var(--link);
}

.case-card__icon {
	width: var(--case-card-icon);
	height: var(--case-card-icon);
	flex: none;
	transition: transform var(--dur) var(--ease);
}

/* The arrow turn and the growing underline are the shared .cue-label pattern
   in base.css, which every arrow affordance on the site uses. Nothing local. */

/* Compact variant, design section 13 */
.case-card--compact {
	--case-card-pad-block-start: calc( var(--gap-lg) + var(--gap-2xs) );    /* 28px */
	--case-card-pad-block-end: calc( var(--gap-xl) - var(--gap-2xs) * 2 );  /* 32px */
	--case-card-gap: var(--gap-sm);
	--case-card-media-ratio: 12 / 5;
	--case-card-icon: 16px;
	--case-card-chip-bg: var(--bg);
	--case-card-chip-fg: var(--fg);
	--case-card-title-size: var(--fs-xl);
	--case-card-title-weight: var(--fw-medium);
	--case-card-title-line: var(--lh-tight);
	--case-card-desc-line: var(--lh-ui);
}

/* ======================= cta-band.css ======================= */
/*
 * Component: cta-band
 *
 * Call-to-action band on the inverted panel ground. Design sections 9 and 16
 * are the same band with different copy, so one part serves both. Section 16's
 * literal #0E1D30 is exactly --panel, so no ground modifier is needed.
 *
 * Built on .section and .section--panel (base.css) and .lede (base.css).
 * .container is defined in parent-overrides.css, not base.css, so the band's
 * width containment and gutter are inherited from the parent-neutralisation
 * layer. .screen-reader-text belongs to the Underscores parent theme; only its
 * :focus state is patched in parent-overrides.css. Provenance is spelled out
 * because the gutter deviation was decided in parent-overrides, not here.
 *
 * The two columns are declared locally rather than borrowed from .grid-even.
 * .grid-even is an even split at a 72px gap that stacks only through base's
 * 899px breakpoint; the design is 1 / 0.9 at 80px, which is --split-wide
 * paired with --gap-4xl.
 *
 * UPSTREAM, declared not worked around: .cta-band__action duplicates the
 * Stage 2 chrome pill .header-cta (custom.css) in geometry, type, tracking and
 * transition, differing only in padding and colour. The right fix is one pill
 * primitive in base.css with colour modifiers over it, which would edit frozen
 * Stage 2 chrome and three other components' surfaces. Logged for the human as
 * an upstream item rather than duplicated silently a fourth time.
 */

/* Design: position:relative and the hairline come from .section, the panel
   ground from .section--panel, the 110px rhythm from --section-y via .section.
   Only the clip is new, and it is what lets a later decorative flourish sit
   inside the band. The heading measure is a local custom property so the
   modifier below stays a single-class selector. */
.cta-band {
	--cta-band-measure: 16ch; /* design section 9 */
	overflow: hidden;
}

/* Design section 16 sets the same heading at 30ch. The two source sections
   were authored separately; both measures are the design's, so both ship.
   No ch token covers either: --measure-label is 20ch and is base.css's 11.5px
   .kicker width, which has no business governing a 52px display heading.
   Declared as literals with this justification, the same way faq-accordion.css
   declares its 260px floor. */
.cta-band--wide {
	--cta-band-measure: 30ch;
}

/* Design: display:grid;grid-template-columns:minmax(0,1fr) minmax(0,0.9fr);
   gap:80px;align-items:center. Expressed as a wrapping flex row on purpose.
   An explicit two-track grid cannot both hold a 1 / 0.9 ratio and collapse to
   one column without a breakpoint, and --split-wide's minmax(0, ...) floors
   would let the tracks overflow the clip on a 390px viewport. Flex wrap with a
   real floor keeps the design's asymmetry, keeps the band breakpoint-free in
   its own right, and cannot overflow. */
.cta-band__layout {
	display: flex;
	flex-wrap: wrap;
	gap: var(--gap-4xl);
	align-items: center;
}

/* flex-grow 1 against 0.9 reproduces --split-wide's ratio exactly: at the
   1800px container, 1704px of content less the 80px gap splits 855 / 769,
   the design's own column widths. The floor is the min(340px, 100%) pattern
   expertise-card.css uses, so the row wraps rather than crushing the heading
   and never exceeds the container below 340px. */
.cta-band__head,
.cta-band__body {
	flex: 1 1 0;
	min-width: min(340px, 100%);
}

.cta-band__body {
	flex-grow: 0.9;
}

/* Display-scale heading whatever level the caller passes, so the band looks
   identical as an h2 or an h3. text-wrap:pretty is the design page's own body
   value (line 40); base.css sets balance on all headings, which is a different
   wrapping algorithm, so it is restored here rather than left different. */
.cta-band__heading {
	max-width: var(--cta-band-measure);
	color: var(--panel-fg);
	font-size: var(--fs-h1);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-heading); /* design -0.016em, token -0.015em */
	text-wrap: pretty;
}

/* .lede carries the 20px size and the 52ch measure, both exact to the design.
   The band lifts the copy to full panel white and tightens the leading to the
   design's 1.55. 34px is derived, not approximated: --gap-lg 24 + --gap-xs 10.
   Single class: the component layer loads after base.css, and the paragraph is
   never :last-child here because the actions row always follows it. */
.cta-band__text {
	margin-bottom: calc(var(--gap-lg) + var(--gap-xs)); /* design 34px */
	color: var(--panel-fg);
	line-height: var(--lh-ui); /* design 1.55 */
}

/* One action today. The flex row is the design's own, and it keeps a second
   pill wrapping cleanly beside the first. */
.cta-band__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--gap-sm);
}

/* The band's pill is white by default and turns accent on hover, the inverse
   of the accent-first .button-primary, so it is its own class.
   Padding is the design's 15px 26px derived from the gap scale rather than
   approximated: 14 + 1 and 24 + 2. No border is declared, because the design
   paints none in either state, so the 1px it used to hold is absorbed into the
   padding instead. line-height:normal is the design's rendered value: the
   slice's anchor sets no line-height and the design page's body sets none
   either, so the 13px label sizes at normal, about 1.2. Inheriting instead
   would take --lh-body 1.72 from parent-overrides.css and make the theme's
   only band control roughly 6px taller than the slice. */
.cta-band__action {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	padding: calc(var(--gap-sm) + var(--gap-2xs) / 4) calc(var(--gap-lg) + var(--gap-2xs) / 2);
	border-radius: var(--radius-pill);
	background: var(--panel-fg);
	color: var(--panel);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	line-height: normal;
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	text-decoration: none;
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* Design: style-hover paints the pill accent. Hover only. */
.cta-band__action:hover {
	background: var(--accent);
	color: var(--accent-fg);
	text-decoration: none;
}

/* The optional second action, outlined rather than filled. Only the About
   band carries one: the design pairs a filled "Get in touch" with a "Career"
   pill drawn as border:1px solid var(--panel-line-strong) on no fill.

   Padding is the design's 14px 24px, one step down from the filled pill's
   15px 26px, which is what keeps the two the same height once the border is
   counted. Taking --gap-sm and --gap-lg straight gives exactly that pair.

   Hover inverts to the filled treatment rather than going accent, which is
   the design's own style-hover for this button and keeps the accent reserved
   for the primary. */
.cta-band__action--ghost {
	padding: var(--gap-sm) var(--gap-lg);
	border: 1px solid var(--panel-line-strong);
	background: transparent;
	color: var(--panel-fg);
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.cta-band__action--ghost:hover {
	background: var(--panel-fg);
	border-color: var(--panel-fg);
	color: var(--panel);
}

/* parent-overrides.css raises classed anchors to (0,2,0) for :link and
   :visited, so the ghost needs the same reach or a visited second action
   reverts to the primary's dark-on-light colour. */
.cta-band__action--ghost:link,
.cta-band__action--ghost:visited {
	color: var(--panel-fg);
}

.cta-band__action--ghost:hover:link,
.cta-band__action--ghost:hover:visited {
	color: var(--panel);
}

/* Focus is styled apart from hover, and never on accent. The global ring in
   parent-overrides.css is 2px of --accent plus a translucent --accent glow, so
   an accent fill under it drops the ring's inner boundary to 1.00:1 and its
   glow to 2.47:1, both below the 3:1 that WCAG 2.1 AA 1.4.11 requires. Holding
   the pill white keeps the ring at 5.13:1 on both edges. --fg and --panel-fg
   are both #FFFFFF, so the fill does not move: the ring alone carries the
   state, which is what the other three pills in the theme do. Ordered after
   :hover so a pointer-hovered, keyboard-focused pill keeps the readable
   pairing. The ring itself is not restated here. */
.cta-band__action:focus-visible {
	background: var(--fg);
	color: var(--bg);
	text-decoration: none;
}

/* The arrow is decorative and sized by its own width and height attributes.
   It must never be the thing that shrinks when the label is long. */
.cta-band__icon {
	flex: 0 0 auto;
}

/* Forced colours replace the pill's background, which is its whole visual
   boundary, so give it an explicit system edge. Matches the pattern in
   article-card.css and case-card.css. The global focus ring is forced to a
   system colour and survives, so it is not restated. */
@media ( forced-colors: active ) {
	.cta-band__action {
		border: 1px solid ButtonText;
	}
}

/* ======================= expertise-card.css ======================= */
/*
 * Expertise card: one practice-area row in the Our Expertise list.
 *
 * Design source: section 2. The design lays each row out as a three-track grid,
 * 104px / minmax(210px, 0.9fr) / minmax(250px, 1.1fr) with a 56px gap, on a
 * hairline top rule, and lifts the row plus nudges it 20px right on hover.
 *
 * Rebuilt as two fluid tracks rather than three: three tracks carry a 676px
 * combined floor and the design has no breakpoints to stack them at 390px. The
 * numeral and the title travel together in .__head, the description and the
 * affordance in .__body, and the two halves auto-fit. The floor is composed
 * from the column gap, the house pattern documented at faq-accordion.css:64,
 * so the grid is pinned to exactly two tracks and the stacking point cannot
 * drift. At the 1800px container (1656px of content) the halves land at 788px
 * each against the design's own 808px head and 792px body, roughly 20px per
 * side; below 760px of row width they stack.
 *
 * The hover nudge is a transform, not padding. As padding it sat inside the
 * grid's own content box, so between 826px and 847px of viewport the row was
 * two columns at rest and one stacked column while hovered or focused, and
 * could oscillate under the cursor.
 *
 * No card shadow here, deliberately. Convention 6 puts --shadow-card and
 * --card-line on cards, and this row is not a boxed surface in the design: it
 * is a full-bleed band ruled top and bottom with a hairline, whose only
 * resting depth is that rule and whose hover state is the --lift ground plus
 * the nudge. A shadow would draw a card edge the design does not have.
 *
 * This file is picked up by the deploy-time concatenation into
 * css/components.css, which functions.php:52 enqueues. There is no
 * per-component enqueue list, and css/components.css has not been generated
 * yet, so no component ships CSS until it is.
 */

.expertise-card {
	/* Component dimensions and the design literals no token covers, in the form
	   testimonial-block.css:18 uses.

	   --expertise-card-numeral: the design sets the numeral at a static 40px.
	   No font-size token matches it: --fs-h3 tops out at 36px and sits below
	   --fs-h2 at every point on the curve, which would make the numeral smaller
	   than the title it labels and invert the design's hierarchy.

	   --expertise-card-numeral-col: the design's numeral column is 104px. It is
	   narrowed to 72px here because the numeral travels with the title rather
	   than in a track of its own. Two tabular digits at 40px measure about
	   44px, so every title still starts at the same x, 128px in rather than
	   160px. */
	--expertise-card-numeral: 40px;
	--expertise-card-numeral-col: 72px;

	/* Title size. The design holds a deliberate step between the section
	   heading, clamp(32px, 3.4vw, 50px), and the row title,
	   clamp(28px, 3vw, 42px). Taking --fs-h2 would render every row title at
	   exactly the size base.css gives the caller's own h2 above it and flatten
	   the hierarchy, so the design's clamp is kept verbatim. It sits between
	   --fs-h3 and --fs-h2 at every point. */
	--expertise-card-title: clamp(28px, 3vw, 42px);

	/* The width the title asks for before it gives up its line and wraps below
	   the numeral, so the 128px numeral gutter is not spent at phone width. */
	--expertise-card-title-min: 12ch;

	position: relative;
	display: grid;
	/* Real floor per decision 3, composed from the column gap as
	   faq-accordion.css:70 does: half the track, less half the 56px gap, less
	   4px for sub-pixel rounding. 340px is the resting floor, the 128px numeral
	   gutter plus a title at its own 28px floor. The 100% ceiling on the clamp
	   keeps the row inside a container narrower than that floor. */
	grid-template-columns: repeat(auto-fit, minmax(clamp(340px, calc(50% - var(--gap-2xl) / 2 - var(--gap-2xs)), 100%), 1fr));
	gap: var(--gap-2xl);
	align-items: start;
	padding-block: var(--gap-xl);
	padding-inline: 0 var(--gap-lg);
	border-top: 1px solid var(--line);
	transition: background-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

/* The caller renders the rows as a list, per convention 1. This only strips the
   marker; the rows carry their own rhythm and rules. */
.expertise-card-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

/* No closing rule. The design draws four rows each with a leading rule and
   nothing after the last, so the section's own border-top is the next line the
   eye meets. Closing the list as well put two hairlines within 110px of each
   other, separated by nothing but the section's bottom padding.

   The :last-child and --last selectors are kept, empty of the border, because
   a caller may still want to target the final row. */

/* Hover and keyboard focus share the nudge, so they are declared once. The
   focus ring itself is not restated: the global :focus-visible in
   parent-overrides.css draws it on the anchor, and nothing here removes it or
   paints over it.

   The design also fills the row with --lift on hover. Removed at Adrian's
   request on review: the nudge and the cue at the end of the row carry the
   state on their own, and the fill made a quiet editorial list feel like a
   menu. A deliberate departure, not an oversight. */
.expertise-card:hover,
.expertise-card:has(.expertise-card__link:focus-visible) {
	transform: translateX(var(--gap-md));
}

/* Head: the numeral and the practice-area name -------------------------- */
.expertise-card__head {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	/* min-width: 0 so the head contributes its shrunk width to the track rather
	   than its min-content width, as faq-accordion.css:80 and its siblings do. */
	min-width: 0;
	column-gap: var(--gap-2xl);
	/* Derived: the design never reaches the wrapped state, so it declares no
	   value for the space between a wrapped numeral and its title. */
	row-gap: var(--gap-sm);
}

/* .serif in base.css carries the family and the regular weight. Tabular
   numerals give every row the same title start, which is what the design's
   fixed numeral column was buying. */
.expertise-card__index {
	flex: 0 0 auto;
	min-width: var(--expertise-card-numeral-col);
	font-size: var(--expertise-card-numeral);
	/* The design's own value. The smallest token, --lh-display at 1.08, drops
	   the numeral roughly 4px off the title's cap-height. */
	line-height: 0.9;
	color: var(--kicker);
	font-variant-numeric: tabular-nums;
}

.expertise-card__title {
	flex: 1 1 var(--expertise-card-title-min);
	min-width: 0;
	font-size: var(--expertise-card-title);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-heading);
	color: var(--fg);
	/* A compound practice-area name has to break rather than push the row wider
	   than the viewport: SC 1.4.10 at 320px, and this site is German as well as
	   English. */
	overflow-wrap: break-word;
	hyphens: auto;
}

/* The row is the click target, the title is the link, so the accessible name is
   the practice-area name and nothing else. The state list covers
   parent-overrides.css:29, which would otherwise recolour the title on hover,
   focus and active. */
.expertise-card__link,
.expertise-card__link:visited,
.expertise-card__link:hover,
.expertise-card__link:focus,
.expertise-card__link:active {
	color: inherit;
	text-decoration: none;
}

/* The stretched hit area, so the whole row is clickable while the link's
   accessible name stays the practice-area name. */
.expertise-card__link::after {
	content: "";
	position: absolute;
	inset: 0;
}

/* --lift composites to about a 1.12:1 change against the page ground, which is
   not a dependable state cue on its own, so the title takes a non-colour one as
   well. Additive: it never touches the focus ring. */
/* The row's hover signal is the cue at the end of it, not a browser-style
   underline dragged across a 36px serif heading. See the .cue-label pattern in
   base.css. */
.expertise-card:hover .expertise-card__link,
.expertise-card:has(.expertise-card__link:focus-visible) .expertise-card__link {
	text-decoration: none;
}

/* Body: description and affordance -------------------------------------- */
.expertise-card__body {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--gap-lg);
	padding-top: var(--gap-xs);
	min-width: 0;
}

.expertise-card__text {
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
	color: var(--fg-2);
	margin: 0;
}

/* wp_kses_post permits <a> in the description, and the stretched ::after above
   would otherwise cover it: keyboard reachable but pointer dead, with the click
   going to the row's own URL instead. Lifting a nested link above the overlay
   also restores text selection inside the paragraph. */
.expertise-card__text a {
	position: relative;
	z-index: 1;
}

/* The cue is decoration, not a second control: the row already has one link,
   the title, and its stretched ::after covers the whole card. But .cue-label
   in base.css is position:relative for its own underline, and both it and the
   overlay sit at z-index auto, so the later element in the DOM wins and the
   cue painted on top of the hit area. Clicking the word Explore did nothing
   while the rest of the row worked. Letting pointer events fall through hands
   the click back to the overlay underneath and keeps a single link per row,
   which is what the accessible name depends on. Found on review 14/09/2026. */
.expertise-card__cta {
	pointer-events: none;
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	line-height: var(--lh-ui);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	color: var(--link);
}

/* Sized in em off the label rather than the design's 18px, so no literal
   remains and the arrow tracks the label if the type scale moves. */
.expertise-card__icon {
	flex: 0 0 auto;
	width: 1.4em;
	height: 1.4em;
}

/* ======================= facts-bar.css ======================= */
/*
 * Facts bar. Design section 39.
 *
 * A shallow strip of label and value pairs under a case study hero. It borrows
 * everything it can from the layers below it: .section for the hairline top
 * rule, .section--panel for the navy ground and the panel foreground,
 * .container for the measure and gutter (parent-overrides.css), .cards-4up for
 * the fluid four-across grid, .kicker for the taupe label, and, from
 * parent-overrides.css, the global :focus-visible ring and the link colours.
 * None of those are restated here. What is left is the strip's tighter rhythm,
 * the pair's leading rule, and the label leading that .kicker does not set.
 *
 * No media queries: .cards-4up carries a real minmax() floor, so the row wraps
 * from four up to two up on its own. It stays two up at 390px, where the 350px
 * content box still holds two 150px tracks plus the 24px gap, so one up is
 * never reached. Long unbroken labels and values are caught by overflow-wrap,
 * not by the type ramp.
 *
 * Single-class selectors throughout. The component layer loads after base.css
 * and parent-overrides.css through $deps in functions.php, so one class already
 * beats .section and .kicker on source order and no compound selector is needed.
 */

/* The strip is shallower than a full section, so .section's 110px rhythm is
   replaced rather than inherited. 56px is exactly --gap-2xl. */
.facts-bar {
	padding-block: var(--gap-2xl);
}

/* The design shows no heading here. This only bites when a caller passes one
   with show_heading true; otherwise .screen-reader-text hides it. */
.facts-bar__heading {
	margin: 0 0 var(--gap-lg);
}

.facts-bar__list {
	margin: 0;
	padding: 0;
	list-style: none;
}

.facts-bar__item {
	min-width: 0;
	padding-inline-start: var(--gap-md);
	border-inline-start: 1px solid var(--panel-line-strong);
}

/* .kicker supplies the colour, size, weight, tracking and case. The strip
   overrides four things, not two. The leading is the important one: .kicker
   declares no line-height, so the label would otherwise inherit --lh-body
   (1.72) from the body rule and sit an 11.5px label in a 19.8px line box
   against the design's ~14.4px, adding about 3px of dead space above the
   value. --lh-tight gives a 14.03px box. The measure cap is dropped because
   20ch wraps these labels far earlier than the design does, and overflow-wrap
   is what keeps a single long token inside its track: the item sets
   min-width: 0, so the grid column cannot grow to rescue it. */
.facts-bar__label {
	line-height: var(--lh-tight);
	margin: 0 0 var(--gap-sm);
	max-width: none;
	overflow-wrap: break-word;
	hyphens: auto;
}

.facts-bar__value {
	display: block;
	color: var(--panel-fg);
	font-size: var(--fs-lg);
	font-weight: var(--fw-regular);
	line-height: var(--lh-tight);
	overflow-wrap: break-word;
}

/* Values are usually plain text, but a caller may pass a link, for example a
   docket reference.
   The strip inverts the theme's global link pair deliberately. The slice puts
   the value at full panel foreground, so a --link rest state would break the
   design, and parent-overrides' a:hover to --fg would then be a no-op. The
   rest state is therefore the inherited white and the hover moves to --link,
   which is also the direction the design page's own a:hover takes. --link on
   --panel measures 5.13:1, so AA holds at this size.
   :hover and :focus-visible are kept apart so the hover can never paint out
   the focus indicator, and the ring itself is left to the global
   :focus-visible rule in parent-overrides.css. Reduced motion is handled
   globally in base.css. */
.facts-bar__value a {
	color: inherit;
	text-decoration: underline;
	transition: color var(--dur) var(--ease);
}

.facts-bar__value a:hover {
	color: var(--link);
}

/* ======================= faq-accordion.css ======================= */
/*
 * Component: faq-accordion.
 *
 * Design source: section 14. Two columns of native <details> rows on a shared
 * hairline grid, one open at a time.
 *
 * Everything comes from tokens.css. The only literals are the 260px minmax
 * floor on the row grid and the 1px hairlines, both noted at their rules: the
 * floor is the design's own and no token expresses a bare track floor, and
 * tokens.css carries border colours but no border width.
 *
 * Layout is fluid, no media queries. The grid uses auto-fit with a real floor,
 * and the header row wraps, so the component works from 320px up.
 *
 * No ground is painted here. The page ground already comes from
 * parent-overrides.css body, so repainting it would only outrank the
 * .section--lift and .section--panel modifiers the class arg advertises.
 *
 * No focus rule either: parent-overrides.css :focus-visible already carries the
 * 2px accent outline and the token ring for every focusable element in the
 * theme, and the design specifies no focus geometry of its own.
 */

.faq-accordion {
	/* Room kept outside the panel clip so a focus ring on a link at the
	   answer's leading edge is never trimmed. Same convention, and the same
	   value, as --testimonial-block-clip-room. */
	--faq-accordion-clip-room: var(--gap-xs);
}

/* Header --------------------------------------------------------------- */

/* space-between with wrap: the title sits left, the optional lede right, and
   they stack rather than squeeze once the lede cannot hold its measure. */
.faq-accordion__head {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: var(--gap-xl);
	padding-bottom: calc(var(--gap-xl) + var(--gap-2xs)); /* design 44px */
}

.faq-accordion__headings {
	flex: 1 1 auto;
	min-width: 0;
}

.faq-accordion__intro {
	flex: 0 1 var(--measure-intro);
	min-width: 0;
	margin: 0;
}

/* Base .kicker is 11.5px; section 14 sets its eyebrow one step up. */
.faq-accordion__kicker {
	font-size: var(--fs-xs);
}

/* Styled by class, not by element, so heading_level cannot change the look.
   Only the declarations that vary by heading level are restated: margin and
   colour already come from the base heading rule at every level. The serif
   family comes from the base .serif utility on the element, matching the design
   page's h1 to h4 rule. */
.faq-accordion__title {
	font-size: var(--fs-h2);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-heading);
}

/* Rows ----------------------------------------------------------------- */

/* Two columns at width, one at phone size. The floor is computed from the
   column gap so the pair actually fits: half the track width, less half the
   72px gap, less a 4px allowance for sub-pixel rounding. The --cards-2up
   token is tuned for a 24px gap and would collapse this grid to a single
   column, so the pattern is rebuilt here from the gap token instead.
   260px: the design's own mobile floor, no token covers it.

   A repeated set of rows is a list, so the grid sits on a <ul>. The marker is
   removed for the design, which is why the markup restates role="list". */
.faq-accordion__list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(clamp(260px, calc(50% - var(--gap-3xl) / 2 - var(--gap-2xs)), 100%), 1fr));
	column-gap: var(--gap-3xl);
	row-gap: 0;
	align-items: start;
	margin: 0;
	padding: 0;
	list-style: none;
	border-top: 1px solid var(--line); /* 1px: tokens.css has colours, no width */
}

.faq-accordion__item {
	min-width: 0;
	border-bottom: 1px solid var(--line);
}

/* Question row --------------------------------------------------------- */

/* Layout only. The type sits on the text child, so it holds whether that child
   is a span or the optional heading. */
.faq-accordion__question {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--gap-lg);
	padding-block: calc(var(--gap-lg) + var(--gap-2xs) / 2); /* design 26px */
	cursor: pointer;
	list-style: none;
}

/* Kill the default disclosure triangle in every engine: the design uses its
   own arrow, and Safari needs the -webkit- pseudo-element. */
.faq-accordion__question::-webkit-details-marker {
	display: none;
}

.faq-accordion__question::marker {
	content: "";
}

/* Held flat across span and h1 to h6, so item_heading_level changes the outline
   and not the type. --fs-lg is 20px against the design's 19px: --fs-h5 starts
   at exactly 19px but scales to 23px, which would grow the row at width. */
.faq-accordion__question-text {
	min-width: 0;
	font-size: var(--fs-lg);
	font-weight: var(--fw-medium);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-body);
	color: var(--fg);
}

/* Icon ----------------------------------------------------------------- */

/* 16px comes from the SVG's own width and height attributes, which base.css
   leaves intact via svg { height: auto }, so no size literal is needed here.
   base.css also makes the svg display: block where the slice had it inline, so
   the arrow sits 2 to 3px higher against the first line than in the design.
   Accepted: it buys a deterministic 16px box and an exact 4px offset. */
.faq-accordion__icon {
	flex: none;
	margin-top: var(--gap-2xs);
	color: var(--link);
	transition: transform var(--dur) var(--ease);
}

/* The design ships the same arrow open and closed. The quarter turn is added
   as a non-textual state cue to sit alongside the native expanded state.
   base.css zeroes transition-duration under prefers-reduced-motion with
   !important, so the turn becomes instant there. The row state lives on the
   ancestor, so this is the shortest selector that can reach the icon. */
.faq-accordion__row[open] .faq-accordion__icon {
	transform: rotate(90deg);
}

/* Answer --------------------------------------------------------------- */

/* overflow: hidden is what lets the JS animate the panel height. With
   JavaScript off it does nothing visible.
 *
 * The clip room is padding pulled straight back by an equal negative margin, so
 * the design geometry is unchanged while a focus ring on a link at the top or
 * either edge of the answer still has somewhere to paint. border-box keeps the
 * scripted 0 to scrollHeight height animation exact with that padding in place,
 * and the parent theme's box-sizing is not ours to assume. */
.faq-accordion__panel {
	box-sizing: border-box;
	overflow: hidden;
	padding-block-start: var(--faq-accordion-clip-room);
	padding-inline: var(--faq-accordion-clip-room);
	margin-block-start: calc(-1 * var(--faq-accordion-clip-room));
	margin-inline: calc(-1 * var(--faq-accordion-clip-room));
}

.faq-accordion__answer {
	max-width: var(--measure);
	padding-bottom: calc(var(--gap-lg) + var(--gap-2xs)); /* design 28px */
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
	color: var(--fg-2);
}

.faq-accordion__answer > :last-child {
	margin-bottom: 0;
}

/* Topic filter, design section 63 ---------------------------------------
 *
 * Single choice, so the group is an ARIA radiogroup and the pressed state
 * rides on aria-checked rather than a class. That keeps one source of truth:
 * the attribute a screen reader reads is the same one the style selects on,
 * so the two cannot drift apart.
 *
 * Only the FAQ hub passes filters, so nothing here fires on the accordions at
 * the foot of the service, case study and news pages.
 */
.faq-accordion__filters {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--gap-xs) - var(--gap-2xs) / 2); /* design 8px */
	padding-bottom: var(--gap-xl);
	margin-bottom: calc(var(--gap-2xl) - var(--gap-xs) + var(--gap-2xs) / 2); /* design 48px */
	border-bottom: 1px solid var(--line);
}

.faq-accordion__pill {
	padding: 0.76em 1.24em; /* design 11px 18px at --fs-sm */
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--fg-2);
	font-family: var(--font-sans);
	font-size: var(--fs-sm);
	font-weight: var(--fw-light);
	cursor: pointer;
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.faq-accordion__pill:hover {
	border-color: var(--line-strong);
	color: var(--fg);
}

.faq-accordion__pill[aria-checked="true"] {
	background: var(--fg);
	border-color: var(--fg);
	color: var(--bg);
	font-weight: var(--fw-medium);
}

/* ======================= home-hero.css ======================= */
/*
 * home-hero
 *
 * Design section 0. The home page opener: a filtered photograph, a navy tint
 * in colour blend mode, two gradient scrims and a left hand content column of
 * kicker, display heading, intro and pill actions.
 *
 * functions.php globs css/components/*.css and enqueues each sheet in its own
 * right, chained through $deps after base.css and before custom.css. There is
 * no css/components.css bundle: the earlier header comment here claimed one,
 * and because none existed on disk the enqueue was silently skipped and this
 * whole file was dead. So .section, .section--hero, .kicker and .lede from
 * base.css and .container from parent-overrides.css are inherited rather than
 * restated, and a single class here already outranks both.
 *
 * The four layer hero stack from CONVENTIONS section 7, bottom to top:
 *   .home-hero__image  z 0  the photograph under --hero-filter-home
 *   ::before           z 1  --hero-tint at mix-blend-mode: color
 *   ::after            z 2  the left to right wash, then the vignette
 *   .home-hero__inner  z 3  the content
 * Explicit z-index is needed because a ::before is first in tree order and
 * would otherwise paint under the image. isolation: isolate keeps the colour
 * blend inside the hero instead of reaching the page behind it.
 *
 * No color-mix() anywhere. The wash and the vignette are the plain rgba()
 * tokens, so there is one declaration per layer, no fallback to keep in sync,
 * and no browser on which the scrims can drop out and leave white type on an
 * unmodified photograph.
 */

.home-hero {
	/* Stated rather than inherited: --hero-min-height sits on the same box as
	   the vertical padding, so the parent theme's box-sizing is not ours to
	   assume. Under content-box the stage floor would be 860 + 216 = 1076px. */
	box-sizing: border-box;
	display: flex;
	align-items: center;
	min-height: var(--hero-min-height);
	/* The design's home stage declares no padding at all: it is a flex box with
	   align-items: center inside min(84vh, 860px), so the centring does the
	   work. The earlier version referenced --section-y-home-hero and
	   --section-y-home-hero-end for a "120/96" pair that is not in the slice
	   and was never declared, so both resolved to nothing. --gap-3xl is kept as
	   a floor so a very short viewport pads rather than clipping. */
	padding-block: var(--gap-3xl);
	/* Not a restatement of the page ground. isolation: isolate makes this
	   element's own painting the backdrop for the blended tint layer, so with
	   no image supplied the tint would otherwise blend against transparency
	   and the band would render as the tint rather than --bg. */
	background: var(--bg);
	isolation: isolate;
}

.home-hero__image {
	position: absolute;
	inset: 0;
	z-index: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: var(--home-hero-focus, 50% 50%);
	filter: var(--home-hero-filter);
}

/* Layer 2, the tint: --home-hero-tint pushed through the photograph's hue and
   saturation channels while the image keeps its own luminosity. The home hero
   blends towards --bg, not towards the page hero's #12395E. */
.home-hero::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background: var(--home-hero-tint);
	mix-blend-mode: color;
	pointer-events: none;
}

/*
 * Layers 3 and 4, the scrims. These are what make white type legible over a
 * photograph, so they are plain rgba() gradients and can never be dropped by
 * an unsupported colour function.
 *
 * Both are now the design's exact ramps, held whole in tokens.css. The earlier
 * version composited two passes of a page-hero stop to approximate this,
 * because the token layer exposed the page hero's stops as if they were generic
 * and the home hero's own ramp had no token. That workaround is gone: the wash
 * runs 0.93 at the left edge to 0.06 at 92 per cent exactly as drawn, all on
 * --bg rather than on the page hero's blues.
 *
 * The first background layer listed paints on top, so the vignette leads and
 * the wash follows, matching the slice's DOM order.
 */
.home-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
	background-image:
		var(--home-hero-vignette),
		var(--home-hero-wash);
}

/* .container, in parent-overrides.css, supplies the max-width, the gutters and
   the centring. width and min-width are stated here rather than left to that
   file: as a flex item this box would otherwise size to max-content, and
   .section--hero clips its overflow, so a flex item left at the default
   min-width: auto could push a long word out of the stage unreachably. */
.home-hero__inner {
	position: relative;
	z-index: 3;
	width: 100%;
	min-width: 0;
}

/* The design caps the column at min(52ch, 640px). At the inherited body size
   52ch resolves below 640px, so the second arm never wins and the token alone
   reproduces it. */
.home-hero__content {
	max-width: var(--measure-intro);
}

/* .kicker gives the taupe, the tracking and the uppercase. The hero runs it
   one step up the scale and lets it use the full column. */
.home-hero__kicker {
	max-width: none;
	margin-bottom: var(--gap-lg);
	font-size: var(--fs-xs);
	line-height: var(--lh-ui);
	overflow-wrap: break-word;
}

/* Set in full rather than leaning on the h1 baseline, because the heading
   level is a caller decision and the display treatment is not. The colour is
   not restated: base.css already sets --fg on h1 to h6 and the PHP allows no
   other tag. overflow-wrap is load bearing, not tidiness: at the 320px reflow
   width the column is 280px and --fs-display clamps to 36px, so a German
   compound longer than about fifteen characters would otherwise be clipped by
   .section--hero's overflow: hidden with no way to scroll to it. */
.home-hero__title {
	margin: 0 0 var(--gap-lg);
	max-width: none;
	font-size: var(--fs-display);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-display);
	overflow-wrap: break-word;
}

/* .lede gives the size, the colour and the measure. The hero tightens the
   leading to the UI value the slice uses. */
.home-hero__intro {
	margin: 0 0 var(--gap-xl);
	line-height: var(--lh-ui);
	overflow-wrap: break-word;
}

/* A repeated set of controls, so a list. The resets are for the parent theme's
   ul, which carries a margin and a marker. */
.home-hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--gap-sm);
	margin: 0;
	padding: 0;
	list-style: none;
}

.home-hero__action-item {
	display: flex;
	min-width: 0;
}

/* :visited is matched explicitly: parent-overrides.css sets a:visited, which
   would otherwise outrank the single class here and repaint a visited primary
   button's label in the link blue on a blue ground. */
.home-hero__action,
.home-hero__action:visited {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	/* The slice's 16px/28px expressed against --fs-xs, so the pill keeps its
	   proportions as the type token moves. There is no control padding token
	   and the nearest --gap values would reshape it. */
	padding: 1.23em 2.15em;
	border: 1px solid transparent;
	border-radius: var(--radius-pill);
	color: var(--fg);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	/* --lh-tight, not --lh-ui. The slice sets no line-height on the anchor, so
	   the pill would inherit --lh-body (1.72) from parent-overrides.css and
	   stand 56.4px tall against the design's roughly 50px. 13 x 1.22 + 32 + 2
	   gives 49.9px, which is the slice's pill to within a pixel. */
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	text-decoration: none;
	transition:
		background-color var(--dur) var(--ease),
		border-color var(--dur) var(--ease),
		color var(--dur) var(--ease);
}

.home-hero__action--primary,
.home-hero__action--primary:visited {
	background-color: var(--accent);
	border-color: var(--accent);
	color: var(--accent-fg);
}

/*
 * The slice borders the secondary pill in rgba(210, 220, 230, 0.42), which the
 * design's line tokens do not hold. --control-line carries the design's own
 * control alpha, 0.45. --line-strong at 0.32 was rejected on measured
 * contrast, not on taste. Against --bg:
 *   0.32 -> 2.42:1, fails WCAG 1.4.11
 *   0.42 -> 3.22:1
 *   0.45 -> 3.51:1
 * Over the real composited ground at the button row, which the doubled wash
 * holds at about 0.91 alpha, it measures 3.37:1 over a dark image region and
 * 3.08:1 over a worst case bright one. The border is this control's only
 * boundary, so it has to clear 3:1 on both.
 */
.home-hero__action--ghost,
.home-hero__action--ghost:visited {
	background-color: transparent;
	border-color: var(--control-line);
	color: var(--fg);
}

/*
 * :focus and :active are matched, not just :hover. parent-overrides.css sets
 * a:hover, a:focus, a:active { color: var(--fg) } at (0,1,1), which outranks
 * .home-hero__action--primary at (0,1,0): a click or a tap leaves :focus set
 * without :focus-visible, and the primary label would repaint white on
 * --accent at 3.31:1, failing 1.4.3 for 13px text. text-decoration is restated
 * for the same reason.
 *
 * :focus-visible is deliberately not listed: it is a subset of :focus, so it
 * is already covered, and the focus indicator itself is the global
 * :focus-visible outline and box-shadow in parent-overrides.css. Nothing in
 * this block touches outline or box-shadow, so no state here can paint the
 * ring out. Measured on the resulting white pill: ring 3.31:1 against the
 * pill, 5.13:1 against the ground.
 */
.home-hero__action:hover,
.home-hero__action:focus,
.home-hero__action:active {
	background-color: var(--fg);
	border-color: var(--fg);
	color: var(--accent-fg);
	text-decoration: none;
}

/* The 15px arrow from the slice, tied to the label's own size. Overrides the
   base svg { height: auto } by class. */
.home-hero__icon {
	flex: 0 0 auto;
	width: 1.15em;
	height: 1.15em;
}

/* No local prefers-reduced-motion block: base.css carries the global one and
   CONVENTIONS section 2 forbids restating a global. */

/* ======================= newsletter-embed.css ======================= */
/*
 * Component: newsletter-embed
 *
 * MailerLite's embedded form, restyled to the newsletter band it sits in.
 *
 * The markup is vendor output and stays byte-identical, because their
 * webforms.min.js binds to those class names and swaps .row-form for
 * .row-success by container id. So every difference between what MailerLite
 * ships and what the design draws is made here.
 *
 * Their stylesheet is inline in the embed, which means it loads AFTER this file
 * and wins any tie. Two things follow. Specificity is raised by scoping through
 * .newsletter-embed, which their selectors cannot match, and the handful of
 * declarations they mark !important are answered in kind rather than fought
 * with longer selectors. Each of those is noted where it appears; none is here
 * out of habit.
 *
 * What is deliberately NOT restyled: the loading spinner and the success body
 * keep their own behaviour, only their colours are brought into the palette.
 * Reimplementing either would mean reimplementing their script.
 */

/* Their wrapper paints a white card with its own padding and a max-width. The
   band already provides the ground and the measure, so the card is removed
   rather than recoloured: a second surface inside the band reads as a panel
   that has nothing to say. -------------------------------------------------- */
.newsletter-embed .ml-form-embedContainer,
.newsletter-embed .ml-form-embedWrapper,
.newsletter-embed .ml-form-embedBody,
.newsletter-embed .ml-form-embedContent,
.newsletter-embed .ml-form-formContent {
	background: transparent !important; /* theirs is #f6f6f6 !important */
	border: 0 !important;
	border-radius: 0 !important;
	box-shadow: none !important;
	padding: 0 !important;
	margin: 0 !important;
	max-width: none !important;
	width: 100% !important;
	text-align: start !important;
}

.newsletter-embed .ml-form-align-center {
	text-align: start;
}

/* The field rows sit in a column with the band's own rhythm. Their default is a
   fixed margin per row, which does not match the gap scale used either side. */
.newsletter-embed .ml-form-fieldRow {
	margin: 0 0 var(--gap-sm) !important;
	padding: 0 !important;
	max-width: none !important;
}

.newsletter-embed .ml-form-fieldRow.ml-last-item {
	margin-bottom: var(--gap-md) !important;
}

/* The inputs, matched to .newsletter-signup__input rather than approximated:
   same border token, same radius, same padding, same transitions, so the two
   forms are indistinguishable where they appear on the same site. Their rule
   sets a white ground, a grey border and #333 text, all !important. ---------- */
.newsletter-embed .ml-form-embedBody input[type="email"],
.newsletter-embed .ml-form-embedBody input[type="text"],
.newsletter-embed .ml-form-embedBody .form-control {
	box-sizing: border-box !important;
	width: 100% !important;
	height: auto !important;
	padding: var(--gap-sm) var(--gap-md) !important;
	border: 1px solid var(--control-line) !important;
	border-radius: var(--radius-md) !important;
	background: transparent !important;
	color: var(--fg) !important;
	font-family: var(--font-sans) !important;
	font-size: var(--fs-sm) !important;
	line-height: var(--lh-ui) !important;
	box-shadow: none !important;
	transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.newsletter-embed .ml-form-embedBody input::placeholder {
	color: var(--ph) !important;
	opacity: 1 !important;
}

.newsletter-embed .ml-form-embedBody input:hover {
	border-color: var(--ph) !important;
}

.newsletter-embed .ml-form-embedBody input:focus {
	border-color: var(--accent) !important;
	background: var(--well) !important;
	color: var(--fg) !important;
}

/* The focus ring is the global one from parent-overrides.css. Their reset
   removes outlines on focus, so it is restored here rather than left off. */
.newsletter-embed .ml-form-embedBody input:focus-visible {
	outline: 2px solid transparent;
	box-shadow: var(--focus-ring) !important;
}

/* The submit, matched to the site's pill: accent ground, uppercase at the
   kicker tracking, full width on its own line the way the band's own button
   sits at narrow widths. Theirs is a square blue block in their brand. ------- */
.newsletter-embed .ml-form-embedSubmit {
	margin: 0 !important;
	padding: 0 !important;
}

.newsletter-embed .ml-form-embedSubmit button[type="submit"],
.newsletter-embed .ml-form-embedSubmit button.primary {
	display: inline-flex !important;
	align-items: center;
	justify-content: center;
	width: 100% !important;
	height: auto !important;
	margin: 0 !important;
	padding: var(--gap-sm) var(--gap-lg) !important;
	border: 1px solid var(--accent) !important;
	border-radius: var(--radius-pill) !important;
	background: var(--accent) !important;
	color: var(--accent-fg) !important;
	font-family: var(--font-sans) !important;
	font-size: var(--fs-xs) !important;
	font-weight: var(--fw-medium) !important;
	letter-spacing: var(--ls-kicker) !important;
	text-transform: uppercase !important;
	line-height: var(--lh-ui) !important;
	box-shadow: none !important;
	cursor: pointer;
	transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* The same inversion the rest of the site's pills use on hover. */
.newsletter-embed .ml-form-embedSubmit button[type="submit"]:hover,
.newsletter-embed .ml-form-embedSubmit button.primary:hover {
	background: var(--fg) !important;
	border-color: var(--fg) !important;
	color: var(--bg) !important;
}

/* Their loading button is shown by their script in place of the submit, so it
   has to match the pill it replaces or the row jumps mid-submission. */
.newsletter-embed .ml-form-embedSubmit button.loading {
	width: 100% !important;
	padding: var(--gap-sm) var(--gap-lg) !important;
	border: 1px solid var(--accent) !important;
	border-radius: var(--radius-pill) !important;
	background: var(--accent) !important;
}

/* The confirmation. Their script reveals this; only the palette is ours. */
.newsletter-embed .ml-form-successBody {
	padding: 0 !important;
	background: transparent !important;
}

.newsletter-embed .ml-form-successContent h4,
.newsletter-embed .ml-form-successContent p,
.newsletter-embed .ml-form-successContent span {
	color: var(--fg) !important;
	font-family: var(--font-sans) !important;
	text-align: start !important;
}

.newsletter-embed .ml-form-successContent h4 {
	margin: 0 0 var(--gap-xs) !important;
	font-family: var(--font-serif) !important;
	font-size: var(--fs-h5) !important;
	font-weight: var(--fw-regular) !important;
}

/* Their embed imports a font sheet from assets.mlcdn.com and applies it here.
   The band is set in the site's own faces, so the import is left to load and
   simply not used; removing it would mean editing vendor output. */
.newsletter-embed .ml-form-embedContainer,
.newsletter-embed .ml-form-embedContainer * {
	font-family: var(--font-sans);
}

/* ======================= newsletter-signup.css ======================= */
/*
 * Component: newsletter-signup
 *
 * Newsletter sign-up block from design section 53: kicker, heading and note on
 * the left, field and submit pill on the right, with a hairline rule closing
 * the block off underneath.
 *
 * Built on .section, .container, .grid-even, .kicker and .small, so only the
 * declarations that differ from those primitives live here. Declarations that
 * base.css already applies to h1..h6, and that parent-overrides.css already
 * applies to input and button, are deliberately not restated.
 *
 * No layout breakpoint of its own. The even split is the slice's own ratio
 * (minmax(0,1fr) minmax(0,1fr) at 72px, which is --split-even) and it stacks
 * through .grid-even in base.css. The field row wraps on flex with a real
 * min(220px, 100%) floor, so the pill drops to its own line rather than the
 * field squeezing at 390px.
 *
 * The block carries no card: the slice gives it a bottom hairline on the page
 * ground and no surface, so --shadow-card and the hover lift do not apply.
 */

/* Design: the section runs a shorter top pad than the standard .section
   rhythm, so the block reads as a coda to the band above rather than a new
   one. The hairline above and the bottom pad come from .section. */
.newsletter-signup {
	padding-block: var(--gap-3xl) var(--section-y);
}

/* Design: the closing rule sits under the two columns, not under the section,
   so it stops at the container gutter. align-items is set because .grid-even
   leaves alignment at the grid default, which would stretch both columns.
   The slice sets align-items:center on this element, and only on this one. */
.newsletter-signup__inner {
	align-items: center;
	padding-bottom: var(--gap-2xl);
	border-bottom: 1px solid var(--line);
}

/* min-width:0 so a long unbroken word in the note cannot widen the column
   past its grid track. */
.newsletter-signup__intro {
	min-width: 0;
}

/* The slice sets this eyebrow at 13px, which is --fs-xs exactly, where the
   base .kicker normalises to --fs-3xs (11.5px). Restored to the slice value,
   which also keeps the kicker and the pill label at one size, as the design
   pairs them. Everything else comes from .kicker. */
.newsletter-signup__kicker {
	font-size: var(--fs-xs);
}

/* Heading scale by class, not by element: the block is intended to sit one
   visual rank below its semantic rank, so a caller can pass the level the
   document outline needs without the type size moving. Colour and family are
   not restated: base.css already sets both on every h1 to h6, and the PHP
   whitelists the level to exactly that set. */
.newsletter-signup__title {
	margin: 0 0 var(--gap-sm);
	font-size: var(--fs-h3);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-heading);
}

/* Design: body copy at the small body size on the 52ch intro measure, not the
   full 72ch measure base.css puts on every p. */
.newsletter-signup__note {
	margin: 0;
	max-width: var(--measure-intro);
	color: var(--fg-2);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
}

/* Field row ---------------------------------------------------------------- */
.newsletter-signup__form {
	min-width: 0;
}

/* Alignment is left at stretch, as the slice leaves it, so the pill and the
   field render flush in height instead of the pill sitting inset against the
   field hairline. */
.newsletter-signup__row {
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	gap: var(--gap-sm);
}

/* The 220px basis is the design's own wrap floor: below it the pill drops to
   its own line rather than the field squeezing to nothing. min() keeps that
   floor from ever exceeding the column, so at 320px the field shrinks
   instead of pushing the row wider than the viewport.

   The resting border is --control-line, not --line. --line is 1.59:1 against
   the page ground, so on a transparent field the only boundary identifying the
   control would fail WCAG 1.4.11, which asks 3:1 for the visual boundary of a
   user-interface component. --control-line is the Stage 5 token minted for
   exactly this, at 3.51:1.

   Family, weight and tracking are not restated: parent-overrides.css already
   sets them on input with the same tokens. Only size and leading differ. */
.newsletter-signup__input {
	flex: 1 1 220px;
	min-width: min(220px, 100%);
	padding: var(--gap-sm) var(--gap-md);
	border: 1px solid var(--control-line);
	border-radius: var(--radius-md);
	background: transparent;
	color: var(--fg);
	font-size: var(--fs-sm);
	line-height: var(--lh-ui);
	transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* opacity:1 because Firefox dims placeholders by default, which would take
   the token below its intended contrast. */
.newsletter-signup__input::placeholder {
	color: var(--ph);
	opacity: 1;
}

/* Hover steps the boundary up to --ph (5.46:1). --line-strong, which the slice
   language would suggest, is 2.42:1: it would both fail 1.4.11 and read as a
   hover that loses contrast against the resting state. */
.newsletter-signup__input:hover {
	border-color: var(--ph);
}

/* The focus fill is declared here from page-ground tokens rather than
   inherited from the panel-scoped pair in parent-overrides.css: this block
   renders on the page ground and has no panel surface, so it must not follow
   --panel if that ever diverges from --bg. The attribute qualifier is needed
   to outrank input[type="email"]:focus in parent-overrides.css, which is
   (0,2,1) against this rule at (0,3,0). The global :focus-visible ring is
   untouched. */
.newsletter-signup__input[type="email"]:focus {
	border-color: var(--accent);
	background: var(--well);
	color: var(--fg);
}

/* Submit ------------------------------------------------------------------- */

/* The accent pill, inverting to white. Its own class rather than a modifier of
   .button-primary because it is a submit control inside a flex row: it needs
   the flex behaviour and the uppercase micro-label the parent button does not
   carry. The border is a transparent size holder, following the cta-band
   convention, so the pill never resizes between states. Buttons carry no
   shadow. Family is not restated: parent-overrides.css sets it on button. */
.newsletter-signup__submit {
	flex: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--gap-xs);
	padding: var(--gap-sm) var(--gap-lg);
	border: 1px solid transparent;
	border-radius: var(--radius-pill);
	background: var(--accent);
	color: var(--accent-fg);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	line-height: var(--lh-ui);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	cursor: pointer;
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* Hover and focus are separate rules, never one shared selector, so no state
   here can paint over the global focus indicator. The bare :focus branch is
   gone: it left the pill stuck in its inverted state after a mouse click. */
.newsletter-signup__submit:hover {
	background: var(--fg);
	color: var(--bg);
}

.newsletter-signup__submit:focus-visible {
	background: var(--fg);
	color: var(--bg);
}

/* The arrow is decorative and sized by its own width and height attributes.
   It must never be the thing that shrinks when the label is long. */
.newsletter-signup__icon {
	flex: 0 0 auto;
}

/* The field hint, which is the aria-describedby target, and the consent or
   privacy line beside it. Neither is in the slice; .small carries the size and
   leading. Both use --fg-2, the token every sibling component uses for
   secondary prose: --faint is 3.05:1 on the page ground, decoration only and
   never text, least of all the line that carries legal weight. */
.newsletter-signup__hint,
.newsletter-signup__consent {
	margin: var(--gap-sm) 0 0;
	max-width: var(--measure-intro);
	color: var(--fg-2);
}

/* base.css already neutralises transitions globally under reduced motion, so
   this block changes nothing on its own. It is kept as a deliberate
   convention, matching css/components/cta-band.css, which states the motion a
   component owns at component scope. It is not a fallback for a load order
   that cannot occur: functions.php chains this layer behind base.css. */
@media (prefers-reduced-motion: reduce) {
	.newsletter-signup__input,
	.newsletter-signup__submit {
		transition: none;
	}
}

/* ======================= page-hero.css ======================= */
/*
 * page-hero
 *
 * Design sections 10, 36 and 47. A full-bleed photograph, colour-blended to
 * navy and washed left to right so the type column stays legible, then the
 * breadcrumb trail, title and either an intro or a pull quote.
 *
 * Reuses .section, .section--hero, .container, .kicker, .lede and .serif.
 * The section rhythm, overflow and border reset all come from .section--hero,
 * so nothing here repeats them.
 *
 * Fluid throughout: no media query, no breakpoint. Reduced motion is handled
 * by the global block at the foot of base.css, which kills the crumb hover
 * transition, so it is not repeated here.
 */

.page-hero {
	/* --page-hero-tint, --page-hero-filter, --page-hero-wash and
	   --page-hero-vignette are global tokens now. They were literals here
	   because the token layer had no value for a photographic tint; it does. */

	/* Default focal point, matching design section 36. Overridden per
	   instance by the inline --page-hero-focus the template part sets. */
	--page-hero-focus: 62% 50%;

	background: var(--bg);
	color: var(--fg);
	/* Confines mix-blend-mode:color to the media stack, so the tint blends
	   with the photograph and never with whatever sits behind the section. */
	isolation: isolate;
}

/* Media stack ---------------------------------------------------------- */
.page-hero__media {
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
}

.page-hero__image,
.page-hero__layer {
	position: absolute;
	inset: 0;
	display: block;
}

.page-hero__image {
	width: 100%;
	/* Overrides the height:auto in the base.css media reset: the backdrop has
	   to fill the section, not follow its intrinsic ratio. */
	height: 100%;
	object-fit: cover;
	object-position: var(--page-hero-focus);
	filter: var(--page-hero-filter);
}

.page-hero__layer--tint {
	background: var(--page-hero-tint);
	mix-blend-mode: color;
}

/*
 * The left to right wash that holds the type column, and the top and bottom
 * vignette that settles the edges against the header and the next section.
 *
 * Both are the design's exact ramps, held whole in tokens.css. The earlier
 * version rebuilt them with color-mix() over --bg-deep, which was wrong twice:
 * the wash crosses three distinct blues rather than two, and the vignette is
 * built on rgba(7,16,28), darker than --bg-deep. It also had no fallback, so an
 * unsupported color-mix() dropped both declarations and left white type on an
 * unwashed photograph. Plain rgba() cannot fail that way.
 */
.page-hero__layer--wash {
	background: var(--page-hero-wash);
}

.page-hero__layer--vignette {
	background: var(--page-hero-vignette);
}

/* Content -------------------------------------------------------------- */
.page-hero__inner {
	position: relative;
	z-index: 1;
}

.page-hero__content {
	max-width: var(--measure);
	min-width: 0;
}

/* Breadcrumbs ---------------------------------------------------------- */
.page-hero__breadcrumbs {
	margin: 0 0 var(--gap-xl);
}

.page-hero__crumbs {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--gap-xs);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--fs-2xs);
	line-height: var(--lh-ui);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
}

.page-hero__crumb {
	display: flex;
	align-items: center;
	gap: var(--gap-xs);
}

.page-hero__crumb-link {
	color: var(--kicker);
	text-decoration: none;
	transition: color var(--dur) var(--ease);
}

.page-hero__crumb-link:hover,
.page-hero__crumb-link:focus-visible {
	color: var(--fg);
	text-decoration: underline;
}

.page-hero__crumb-current {
	color: var(--fg-2);
}

.page-hero__crumb-sep {
	color: var(--faint);
}

/* Title ---------------------------------------------------------------- */
.page-hero__title {
	/* Set here rather than leaning on the .hero h1 rule in base.css, because
	   the caller chooses the heading level: the display size belongs to the
	   hero, not to h1. */
	font-size: var(--fs-display);
	line-height: var(--lh-display);
	font-weight: var(--fw-regular);
	letter-spacing: var(--ls-tight);
	color: var(--fg);
	max-width: var(--measure-label);
	margin: 0 0 var(--gap-lg);
}

/* Intro ---------------------------------------------------------------- */
/* One class is enough. The enqueue order is not a guess: functions.php chains
   $deps so the component layer always loads after base.css, so a single class
   here already beats .lede. */
.page-hero__intro {
	/* .lede supplies the size, weight and colour. The hero runs its intro
	   wider and tighter than a standard lede. */
	max-width: var(--measure-tight);
	line-height: var(--lh-ui);
}

.page-hero__intro > p {
	max-width: 100%;
}

.page-hero__intro > :last-child {
	margin-bottom: 0;
}

/* Pull quote, design section 47 ---------------------------------------- */
.page-hero__quote {
	position: relative;
	max-width: var(--measure-tight);
	padding-left: var(--gap-xl);
	margin: 0;
}

.page-hero__quote-mark {
	position: absolute;
	left: 0;
	/* Optical nudge so the glyph aligns with the first line of the quote
	   rather than its box. Em-relative on purpose: it has to track the fluid
	   quote size, and no token covers an optical offset. */
	top: -0.16em;
	font-family: var(--font-serif);
	font-size: var(--fs-display);
	line-height: 1;
	color: var(--kicker);
	opacity: 0.65;
}

/* One class, same reason as the intro: the component layer loads after
   base.css, so this beats .serif's weight 400 while .serif still supplies the
   family. */
.page-hero__quote-text {
	/* Clears the border-left and padding the parent theme puts on every
	   blockquote: here the serif quote mark does that job. */
	border-left: 0;
	padding: 0;
	margin: 0 0 var(--gap-lg);
	font-size: var(--fs-h5);
	line-height: var(--lh-ui);
	font-weight: var(--fw-light);
	font-style: italic;
	letter-spacing: var(--ls-body);
	color: var(--fg);
}

.page-hero__quote-text p {
	max-width: 100%;
}

.page-hero__quote-text > :last-child {
	margin-bottom: 0;
}

.page-hero__quote-cite {
	display: block;
	font-size: var(--fs-sm);
	line-height: var(--lh-ui);
	font-weight: var(--fw-regular);
	font-style: normal;
	color: var(--kicker);
}

/* Meta row, design section 54 ---------------------------------------------
 *
 * The article hero carries a type pill and a byline under the standfirst. It
 * is optional on every other page that uses this hero, so nothing here fires
 * unless the caller passes 'pill' or 'meta'.
 *
 * The byline reuses --fg-2 rather than .meta's --ph: this sits on the hero's
 * darkened photograph, where the quieter colour loses the contrast it has on
 * the page ground.
 */
.page-hero__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--gap-sm);
	margin: calc(var(--gap-2xl) - var(--gap-lg)) 0 0; /* design 32px */
	max-width: 100%;
}

.page-hero__pill {
	display: inline-block;
	padding: 0.42em 1.08em; /* design 5px 13px at --fs-2xs */
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	color: var(--fg-2);
	font-size: var(--fs-2xs);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
}

.page-hero__byline {
	color: var(--fg-2);
	font-size: var(--fs-sm);
	font-weight: var(--fw-light);
}

/* ======================= process-steps.css ======================= */
/*
 * process-steps
 *
 * Numbered process steps (design section 41) and the "three ways of working"
 * editorial rows (design section 11). One head, two variants.
 *
 * Layout is fluid and owns no breakpoint. The steps ladder rebuilds the slice's
 * own two-up pattern rather than borrowing --cards-2up, whose 280px floor and
 * 24px-tuned gap would hold the pair in one column across a band of viewports
 * where the design shows two. The ways rows compose .grid-split, whose
 * --split-editorial ratio, 72px gap and align-items are the slice's values
 * exactly. Both grids declare a real minmax() floor, so a row wraps rather than
 * squeezes at 390px.
 *
 * The badge diameter, the step indent, the step rhythm, the step grid gap and
 * the ladder measure are the design's own values, held in five component-scoped
 * custom properties so the badge, the connector line, the indent and the cap can
 * never drift apart.
 *
 * prefers-reduced-motion and the :focus-visible ring are globals (base.css and
 * parent-overrides.css) and are deliberately not restated here, per conventions
 * 2 and 5. Nothing below sets outline or box-shadow, so the ring survives both
 * hover and focus.
 *
 * Three emitted classes carry no rule here and are reserved hooks, not
 * omissions: .process-steps and .process-steps--steps / --ways are variant
 * handles for a caller or for custom.css, and .process-steps__inner matches the
 * live convention in home-hero, newsletter-signup and page-hero. The section
 * ground is not painted, because body already carries background: var(--bg) and
 * convention 2 forbids restating it.
 *
 * Known cross-component duplication, out of this component's file scope: the
 * four-declaration .process-steps__heading block below is byte-identical to
 * .audience-grid__title, .expertise-card__title and .faq-accordion__title. It
 * wants promoting to a single .section-heading class in base.css, which is a
 * shared file this component does not own. The dead color: var(--fg) that three
 * of the four also carried has been dropped here: base.css already sets it on
 * every h1-h6, and this class is only ever emitted on one.
 */

/* Head ------------------------------------------------------------------- */
.process-steps__head {
	padding-bottom: var(--gap-2xl);
}

.process-steps__head--ways {
	padding-bottom: var(--gap-xl);
}

/* The design sets this eyebrow one step above the .kicker default, as
   testimonial-block and faq-accordion also do. Given its own class rather than
   a descendant selector, so the rule stays one class deep. */
.process-steps__kicker {
	font-size: var(--fs-xs);
}

/* Size is set here, not inherited from the element, so heading_level can change
   the outline without changing the design. ------------------------------- */
.process-steps__heading {
	font-size: var(--fs-h2);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-heading);
}

/* Steps variant ---------------------------------------------------------- */
.process-steps__list {
	--process-steps-badge: clamp(42px, 4vw, 52px);
	--process-steps-indent: clamp(38px, 4vw, 64px);
	--process-steps-rhythm: clamp(40px, 4vw, 60px);
	--process-steps-gap: clamp(20px, 2.5vw, 48px);
	--process-steps-measure: 1020px;

	list-style: none;
	/* Half the badge hangs outside the ladder, so the ladder starts half a
	   badge in and the badge still sits inside the container gutter. */
	margin: 0 0 0 calc(var(--process-steps-badge) / 2);
	padding: 0;
}

.process-steps__step {
	/* Stated rather than assumed, following case-card.css: the measure below is
	   a padding box that also carries the indent, and no global border-box
	   exists in this theme. The design page sets * { box-sizing: border-box },
	   so border-box is what makes 1020px the design's own ladder measure. */
	box-sizing: border-box;
	position: relative;
	display: grid;
	/* The slice's own floor. Rebuilt rather than borrowed, per the file header. */
	grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
	gap: var(--process-steps-gap);
	align-items: start;
	max-width: var(--process-steps-measure);
	padding: 0 0 var(--process-steps-rhythm) var(--process-steps-indent);
}

/* The connector line. A pseudo-element, so there is nothing for a screen
   reader to skip past and no markup to keep in sync. --------------------- */
.process-steps__step::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0;
	bottom: 0;
	width: 1px;
	background: var(--line);
}

.process-steps__step:last-child::before {
	content: none;
}

.process-steps__num {
	position: absolute;
	left: 0;
	top: 0;
	transform: translateX(-50%);
	width: var(--process-steps-badge);
	height: var(--process-steps-badge);
	display: flex;
	align-items: center;
	justify-content: center;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius-pill);
	/* Punches the connector line out behind the badge. */
	background: var(--bg);
	color: var(--kicker);
	font-family: var(--font-serif);
	font-size: var(--fs-lg);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
}

/* Item titles. color is inherited from the base h1-h6 rule, not restated. -- */
.process-steps__title {
	font-weight: var(--fw-regular);
	letter-spacing: var(--ls-tight);
}

.process-steps__title--step {
	font-size: var(--fs-h4);
	line-height: var(--lh-heading);
	max-width: var(--measure-label);
	padding-top: var(--gap-xs);
}

.process-steps__title--row {
	font-size: var(--fs-h3);
	line-height: var(--lh-display);
	max-width: var(--measure-label);
}

/* Item body copy. The grid column is the measure by default; the steps ladder
   caps it, because its column is far wider than the design's line. ------- */
.process-steps__text {
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
	color: var(--fg-2);
	max-width: none;
}

.process-steps__text--step {
	max-width: var(--measure-tight);
	padding-top: var(--gap-sm);
}

/* Cancels p { max-width: var(--measure) } from base.css, so the wrapper owns
   the measure. Nothing between body and here re-declares size or leading on
   p, so those are inherited rather than restated. ------------------------ */
.process-steps__text p {
	max-width: none;
}

/* base.css already zeroes the trailing margin on p:last-child. This covers only
   what it does not: an editor-supplied trailing list or quote. ----------- */
.process-steps__text > ul:last-child,
.process-steps__text > ol:last-child,
.process-steps__text > blockquote:last-child {
	margin-bottom: 0;
}

/* Ways variant ----------------------------------------------------------- */
.process-steps__rows {
	list-style: none;
	margin: 0;
	padding: 0;
}

.process-steps__row {
	border-top: 1px solid var(--line);
	padding-block: var(--gap-2xl);
}

/* Long words in either column must not blow the asymmetric split open. */
.process-steps__row-label,
.process-steps__row-body {
	min-width: 0;
}

/* Ghost pill link. Sibling of .header-cta in custom.css, deliberately not
   sharing its class: that one is the filled primary. The boundary uses
   --control-line, not --line-strong: --line-strong is a divider tint and
   measures 2.42:1 on this ground, which fails WCAG 1.4.11 for the only
   affordance identifying this control. --control-line passes at 3.51:1.
   The shared declarations here also sit on .button in parent-overrides.css and
   on .header-cta in custom.css, and want factoring into one .pill primitive:
   both of those are shared files this component does not own. ------------ */
.process-steps__cta-wrap {
	margin: var(--gap-sm) 0 0;
	max-width: none;
}

.process-steps__cta {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	padding: var(--gap-sm) var(--gap-lg);
	border: 1px solid var(--control-line);
	border-radius: var(--radius-pill);
	color: var(--fg);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	text-decoration: none;
	transition:
		background var(--dur) var(--ease),
		border-color var(--dur) var(--ease),
		color var(--dur) var(--ease);
}

.process-steps__cta:hover {
	background: var(--fg);
	border-color: var(--fg);
	color: var(--bg);
}

/* Written as its own rule rather than shared with :hover, per convention 5, so
   no hover declaration can ever reach the focused state. The global ring in
   parent-overrides.css is not restated and nothing here touches outline or
   box-shadow, so it stays visible on top of this fill. ------------------- */
.process-steps__cta:focus-visible {
	background: var(--fg);
	border-color: var(--fg);
	color: var(--bg);
}

.process-steps__cta-icon {
	font-size: var(--fs-lg);
	line-height: var(--lh-display);
}

/* ======================= team-card.css ======================= */
/*
 * Component: team-card
 *
 * The editorial team row from design section 48: a taupe role kicker, the
 * lawyer's name with optional post-nominals, a role line, specialism pills, a
 * biography, up to three fact columns, contact actions, and a portrait that
 * bleeds out through the container gutter. Two variants: the base row and
 * .team-card--lead, the larger founder row at the top of the section.
 *
 * Layout notes:
 * - No breakpoints. The row is a wrapping flex pair. Each column's flex-basis
 *   carries the design's own track ratio minus that column's share of the gap,
 *   and the bleeding column adds the bleed back, so the two hypothetical sizes
 *   plus the gap sum to exactly the line width. Free space is therefore zero
 *   and flex-grow never widens a column past its designed track. The clamp()
 *   floors are what force the wrap, the same construction as --cards-2up.
 * - The row gap is fluid, as the design draws it, and the flex bases are
 *   expressed against that same variable rather than a flattened constant.
 * - Reduced motion is handled once, globally, in base.css, which zeroes every
 *   transition under prefers-reduced-motion. This file adds no query.
 * - The portrait bleed uses logical margins so a right-to-left locale flips
 *   with the text.
 * - Elevation: the row itself has no surface, so the design's card shadow and
 *   hairline sit on the portrait, the one card-like object in the row, with the
 *   hover lift driven from the row.
 *
 * Values come from css/tokens.css, or from token arithmetic in calc() with the
 * design value named in a comment. Reused from base.css: .kicker, .small.
 * Reused from the parent: .screen-reader-text.
 */

/* Row ---------------------------------------------------------------------- */
.team-card {
	/* Column gap, design row 2: clamp(40px, 4.5vw, 72px). The flex bases below
	   are written against this, so changing it keeps the geometry sound. */
	--team-card-gap: clamp(var(--gap-xl), 4.5vw, var(--gap-3xl));

	/* Bleed distance. 0 unless the caller asks for the bleed, so the negative
	   margins below are a no-op by default and the basis arithmetic holds in
	   both states. The 2px overshoot is the design's own (section 48 uses
	   calc(-1 * clamp(24px,4vw,72px) - 2px)); it hides a hairline of page
	   ground at fractional device pixel ratios. */
	--team-card-bleed: 0px;

	/* The design's portrait caps, section 48 line 27, were 535px on this row and
	   549px on the founder row: the portrait stopped growing while its column
	   carried on. Retired on 15/09/2026, because that cap was exactly what held
	   the image short of the container height. Recorded here rather than
	   silently dropped, since it is a design value someone may ask about. */

	/* Fact grid, design section 48 line 38. The floor is the design's own, not
	   the 260px that --cards-3up imposes: this block is capped at a measure
	   that cannot hold three 260px tracks, so the base class would render 2+1
	   where the design renders 3-up. */
	--team-card-fact-gap: calc(var(--gap-sm) * 2 + var(--gap-2xs)); /* design 32px */
	--team-card-fact-floor: 180px;

	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: var(--team-card-gap);
	padding-top: var(--gap-3xl); /* design 72px */
	border-top: 1px solid var(--line);
}

.team-card--lead {
	/* Design row 1: gap clamp(40px, 4.5vw, 80px), portrait cap 549px, and the
	   founder's fact block is uncapped, so it keeps the --cards-3up floor. */
	--team-card-gap: clamp(var(--gap-xl), 4.5vw, var(--gap-4xl));
	--team-card-fact-floor: 260px;

	padding-top: calc(var(--gap-xl) + var(--gap-lg)); /* design 64px */
}

.team-card--bleed {
	--team-card-bleed: calc(var(--gutter) + 2px);
}

/* Tracks. Design row 2 is minmax(0,0.82fr) minmax(0,1.18fr) with the portrait
   first, so 41% / 59%; row 1 is 1.08fr / 0.92fr, so 54% / 46%. */
.team-card__body {
	flex: 1 1 clamp(280px, calc(59% - var(--team-card-gap) * 0.59), 100%);
	min-width: 0;
	/* The design lifts the copy off the foot of the portrait with a bottom
	   margin on the actions row. Carried on the column instead so the offset
	   survives a card with no actions. */
	padding-bottom: calc(var(--gap-xl) + var(--gap-2xs) * 2); /* design 48px */
}

/* Stretched to the row, not sized by the portrait.

   Adrian, 15/09/2026: "Can you raise her image to height of the container".
   Measured at 1880px the founder row was 878px tall and the portrait 549x686,
   leaving 192px of dead space above her head; at the narrower pane width the
   gap was 717px. The cause was the --team-card-portrait cap: the column was
   797px wide but the image was not allowed past 549px, so its 4/5 height
   stopped well short of the row.

   The column now stretches and the image fills it absolutely. Absolute is what
   makes this work at all: in flow, the image's own intrinsic height feeds back
   into the row height, and an earlier attempt grew the row from 878 to 1565
   chasing itself. Out of flow, the body column alone sets the height and the
   portrait simply fills whatever that is. */
.team-card__portrait {
	flex: 1 1 clamp(260px, calc(41% - var(--team-card-gap) * 0.41 + var(--team-card-bleed)), 100%);
	min-width: 0;
	align-self: stretch;
	position: relative;
}

.team-card--lead .team-card__body {
	flex-basis: clamp(280px, calc(54% - var(--team-card-gap) * 0.54), 100%);
}

.team-card--lead .team-card__portrait {
	flex-basis: clamp(260px, calc(46% - var(--team-card-gap) * 0.46 + var(--team-card-bleed)), 100%);
}

/* Portrait ----------------------------------------------------------------- */
.team-card--media-right .team-card__portrait {
	margin-inline-end: calc(-1 * var(--team-card-bleed));
}

.team-card--media-left .team-card__portrait {
	margin-inline-start: calc(-1 * var(--team-card-bleed));
}

/* No frame and no elevation. The Stage 4 build gave the portrait the card
   hairline and shadow, reasoning that it was the one card-like object in an
   otherwise unbounded row. On review that reads as a framed stock photo rather
   than an editorial portrait bleeding off the page edge, which is what the
   design draws. The lift on hover stays; only the frame goes. */
/* Anchored to the top edge, not the centre. These portraits are cut-outs with
   almost no headroom: measured on the source files, the hair starts 3px from
   the top on Sarah's and 33px on Lisa's. Sarah's is 1086x1393, which is 0.7796
   against the frame's 0.8, so cover scales to the width and trims roughly 18
   source pixels off each of the top and bottom. Eighteen into three is a
   chopped head, which is what Adrian saw.

   object-position: 50% 0 moves the whole trim to the bottom, where these
   figures are already bleeding out of the layout and nothing is lost. Lisa's
   file is 1122x1402, a hair over 4/5 exactly, so it is not cropped either way
   and this changes nothing for her.

   Left as a rule rather than a per-image focal point: the constraint is the
   same for every portrait the firm will ever supply, and the fix should not
   depend on someone remembering to set a field. */
/* contain, not cover. These portraits are cut-outs on transparency and the
   figure runs nearly edge to edge: measured on Lisa's 1122x1402 source she
   spans about 79% of the width. Filling the taller box with cover would have
   scaled to the height and trimmed 28% off each side, taking both her arms
   with it. contain scales her to the box instead and never crops; the slack is
   transparent, so it reads as the page ground rather than a letterbox. */
.team-card__img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
	transition: transform var(--dur) var(--ease);
}

.team-card:hover .team-card__img {
	transform: var(--card-hover-lift);
}

/* The figure sits on the bottom of its box and hugs the edge it bleeds through,
   so any slack falls into the gap between the columns and she still runs off
   the foot of the row the way the design draws her. Margin auto did this while
   the image was in flow and capped; object-position does it now that the image
   fills the column and the slack is inside the box rather than around it. */
.team-card--media-right .team-card__img {
	object-position: 100% 100%;
}

.team-card--media-left .team-card__img {
	object-position: 0% 100%;
}

/* Award badge, added 15/09/2026 on Adrian's brief: "next to lisa kind of
   covering her", and mirrored for Sarah.

   It sits on the side of the portrait that faces the text, which is the side
   the figure is NOT hugging: object-position pins her to the bleeding edge, so
   the opposite edge of the box is empty and the badge lands beside her with a
   small overlap rather than across her face.

   The width is set so the badge always laps ONTO the figure. How much clear
   space there is depends on the figure's ratio against its box, and the two
   rows differ: at 1880px Lisa's box is 797px against a 651px figure, 146px
   clear, while Sarah's is 719px against 567px, 152px clear. A 168px badge
   covered Lisa by 21px and missed Sarah by 1px, sitting flush instead of over
   her. 24% capped at 184px overlaps both, 38px and 21px respectively.

   Inline-start for the media-right row and inline-end for media-left, so the
   two lawyers mirror each other and a right-to-left locale flips with them.

   The badge is a third party's mark on its own light card. It keeps its own
   ground: no tinting, no blend mode, no opacity. */
.team-card__award {
	position: absolute;
	/* Sitting on the rule, not floating above it. The portrait column stretches
	   to the full row, so its bottom edge IS the hairline between this lawyer
	   and the next, and 0 rests the badge exactly on it. It was 8% before, which
	   left it hovering 65px clear of the line with no relationship to anything.

	   Stacked, Sarah's portrait comes before her text rather than after it, so
	   there is no rule directly beneath it. The badge then sits on the foot of
	   her photograph, which is the same gesture against the nearest edge. */
	inset-block-end: 0;
	z-index: 1;
	width: clamp(92px, 24%, 184px);
	height: auto;
	border-radius: 2px;
}

.team-card--media-right .team-card__award {
	inset-inline-start: 0;
}

.team-card--media-left .team-card__award {
	inset-inline-end: 0;
}

/* Stacked. Below the wrap the portrait has no sibling to stretch against, and
   an absolutely positioned image would collapse the column to nothing, so the
   column takes the design's own 4/5 box back. 899px matches the breakpoint
   custom.css already uses for .case-detail__split rather than inventing one. */
@media (max-width: 899px) {
	.team-card__portrait {
		align-self: auto;
		aspect-ratio: 4 / 5;
	}
}

/* Name block --------------------------------------------------------------- */
/* .kicker supplies the colour, size, weight, tracking and case. Only the
   design's 18px offset differs from the base class. */
.team-card__kicker {
	margin-bottom: calc(var(--gap-sm) + var(--gap-2xs)); /* design 18px */
}

.team-card__name {
	margin: 0 0 calc(var(--gap-2xs) * 3); /* design 12px */
	color: var(--fg);
	font-size: var(--fs-h3);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-heading);
}

/* The name's size is fixed by variant and never tracks heading_level: the
   design draws the founder's name larger than the section heading above it. */
.team-card--lead .team-card__name {
	font-size: var(--fs-h1);
}

/* Post-nominals read as part of the name, so they carry no styling of their
   own. The hook exists to stop "LL.M. oec." breaking across two lines. */
.team-card__post-nominals {
	white-space: nowrap;
}

.team-card__role {
	margin: 0 0 var(--gap-md); /* design 20px */
	color: var(--fg-2);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
}

.team-card--lead .team-card__role {
	margin-bottom: calc(var(--gap-sm) + var(--gap-2xs) * 2); /* design 22px */
}

/* Specialism pills --------------------------------------------------------- */
.team-card__tags {
	display: flex;
	flex-wrap: wrap;
	gap: calc(var(--gap-2xs) * 2); /* design 8px */
	margin: 0 0 calc(var(--gap-sm) * 2 + var(--gap-2xs)); /* design 32px */
	padding: 0;
	list-style: none;
}

.team-card--lead .team-card__tags {
	margin-bottom: calc(var(--gap-md) + var(--gap-sm)); /* design 34px */
}

/* A specialism is caller copy and can be a long German compound, so the pill
   wraps inside itself rather than pushing the page sideways at 320px. */
.team-card__tag {
	display: inline-block;
	max-width: 100%;
	padding: 0.45em 0.95em; /* design 5px 11px at --fs-3xs */
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	color: var(--fg-2);
	font-size: var(--fs-3xs);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	overflow-wrap: anywhere;
}

/* Biography ---------------------------------------------------------------- */
.team-card__bio {
	display: flex;
	flex-direction: column;
	gap: calc(var(--gap-sm) + var(--gap-2xs)); /* design 18px */
	margin: 0 0 var(--gap-xl); /* design 40px */
	color: var(--fg-2);
	font-size: var(--fs-body);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
}

.team-card__bio > * {
	margin: 0;
	max-width: var(--measure-tight); /* design 62ch, nearest token */
}

.team-card--lead .team-card__bio {
	gap: var(--gap-md); /* design 20px */
	margin-bottom: calc(var(--gap-xl) + var(--gap-2xs)); /* design 44px */
	font-size: var(--fs-lg);
}

.team-card--lead .team-card__bio > * {
	max-width: var(--measure-intro); /* design 58ch, nearest token */
}

/* Fact columns ------------------------------------------------------------- */
.team-card__facts {
	display: grid;
	grid-template-columns: repeat(
		auto-fit,
		minmax(clamp(var(--team-card-fact-floor), calc(33.333% - var(--team-card-fact-gap) * 2 / 3), 100%), 1fr)
	);
	gap: var(--team-card-fact-gap);
	max-width: var(--measure-tight); /* design 60ch, nearest token */
	margin: 0;
	padding: 0;
	list-style: none;
}

.team-card--lead .team-card__facts {
	max-width: none;
}

.team-card__fact {
	min-width: 0;
}

/* .kicker carries the design's label treatment verbatim. Three things differ:
   the hairline, the 16px drop to the first bullet, and the label runs the full
   column width rather than .kicker's --measure-label. */
.team-card__fact-label {
	margin-bottom: calc(var(--gap-md) - var(--gap-2xs)); /* design 16px */
	padding-bottom: var(--gap-sm); /* design 14px */
	border-bottom: 1px solid var(--line);
	max-width: none;
}

.team-card__fact-list {
	margin: 0;
	padding: 0 0 0 var(--gap-md); /* design 20px */
	color: var(--fg-2);
	font-weight: var(--fw-light);
	list-style: disc;
}

.team-card__fact-list li + li {
	margin-top: var(--gap-xs); /* design 10px */
}

/* Actions ------------------------------------------------------------------ */
.team-card__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--gap-sm); /* design 14px */
	margin-top: calc(var(--gap-xl) - var(--gap-2xs)); /* design 36px */
}

/* The outline pill's only identifying boundary is its border, so that border
   must clear 3:1 against the ground for 1.4.11. The design's --line-strong
   composites to 2.42:1 and fails; --control-line is the token the Stage 5 pass
   added for exactly this, at 3.51:1. The label alone cannot carry the
   affordance, because it is --fg against --fg-2 body copy.
   The label is caller copy, so the pill wraps inside itself at 320px. */
.team-card__action {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs); /* design 10px */
	max-width: 100%;
	padding: 1.1em 1.85em; /* design 14px 24px at --fs-xs */
	border: 1px solid var(--control-line);
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--fg);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	line-height: var(--lh-ui);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	text-decoration: none;
	overflow-wrap: anywhere;
	transition:
		background var(--dur) var(--ease),
		border-color var(--dur) var(--ease),
		color var(--dur) var(--ease);
}

.team-card__action--primary {
	border-color: var(--accent);
	background: var(--accent);
	color: var(--accent-fg);
}

/* :hover only. The focus indicator is the global :focus-visible ring in
   parent-overrides.css and must not be shared with, or painted out by, this
   rule. */
.team-card__action:hover {
	border-color: var(--fg);
	background: var(--fg);
	color: var(--bg);
	text-decoration: none;
}

.team-card__action-icon {
	flex: none;
	width: 1.25em;
	height: 1.25em;
}

/* ======================= testimonial-block.css ======================= */
/*
 * Testimonial block, design section 4.
 *
 * A rule divided grid of pull quotes, each attributed with an initials disc, a
 * name and a role. Reuses .section, .container and .kicker. This file carries
 * the grid, the divider and the attribution treatment.
 *
 * No media queries. The grid declares its own auto-fit floor rather than
 * borrowing .cards-2up: this list is deliberately wider than its container, so
 * a percentage floor written for the container would resolve against the wrong
 * box and hold two columns while the text measure fell under 280px. See the
 * comment on .testimonial-block__list for the working.
 */

.testimonial-block {
	/* The column floor: 280px of readable text, which is the floor base.css
	   declares inside --cards-2up. The token exposes it only inside a
	   composite value, so it is restated here rather than reached for.

	   It used to carry an extra 57px, because each column gave that back to
	   the divider that ran down its leading edge. The divider was removed on
	   review, so the compensation went with it. */
	--testimonial-block-col-floor: 280px;

	/* Disc diameter, from the design. No token covers component dimensions. */
	--testimonial-block-avatar: 76px;

	/* Room held outside the clip on all four sides, so the global focus ring
	   on a link a caller puts inside a quote is never trimmed. The ring reaches
	   4px past the element box and this is 10px. It stays far under the 57px
	   offset, so a leading divider is still clipped. */
	--testimonial-block-clip-room: var(--gap-xs);
}

/* Head ----------------------------------------------------------------- */
.testimonial-block__head {
	padding-bottom: var(--gap-xl);
	margin-bottom: var(--gap-2xl);
	border-bottom: 1px solid var(--line);
}

/* Base .kicker is 11.5px; the design sets this eyebrow two steps up the static
   ramp, at 13px. max-width: none drops base's 20ch label measure, which this
   head does not have: the slice runs the eyebrow on one line, and a wrapped
   eyebrow would push the hairline and the whole grid down. Same reason as
   facts-bar. */
.testimonial-block__kicker {
	font-size: var(--fs-xs);
	max-width: none;
}

/* Weight and tracking are the design's. The size is deliberately not pinned:
   the element keeps the size base.css gives its own level, so a caller who
   lowers the level to keep the outline right does not get the largest heading
   on the page. A caller who wants a display size at a lower level asks for it
   explicitly through the heading_size arg, which adds one modifier below. */
.testimonial-block__title {
	font-weight: var(--fw-regular);
	letter-spacing: var(--ls-heading);
}

.testimonial-block__title--display {
	font-size: var(--fs-display);
	line-height: var(--lh-display);
}

/* The design's own section 4 treatment: clamp(32px, 3.4vw, 50px) at 1.06. */
.testimonial-block__title--h1 {
	font-size: var(--fs-h1);
	line-height: var(--lh-display);
}

.testimonial-block__title--h2 {
	font-size: var(--fs-h2);
	line-height: var(--lh-heading);
}

.testimonial-block__title--h3 {
	font-size: var(--fs-h3);
	line-height: var(--lh-tight);
}

.testimonial-block__title--h4 {
	font-size: var(--fs-h4);
	line-height: var(--lh-snug);
}

.testimonial-block__title--h5 {
	font-size: var(--fs-h5);
	line-height: var(--lh-snug);
}

.testimonial-block__title--h6 {
	font-size: var(--fs-lg);
	line-height: var(--lh-ui);
}

/* Grid ----------------------------------------------------------------- */

/*
 * The clip once hid the first column's divider, which ran down the leading
 * edge of every item and was pulled outside the container so the first one of
 * each row fell off the edge. The divider was removed on review and the whole
 * arrangement went with it.
 *
 * The clip stays, doing the smaller job it always also did: holding 10px on
 * all four sides so the global focus ring on a link inside a quote is never
 * trimmed. The room is symmetric and the negative margin gives it back, so the
 * head's 56px separation and the section's own padding are unchanged.
 */
.testimonial-block__viewport {
	overflow: hidden;
	padding: var(--testimonial-block-clip-room);
	margin: calc(-1 * var(--testimonial-block-clip-room));
}

/*
 * The floor, worked out rather than borrowed.
 *
 * 280px of readable text per column, the same floor base.css uses inside
 * --cards-2up. auto-fit holds two columns while 2F + 56 fits the grid width,
 * so with F = 280 the row collapses to one column below 616px of container.
 *
 * The middle clamp value is half the gap short of half the grid, which is the
 * exact two column track, so above the floor auto-fit resolves to two and
 * never to three.
 *
 * .cards-2up is deliberately not reused. Its gap would be overridden, which is
 * reuse that changes the geometry rather than reuse that saves work, so the
 * grid is declared outright.
 *
 * This used to carry an extra 57px per column, given back to a leading
 * divider. That divider is gone; so is the compensation.
 */
.testimonial-block__list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(clamp(var(--testimonial-block-col-floor), calc(50% - var(--gap-2xl) / 2), 100%), 1fr));
	gap: var(--gap-2xl);
	list-style: none;
	margin: 0;
	padding: 0;
}

/* min-inline-size overrides a grid item's automatic minimum, which would
   otherwise let an unbreakable string in caller rich text push the item past
   its track and into the clip. */
.testimonial-block__item {
	min-inline-size: 0;
	overflow-wrap: break-word;
}

.testimonial-block__figure {
	display: flex;
	flex-direction: column;
	gap: var(--gap-lg);
	min-inline-size: 0;
}

/* Quote ---------------------------------------------------------------- */

/* border: 0 because this is a blockquote, and parent-overrides.css gives every
   blockquote a leading hairline. That is right for a pull quote inside an
   article, which is where the design draws one, and wrong here: a testimonial
   is a card, not an aside, and the rule it inherited was the second line to
   come off this component. */
.testimonial-block__quote {
	border: 0;
	margin: 0;
	min-inline-size: 0;
	overflow-wrap: break-word;
	font-size: var(--fs-xl);
	line-height: var(--lh-snug);
	font-weight: var(--fw-light);
	letter-spacing: var(--ls-tight);
	color: var(--fg);
}

/* Callers may pass rich text, so keep the last block flush with the caption. */
.testimonial-block__quote > :last-child {
	margin-bottom: 0;
}

/* A preformatted block cannot wrap, so it scrolls inside itself rather than
   running into the clip, which has no scroll mechanism of its own. */
.testimonial-block__quote pre {
	max-inline-size: 100%;
	overflow-x: auto;
}

.testimonial-block__quote table {
	max-inline-size: 100%;
}

/* Attribution ---------------------------------------------------------- */
.testimonial-block__attribution {
	display: flex;
	align-items: center;
	gap: var(--gap-lg);
}

.testimonial-block__avatar {
	flex: none;
	display: flex;
	align-items: center;
	justify-content: center;
	inline-size: var(--testimonial-block-avatar);
	block-size: var(--testimonial-block-avatar);
	border-radius: var(--radius-pill);
	background: var(--fg);
	color: var(--bg-deep);
	font-size: var(--fs-xl);
	font-weight: var(--fw-medium);
}

.testimonial-block__ident {
	display: flex;
	flex-direction: column;
	gap: var(--gap-2xs);
	min-inline-size: 0;
}

.testimonial-block__name {
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-regular);
	line-height: var(--lh-ui);
	color: var(--fg);
}

.testimonial-block__role {
	font-size: var(--fs-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-ui);
	color: var(--fg-2);
}
