/*
 * Bohardien component and page styles.
 *
 * Tokens live in css/tokens.css. Never hardcode a colour, size, radius or
 * spacing value here.
 *
 * Stage 2: utility bar, header, primary navigation, service tab strip, footer.
 */

/* ===========================================================================
 * Utility bar
 * The one deliberate light-on-dark inversion in the system.
 * ======================================================================== */
.utility-bar {
	background: var(--util);
	color: var(--util-fg);
}

.utility-bar__inner {
	min-height: 42px;
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: 28px;
	font-size: var(--fs-2xs);
	letter-spacing: 0.01em;
}

.utility-bar__link {
	color: var(--util-fg-2);
	text-decoration: none;
}

/* Hover lifts the weight as well as the colour. Free of charge here: Host
   Grotesk is duplexed, so 300 and 500 share advance widths to the sub-pixel
   (measured at 100px: 885.55px at 300, 500, 700 and 900 alike). Nothing
   reflows, so no hidden bold copy is needed to reserve the space. 500 is the
   top of the design's weight scale, which is light-first. */
.utility-bar__link:hover,
.utility-bar__link:focus-visible {
	color: var(--util-fg);
	font-weight: var(--fw-medium);
}

/* The EN / DE switch. The design sets the pair 8px apart with a slash between,
   the current language at --util-fg and medium weight, the other at
   --util-fg-2 lifting to --util-fg on hover. */
.utility-bar__langs {
	display: flex;
	align-items: center;
	gap: var(--gap-2xs) calc(var(--gap-2xs) * 2); /* design 8px */
}

.utility-bar__lang {
	color: var(--util-fg-2);
	text-decoration: none;
	transition: color var(--dur) var(--ease);
}

/* :link and :visited named for the same reason as everything else in this
   file: parent-overrides.css sets a:visited at (0,1,1), and a visited German
   link would otherwise take the global link colour on the light utility bar. */
.utility-bar__lang:link,
.utility-bar__lang:visited {
	color: var(--util-fg-2);
}

.utility-bar__lang:hover,
.utility-bar__lang:focus-visible,
.utility-bar__lang.is-current,
.utility-bar__lang.is-current:link,
.utility-bar__lang.is-current:visited {
	color: var(--util-fg);
}

.utility-bar__lang.is-current {
	font-weight: var(--fw-medium);
}

.utility-bar__lang-sep {
	color: var(--util-fg-2);
	user-select: none;
}

/* The online shop has no destination yet, so the label says so on hover rather
   than pointing at #. Both labels occupy the same grid cell, so the anchor
   takes the width of the wider one and the bar holds still while they
   cross-fade. */
.utility-bar__swap {
	display: inline-grid;
	align-items: center;
}

.utility-bar__swap > span {
	grid-area: 1 / 1;
	white-space: nowrap;
	transition: opacity var(--dur) var(--ease);
}

/* The arrow is the first label's, not the anchor's, so it leaves when that
   label does. "Coming soon ↗" would point at nothing. */
.utility-bar__swap-from::after {
	content: " \2197";
}

.utility-bar__swap-to {
	opacity: 0;
}

.utility-bar__link--swap:hover .utility-bar__swap-from,
.utility-bar__link--swap:focus-visible .utility-bar__swap-from {
	opacity: 0;
}

.utility-bar__link--swap:hover .utility-bar__swap-to,
.utility-bar__link--swap:focus-visible .utility-bar__swap-to {
	opacity: 1;
}

/* ===========================================================================
 * Header
 * ======================================================================== */
.site-header {
	position: sticky;
	top: 0;
	z-index: 40;
	background: var(--hdr);
	backdrop-filter: blur(14px);
	-webkit-backdrop-filter: blur(14px);
	/* The border itself is declared transparent in parent-overrides.css, where
	   the parent's near-white one is neutralised. Only the colour changes here,
	   so nothing reflows when the line appears. */
	transition: border-color var(--dur) var(--ease);
}

/* Sticky only. At rest the header sits on the page with no divider, as the
   design draws it; once it lifts off and starts overlapping content it needs
   the faintest possible edge to separate itself. --line-soft is the quietest
   line in the token set.

   The class is set by an IntersectionObserver in js/custom.js, because a
   sticky element has no CSS state of its own. With JavaScript off the line
   simply never appears, which is the right way round: the resting state is
   the designed one. */
.site-header.is-stuck {
	border-bottom-color: var(--line-soft);
}

/* The sentinel js/custom.js inserts above the header to detect the stuck
   state. One pixel tall so the observer has a real box to watch, with that
   pixel cancelled again by the negative margin so it cannot move anything. */
.site-header__sentinel {
	height: 1px;
	margin-bottom: -1px;
	pointer-events: none;
}

.site-header__inner {
	padding-block: 14px;
	min-height: 84px;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	row-gap: var(--gap-xs);
	column-gap: clamp(16px, 2.5vw, 48px);
	min-width: 0;
}

.site-branding {
	flex: none;
	display: flex;
	align-items: center;
}

.site-branding__link {
	display: flex;
	align-items: center;
	gap: 12px;
	text-decoration: none;
	color: var(--fg);
}

.site-branding__link svg {
	display: block;
	flex: none;
	width: 143px;
	height: auto;
}

.site-branding__text {
	font-size: var(--fs-lg);
	font-weight: var(--fw-medium);
	color: var(--fg);
}

