/* Containers, sections, grids, header, footer */

.container {
	max-width: var(--container-max);
	margin-inline: auto;
	padding-inline: var(--container-pad);
}

.section { padding-block: var(--section-pad); }

/* Grained on the big flat bands only (here, .trust-strip, .pricing-includes,
   .related-posts). The tile *is* --color-tint plus grain, so the colour behind
   it is a fallback rather than a blend, and the chips/badges that share the
   token are left flat — a 512px tile inside a 2rem pill shows nothing and only
   costs paint. */
.section--tint {
	background: var(--surface-grain-light) repeat, var(--color-tint);
}

.section--dark {
	background: var(--gradient-dark);
	color: var(--color-on-dark);
}

/* White, underlined — not the accent. A link is body copy and answers to 4.5:1,
   and no value in the red family reaches that against the #C41C1C end of this
   band. The underline is not decoration: with the link now the same colour as
   the text around it, it is the only thing distinguishing the two (1.4.1). */
.section--dark a {
	color: var(--color-link-on-dark);
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

/* Buttons opt back out: they are not runs of body text, they carry their own
   fill and border, and an underlined button label reads as a mistake. */
.section--dark a.btn { text-decoration: none; }

/* Decorative dot pattern overlay for dark sections */
.section--dots { position: relative; overflow: hidden; }

.section--dots::before {
	content: "";
	position: absolute;
	inset: 0;
	background-image: radial-gradient(circle at 2px 2px, rgba(255, 255, 255, 1) 1px, transparent 0);
	background-size: 40px 40px;
	opacity: 0.1;
	pointer-events: none;
}

.section--dots > * { position: relative; }

/* ===== Torn section seams =====
   A dark<->light section flip used to land as a ruled line. These draw a strip
   of the boundary's *light* colour masked by a hand-torn edge
   (assets/img/textures/seam-torn.svg, tileable with repeat-x) so the flip reads
   as one surface having been ripped off the other — on-brand with the sketch
   language rather than a gradient fade.

   Three placements exist because the strip is a flat fill and it has to meet
   its own side of the boundary invisibly. Every light surface in this theme is
   a solid colour and every dark one is a gradient, so the flat fill always goes
   on the light side and the torn edge always faces into the dark:

     .section--seam        strip inside this section's TOP edge, painted
                           --seam-from (the previous section's colour), torn
                           along its bottom. Needs a solid-coloured predecessor.
     .section--seam-above  strip just ABOVE this section's top edge, painted
                           --seam-to (this section's own colour), torn along its
                           top. For a gradient predecessor, where no flat fill
                           could meet the boundary without showing a line.
     .section--seam-below  the same up-facing strip, hosted on the PRECEDING
                           section's bottom edge — used where the element after
                           the boundary is sticky or overlaps upward and so
                           cannot carry it.

   The concrete selectors alongside each class are the real boundaries on this
   site; no template markup carries these classes yet, and the sequence they
   encode is the one documented per page below.

   Decorative only: absolutely positioned so nothing reflows, pointer-events
   none, and an empty pseudo-element is not exposed to assistive tech. */
.section--seam,
.section--seam-above,
.section--seam-below,
.trust-strip + .section--tint,
.solution + .features-section,
.blog-cta,
.benefits-cta,
.benefits-hero,
.site-footer,
.site-footer__brand-band { position: relative; }

.section--seam::before,
.section--seam-above::before,
.section--seam-below::after,
.trust-strip + .section--tint::before,
.solution::after,
.solution + .features-section::before,
.testimonials-section + .demo-section::before,
.blog-cta::before,
.benefits-cta::before,
.benefits-hero::after,
.contact-page .page-hero--dark::after,
.legal-page .page-hero--dark::after,
.blog-index__hero::after,
.blog-archive__hero::after,
.blog-single__hero::before,
.demo-page__form::after,
.site-footer::before,
.site-footer__brand-band::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	height: var(--seam-height);
	pointer-events: none;
	-webkit-mask-image: url("../img/textures/seam-torn.svg");
	mask-image: url("../img/textures/seam-torn.svg");
	-webkit-mask-repeat: repeat-x;
	mask-repeat: repeat-x;
	-webkit-mask-size: var(--seam-tile) 100%;
	mask-size: var(--seam-tile) 100%;
	-webkit-mask-position: var(--seam-shift, 0) top;
	mask-position: var(--seam-shift, 0) top;
}

