/**
 * AksharAvira — front end.
 *
 * =============================================================================
 * FROZEN SELECTORS — the plugin's inquiry.js binds to these by name.
 * Restyle them freely; NEVER rename them. Breaking one means the modal silently
 * stops opening, with no console error.
 *
 *   #av-inquiry-modal   #av-inquiry-form   #av-newsletter-form
 *   .av-modal__panel    .av-form__status   .av-form__submit
 *   .av-newsletter__status                 .has-error
 *   [data-av-open-inquiry]  [data-av-close-inquiry]
 *   body.av-modal-open      .is-open
 *   .av-hp  — honeypot. Must stay visually hidden but NOT display:none:
 *            some bots treat display:none as a tell, and screen readers skip it.
 * =============================================================================
 *
 * There are no brand colours, font names or sizes in this file. They arrive as
 * custom properties from inc/tokens.php (settings-derived) and tokens.css
 * (static). If you are about to type a hex value here, add a setting instead.
 * The only literals are neutral scaffolding and WhatsApp's brand green, which
 * WhatsApp fixes, not us.
 *
 * Layer order is declared once, below. Note the UNLAYERED TAIL at the very
 * bottom: unlayered CSS always beats layered CSS regardless of source order, and
 * WordPress core block styles are unlayered. Block-editor overrides must live
 * there or core silently outranks every component here.
 *
 * Naming:
 *   .av-block__element--modifier   structure  (CSS owns this)
 *   .is-* / .has-*                 state      (JS → CSS channel)
 *   [data-av-*]                    behaviour  (JS owns this; CSS never styles it)
 *   .av-u-*                        utility
 */

@layer reset, base, layout, components, utilities;

/* ============================================================================
   RESET
   ========================================================================== */

@layer reset {

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

	html {
		-webkit-text-size-adjust: 100%;
		scroll-behavior: smooth;
		/* The whole rem scale — spacing included — hangs off this. */
		font-size: var(--av-base);
		scroll-padding-top: calc(var(--av-header-h) + 1rem);
	}

	/*
	 * The single most valuable accessibility block in the file. It neutralises
	 * every transition and animation defined below, so no component needs to
	 * think about reduced motion individually.
	 */
	@media (prefers-reduced-motion: reduce) {

		html {
			scroll-behavior: auto;
		}

		*,
		*::before,
		*::after {
			animation-duration: 0.01ms !important;
			animation-iteration-count: 1 !important;
			transition-duration: 0.01ms !important;
			scroll-behavior: auto !important;
		}
	}

	body {
		margin: 0;
		background: var(--av-ground);
		color: var(--av-text);
		font-family: var(--av-font-body);
		font-size: var(--av-fs-base);
		line-height: var(--av-lh);
		-webkit-font-smoothing: antialiased;
		-moz-osx-font-smoothing: grayscale;

		/*
		 * clip, NOT hidden. `overflow-x: hidden` forces overflow-y to compute
		 * to auto, which makes <body> a scroll container — but the page
		 * actually scrolls on <html>, so body's scrollHeight equals its
		 * clientHeight and it never moves. Every `position: sticky`
		 * descendant then resolves against that frozen scrollport and can
		 * never activate. Measured: the article sidebar, and the asides on
		 * the contact, product and service templates, all sat still.
		 *
		 * `clip` blocks the same horizontal overflow without establishing a
		 * scroll container, so sticky resolves against <html> and works.
		 * Unsupported in Safari < 16, where the declaration is simply dropped
		 * — no horizontal guard, which the layout audit says nothing needs.
		 */
		overflow-x: clip;
	}

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

	/* <figure> is normalised in the unlayered tail — see the note there. It cannot
	   be done from inside a layer. */

	button,
	input,
	select,
	textarea {
		font: inherit;
		color: inherit;
	}
}

/* ============================================================================
   BASE — element defaults
   ========================================================================== */

@layer base {

	h1, h2, h3, h4, h5, h6 {
		margin: 0;
		font-family: var(--av-font-head);
		font-weight: var(--av-head-weight);
		letter-spacing: var(--av-head-track);
		line-height: var(--av-lh-tight);
		color: var(--av-ink);
		text-wrap: balance;
	}

	h1 { font-size: var(--av-fs-h1); }
	h2 { font-size: var(--av-fs-h2); }
	h3 { font-size: var(--av-fs-h3); }
	h4 { font-size: var(--av-fs-h4); }

	p { margin: 0; }

	a {
		color: var(--av-primary);
		text-underline-offset: 0.18em;
	}

	a:hover { color: var(--av-primary-hover); }

	/* Visible, generous, and never removed. */
	:focus-visible {
		outline: 2px solid var(--av-primary);
		outline-offset: 3px;
		border-radius: var(--av-radius-sm);
	}

	::selection {
		background: rgb(var(--av-primary-rgb) / 15%);
	}

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

	.av-skip {
		position: absolute;
		left: -9999px;
		top: 0;
		z-index: var(--av-z-skip);
		padding: 0.75rem 1.25rem;
		background: var(--av-primary);
		color: var(--av-on-primary);
		border-radius: 0 0 var(--av-radius-sm) 0;
		text-decoration: none;
		font-weight: 600;
	}

	.av-skip:focus {
		left: 0;
		top: 0;
		color: var(--av-on-primary);
	}

	/* Icons inherit colour and take their stroke from the token. */
	.av-icon {
		stroke-width: var(--av-icon-stroke);
		flex: 0 0 auto;
	}
}

/* ============================================================================
   LAYOUT
   ========================================================================== */

@layer layout {

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

	.av-wrap--wide {
		max-width: var(--av-container-wide);
	}

	.av-wrap--narrow {
		max-width: 820px;
	}

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

	.av-section--sm { padding-block: var(--av-section-y-sm); }
	.av-section--flush-top { padding-block-start: 0; }
	.av-section--flush-bottom { padding-block-end: 0; }

	/* Alternating grounds. --dark re-themes its descendants via the card chassis. */
	.av-section--muted { background: var(--av-muted); }
	.av-section--surface { background: var(--av-surface); }

	.av-section--dark {
		background: var(--av-deep);
		color: var(--av-on-dark-muted);
	}

	/* The --dark text overrides are NOT here. They set the same properties as
	   rules in the components layer, and layer order beats specificity, so a
	   layout-layer override loses no matter how specific it is. They live at
	   the end of the components layer instead — search "dark-ground text". */

	/*
	 * Generic gradient surface. Currently unused — the hero, CTA and newsletter
	 * each carry their own version — but kept as the shared definition, and kept
	 * on the SAME vivid ramp as those three so a future section that reaches for
	 * it does not reintroduce the near-black gradient the others were moved off.
	 */
	.av-section--gradient {
		position: relative;
		background:
			radial-gradient(65% 85% at 88% 88%, rgb(var(--av-accent-rgb) / 30%) 0%, transparent 62%),
			radial-gradient(60% 80% at 8% 4%, rgb(255 255 255 / 12%) 0%, transparent 58%),
			linear-gradient(125deg, var(--av-primary-hover) 0%, var(--av-primary) 45%, var(--av-secondary) 100%);
		color: var(--av-on-dark-muted);
		isolation: isolate;
	}

	/*
	 * Prose — long-form editor content.
	 *
	 * 68ch is the readable measure, but a 68ch column pinned to the left of a
	 * 1280px container leaves ~600px of dead space beside it, which is what the
	 * About page looked like. Centre it by default; the split layouts below opt
	 * out, because there the column sits beside something.
	 */
	.av-prose {
		max-width: 68ch;
		margin-inline: auto;
		display: flex;
		flex-direction: column;
		gap: var(--av-para-space);
	}

	/* Inside a split or a card, prose is one column of two — do not centre it.
	   The product page's main column is the same situation: centring pushed its
	   copy 63px right of the page h1 it sits under, so nothing lined up. */
	.av-u-split .av-prose,
	.av-card .av-prose,
	.av-product__main .av-prose {
		margin-inline: 0;
		max-width: none;
	}

	/*
	 * When prose IS the page container — page.php and page-about.php put both
	 * .av-wrap and .av-prose on one element — fill the container like the
	 * homepage instead of collapsing to a 68ch strip with dead side margins
	 * (the "not the same container as homepage" complaint). The container width
	 * comes back; only the text RUNS keep a readable measure (left-anchored), so
	 * grids, columns, wide blocks and the product shortcode span full width.
	 *
	 * Scoped to .av-wrap.av-prose so a standalone reading column (single.php's
	 * blog prose, nested inside .av-wrap--narrow) still gets its 68ch measure.
	 */
	.av-wrap.av-prose { max-width: none; }

	.av-wrap.av-prose > :not(.alignwide):not(.alignfull):not(.wp-block-columns):not(.av-prose__bleed):not([class*="av-u-grid"]) {
		max-width: 72ch;
	}

	/* Explicit full-container breakout for shortcode/grid output inside prose. */
	.av-prose__bleed { max-width: none; width: 100%; }

	.av-prose > h2 { margin-block-start: 1.5em; }
	.av-prose > h3 { margin-block-start: 1em; }
	.av-prose > :first-child { margin-block-start: 0; }

	.av-prose ul,
	.av-prose ol {
		margin: 0;
		padding-inline-start: 1.3rem;
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
	}

	.av-prose img { border-radius: var(--av-radius-img); }
}

/* ============================================================================
   UTILITIES
   ========================================================================== */

@layer utilities {

	/*
	 * One auto-fit grid replaces the five bespoke grid classes the old CSS had
	 * (cards-grid, offer-grid, quotes, certgrid, statband__grid).
	 */
	.av-u-grid {
		display: grid;
		gap: var(--av-gap);
		grid-template-columns: repeat(auto-fit, minmax(min(var(--min, 260px), 100%), 1fr));
	}

	.av-u-grid--min-200 { --min: 200px; }
	.av-u-grid--min-240 { --min: 240px; }
	.av-u-grid--min-280 { --min: 280px; }
	.av-u-grid--min-320 { --min: 320px; }

	/*
	 * auto-fill keeps the empty tracks that auto-fit collapses, so a grid with a
	 * single item leaves it at column width instead of stretching it across the
	 * whole row. Used by search/blog results, where a 1-result page otherwise
	 * rendered one enormous full-width card.
	 */
	.av-u-grid--fill {
		grid-template-columns: repeat(auto-fill, minmax(min(var(--min, 260px), 100%), 1fr));
		justify-content: start;
	}

	/*
	 * Equal card height in a row.
	 *
	 * .av-u-grid relies on the default align-items:stretch, so a card that is a
	 * direct grid child already fills its row. But where a card is wrapped (the
	 * product archive wraps each in a data-av-reveal div for the stagger), the
	 * WRAPPER stretches and the card inside sits at content height. Make the
	 * wrapper a flex column and let the card fill it, so tiles line up whether or
	 * not they are wrapped.
	 */
	/*
	 * :not(.av-card) is essential. This rule exists for a plain WRAPPER div that
	 * holds a card — but some templates put data-av-reveal on the card itself
	 * (the certification list does). Without the guard this forced those cards to
	 * flex-direction:column and stretched their children, which silently turned
	 * the row-layout cert card into a centred stack with an orphaned tick.
	 */
	.av-u-grid > [data-av-reveal]:not(.av-card),
	.av-u-cols-2 > [data-av-reveal]:not(.av-card),
	.av-u-cols-3 > [data-av-reveal]:not(.av-card),
	.av-u-cols-4 > [data-av-reveal]:not(.av-card) {
		display: flex;
		flex-direction: column;
	}

	.av-u-grid > [data-av-reveal]:not(.av-card) > *,
	.av-u-cols-2 > [data-av-reveal]:not(.av-card) > *,
	.av-u-cols-3 > [data-av-reveal]:not(.av-card) > *,
	.av-u-cols-4 > [data-av-reveal]:not(.av-card) > * { flex: 1 1 auto; }

	.av-u-grid > .av-card,
	.av-u-cols-2 > .av-card,
	.av-u-cols-3 > .av-card,
	.av-u-cols-4 > .av-card,
	.av-u-grid > [data-av-reveal] > .av-card,
	.av-u-cols-2 > [data-av-reveal] > .av-card,
	.av-u-cols-3 > [data-av-reveal] > .av-card,
	.av-u-cols-4 > [data-av-reveal] > .av-card { height: 100%; }

	/* Fixed column counts, collapsing on small screens. */
	.av-u-cols-2,
	.av-u-cols-3,
	.av-u-cols-4 {
		display: grid;
		gap: var(--av-gap);
	}

	.av-u-cols-2 { grid-template-columns: repeat(2, 1fr); }
	.av-u-cols-3 { grid-template-columns: repeat(3, 1fr); }
	.av-u-cols-4 { grid-template-columns: repeat(4, 1fr); }

	@media (max-width: 900px) {
		.av-u-cols-3,
		.av-u-cols-4 { grid-template-columns: repeat(2, 1fr); }
	}

	@media (max-width: 560px) {
		.av-u-cols-2,
		.av-u-cols-3,
		.av-u-cols-4 { grid-template-columns: 1fr; }
	}

	/* Split layouts — the alternating image/text rhythm. */
	.av-u-split {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: clamp(2rem, 5vw, var(--av-gap-lg));
		align-items: center;
	}

	.av-u-split--wide-left { grid-template-columns: 1.15fr 0.85fr; }
	.av-u-split--wide-right { grid-template-columns: 0.85fr 1.15fr; }

	@media (max-width: 900px) {
		.av-u-split,
		.av-u-split--wide-left,
		.av-u-split--wide-right { grid-template-columns: 1fr; }
	}

	/* Reverse visual order without touching DOM order (keeps reading order sane). */
	@media (min-width: 901px) {
		.av-u-split--reverse > :first-child { order: 2; }
	}

	.av-u-stack {
		display: flex;
		flex-direction: column;
		gap: var(--av-gap);
		align-items: flex-start;
	}

	.av-u-stack--sm { gap: var(--av-gap-sm); }
	.av-u-stack--center { align-items: center; text-align: center; }

	.av-u-row {
		display: flex;
		flex-wrap: wrap;
		gap: var(--av-gap-sm);
		align-items: center;
	}

	/* Anything wide must scroll inside itself, never the page. */
	.av-u-scroll-x {
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
	}

	/*
	 * The utilities below exist because the same eight declarations were being
	 * repeated as inline style="" attributes across 22 call sites. Inline styles
	 * cannot be kept consistent, cannot be overridden by a modifier, and cannot
	 * be audited — which is exactly how the spacing drifted apart.
	 */

	/* Strip list chrome. A <ul> used for layout still needs semantics. */
	.av-u-bare {
		list-style: none;
		margin: 0;
		padding: 0;
	}

	.av-u-full { width: 100%; }

	/* Rhythm helpers — the ONLY sanctioned way to add space outside a section. */
	.av-u-mb { margin-block-end: var(--av-gap-lg); }
	.av-u-mt { margin-block-start: var(--av-gap-lg); }

	/*
	 * Top-align a split. The default is centre, which is right for image+text
	 * but wrong whenever one column is much taller than the other.
	 */
	.av-u-split--top { align-items: start; }

	/* A column that follows the reader, clearing the sticky header. */
	.av-u-sticky {
		position: sticky;
		top: calc(var(--av-header-h) + var(--av-space-4));
	}

	@media (max-width: 960px) {
		.av-u-sticky { position: static; }
	}

	/* A sub-heading inside a section — smaller than the section title. */
	.av-u-h4 {
		font-size: var(--av-fs-h4);
		font-family: var(--av-font-head);
		font-weight: var(--av-head-weight);
		letter-spacing: var(--av-head-track);
		line-height: var(--av-lh-tight);
		color: var(--av-ink);
	}

	.av-section--dark .av-u-h4,
	.av-card--dark .av-u-h4 { color: var(--av-on-dark); }

	/* --- scroll reveal ---
	 * Gated on html.js, set by an inline script before paint. Without that gate,
	 * a JS failure would hide the entire page. */
	html.js [data-av-reveal] {
		opacity: 0;
		transform: translateY(12px);
	}

	html.js [data-av-reveal].is-in {
		opacity: 1;
		transform: none;
		transition:
			opacity var(--av-dur-slow) var(--av-ease-out),
			transform var(--av-dur-slow) var(--av-ease-out);
	}

	/* --- skeleton --- */
	.av-skeleton {
		background: linear-gradient(
			90deg,
			var(--av-muted) 25%,
			rgb(var(--av-ink-rgb) / 5%) 37%,
			var(--av-muted) 63%
		);
		background-size: 400% 100%;
		animation: av-shimmer 1.4s ease infinite;
		border-radius: var(--av-radius-sm);
	}

	@keyframes av-shimmer {
		0% { background-position: 100% 50%; }
		100% { background-position: 0 50%; }
	}
}