/* Header call to action --------------------------------------------------- */
.header-cta {
	flex: none;
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	padding: 13px 22px;
	border: 1px solid var(--accent);
	border-radius: var(--radius-pill);
	background: var(--accent);
	color: var(--accent-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), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.header-cta:hover,
.header-cta:focus-visible {
	background: var(--fg);
	border-color: var(--fg);
	color: var(--bg);
}

/* ===========================================================================
 * Primary navigation
 * The parent js/navigation.js owns the off-canvas panel and adds .focus to
 * parent list items on keyboard focus. This styles both that and hover.
 * ======================================================================== */
.main-navigation {
	display: flex;
	align-items: center;
	margin-left: auto;
	min-width: 0;
	flex: 1 1 auto;
}

.main-navigation .nav-menu {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	justify-content: flex-end;
	gap: 2px;
	list-style: none;
	margin: 0;
	padding: 0;
	width: 100%;
}

.main-navigation .nav-menu li {
	position: relative;
}

.main-navigation .nav-menu a {
	display: flex;
	align-items: center;
	gap: 7px;
	padding: var(--gap-xs) clamp(8px, 1vw, 14px);
	border-radius: var(--radius-md);
	font-size: 16px;
	font-weight: var(--fw-light);
	color: var(--fg-2);
	text-decoration: none;
	white-space: nowrap;
	transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.main-navigation .nav-menu a:hover,
.main-navigation .nav-menu a:focus-visible,
.main-navigation .nav-menu .current-menu-item > a,
.main-navigation .nav-menu .current-menu-ancestor > a {
	color: var(--fg);
	background: rgba(210, 220, 230, 0.08);
}

/* Caret on items with a submenu. Four lines of CSS instead of a whole
   Walker_Nav_Menu subclass. --------------------------------------------- */
.main-navigation .menu-item-has-children > a::after {
	content: "";
	width: 9px;
	height: 9px;
	flex: none;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: translateY(-2px) rotate(45deg);
	opacity: 0.75;
	transition: transform var(--dur) var(--ease);
}

.main-navigation .menu-item-has-children:hover > a::after,
.main-navigation .menu-item-has-children.focus > a::after,
.main-navigation .menu-item-has-children:focus-within > a::after {
	transform: translateY(1px) rotate(225deg);
}

/* Submenu ---------------------------------------------------------------- */
.main-navigation .nav-menu ul {
	position: absolute;
	top: 100%;
	left: 0;
	/* Sized to its longest item rather than to a fixed 268px. That width was
	   arbitrary and about 96px wider than the content needs, which left a
	   short entry like Overview with a highlight running 178px past the end of
	   the word. max-content hugs the longest label; the floor keeps a
	   two-word item from looking cramped and the ceiling stops a long one
	   running away. */
	width: max-content;
	/* The floor is the trigger it hangs from, not a number someone picked: a
	   panel narrower than the item that opened it looks like a mistake, and
	   anything wider than its longest label is dead space after every short
	   one. */
	min-width: 100%;
	max-width: 320px;
	margin: 0;
	padding: var(--gap-2xs) 0 0;
	list-style: none;
	display: none;
	z-index: 41;
}

.main-navigation .nav-menu li:hover > ul,
.main-navigation .nav-menu li.focus > ul,
.main-navigation .nav-menu li:focus-within > ul {
	display: block;
}

.main-navigation .nav-menu ul li {
	display: block;
}

/* The panel is drawn on a pseudo-element so the outer ul keeps a hover-safe
   gap between the trigger and the panel. -------------------------------- */
.main-navigation .nav-menu ul::before {
	content: "";
	position: absolute;
	inset: 8px 0 0;
	background: var(--bg);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.55);
	z-index: -1;
}

.main-navigation .nav-menu ul a {
	position: relative;
	display: block;
	margin: 0 var(--gap-2xs);
	padding: var(--gap-xs) 12px;
	border-radius: var(--radius-sm);
	font-size: var(--fs-sm);
	white-space: normal;
}

.main-navigation .nav-menu ul li:first-child a {
	margin-top: 12px;
}

.main-navigation .nav-menu ul li:last-child a {
	margin-bottom: var(--gap-2xs);
}

/* ===========================================================================
 * Off-canvas panel, mobile
 * ======================================================================== */
/* Both controls are round, to sit with the pill language the rest of the site
   uses rather than the squared-off default box. 44px is the tap target, and it
   is the size the Get in touch pill already stands beside. ---------------- */
.menu-toggle,
.nav-close {
	display: none;
	place-items: center;
	width: 44px;
	height: 44px;
	padding: 0;
	background: transparent;
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	color: var(--fg);
	cursor: pointer;
	transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.menu-toggle:hover,
.nav-close:hover {
	background: rgba(210, 220, 230, 0.08);
	border-color: var(--line-strong);
}

/* Three 16px bars on a 2px rhythm. The middle bar is the element, the outer
   two are its pseudo-elements, which is what lets the whole thing become an X
   without a second icon: the middle fades, the outer two travel to the centre
   and cross. Transform and opacity only, so it composites. */
.menu-toggle-bars,
.menu-toggle-bars::before,
.menu-toggle-bars::after {
	display: block;
	width: 16px;
	height: 1.5px;
	border-radius: 2px;
	background: currentColor;
	position: relative;
	transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease), background var(--dur) var(--ease);
}

.menu-toggle-bars::before,
.menu-toggle-bars::after {
	content: "";
	position: absolute;
	left: 0;
}

.menu-toggle-bars::before {
	top: -5px;
}

.menu-toggle-bars::after {
	top: 5px;
}

/* navigation.js in the parent puts .nav-open on the body, so the burger can
   answer the drawer's state without any JavaScript of our own. */
body.nav-open .menu-toggle {
	border-color: var(--line-strong);
}

body.nav-open .menu-toggle-bars {
	background: transparent;
}

body.nav-open .menu-toggle-bars::before {
	transform: translateY(5px) rotate(45deg);
}

body.nav-open .menu-toggle-bars::after {
	transform: translateY(-5px) rotate(-45deg);
}

/* Drawer-only chrome. Above the breakpoint the nav is an inline row in the
   header, where a wordmark and a second Get in touch would be duplicates of
   what is already beside them, so both are off until the drawer exists. */
.nav-drawer__head,
.nav-drawer__foot {
	display: none;
}

.nav-overlay {
	display: none;
}

@media (max-width: 1023px) {
	/* backdrop-filter makes an element a containing block for position: fixed
	   descendants, which anchors the drawer and its overlay to the header
	   instead of the viewport. The header ground is already 90% opaque, so
	   dropping the blur at drawer widths costs almost nothing visually and is
	   the only fix that does not move the nav out of the header. ---------- */
	.site-header {
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
		background: var(--bg);
	}

	.menu-toggle {
		display: grid;
		margin-left: auto;
		order: 2;
	}

	.header-cta {
		order: 3;
	}

	/* The scrim. Without it the drawer slid over a page still at full
	   brightness, which is most of why the menu read as unfinished: nothing
	   said the page behind had been put aside. Driven off body.nav-open, so
	   it needs no JavaScript beyond what the parent already does. */
	.nav-overlay {
		display: block;
		position: fixed;
		inset: 0;
		z-index: 55;
		background: rgba(6, 14, 24, 0.62);
		backdrop-filter: blur(4px);
		-webkit-backdrop-filter: blur(4px);
		opacity: 0;
		visibility: hidden;
		transition: opacity 240ms var(--ease), visibility 240ms var(--ease);
	}

	body.nav-open .nav-overlay {
		opacity: 1;
		visibility: visible;
	}

	.main-navigation {
		position: fixed;
		inset: 0 0 0 auto;
		width: min(88vw, 400px);
		background: var(--bg-deep);
		border-left: 1px solid var(--line);
		/* The drawer is the nearest thing on the page, so it casts the deepest
		   shadow the tokens carry. */
		box-shadow: var(--shadow-overlay);
		padding: var(--gutter) var(--gutter) calc(var(--gutter) + env(safe-area-inset-bottom));
		display: flex;
		flex-direction: column;
		align-items: stretch;
		overflow-y: auto;
		overscroll-behavior: contain;
		transform: translateX(101%);
		transition: transform 280ms var(--ease);
		z-index: 60;
		margin-left: 0;
		flex: none;
	}

	.main-navigation.toggled {
		transform: translateX(0);
	}

	/* Masthead: wordmark on the left, close on the right, on one line. The old
	   panel opened with 88px of nothing and a floating cross in the corner. */
	.nav-drawer__head {
		display: flex;
		align-items: center;
		min-height: 44px;
		padding-inline-end: 56px; /* clears the close button */
		margin-bottom: var(--gap-xl);
	}

	.nav-drawer__head svg {
		width: 124px;
		height: auto;
		display: block;
	}

	.nav-close {
		display: grid;
		position: absolute;
		top: var(--gutter);
		right: var(--gutter);
	}

	.main-navigation .nav-menu {
		flex-direction: column;
		align-items: stretch;
		justify-content: flex-start;
		gap: 0;
	}

	/* Serif, at heading size. The drawer is the one place the navigation has
	   room to speak in the same editorial voice as the pages it leads to;
	   13px sans links in a 400px panel were what made it read as default. */
	.main-navigation .nav-menu > li > a {
		font-family: var(--font-serif);
		font-size: var(--fs-h4);
		font-weight: var(--fw-regular);
		line-height: var(--lh-display);
		letter-spacing: var(--ls-heading);
		text-transform: none;
		padding: 14px 0;
		white-space: normal;
	}

	.main-navigation .nav-menu > li + li {
		border-top: 1px solid var(--line);
	}

	/* The current page is marked with the accent and a rule, not the filled
	   grey slab the shared rule paints. A block of background inside a drawer
	   reads as a button that does nothing. */
	.main-navigation .nav-menu a:hover,
	.main-navigation .nav-menu a:focus-visible,
	.main-navigation .nav-menu .current-menu-item > a,
	.main-navigation .nav-menu .current-menu-ancestor > a {
		background: transparent;
		color: var(--accent);
	}

	.main-navigation .nav-menu a {
		font-size: var(--fs-lg);
		padding: 12px 0;
		white-space: normal;
	}

	/* Submenus sit inline on mobile. There is no hover on touch, and a
	   floating panel inside a sliding drawer is a trap. ----------------- */
	.main-navigation .nav-menu ul {
		position: static;
		display: block;
		min-width: 0;
		padding: 0 0 var(--gap-xs) 14px;
		/* Reset the panel border and radius set for the desktop dropdown in
		   parent-overrides.css. Inline, it only needs the left rule. */
		border: 0;
		border-left: 1px solid var(--line);
		border-radius: 0;
		background: transparent;
		margin: 0 0 var(--gap-xs);
	}

	.main-navigation .nav-menu ul::before {
		display: none;
	}

	.main-navigation .nav-menu ul a {
		margin: 0;
		padding: var(--gap-xs) 0;
		font-size: var(--fs-body-sm);
	}

	.main-navigation .nav-menu ul li:first-child a,
	.main-navigation .nav-menu ul li:last-child a {
		margin: 0;
	}

	/* Children stay sans and quiet, so the serif parents carry the structure.
	   The left rule is the only thing holding them in; the per-item hairlines
	   the desktop dropdown uses just made a ladder of lines in here. */
	.main-navigation .nav-menu ul a {
		font-family: var(--font-sans);
		font-size: var(--fs-body-sm);
		color: var(--fg-2);
		padding: 9px 0;
		border: 0;
	}

	.main-navigation .nav-menu ul a:hover,
	.main-navigation .nav-menu ul a:focus-visible,
	.main-navigation .nav-menu ul .current-menu-item > a {
		color: var(--accent);
	}

	.main-navigation .menu-item-has-children > a::after {
		display: none;
	}

	/* The call to action sits at the foot of the panel, pushed there by the
	   auto margin so it lands at the bottom on a short menu and after the last
	   link on a long one. */
	.nav-drawer__foot {
		display: block;
		margin-top: auto;
		padding-top: var(--gap-xl);
	}

	/* Scoped through .main-navigation on purpose. The parent theme carries
	   `.main-navigation a { display: block; padding: 0.75rem 0 }` at (0,1,1),
	   which outranks the shared `.button` at (0,1,0), so the pill rendered as a
	   full-width block: label jammed against the left edge, arrow dropped onto
	   a second line, the rounded background sitting behind none of it. Two
	   classes beat one class and an element. */
	.main-navigation .nav-drawer__cta {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: var(--gap-xs);
		width: 100%;
		padding: var(--gap-sm) var(--gap-lg);
		text-align: center;
	}

	/* The links arrive just behind the panel, one after another. Small numbers
	   on purpose: 26ms apart and 260ms long, so the last item has landed before
	   the drawer itself has finished, and it reads as one movement rather than
	   a list assembling itself. */
	@keyframes bohardien-nav-in {
		from {
			opacity: 0;
			transform: translateX(14px);
		}
	}

	body.nav-open .main-navigation .nav-menu > li,
	body.nav-open .nav-drawer__foot {
		animation: bohardien-nav-in 260ms var(--ease) backwards;
	}

	body.nav-open .main-navigation .nav-menu > li:nth-child(1) { animation-delay: 60ms; }
	body.nav-open .main-navigation .nav-menu > li:nth-child(2) { animation-delay: 86ms; }
	body.nav-open .main-navigation .nav-menu > li:nth-child(3) { animation-delay: 112ms; }
	body.nav-open .main-navigation .nav-menu > li:nth-child(4) { animation-delay: 138ms; }
	body.nav-open .main-navigation .nav-menu > li:nth-child(5) { animation-delay: 164ms; }
	body.nav-open .main-navigation .nav-menu > li:nth-child(n+6) { animation-delay: 190ms; }
	body.nav-open .nav-drawer__foot { animation-delay: 216ms; }

	/* base.css zeroes transitions under prefers-reduced-motion but not
	   animations, so this one states its own opt-out rather than assuming. */
	@media (prefers-reduced-motion: reduce) {
		body.nav-open .main-navigation .nav-menu > li,
		body.nav-open .nav-drawer__foot {
			animation: none;
		}

		.main-navigation {
			transition: none;
		}
	}

	/* The scrim itself is declared once, further up this same query, where it
	   gains its blur and its fade. This block used to redeclare it: two rules
	   setting the background and z-index of the same element a couple of
	   hundred lines apart, with the later one silently winning. Only the scroll
	   lock belongs here. */
	body.nav-open {
		overflow: hidden;
	}

	/* The toggle sits inside the header and paints over the open drawer, which
	   left two close affordances stacked on each other. Hidden by visibility
	   rather than display so the header layout does not shift. ----------- */
	body.nav-open .menu-toggle {
		visibility: hidden;
	}
}

@media (max-width: 599px) {
	.site-branding__link svg {
		width: 118px;
	}

	.header-cta {
		padding: 11px 16px;
		font-size: var(--fs-3xs);
	}

	.utility-bar__inner {
		gap: var(--gap-md);
		justify-content: flex-start;
	}
}

/* ===========================================================================
 * Service tab strip
 * ======================================================================== */
.service-tabs {
	position: sticky;
	top: 84px;
	z-index: 19;
	background: var(--hdr);
	backdrop-filter: blur(10px);
	-webkit-backdrop-filter: blur(10px);
	border-bottom: 1px solid var(--line);
}

.service-tabs__inner {
	display: flex;
	gap: 6px;
	overflow-x: auto;
	scrollbar-width: none;
}

.service-tabs__inner::-webkit-scrollbar {
	display: none;
}

.service-tabs__tab {
	display: inline-flex;
	align-items: baseline;
	gap: 12px;
	padding: 19px 22px;
	color: var(--fg-2);
	text-decoration: none;
	white-space: nowrap;
	border-bottom: 2px solid transparent;
	transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.service-tabs__tab:hover,
.service-tabs__tab:focus-visible {
	color: var(--fg);
}

.service-tabs__tab.is-current {
	color: var(--fg);
	border-bottom-color: var(--accent);
}

.service-tabs__num {
	font-size: 12px;
	font-weight: var(--fw-medium);
	letter-spacing: 0.14em;
	color: var(--kicker);
}

.service-tabs__label {
	font-size: 15px;
	font-weight: var(--fw-medium);
	letter-spacing: 0.01em;
}

/* ===========================================================================
 * Footer
 * ======================================================================== */
.site-footer {
	border-top: 1px solid var(--line);
	padding: 100px 0 40px;
}

.site-footer__top {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
	align-items: start;
	gap: 48px var(--gap-xl);
	padding-bottom: var(--gap-3xl);
	border-bottom: 1px solid var(--line);
}

/* Mark and award side by side, Adrian 15/09/2026: "Logo left - award on the
   right". They were stacked, which read as two separate marks down the column
   rather than one credential sitting beside the identity. Centred on each
   other because the badge is the taller of the two, so aligning either edge
   would leave it visibly hanging. Wraps rather than squashing when the column
   is narrow. */
.site-footer__brand {
	grid-column: span 2;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--gap-lg);
}

.site-footer__brand svg {
	width: 135px;
	height: 135px;
	display: block;
}

/* The firm's Handelsblatt award, added 15/09/2026. The badge is supplied as a
   light card with its own white ground and drop shadow, so it sits on the dark
   footer as an object rather than being tinted to match: it is a third party's
   mark and recolouring it would misrepresent it. Sized against the symbol above
   it rather than a round number, so the two read as a pair. */
.site-footer__award {
	display: block;
	width: 110px;
	height: auto;
	border-radius: 2px;
}

.site-footer__col-heading {
	margin: 0 0 6px;
	font-size: 12px;
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker-wide);
	text-transform: uppercase;
	color: var(--kicker);
}