/* Down-facing tear: the previous section's colour sits across this section's
   top edge and is torn away toward the content. */
.section--seam::before,
.trust-strip + .section--tint::before,
.solution::after,
.testimonials-section + .demo-section::before,
.blog-cta::before,
.benefits-cta::before {
	top: 0;
	background: var(--seam-from, transparent);
}

/* Up-facing tear, overhanging into the previous section. scaleY(-1) reuses the
   single asset for the mirrored edge — the flip is applied after masking, and a
   different --seam-shift keeps it from reading as a copy of the down-facing
   seams on the same page. */
.section--seam-above::before,
.solution + .features-section::before {
	top: calc(-1 * var(--seam-height));
	background: var(--seam-to, transparent);
	transform: scaleY(-1);
	--seam-shift: -213px;
}

/* The footer's brand band joined this group when it went light: the band is the
   light side and the closing band above it is a gradient, so no flat fill could
   meet the boundary from above. Its own --seam-shift keeps the tear from reading
   as a copy of the one at solution -> features on the same page. */
.site-footer::before {
	top: calc(-1 * var(--seam-height));
	background: var(--seam-to, transparent);
	transform: scaleY(-1);
	--seam-shift: -97px;
}

/* The light band's own bottom edge is a light-to-dark flip inside the footer, so
   it takes a tear like every other one rather than the ruled line it would be
   otherwise. Down-facing and unconditional: unlike the boundary above, this one
   exists on every page, and the band's flat tint can meet it from above. A third
   --seam-shift so the two tears the footer carries never line up. */
.site-footer__brand-band::after {
	bottom: calc(-1 * var(--seam-height));
	background: var(--surface-grain-light) repeat, var(--color-tint);
	--seam-shift: -331px;
}

/* Same up-facing tear, hosted on the preceding section's own bottom edge. */
.section--seam-below::after,
.benefits-hero::after,
.contact-page .page-hero--dark::after,
.legal-page .page-hero--dark::after,
.blog-index__hero::after,
.blog-archive__hero::after,
.blog-single__hero::before,
.demo-page__form::after {
	bottom: 0;
	background: var(--seam-to, transparent);
	transform: scaleY(-1);
	--seam-shift: -419px;
}

/* --- Boundary colours ---------------------------------------------------- */

/* Home: hero(dark) trust-strip(dark) | problem(tint) | solution(dark) |
   features(white) benefits(tint->white) comparison(white)
   integrations(white->tint) testimonials(tint->white) | demo(dark) footer(dark).
   Only the four flips marked | are seamed; the rest already chain colours. */
.trust-strip + .section--tint { --seam-from: var(--color-dark); }
.solution { --seam-from: var(--color-tint); }
.solution + .features-section { --seam-to: var(--color-white); }
.testimonials-section + .demo-section { --seam-from: var(--color-white); }

/* Dark heroes into light content: /contact/, /blog/, category archives, legal,
   single posts, and /benefits/ (whose hero hands off to the sticky chip bar, so
   the seam has to live on the hero). /demo/ is absent on purpose — its hero runs
   straight into a second dark band, so that boundary is dark-to-dark. */
.contact-page .page-hero--dark,
.legal-page .page-hero--dark,
.blog-index__hero,
.blog-archive__hero,
.blog-single__hero,
.benefits-hero { --seam-to: var(--color-white); }

/* /demo/: the dark form band closes on main's white->tint wash, which is within
   2/255 of the tint token by the time it reaches that boundary. */
.demo-page__form { --seam-to: var(--color-tint); }

/* The dark CTA bands that close /features/, /blog/, single posts, /benefits/. */
.blog-cta,
.benefits-cta { --seam-from: var(--color-white); }

.section--tint + .blog-cta,
.related-posts + .blog-cta,
.section--tint + .benefits-cta { --seam-from: var(--color-tint); }