/* ============================================================================
   COMPONENTS
   ========================================================================== */

@layer components {

	/* ------------------------------------------------------------------ CARD
	 * The chassis. Every card-shaped component composes this and re-themes via
	 * LOCAL custom properties rather than re-declaring rules — which is why a
	 * --dark variant costs five lines instead of ninety, and why the two
	 * parallel testimonial-card systems this replaces cannot re-fork.
	 */

	.av-card {
		--card-bg: var(--av-surface);
		--card-fg: var(--av-text);
		--card-head: var(--av-ink);
		--card-bd: var(--av-border);
		--card-pad: var(--av-card-pad);
		--card-radius: var(--av-radius-card);
		--card-shadow: var(--av-shadow-sm);
		--card-shadow-hover: var(--av-shadow-lg);
		--card-accent: var(--av-primary);

		position: relative;
		display: flex;
		flex-direction: column;
		gap: var(--av-gap-sm);
		background: var(--card-bg);
		color: var(--card-fg);
		border: 1px solid var(--card-bd);
		border-radius: var(--card-radius);
		padding: var(--card-pad);
		box-shadow: var(--card-shadow);
		transition:
			transform var(--av-dur) var(--av-ease-out),
			box-shadow var(--av-dur) var(--av-ease-out),
			border-color var(--av-dur) var(--av-ease-out);
	}

	.av-card h3,
	.av-card h4 { color: var(--card-head); }

	.av-card--dark {
		--card-bg: var(--av-on-dark-surface);
		--card-fg: var(--av-on-dark-muted);
		--card-head: var(--av-on-dark);
		--card-bd: var(--av-on-dark-border);
		--card-shadow: none;
		--card-shadow-hover: none;
		--card-accent: var(--av-accent);
		backdrop-filter: blur(6px);
	}

	.av-card--flat {
		--card-shadow: none;
		--card-shadow-hover: none;
	}

	.av-card--ghost {
		--card-bg: transparent;
		--card-bd: transparent;
		--card-shadow: none;
		--card-pad: 0;
	}

	/*
	 * Media-led card: the image runs to the card edge, and the body carries its
	 * own padding. Replaces style="--card-pad:0" at the call sites.
	 */
	.av-card--bleed {
		--card-pad: 0;
		overflow: hidden;
	}

	/* A card whose content is laid out as a row rather than a stack. */
	.av-card--row {
		flex-direction: row;
		align-items: center;
	}

	/*
	 * Interactive cards get a brand edge that wipes across the top on hover.
	 * A lift alone is easy to miss on a white card against a near-white ground;
	 * a coloured edge reads instantly as "this responds".
	 */
	.av-card--interactive {
		position: relative;
		overflow: hidden;
	}

	.av-card--interactive::after {
		content: "";
		position: absolute;
		inset-inline: 0;
		inset-block-start: 0;
		height: 3px;
		pointer-events: none;
		background: linear-gradient(90deg, var(--av-primary) 0%, var(--av-secondary) 55%, var(--av-accent) 100%);
		transform: scaleX(0);
		transform-origin: left center;
		transition: transform var(--av-dur-slow) var(--av-ease-out);
	}

	.av-card--interactive:hover::after,
	.av-card--interactive:focus-within::after { transform: scaleX(1); }

	.av-card--interactive:hover,
	.av-card--interactive:focus-within {
		transform: translateY(var(--av-lift));
		box-shadow: var(--card-shadow-hover);
		border-color: rgb(var(--av-primary-rgb) / 30%);
	}

	/* The icon tile answers the hover too, so the whole card feels connected. */
	.av-card--interactive .av-icon-tile {
		transition:
			background-color var(--av-dur) var(--av-ease),
			color var(--av-dur) var(--av-ease),
			transform var(--av-dur-slow) var(--av-ease-spring);
	}

	.av-card--interactive:hover .av-icon-tile {
		background: var(--av-primary);
		color: var(--av-on-primary);
		transform: translateY(-2px) rotate(-6deg);
	}

	.av-card--dark.av-card--interactive:hover {
		border-color: rgb(var(--av-accent-rgb) / 45%);
	}

	/* Stretch a link over the whole card without nesting interactive elements. */
	.av-card__link::after {
		content: "";
		position: absolute;
		inset: 0;
		border-radius: inherit;
	}

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

	/* The tinted icon tile every card type shares. */
	.av-card__icon,
	.av-icon-tile {
		display: grid;
		place-items: center;
		width: 48px;
		height: 48px;
		border-radius: var(--av-radius-sm);
		background: rgb(var(--av-primary-rgb) / 9%);
		color: var(--card-accent, var(--av-primary));
		flex: 0 0 auto;
	}

	.av-card--dark .av-icon-tile,
	.av-section--dark .av-icon-tile {
		background: rgb(255 255 255 / 10%);
		color: var(--av-accent);
	}

	.av-icon-tile--sm { width: 36px; height: 36px; }
	.av-icon-tile--lg { width: 56px; height: 56px; }

	.av-icon-tile--round { border-radius: 50%; }

	.av-icon-tile--solid {
		background: var(--av-primary);
		color: var(--av-on-primary);
	}

	/*
	 * For use on the deep/gradient grounds, where the tinted primary tile has no
	 * contrast to work against. Replaces an inline rgb() blob in the partners
	 * banner.
	 */
	.av-icon-tile--on-dark {
		background: rgb(255 255 255 / 12%);
		border: 1px solid var(--av-on-dark-border);
		color: var(--av-on-dark);
	}

	/* ---------------------------------------------------------------- BUTTON */

	.av-btn {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 0.5rem;
		padding: 0.85rem 1.5rem;
		border: 1px solid transparent;
		border-radius: var(--av-radius-pill);
		font-family: var(--av-font-head);
		font-weight: 600;
		font-size: 0.9375rem;
		line-height: 1.2;
		text-decoration: none;
		white-space: nowrap;
		cursor: pointer;
		transition:
			background-color var(--av-dur) var(--av-ease),
			border-color var(--av-dur) var(--av-ease),
			color var(--av-dur) var(--av-ease),
			box-shadow var(--av-dur) var(--av-ease),
			transform var(--av-dur-fast) var(--av-ease);
	}

	.av-btn:active { transform: translateY(1px); }

	/* The arrow nudges; other icons stay put. */
	.av-btn:hover .av-icon--arrow,
	.av-btn:hover .av-icon--arrow-up-right { transform: translateX(3px); }
	.av-btn .av-icon { transition: transform var(--av-dur) var(--av-ease-out); }

	.av-btn--primary {
		background: var(--av-primary);
		color: var(--av-on-primary);
		box-shadow: var(--av-shadow-sm);
	}

	.av-btn--primary:hover {
		background: var(--av-primary-hover);
		color: var(--av-on-primary);
		box-shadow: var(--av-shadow-md);
	}

	.av-btn--secondary {
		background: var(--av-secondary);
		color: var(--av-on-secondary);
	}

	/* Hover darkens secondary in its OWN hue — not a jump to primary, which read
	   as the button changing identity mid-interaction. */
	.av-btn--secondary:hover {
		background: var(--av-secondary-hover);
		color: var(--av-on-secondary);
	}

	.av-btn--ghost {
		background: transparent;
		color: var(--av-primary);
		border-color: var(--av-border);
	}

	.av-btn--ghost:hover {
		background: rgb(var(--av-primary-rgb) / 6%);
		border-color: rgb(var(--av-primary-rgb) / 35%);
		color: var(--av-primary);
	}

	/* Translucent — for use on the deep/gradient grounds. */
	.av-btn--glass {
		background: rgb(255 255 255 / 10%);
		color: #fff;
		border-color: rgb(255 255 255 / 26%);
		backdrop-filter: blur(6px);
	}

	.av-btn--glass:hover {
		background: rgb(255 255 255 / 18%);
		color: #fff;
	}

	/*
	 * WhatsApp green is fixed by WhatsApp's brand guidelines, not our palette —
	 * same two colours as the floating button below, so the site has one
	 * WhatsApp treatment rather than several.
	 *
	 * The label is near-black on purpose: measured, #04310F on #25D366 is
	 * 7.30:1, where the obvious white-on-green is 1.98:1 and fails badly.
	 */
	.av-btn--whatsapp {
		background: #25d366;
		color: #04310f;
		border: 1px solid transparent;
	}

	.av-btn--whatsapp:hover {
		background: #1ebe5a;   /* 6.05:1 with the same label colour */
		color: #04310f;
	}

	/* Inverted — a light pill on a deep ground. */
	.av-btn--on-dark {
		background: #fff;
		color: var(--av-deep);
	}

	/* On a deep ground the white pill fills with primary and its text goes white
	   on hover — a solid, high-contrast, on-brand press. The old hover flipped to
	   accent cyan, which left the button palette and computed to borderline
	   contrast. (A translucent tint would read dark-on-dark here, so it must be a
	   solid fill.) */
	.av-btn--on-dark:hover {
		background: var(--av-primary);
		color: var(--av-on-primary);
	}

	.av-btn--sm { padding: 0.6rem 1.1rem; font-size: 0.86rem; }
	.av-btn--lg { padding: 1rem 1.85rem; font-size: 1rem; }
	.av-btn--block { width: 100%; }

	.av-btn[disabled],
	.av-btn[aria-disabled="true"] {
		opacity: 0.55;
		pointer-events: none;
	}

	/* ------------------------------------------------------------- EYEBROW */

	/*
	 * Section eyebrow.
	 *
	 * Was a bare line of small caps with a hairline rule — legible, but it read
	 * as a caption rather than a deliberate label, and it was the first thing on
	 * every section, so every section opened flat. A tinted pill with a live
	 * status dot gives each section a confident start and one small point of
	 * motion above the fold.
	 */
	/*
	 * Section eyebrow — a quiet tinted pill.
	 *
	 * align-self/width are load-bearing, not cosmetic: .av-secthead is a flex
	 * column, so a flex item stretches across the whole column by default and
	 * `display: inline-flex` does NOT prevent it. Without these the pill ran the
	 * full width of the section and read as a broken bar.
	 */
	.av-eyebrow {
		display: inline-flex;
		align-self: flex-start;
		width: fit-content;
		max-width: 100%;
		align-items: center;
		gap: 0.5rem;
		padding: 0.35rem 0.8rem 0.35rem 0.65rem;
		border-radius: var(--av-radius-pill);
		background: rgb(var(--av-primary-rgb) / 7%);
		font-family: var(--av-font-head);
		font-size: var(--av-fs-xs);
		font-weight: 700;
		letter-spacing: 0.1em;
		text-transform: var(--av-eyebrow-transform);
		color: var(--av-primary);
	}

	/* Centred section headers centre their own children. */
	.av-secthead--center .av-eyebrow { align-self: center; }

	/* A static marker dot. It was a pulsing halo; with an eyebrow on every
	   section that meant five things blinking at once, which is noise. */
	.av-eyebrow::before {
		content: "";
		flex: 0 0 auto;
		width: 6px;
		height: 6px;
		border-radius: 50%;
		background: currentColor;
	}

	.av-section--dark .av-eyebrow,
	.av-hero .av-eyebrow {
		background: rgb(255 255 255 / 10%);
		color: var(--av-accent);
	}

	/* The highlighted phrase inside a split heading. */
	.av-hl { color: var(--av-secondary); }
	.av-section--dark .av-hl,
	.av-section--gradient .av-hl,
	.av-hero .av-hl { color: var(--av-accent); }

	/* --------------------------------------------------- 2. SECTION HEADER */

	.av-secthead {
		display: flex;
		flex-direction: column;
		gap: 0.85rem;
		margin-block-end: var(--av-gap-lg);
		max-width: 62ch;
	}

	.av-secthead--center {
		align-items: center;
		text-align: center;
		margin-inline: auto;
	}

	.av-secthead__title { max-width: 22ch; }
	.av-secthead--center .av-secthead__title { max-width: 26ch; }

	.av-secthead__intro {
		color: var(--av-text-muted);
		font-size: var(--av-fs-lg);
	}

	/* Header + action on one line. */
	.av-secthead--split {
		flex-direction: row;
		align-items: flex-end;
		justify-content: space-between;
		max-width: none;
		gap: var(--av-gap);
	}

	@media (max-width: 720px) {
		.av-secthead--split {
			flex-direction: column;
			align-items: flex-start;
		}
	}

	/* ---------------------------------------------------- 1. HERO BANNER */

	.av-hero {
		position: relative;
		padding-block: clamp(3.5rem, 8vw, 6.5rem);
		overflow: hidden;
	}

	/*
	 * A saturated blue field, not the near-black navy this used to be. The old
	 * ramp started at --av-deep (#0F172A), which is almost black, so the hero
	 * read heavy and the blue brand colour never actually appeared. Running
	 * primary-hover → primary → secondary keeps it unmistakably brand blue while
	 * still darkening behind the headline for contrast.
	 */
	.av-hero--home {
		background:
			radial-gradient(65% 85% at 88% 88%, rgb(var(--av-accent-rgb) / 30%) 0%, transparent 62%),
			radial-gradient(60% 80% at 8% 4%, rgb(255 255 255 / 12%) 0%, transparent 58%),
			linear-gradient(125deg, var(--av-primary-hover) 0%, var(--av-primary) 45%, var(--av-secondary) 100%);
		color: var(--av-on-dark-muted);
		isolation: isolate;
	}

	.av-hero--home h1 { color: var(--av-on-dark); }

	/* A faint grid — reads as engineering, not decoration. */
	.av-hero--home::before {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		background-image:
			linear-gradient(rgb(255 255 255 / 4%) 1px, transparent 1px),
			linear-gradient(90deg, rgb(255 255 255 / 4%) 1px, transparent 1px);
		background-size: 64px 64px;
		mask-image: radial-gradient(circle at 70% 40%, #000 0%, transparent 70%);
		pointer-events: none;
	}

	.av-hero__inner {
		display: grid;
		grid-template-columns: 1.05fr 0.95fr;
		gap: clamp(2rem, 5vw, 4rem);
		align-items: center;
	}

	@media (max-width: 960px) {
		.av-hero__inner { grid-template-columns: 1fr; }
	}

	/*
	 * Two columns of equal height rather than two centred blocks.
	 *
	 * Centring left the text column ~30px taller than the image, so neither
	 * edge lined up with anything and the media floated in loose blue. Stretching
	 * makes the image share the text block's top and bottom edge, which is what
	 * makes the split read as deliberate.
	 */
	@media (min-width: 961px) {
		.av-hero__inner { align-items: stretch; }

		.av-hero__media {
			display: flex;
			flex-direction: column;
			min-height: 26rem;   /* floor, for pages where the copy is short */
		}

		.av-hero__figure { flex: 1; min-height: 0; }

		.av-hero__figure > img,
		.av-hero__figure > video,
		.av-hero__figure > .av-ph {
			height: 100%;
			aspect-ratio: auto;
			object-fit: cover;
		}
	}

	/*
	 * Rhythm is set per gap, not by one uniform stack gap.
	 *
	 * At a flat 24px the badge, headline, intro, buttons and credentials all read
	 * as five equally-weighted items. Grouping them — label tight to headline,
	 * headline tight to intro, then a real break before the CTA and another
	 * before the credentials — is what gives the column a shape.
	 */
	.av-hero__content {
		display: flex;
		flex-direction: column;
		align-items: flex-start;
		gap: 0;
	}

	.av-hero__content > .av-hero__title { margin-block-start: 1.25rem; }
	.av-hero__content > .av-hero__intro { margin-block-start: 1rem; }

	.av-hero__badge {
		display: inline-flex;
		align-items: center;
		gap: 0.55rem;
		padding: 0.45rem 1rem;
		border-radius: var(--av-radius-pill);
		background: rgb(255 255 255 / 10%);
		border: 1px solid rgb(255 255 255 / 18%);
		font-size: var(--av-fs-sm);
		font-weight: 500;
		color: rgb(255 255 255 / 90%);
		backdrop-filter: blur(6px);
	}

	.av-hero__dot {
		width: 7px;
		height: 7px;
		border-radius: 50%;
		background: var(--av-accent);
		box-shadow: 0 0 0 3px rgb(var(--av-accent-rgb) / 25%);
		flex: 0 0 auto;
	}

	.av-hero__title { max-width: 16ch; }

	.av-hero__intro {
		font-size: var(--av-fs-lg);
		max-width: 52ch;
		color: var(--av-on-dark-muted);
	}

	/* Scoped to the home hero — the compact page hero shares .av-hero__intro and
	   wants the wider measure. 46ch keeps the intro visibly narrower than the
	   headline block so the two do not form one grey slab. */
	.av-hero--home .av-hero__intro { max-width: 46ch; }

	.av-hero--compact .av-hero__intro { color: var(--av-text-muted); }

	.av-hero__actions {
		display: flex;
		flex-wrap: wrap;
		align-items: stretch;   /* both pills share a height even if one wraps */
		gap: 0.75rem;
		margin-block-start: 2rem;
	}

	.av-hero__stats {
		display: flex;
		flex-wrap: wrap;
		gap: 0.6rem;
		list-style: none;
		margin: 1.75rem 0 0;
		padding: 0;
		width: 100%;
	}

	/*
	 * Trust markers as chips rather than a bare row of text: each gets its own
	 * translucent panel and icon tile, so they read as credentials instead of
	 * running together as a caption under the buttons.
	 */
	/*
	 * Pill, to match the badge above and the buttons between them. The section
	 * previously ran four different corner radii — 999px badge, 999px buttons,
	 * 14px chips, 20px frame, 10px floating cards — which is what made a tidy
	 * layout still feel unresolved. Now the left column is one pill family and
	 * the media column is nested rounded rectangles.
	 */
	.av-hero__chip {
		display: flex;
		align-items: center;
		gap: 0.6rem;
		padding: 0.5rem 1.1rem 0.5rem 0.5rem;
		border-radius: var(--av-radius-pill);
		background: rgb(255 255 255 / 10%);
		border: 1px solid rgb(255 255 255 / 16%);
		backdrop-filter: blur(6px);
	}

	.av-hero__chip-text {
		display: flex;
		flex-direction: column;
		gap: 0.05rem;
		line-height: 1.25;
	}

	.av-hero__stat-v {
		font-family: var(--av-font-head);
		font-weight: 700;
		font-size: 1.05rem;
		color: var(--av-on-dark);
	}

	.av-hero__stat-l {
		font-size: var(--av-fs-xs);
		color: var(--av-on-dark-subtle);
	}

	.av-hero__media { position: relative; }

	/*
	 * The frame sat on the blue with only a shadow, which the saturated ground
	 * swallows. A hairline highlight on top of the shadow gives it an edge to
	 * read against without introducing another colour.
	 */
	.av-hero__figure {
		margin: 0;
		border-radius: var(--av-radius-img);
		overflow: hidden;
		border: 1px solid rgb(255 255 255 / 14%);
		box-shadow:
			var(--av-shadow-xl),
			0 0 0 1px rgb(255 255 255 / 6%),
			0 24px 60px -20px rgb(2 6 23 / 55%);
	}

	.av-hero__img,
	.av-hero__video {
		width: 100%;
		aspect-ratio: 4 / 3;
		object-fit: cover;
	}

	/* display:block removes the inline-element baseline gap that would otherwise
	   show as a hairline of figure background under the video. */
	.av-hero__video {
		display: block;
		background: var(--av-deep);
	}

	/*
	 * The two cards are a matched pair on a diagonal: same overhang, same inset,
	 * opposite corners. They previously used different offsets (-16px vs -12px)
	 * and different vertical insets, so they read as two accidents rather than a
	 * composition. 14px sits inside the frame's 20px — an inner radius should be
	 * smaller than the one it nests in, or the corners fight.
	 */
	.av-hero__card {
		--card-overhang: 1rem;
		--card-inset: 1.5rem;

		position: absolute;
		left: calc(var(--card-overhang) * -1);
		bottom: var(--card-inset);
		display: flex;
		align-items: center;
		gap: 0.75rem;
		background: var(--av-surface);
		color: var(--av-ink);
		border-radius: var(--av-radius-input);
		padding: 0.75rem 1.1rem;
		box-shadow: var(--av-shadow-lg);
		max-width: 78%;
	}

	/*
	 * Second card, over the top-right of the image. Dark glass rather than the
	 * white of the lower card, so the two read as a pair without competing —
	 * and so it stays legible over a bright image.
	 */
	.av-hero__card--top {
		left: auto;
		bottom: auto;
		right: calc(var(--card-overhang) * -1);
		top: var(--card-inset);
		background: rgb(var(--av-deep-rgb) / 78%);
		border: 1px solid rgb(255 255 255 / 14%);
		backdrop-filter: blur(10px);
		color: var(--av-on-dark);
		padding: 0.75rem 1.1rem;
	}

	.av-hero__card--top .av-hero__card-value {
		display: flex;
		align-items: center;
		gap: 0.4rem;
		color: var(--av-on-dark);
		font-size: 1.25rem;
	}

	.av-hero__card--top .av-hero__card-value .av-icon { color: var(--av-accent); }
	.av-hero__card--top .av-hero__card-label { color: var(--av-on-dark-muted); }

	/* Single column: the media spans the full width, so an overhang would push
	   the cards against the viewport edge. Tuck them inside instead. */
	@media (max-width: 960px) {
		.av-hero__card { left: 1rem; }
		.av-hero__card--top { left: auto; right: 1rem; }
	}

	/* Below ~520px two floating cards over a full-bleed image crowd it and start
	   covering the subject. Keep the headline stat, drop the second. */
	@media (max-width: 520px) {
		.av-hero__card { max-width: calc(100% - 2rem); }
		.av-hero__card--top { display: none; }
	}

	.av-hero__card-text {
		display: flex;
		flex-direction: column;
		line-height: 1.3;
	}

	.av-hero__card-label {
		font-size: var(--av-fs-xs);
		color: var(--av-text-subtle);
	}

	.av-hero__card-value {
		font-family: var(--av-font-head);
		font-weight: 700;
		font-size: 0.9rem;
		color: var(--av-ink);
	}

	/* Interior page hero. */
	.av-hero--compact {
		background: var(--av-muted);
		border-bottom: 1px solid var(--av-border);
		padding-block: clamp(2rem, 4vw, 3.5rem);
	}

	.av-hero--compact .av-hero__title { max-width: 26ch; }

	.av-hero__meta {
		color: var(--av-text-subtle);
		font-size: var(--av-fs-sm);
	}

	/* --------------------------------------------------- 3. FEATURE CARD */

	.av-feature__title { font-size: 1rem; }

	.av-feature__text {
		font-size: var(--av-fs-sm);
		color: var(--card-fg);
	}

	.av-feature--row {
		flex-direction: row;
		align-items: flex-start;
		gap: var(--av-gap-sm);
	}

	.av-feature--row .av-feature__body {
		display: flex;
		flex-direction: column;
		gap: 0.25rem;
	}

	/* --------------------------------------------------- 4. SERVICE CARD */

	.av-service__title { font-size: 1.0625rem; }

	/*
	 * Cards in a row are all as tall as the longest copy, and the "Learn more"
	 * is pinned to the bottom — so a short description left a visible hole in
	 * the middle of three of the four offering cards. Centring the copy in its
	 * slack splits that space above and below instead of pooling it in one gap.
	 */
	.av-service__text {
		font-size: var(--av-fs-sm);
		color: var(--card-fg);
		flex: 1;
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: 0.6em;
	}

	.av-service__more,
	.av-more {
		display: inline-flex;
		align-items: center;
		gap: 0.35rem;
		font-family: var(--av-font-head);
		font-size: var(--av-fs-sm);
		font-weight: 600;
		color: var(--card-accent, var(--av-primary));
		text-decoration: none;
		margin-block-start: auto;
	}

	.av-card--interactive:hover .av-more .av-icon,
	.av-card--interactive:hover .av-service__more .av-icon {
		transform: translateX(3px);
	}

	.av-more .av-icon,
	.av-service__more .av-icon {
		transition: transform var(--av-dur) var(--av-ease-out);
	}

	/* Numbered variant — the 01/02/03 offering cards. */
	.av-service__top {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--av-gap-sm);
	}

	.av-service__n {
		font-family: var(--av-font-head);
		font-size: 2rem;
		font-weight: 700;
		line-height: 1;
		color: rgb(var(--av-ink-rgb) / 9%);
		font-variant-numeric: tabular-nums;
		transition: color var(--av-dur) var(--av-ease);
	}

	.av-card--interactive:hover .av-service__n {
		color: rgb(var(--av-primary-rgb) / 20%);
	}

	/* ------------------------------------------------- 5. STATISTIC BAND */

	/*
	 * These figures are the page's proof, so they read as one claim on one
	 * panel rather than four disconnected boxes. The panel carries the world
	 * map as a dotted backdrop — the numbers are about global reach, so the
	 * texture is the subject, not decoration.
	 */
	.av-statband__panel {
		position: relative;
		overflow: hidden;
		border-radius: calc(var(--av-radius-card) * 1.5);
		background:
			linear-gradient(160deg,
				rgb(var(--av-accent-rgb) / 7%) 0%,
				rgb(var(--av-primary-rgb) / 4%) 100%),
			var(--av-surface);
		padding: clamp(2rem, 5vw, 3.5rem) clamp(1.25rem, 4vw, 3rem);
	}

	/*
	 * The dots are the panel's own background; the map PNG is only an alpha
	 * mask over them. So the dot grid appears over land and nowhere else, and
	 * the colour follows --av-primary from settings instead of being baked
	 * into the file. Same masking technique as .av-worldmap__plate.
	 */
	.av-statband__map {
		position: absolute;
		inset: 0;
		pointer-events: none;
		opacity: 0.5;
		background-image: radial-gradient(
			circle,
			rgb(var(--av-primary-rgb) / 55%) 1px,
			transparent 1.2px
		);
		background-size: 11px 11px;
	}

	@supports (mask-image: url("")) or (-webkit-mask-image: url("")) {
		.av-statband__map {
			-webkit-mask-image: url("../img/worldmap.png");
			mask-image: url("../img/worldmap.png");
			-webkit-mask-size: cover;
			mask-size: cover;
			-webkit-mask-repeat: no-repeat;
			mask-repeat: no-repeat;
			-webkit-mask-position: center;
			mask-position: center;
		}
	}

	/* Without mask support the bare dot grid would tile the whole panel edge to
	   edge, which reads as noise rather than a map. Drop it instead. */
	@supports not ((mask-image: url("")) or (-webkit-mask-image: url(""))) {
		.av-statband__map { display: none; }
	}

	.av-statband__body {
		position: relative; /* lifts the content above the map */
	}

	.av-statband__grid {
		display: grid;
		gap: clamp(1.75rem, 4vw, 2.5rem);
		grid-template-columns: 1fr;
	}

	/*
	 * Explicit column counts rather than auto-fit, because the hairline
	 * dividers below key off :nth-child() — with auto-fit the wrap point is
	 * unknown, so a divider would land at the start of a wrapped row.
	 */
	@media (min-width: 640px) {
		.av-statband__grid { grid-template-columns: repeat(2, 1fr); }
		.av-statband__grid > :not(:nth-child(2n + 1)) {
			border-inline-start: 1px solid rgb(var(--av-ink-rgb) / 9%);
			padding-inline-start: clamp(1.75rem, 4vw, 2.5rem);
		}
	}

	@media (min-width: 1024px) {
		.av-statband__grid { grid-template-columns: repeat(4, 1fr); }
		.av-statband__grid > :not(:nth-child(2n + 1)) {
			border-inline-start: 0;
			padding-inline-start: 0;
		}
		.av-statband__grid > :not(:nth-child(4n + 1)) {
			border-inline-start: 1px solid rgb(var(--av-ink-rgb) / 9%);
			padding-inline-start: clamp(1.75rem, 4vw, 2.5rem);
		}
	}

	.av-stat {
		display: flex;
		flex-direction: column;
		align-items: flex-start;
		gap: 0.4rem;
	}

	.av-stat--center { align-items: center; text-align: center; }

	.av-stat__v {
		font-family: var(--av-font-head);
		font-size: clamp(2.25rem, 4.5vw, 3.25rem);
		font-weight: 700;
		line-height: 1;
		/* --av-secondary, not --av-accent: the reference sets these numbers in
		   teal, which computes to ~2.2:1 on a panel this pale and fails even
		   the 3:1 large-text floor. Secondary keeps the lift at 4.5:1+. */
		color: var(--card-accent, var(--av-secondary));
		font-variant-numeric: tabular-nums;
	}

	.av-stat__l {
		font-family: var(--av-font-head);
		font-size: var(--av-fs-lg);
		font-weight: 600;
		line-height: 1.3;
		color: var(--card-fg, var(--av-ink));
	}

	.av-stat__t {
		margin: 0;
		font-size: var(--av-fs-sm);
		line-height: 1.55;
		color: var(--av-text);
		max-width: 32ch;
	}

	.av-card--dark .av-stat__v { color: var(--av-accent); }

	/*
	 * Badge card — a stat laid out as a row beside an icon, rather than stacked.
	 * Distinct enough from .av-stat to earn its own element names; it was being
	 * approximated with three inline styles before.
	 */
	.av-badge-card { gap: var(--av-gap-sm); }

	.av-badge-card__body {
		display: flex;
		flex-direction: column;
	}

	.av-badge-card__v {
		font-family: var(--av-font-head);
		font-size: var(--av-fs-h4);
		font-weight: 700;
		line-height: 1.2;
		color: var(--card-head);
		font-variant-numeric: tabular-nums;
	}

	.av-badge-card__l {
		font-size: var(--av-fs-xs);
		color: var(--av-text-subtle);
	}

	/* ------------------------------------------------------ 6. TIMELINE */

	.av-timeline {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: column;
		gap: var(--av-gap-lg);
		position: relative;
	}

	/* The spine. */
	.av-timeline::before {
		content: "";
		position: absolute;
		inset-block: 0.75rem 0.75rem;
		inset-inline-start: 11px;
		width: 2px;
		background: linear-gradient(
			to bottom,
			var(--av-primary) 0%,
			rgb(var(--av-primary-rgb) / 15%) 100%
		);
		border-radius: 2px;
	}

	.av-timeline__item {
		position: relative;
		padding-inline-start: 3rem;
		display: flex;
		flex-direction: column;
		gap: 0.35rem;
	}

	.av-timeline__marker {
		position: absolute;
		inset-inline-start: 0;
		top: 0.25rem;
		width: 24px;
		height: 24px;
		border-radius: 50%;
		background: var(--av-surface);
		border: 2px solid var(--av-primary);
		display: grid;
		place-items: center;
		color: var(--av-primary);
	}

	.av-timeline__marker::after {
		content: "";
		width: 8px;
		height: 8px;
		border-radius: 50%;
		background: var(--av-primary);
	}

	.av-timeline__year {
		font-family: var(--av-font-head);
		font-size: var(--av-fs-sm);
		font-weight: 700;
		color: var(--av-primary);
		font-variant-numeric: tabular-nums;
	}

	.av-timeline__title { font-size: 1.0625rem; }

	.av-timeline__text {
		font-size: var(--av-fs-sm);
		color: var(--av-text-muted);
	}

	.av-section--dark .av-timeline__marker {
		background: var(--av-deep);
		border-color: var(--av-accent);
	}

	.av-section--dark .av-timeline__marker::after { background: var(--av-accent); }
	.av-section--dark .av-timeline__year { color: var(--av-accent); }
	.av-section--dark .av-timeline__text { color: var(--av-on-dark-muted); }

	/* ------------------------------------------------- 7. IMAGE + TEXT */

	.av-mediatext__media {
		margin: 0;
		border-radius: var(--av-radius-img);
		overflow: hidden;
		position: relative;
	}

	.av-mediatext__img {
		width: 100%;
		aspect-ratio: 4 / 3;
		object-fit: cover;
	}

	/* ------------------------------------------------------ WHO WE ARE */

	/* The values row is a second band under the lead, not a continuation of the
	   right-hand column — it needs the gap that says so. */
	.av-about__values { margin-block-start: var(--av-gap-lg); }

	/* In the lead band the heading sits beside the image, so top-align the two
	   rather than centring: a short heading floating against a tall image is the
	   imbalance this section had in the first place. */
	.av-about__lead { align-items: start; }

	.av-about__media { align-self: stretch; }

	.av-about__media .av-mediatext__img {
		height: 100%;
		aspect-ratio: 5 / 4;
	}

	@media (max-width: 900px) {
		.av-about__media .av-mediatext__img {
			height: auto;
			aspect-ratio: 4 / 3;
		}
	}

	.av-mediatext__content {
		display: flex;
		flex-direction: column;
		gap: var(--av-gap-sm);
		align-items: flex-start;
	}

	.av-mediatext__text { color: var(--av-text-muted); }

	/* ------------------------------------------- 8. CERTIFICATION CARD */

	.av-cert {
		flex-direction: row;
		align-items: center;
		justify-content: flex-start;
		gap: 0.85rem;
		padding: 0.95rem 1.15rem;
		position: relative;
		overflow: hidden;
		transition:
			transform var(--av-dur) var(--av-ease-out),
			box-shadow var(--av-dur) var(--av-ease-out),
			border-color var(--av-dur) var(--av-ease);
	}

	/* A brand wash that wipes in from the seal side on hover. */
	.av-cert::before {
		content: "";
		position: absolute;
		inset: 0;
		pointer-events: none;
		opacity: 0;
		background: linear-gradient(100deg, rgb(var(--av-primary-rgb) / 9%) 0%, transparent 62%);
		transition: opacity var(--av-dur) var(--av-ease);
	}

	.av-cert:hover {
		transform: translateY(-3px);
		box-shadow: var(--av-shadow-lg);
		border-color: rgb(var(--av-primary-rgb) / 32%);
	}

	.av-cert:hover::before { opacity: 1; }

	.av-cert__seal {
		flex: 0 0 auto;
		display: grid;
		place-items: center;
		color: var(--av-success);
		transition: color var(--av-dur) var(--av-ease), transform var(--av-dur-slow) var(--av-ease-spring);
	}

	.av-cert:hover .av-cert__seal {
		color: var(--av-primary);
		transform: rotate(-8deg) scale(1.06);
	}

	.av-seal { display: block; }

	.av-card--dark .av-cert__seal { color: var(--av-accent); }

	.av-cert__body {
		display: flex;
		flex-direction: column;
		min-width: 0;
		position: relative; /* above the ::before wash */
	}

	.av-cert__name {
		font-family: var(--av-font-head);
		font-weight: 700;
		font-size: 0.9375rem;
		color: var(--card-head);
		letter-spacing: -0.01em;
	}

	.av-cert__note {
		font-size: var(--av-fs-xs);
		color: var(--av-text-subtle);
	}

	.av-card--dark .av-cert__note { color: var(--av-on-dark-subtle); }

	/*
	 * Section grounds are left to the plain --muted / --ground alternation.
	 *
	 * A dot grid plus two brand washes was tried here and on the offerings, why
	 * and stat sections. Stacked across four consecutive sections it read as
	 * noise, and it competed with the cards it was meant to support. The
	 * separation those blocks needed came from card contrast, elevation and
	 * hover — not from patterning the page behind them.
	 */
	.av-excellence {
		position: relative;
		background-color: var(--av-muted);
	}

	/* ---------------------------------------------------- 9. INDUSTRY CARD */

	.av-industry__title { font-size: 1rem; }

	.av-industry__text {
		font-size: var(--av-fs-sm);
		color: var(--card-fg);
	}

	/* ------------------------------------------------ 10. TESTIMONIAL CARD */

	/*
	 * The testimonial block used to read as dead: white cards on a near-white
	 * ground, hairline details, and — because every card is forced to the height
	 * of the longest quote — a void between a short quote and its author. The
	 * cure is contrast and scale, not more elements: lift the card off the
	 * ground, set the quote at reading size, and let an oversized quote glyph
	 * occupy the space the text does not.
	 */
	.av-quote {
		gap: var(--av-gap-sm);
		position: relative;
		overflow: hidden;
		box-shadow: var(--av-shadow-md);
		transition:
			transform var(--av-dur) var(--av-ease-out),
			box-shadow var(--av-dur) var(--av-ease-out),
			border-color var(--av-dur) var(--av-ease);
	}

	.av-quote:hover {
		transform: translateY(var(--av-lift));
		box-shadow: var(--av-shadow-lg);
		border-color: rgb(var(--av-primary-rgb) / 28%);
	}

	/* Watermark: fills the dead space a short quote leaves behind. */
	.av-quote::after {
		content: "";
		position: absolute;
		right: -0.35em;
		bottom: -0.55em;
		width: 8rem;
		height: 8rem;
		pointer-events: none;
		background: radial-gradient(circle at 70% 70%, rgb(var(--av-primary-rgb) / 7%) 0%, transparent 68%);
	}

	.av-quote__mark {
		color: rgb(var(--av-primary-rgb) / 20%);
		align-self: flex-start;
	}

	.av-quote__mark .av-icon {
		width: 34px;
		height: 34px;
	}

	.av-card--dark .av-quote__mark { color: rgb(var(--av-accent-rgb) / 35%); }

	.av-stars {
		display: inline-flex;
		gap: 0.15rem;
		color: var(--av-star);
		font-size: 1.0625rem;
		line-height: 1;
	}

	/*
	 * Every card is as tall as the longest quote in the row, so a short quote
	 * has slack. Centring the text in that slack splits it above and below
	 * instead of dumping it all between the quote and the author, which is what
	 * made the shorter cards look half-empty.
	 */
	.av-quote__text {
		margin: 0;
		font-size: 1.0625rem;
		line-height: 1.68;
		color: var(--av-ink);
		flex: 1;
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: 0.7em;
	}

	.av-card--dark .av-quote__text { color: var(--card-fg); }

	/* A single feature quote — the leadership message, not a grid item. */
	.av-quote--lead .av-quote__text { font-size: var(--av-fs-lg); }

	.av-quote__text p { margin: 0; }

	.av-quote__by {
		display: flex;
		align-items: center;
		gap: 0.75rem;
		border-top: 1px solid var(--card-bd);
		padding-block-start: var(--av-gap-sm);
	}

	/* Initial-avatar — a named face without a stock photo of a stranger.
	   A solid gradient chip rather than a 10% tint: at 10% it disappeared into
	   the card and the author line had no anchor. */
	.av-quote__avatar {
		flex: 0 0 auto;
		width: 48px;
		height: 48px;
		border-radius: 50%;
		display: grid;
		place-items: center;
		background: linear-gradient(135deg, var(--av-primary) 0%, var(--av-secondary) 100%);
		color: var(--av-on-primary);
		font-family: var(--av-font-head);
		font-weight: 700;
		font-size: 1.125rem;
		line-height: 1;
		box-shadow: 0 2px 8px rgb(var(--av-primary-rgb) / 30%);
	}

	.av-card--dark .av-quote__avatar {
		background: rgb(255 255 255 / 12%);
		color: var(--av-accent);
	}

	.av-quote__id {
		display: flex;
		flex-direction: column;
		gap: 0.1rem;
		min-width: 0;
	}

	.av-quote__name {
		font-family: var(--av-font-head);
		font-weight: 700;
		font-size: 0.9375rem;
		color: var(--card-head);
	}

	.av-quote__meta {
		font-size: var(--av-fs-sm);
		color: var(--av-text-muted);
	}

	.av-card--dark .av-quote__meta { color: var(--av-on-dark-subtle); }

	/* Testimonial card as a slide: full height so a row lines up, mark up top. */
	.av-quote--card {
		height: 100%;
		--card-pad: clamp(1.6rem, 3vw, 2.1rem);
	}

	/*
	 * Section ground. Flat --muted behind white cards gave almost no separation,
	 * which is most of why the block read as inert. A soft brand wash plus the
	 * cards' own shadow gives the white something to sit on.
	 */
	.av-quotesec {
		background:
			radial-gradient(70% 60% at 15% 0%, rgb(var(--av-primary-rgb) / 7%) 0%, transparent 60%),
			radial-gradient(60% 60% at 90% 100%, rgb(var(--av-accent-rgb) / 8%) 0%, transparent 62%),
			var(--av-muted);
	}

	/* ------------------------------------------------------ SLIDER (carousel)
	 * A scroll-snap track: usable as a plain horizontal scroller before ui.js
	 * runs and without JS at all. ui.js adds arrows, dots and autoplay.
	 * ------------------------------------------------------------------------ */
	.av-slider { margin-block-start: var(--av-gap-lg); }

	.av-slider__track {
		list-style: none;
		margin: 0;
		padding: 0.25rem 0.25rem 0.75rem;
		display: flex;
		gap: var(--av-gap);
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		scroll-behavior: smooth;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}

	.av-slider__track::-webkit-scrollbar { display: none; }

	.av-slider__track:focus-visible {
		outline: 2px solid var(--av-primary);
		outline-offset: 3px;
		border-radius: var(--av-radius-sm);
	}

	.av-slider__slide {
		flex: 0 0 100%;
		scroll-snap-align: start;
	}

	@media (min-width: 640px) {
		.av-slider__slide { flex-basis: calc(50% - var(--av-gap) / 2); }
	}

	@media (min-width: 1000px) {
		.av-slider__slide { flex-basis: calc((100% - var(--av-gap) * 2) / 3); }
	}

	/* A single quote renders centred, not stretched across a 3-up track. */
	.av-slider--static .av-slider__track { justify-content: center; }
	.av-slider--static .av-slider__slide { flex-basis: min(100%, 520px); }

	.av-slider__nav {
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 1.25rem;
		margin-block-start: var(--av-gap);
	}

	.av-slider__arrow {
		flex: 0 0 auto;
		width: 48px;
		height: 48px;
		display: grid;
		place-items: center;
		border-radius: var(--av-radius-pill);
		border: 1px solid var(--av-border);
		background: var(--av-surface);
		color: var(--av-primary);
		cursor: pointer;
		box-shadow: var(--av-shadow-sm);
		transition:
			background-color var(--av-dur) var(--av-ease),
			border-color var(--av-dur) var(--av-ease),
			color var(--av-dur) var(--av-ease),
			box-shadow var(--av-dur) var(--av-ease),
			transform var(--av-dur) var(--av-ease-out),
			opacity var(--av-dur) var(--av-ease);
	}

	.av-slider__arrow:hover {
		background: var(--av-primary);
		border-color: var(--av-primary);
		color: var(--av-on-primary);
		box-shadow: var(--av-shadow-md);
		transform: translateY(-2px);
	}

	.av-slider__arrow:disabled {
		opacity: 0.35;
		cursor: default;
		background: var(--av-surface);
		border-color: var(--av-border);
		color: var(--av-primary);
	}

	/* One shared arrow glyph; the previous button points the other way. */
	[data-av-slider-prev] .av-icon { transform: rotate(180deg); }

	.av-slider__dots {
		display: flex;
		align-items: center;
		gap: 0.5rem;
	}

	.av-slider__dot {
		width: 9px;
		height: 9px;
		padding: 0;
		border: 0;
		border-radius: var(--av-radius-pill);
		background: rgb(var(--av-deep-rgb) / 18%);
		cursor: pointer;
		transition: background-color var(--av-dur) var(--av-ease), width var(--av-dur) var(--av-ease);
	}

	.av-slider__dot:hover { background: rgb(var(--av-primary-rgb) / 45%); }

	.av-slider__dot.is-active {
		width: 28px;
		background: var(--av-primary);
	}

	/* ------------------------------------------------------ 11. CTA BANNER */

	/*
	 * Same vivid ramp as the hero.
	 *
	 * This panel was left on the old --av-deep → --av-deep-2 gradient, which
	 * starts at #0F172A — almost black. That is precisely what made the hero
	 * read heavy and lifeless before it was changed, and the brand blue never
	 * actually appeared. The closing call to action is the last thing a visitor
	 * sees; it should not be the dullest block on the page.
	 */
	.av-cta__panel {
		position: relative;
		border-radius: var(--av-radius-card);
		padding: clamp(2.75rem, 6vw, 4.25rem) clamp(1.5rem, 4vw, 3rem);
		background:
			radial-gradient(60% 100% at 88% 90%, rgb(var(--av-accent-rgb) / 32%) 0%, transparent 62%),
			radial-gradient(55% 90% at 10% 0%, rgb(255 255 255 / 14%) 0%, transparent 58%),
			linear-gradient(125deg, var(--av-primary-hover) 0%, var(--av-primary) 45%, var(--av-secondary) 100%);
		color: var(--av-on-dark-muted);
		display: flex;
		flex-direction: column;
		align-items: center;
		text-align: center;
		gap: var(--av-gap-sm);
		overflow: hidden;
	}

	/* This button is the whole purpose of the section — size it that way. */
	.av-cta__actions .av-btn {
		padding: 1rem 1.9rem;
		font-size: 1rem;
	}

	.av-cta__title {
		color: var(--av-on-dark);
		max-width: 24ch;
	}

	.av-cta__text {
		max-width: 58ch;
		color: var(--av-on-dark-muted);
		font-size: 0.9375rem;
	}

	.av-cta__actions {
		display: flex;
		flex-wrap: wrap;
		gap: var(--av-gap-sm);
		justify-content: center;
		margin-block-start: 0.75rem;
	}

	/* Split variant — copy left, actions right. */
	.av-cta__panel--split {
		flex-direction: row;
		align-items: center;
		text-align: start;
		justify-content: space-between;
		gap: var(--av-gap);
	}

	@media (max-width: 820px) {
		.av-cta__panel--split {
			flex-direction: column;
			text-align: center;
		}
	}

	/* --------------------------------------------------- 12. FAQ ACCORDION */

	.av-faqs {
		display: flex;
		flex-direction: column;
		gap: 0.6rem;
	}

	.av-faq {
		border: 1px solid var(--av-border);
		border-radius: var(--av-radius-sm);
		background: var(--av-surface);
		overflow: hidden;
		transition: border-color var(--av-dur) var(--av-ease);
	}

	.av-faq[open] {
		border-color: rgb(var(--av-primary-rgb) / 30%);
		box-shadow: var(--av-shadow-sm);
	}

	.av-faq__q {
		cursor: pointer;
		padding: 1.1rem 1.25rem;
		font-family: var(--av-font-head);
		font-weight: 600;
		font-size: 0.9375rem;
		color: var(--av-ink);
		list-style: none;
		display: flex;
		justify-content: space-between;
		align-items: center;
		gap: 1rem;
	}

	.av-faq__q::-webkit-details-marker { display: none; }

	.av-faq__q::after {
		content: "+";
		font-size: 1.35rem;
		font-weight: 400;
		line-height: 1;
		color: var(--av-primary);
		flex: 0 0 auto;
		transition: transform var(--av-dur) var(--av-ease);
	}

	.av-faq[open] .av-faq__q::after { transform: rotate(45deg); }
	.av-faq__q:hover { background: var(--av-muted); }

	.av-faq__a {
		padding: 0 1.25rem 1.25rem;
		color: var(--av-text-muted);
		font-size: 0.9375rem;
	}

	/*
	 * Smooth open as progressive enhancement. Browsers without
	 * ::details-content get an instant, fully working accordion — no JS,
	 * no height measuring.
	 */
	@supports (interpolate-size: allow-keywords) {

		:root { interpolate-size: allow-keywords; }

		.av-faq::details-content {
			height: 0;
			overflow: hidden;
			transition:
				height var(--av-dur-slow) var(--av-ease),
				content-visibility var(--av-dur-slow) allow-discrete;
		}

		.av-faq[open]::details-content { height: auto; }
	}

	/* --------------------------------------------------- 17. ICON LIST */

	.av-iconlist {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: column;
		gap: 0.6rem;
		width: 100%;
	}

	.av-iconlist__item {
		display: flex;
		align-items: flex-start;
		gap: 0.6rem;
		font-size: 0.9375rem;
	}

	.av-iconlist__icon {
		color: var(--av-success);
		margin-block-start: 0.15rem;
	}

	.av-section--dark .av-iconlist__icon { color: var(--av-accent); }

	.av-iconlist--two {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: 0.6rem 1.5rem;
	}

	@media (max-width: 560px) {
		.av-iconlist--two { grid-template-columns: 1fr; }
	}

	.av-iconlist--boxed {
		gap: 0.6rem;
	}

	.av-iconlist--boxed .av-iconlist__item {
		background: var(--av-muted);
		border: 1px solid var(--av-border);
		border-radius: var(--av-radius-sm);
		padding: 0.85rem 1rem;
		align-items: center;
	}

	.av-iconlist--inline {
		flex-direction: row;
		flex-wrap: wrap;
		gap: 0.5rem 1.5rem;
	}

	/* ---------------------------------------------------- 15. COUNTRY CARD */

	.av-country {
		flex-direction: row;
		align-items: center;
		gap: 0.75rem;
		padding: 0.85rem 1rem;
	}

	.av-country__name {
		font-family: var(--av-font-head);
		font-weight: 600;
		font-size: 0.9rem;
		color: var(--card-head);
	}

	.av-country__note {
		font-size: var(--av-fs-xs);
		color: var(--av-text-subtle);
	}

	/* Chip variant — the compact region list. */
	.av-chips {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-wrap: wrap;
		gap: 0.5rem;
	}

	.av-chip {
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		border: 1px solid var(--av-border);
		border-radius: var(--av-radius-pill);
		padding: 0.4rem 0.9rem;
		font-size: var(--av-fs-sm);
		font-weight: 500;
		color: var(--av-text);
		background: var(--av-surface);
	}

	.av-section--dark .av-chip {
		background: rgb(255 255 255 / 7%);
		border-color: var(--av-on-dark-border);
		color: var(--av-on-dark-muted);
	}

	.av-chip__flag {
		display: inline-flex;
		align-items: center;
		flex: 0 0 auto;
		line-height: 1;
	}

	/* Drawn flags get a hairline + rounding so a white field (e.g. Japan) still
	   reads as a flag rather than a gap. */
	.av-flag {
		display: block;
		border-radius: 2px;
		box-shadow: 0 0 0 1px rgb(var(--av-deep-rgb) / 12%);
	}

	/* Region chips that link to the map are interactive. */
	.av-chip[data-region] {
		cursor: default;
		transition:
			border-color var(--av-dur) var(--av-ease),
			color var(--av-dur) var(--av-ease),
			background-color var(--av-dur) var(--av-ease),
			transform var(--av-dur) var(--av-ease);
	}

	.av-chip[data-region]:hover,
	.av-chip[data-region]:focus-visible,
	.av-chip[data-region].is-active {
		border-color: var(--av-primary);
		color: var(--av-primary);
		transform: translateY(-1px);
		outline: none;
	}

	/* ------------------------------------------------ GLOBAL PRESENCE MAP */

	.av-presence__map {
		display: flex;
		align-items: center;
	}

	.av-worldmap {
		position: relative;
		width: 100%;
		aspect-ratio: 1475 / 806;
	}

	/*
	 * The map artwork is an ALPHA MASK, not a picture: the dots are opacity and
	 * the paint is --av-primary. That is what lets a raster map follow the brand
	 * colour from settings instead of being locked to the source file's teal.
	 *
	 * Wrapped in @supports so a browser without mask-image gets nothing rather
	 * than a solid primary-coloured slab where the map should be.
	 */
	.av-worldmap__plate {
		position: absolute;
		inset: 0;
	}

	@supports (mask-image: url("")) or (-webkit-mask-image: url("")) {
		.av-worldmap__plate {
			background-color: var(--av-primary);
			opacity: 0.55;
			-webkit-mask-image: url("../img/worldmap.png");
			mask-image: url("../img/worldmap.png");
			-webkit-mask-size: contain;
			mask-size: contain;
			-webkit-mask-repeat: no-repeat;
			mask-repeat: no-repeat;
			-webkit-mask-position: center;
			mask-position: center;
		}
	}

	/*
	 * Served-region pins. The source art has no per-country geometry, so a
	 * region is marked rather than tinted. Positions are per-continent classes
	 * — the continent set is fixed, so they belong in CSS, and that keeps the
	 * template free of inline styles.
	 */
	.av-worldmap__pin {
		position: absolute;
		width: 14px;
		height: 14px;
		transform: translate(-50%, -50%);
	}

	.av-worldmap__pin--north-america { left: 19%; top: 37%; }
	.av-worldmap__pin--south-america { left: 30%; top: 71%; }
	.av-worldmap__pin--europe        { left: 54%; top: 26%; }
	.av-worldmap__pin--africa        { left: 52%; top: 68%; }
	.av-worldmap__pin--asia          { left: 76%; top: 40%; }
	.av-worldmap__pin--oceania       { left: 83%; top: 86%; }

	.av-worldmap__dot,
	.av-worldmap__pulse {
		position: absolute;
		inset: 0;
		border-radius: 50%;
	}

	.av-worldmap__dot {
		background: var(--av-primary);
		box-shadow: 0 0 0 3px rgb(var(--av-surface-rgb) / 85%);
		transition: background-color var(--av-dur) var(--av-ease);
	}

	.av-worldmap__pulse {
		background: rgb(var(--av-primary-rgb) / 45%);
		animation: av-ping 2.4s var(--av-ease-out) infinite;
	}

	.av-worldmap__pin.is-active .av-worldmap__pulse {
		background: rgb(var(--av-accent-rgb) / 55%);
		animation-duration: 1.1s;
	}

	.av-worldmap__pin.is-active .av-worldmap__dot { background: var(--av-accent); }

	@keyframes av-ping {
		0%   { transform: scale(0.6); opacity: 0.9; }
		70%  { transform: scale(2.8); opacity: 0; }
		100% { transform: scale(2.8); opacity: 0; }
	}

	@media (prefers-reduced-motion: reduce) {
		.av-worldmap__pulse { animation: none; opacity: 0.5; }
	}

	@media (max-width: 640px) {
		.av-presence__map { margin-block-start: var(--av-gap); }
	}

	/* -------------------------------------------------- 16. DOWNLOAD CARD */

	.av-download {
		flex-direction: row;
		align-items: center;
		gap: var(--av-gap-sm);
	}

	.av-download__body {
		display: flex;
		flex-direction: column;
		flex: 1;
		min-width: 0;
	}

	.av-download__title {
		font-family: var(--av-font-head);
		font-weight: 600;
		font-size: 0.9375rem;
		color: var(--card-head);
	}

	.av-download__meta {
		font-size: var(--av-fs-xs);
		color: var(--av-text-subtle);
		text-transform: uppercase;
		letter-spacing: 0.06em;
	}

	.av-download__action {
		color: var(--av-primary);
		flex: 0 0 auto;
	}

	/* ---------------------------------------------------- 18. LOGO SLIDER */

	.av-logos {
		--logo-h: 40px;
		position: relative;
		overflow: hidden;
		/* Fade the edges so the loop has no hard cut. */
		mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
	}

	.av-logos__track {
		display: flex;
		align-items: center;
		gap: clamp(2rem, 5vw, 4rem);
		width: max-content;
		animation: av-marquee 40s linear infinite;
	}

	.av-logos:hover .av-logos__track { animation-play-state: paused; }

	@media (prefers-reduced-motion: reduce) {
		.av-logos__track { animation: none; }
		.av-logos { mask-image: none; }
	}

	.av-logos--static .av-logos__track {
		animation: none;
		width: auto;
		flex-wrap: wrap;
		justify-content: center;
	}

	.av-logos__item {
		flex: 0 0 auto;
		height: var(--logo-h);
		display: grid;
		place-items: center;
	}

	.av-logos__img {
		max-height: var(--logo-h);
		width: auto;
		/* Desaturate so mismatched client logos read as one row. */
		filter: grayscale(1);
		opacity: 0.55;
		transition: filter var(--av-dur) var(--av-ease), opacity var(--av-dur) var(--av-ease);
	}

	.av-logos__item:hover .av-logos__img {
		filter: grayscale(0);
		opacity: 1;
	}

	@keyframes av-marquee {
		from { transform: translateX(0); }
		to { transform: translateX(-50%); }
	}

	/* ------------------------------------------------------ 19. BREADCRUMB */

	/*
	 * Breadcrumbs. A chevron separator reads as direction where a slash reads as
	 * punctuation, and the trail gets a little more presence: links respond, and
	 * the current page is the one item in full ink so you can see where you are
	 * at a glance.
	 */
	.av-crumbs ol {
		list-style: none;
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: 0.4rem;
		margin: 0 0 0.9rem;
		padding: 0;
		font-size: var(--av-fs-sm);
		color: var(--av-text-subtle);
	}

	.av-crumbs li {
		display: flex;
		align-items: center;
		gap: 0.4rem;
	}

	.av-crumbs li + li::before {
		content: "";
		width: 5px;
		height: 5px;
		border-inline-end: 1.5px solid currentColor;
		border-block-start: 1.5px solid currentColor;
		transform: rotate(45deg);
		opacity: 0.45;
	}

	.av-crumbs a {
		color: var(--av-text-muted);
		border-radius: var(--av-radius-sm);
		padding: 0.1rem 0.25rem;
		transition: color var(--av-dur) var(--av-ease), background-color var(--av-dur) var(--av-ease);
	}

	.av-crumbs a:hover {
		color: var(--av-primary);
		background: rgb(var(--av-primary-rgb) / 8%);
	}

	/* The current page — last item, not a link. */
	.av-crumbs li:last-child {
		color: var(--av-ink);
		font-weight: 600;
	}

	.av-crumbs a {
		color: inherit;
		text-decoration: none;
	}

	/* Colour shift only — a breadcrumb's position already says it is a link. */
	.av-crumbs a:hover {
		color: var(--av-primary);
	}

	.av-crumbs [aria-current="page"] { color: var(--av-text); }

	/* ----------------------------------------------------- 20. EMPTY STATE */

	.av-empty {
		display: flex;
		flex-direction: column;
		align-items: center;
		text-align: center;
		gap: 0.6rem;
		padding: clamp(2rem, 5vw, 3.5rem) 1.5rem;
		color: var(--av-text-subtle);
	}

	.av-empty__icon {
		color: rgb(var(--av-ink-rgb) / 20%);
		margin-block-end: 0.25rem;
	}

	.av-empty__title {
		font-size: 1rem;
		color: var(--av-ink);
	}

	.av-empty__text { font-size: var(--av-fs-sm); max-width: 46ch; }

	/* Admin-only variant: dashed, so it reads as scaffolding not content. */
	.av-empty--admin {
		border: 1px dashed var(--av-border);
		border-radius: var(--av-radius-card);
		background: var(--av-muted);
	}

	.av-empty__hint {
		font-size: var(--av-fs-xs);
		color: var(--av-text-subtle);
	}

	/* ------------------------------------------------ 21. PLACEHOLDER IMAGE
	 * Not decorative — 0 of 80 products have a photo, so this IS the catalogue's
	 * default state. Pure CSS: zero image requests, zero bytes.
	 * Reserves the same aspect ratio as the av-card image size (640x420), so
	 * dropping in real photography later causes no layout shift.
	 */

	.av-ph {
		position: relative;
		display: grid;
		place-items: center;
		gap: 0.5rem;
		aspect-ratio: 640 / 420;
		background: var(--av-muted);
		border-radius: var(--av-radius-img);
		overflow: hidden;
		color: rgb(var(--av-primary-rgb) / 35%);
	}

	/* Hairline diagonals — texture without an image. */
	.av-ph::before {
		content: "";
		position: absolute;
		inset: 0;
		background-image: repeating-linear-gradient(
			45deg,
			rgb(var(--av-primary-rgb) / 3%) 0 2px,
			transparent 2px 10px
		);
	}

	.av-ph__inner {
		position: relative;
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 0.5rem;
	}

	.av-ph__label {
		font-family: var(--av-font-head);
		font-size: var(--av-fs-xs);
		font-weight: 600;
		letter-spacing: 0.14em;
		text-transform: uppercase;
		/* Was ink/28% — 1.84:1, unreadable. A placeholder that looks broken is
		   worse than no placeholder, and this label is what tells the client
		   which image is missing. */
		color: var(--av-text-subtle);
	}

	.av-ph--square { aspect-ratio: 1; }
	.av-ph--wide { aspect-ratio: 16 / 9; }
	.av-ph--portrait { aspect-ratio: 3 / 4; }

	.av-ph--dark {
		background: rgb(255 255 255 / 5%);
		color: rgb(var(--av-accent-rgb) / 50%);
	}

	.av-ph--dark .av-ph__label { color: var(--av-on-dark-subtle); }

	/* ------------------------------------------------------ MAP (click-to-load)
	 * An embedded map iframe sets third-party cookies and sends the visitor's IP
	 * to Google before any consent, and costs ~800KB on a page whose job is a
	 * phone number. This is a link that looks like a map.
	 */

	.av-maplink {
		position: relative;
		display: block;
		border-radius: var(--av-radius-img);
		overflow: hidden;
		text-decoration: none;
		border: 1px solid var(--av-border);
		transition: border-color var(--av-dur) var(--av-ease);
	}

	.av-maplink:hover { border-color: rgb(var(--av-primary-rgb) / 40%); }

	/*
	 * Cap the map affordance's height.
	 *
	 * It inherits the 16:9 placeholder, which at container width renders about
	 * 1278x719 — 80% of the viewport for a grey panel holding a pin, an address
	 * and a button. A band is enough to read as "map, click me"; the height was
	 * pushing the rest of the contact page off the screen.
	 */
	.av-maplink .av-ph {
		aspect-ratio: auto;
		height: clamp(240px, 26vw, 360px);
	}

	.av-maplink__cta {
		position: absolute;
		inset-inline-end: 1rem;
		inset-block-end: 1rem;
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		padding: 0.6rem 1rem;
		border-radius: var(--av-radius-pill);
		background: var(--av-surface);
		color: var(--av-primary);
		font-family: var(--av-font-head);
		font-size: var(--av-fs-sm);
		font-weight: 600;
		box-shadow: var(--av-shadow-md);
	}

	/* ------------------------------------------- 13. PRODUCT & POST CARDS */
	/*
	 * Moved here from catalogue.css, which only loads on catalogue templates
	 * (av_is_catalogue() in inc/assets.php). Two places render these cards
	 * outside that condition and so got no styling at all:
	 *
	 *   - inc/patterns.php renders card/product in a block pattern, which can
	 *     be placed on any page — the "Selected APIs" grid;
	 *   - index.php renders card/post for the blog, and that card reuses
	 *     .av-pcard__media / .av-pcard__body.
	 *
	 * Unstyled, the spec <dl> fell back to the UA default — each value on its
	 * own line under a 40px indent — the body had no padding, and because the
	 * body never grew, the auto top margin on .av-more had nothing to push
	 * against, so CTAs sat wherever the copy happened to end.
	 *
	 * This is the shared component; catalogue.css keeps only the filter bar and
	 * other archive-specific furniture.
	 */

	.av-pcard {
		--card-pad: 0;
		overflow: hidden;
	}

	.av-pcard__media {
		position: relative;
		border-radius: 0;
	}

	.av-pcard__media img,
	.av-pcard__media .av-ph {
		width: 100%;
		aspect-ratio: 640 / 420;
		object-fit: cover;
		border-radius: 0;
	}

	/*
	 * Text-only card, when Catalogue → "Image on product cards" suppresses the
	 * media block.
	 *
	 * A thin brand rule along the top replaces the visual weight the image was
	 * carrying. Without it a grid of text cards reads as a table that has lost
	 * its lines — there is nothing to mark where one product ends and the next
	 * begins beyond a hairline border.
	 */
	.av-pcard--flat {
		border-block-start: 3px solid var(--av-primary);
	}

	/* The body's top padding was tuned to sit under an image; with the media
	   gone the title crowds the rule above it. */
	.av-pcard--flat .av-pcard__body {
		padding-block-start: 1.4rem;
	}

	.av-pcard__body {
		padding: 1.15rem 1.25rem 1.35rem;
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
		flex: 1;
		min-width: 0;
	}

	.av-pcard__title {
		font-size: 1rem;
		line-height: 1.3;
	}

	/* CAS is what B2B buyers scan for — give it monospace-like alignment. */
	.av-pcard__specs {
		margin: 0;
		display: flex;
		flex-direction: column;
		gap: 0.15rem;
		font-size: var(--av-fs-xs);
	}

	.av-pcard__spec {
		display: flex;
		gap: 0.4rem;
	}

	.av-pcard__spec dt {
		color: var(--av-text-subtle);
		font-weight: 600;
		flex: 0 0 auto;
		min-width: 2.4rem;
	}

	/* margin:0 kills the UA's default <dd> indent. */
	.av-pcard__spec dd {
		margin: 0;
		font-variant-numeric: tabular-nums;
		color: var(--av-text);
		overflow-wrap: anywhere;
	}

	.av-pcard__foot {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.5rem;
		margin-block-start: auto;
		padding-block-start: 0.6rem;
		border-top: 1px solid var(--av-border);
	}

	/* .av-more carries margin-block-start:auto for cards where it is the only
	   footer child; in this flex row that would push it off the baseline. */
	.av-pcard__foot .av-more { margin-block-start: 0; }

	/* Right-hand group: WhatsApp then Details, with the badge still pushed left
	   by the foot's space-between. */
	.av-pcard__actions {
		display: inline-flex;
		align-items: center;
		gap: 0.6rem;
	}

	/*
	 * WhatsApp enquiry on the card.
	 *
	 * position/z-index are load-bearing: the card title carries .av-card__link,
	 * whose ::after stretches over the whole card to make it one big click
	 * target. Without lifting above that overlay this link would be unclickable
	 * — the card would swallow every tap.
	 *
	 * 32px is below the 44px pointer-target guideline, which is a deliberate
	 * trade in a dense 4-across grid: it sits beside the always-available
	 * full-size button on the product page, and the whole card remains tappable
	 * for the primary action.
	 */
	.av-pcard__wa {
		position: relative;
		z-index: 1;
		display: grid;
		place-items: center;
		width: 32px;
		height: 32px;
		flex: none;
		border-radius: 50%;
		background: rgb(37 211 102 / 14%);
		color: #0b6b35;   /* 5.96:1 on the tint over a white card */
		transition:
			background-color var(--av-dur) var(--av-ease),
			color var(--av-dur) var(--av-ease);
	}

	.av-pcard__wa:hover,
	.av-pcard__wa:focus-visible {
		background: #25d366;
		color: #04310f;   /* 7.30:1 — same pair as the other WhatsApp controls */
	}

	/* Availability badge. */
	.av-badge {
		display: inline-flex;
		align-items: center;
		gap: 0.3rem;
		font-size: 0.6875rem;
		font-weight: 700;
		letter-spacing: 0.05em;
		text-transform: uppercase;
		padding: 0.25rem 0.6rem;
		border-radius: var(--av-radius-pill);
	}

	/*
	 * Brand green on its own 10% tint measures 3.13:1 — under AA, and this text
	 * is 11px, so the large-text allowance does not apply. Mixing toward ink
	 * lands ~4.9:1 and still reads as the same green.
	 */
	.av-badge.is-yes {
		background: rgb(var(--av-success-rgb) / 10%);
		color: color-mix(in srgb, var(--av-success) 72%, var(--av-ink));
	}

	.av-badge.is-no {
		background: rgb(var(--av-ink-rgb) / 7%);
		color: var(--av-text-subtle);
	}

	/* The post card reuses .av-service__text, whose vertical centring is meant
	   for short service blurbs and leaves a blog excerpt floating. */
	.av-card--bleed .av-service__text { justify-content: flex-start; }

	/* --------------------------------------------------------- BLOG SECTION */

	/*
	 * The grid itself is .av-u-grid, which auto-fits from three columns down to
	 * one — no breakpoints here on purpose.
	 *
	 * .av-u-stack aligns its children to flex-start. The CTA reads as a closing
	 * action so it centres under the grid, done on the element itself rather
	 * than with .av-u-stack--center, which would also force text-align:center
	 * onto every card in the row.
	 */
	.av-blogsec__more {
		align-self: center;
		margin: 0;
	}

	/* ------------------------------------------------------------- PERSON */

	.av-person { overflow: hidden; }

	.av-person .av-pcard__media img,
	.av-person .av-pcard__media .av-ph {
		aspect-ratio: 1;
		object-fit: cover;
		border-radius: 0;
	}

	/* ------------------------------------------------------------- PROCESS */

	/* Connect the steps on wide screens — reads as a flow, not four cards. */
	@media (min-width: 901px) {

		.av-process .av-u-grid { position: relative; }

		.av-process__step::after {
			content: "";
			position: absolute;
			top: calc(var(--av-card-pad) + 24px);
			inset-inline-start: 100%;
			width: var(--av-gap);
			height: 2px;
			background: repeating-linear-gradient(
				90deg,
				rgb(var(--av-primary-rgb) / 30%) 0 4px,
				transparent 4px 8px
			);
		}

		.av-process__step:last-child::after { display: none; }
	}
}