.site-footer__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--gap-sm);
}

.site-footer__list a {
	font-size: 15px;
	font-weight: var(--fw-light);
	color: var(--fg-2);
	text-decoration: none;
}

.site-footer__list a:hover,
.site-footer__list a:focus-visible {
	color: var(--fg);
	text-decoration: underline;
}

/* External links get the diagonal arrow by class, so the client never has to
   type the glyph into a menu label. ------------------------------------- */
.site-footer__list a.is-external::after,
.utility-bar__link.is-external::after {
	content: " \2197";
}

/* Bottom row ------------------------------------------------------------- */
.site-footer__bottom {
	padding-top: 32px;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px 28px;
	font-size: 13.5px;
	font-weight: var(--fw-light);
	color: var(--fg-2);
}

.site-footer__policies-list {
	list-style: none;
	display: inline-flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--gap-xs);
	margin: 0;
	padding: 0;
}

.site-footer__policies-list a {
	color: var(--fg-2);
	text-decoration: none;
}

.site-footer__policies-list a:hover,
.site-footer__policies-list a:focus-visible {
	color: var(--fg);
	text-decoration: underline;
}

/* The middle dot separator, rationed to this one metadata row. ---------- */
.site-footer__policies-list li + li::before {
	content: "\00b7";
	color: var(--faint);
	margin-right: var(--gap-xs);
}

.site-footer__carbon {
	margin-left: auto;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 6px 12px;
	border: 1px solid rgba(210, 220, 230, 0.24);
	border-radius: var(--radius-pill);
	font-size: var(--fs-2xs);
}

.site-footer__carbon-dot {
	width: 7px;
	height: 7px;
	border-radius: var(--radius-pill);
	background: var(--accent);
	flex: none;
}

.site-footer__credit strong {
	font-weight: var(--fw-medium);
	color: var(--fg);
}

/* The credit reads as text, not as a blue link: the design draws it as plain
   white emphasis and only the underline on hover says it is clickable.

   :link and :visited are named because parent-overrides.css sets a:visited to
   --link at (0,1,1); .site-footer__credit-link is (0,2,0) and clears it, the
   same lift the other component anchors in that file already take. */
.site-footer__credit-link,
.site-footer__credit-link:link,
.site-footer__credit-link:visited {
	color: var(--fg);
	text-decoration: none;
}

.site-footer__credit-link:hover,
.site-footer__credit-link:focus-visible {
	color: var(--fg);
	text-decoration: underline;
	text-underline-offset: 2px;
}

@media (max-width: 899px) {
	.site-footer {
		padding-top: var(--gap-3xl);
	}

	.site-footer__brand {
		grid-column: span 1;
	}

	.site-footer__brand svg {
		width: 96px;
		height: 96px;
	}

	.site-footer__award {
		width: 88px;
	}

	.site-footer__carbon {
		margin-left: 0;
	}
}

/* ===========================================================================
 * Home page
 * Stage 5a. Section-specific layout that no component owns.
 * ======================================================================== */

/* Who we are, design section 1. A two up auto-fit band, not one of the four
   split ratios: the design pairs a 22ch heading column with a 56ch prose
   column on an 88px gap, and gives the band its own taller rhythm. The floor
   is the design's own clamp(280px, calc(50% - 44px), 100%), tuned to that gap,
   so --cards-2up cannot be reused: its floor assumes a 24px gap and would
   collapse this band to one column at every width. -------------------------- */
.home-about {
	padding-block: clamp(96px, 11vw, 164px);
}

.home-about__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(clamp(280px, calc(50% - 44px), 100%), 1fr));
	gap: clamp(44px, 4.5vw, 88px);
	align-items: start;
}

.home-about__heading {
	max-width: 22ch;
}

.home-about__body {
	max-width: 56ch;
}

/* The design's own offset above the button, carried on the paragraph's
   bottom margin there: clamp(36px, 3.5vw, 52px). */
.home-about__more {
	margin-top: clamp(calc(var(--gap-xl) - var(--gap-2xs)), 3.5vw, calc(var(--gap-2xl) - var(--gap-2xs)));
}

/* The affordance is an outlined pill in the design, not a text link, so it
   uses the shared .button rather than a bespoke rule. Only the icon spacing
   is local. */
.home-about__link {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	text-decoration: none;
}

.home-about__link .icon {
	transition: transform var(--dur) var(--ease);
}

.home-about__link:hover .icon {
	transform: translateX(var(--gap-2xs));
}

/* Our expertise, design section 2. The list is a plain stack of rows; the
   component owns the row itself. ------------------------------------------- */
.expertise-card-list {
	list-style: none;
	margin: var(--gap-3xl) 0 0;
	padding: 0;
}

/* Our work, design section 3 --------------------------------------------- */
.home-work__head {
	margin-bottom: var(--gap-3xl);
}

/* Design: margin-top 56px. The first pass used 40px. */
.home-work__more {
	margin-top: var(--gap-2xl);
}

/* ===========================================================================
 * Related case studies, on a practice area page
 * Design section 13. The head is ruled off from the grid, as on the career
 * page, and the grid runs the design's 32px gap rather than the 24px that
 * .cards-3up gives by default.
 * ======================================================================== */
.service-cases__head {
	padding-bottom: calc(var(--gap-xl) + var(--gap-2xs)); /* design 44px */
	margin-bottom: var(--gap-2xl);
	border-bottom: 1px solid var(--line);
}

.service-cases__heading {
	font-size: var(--fs-h2);
}

.service-cases__grid {
	gap: calc(var(--gap-2xl) - var(--gap-lg)); /* design 32px */
}

/* The button had no rule at all, so it inherited a paragraph's zero top
   margin and sat flush against the grid. The design's equivalent on the home
   page stands 56px clear. */
.service-cases__more {
	margin-top: var(--gap-2xl);
}

/* ===========================================================================
 * Case studies index
 * Stage 5e. Design section 37: search, four facet dropdowns, count, clear.
 * ======================================================================== */
.case-index__toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--gap-sm) var(--gap-md);
	margin: var(--gap-3xl) 0 var(--gap-xl);
	padding-bottom: var(--gap-md);
	border-bottom: 1px solid var(--line);
}

.case-index__search {
	flex: 1 1 clamp(200px, 28%, 320px);
	display: block;
}

.case-index__search-input[type="search"] {
	width: 100%;
	background: var(--panel-field);
	color: var(--panel-field-fg);
	border: 1px solid var(--control-line);
	border-radius: var(--radius-pill);
	padding: 11px 18px;
	font-family: var(--font-sans);
	font-size: var(--fs-sm);
}

.case-index__search-input::placeholder {
	color: var(--panel-field-ph);
}

/* Facets ----------------------------------------------------------------- */
.case-index__facets {
	display: flex;
	flex-wrap: wrap;
	gap: var(--gap-xs);
}

.case-index__facet {
	position: relative;
}

.case-index__facet-toggle {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	padding: 10px 16px;
	background: transparent;
	border: 1px solid var(--control-line);
	border-radius: var(--radius-pill);
	color: var(--fg-2);
	font-family: var(--font-sans);
	font-size: var(--fs-sm);
	font-weight: var(--fw-medium);
	cursor: pointer;
	transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.case-index__facet-toggle:hover,
.case-index__facet.is-open .case-index__facet-toggle {
	color: var(--fg);
	border-color: var(--fg);
}

.case-index__facet.has-selection .case-index__facet-toggle {
	color: var(--accent-fg);
	background: var(--accent);
	border-color: var(--accent);
}

.case-index__facet-count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 1.35em;
	height: 1.35em;
	border-radius: var(--radius-pill);
	background: var(--accent-fg);
	color: var(--accent);
	font-size: var(--fs-3xs);
}