/* Footer. Its brand band is light on every page, so it needs a seam only where
   the page's closing band is DARK — the inverse of when the whole footer was
   dark. Because the footer is a sibling of <main>, no combinator can reach that
   last section (:has() would, and is unused here), so the boundary is declared
   per <main> class. Still opt-in: the transparent fallback in var(--seam-to)
   means an unlisted template paints nothing rather than a light strip in the
   wrong place. These are every template that closes on a dark CTA band;
   the ones that close on a white or tint wash chain into the tint band
   directly and take no seam. Keep this list in step with whatever section each
   template ends on: a template listed here whose closing band is not dark
   would paint a torn tint strip over a light section, with no boundary to
   mask. */
.front-page + .site-footer,
.benefits-page + .site-footer,
.company-page + .site-footer,
.resources-archive + .site-footer,
.resource-single + .site-footer,
.blog-index + .site-footer,
.blog-single + .site-footer { --seam-to: var(--color-tint); }

/* The photo hero paints a wash in ::after and its copy sits at z-index 2, so
   the seam has to opt out of DOM paint order to stay on top. Its 4rem of
   bottom padding keeps it clear of the headline. */
.blog-single__hero::before { z-index: 3; }

/* /contact/ below 64rem: the channel cards only lift 2rem into the hero there
   rather than 4.5rem (pages.css), so all that survives of a 2.5rem seam is a
   sliver in each side gutter — noise rather than a readable torn edge. The
   cards overlapping the boundary already do the job at that width. */
@media (max-width: 63.99rem) {
	.contact-page .page-hero--dark::after { content: none; }
}

/* Grids */
.grid { display: grid; gap: 1.5rem; }

@media (min-width: 40rem) {
	.grid--2, .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 64rem) {
	.grid--3 { grid-template-columns: repeat(3, 1fr); }
	.grid--4 { grid-template-columns: repeat(4, 1fr); }
	.grid { gap: 2rem; }
}

/* Integration cards carry only an icon, a short name and a category, so two
   fit per row on a phone where .grid--4's single column produced ~2,900px of
   repetitive scrolling. Scoped to .integration-grid so the taller feature
   cards that also use .grid--4 keep stacking one-up. */
@media (max-width: 39.99rem) {
	.integration-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 0.75rem;
	}

	/* Two columns leave ~135px per card at 320px — trim the card's own inset
	   so the longest label ("Communication") still sets without breaking. */
	.integration-grid .integration-card { padding: 1.25rem 0.6rem; }
}

/* Section headers */
.section-header {
	max-width: 48rem;
	margin-inline: auto;
	margin-bottom: 3rem;
	text-align: center;
}

/* V2 stamp chip — white on light sections, tint on dark sections. Both fills are
   light on purpose: the brand mark is navy/red with no white-ink version, so a
   near-black chip could only show it on a little white plate, which read as a
   sticker rather than part of the chip. A white fill with the standard hairline
   border and red under-shadow is the same stamp language as the cards, and the
   mark sits directly on it. */
.section-header__eyebrow {
	display: inline-flex;
	align-items: center;
	gap: 0.5em;
	padding: 0.375rem 1rem;
	border: 2px solid rgba(0, 0, 0, 0.10);
	border-radius: var(--radius-sm);
	background: var(--color-white);
	color: var(--color-dark);
	font-family: var(--font-heading);
	font-size: var(--text-base);
	font-weight: 700;
	letter-spacing: 0.03em;
	margin-bottom: 1rem;
	box-shadow: var(--shadow-stamp-chip);
}

/* Brand hexagon mark inside every eyebrow chip (per V2 design). One rule for
   both chip variants now that both fills are light — the mark reads directly on
   either, so there is no per-surface override and no plate. */
.section-header__eyebrow::before,
.eyebrow::before {
	content: "";
	flex-shrink: 0;
	width: 1em;
	height: 1.15em;
	background: url("../img/logo-mark-color.webp") center / contain no-repeat;
}

.section-header--dark .section-header__eyebrow {
	border-color: rgba(0, 0, 0, 0.10);
	background: var(--color-tint);
	color: var(--color-primary);
}

.section-header__title { font-size: var(--text-4xl); }

/* V2 titles break before the red segment (matches Figma line breaks).
   .text-inline opts a span out (single-line titles like testimonials). */