/* ============================================================================
   CHROME — topbar, header, footer
   ========================================================================== */

@layer components {

	/* ---------------------------------------------------------------- TOPBAR */

	/*
	 * Shimmer — a slow highlight sweeping across the deep bar.
	 *
	 * Done as a moving BACKGROUND rather than a sliding ::after element, and
	 * that choice is load-bearing: the element version needed `overflow: hidden`
	 * to stop the sheen spilling out of the bar, and that clipped the language
	 * switcher's dropdown — a 320px menu inside a 45px bar, so it opened and was
	 * entirely invisible. A background layer is confined to the box for free, so
	 * the bar can stay `overflow: visible` and the dropdown can escape it.
	 *
	 * Low alpha, behind the text, and off entirely under reduced motion.
	 */
	.av-topbar {
		background-color: var(--av-deep);
		background-image: linear-gradient(
			100deg,
			transparent 40%,
			rgb(255 255 255 / 7%) 48%,
			rgb(var(--av-accent-rgb) / 10%) 53%,
			transparent 61%
		);
		background-repeat: no-repeat;
		background-size: 260% 100%;
		color: var(--av-on-dark-muted);
		font-size: var(--av-fs-xs);
		position: relative;
		animation: av-sheen 7s var(--av-ease) infinite;
	}

	@keyframes av-sheen {
		0%   { background-position: 130% 0; }
		55%  { background-position: -30% 0; }
		100% { background-position: -30% 0; }
	}

	@media (prefers-reduced-motion: reduce) {
		.av-topbar { animation: none; background-image: none; }
	}

	.av-topbar__inner {
		display: flex;
		flex-wrap: wrap;
		gap: 0.3rem 1.5rem;
		align-items: center;
		justify-content: space-between;
		padding-block: 0.6rem;
	}

	.av-topbar__msg {
		display: inline-flex;
		align-items: center;
		gap: 0.5rem;
	}

	.av-topbar__contact {
		display: flex;
		flex-wrap: wrap;
		gap: 1.25rem;
	}

	.av-topbar a {
		color: inherit;
		text-decoration: none;
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		transition: color var(--av-dur) var(--av-ease);
	}

	.av-topbar a:hover { color: var(--av-on-dark); }
	.av-topbar .av-icon { opacity: 0.55; }

	@media (max-width: 860px) {
		.av-topbar__msg { display: none; }
		.av-topbar__inner { justify-content: center; }
	}

	/* ---------------------------------------------------------------- HEADER */

	.av-sentinel {
		position: absolute;
		height: 1px;
		width: 1px;
		pointer-events: none;
	}

	.av-header {
		background: var(--av-surface);
		border-bottom: 1px solid var(--av-border);
		z-index: var(--av-z-sticky);
		transition:
			box-shadow var(--av-dur) var(--av-ease),
			background-color var(--av-dur) var(--av-ease);
	}

	.av-header--sticky {
		position: sticky;
		top: 0;
	}

	.av-header.is-stuck {
		box-shadow: var(--av-shadow-md);
		background: rgb(var(--av-surface-rgb) / 92%);
		backdrop-filter: blur(10px);
	}

	.av-header__inner {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--av-gap);
		min-height: var(--av-header-h);
		transition: min-height var(--av-dur) var(--av-ease);
	}

	.av-header.is-stuck .av-header__inner { min-height: var(--av-header-h-stuck); }

	/* ------------------------------------------------ FLOATING HEADER STYLE
	 * Optional layout (Site Settings → Header → Header style): the bar becomes a
	 * dark rounded pill inset from the page edges rather than a full-width band.
	 * The chrome moves onto .av-header__inner; .av-header itself goes
	 * transparent so the pill appears to float over the page.
	 * -------------------------------------------------------------------- */

	.av-header--floating {
		background: transparent;
		border-bottom: 0;
		padding-block: 0.85rem;
	}

	.av-header--floating .av-header__inner {
		background: var(--av-deep);
		border-radius: var(--av-radius-pill);
		padding-inline: 1.25rem;
		min-height: 64px;
		box-shadow: var(--av-shadow-lg);
		transition:
			min-height var(--av-dur) var(--av-ease),
			background-color var(--av-dur) var(--av-ease);
	}

	/* Stuck: the pill tightens and frosts; the bar behind it stays transparent
	   so the full-width white sheet never reappears. */
	.av-header--floating.is-stuck {
		background: transparent;
		box-shadow: none;
		backdrop-filter: none;
	}

	.av-header--floating.is-stuck .av-header__inner {
		background: rgb(var(--av-deep-rgb) / 90%);
		backdrop-filter: blur(12px);
		min-height: 56px;
	}

	.av-header--floating .av-logo { color: var(--av-on-dark); }

	/*
	 * Colour inversion is DESKTOP ONLY, and that scope is load-bearing.
	 *
	 * Below 1025px the nav is a fixed drawer with a white (--av-surface)
	 * background. Inverting these links globally would paint white text on that
	 * white drawer and make the whole menu invisible — the same failure mode as
	 * the newsletter button. Above 1025px the links sit on the dark pill, which
	 * is the only place the inversion is correct.
	 */
	@media (min-width: 1025px) {

		.av-header--floating .av-menu a { color: var(--av-on-dark-muted); }

		.av-header--floating .av-menu > li > a:hover,
		.av-header--floating .av-menu > .current-menu-item > a,
		.av-header--floating .av-menu > .current-menu-ancestor > a { color: var(--av-on-dark); }

		.av-header--floating .av-menu > li > a::after { background: var(--av-accent); }

		/* The dropdown is still a light card, so its links keep ink. */
		.av-header--floating .av-menu .sub-menu a { color: var(--av-ink); }
		.av-header--floating .av-menu .sub-menu a:hover { color: var(--av-primary); }

		.av-header--floating .av-nav__quote { color: var(--av-on-dark-muted); }
		.av-header--floating .av-nav__quote:hover { color: var(--av-on-dark); }
	}

	/* The burger sits on the pill at every width, so it inverts unconditionally.
	   Its bars are all `background: currentColor`, so setting the colour on the
	   button covers the span and both pseudo-elements in one rule. */
	.av-header--floating .av-burger { color: var(--av-on-dark); }

	.av-logo {
		display: inline-flex;
		align-items: center;
		text-decoration: none;
		color: var(--av-ink);
		flex: 0 0 auto;
	}

	.av-logo__img {
		max-height: 44px;
		width: auto;
		transition: max-height var(--av-dur) var(--av-ease);
	}

	.av-header.is-stuck .av-logo__img { max-height: 36px; }

	.av-logo__text {
		font-family: var(--av-font-head);
		font-weight: 700;
		font-size: 1.15rem;
		letter-spacing: -0.02em;
	}

	/* --- primary nav --- */

	.av-nav {
		display: flex;
		align-items: center;
		gap: var(--av-gap-lg);
	}

	.av-menu {
		display: flex;
		align-items: center;
		gap: 1.75rem;
		list-style: none;
		margin: 0;
		padding: 0;
	}

	.av-menu > li { position: relative; }

	.av-menu a {
		color: var(--av-ink);
		text-decoration: none;
		font-family: var(--av-font-head);
		font-weight: 500;
		font-size: 0.9375rem;
		padding-block: 0.5rem;
		display: inline-flex;
		align-items: center;
		gap: 0.3rem;
		position: relative;
	}

	/* Underline grows from the centre on hover / when current. */
	.av-menu > li > a::after {
		content: "";
		position: absolute;
		left: 0;
		right: 0;
		bottom: 0;
		height: 2px;
		background: var(--av-primary);
		border-radius: 2px;
		transform: scaleX(0);
		transition: transform var(--av-dur) var(--av-ease-out);
	}

	.av-menu > li > a:hover::after,
	.av-menu > .current-menu-item > a::after,
	.av-menu > .current-menu-ancestor > a::after,
	.av-menu > .current-menu-parent > a::after {
		transform: scaleX(1);
	}

	.av-menu > li > a:hover,
	.av-menu > .current-menu-item > a { color: var(--av-primary); }

	/* --- mega menu --- */

	.av-menu .sub-menu {
		position: absolute;
		top: calc(100% + 0.5rem);
		left: 0;
		min-width: 260px;
		background: var(--av-surface);
		border: 1px solid var(--av-border);
		border-radius: var(--av-radius-sm);
		box-shadow: var(--av-shadow-lg);
		list-style: none;
		margin: 0;
		padding: 0.5rem;
		display: flex;
		flex-direction: column;
		z-index: var(--av-z-dropdown);

		/* Fade + lift. visibility keeps it out of the tab order when closed. */
		opacity: 0;
		visibility: hidden;
		transform: translateY(-6px);
		transition:
			opacity var(--av-dur) var(--av-ease),
			transform var(--av-dur) var(--av-ease-out),
			visibility var(--av-dur);
	}

	.av-menu li:hover > .sub-menu,
	.av-menu li:focus-within > .sub-menu,
	.av-menu li.is-open > .sub-menu {
		opacity: 1;
		visibility: visible;
		transform: translateY(0);
	}

	.av-menu .sub-menu a {
		padding: 0.6rem 0.8rem;
		border-radius: var(--av-radius-sm);
		display: block;
		font-weight: 500;
		width: 100%;
	}

	.av-menu .sub-menu a::after { display: none; }

	.av-menu .sub-menu a:hover {
		background: var(--av-muted);
		color: var(--av-primary);
	}

	.av-nav__actions {
		display: flex;
		align-items: center;
		gap: var(--av-gap-sm);
		flex: 0 0 auto;
	}

	.av-nav__quote {
		background: none;
		border: 0;
		font-family: var(--av-font-head);
		font-weight: 500;
		font-size: 0.9375rem;
		color: var(--av-ink);
		cursor: pointer;
		padding: 0.5rem 0;
		white-space: nowrap;
	}

	.av-nav__quote:hover { color: var(--av-primary); }

	.av-nav__empty a { font-size: var(--av-fs-sm); }

	/* --- burger --- */

	.av-burger {
		display: none;
		background: none;
		border: 0;
		padding: 0.6rem;
		cursor: pointer;
		color: var(--av-ink);
		order: 3;
	}

	.av-burger__box,
	.av-burger__box span,
	.av-burger__box::before,
	.av-burger__box::after {
		display: block;
		width: 24px;
		height: 2px;
		background: currentColor;
		border-radius: 2px;
		transition: transform var(--av-dur) var(--av-ease), opacity var(--av-dur) var(--av-ease);
	}

	.av-burger__box {
		position: relative;
		background: transparent;
	}

	.av-burger__box::before,
	.av-burger__box::after {
		content: "";
		position: absolute;
		left: 0;
	}

	.av-burger__box::before { top: -7px; }
	.av-burger__box::after { top: 7px; }

	.av-burger[aria-expanded="true"] .av-burger__box span { opacity: 0; }
	.av-burger[aria-expanded="true"] .av-burger__box::before { transform: translateY(7px) rotate(45deg); }
	.av-burger[aria-expanded="true"] .av-burger__box::after { transform: translateY(-7px) rotate(-45deg); }

	@media (max-width: 1024px) {

		.av-burger { display: block; }

		.av-nav {
			position: fixed;
			inset: 0 0 0 26%;
			background: var(--av-surface);
			flex-direction: column;
			align-items: stretch;
			justify-content: flex-start;
			gap: 0;
			padding: 5.5rem 1.5rem 2rem;
			transform: translateX(100%);
			transition: transform var(--av-dur-slow) var(--av-ease-out);
			overflow-y: auto;
			box-shadow: -20px 0 40px -20px rgb(var(--av-deep-rgb) / 30%);
			z-index: var(--av-z-overlay);
		}

		.av-nav.is-open { transform: translateX(0); }

		.av-menu {
			flex-direction: column;
			align-items: stretch;
			gap: 0;
		}

		.av-menu > li { border-bottom: 1px solid var(--av-border); }

		.av-menu a {
			padding: 0.9rem 0;
			font-size: 1rem;
		}

		.av-menu > li > a::after { display: none; }

		.av-menu .sub-menu {
			position: static;
			opacity: 1;
			visibility: visible;
			transform: none;
			border: 0;
			box-shadow: none;
			padding: 0 0 0.5rem 0.85rem;
			min-width: 0;
		}

		.av-nav__actions {
			flex-direction: column;
			align-items: stretch;
			margin-block-start: var(--av-gap);
			gap: var(--av-gap-sm);
		}

		.av-nav__actions .av-btn { width: 100%; }

		body.av-nav-open { overflow: hidden; }
	}

	@media (max-width: 560px) {
		.av-nav { inset: 0; }
	}

	/* ---------------------------------------------------------------- FOOTER */

	.av-footer {
		background: var(--av-footer);
		color: var(--av-on-footer-muted);
		margin-block-start: auto;
	}

	.av-footer__grid {
		display: grid;
		grid-template-columns: 1.6fr 1fr 1fr 1.2fr;
		gap: var(--av-gap-lg);
		padding-block: clamp(3rem, 6vw, 4.5rem);
	}

	@media (max-width: 1024px) {
		.av-footer__grid { grid-template-columns: 1fr 1fr; }
	}

	@media (max-width: 560px) {
		.av-footer__grid { grid-template-columns: 1fr; gap: var(--av-gap); }
	}

	.av-footer .av-logo,
	.av-footer .av-logo__text { color: var(--av-on-footer); }

	.av-footer__brand {
		display: flex;
		flex-direction: column;
		gap: var(--av-gap-sm);
		align-items: flex-start;
	}

	.av-footer__intro {
		font-size: var(--av-fs-sm);
		max-width: 42ch;
		color: var(--av-on-footer-subtle);
	}

	.av-footer__title {
		font-family: var(--av-font-head);
		font-size: var(--av-fs-xs);
		font-weight: 600;
		letter-spacing: 0.12em;
		text-transform: uppercase;
		color: var(--av-on-footer);
		margin-block-end: 1rem;
	}

	.av-footer__menu,
	.av-contact-list,
	/* Icon-only round buttons. These were always sized for an icon — the markup
	   was emitting the network's NAME as text, which then had to fit a 36px
	   circle. The brand marks come from Core\Brands; the name stays as
	   screen-reader text. */
	.av-social {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: row;
		flex-wrap: wrap;
		gap: 0.5rem;
	}

	.av-social a {
		display: grid;
		place-items: center;
		width: 38px;
		height: 38px;
		border-radius: 50%;
		background: var(--av-on-footer-surface);
		border: 1px solid var(--av-on-footer-border);
		color: var(--av-on-footer-muted);
		transition:
			background-color var(--av-dur) var(--av-ease),
			border-color var(--av-dur) var(--av-ease),
			color var(--av-dur) var(--av-ease),
			transform var(--av-dur) var(--av-ease-out);
	}

	.av-social a:hover {
		background: var(--av-primary);
		border-color: var(--av-primary);
		color: var(--av-on-primary);
		transform: translateY(-2px);
	}

	.av-footer a {
		color: inherit;
		text-decoration: none;
		font-size: var(--av-fs-sm);
		transition: color var(--av-dur) var(--av-ease);
	}

	.av-footer a:hover { color: var(--av-on-footer); }

	.av-footer__menu a:hover { padding-inline-start: 0.25rem; }

	.av-footer__menu a {
		transition: color var(--av-dur) var(--av-ease), padding var(--av-dur) var(--av-ease);
	}

	.av-contact-list li {
		display: flex;
		gap: 0.6rem;
		align-items: flex-start;
		font-size: var(--av-fs-sm);
	}

	.av-contact-list .av-icon {
		margin-block-start: 0.15rem;
		opacity: 0.45;
	}

	.av-footer__bar { border-top: 1px solid var(--av-on-footer-border); }

	.av-footer__bar-inner {
		display: flex;
		flex-wrap: wrap;
		gap: 0.75rem 1.5rem;
		align-items: center;
		justify-content: space-between;
		padding-block: 1.25rem;
		font-size: var(--av-fs-xs);
	}

	.av-legal__menu {
		list-style: none;
		display: flex;
		flex-wrap: wrap;
		gap: 1.5rem;
		margin: 0;
		padding: 0;
	}

	/* Certification strip in the footer. */
	.av-footer__certs {
		display: flex;
		flex-wrap: wrap;
		gap: 0.5rem;
		margin-block-start: 0.5rem;
	}

	.av-footer__cert {
		font-size: 0.6875rem;
		font-weight: 600;
		letter-spacing: 0.06em;
		text-transform: uppercase;
		padding: 0.25rem 0.6rem;
		border-radius: var(--av-radius-pill);
		background: var(--av-on-footer-surface);
		border: 1px solid var(--av-on-footer-border);
		color: var(--av-on-footer-muted);
	}

	/* -------------------------------------------------------------- WHATSAPP */
	/* WhatsApp green is fixed by WhatsApp's brand guidelines, not our palette. */

	.av-whatsapp {
		position: fixed;
		right: 18px;
		bottom: 18px;
		z-index: var(--av-z-sticky);
		display: inline-flex;
		align-items: center;
		gap: 0.5rem;
		background: #25d366;
		color: #04310f;
		border-radius: var(--av-radius-pill);
		padding: 0.75rem 1.15rem;
		font-family: var(--av-font-head);
		font-weight: 700;
		font-size: var(--av-fs-sm);
		text-decoration: none;
		box-shadow: var(--av-shadow-lg);
		transition: transform var(--av-dur) var(--av-ease);
	}

	.av-whatsapp:hover {
		color: #04310f;
		transform: translateY(-2px);
	}

	/* On mobile the sticky CTA bar takes the bottom; move the FAB up. */
	@media (max-width: 720px) {
		.av-whatsapp { bottom: 76px; }
	}

	/* ------------------------------------------------------- STICKY MOBILE CTA */

	.av-stickycta {
		display: none;
		position: fixed;
		inset-inline: 0;
		bottom: 0;
		z-index: var(--av-z-sticky);
		background: rgb(var(--av-surface-rgb) / 96%);
		backdrop-filter: blur(10px);
		border-top: 1px solid var(--av-border);
		padding: 0.6rem var(--av-gutter);
		gap: 0.5rem;
	}

	@media (max-width: 720px) {
		.av-stickycta { display: flex; }
		.av-stickycta .av-btn { flex: 1; }
		body { padding-block-end: 68px; }
	}
}