.case-index__facet-caret {
	width: 7px;
	height: 7px;
	flex: none;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: translateY(-2px) rotate(45deg);
	transition: transform var(--dur) var(--ease);
}

.case-index__facet.is-open .case-index__facet-caret {
	transform: translateY(1px) rotate(225deg);
}

.case-index__facet-panel {
	position: absolute;
	top: calc(100% + var(--gap-2xs));
	left: 0;
	z-index: 20;
	min-width: 248px;
	max-height: 340px;
	overflow-y: auto;
	background: var(--bg-deep);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-overlay);
	padding: var(--gap-xs);
}

.case-index__facet-set {
	border: 0;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
}

.case-index__option {
	display: flex;
	align-items: center;
	gap: var(--gap-xs);
	padding: 9px 12px;
	border-radius: var(--radius-sm);
	color: var(--fg-2);
	font-size: var(--fs-sm);
	cursor: pointer;
}

.case-index__option:hover {
	color: var(--fg);
	background: var(--panel-card);
}

.case-index__option input {
	accent-color: var(--accent);
	flex: none;
}

/* Status ---------------------------------------------------------------- */
.case-index__status {
	display: flex;
	align-items: center;
	gap: var(--gap-md);
	margin-left: auto;
}

.case-index__count {
	margin: 0;
	white-space: nowrap;
}

.case-index__clear {
	background: transparent;
	border: 0;
	padding: 0;
	color: var(--accent);
	font-family: var(--font-sans);
	font-size: var(--fs-sm);
	font-weight: var(--fw-medium);
	text-decoration: underline;
	cursor: pointer;
}

.case-index__clear:hover {
	color: var(--fg);
}

/* Empty state ----------------------------------------------------------- */
.case-index__empty {
	padding: var(--gap-3xl) 0;
	max-width: var(--measure-intro);
}

.case-index__empty-heading {
	color: var(--fg);
	font-family: var(--font-serif);
	font-size: var(--fs-h4);
	margin-bottom: var(--gap-xs);
}

.case-index__empty .button {
	margin-top: var(--gap-md);
}

/* ===========================================================================
 * Case study detail
 * Stage 5f. Sections 40 and 43 are --split-lean with the fluid gap, which is
 * exactly what those tokens were added for.
 * ======================================================================== */
.case-detail__band--open {
	border-top: 0;
}

.case-detail__split {
	display: grid;
	grid-template-columns: var(--split-lean);
	gap: var(--gap-fluid);
	align-items: start;
}

.case-detail__prose {
	max-width: 70ch;
	margin-top: var(--gap-md);
}

.case-detail__caption {
	margin-top: var(--gap-md);
}

.case-detail__back {
	padding: 64px 0 72px;
	border-top: 1px solid var(--line);
}

@media (max-width: 899px) {
	.case-detail__split {
		grid-template-columns: minmax(0, 1fr);
		gap: var(--gap-xl);
	}

	.case-index__status {
		margin-left: 0;
		width: 100%;
	}
}

/* ===========================================================================
 * About
 * Stage 5g. Design sections 47 to 49. The team rows carry their own rule and
 * top padding in team-card.css, so this file only sets the section head and
 * the section's own padding.
 * ======================================================================== */
.about-team {
	/* Design section 48 runs 110px 0 0: the last team row closes the section,
	   so there is no bottom padding to double up with the row's own. */
	padding-block-end: 0;
	overflow: hidden;
}

.about-team__head {
	padding-bottom: calc(var(--gap-xl) + var(--gap-2xs)); /* design 44px */
}

.about-team__heading {
	font-size: var(--fs-h2);
}

/* ===========================================================================
 * News index
 * Stage 5h. Design sections 50 to 53. The grid sits on the inverted panel,
 * which is why the fields below reach for the --panel-* tokens directly
 * rather than the page-ground ones: .section--panel changes the background,
 * not the token set.
 *
 * The design draws the three filters as 8px fields with a caret. They are
 * built as real select elements: two single-choice filters and a sort do not
 * need the disclosure pattern the case studies index uses, and a native
 * select is keyboard and screen reader correct with nothing added.
 * ======================================================================== */
/* The toolbar matches the case studies index, at Adrian's request on review.
   Same row gap and same wider column gap, so the two indexes read as one
   control system rather than two. */
.news-index__toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--gap-sm) var(--gap-md);
	padding-bottom: calc(var(--gap-xl) - var(--gap-2xs)); /* design 36px */
	margin-bottom: calc(var(--gap-2xl) - var(--gap-xs) + var(--gap-2xs) / 2); /* design 48px */
	border-bottom: 1px solid var(--panel-line);
}

.news-index__search {
	display: block;
	flex: 1 1 240px;
	min-width: 240px;
}

/* Pill, not a rounded rectangle, and the case index's 11px 18px. */
.news-index__search-input[type="search"] {
	width: 100%;
	padding: 11px 18px;
	background: var(--panel-field);
	border: 1px solid var(--control-line);
	border-radius: var(--radius-pill);
	color: var(--panel-field-fg);
	font-family: var(--font-sans);
	font-size: var(--fs-sm);
	font-weight: var(--fw-light);
}

.news-index__search-input::placeholder {
	color: var(--panel-field-ph);
}

.news-index__selects {
	display: flex;
	flex-wrap: wrap;
	gap: var(--gap-xs);
}

/* The label wraps the select so the caret can be drawn on it. The platform
   arrow is removed, so the caret is the only affordance left and must not
   steal the click: pointer-events stay off it. */
.news-index__select {
	position: relative;
	display: inline-flex;
	min-width: 0;
}

/* Styled as the case index's facet pills: transparent, pill radius, the
   secondary text colour and medium weight, with the case index's 10px 16px
   plus room on the end for the caret. These stay native selects, because the
   news toolbar is three single-choice controls where the case index is four
   multi-select facets; only the appearance is shared, not the mechanism. */
.news-index__select select {
	appearance: none;
	padding: 10px calc(var(--gap-xl) - var(--gap-2xs)) 10px 16px;
	background: transparent;
	border: 1px solid var(--control-line);
	border-radius: var(--radius-pill);
	color: var(--panel-fg-2);
	font-family: var(--font-sans);
	font-size: var(--fs-sm);
	font-weight: var(--fw-medium);
	cursor: pointer;
	max-width: 100%;
	transition: color var(--dur) var(--ease), background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.news-index__select select:hover {
	color: var(--panel-fg);
	border-color: var(--panel-fg);
}

/* The filled state the case facets use once a filter is on. Applied by
   news-filter.js to the topic and type controls only: sort always holds a
   value, so filling it would say "filtering" when nothing is filtered. */
.news-index__select.is-selected select {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--accent-fg);
}

/* The open option list is painted by the platform, not the page, so it does
   not inherit the panel colours. Naming them keeps it legible wherever the
   platform does honour them. */
.news-index__select option {
	background: var(--panel);
	color: var(--panel-fg);
}

/* The case index's chevron, not a solid triangle: two borders on a rotated
   square. currentColor is inherited from the label, so it is set here to
   follow whichever state the select is in. */
.news-index__select::after {
	content: "";
	position: absolute;
	inset-inline-end: 16px;
	top: 50%;
	width: 7px;
	height: 7px;
	margin-top: -5px;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate(45deg);
	color: var(--panel-fg-2);
	pointer-events: none;
	transition: color var(--dur) var(--ease);
}

.news-index__select:hover::after {
	color: var(--panel-fg);
}

.news-index__select.is-selected::after {
	color: var(--accent-fg);
}

/* margin-inline-start: auto pushes the count and the clear link to the end of
   the row, which is what .case-index__status does with its own wrapper. The
   news toolbar has no wrapper, so the count carries it and the clear link
   follows on the toolbar's own gap. */
.news-index__count {
	margin: 0;
	margin-inline-start: auto;
	color: var(--kicker);
	font-size: var(--fs-xs);
	font-weight: var(--fw-light);
	white-space: nowrap;
}

.news-index__clear {
	background: transparent;
	border: 0;
	padding: 0;
	color: var(--accent);
	font-family: var(--font-sans);
	font-size: var(--fs-sm);
	font-weight: var(--fw-medium);
	text-decoration: underline;
	cursor: pointer;
}

.news-index__clear:hover {
	color: var(--panel-fg);
}

.news-index__grid {
	/* The design's news grid runs a 32px gap, which is also the gap the
	   --cards-3up floor was calculated against, so it is set here rather than
	   inheriting the 24px .cards-3up default. */
	gap: calc(var(--gap-2xl) - var(--gap-lg)); /* design 32px */
}

.news-index__item {
	display: flex;
	min-width: 0;
}

.news-index__empty {
	padding: var(--gap-3xl) 0;
	max-width: var(--measure-intro);
}

.news-index__empty-heading {
	color: var(--panel-fg);
	font-family: var(--font-serif);
	font-size: var(--fs-h4);
	margin-bottom: var(--gap-xs);
}

.news-index__empty .button {
	margin-top: var(--gap-md);
}

/* ===========================================================================
 * Article
 * Stage 5i. Design sections 54 to 56. A single column at --measure-prose.
 *
 * Everything under .article-body__prose styles editor output, so this is the
 * one place in the theme where element selectors are correct: the client's
 * paragraphs and headings arrive without classes.
 * ======================================================================== */
.article-body__prose {
	max-width: var(--measure-prose);
}

/* base.css caps every paragraph at --measure. Inside the prose column the
   column itself is the measure, so the inner cap would narrow it twice. */
.article-body__prose p {
	max-width: 100%;
	color: var(--fg-2);
	font-weight: var(--fw-light);
	line-height: var(--lh-body);
}

.article-body__prose h2 {
	margin: var(--gap-2xl) 0 var(--gap-md); /* design 56px 0 20px */
	font-size: var(--fs-h3);
}

.article-body__prose h3 {
	margin: var(--gap-xl) 0 var(--gap-sm);
	font-size: var(--fs-h4);
}