.section-header__title .text-gradient { display: block; }
.section-header__title .text-inline { display: inline; }

.section-header__lede {
	margin-top: 1rem;
	font-size: var(--text-lg);
	color: var(--color-body-muted);
}

.section-header--dark .section-header__lede { color: var(--color-on-dark-muted); }

/* ===== Site header — light chrome so the sketch logo's navy ink reads ===== */
.site-header {
	position: fixed;
	inset-inline: 0;
	top: 0;
	z-index: 100;
	background: rgba(255, 255, 255, 0.92);
	border-bottom: 1px solid rgba(0, 0, 0, 0.08);
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
	transition: background-color var(--transition), box-shadow var(--transition);
}

.site-header.is-scrolled {
	background: var(--color-white);
	/* Hard offset, not a 20px blur — the scrolled header was the last blurred
	   shadow in the theme after the sub-nav rail gave up its own. */
	box-shadow: 0 3px 0 rgba(26, 13, 13, 0.10);
	/* The background above is fully opaque, so the blur declared on .site-header
	   can no longer produce a visible result — but the compositor keeps sampling
	   and blurring the backdrop behind it on every scrolled frame regardless.
	   Dropped here rather than on .site-header, because the unscrolled bar IS
	   translucent (0.92) and does need it. */
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
}

/* Editor-only: clear the 32px admin bar, which is fixed and adds
   html{margin-top:32px}. Without this the fixed header slides under it, and any
   page whose top offset is outer rather than internal overshoots by the same
   32px and opens a strip of background beneath the header. Never seen by a
   visitor.

   Scoped to >=783px on purpose: below that the bar becomes absolute at 46px and
   scrolls away, so a permanent offset would leave a gap once the page moves. */
@media screen and (min-width: 783px) {
	/* One token, not one offset per element. Anything that has to clear the fixed
	   header reads --chrome-top, so setting it here moves the header AND the
	   sticky sub-nav rail, the sticky legal sidebar and every anchor's
	   scroll-margin in step. Before this, only the header knew about the bar and
	   the rail stuck 32px too high — straight behind the header. */
	body.admin-bar { --chrome-top: 32px; }
	body.admin-bar .site-header { top: var(--chrome-top); }
	/* The skip link is fixed near the top too, so it lands behind the bar when
	   focused — which defeats the one control that has to be reachable. */
	body.admin-bar .skip-link:focus { top: 2.5rem; }
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	height: var(--nav-h);
}

.site-header__logo img { height: 3.5rem; width: auto; }

/* Below 48rem the bar drops to 64px: on /benefits/ the 80px header plus the
   sticky chip bar was eating ~123px of an 812px phone viewport. The token is
   overridden (rather than just .site-header's height) so the sticky sub-nav
   offset, [id] scroll-margin and .site-main padding all follow in step. */
@media (max-width: 47.99rem) {
	:root { --nav-h: 4rem; }

	/* 44px tall keeps the 413x112 master's aspect ratio and clears the bar. */
	.site-header__logo img { height: 2.75rem; }
}

.site-nav { display: none; }

@media (min-width: 48rem) {
	/* flex:1 rather than intrinsic width. The nav has to span the gap between the
	   logo and the right edge so the CTA can be pinned to that edge — with the
	   nav shrink-to-fit, `space-between` on the header parked the whole group
	   mid-bar and the CTA floated somewhere in the middle. */
	.site-nav {
		display: flex;
		flex: 1;
		align-items: center;
		gap: 2rem;
	}

	.site-header__toggle { display: none; }
}

.site-nav__list {
	display: flex;
	align-items: center;
	gap: 1.75rem;
	width: 100%;
}

/* Push the demo CTA to the right-hand edge of the bar. Keyed on the item that
   CONTAINS the CTA link rather than on its position: menu order is editable, so
   :last-child would silently push the wrong item the day it changes. The .site-nav__cta
   class is added server-side. :has() is already load-bearing for the dropdowns below. */
.site-nav__list > li:has(> a.site-nav__cta) { margin-left: auto; }

.site-nav__list a {
	color: var(--color-dark);
	text-decoration: none;
	font-weight: 600;
	transition: color var(--transition);
}