/* ============================================================================
   PLUGIN-OWNED MARKUP — modal + forms
   The theme styles HTML it does not emit. See the frozen list at the top.
   ========================================================================== */

@layer components {

	.av-modal {
		position: fixed;
		inset: 0;
		z-index: var(--av-z-modal);
		display: grid;
		place-items: center;
		padding: 1.25rem;
	}

	.av-modal[hidden] { display: none; }

	.av-modal__backdrop {
		position: absolute;
		inset: 0;
		background: var(--av-overlay);
		backdrop-filter: blur(3px);
		opacity: 0;
		transition: opacity var(--av-dur) var(--av-ease);
	}

	.av-modal.is-open .av-modal__backdrop { opacity: 1; }

	.av-modal__panel {
		position: relative;
		width: min(100%, 640px);
		max-height: 90vh;
		overflow-y: auto;
		background: var(--av-surface);
		border-radius: var(--av-radius-card);
		padding: clamp(1.5rem, 4vw, 2.5rem);
		box-shadow: var(--av-shadow-xl);
		transform: translateY(12px) scale(0.99);
		opacity: 0;
		transition:
			transform var(--av-dur-slow) var(--av-ease-out),
			opacity var(--av-dur-slow) var(--av-ease);
	}

	.av-modal.is-open .av-modal__panel {
		transform: none;
		opacity: 1;
	}

	/*
	 * Form panels are banded instead of being one scrolling box: fixed head,
	 * scrolling body, fixed foot. The old single-scroll panel pushed the heading
	 * out of view and left the submit button below the fold, so the visitor had
	 * to scroll a dialog to find out how to finish it.
	 *
	 * Padding moves to the bands so the scrollbar tracks the panel edge rather
	 * than floating inside a padded box.
	 */
	.av-modal__panel--form {
		display: flex;
		flex-direction: column;
		padding: 0;
		overflow: hidden;
		width: min(100%, 620px);
	}

	.av-modal__head {
		flex: none;
		padding: clamp(1.35rem, 3.5vw, 1.85rem) clamp(1.35rem, 4vw, 2.15rem) clamp(1rem, 2.5vw, 1.25rem);
		padding-inline-end: 3.75rem;   /* clears the close button */
		background: linear-gradient(180deg, rgb(var(--av-primary-rgb) / 6%), transparent);
		border-block-end: 1px solid var(--av-border-soft);
	}

	.av-modal__close {
		position: absolute;
		top: 0.85rem;
		right: 0.85rem;
		z-index: 1;
		display: grid;
		place-items: center;
		/* 40px is a comfortable pointer target; the bare glyph before this was
		   roughly 24px and easy to miss. */
		width: 40px;
		height: 40px;
		background: var(--av-surface);
		border: 1px solid var(--av-border);
		border-radius: 50%;
		cursor: pointer;
		color: var(--av-text-muted);
		padding: 0;
		transition:
			background-color var(--av-dur) var(--av-ease),
			border-color var(--av-dur) var(--av-ease),
			color var(--av-dur) var(--av-ease);
	}

	.av-modal__close:hover {
		color: var(--av-ink);
		background: var(--av-muted);
		border-color: var(--av-border-strong);
	}

	.av-modal__title {
		font-size: var(--av-fs-h3);
		padding-inline-end: 2rem;
	}

	.av-modal__panel--form .av-modal__title {
		padding-inline-end: 0;
		margin: 0;
	}

	.av-modal__intro {
		margin-block-start: 0.5rem;
		color: var(--av-text-muted);
		font-size: 0.9375rem;
	}

	.av-modal__panel--form .av-modal__intro {
		margin-block-start: 0.4rem;
		margin-block-end: 0;
		max-width: 46ch;
	}

	body.av-modal-open { overflow: hidden; }

	/* ------------------------------------------------------ 14. INQUIRY FORM */

	.av-form {
		display: flex;
		flex-direction: column;
		gap: var(--av-gap-sm);
		margin-block-start: var(--av-gap);
	}

	/* ---- banded layout, used by the inquiry modal ---- */

	/* min-height:0 is what actually lets the body scroll: without it the flex
	   item refuses to shrink below its content and the panel grows instead. */
	.av-modal__panel--form .av-form {
		flex: 1;
		min-height: 0;
		margin-block-start: 0;
		gap: 0;
	}

	.av-form__body {
		flex: 1;
		min-height: 0;
		overflow-y: auto;
		overscroll-behavior: contain;   /* stops the page scrolling once the body bottoms out */
		display: flex;
		flex-direction: column;
		gap: var(--av-gap-sm);
		padding: clamp(1.1rem, 3vw, 1.5rem) clamp(1.35rem, 4vw, 2.15rem);
	}

	.av-form__foot {
		flex: none;
		display: flex;
		flex-direction: column;
		gap: 0.75rem;
		padding: clamp(1rem, 2.5vw, 1.25rem) clamp(1.35rem, 4vw, 2.15rem);
		background: var(--av-ground);
		border-block-start: 1px solid var(--av-border-soft);
	}

	.av-form__grid {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: var(--av-gap-sm);
	}

	@media (max-width: 520px) {
		.av-form__grid { grid-template-columns: 1fr; }
	}

	.av-form__row {
		display: flex;
		flex-direction: column;
		gap: 0.35rem;
	}

	.av-form label {
		font-family: var(--av-font-head);
		font-size: var(--av-fs-sm);
		font-weight: 600;
		color: var(--av-ink);
	}

	.av-form input,
	.av-form select,
	.av-form textarea {
		width: 100%;
		padding: 0.75rem 0.9rem;
		border: 1px solid var(--av-border);
		border-radius: var(--av-radius-input);
		font-size: 0.9375rem;
		background: var(--av-surface);
		color: var(--av-ink);
		transition: border-color var(--av-dur) var(--av-ease), box-shadow var(--av-dur) var(--av-ease);
	}

	.av-form input::placeholder,
	.av-form textarea::placeholder { color: var(--av-text-subtle); }

	.av-form input:focus,
	.av-form select:focus,
	.av-form textarea:focus {
		border-color: var(--av-primary);
		box-shadow: var(--av-shadow-focus);
		outline: none;
	}

	/* Applied by inquiry.js on a 422. */
	.av-form .has-error {
		border-color: var(--av-danger);
		background: rgb(var(--av-danger-rgb) / 4%);
	}

	.av-form textarea {
		resize: vertical;
		min-height: 104px;
	}

	.av-form__consent {
		display: flex;
		gap: 0.6rem;
		align-items: flex-start;
	}

	.av-form__consent input {
		width: auto;
		margin-block-start: 0.3rem;
		flex: 0 0 auto;
	}

	.av-form__consent label {
		font-weight: 400;
		font-size: var(--av-fs-sm);
		line-height: 1.5;
		color: var(--av-text-muted);
	}

	/* Native selects: draw our own chevron so Country/Interest match the inputs
	   rather than showing the OS default arrow. */
	.av-form select {
		appearance: none;
		-webkit-appearance: none;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23475569' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
		background-repeat: no-repeat;
		background-position: right 0.85rem center;
		padding-inline-end: 2.4rem;
	}

	.av-form__submit {
		margin-block-start: 0.6rem;
		width: 100%;
		padding-block: 0.95rem;
		font-size: 1rem;
	}

	/* In the banded foot the button is already spaced by the flex gap. */
	.av-form__foot .av-form__submit { margin-block-start: 0; }

	/* The status line only occupies space once inquiry.js writes into it —
	   otherwise an empty <p> would leave a permanent gap above the button. */
	.av-form__foot .av-form__status:empty {
		display: none;
	}

	.av-form__foot .av-form__trust {
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
		gap: 0.4rem 1.1rem;
		margin: 0;
		padding: 0;
		list-style: none;
		font-size: var(--av-fs-xs);
		color: var(--av-text-muted);
	}

	.av-form__foot .av-form__trust li {
		display: inline-flex;
		align-items: center;
		gap: 0.35rem;
	}

	.av-form__foot .av-form__trust .av-icon { color: var(--av-success); }

	/* Required marker — the bare red asterisk read as an error character. */
	.av-form label span[aria-hidden="true"] {
		color: var(--av-danger);
		font-weight: 700;
	}

	/*
	 * Mobile: a bottom sheet rather than a floating card. A centred dialog on a
	 * short viewport leaves slivers of page above and below and puts the submit
	 * button mid-screen; anchoring to the bottom keeps it under the thumb.
	 */
	@media (max-width: 560px) {
		/* Scoped with :has() so this only reshapes the form dialog — a future
		   confirm/alert modal keeps the centred card. */
		.av-modal:has(.av-modal__panel--form) {
			padding: 0;
			place-items: end stretch;
		}

		.av-modal__panel--form {
			width: 100%;
			max-height: 92vh;
			border-radius: var(--av-radius-card) var(--av-radius-card) 0 0;
			transform: translateY(24px);
		}
	}

	.av-form__status {
		margin: 0;
		font-size: var(--av-fs-sm);
		min-height: 1.2em;
		color: var(--av-text-muted);
	}

	/* Trust row under the submit — quiet reassurance, not a second CTA. */
	.av-form__trust {
		list-style: none;
		margin: 0.35rem 0 0;
		padding: 0;
		display: flex;
		flex-wrap: wrap;
		gap: 0.4rem 1.25rem;
	}

	.av-form__trust li {
		display: flex;
		align-items: center;
		gap: 0.4rem;
		font-size: var(--av-fs-xs);
		color: var(--av-text-muted);
	}

	.av-form__trust .av-icon { color: var(--av-success); flex: 0 0 auto; }

	.av-form__status.is-error { color: var(--av-danger); }

	.av-form__done {
		text-align: center;
		padding: 2rem 0.5rem;
		font-size: 1rem;
		color: var(--av-success);
		font-weight: 600;
	}

	/*
	 * Honeypot. Visually hidden but NOT display:none — some bots read that as a
	 * tell, and it must stay in the DOM for the server-side check.
	 */
	.av-hp {
		position: absolute !important;
		left: -9999px;
		top: -9999px;
		height: 0;
		overflow: hidden;
	}

	/* ---------------------------------------------------------- NEWSLETTER */

	/*
	 * Mirrored version of the CTA ramp. This sat on deep-2 → deep, ending at
	 * near-black, and it renders directly beneath the CTA panel — so the page
	 * closed on two dark slabs in a row, both dull. Running the gradient the
	 * other way keeps the pair related without making them identical.
	 */
	.av-newsletter__panel {
		border-radius: var(--av-radius-card);
		padding: clamp(2rem, 5vw, 3rem);
		background:
			radial-gradient(70% 120% at 100% 0%, rgb(var(--av-accent-rgb) / 26%) 0%, transparent 60%),
			linear-gradient(125deg, var(--av-secondary) 0%, var(--av-primary) 55%, var(--av-primary-hover) 100%);
		color: var(--av-on-dark-muted);
		display: grid;
		grid-template-columns: 1.1fr 0.9fr;
		gap: clamp(1.5rem, 4vw, 2.5rem);
		align-items: center;
	}

	@media (max-width: 860px) {
		.av-newsletter__panel { grid-template-columns: 1fr; }
	}

	.av-newsletter__copy {
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
	}

	.av-newsletter__title {
		color: var(--av-on-dark);
		max-width: 20ch;
		font-size: var(--av-fs-h3);
	}

	.av-newsletter__text {
		color: var(--av-on-dark-muted);
		font-size: var(--av-fs-sm);
		max-width: 48ch;
	}

	.av-newsletter__form {
		display: flex;
		flex-wrap: wrap;
		gap: 0.6rem;
		align-items: center;
	}

	.av-newsletter__input {
		flex: 1 1 200px;
		min-width: 0;
		padding: 0.85rem 1.1rem;
		border-radius: var(--av-radius-pill);
		border: 1px solid rgb(255 255 255 / 20%);
		background: rgb(255 255 255 / 10%);
		color: #fff;
		font-size: 0.9375rem;
	}

	.av-newsletter__input::placeholder { color: rgb(255 255 255 / 45%); }

	.av-newsletter__input:focus {
		outline: none;
		border-color: var(--av-accent);
		background: rgb(255 255 255 / 16%);
		box-shadow: 0 0 0 3px rgb(var(--av-accent-rgb) / 25%);
	}

	/* Colour comes from .av-btn--on-dark (white pill → primary fill on hover),
	   which carries the unlayered text guard so the label can't vanish. This
	   used to force .av-btn--primary here, whose unlayered white-text guard
	   painted white on the white surface below — an invisible button. Layout
	   only now. */
	.av-newsletter__btn {
		flex: 0 0 auto;
	}

	.av-newsletter__status {
		flex: 1 0 100%;
		margin: 0;
		font-size: var(--av-fs-xs);
		min-height: 1.1em;
		color: var(--av-on-dark-subtle);
	}

	.av-newsletter__status.is-error { color: #ffb4ab; }
	.av-newsletter__status.is-ok { color: var(--av-accent); }
}

/* ============================================================================
   PAGINATION + SEARCH
   ========================================================================== */

@layer components {

	.navigation.pagination {
		margin-block-start: var(--av-gap-lg);
		display: flex;
		justify-content: center;
	}

	.nav-links {
		display: flex;
		gap: 0.4rem;
		flex-wrap: wrap;
	}

	.page-numbers {
		display: inline-grid;
		place-items: center;
		min-width: 40px;
		height: 40px;
		padding-inline: 0.65rem;
		border: 1px solid var(--av-border);
		border-radius: var(--av-radius-sm);
		background: var(--av-surface);
		text-decoration: none;
		color: var(--av-ink);
		font-size: var(--av-fs-sm);
		font-variant-numeric: tabular-nums;
		transition: background-color var(--av-dur) var(--av-ease), color var(--av-dur) var(--av-ease);
	}

	.page-numbers.current,
	.page-numbers:hover {
		background: var(--av-primary);
		border-color: var(--av-primary);
		color: var(--av-on-primary);
	}

	.page-numbers.dots {
		border-color: transparent;
		background: none;
	}

	.av-search {
		display: flex;
		gap: 0.5rem;
		max-width: 520px;
		width: 100%;
	}

	.av-search__input {
		flex: 1;
		padding: 0.8rem 1rem;
		border: 1px solid var(--av-border);
		border-radius: var(--av-radius-input);
		background: var(--av-surface);
		color: var(--av-ink);
		font-size: 0.9375rem;
	}

	.av-search__input:focus {
		outline: none;
		border-color: var(--av-primary);
		box-shadow: var(--av-shadow-focus);
	}

	.av-404__links {
		list-style: none;
		display: flex;
		flex-wrap: wrap;
		gap: 1.25rem;
		padding: 0;
		margin: var(--av-gap) 0 0;
	}

	/* ------------------------------------------- ARTICLE + STICKY SIDEBAR */

	/*
	 * A fixed sidebar track rather than a fraction. .av-u-split would give
	 * 0.85fr here — about 520px on a 1280 container — which is a second
	 * column, not a sidebar. A fixed 320px also keeps the reading column at a
	 * sane measure as the viewport grows instead of widening with it.
	 *
	 * minmax(0, 1fr) not 1fr: a grid item defaults to min-width:auto, so one
	 * wide table or <pre> in a post would push the column past its track and
	 * blow out the page. This is long-form content — that will happen.
	 */
	.av-article {
		display: grid;
		/* 72ch, not 1fr: on a 1280 container a fraction gave the reading column
		   904px — past 90 characters a line, which is punishing over an article
		   this long. Capping the track holds the measure and lets the pair
		   centre in whatever space is left. */
		grid-template-columns: minmax(0, 72ch) 320px;
		justify-content: center;
		gap: clamp(2rem, 4vw, 3.5rem);
		align-items: start; /* required, or the sticky item stretches and never sticks */
	}

	@media (max-width: 960px) {
		.av-article { grid-template-columns: minmax(0, 1fr); }
	}

	/* Prose centres itself and caps at 68ch when it is the page container. As
	   one column of two it should fill its track and stay left. */
	.av-article__body .av-prose {
		max-width: none;
		margin-inline: 0;
	}

	.av-article__comments {
		margin-block-start: var(--av-gap-lg);
		padding-block-start: var(--av-gap-lg);
		border-block-start: 1px solid var(--av-border-soft);
	}

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

	.av-sidecard {
		background: var(--av-surface);
		border: 1px solid var(--av-border-strong);
		border-radius: var(--av-radius-card);
		padding: var(--av-card-pad);
	}

	.av-sidecard__title {
		font-family: var(--av-font-head);
		font-size: var(--av-fs-lg);
		font-weight: 600;
		line-height: 1.3;
		color: var(--av-ink);
		margin: 0 0 var(--av-gap-sm);
	}

	.av-sidecard__text {
		margin: 0 0 var(--av-gap);
		font-size: var(--av-fs-sm);
		line-height: 1.6;
		color: var(--av-text-muted);
	}

	.av-sidecard__list {
		display: flex;
		flex-direction: column;
		gap: 0.15rem;
	}

	.av-sidecard__link {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.75rem;
		padding: 0.55rem 0.7rem;
		border-radius: var(--av-radius-sm);
		font-size: var(--av-fs-sm);
		color: var(--av-text);
		transition:
			background-color var(--av-dur) var(--av-ease),
			color var(--av-dur) var(--av-ease);
	}

	.av-sidecard__link:hover,
	.av-sidecard__link:focus-visible {
		background: rgb(var(--av-primary-rgb) / 7%);
		color: var(--av-primary);
	}

	/*
	 * The current topic is marked with weight and a tinted ground as well as
	 * colour. Colour alone would fail WCAG 1.4.1 — it is the only thing
	 * separating this row from its neighbours.
	 */
	.av-sidecard__link.is-current {
		background: rgb(var(--av-primary-rgb) / 9%);
		color: var(--av-primary);
		font-weight: 600;
	}

	.av-sidecard__count {
		flex: 0 0 auto;
		font-size: var(--av-fs-xs);
		font-variant-numeric: tabular-nums;
		color: var(--av-text-subtle);
	}

	.av-sidecard__link.is-current .av-sidecard__count { color: inherit; }

	.av-sidecard--contact {
		background: var(--av-muted);
		border-color: transparent;
	}

	.av-sidecard__actions {
		display: flex;
		flex-direction: column;
		gap: var(--av-gap-sm);
		margin-block-start: var(--av-gap);
	}

	/* Full-width buttons: in a 320px column two side-by-side pills wrap into a
	   ragged pair, and the CTA reads as an afterthought. */
	.av-sidecard__actions .av-btn {
		width: 100%;
		justify-content: center;
	}
}

/* ---------------------------------------------------------- dark-ground text
 * These were in the layout layer, where they were dead: they set the same
 * properties as the component rules above, and a later layer wins over an
 * earlier one no matter how specific the earlier selector is. Nothing used
 * --dark until the Process section did, so the breakage never surfaced —
 * section intros rendered at 2.14:1 on the deep ground.
 *
 * Kept last in components so ordinary component colours are what they
 * override, and so this stays out of the unlayered tail, which is reserved
 * for beating WordPress core.
 */
@layer components {
	/* :not(.av-card ...) is load-bearing. A dark section still contains white
	   cards, and those keep their own light surface — an unscoped
	   `.av-section--dark h3` painted card titles white on white. The card
	   chassis already themes its own text through --card-fg, so headings
	   inside a card must be left to it. */
	.av-section--dark h2:not(.av-card h2),
	.av-section--dark h3:not(.av-card h3),
	.av-section--dark h4:not(.av-card h4),
	.av-section--dark .av-secthead__title { color: var(--av-on-dark); }

	.av-section--dark .av-secthead__intro,
	.av-section--dark > .av-wrap > p { color: var(--av-on-dark-muted); }

	.av-section--dark .av-eyebrow {
		color: var(--av-accent);
		background: rgb(255 255 255 / 10%);
	}
}

/* ============================================================================
   UNLAYERED TAIL
   ----------------------------------------------------------------------------
   MUST stay outside @layer. Unlayered CSS beats layered CSS regardless of
   specificity or source order, and WordPress emits its global styles unlayered.
   Nothing in the @layer blocks above can override them — these rules are the
   only ones that can.

   Because both sides are unlayered here, ordinary specificity decides. Core
   wraps its selectors in :where(), which contributes ZERO specificity, so
   `body a` (0,0,2) beats `a:where(:not(.wp-element-button))` (0,0,1).

   Keep this section short and say why each rule is here.
   ========================================================================== */

/*
 * Links.
 *
 * WordPress core emits, unlayered, from its own theme.json:
 *
 *     a:where(:not(.wp-element-button)) { text-decoration: underline; }
 *
 * That underlines every link on the site — the primary navigation, footer
 * columns, breadcrumbs, card links and any button rendered as an <a>. It cannot
 * be turned off from inside a layer.
 *
 * (The matching `color: var(--av-primary)` came from THIS theme's own
 * theme.json elements.link and has been removed there — it was painting
 * "Explore Products" primary-blue on a primary-blue button, i.e. invisible,
 * while "Watch Overview" was fine because it is a <button>, not an <a>.)
 *
 * No link on the site carries a resting underline.
 */
body a {
	text-decoration: none;
}

/*
 * Figures.
 *
 * WordPress core emits, unlayered, from its own theme.json:
 *
 *     :where(figure) { margin: 0 0 1em; }
 *
 * That put a stray 16px under every <figure> on the site. Two visible effects:
 * the hero's media frame sat 16px short of its wrapper, so the two floating
 * cards — which anchor to the wrapper — landed at 8px and 24px insets instead
 * of matching; and every table in an article carried an extra gap, since the
 * table markup wraps them in <figure class="wp-block-table">.
 *
 * `figure` is (0,0,1) and core's `:where(figure)` is (0,0,0), so this wins on
 * specificity now that both sides are unlayered. It cannot be fixed from inside
 * a layer at any specificity.
 */
figure {
	margin: 0;
}

/*
 * Prose links: no underline, but still identifiable.
 *
 * A link inside a paragraph cannot be marked by colour alone here — measured,
 * --av-primary (#123B8F) against --av-text (#475569) is 1.35:1, far below the
 * 3:1 WCAG 1.4.1 requires for colour-only identification (technique G183). A
 * reader who cannot separate those hues would have no way to tell it is a link.
 *
 * Weight is the non-colour cue: it is visible without colour vision, and it
 * costs no underline. The underline returns on hover and focus as confirmation,
 * which is the pattern G183 itself describes.
 *
 * Chrome links (nav, footer, cards, buttons, breadcrumbs) need none of this —
 * their position and styling already identify them.
 */
body .av-prose a,
body .av-faq__a a,
body .av-quote__text a {
	font-weight: 600;
	text-decoration: none;
}

body .av-prose a:hover,
body .av-prose a:focus-visible,
body .av-faq__a a:hover,
body .av-faq__a a:focus-visible,
body .av-quote__text a:hover,
body .av-quote__text a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 0.18em;
}