.article-body__prose ul,
.article-body__prose ol {
	margin: 0 0 var(--gap-md);
	padding-inline-start: var(--gap-md);
	color: var(--fg-2);
	font-weight: var(--fw-light);
	line-height: var(--lh-body);
}

.article-body__prose li {
	margin-bottom: var(--gap-xs);
}

/* Pull quote, set against the ruled edge rather than in quotation marks. The
   serif and its 400 weight come from base.css, which sets blockquote with the
   headings; the design drops this one to 300. */
.article-body__prose blockquote {
	margin: calc(var(--gap-xl) + var(--gap-2xs)) 0; /* design 44px */
	padding-inline-start: calc(var(--gap-lg) + var(--gap-2xs)); /* design 28px */
	border-inline-start: 1px solid var(--accent);
	color: var(--fg);
	font-size: var(--fs-xl);
	font-weight: var(--fw-light);
	line-height: var(--lh-snug);
	letter-spacing: var(--ls-tight);
}

.article-body__prose blockquote p {
	color: inherit;
	font-size: inherit;
	font-weight: inherit;
	line-height: inherit;
}

/* Media dropped into the body. The design draws a fixed 380px band, but the
   editor inserts images of whatever shape the author uploaded, so the height
   is left to the image and only the frame is styled. Cropping a client's
   photograph to a band it was never composed for is the worse failure. */
.article-body__prose figure,
.article-body__prose img {
	margin: calc(var(--gap-xl) + var(--gap-2xs)) 0; /* design 44px */
	border-radius: var(--radius-figure);
	overflow: hidden;
	background: var(--well);
}

.article-body__prose figure img {
	margin: 0;
	display: block;
	width: 100%;
	height: auto;
}

.article-body__prose figcaption {
	margin-top: var(--gap-xs);
	color: var(--ph);
	font-size: var(--fs-xs);
}

.article-body__back {
	margin-top: var(--gap-2xl); /* design 56px */
}

.article-body__back-link {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-xs);
	color: var(--link);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	text-decoration: none;
}

.article-body__back-link:hover {
	text-decoration: underline;
}

/* ===========================================================================
 * Contact
 * Stage 5j. Design sections 57 to 59. Two columns: the enquiry form on the
 * left, booking and the office on the right.
 *
 * Static sizes follow the normalisation tokens.css documents: the design's
 * 17px reads as --fs-body-sm, its 14px and 13.5px as --fs-sm and --fs-xs, its
 * 1.62 leading as --lh-body-sm.
 * ======================================================================== */
.contact__grid {
	display: grid;
	grid-template-columns: var(--cards-2up);
	/* The design's 56px, which is also the gap --cards-2up's floor was
	   calculated against, so it is set rather than inheriting .cards-2up. */
	gap: var(--gap-2xl);
	align-items: start;
}

.contact__form {
	display: flex;
	flex-direction: column;
	gap: var(--gap-lg);
	min-width: 0;
}

.contact__aside {
	display: flex;
	flex-direction: column;
	gap: var(--gap-md);
	min-width: 0;
}

/* With no enquiry form the grid has a single child, and auto-fit collapses the
   empty track and lets that child stretch the full container: a 16/9 map on a
   1700px line is nearly a thousand pixels tall. The aside is a sidebar and
   should never outgrow the column the design gives it, whether or not the
   other column is there. */
.contact__aside:only-child {
	max-width: var(--measure);
}

/* The design draws these two headings at 500, heavier than the 400 base.css
   gives every heading. They stay in the serif: the design overrides the weight
   on this rule and not the family. */
.contact__block-title {
	font-size: var(--fs-xl);
	font-weight: var(--fw-medium);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-heading);
	margin-bottom: var(--gap-md);
}

.contact__block-intro {
	max-width: none;
	color: var(--fg-2);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
	margin-bottom: var(--gap-md);
}

/* Booking ----------------------------------------------------------------- */
.booking-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: calc( ( var(--gap-xs) + var(--gap-sm) ) / 2 ); /* design 12px */
}

/* The design renders these as buttons opening an inline scheduling widget, and
   styles them from a design-tool variable rather than literal markup, so the
   card surface is not in the source. Built from the same card language the
   rest of the theme uses: the well, the card hairline and the shared lift. */
.booking-card {
	display: flex;
	align-items: center;
	gap: var(--gap-sm);
	padding: var(--gap-sm);
	border: 1px solid var(--card-line);
	border-radius: var(--radius-lg);
	background: var(--well);
	color: inherit;
	text-decoration: none;
	transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.booking-card:hover {
	background: var(--lift);
	border-color: var(--line-strong);
	transform: var(--card-hover-lift);
	text-decoration: none;
}

.booking-card__portrait {
	flex: none;
	width: 56px;
	height: 56px;
	border-radius: var(--radius-pill);
	object-fit: cover;
	/* The design pulls both portraits up so the crop lands on the face. */
	object-position: 50% 12%;
	background: var(--well);
}

.booking-card__body {
	display: flex;
	flex-direction: column;
	gap: var(--gap-2xs);
	min-width: 0;
	flex: 1;
}

.booking-card__name {
	color: var(--fg);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-body);
}

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

.booking-card__cue {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-2xs);
	flex: none;
	color: var(--link);
	font-size: var(--fs-2xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	white-space: nowrap;
}

.booking-card:hover .booking-card__cue {
	transform: translateX(var(--gap-2xs));
	transition: transform var(--dur) var(--ease);
}

/* Office ------------------------------------------------------------------ */
.office-details {
	list-style: none;
	margin: 0 0 var(--gap-md);
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--gap-sm);
}

.office-details__item {
	display: flex;
	flex-wrap: wrap;
	gap: var(--gap-2xs) var(--gap-sm);
	align-items: baseline;
}

.office-details__label {
	flex: none;
	min-width: var(--measure-label);
	color: var(--kicker);
	font-size: var(--fs-3xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
}

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

/* Map --------------------------------------------------------------------- */

/* A static schematic, not an embed. Nothing third party loads, so there is no
   consent gate to get wrong and no map tile request before the visitor has
   agreed to anything. The link out is the only thing that leaves the site. */
.office-map {
	position: relative;
	aspect-ratio: 16 / 9;
	border: 1px solid var(--line);
	border-radius: var(--radius-figure);
	background: var(--well);
	overflow: hidden;
}

.office-map__svg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	display: block;
}

.office-map__address,
.office-map__link {
	position: absolute;
	bottom: var(--gap-sm);
	/* The scrim is what keeps both chips legible over the artwork. */
	background: var(--chip-scrim);
	border: 1px solid var(--line-strong);
}

.office-map__address {
	inset-inline-start: var(--gap-sm);
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin: 0;
	max-width: calc(100% - var(--gap-3xl));
	padding: var(--gap-xs) var(--gap-sm);
	border-radius: var(--radius-md);
}

.office-map__street {
	color: var(--fg);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
}

.office-map__city {
	color: var(--fg-2);
	font-size: var(--fs-2xs);
	font-weight: var(--fw-light);
}

.office-map__link {
	inset-inline-end: var(--gap-sm);
	display: inline-flex;
	align-items: center;
	gap: var(--gap-2xs);
	padding: var(--gap-xs) var(--gap-sm);
	border-radius: var(--radius-pill);
	color: var(--fg);
	font-size: var(--fs-3xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	text-decoration: none;
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.office-map__link:hover {
	background: var(--fg);
	border-color: var(--fg);
	color: var(--bg);
	text-decoration: none;
}

.office-map__external {
	font-size: var(--fs-sm);
	line-height: 1;
}

/* Gravity Forms sit inside .contact__form. The fields themselves are handled
   globally in parent-overrides.css, so only the form's own scaffolding is
   named here, and only once the form exists in Stage 6. */
.contact__form .gform_wrapper {
	margin: 0;
}

/* ===========================================================================
 * Action row
 * Stage 5k. Design section 61, reused by the 404 and external-link screens.
 * ======================================================================== */
.action-row__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--gap-sm);
}

.action-row__item {
	display: flex;
	min-width: 0;
}

/* ===========================================================================
 * Career
 * Stage 5m. Design sections 64 to 66. The closing band is cta-band on the
 * panel, which it already is by default, so nothing is needed for it here.
 * ======================================================================== */
.career__head {
	padding-bottom: calc(var(--gap-xl) + var(--gap-2xs)); /* design 44px */
	margin-bottom: var(--gap-2xl);
	border-bottom: 1px solid var(--line);
}

.career__heading {
	font-size: var(--fs-h2);
}

.career__list {
	list-style: none;
	margin: 0;
	padding: 0;
	/* The gap is named once and the track floor is written against it, the same
	   construction team-card.css uses, so the two cannot drift apart. The
	   design's own floor is calc(50% - 17px) against a 32px gap. */
	--career-gap: calc(var(--gap-2xl) - var(--gap-lg)); /* design 32px */

	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(clamp(260px, calc(50% - var(--career-gap) / 2 - 1px), 100%), 1fr));
	gap: var(--career-gap);
}

.career__item {
	display: flex;
	min-width: 0;
}

.vacancy-card {
	display: flex;
	flex-direction: column;
	gap: calc(var(--gap-md) - 2px); /* design 18px */
	flex: 1;
	min-width: 0;
	padding: calc(var(--gap-2xl) - var(--gap-md) - 2px); /* design 34px */
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-card);
	color: inherit;
	text-decoration: none;
	transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.vacancy-card:hover {
	background: var(--lift);
	border-color: var(--line-strong);
	transform: var(--card-hover-lift);
	text-decoration: none;
}

/* The house mark, turned 45 degrees as the design has it. Sized on the span so
   the inline SVG inherits it rather than carrying width attributes. */
.vacancy-card__mark {
	display: block;
	width: var(--gap-md);
	height: var(--gap-md);
	flex: none;
	color: var(--kicker);
	transform: rotate(45deg);
}

.vacancy-card__mark svg {
	display: block;
	width: 100%;
	height: 100%;
}

.vacancy-card__title {
	margin: 0;
	font-size: var(--fs-xl);
	font-weight: var(--fw-medium);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-heading);
}

.vacancy-card__summary {
	margin: 0;
	flex: 1;
	max-width: none;
	color: var(--fg-2);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-snug);
}