.site-nav__list a:hover { color: var(--color-primary); }
.site-nav__list a.site-nav__cta:hover { color: var(--color-white); }

/* current page — #C41C1C on white = 5.92:1 ✓ */
.site-nav__list .current-menu-item a,
.site-nav__list .current_page_item a {
	color: var(--color-primary);
}

.site-nav__list a.site-nav__cta,
.site-nav__list .current-menu-item a.site-nav__cta,
.site-nav__list .current_page_item a.site-nav__cta {
	color: var(--color-white);
	/* The generic .site-nav__list a rule above is (0,1,1) and replaces .btn's
	   whole transition list with `color` alone. The CTA has carried .btn since
	   the first scaffold commit, so its translateY(-2px) hover lift and its
	   5px->7px shadow step have always SNAPPED while every other button eases
	   over 200ms — a site-wide scan found it was the ONLY control on any page
	   missing a transform/box-shadow transition. Restored here, where the
	   selector already outranks the generic rule. */
	transition: transform var(--transition), box-shadow var(--transition), background-color var(--transition), color var(--transition), border-color var(--transition);
}

/* The transition list above is (0,2,1) and so outranks .btn:active's 80ms
   press override at (0,2,0), which would leave this button pressing at the
   200ms hover pace instead of snapping down in 80ms. Any selector that
   re-declares .btn's transition owes the press its speed back. */
.site-nav__list a.site-nav__cta:active {
	transition-duration: var(--transition-press);
}

/* The nav CTA is the same Structured Stamp button as every other CTA — it used
   to be a gradient pill with no stamp shadow, which made the header the one
   place the button language broke. Only the scale is nav-specific: it has to
   fit a 64-80px bar, so it sits a step below the in-page buttons. */
.site-nav__cta,
.site-nav__list a.site-nav__cta {
	padding: 0.5rem 1.1rem;
	gap: 0.5rem;
	font-size: var(--text-sm);
}

.site-nav__cta .sketch-icon { width: 17px; height: 17px; }

.site-header__toggle {
	display: none;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	background: transparent;
	border: 0;
	color: var(--color-dark);
	cursor: pointer;
	border-radius: var(--radius);
}

.js .site-header__toggle { display: inline-flex; }

.site-header__toggle .icon,
.site-header__toggle .sketch-icon { width: 1.75rem; height: 1.75rem; }

.site-header__toggle .icon-close { display: none; }

.site-header__toggle[aria-expanded="true"] .icon-open { display: none; }

.site-header__toggle[aria-expanded="true"] .icon-close { display: block; }

.site-header__mobile {
	background: var(--color-white);
	border-top: 1px solid rgba(0, 0, 0, 0.08);
	padding: 1rem var(--container-pad) 1.5rem;
}

/* With JS disabled the mobile links remain visible and usable. */
.js .site-header__mobile { display: none; }

/* Open, the panel becomes a full-height sheet down to the bottom edge of the
   viewport. There is no separate scrim element, so without this the page kept
   showing through below the panel — putting the hero's red CTA a few dozen
   pixels under the panel's identical red CTA. Long menus scroll inside it. */
.js .site-header__mobile.is-open {
	display: block;
	height: calc(100vh - var(--nav-h));
	height: calc(100dvh - var(--nav-h));
	overflow-y: auto;
	overscroll-behavior: contain;
	-webkit-overflow-scrolling: touch;
}

@media (min-width: 48rem) {
	.site-header__mobile { display: none; }
	.js .site-header__toggle { display: none; }
}

.site-header__mobile-list { display: flex; flex-direction: column; }

.site-header__mobile-list a {
	display: block;
	padding: 0.875rem 0.5rem;
	color: var(--color-dark);
	text-decoration: none;
	font-weight: 600;
	border-radius: var(--radius);
}

.site-header__mobile-list a:hover { color: var(--color-primary); }
.site-header__mobile-list a.site-nav__cta:hover { color: var(--color-white); }