/*
 * And make the button colour un-overridable, so a future core change that
 * re-introduces an unlayered link colour cannot make button labels vanish
 * again. Class beats :where().
 */
body .av-btn--primary { color: var(--av-on-primary); }
body .av-btn--secondary { color: var(--av-on-secondary); }
body .av-btn--on-dark { color: var(--av-deep); }
body .av-btn--glass { color: #fff; }
body .av-btn--ghost { color: var(--av-primary); }

/* on-dark is the one button whose text colour CHANGES on hover (deep → white as
   it fills primary). Its hover colour must be unlayered too, or the guard above
   pins the label to --av-deep and it goes dark-on-primary — the same
   invisible-label failure the guard exists to prevent. :hover adds specificity,
   so this wins over the base guard. */
body .av-btn--on-dark:hover { color: var(--av-on-primary); }

/* The floating header renders its CTA as --on-dark so it reads as a light pill
   on the dark bar. Below 1025px that same button moves into the mobile drawer,
   whose ground is white — a white button on white, with a readable label but no
   visible edge. Restore the primary fill for the drawer only. This has to be
   unlayered: a layered `color` here would lose to the --on-dark guard above and
   pin the label to --av-deep over the primary fill. */
@media (max-width: 1024px) {
	body .av-header--floating .av-nav__actions .av-btn--on-dark {
		background: var(--av-primary);
		color: var(--av-on-primary);
	}

	body .av-header--floating .av-nav__actions .av-btn--on-dark:hover {
		background: var(--av-primary-hover);
		color: var(--av-on-primary);
	}
}

/* ---------------------------------------------------------- block editor */

/*
 * Core ships container blocks with their own bottom margin (columns get
 * 1.75em). Inside .av-prose the vertical rhythm comes from the flow gap plus
 * the heading margins, so a core margin does not replace that spacing — it
 * stacks on top of it. Measured on About: the gap before "Who We Are" was 93px
 * (28 core + 47 heading + 18 flow) where every other h2 on the page sat at 65.
 *
 * Unlayered on purpose. Core block CSS is itself unlayered, so a layered rule
 * here would lose no matter how specific it is. Scoped to direct children of
 * .av-prose so block spacing elsewhere is untouched, and deliberately NOT
 * written as [class*="wp-block-"] — that would also zero out
 * h2.wp-block-heading's top margin and collapse every heading into its
 * preceding paragraph.
 */
.av-prose > .wp-block-columns,
.av-prose > .wp-block-group,
.av-prose > .wp-block-buttons {
	margin-block: 0;
}

.wp-block-button__link {
	border-radius: var(--av-radius-pill);
	padding: 0.85rem 1.5rem;
	font-family: var(--av-font-head);
	font-weight: 600;
	font-size: 0.9375rem;
	background-color: var(--av-primary);
	color: var(--av-on-primary);
	transition: background-color var(--av-dur) var(--av-ease);
}

.wp-block-button__link:hover {
	background-color: var(--av-primary-hover);
	color: var(--av-on-primary);
}

.wp-block-button.is-style-outline .wp-block-button__link {
	background-color: transparent;
	border-color: var(--av-border);
	color: var(--av-primary);
}

.wp-block-image img,
.wp-block-post-featured-image img {
	border-radius: var(--av-radius-img);
}

.wp-block-columns { gap: var(--av-gap); }

/*
 * Panel — the surface the block patterns sit on.
 *
 * The patterns used to hand-write `border-radius:8px;padding:2rem` into the
 * block markup. That pinned them to 8px next to the theme's 20px cards, and put
 * the value out of reach of the radius setting: a client moving the slider
 * changed every card on the site except the ones inside a pattern.
 *
 * Carrying it as a className instead means the pattern declares WHAT the box is
 * and the stylesheet decides how it looks, which is the same deal every other
 * component in this file gets.
 */
.av-panel {
	border-radius: var(--av-radius-card);
	padding: var(--av-card-pad);
}

/*
 * The inline padding has to be restated at higher specificity, because core
 * adds .has-global-padding to a group block and that rule sets
 *   padding-right/left: var(--wp--style--root--padding-right/left)
 * Those custom properties are EMPTY here — theme.json declares no root
 * padding — and a var() with no fallback that resolves to nothing is invalid
 * at computed-value time, so the property falls back to its initial value, 0.
 * It only clobbers the inline axis, which is why the panel kept its 32px top
 * and bottom while the text sat flush against the left edge.
 *
 * Restating the shorthand would not help: .has-global-padding (0,1,0) ships in
 * the global-styles block after this file, so it wins on source order at equal
 * specificity. .av-panel.has-global-padding is (0,2,0) and wins outright.
 */
.av-panel.has-global-padding,
.av-panel {
	padding-inline: var(--av-card-pad);
}

/*
 * A panel nested inside a column, rather than being the column itself, sits at
 * content height while the column stretches to the row — Mission measured
 * 206px against Vision's 178px, so the two boxes ended at different points.
 * Both markup shapes exist: the shipped pattern puts .av-panel on the column,
 * the stored About content nests a group inside it.
 */
.wp-block-column > .av-panel {
	height: 100%;
}

.av-panel--cta {
	padding-block: var(--av-space-12);
}

.has-muted-background-color { background-color: var(--av-muted) !important; }
.has-surface-background-color { background-color: var(--av-surface) !important; }
.has-primary-color { color: var(--av-primary) !important; }