.vacancy-card__cue {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-2xs);
	color: var(--link);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
}

.vacancy-card:hover .vacancy-card__cue {
	transform: translateX(var(--gap-2xs));
	transition: transform var(--dur) var(--ease);
}

/* ===========================================================================
 * Site search
 * Stage 5o and 5p. Design sections 70 and 79: one field, optionally with a
 * count beside it. Field colours come from parent-overrides.css; only the two
 * that differ from the global default are named here.
 * ======================================================================== */
.site-search {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--gap-sm);
	padding-bottom: calc(var(--gap-xl) - var(--gap-2xs)); /* design 36px */
	margin-bottom: calc(var(--gap-2xl) - var(--gap-xs) + var(--gap-2xs) / 2); /* design 48px */
	border-bottom: 1px solid var(--line);
}

.site-search__form {
	display: flex;
	flex: 1 1 240px;
	gap: var(--gap-xs);
	min-width: 240px;
}

.site-search__input[type="search"] {
	flex: 1;
	min-width: 0;
	padding: calc((var(--gap-xs) + var(--gap-sm)) / 2) calc(var(--gap-md) - var(--gap-2xs)); /* design 12px 16px */
	background: var(--panel-field);
	border-radius: var(--radius-md);
	font-size: var(--fs-sm);
	font-weight: var(--fw-light);
}

.site-search__submit {
	flex: none;
	padding: calc((var(--gap-xs) + var(--gap-sm)) / 2) var(--gap-md);
	border: 1px solid var(--control-line);
	border-radius: var(--radius-md);
	background: transparent;
	color: var(--fg);
	font-family: var(--font-sans);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
	cursor: pointer;
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.site-search__submit:hover {
	background: var(--fg);
	border-color: var(--fg);
	color: var(--bg);
}

.site-search__count {
	margin: 0;
	color: var(--kicker);
	font-size: var(--fs-xs);
	font-weight: var(--fw-light);
	white-space: nowrap;
}

/* ===========================================================================
 * Search results
 * Stage 5o. Design section 70: a ruled list, no cards.
 * ======================================================================== */
.search-results__list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.search-results__item {
	min-width: 0;
}

/* The design fills this row with --lift on hover, and the fill's left edge
   landed on the first letter. Rather than pad around the fill, the fill is gone
   at Adrian's request on review: the row now answers the way the expertise rows
   do, with the nudge and the cue at the end of it.

   Only the nudge is declared here. The underline under the cue label and the
   arrow's rotation come from the shared pattern in base.css, which already
   lists .search-result alongside the five other rows that use it. */
.search-result {
	display: flex;
	flex-direction: column;
	gap: calc((var(--gap-xs) + var(--gap-sm)) / 2); /* design 12px */
	padding: calc(var(--gap-2xl) - var(--gap-lg)) 0; /* design 32px */
	border-top: 1px solid var(--line);
	color: inherit;
	text-decoration: none;
	transition: transform var(--dur) var(--ease);
}

/* The rule is on the row, so it travels with it, exactly as the expertise list
   behaves. */
.search-result:hover,
.search-result:focus-visible {
	transform: translateX(var(--gap-md));
	text-decoration: none;
}

.search-result__type {
	margin: 0;
}

.search-result__title {
	margin: 0;
	font-size: var(--fs-h4);
	font-weight: var(--fw-medium);
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-tight);
}

.search-result__extract {
	margin: 0;
	max-width: var(--measure-tight);
	color: var(--fg-2);
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-body-sm);
}

.search-result__cue {
	display: inline-flex;
	align-items: center;
	gap: var(--gap-2xs);
	color: var(--link);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
}

.search-results__empty {
	padding: var(--gap-3xl) 0;
	max-width: var(--measure-intro);
}

.search-results__empty-heading {
	color: var(--fg);
	font-family: var(--font-serif);
	font-size: var(--fs-h4);
	margin-bottom: var(--gap-xs);
}

/* ===========================================================================
 * Sitemap
 * Stage 5q. Design section 81: a card per section of the site.
 * ======================================================================== */
/* The design writes repeat(4, minmax(0, 1fr)), which has no floor and squeezes
   four tracks into a 390px viewport. --cards-4up is the same four columns with
   the floor convention 3 requires, so the row wraps instead. */
.sitemap__grid {
	display: grid;
	grid-template-columns: var(--cards-4up);
	gap: var(--gap-lg);
	align-items: start;
}

.sitemap__card {
	min-width: 0;
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-card);
	overflow: hidden;
}

.sitemap__card-title {
	margin: 0;
	padding: var(--gap-sm) var(--gap-md); /* design 16px 20px */
	border-bottom: 1px solid var(--line);
	font-family: var(--font-sans);
	font-size: var(--fs-sm);
	font-weight: var(--fw-medium);
	color: var(--fg);
}

.sitemap__links {
	list-style: none;
	margin: 0;
	padding: var(--gap-md);
	display: flex;
	flex-direction: column;
	gap: calc((var(--gap-xs) + var(--gap-sm)) / 2); /* design 12px */
}

.sitemap__link-item {
	min-width: 0;
}

.sitemap__link {
	color: var(--fg-2);
	font-size: var(--fs-sm);
	font-weight: var(--fw-light);
	text-decoration: none;
	overflow-wrap: anywhere;
}

.sitemap__link:hover {
	color: var(--fg);
	text-decoration: underline;
}

/* ===========================================================================
 * Legal pages
 * Stage 5s to 5u. Design sections 71 to 76: imprint, data policy, cookie
 * notice. One column at --measure-prose with a rule above each heading.
 *
 * The body is editor output, so these are element selectors by necessity,
 * the same reasoning as .article-body__prose.
 * ======================================================================== */
.legal__body {
	max-width: var(--measure-prose);
}

.legal__head {
	margin-bottom: var(--gap-2xl);
}

/* The global .kicker caps itself at --measure-label, 149.5px, which is right
   for the eyebrow labels it was written for: "Clients", "Good to know", "Who we
   advised". The legal kickers are not labels, they are sentences carrying a
   statutory reference, and at 149.5px inside an 887px column "Information
   pursuant to Section 5 TMG" broke onto three lines. The German is worse:
   "Angaben gemäß § 5 Digitale Dienste Gesetz (DDG)". Uncapped here only. */
.legal__head .kicker {
	max-width: none;
}

.legal__title {
	font-size: var(--fs-h3);
}

/* The design rules off each block and leaves 44px above its heading. Carried
   on the heading rather than a wrapper, because the editor produces a flat
   run of headings and paragraphs with nothing to wrap. */
.legal__prose h2 {
	margin: var(--gap-2xl) 0 var(--gap-md);
	padding-top: calc(var(--gap-xl) + var(--gap-2xs)); /* design 44px */
	border-top: 1px solid var(--line);
	font-size: var(--fs-h4);
}

/* The first block sits directly under the head, which already carries the
   space, so it needs neither rule nor padding. */
.legal__prose > h2:first-child {
	margin-top: 0;
	padding-top: 0;
	border-top: 0;
}

.legal__prose h3 {
	margin: var(--gap-xl) 0 var(--gap-sm);
	font-size: var(--fs-h5);
}

.legal__prose p {
	max-width: 100%;
	margin: 0 0 var(--gap-md);
	color: var(--fg-2);
	font-weight: var(--fw-light);
	line-height: var(--lh-body);
}

.legal__prose ul,
.legal__prose ol {
	margin: 0 0 var(--gap-md);
	padding-inline-start: var(--gap-md);
	color: var(--fg-2);
	font-weight: var(--fw-light);
	line-height: var(--lh-body);
}

.legal__prose li {
	margin-bottom: var(--gap-xs);
}

.legal__revised {
	margin-top: var(--gap-2xl);
	padding-top: var(--gap-md);
	border-top: 1px solid var(--line);
}

/* ===========================================================================
 * Gravity Forms
 * Stage 6. Design section 58 draws the enquiry form as a static mock; this is
 * the same form as real markup.
 *
 * The plugin's own stylesheet is switched off in its settings, so everything
 * a form needs is here. Fields themselves are already correct from the global
 * block in parent-overrides.css, so only the scaffolding, the states the
 * design never drew, and the one thing that breaks when the plugin CSS goes
 * are named below.
 * ======================================================================== */

/* The honeypot. Gravity Forms relies on its own stylesheet to hide this, so
   with plugin CSS off it renders as a visible "LinkedIn" field asking to be
   left unchanged. Hidden from sight AND from the accessibility tree: a screen
   reader user must never be offered a trap field. Not display:none, which
   some spam bots check for. */
.gform_validation_container,
.gform_wrapper .gform_validation_container {
	position: absolute !important;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.gform_fields {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--gap-lg); /* design 24px */
}

.gfield {
	display: flex;
	flex-direction: column;
	gap: var(--gap-xs); /* design 10px */
	min-width: 0;
}

.gfield_label {
	color: var(--kicker);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
}

.gfield_required {
	color: var(--kicker);
}

.gfield_description {
	color: var(--ph);
	font-size: var(--fs-xs);
	line-height: var(--lh-ui);
}

.ginput_container {
	display: flex;
	min-width: 0;
}

.ginput_container input,
.ginput_container select,
.ginput_container textarea {
	width: 100%;
	min-width: 0;
}

/* The design gives the message field a floor rather than a fixed height, so a
   long enquiry grows the field instead of scrolling inside it. */
.ginput_container textarea {
	min-height: 132px;
	resize: vertical;
}

/* Select needs the caret the native arrow no longer draws, same construction
   as the news index filters. */
.gfield--type-select .ginput_container {
	position: relative;
}

.gfield--type-select select {
	appearance: none;
	padding-inline-end: calc(var(--gap-xl) - var(--gap-2xs));
	cursor: pointer;
}

.gfield--type-select .ginput_container::after {
	content: "";
	position: absolute;
	inset-inline-end: var(--gap-md);
	top: 50%;
	width: 0;
	height: 0;
	margin-top: -2px;
	border-inline: 4px solid transparent;
	border-top: 5px solid currentColor;
	color: var(--fg);
	opacity: 0.7;
	pointer-events: none;
}

.gfield--type-select option {
	background: var(--bg);
	color: var(--fg);
}