/* Resting-state twin of the :hover rule above — the half that never got
   backported. The generic `.site-header__mobile-list a` block is (0,1,1) and so
   outranks every single-class .btn rule in base.css, which left the CTA as a
   BLOCK element: the icon took its own line (base.css makes img display:block),
   the label rendered in --color-dark on the red fill at 3.19:1, and .btn__divider
   silently collapsed to 0px because width and align-self are both inert on a
   non-replaced inline box. The desktop list has had its equivalent override
   since day one at .site-nav__list a.site-nav__cta. (0,3,1) wins this back.
   0.6rem of block padding rather than the desktop 0.5rem: it takes the tap
   target to 45.6px, over the 44px minimum, which matters here and not there. */
.site-header__mobile-list a.site-nav__cta {
	display: inline-flex;
	padding: 0.6rem 1.1rem;
	color: var(--color-white);
}

/* Rows, not a loose stack. A hairline between items gives the list an edge to
   read against — the same rgba(0,0,0,0.10) hairline the chips and cards use —
   and the panel closes to its content instead of leaving most of itself empty. */
.site-header__mobile-list li + li { border-top: 1px solid var(--color-border); }

.site-header__mobile-list a:not(.site-nav__cta) {
	display: flex;
	align-items: center;
	min-height: 2.75rem;
	border-radius: 0;
}

.site-header__mobile-list a[aria-current] { color: var(--color-primary); }

.site-header__mobile .btn { margin-top: 1rem; width: 100%; }

/* ===== Dropdowns =====
   One level only, in both navs: a real link to the parent's own page plus a
   sibling <button> that discloses the panel.

   The no-JS contract is the same one the mobile sheet uses: submenus are VISIBLE
   by default and only hidden under `.js`, so with scripting off every sub-item is
   reachable and the disclosure — which can do nothing without JS — is hidden with
   it. Everything below that hides a panel is therefore scoped to `.js`. */

/* The chevron is DECORATION inside the parent's link, not a control — the link
   navigates and the panel opens on hover/focus. Nothing here is clickable in its
   own right, which is why there is no button, no hit target and no cursor rule. */
.site-nav__chevron {
	width: 0.7rem;
	height: 0.7rem;
	margin-left: 0.3rem;
	transition: rotate var(--transition);
}

/* rotate, not transform: the cards in this theme animate `transform` on hover and
   `translate` on reveal precisely so the two compose instead of overwriting each
   other. A chevron that used transform would be the one glyph that cannot be
   given a lift later without a fight. */
.site-nav__list > li:hover > a > .site-nav__chevron,
.site-nav__list > li:focus-within > a > .site-nav__chevron { rotate: 180deg; }

/* ---- Desktop panel ---- */
@media (min-width: 48rem) {
	.site-nav__list > li:has(> .site-nav__submenu) { position: relative; }

	.site-nav__list > li > a { display: inline-flex; align-items: center; }

	/* The panel escapes the bar without new z-index work: .site-header is
	   position:fixed at z-index 100 and neither it nor .site-header__inner sets
	   overflow. Its backdrop-filter does create a stacking context, so this
	   z-index is scoped inside the header — which is all it needs to be. */
	.site-nav__submenu {
		position: absolute;
		top: calc(100% + 0.9rem);
		left: -1rem;
		z-index: 1;
		display: flex;
		flex-direction: column;
		min-width: 13rem;
		padding: 0.5rem;
		border: 2px solid rgba(0, 0, 0, 0.10);
		border-radius: var(--radius-lg);
		background: var(--color-white);
		/* Two-axis offset: this is an object lying on the page, not a control
		   standing up off it. Same 6px tier as a card. */
		box-shadow: var(--shadow-stamp);
	}

	/* ---- Grace period ----
	   visibility + opacity rather than display, because display cannot be
	   transitioned and this panel needs to survive the pointer leaving it.

	   Closing waits --nav-panel-grace before it starts, and the panel stays
	   interactive for the whole of that wait — so a diagonal move from the parent
	   toward an item, or a hand that drifts off the edge of a row, does not drop
	   the menu. Opening has no delay at all.

	   The delays are deliberately not durations: the blanket reduced-motion rule
	   in base.css overrides transition-DURATION only, so under reduced motion the
	   fade disappears and the grace period survives — which is right, because the
	   wait is a usability affordance rather than an animation. */
	.site-nav__submenu {
		visibility: hidden;
		opacity: 0;
		transition:
			opacity var(--nav-panel-fade) ease-out var(--nav-panel-grace),
			visibility 0s linear calc(var(--nav-panel-grace) + var(--nav-panel-fade));
	}

	/* Hover-forgiveness bridge. The panel is offset 0.9rem below the button, and
	   that gap belongs to neither element — so a pointer travelling from the
	   button to the panel left the <li> and the menu closed under the cursor.
	   This fills the gap with a transparent strip that IS inside the panel, so
	   the pointer never leaves. It only exists while the panel is displayed,
	   which is exactly when it is needed. */
	.site-nav__submenu::before {
		content: "";
		position: absolute;
		inset-inline: 0;
		bottom: 100%;
		height: 0.9rem;
	}

	/* Opened by hover, or by focus landing anywhere inside the group. Pure CSS on
	   purpose: it works with scripting off, it cannot get out of step with the
	   pointer, and :focus-within is what makes the panel keyboard-reachable — tab
	   to the parent and it opens, tab again and you are inside it. main.js mirrors
	   the same state into aria-expanded for assistive technology and adds Escape;
	   it does not drive visibility.

	   Opening cancels the closing delay by re-declaring the transition with no
	   delay, so returning to a panel that was mid-grace-period is instant. */
	.site-nav__list > li:hover > .site-nav__submenu,
	.site-nav__list > li:focus-within > .site-nav__submenu {
		visibility: visible;
		opacity: 1;
		transition: opacity var(--nav-panel-fade) ease-out 0s, visibility 0s linear 0s;
	}

	/* Escape. While focus stays inside the group :focus-within holds the panel
	   open, so a keyboard "close" has to override the open rule — main.js puts
	   this class on the <li> when Escape is pressed and strips it on any fresh
	   approach. Declared after the open rule at equal specificity so it wins on
	   source order, and transition:none because a deliberate dismissal should
	   not linger through the grace period built for pointer near-misses. */
	.site-nav__list > li.nav-dropdown-suppressed > .site-nav__submenu {
		visibility: hidden;
		opacity: 0;
		transition: none;
	}

	.site-nav__list > li.nav-dropdown-suppressed > a > .site-nav__chevron { rotate: none; }

	.site-nav__submenu a {
		display: block;
		padding: 0.6rem 0.75rem;
		border-radius: var(--radius-sm);
		font-size: var(--text-sm);
		white-space: nowrap;
	}

	.site-nav__submenu a:hover,
	.site-nav__submenu a:focus-visible { background: var(--color-tint); color: var(--color-primary); }
}

/* ---- Mobile accordion ---- */
/* The panel is already height:calc(100dvh - var(--nav-h)) with overflow-y:auto
   and overscroll-behavior:contain, so an expanded group scrolls inside the sheet
   rather than the page. Nothing here needs to manage that. */
/* Always open. There is no hover on a touch screen and a tap on the parent has to
   navigate, so a collapsed panel here would have no gesture left to open it. The
   sheet is full-height and scrolls, so the room is free — and this removes the
   accordion, its toggle and the whole expanded/collapsed state from mobile. */
.site-header__mobile__submenu {
	padding-left: 0.75rem;
	border-left: 2px solid var(--color-border);
	margin-bottom: 0.5rem;
}

.site-header__mobile__submenu a {
	display: flex;
	align-items: center;
	min-height: 2.75rem;
	padding: 0.6rem 0.5rem;
	color: var(--color-body-muted);
	font-size: var(--text-sm);
	font-weight: 600;
	text-decoration: none;
}

.site-header__mobile__submenu a:hover { color: var(--color-primary); }
.site-header__mobile__submenu li + li { border-top: 1px solid var(--color-border); }

/* ---- Ancestor state ----
   Never existed: rules covered .current-menu-item only, so a parent stayed inert
   while the visitor was on one of its children. With children that is now the
   normal case rather than an edge one. */
.site-nav__list .current-menu-ancestor > a,
.site-nav__list .current-menu-parent > a,
.site-nav__list .current_page_ancestor > a,
.site-header__mobile-list .current-menu-ancestor > a,
.site-header__mobile-list .current-menu-parent > a,
.site-header__mobile-list .current_page_ancestor > a {
	color: var(--color-primary);
}