/* Consent. The firm's data policy puts this form on Article 6(1)(a), consent,
   so the box is a real unticked checkbox: consent has to be a clear
   affirmative action, and a pre-ticked or absent box is not consent. */
.gfield--type-consent .ginput_container {
	align-items: flex-start;
	gap: var(--gap-xs);
}

.gfield--type-consent input[type="checkbox"] {
	flex: none;
	width: auto;
	margin-top: 3px;
}

.gfield_consent_label {
	color: var(--fg-2);
	font-size: var(--fs-sm);
	font-weight: var(--fw-light);
	line-height: var(--lh-ui);
	text-transform: none;
	letter-spacing: normal;
}

/* Footer ------------------------------------------------------------------ */
.gform_footer {
	margin-top: var(--gap-xs); /* design 8px */
}

/* The parent's .button is already on the submit, so this only carries the
   accent fill and the uppercase micro-label the design draws. */
.gform_button {
	background: var(--accent);
	border: 1px solid var(--accent);
	color: var(--accent-fg);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	letter-spacing: var(--ls-kicker);
	text-transform: uppercase;
}

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

/* Validation --------------------------------------------------------------
 *
 * The design draws no error state at all, so --error is an addition rather
 * than a design value, flagged as such in decisions.md. It is 8.53:1 on the
 * page ground, comfortably past AA, and the message carries text rather than
 * relying on colour alone, so it does not fail 1.4.1 either.
 */
.gform_validation_errors {
	margin-bottom: var(--gap-lg);
	padding: var(--gap-sm) var(--gap-md);
	border: 1px solid var(--error);
	border-radius: var(--radius-md);
	background: var(--error-wash);
	color: var(--fg);
}

.gform_submission_error {
	margin: 0 0 var(--gap-xs);
	font-family: var(--font-serif);
	font-size: var(--fs-h5);
}

.gform_validation_errors ol {
	margin: 0;
	padding-inline-start: var(--gap-md);
	font-size: var(--fs-sm);
}

.gfield_validation_message,
.validation_message {
	padding: var(--gap-2xs) 0 0;
	color: var(--error);
	font-size: var(--fs-sm);
	font-weight: var(--fw-light);
}

.gfield_error input,
.gfield_error select,
.gfield_error textarea {
	border-color: var(--error);
}

/* Confirmation, for the case where a form is ever set to show a message in
   place rather than redirect to the thank-you page. */
.gform_confirmation_message {
	max-width: var(--measure-intro);
	font-size: var(--fs-lg);
	font-weight: var(--fw-light);
}

/* ===========================================================================
 * Client review, 16/09/2026
 * Two measures the German copy overruns. Both were raised against the live
 * site; the English copy fits and is untouched.
 * ======================================================================== */

/* The German heading carries a deliberate line break after its first sentence.
   front-page.php prints this heading through esc_html(), so the break is a real
   newline in the ACF field rather than a <br>, and this rule is what renders it.
   pre-line still wraps normally, so narrow screens are unaffected.

   The measure is raised with it. At 22ch the break rendered three lines, not
   two: 'Die Beratung muss es nicht sein.' needs 606px at 46px and 22ch gives
   573px, so the sentence the break was meant to isolate wrapped again. 24ch is
   621px, which clears it and still sits inside the 632px grid column, so the
   two-column band is untouched. The English heading is shorter and fitted at
   either value. Below about 1440px the column itself is narrower than 621px and
   the second sentence wraps as any long line would. */
.home-about__heading {
	max-width: 24ch;
	white-space: pre-line;
}

/* Closing CTA band. 'UNSER VERSPRECHEN' overruns the label measure by a single
   pixel and 'Keine Überraschungen.' overruns the 16ch heading measure by about
   thirty, so both wrapped to two lines. max-content sizes each to its own text
   and never exceeds the space available, so they still wrap where the column is
   genuinely too narrow rather than overflowing. Desktop only, on the same
   breakpoint parent-overrides.css already uses; the design's measure stands on
   tablet and mobile. */
@media (min-width: 1024px) {

	.cta-band__kicker,
	.cta-band__heading {
		max-width: max-content;
	}
}

/* cta-band.php prints its heading through esc_html(), so a <br> would show as
   literal markup. Where the copy carries a deliberate break it is a real newline
   in the field, and this renders it. Headings without one are untouched and stay
   on a single line, which is what the measure rule above is for. */
.cta-band__heading {
	white-space: pre-line;
}

/* Same arrangement on the contact page blocks: page-contact.php escapes these
   with esc_html(), so an editor's deliberate break is a newline in the field. */
.contact__block-intro {
	white-space: pre-line;
}

/* reCAPTCHA attribution under the enquiry form. Google's terms require this
   sentence whenever the badge is hidden, so it cannot be shortened. It was
   inheriting the form's prose measure and wrapping to two lines at 585px when
   it needs 724px and the field itself is 824px wide, so the cap comes off and
   it sits on one line. It still wraps below about 780px, which is correct. */
.boh-recaptcha-notice {
	max-width: none;
}

/* CTA band columns were 854/769 either side of an 80px gap, which started the
   body at 1007 while the section above it starts its right column at 960. Equal
   columns over a 72px gutter reproduce the grid the rest of the page uses, so
   the CTA body lines up with whatever sits above it. Client asked for this on
   every CTA, so it is not scoped to one template. */
.cta-band__layout {
	gap: var(--gap-3xl);

	/* The component centres the two columns against each other, so a two-line
	   heading sits lower than the taller body beside it. Top alignment puts the
	   heading's first line level with the body's, which is what was asked for. */
	align-items: flex-start;
}

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

/* Footer award badge sat tight against the monogram, with all the slack behind
   it. Auto inline margins hand that slack to both sides instead, so the badge
   settles midway between the monogram and the Company column, as asked. No
   fixed offset, so it stays centred if the footer's proportions change.
   Desktop only: the row stacks below this and there is no slack to divide. */
@media (min-width: 1024px) {

	.site-footer__award {
		margin-inline: auto;
	}
}

/* Service detail row titles in the palette's warm neutral rather than plain
   white, to give the long left column some relief. Client request, 16/09/2026.
   --kicker is the only beige the palette carries; it reads 8:1 on the panel
   ground, so it clears AA comfortably at this size. Scoped to the row variant
   so the FAQ questions below, which share the base title class, stay white. */
.process-steps__title--row {
	color: var(--kicker);
}

/* Page hero titles carried the 20ch label measure, which is narrower than a
   single long German compound at display size, so the word before it was left
   alone on a line. --measure-title is 22ch, the smallest measure that lets the
   compound sit with its article. See tokens.css for the working. */
.page-hero__title {
	max-width: var(--measure-title);

	/* page-hero.php prints the title through esc_html(), so where an editor wants
	   a deliberate break it is a real newline in the title itself. pre-line renders
	   it here, and everywhere else the title is reused -- breadcrumb, document
	   title, cards -- the newline collapses to an ordinary space. */
	white-space: pre-line;
}

/* newsletter-signup__inner sets align-items:center, which is right for the
   first-party band on the news index and every article: the two columns are
   close in height there, so centring reads as deliberate. This page swaps our
   form for MailerLite's taller embed, and centring then drops the heading and
   its note well below the first field. Start-align the embed variant only, so
   the shared band keeps the alignment it was designed with. */
.newsletter-embed .newsletter-signup__inner {
	align-items: start;
}

/* Emphasis in article bodies, 16/09/2026. The base reset says
   'b, strong { font-weight: bolder }', and bolder is relative, not absolute: it
   steps up from the inherited weight. Article prose is set in --fw-light (300),
   where bolder resolves to 400, so every bold run the copy asked for rendered as
   ordinary text. An absolute weight fixes it. --fw-medium is the theme's own
   emphasis step, already used by .site-footer__credit strong, and Host Grotesk is
   a 100-900 variable face so 500 is a real weight rather than a synthesised one.
   Scoped to article prose: elsewhere the body is 400, bolder resolves to 700 and
   that is correct, so the reset is left alone. */
.article-body__prose b,
.article-body__prose strong {
	font-weight: var(--fw-medium);
}

/* Article prose runs its headings one rank below their semantic level: the
   post title is the h1, so a body h2 takes --fs-h3 and an h3 takes --fs-h4.
   Bye Bye Schriftform is the first article deep enough to need a fourth level
   (the four Maßnahmen sit under 'Konkrete Schritte für Eigentümer'), and without
   a rule it inherited the h3 size with no space above it, so the two levels
   read as one. --fs-h5 continues the same one-rank-down ladder, and the top
   margin sits between the h3's 40px and body rhythm. */
.article-body__prose h4 {
	margin: calc(var(--gap-xl) - var(--gap-xs)) 0 var(--gap-xs);
	font-size: var(--fs-h5);
}

/* LinkedIn in the footer's More column, client request 16/09/2026. The footer
   columns are nav menus, so this is an ordinary menu item: the URL can be
   changed under Appearance > Menus without a deploy, which matters here because
   the profile is blegal.biz for now and moves to Bohardien later.

   Shown as the mark rather than the word. The label stays in the DOM, so a
   screen reader still announces "LinkedIn"; text-indent pushes it out of the
   22px box. The glyph is a mask rather than an image so it inherits the link
   colour and the hover the list already has. */
.site-footer__list .site-footer__social {
	margin-top: var(--gap-xs);
}