/* Push page content below the fixed header */
.site-main { padding-top: var(--nav-h); }

/* Anchor targets never hide under fixed bars */
[id] { scroll-margin-top: calc(var(--nav-h) + var(--chrome-top) + 1.5rem); }

/* ===== Footer ===== */
/* The footer opens on a light brand band and goes dark from the columns down.
   The brand lockup is navy/red artwork with no white-ink version and measured
   1.00:1 against this gradient's dark end; standing it on a white plate read as
   a sticker, so instead the top of the footer is a surface the lockup was drawn
   for. Everything below stays dark, so every page still closes dark and the only
   structural consequence is that the torn seam moves to the band's top edge.
   The flat-dark settle that used to sit here is gone with it — the band now
   covers the footer's top, so nothing dark meets the boundary to chain with. */
.site-footer {
	background: var(--gradient-dark);
	color: var(--color-on-dark);
	padding-block: 0 1.25rem;
}

/* Full-bleed light band; the container inside keeps the lockup on the grid.
   Tint rather than white because five templates close on a white->tint wash and
   chain into it invisibly, against three that close on flat white. Carries the
   paper grain for continuity with the other tint bands. */
.site-footer__brand-band {
	background: var(--surface-grain-light) repeat, var(--color-tint);
	color: var(--color-body);
	padding-block: 2rem;
	/* The torn edge below overhangs --seam-height (2.5rem); the rest is clearance
	   between the deepest point of the tear and the column headings. */
	margin-bottom: 4rem;
}

/* The band is light, so the whitened ring below would vanish on it. Nothing in
   the band is focusable today — this keeps that from silently becoming an
   invisible ring the moment a link is added. */
.site-footer__brand-band :focus-visible { outline-color: var(--color-primary); }

/* base.css only whitens the focus ring under .on-dark / .section--dark, and the
   footer is neither — its gradient ends on the very red the default ring uses,
   so the ring disappeared across the footer's right half. */
.site-footer :focus-visible { outline-color: var(--color-white); }

.site-footer a { color: var(--color-on-dark-muted); text-decoration: none; }

.site-footer a:hover { color: var(--color-white); text-decoration: underline; }

.site-footer__grid {
	display: grid;
	gap: 1.75rem;
	grid-template-columns: 1fr;
}

/* Contact took the brand's place as the wide first column; it still has to hold
   the full phone line without wrapping.

   Five columns is too many for a tablet: at 48rem the four link columns had
   ~7rem each, which breaks "Marketing Materials" and "Accessibility Statement"
   across three lines. So the middle step is 2-up with Contact spanning the row,
   and the full row only arrives at 64rem — the same breakpoint .grid--4 uses to
   go four-wide, for the same reason. */
@media (min-width: 48rem) {
	.site-footer__grid { grid-template-columns: repeat(2, 1fr); }
	.site-footer__grid > :first-child { grid-column: 1 / -1; }
}

@media (min-width: 64rem) {
	.site-footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1fr 1fr; }
	.site-footer__grid > :first-child { grid-column: auto; }
}

/* Bare on the light band — no plate, no border, no shadow. Capped rather than
   full-width now that it is not filling a grid column. */
.site-footer__brand > img {
	display: block;
	width: 100%;
	max-width: 18rem;
	height: auto;
}

.site-footer__brand p {
	color: var(--color-body-muted);
	max-width: 26rem;
	margin-top: 0.9rem;
	font-size: var(--text-sm);
}

.site-footer__contact {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
}

.site-footer__contact li {
	display: flex;
	align-items: center;
	gap: 0.55rem;
	color: var(--color-on-dark-muted);
	font-size: var(--text-sm);
}

.site-footer__heading {
	font-size: var(--text-sm);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	margin-bottom: 0.75rem;
	color: var(--color-white);
}

.site-footer__col ul {
	display: flex;
	flex-direction: column;
	gap: 0.45rem;
}

.site-footer__col a { font-size: var(--text-sm); }

.site-footer__bottom {
	margin-top: 1.75rem;
	padding-top: 1rem;
	border-top: 1px solid var(--color-border-dark);
	color: var(--color-on-dark-muted);
	font-size: var(--text-sm);
}