.site-footer__list .site-footer__social > a {
	display: block;
	width: 22px;
	height: 22px;
	overflow: hidden;
	white-space: nowrap;
	text-indent: 100%;
	background-color: currentColor;
	-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zm1.78 13.02H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.73v20.54C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.73V1.73C24 .77 23.2 0 22.22 0z'/%3E%3C/svg%3E") no-repeat center / contain;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zm1.78 13.02H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.73v20.54C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.73V1.73C24 .77 23.2 0 22.22 0z'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* The diagonal arrow marks worded outbound links; on a logo it is noise. */
.site-footer__list .site-footer__social > a.is-external::after {
	content: none;
}

/* "What changed." as two fanned stacks of pages. Lisa 17/09/2026: "i want the
   case studie images to look like this with all 3 befores, and then in the block
   next to it the 3 afters in the same layout. So 2 containers - 50% each, each
   container has them stacked like that."

   This replaces the wipe slider that stood here, and the three-in-a-row rule
   that arranged it. The slider compared one page against its own rewrite, which
   is a narrower claim than the one the band is making: that the whole document
   got shorter. Three pages fanned beside three pages says that at a glance.

   The geometry is derived, not hand-tuned, so a case study carrying two or four
   pages still works. Each page is offset by a fraction of its own width and
   height, --stack-dx and --stack-dy, so n pages span (1 + (n-1)*dx) page widths
   and (1 + (n-1)*dy) page heights. Feeding that back into the container's
   aspect-ratio is what keeps the stack in flow: no fixed heights, and no
   absolutely positioned page escaping a parent that has collapsed to nothing.

   --stack-n and --page-ratio are written onto the element by the template from
   the real attachment dimensions, so an editor who uploads landscape scans gets
   a correctly shaped stack rather than a squashed one.

   The static aspect-ratio below the calc is a fallback, not a duplicate: it is
   the value the calc resolves to for three A4 pages, so a browser that cannot
   parse the calc gets a stack of very nearly the right height instead of one
   with no height at all. */
.case-visuals {
	display: grid;
	gap: var(--gap-2xl);
	margin-top: var(--gap-xl);
}

@media (min-width: 900px) {

	.case-visuals {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		align-items: start;
	}
}

.case-visuals__side {
	margin: 0;
	/* So a stack contributes its shrunk width to the track rather than its
	   min-content width, the pattern the other two-track bands here use. */
	min-width: 0;
}

.case-visuals__label {
	display: block;
	margin-bottom: var(--gap-sm);
}

.case-stack {
	--stack-n: 3;
	--page-ratio: 794 / 1123;
	/* Enough offset that each page reads as a separate sheet, little enough
	   that the top-left corner of every one still shows its own heading. */
	--stack-dx: 0.42;
	--stack-dy: 0.3;
	--stack-spanx: calc(1 + (var(--stack-n) - 1) * var(--stack-dx));
	--stack-spany: calc(1 + (var(--stack-n) - 1) * var(--stack-dy));
	position: relative;
	aspect-ratio: 4 / 5;
	aspect-ratio: calc(var(--page-ratio) * var(--stack-spanx) / var(--stack-spany));
}

.case-stack__page {
	position: absolute;
	width: calc(100% / var(--stack-spanx));
	left: calc(var(--i, 0) * var(--stack-dx) * 100% / var(--stack-spanx));
	top: calc(var(--i, 0) * var(--stack-dy) * 100% / var(--stack-spany));
	/* Later pages sit on top, which is how a fanned pile reads. Source order
	   already does this; stating it stops a future position:relative on a
	   sibling quietly reversing the pile. */
	z-index: var(--i, 0);
	/* These are documents, so the sheet is white whatever the band behind it
	   does, and the shadow is what lifts it off a dark ground. */
	background: #fff;
	border: 1px solid var(--line);
	box-shadow: 0 18px 44px rgb(0 0 0 / 0.45);
}

.case-stack__img {
	display: block;
	width: 100%;
	height: auto;
}

/* Case study detail bands, Adrian 16/09/2026, from Lisa's "Structure" note on
   the client and brief band.

   These were two near-equal columns, which only works if they carry near-equal
   copy. Measured across all eight German case studies, the right column runs
   2.5x to 7x longer than the left, averaging about four times, so the left
   always ran out first and left a void beneath it: 254px of content against
   606px on the page that was flagged.

   Narrowing the left column was tried first and roughly halved the gap, but a
   gap that size cannot be closed by column widths alone, because the right
   column's paragraphs are already capped at their reading measure and so do not
   shorten when the column widens. Adrian's call was to stack them instead, which
   removes the problem at the source: with one column per row there is no second
   column to run short against, whatever the copy lengths do later.

   The prose keeps its own measure cap, so full width here means the blocks span
   the container while the text still wraps at a comfortable line length.

   Below 900px this already stacked, so that path is unchanged and the rule only
   has to bring desktop into line with it. */
@media (min-width: 900px) {

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

/* Stretch the prose to the band, Adrian 16/09/2026. Once the blocks stacked,
   the 70ch cap left the text ending well short of the right edge with a column
   of empty space beside it, which is what the mark on the screenshot points at.
   Removing the cap lets the paragraphs run the full width of the band so they
   line up with the headings above them. Scoped to the stacked layout: below
   900px the container is narrower than 70ch anyway, so the cap never bound
   there and nothing changes. */
@media (min-width: 900px) {

	.case-detail__prose,
	.case-detail__prose p {
		max-width: none;
	}
}

/* "Get in touch" moves into the utility bar on phones, Lisa 16/09/2026: "I
   think the header takes up too much space. Shall we put the get in touch into
   the top row (with Search, Online Shop, etc)?"

   The pill is 148px with its arrow, and beside the 118px wordmark and the 44px
   burger it needs 342px of the 335px available on a 375px screen. Missing by
   seven pixels is enough to wrap it onto a row of its own, which is what pushed
   the chrome above the content to 236px, 29% of the phone.

   The link itself is added by the utility-bar-cta.php sandbox file, on the
   theme's own bohardien_utility_bar_items hook. It is hidden by default and
   only revealed at the width where the pill is hidden, so the two never appear
   together. 599px is the phone breakpoint custom.css already uses.

   Above this width nothing changes: the pill has room and reads as the primary
   action, which a utility link would not. */
.utility-bar__link--cta {
	display: none;
}

@media (max-width: 599px) {

	.utility-bar__link--cta {
		display: inline-flex;
	}

	.header-cta {
		display: none;
	}
}
/* Services as a real disclosure in the mobile drawer, Adrian 16/09/2026:
   "Lets add a drop down indicator to that services".

   The drawer printed all five children inline, because there is no hover on
   touch, which cost 247px and pushed About us and News to 468px and 522px on a
   375px phone. Hiding them shortened the menu but left a dead end, so this
   gives the row the control the caret implies: tap to open, tap to close.

   The submenu is hidden only under body.has-submenu-toggles, which the script
   sets after it has actually built a button. Without JavaScript the children
   stay listed inline exactly as before, rather than disappearing behind a
   control that never loads.

   Flex on the row rather than absolute positioning: the caret centres itself
   against whatever the link height turns out to be, and the submenu wraps onto
   its own line, with no offsets to go stale. */
.submenu-toggle {
	display: none;
}

@media (max-width: 1023px) {

	.main-navigation .nav-menu > .menu-item-has-children {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
	}

	.main-navigation .nav-menu > .menu-item-has-children > a {
		flex: 1 1 auto;
	}

	.main-navigation .nav-menu > .menu-item-has-children > ul {
		flex: 0 0 100%;
	}

	.submenu-toggle {
		flex: none;
		display: grid;
		place-items: center;
		/* 44px, the smallest target that is comfortable under a thumb. */
		width: 44px;
		height: 44px;
		padding: 0;
		border: 0;
		background: none;
		color: inherit;
		cursor: pointer;
	}

	/* The caret the desktop dropdown already uses, so both read as one idea. */
	.submenu-toggle::after {
		content: "";
		width: 9px;
		height: 9px;
		border-right: 1.5px solid currentColor;
		border-bottom: 1.5px solid currentColor;
		transform: translateY(-2px) rotate(45deg);
		opacity: 0.75;
		transition: transform var(--dur) var(--ease);
	}

	.main-navigation .menu-item-has-children.is-submenu-open > .submenu-toggle::after {
		transform: translateY(2px) rotate(225deg);
	}

	body.has-submenu-toggles .main-navigation .nav-menu > .menu-item-has-children > ul {
		display: none;
	}

	body.has-submenu-toggles .main-navigation .nav-menu > .menu-item-has-children.is-submenu-open > ul {
		display: block;
	}
}

/* Our expertise rows: the numeral and the practice-area name. Lisa 17/09/2026,
   from a phone: "reduce the spacing between number and heading and align the
   size of the fonts (make the heading as big as the number)".

   Two separate causes sat behind the gap she boxed. The head is a flex row
   whose column-gap is --gap-2xl, 56px, and the numeral also carries a 72px
   min-width so that every title starts at the same x. Two tabular digits at
   40px measure about 44px, so 28px of that column stood empty before the 56px
   gap even began: 84px of air between "01" and the title, inside a row only
   340px wide.

   The sizes were the design's own. The numeral is a static 40px while the
   title is clamp(28px, 3vw, 42px), which does not reach 40px until a 1333px
   viewport. Below that the label was larger than the heading it labels, worst
   on a phone where the title sits at its 28px floor against a 40px numeral.

   Both are corrected at the source rather than per breakpoint. The title's
   floor is raised to the numeral's size and the numeral is then tied to the
   title, so the two track each other at every width instead of crossing over
   at 1333px: 40px together below that point, 42px together above it. The
   component's font-variant-numeric: tabular-nums still gives every row the
   same title start once the fixed column is gone, which is what that column
   was buying.

   This moves desktop as well, deliberately. The same 84px gap and the same
   40/42 mismatch were present at every width, and holding one relationship on
   a phone and another on a laptop would only invite the note a second time. At
   1440px the title now starts 61px further left and all four names still sit
   on a single line. */
.expertise-card {
	--expertise-card-title: clamp(40px, 3vw, 42px);
	--expertise-card-numeral: var(--expertise-card-title);

	/* The numeral sizes its own box now; tabular digits keep the titles aligned. */
	--expertise-card-numeral-col: auto;
}

.expertise-card__head {
	column-gap: var(--gap-md);

	/* Lisa, 17/09/2026, on the row above: "see how these are missaligned here".
	   The component aligns this row flex-start and gives the numeral
	   line-height 0.9, which was tuned to sit its cap-height against a title
	   that was then the smaller of the two. Now both are the same size and the
	   same face, so flex-start lines up the two line boxes rather than the type
	   inside them, and their half-leading differs: 40px glyphs in a 36px box
	   against the same glyphs in a 43.2px one. Measured, that left the numeral
	   3px high on a phone and 4px high at 1440.

	   Aligning the baselines aligns the type itself, so the numeral sits on the
	   title's first line whatever either line-height does later. */
	align-items: baseline;
}