/*
Theme Name: Secure Debug Aegis
Theme URI: https://securedebug.com
Author: Secure Debug
Description: Deep-navy block theme for Secure Debug. Oversized display headlines, periwinkle CTAs, proof-led landing structure. Visual direction adapted from a CyberGuard demo layout; all code, tokens and assets are original.
Version: 1.3.0
Requires at least: 6.6
Requires PHP: 8.0
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: securedebug-aegis
*/

/* ============================================================
   1. TOKENS
   ============================================================ */
:root {
	/* Surfaces — aliases of the theme.json presets so the hex lives in one file. */
	--ag-bg: var(--wp--preset--color--base);
	--ag-surface: var(--wp--preset--color--surface);
	--ag-panel: var(--wp--preset--color--panel);

	/*
	 * Text tiers. Every tier clears WCAG AA (4.5:1) on all three surfaces.
	 * Measured worst case (the #222E66 panel): contrast 12.72, muted 5.24,
	 * faint 4.71, accent-text 4.91.
	 *
	 * The reference palette these were derived from does not: its body grey
	 * (#797A8C) measures 4.31 / 3.98 / 3.01 and its link blue (#4A81D3)
	 * drops to 3.25 on the panel. Same character, corrected luminance.
	 */
	--ag-fg: var(--wp--preset--color--contrast);
	--ag-fg-muted: var(--wp--preset--color--muted);
	--ag-fg-faint: var(--wp--preset--color--faint);
	--ag-accent: var(--wp--preset--color--accent);
	--ag-accent-text: var(--wp--preset--color--accent-text);
	--ag-gold: var(--wp--preset--color--gold);

	/*
	 * A third surface, one step above --ag-surface. The .ag-slab sections
	 * (services, testimonials) paint --ag-surface as their ground, so a card
	 * that also used --ag-surface measured 1.00:1 against the section behind
	 * it — the card component stopped reading as a card in exactly the two
	 * places it was supposed to sit proudest. Cards must always be one step
	 * above their ground, whichever ground that is.
	 *
	 * Deliberately not in the theme.json palette: it is a surface treatment
	 * the stylesheet applies, not a colour an editor should be able to pick.
	 */
	--ag-surface-2: #1F2142;

	/*
	 * Borders — cool, low alpha, matching the navy family.
	 *
	 * Two grades, split by what the border is doing:
	 *   line-1/2/3  decorative grouping (card edges, rules, dividers). Not
	 *               user-interface components, so WCAG 1.4.11's 3:1 does not
	 *               apply; they only have to be *seen*. 0.18 measured
	 *               1.34:1 — effectively invisible. 0.32 lands near 1.8:1.
	 *   line-control the sole visual boundary of an actual control (outline
	 *               button, pagination link). 1.4.11 does apply. 0.72
	 *               measures 3.62 / 3.53 / 3.02:1 on base / surface / panel.
	 */
	--ag-line-1: rgba(148, 156, 200, 0.10);
	--ag-line-2: rgba(148, 156, 200, 0.32);
	--ag-line-3: rgba(148, 156, 200, 0.44);
	--ag-line-control: rgba(148, 156, 200, 0.72);

	--ag-inset-hi: inset 0 1px 0 rgba(255, 255, 255, 0.05);
	--ag-shadow-1: 0 2px 6px rgba(0, 0, 0, 0.30), 0 12px 28px rgba(0, 0, 0, 0.24);
	--ag-shadow-2: 0 4px 10px rgba(0, 0, 0, 0.35), 0 20px 48px rgba(0, 0, 0, 0.34);
	--ag-glow-accent: 0 0 32px rgba(63, 114, 192, 0.28);

	--ag-radius: 10px;
	--ag-radius-lg: 16px;

	/*
	 * Distance from the viewport edge to the 1290px container's edge — the left
	 * gutter every section's first character sits on.
	 *
	 * 100vw rather than 100%: the split section's copy half is a grid item, so a
	 * percentage there resolves against its own 50% track and the gutter would
	 * halve. vw overshoots by the scrollbar width on platforms that reserve one,
	 * which is padding inside a grid item and can never produce overflow.
	 */
	--ag-edge: max(clamp(1rem, 0.5rem + 2vw, 2.25rem), (100vw - 1290px) / 2);

	/* 300-450ms with a slight overshoot — the motion tier the design system
	   returned for this pattern (Stagger List / Standard). */
	--ag-ease: cubic-bezier(0.22, 1, 0.36, 1);
	--ag-dur: 0.28s;
}

/* ============================================================
   2. GLOBAL
   ============================================================ */
body {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

html {
	scroll-padding-top: 6rem; /* sticky header measures 77-84px */
}

html,
body {
	overflow-x: clip;
}

/*
 * Form controls do not inherit box-sizing from anywhere: the UA gives them
 * content-box, and neither WP's reset nor theme.json changes it. So a
 * `width: 100%` field added its own 1rem padding and 1px border on top of
 * the track it was in — the two-up name/email row rendered 378px inputs in
 * 344px columns, and the 18px gap between them became a 16px overlap with
 * the borders touching.
 *
 * Scoped to controls rather than a universal `*` reset: the block editor's
 * layout CSS assumes the UA box model for content elements.
 */
input,
textarea,
select,
button {
	box-sizing: border-box;
}

/*
 * WP gives every top-level block a blockGap margin. Every top-level block in
 * this theme is a full-bleed slab that supplies its own padding, so that
 * margin can only ever surface as a stripe of page background *between* two
 * slabs — most visibly the 20px of #12132C that sat between the blue CTA
 * panel and the footer, the one section boundary on the page that did not
 * meet flush. (The gap is on the template-part wrapper, not on .ag-footer,
 * which is the group inside it — targeting the inner element did nothing.)
 *
 * Core's rule is :where()-wrapped, so it carries zero specificity and a
 * plain class selector beats it without !important.
 */
.wp-site-blocks > * {
	margin-block-start: 0;
}

:where(a, button, input, select, textarea, [tabindex], .wp-block-button__link):focus-visible {
	outline: 2px solid var(--ag-accent-text);
	outline-offset: 2px;
	/* inherit, not 4px: a flat 4px visibly reshaped the 6px buttons and the
	   16px cards the moment they took keyboard focus. */
	border-radius: inherit;
}

/* Core injects its own skip link and points it at <main>'s id; this only
   restyles it for the dark palette. See the note in functions.php. */
.skip-link.screen-reader-text:focus {
	z-index: 200;
	top: 4px;
	left: 4px;
	padding: 0.75rem 1.25rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: 0.95rem;
	font-weight: 600;
	color: #fff;
	background: var(--ag-accent);
	border-radius: var(--ag-radius);
	box-shadow: var(--ag-shadow-2);
	text-decoration: none;
}

#ag-main:focus {
	outline: none;
}

/* ============================================================
   3. TYPOGRAPHY
   ============================================================ */

/*
 * The display headline is the signature of this layout: very large, tight,
 * and set in caps. Caps are done with text-transform rather than typed
 * capitals so the accessible name stays sentence case for screen readers.
 */
.ag-display {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: -0.025em;
	line-height: 0.98;
	color: var(--ag-fg);
	text-wrap: balance;
}

/*
 * The hero headline is emitted from a wp:html block, which in a constrained
 * layout is a direct child and therefore inherits max-width: <contentSize>.
 * At 760px "SMART PROTECTION" broke over two lines and the two-line
 * composition became four. It has to span the wide container instead.
 *
 * Scoped to h1 on purpose. As an unscoped `.ag-display` this escape hatch
 * also governed the CTA banner's h2, whose group declares contentSize 820px:
 * the heading rendered 1408px wide over an 820px paragraph, so the headline
 * and the copy it introduces sat on two different measures. Every other
 * .ag-display on the site (hero, single, page, 404) is an h1 and still gets
 * it; the CTA h2 now inherits its group's measure like everything else.
 */
h1.ag-display {
	max-width: none !important;
}

/* Second line of the hero headline, pushed right on wide screens */
.ag-display--offset {
	display: block;
}

@media (min-width: 1024px) {
	.ag-display--offset {
		margin-left: 12%;
	}
}

/* Section eyebrow: small, tracked, accent, with a short rule */
.ag-eyebrow {
	display: inline-flex;
	align-items: center;
	gap: 0.6em;
	/* was flat 0.8rem — frozen at 12.8px while every heading around it scaled,
	   so the label crept proportionally larger as the viewport narrowed */
	font-size: clamp(0.75rem, 0.72rem + 0.1vw, 0.8rem);
	font-weight: 600;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--ag-accent-text);
}

.ag-eyebrow::before {
	content: "";
	width: 1.75rem;
	height: 2px;
	background: var(--ag-accent);
	border-radius: 2px;
}

.ag-section-title {
	font-size: var(--wp--preset--font-size--x-large);
	max-width: 22ch;
	/* Four of the six section titles dropped a stub onto line two — the worst,
	   "Comprehensive cybersecurity solutions", broke 608px / 175px, a single
	   word orphaned at 48px directly under the hero. .ag-display already had
	   balance; the section titles were simply missed. */
	text-wrap: balance;
	/* max-width inside a constrained layout inherits auto side margins, which
	   centred the title while its eyebrow stayed left. !important is needed,
	   not preferred: WP emits `margin-left: auto !important` for every child
	   of a constrained group, so an unweighted declaration loses to it. */
	margin-inline: 0 !important;
}

.ag-lead {
	color: var(--ag-fg-muted);
	/*
	 * Not the `large` preset (its clamp topped out at 22px and overtook the
	 * card headings), but not flat 1.25rem either: flat froze the lead at
	 * 20px while every heading around it scaled down, so below ~800px the
	 * body copy was *larger* than the headings it introduces — at 390px the
	 * lead measured 39% bigger than a services card title.
	 *
	 * This curve sits under the heading curve at both ends: 16.2px at 390
	 * against the `large` preset's 18.4px floor, 20px at 1440 against 22px.
	 */
	font-size: clamp(1rem, 0.9rem + 0.45vw, 1.25rem);
	line-height: 1.6;
	/*
	 * The hero lead measured 94.5 characters per line — the paragraph sat in
	 * a wp:columns wrapper containing a single column, so it imposed no
	 * constraint at all and the copy ran the full 1290px wide container.
	 * Roughly double a comfortable measure, at the largest body size on the
	 * page, in the first paragraph anyone reads.
	 */
	max-width: 46ch;
	/*
	 * Identical to the .ag-section-title case: a max-width inside a constrained
	 * layout inherits WP's `margin-left: auto !important` and centres itself. In
	 * the hero the lead sits inside a column and never showed it; as a direct
	 * child of the services section's group it rendered 270px right of the
	 * eyebrow and heading it introduces.
	 */
	margin-inline: 0 !important;
}

/* ============================================================
   4. LAYERS & TEXTURE
   ============================================================ */

/* Constellation mesh — the reference's hero backdrop is a network graphic.
   Drawn in CSS from repeating gradients rather than shipping an image, so it
   costs no request and scales at any viewport. */
.ag-mesh {
	position: relative;
	isolation: isolate;
}

.ag-mesh::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-image:
		radial-gradient(circle at 18% 22%, rgba(111, 163, 234, 0.20) 0 2px, transparent 3px),
		radial-gradient(circle at 72% 14%, rgba(111, 163, 234, 0.16) 0 2px, transparent 3px),
		radial-gradient(circle at 88% 62%, rgba(111, 163, 234, 0.18) 0 2px, transparent 3px),
		radial-gradient(circle at 34% 78%, rgba(111, 163, 234, 0.14) 0 2px, transparent 3px),
		linear-gradient(64deg, transparent 49.6%, rgba(111, 163, 234, 0.10) 49.8%, transparent 50.1%),
		linear-gradient(-38deg, transparent 49.7%, rgba(111, 163, 234, 0.08) 49.9%, transparent 50.2%),
		radial-gradient(ellipse 70% 60% at 50% 0%, rgba(63, 114, 192, 0.22) 0%, transparent 70%);
	-webkit-mask-image: linear-gradient(180deg, black 55%, transparent 100%);
	mask-image: linear-gradient(180deg, black 55%, transparent 100%);
}

.ag-mesh > * {
	position: relative;
	z-index: 1;
}

/* Full-bleed raised slab */
.ag-slab {
	background: var(--ag-surface);
	border-top: 1px solid var(--ag-line-1);
	border-bottom: 1px solid var(--ag-line-1);
}

/*
 * Home hero radar. The lead is capped at 46ch, so on desktop the right half
 * of the hero was ~600px of empty mesh. Drawn in CSS and one inline SVG: no
 * request, nothing that can become the LCP element, hidden from assistive
 * tech. Below 1024px the hero already fills the screen, so it goes.
 */
.wp-block-columns.ag-hero-cols {
	align-items: flex-start;
}

.wp-block-columns.ag-hero-cols > .ag-hero-visual-col {
	display: none;
}

@media (min-width: 1024px) {
	/* 35rem holds the 46ch lead and the rating strip on one line each, as
	   they were before the hero had a second column. The radar side gives way. */
	.wp-block-columns.ag-hero-cols > .wp-block-column:first-child {
		flex: 1 1 0;
		min-width: 35rem;
	}

	/* The negative bottom margin lets the scope hang into the hero's bottom
	   padding instead of making the whole hero taller. */
	.wp-block-columns.ag-hero-cols > .wp-block-column.ag-hero-visual-col {
		display: flex;
		justify-content: center;
		align-items: flex-start; /* stretch would override the scope's aspect-ratio */
		flex: 0 1 540px;
		min-width: 0;
		margin-block: -1.5rem -5.5rem;
	}
}

.ag-radar {
	position: relative;
	width: min(100%, 30vw, 380px);
	aspect-ratio: 1;
	border: 1px solid var(--ag-line-2);
	border-radius: 50%;
	background:
		radial-gradient(circle, rgba(63, 114, 192, 0.24) 0 17%, transparent 17.5%),
		repeating-radial-gradient(circle closest-side, transparent 0 calc(25% - 1px), rgba(148, 156, 200, 0.2) calc(25% - 1px) 25%),
		linear-gradient(rgba(148, 156, 200, 0.14), rgba(148, 156, 200, 0.14)) center / 1px 100% no-repeat,
		linear-gradient(rgba(148, 156, 200, 0.14), rgba(148, 156, 200, 0.14)) center / 100% 1px no-repeat,
		radial-gradient(circle closest-side, rgba(25, 27, 54, 0.92), rgba(18, 19, 44, 0.6));
	box-shadow: var(--ag-glow-accent), inset 0 0 60px rgba(63, 114, 192, 0.16);
}

/* Bezel ticks, one every 7.5deg, just outside the scope */
.ag-radar::before {
	content: "";
	position: absolute;
	inset: -14px;
	border-radius: 50%;
	background: repeating-conic-gradient(rgba(148, 156, 200, 0.38) 0 0.6deg, transparent 0.6deg 7.5deg);
	-webkit-mask-image: radial-gradient(closest-side, transparent calc(100% - 7px), #000 calc(100% - 7px));
	mask-image: radial-gradient(closest-side, transparent calc(100% - 7px), #000 calc(100% - 7px));
}

/* Leading edge at 0deg with the trail behind it, so a clockwise turn reads
   as a sweep. The resting angle is the reduced-motion frame: just past IDOR. */
.ag-radar__sweep {
	position: absolute;
	inset: 0;
	border-radius: 50%;
	background: conic-gradient(transparent 0 72%, rgba(111, 163, 234, 0.07) 84%, rgba(111, 163, 234, 0.4) 100%);
	transform: rotate(62deg);
}

.ag-radar__mark {
	position: absolute;
	left: 50%;
	top: 50%;
	width: 22%;
	transform: translate(-50%, -50%);
	fill: #fff;
	filter: drop-shadow(0 0 18px rgba(111, 163, 234, 0.55));
}

/* --at is the blip's angle clockwise from 12 o'clock as a fraction of a
   turn; the motion block uses it to light the blip as the sweep passes. */
.ag-radar__blip {
	position: absolute;
	left: var(--x);
	top: var(--y);
	width: 10px;
	height: 10px;
	margin: -5px 0 0 -5px;
	border-radius: 50%;
	background: var(--ag-accent-text);
	box-shadow: 0 0 0 4px rgba(111, 163, 234, 0.2), 0 0 12px rgba(111, 163, 234, 0.9);
}

.ag-radar__blip--1 { --x: 72%; --y: 30%; --at: 0.1325; } /* 47.7deg  */
.ag-radar__blip--2 { --x: 66%; --y: 74%; --at: 0.4064; } /* 146.3deg */
.ag-radar__blip--3 { --x: 22%; --y: 70%; --at: 0.6514; } /* 234.5deg */

.ag-radar__blip--hot {
	background: var(--ag-gold);
	box-shadow: 0 0 0 4px rgba(254, 196, 44, 0.2), 0 0 12px rgba(254, 196, 44, 0.9);
}

.ag-radar__blip b {
	position: absolute;
	left: 16px;
	top: 50%;
	transform: translateY(-50%);
	padding: 0.2em 0.55em;
	border: 1px solid var(--ag-line-2);
	border-radius: 6px;
	background: rgba(18, 19, 44, 0.85);
	color: var(--ag-fg-muted);
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.7rem;
	font-weight: 500;
	white-space: nowrap;
}

/* ============================================================
   5. TOP BAR + HEADER
   ============================================================ */
.ag-topbar {
	font-size: 0.85rem;
	color: var(--ag-fg-faint);
	border-bottom: 1px solid var(--ag-line-1);
}

/* padding + negative margin buys the 44px target without moving the row */
.ag-topbar a {
	display: inline-block;
	padding-block: 0.85rem;
	margin-block: -0.85rem;
	color: var(--ag-fg-faint);
	text-decoration: none;
}

.ag-topbar a:hover {
	color: var(--ag-accent-text);
}

/*
 * backdrop-filter must not sit on .ag-header itself — it would create a
 * containing block for the fixed nav overlay and clip it to the header box.
 * The tint and the blur both live on the pseudo-element instead.
 */
.ag-header {
	position: sticky;
	top: 0;
	z-index: 100;
	background: transparent !important;
	border-bottom: 1px solid var(--ag-line-1);
}

.ag-header::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: rgba(18, 19, 44, 0.82);
	backdrop-filter: blur(14px);
	-webkit-backdrop-filter: blur(14px);
}

/* Core ships `.wp-block-navigation-item__content.wp-block-navigation-item__content`
   — a deliberately doubled class at (0,3,0) — which beat the old
   `.ag-header .wp-block-navigation a` (0,2,1), so the rest colour never
   applied and only the hover state showed. Match its specificity. */
.ag-header .wp-block-navigation .wp-block-navigation-item__content,
.ag-header .wp-block-navigation a {
	position: relative;
	color: #D4D6E6;
	font-weight: 500;
	/* padding + negative margin buys a 44px hit area without moving anything */
	padding: 0.75rem 0.2rem;
	margin: -0.75rem -0.2rem;
	transition: color var(--ag-dur) ease;
}

.ag-header .wp-block-navigation a:hover {
	color: var(--ag-fg);
	text-decoration: none;
}

/* Core ships the menu toggle at 24x24. It is the only control on the page
   for a phone user, and an icon-only button is exactly the case the 44px
   floor exists for.

   Size only — never `display`. Core toggles these buttons between none and
   flex to decide whether the menu is collapsed at this width; forcing a
   display value here made both the open and the close icon appear on
   desktop and pushed the nav onto its own row. */
.wp-block-navigation__responsive-container-open,
.wp-block-navigation__responsive-container-close {
	min-width: 44px;
	min-height: 44px;
	align-items: center;
	justify-content: center;
}

/* The lockup is ~5.4:1: 195px wide is 36px tall, the width the old mark +
   site title took. Sized by width so max-width can shrink it inside a
   narrow footer column instead of letting it run into the next one. The
   padding lifts the link to the 44px target without making the header
   any taller. */
.ag-brand {
	display: inline-flex;
	flex-shrink: 0;
	max-width: 100%;
	padding-block: 0.25rem;
	margin-block: -0.25rem;
	border-radius: 4px;
}

.ag-brand img {
	display: block;
	width: 195px;
	max-width: 100%;
	height: auto;
}

@media (max-width: 599.98px) {
	.ag-brand img {
		width: 163px;
	}
}

/* ============================================================
   6. BUTTONS
   ============================================================ */
.wp-block-button__link {
	transition: background-color var(--ag-dur) ease, box-shadow var(--ag-dur) ease,
	            transform var(--ag-dur) var(--ag-ease);
}

.wp-block-button:not(.is-style-outline) .wp-block-button__link:hover {
	box-shadow: var(--ag-glow-accent);
	transform: translateY(-2px);
}

.wp-block-button.is-style-outline .wp-block-button__link {
	background: transparent;
	/*
	 * This border is the only thing identifying the control — no fill, no
	 * underline — so WCAG 1.4.11's 3:1 applies. The previous 0.55 alpha was
	 * annotated "~3.1:1" but measures 2.91 / 2.84 / 2.46:1 composited on
	 * base / surface / panel: short everywhere, 18% short on the CTA panel.
	 * See --ag-line-control for the measured replacement.
	 */
	border: 1px solid var(--ag-line-control);
	color: var(--ag-fg);
	/*
	 * Core's is-style-outline ships its own `padding: .667em 1.33em`, which
	 * beat the theme.json button padding and made every outline button 7.1px
	 * shorter than the filled one beside it. Visible in the hero pair, the
	 * CTA pair, and worst in the pricing row, where margin-top:auto aligns
	 * the three CTAs' bottoms so the mismatch surfaces as three tops on two
	 * different axes — in the section that sells the product.
	 *
	 * The -1px is the border: these are border-box, so subtracting it makes
	 * the outline button's outer height identical to the filled one's.
	 */
	padding: calc(0.95rem - 1px) calc(1.8rem - 1px);
}

.wp-block-button.is-style-outline .wp-block-button__link:hover {
	border-color: var(--ag-accent-text);
	color: var(--ag-fg);
	background: rgba(63, 114, 192, 0.12);
}

/* ============================================================
   7. CARDS
   ============================================================ */
.ag-card {
	--ag-card-pad: clamp(1.4rem, 1rem + 1.8vw, 2.1rem);

	position: relative;
	padding: var(--ag-card-pad) !important;
	background: var(--ag-surface);
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius-lg);
	box-shadow: var(--ag-inset-hi), var(--ag-shadow-1);
	transition: border-color var(--ag-dur) ease, box-shadow var(--ag-dur) ease,
	            transform var(--ag-dur) var(--ag-ease);
}

/*
 * A card must sit one step above whatever it is standing on. On the .ag-slab
 * sections the ground is --ag-surface, so a --ag-surface card measured 1.00:1
 * against it and dissolved into the section.
 */
.ag-slab .ag-card {
	background: var(--ag-surface-2);
}

/*
 * The lift is this page's primary "there is something behind this" signal.
 * 9 of the 21 cards — the four About stat tiles, three case studies and two
 * testimonials — contain no link at all, and spending the full lift on them
 * devalued it everywhere it was real. Non-linked cards keep the border
 * brighten (acknowledges the pointer) but not the motion (promises nothing).
 */
.ag-card:has(a):hover {
	border-color: var(--ag-line-3);
	box-shadow: var(--ag-inset-hi), var(--ag-shadow-2);
	transform: translateY(-4px);
}

.ag-card:not(:has(a)):hover {
	border-color: var(--ag-line-3);
}

/* Feature strip directly under the hero — three panels, no gap, blue slab */
.ag-feature-strip {
	background: var(--ag-panel);
}

.ag-feature-strip .wp-block-column {
	/* was a fourth bespoke clamp that disagreed with both spacing|50 and
	   --ag-card-pad, leaving the strip's first heading 100-130px right of
	   every other section's content edge */
	padding: var(--wp--preset--spacing--50);
	border-left: 1px solid rgba(255, 255, 255, 0.10);
}

.ag-feature-strip .wp-block-column:first-child {
	border-left: 0;
}

.ag-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	height: 52px;
	border-radius: 50%;
	border: 1px solid rgba(255, 255, 255, 0.22);
	color: var(--ag-fg);
	flex-shrink: 0;
}

.ag-icon svg {
	width: 24px;
	height: 24px;
}

/*
 * Four-up card grid: 4 across, 2 x 2, then 1 — never 3 + an orphan, which is
 * what auto-fill gives four or eight cards at in-between widths. Two classes
 * so it outranks the single-class rule the grid layout support prints.
 */
.wp-block-group.ag-grid-4 {
	grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 640px) {
	.wp-block-group.ag-grid-4 {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 1100px) {
	.wp-block-group.ag-grid-4 {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

/* Numbered service card */
.ag-num {
	font-family: var(--wp--preset--font-family--display);
	font-size: 2.6rem;
	font-weight: 700;
	line-height: 1;
	color: transparent;
	-webkit-text-stroke: 1px var(--ag-line-3);
}

/* ============================================================
   8. STATS & FEATURE LISTS
   ============================================================ */
/* ------------------------------------------------------------------
   Stat figure — a number and the thing it counts.

   Was borrowing the pricing component's `.ag-price` and then killing its
   fluid clamp with an inline `font-size: 2.6rem` on every instance: the
   four About stats sat frozen at 41.6px from 320px to 2560px while every
   heading and paragraph around them scaled. The pricing section has since
   been removed from the site; this is the only stat type left.
   ------------------------------------------------------------------ */
.ag-stat {
	margin: 0;
}

.ag-stat__num,
.ag-figure__num {
	display: block;
	font-family: var(--wp--preset--font-family--display);
	font-size: clamp(2rem, 1.5rem + 1.8vw, 2.9rem);
	font-weight: 700;
	line-height: 1;
	color: var(--ag-fg);
}

.ag-stat__label,
.ag-figure__label {
	display: block;
	margin-top: 0.45rem;
	font-size: 0.9375rem;
	line-height: 1.4;
	color: var(--ag-fg-muted);
}

/* Feature list with tick marks */
.ag-ticks {
	list-style: none;
	padding-left: 0;
	margin: 0 !important; /* same constrained-layout !important as above */
}

.ag-ticks li {
	position: relative;
	padding-left: 1.75rem;
	margin-block: 0.55rem;
	color: var(--ag-fg-muted);
	font-size: var(--wp--preset--font-size--small);
}

.ag-ticks li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.42em;
	width: 1rem;
	height: 0.55rem;
	border-left: 2px solid var(--ag-accent-text);
	border-bottom: 2px solid var(--ag-accent-text);
	transform: rotate(-45deg);
}

/* ============================================================
   9. RATING STRIP / TESTIMONIALS
   ============================================================ */
.ag-rating {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	flex-wrap: wrap;
	padding-top: 1.5rem;
	border-top: 1px solid var(--ag-line-2);
	color: var(--ag-fg-muted);
	font-size: var(--wp--preset--font-size--small);
}

.ag-stars {
	color: var(--ag-gold);
	letter-spacing: 0.1em;
}

.ag-quote-mark {
	font-family: var(--wp--preset--font-family--display);
	font-size: 3.5rem;
	line-height: 0.8;
	color: var(--ag-accent);
	opacity: 0.55;
}

.ag-avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 46px;
	border-radius: 50%;
	background: var(--ag-accent);
	color: #fff;
	font-weight: 700;
	font-size: 0.9rem;
	flex-shrink: 0;
}

/* ============================================================
   10. POST CARDS
   ============================================================ */
.ag-card-media,
.ag-card-media--empty {
	/* !important is required: the card is a constrained layout and WP emits
	   `margin-left: auto !important` for every child of one. */
	margin-top: calc(var(--ag-card-pad) * -1) !important;
	margin-left: calc(var(--ag-card-pad) * -1) !important;
	margin-right: calc(var(--ag-card-pad) * -1) !important;
	margin-bottom: var(--ag-card-pad) !important;
	max-width: none !important;
	border-radius: var(--ag-radius-lg) var(--ag-radius-lg) 0 0;
	overflow: hidden;
	background: var(--ag-panel);
	position: relative;
}

.ag-card-media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.4s var(--ag-ease);
}

.ag-card:hover .ag-card-media img {
	transform: scale(1.04);
}

/* Drawn placeholder for posts with neither a featured image nor one in the
   body — emitted by functions.php so the grid never shows a bare card. */
.ag-card-media--empty {
	display: flex;
	align-items: center;
	justify-content: center;
	aspect-ratio: 16 / 9;
	font-family: var(--wp--preset--font-family--mono);
	font-size: 1.4rem;
	color: rgba(111, 163, 234, 0.45);
	background-image:
		radial-gradient(ellipse 70% 90% at 50% 0%, rgba(63, 114, 192, 0.22), transparent 70%),
		linear-gradient(rgba(148, 156, 200, 0.07) 1px, transparent 1px),
		linear-gradient(90deg, rgba(148, 156, 200, 0.07) 1px, transparent 1px);
	background-size: auto, 26px 26px, 26px 26px;
}

.ag-card-media--empty::before {
	content: ">_";
}

.ag-post-meta {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.8rem;
	color: var(--ag-fg-faint);
}

/* WP renders the excerpt's more-link in its own paragraph, so it is a
   standalone target rather than a link inside a sentence — it needs the
   44px box that WCAG 2.5.8's inline exception would otherwise excuse. */
.wp-block-post-excerpt__more-link {
	display: inline-block;
	padding-block: 0.7rem;
	margin-block: -0.55rem;
	color: var(--ag-accent-text);
	font-weight: 600;
}

.ag-card :is(h2, h3) a {
	display: inline-block;
	padding-block: 0.85rem;
	margin-block: -0.85rem;
	color: var(--ag-fg);
	transition: color var(--ag-dur) ease;
}

.ag-card :is(h2, h3) a:hover {
	color: var(--ag-accent-text);
	text-decoration: none;
}

/* ------------------------------------------------------------------
   Search + pagination.

   Both were shipping raw UA defaults: a white, square-cornered input with
   black text on #12132C, and unstyled blue pagination links. Pagination
   only stays invisible while there are fewer posts than perPage.
   ------------------------------------------------------------------ */
.wp-block-search__input {
	background: var(--ag-surface);
	color: var(--ag-fg);
	border: 1px solid var(--ag-line-3);
	border-radius: var(--ag-radius);
	padding: 0.85rem 1rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: 1rem;
}

.wp-block-search__input::placeholder {
	color: var(--ag-fg-faint);
	opacity: 1; /* Firefox dims placeholders by default */
}

.wp-block-search__button {
	background: var(--ag-accent);
	color: #fff;
	border: 0;
	border-radius: var(--ag-radius);
	padding: 0.85rem 1.5rem;
	font-weight: 600;
	transition: background-color var(--ag-dur) ease;
}

.wp-block-search__button:hover {
	background: #3768B4;
}

.wp-block-query-pagination {
	gap: 0.4rem;
}

.wp-block-query-pagination a,
.wp-block-query-pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 44px;
	min-height: 44px;
	padding: 0 0.75rem;
	/* control boundary, not decoration — same 1.4.11 reasoning as the
	   outline button */
	border: 1px solid var(--ag-line-control);
	border-radius: var(--ag-radius);
	color: var(--ag-fg-muted);
	text-decoration: none;
	transition: border-color var(--ag-dur) ease, color var(--ag-dur) ease;
}

.wp-block-query-pagination a:hover {
	border-color: var(--ag-accent-text);
	color: var(--ag-fg);
}

.wp-block-query-pagination .page-numbers.current {
	background: var(--ag-accent);
	border-color: var(--ag-accent);
	color: #fff;
}

/* ============================================================
   11. FOOTER
   ============================================================ */
.ag-footer {
	background: var(--ag-surface);
	border-top: 1px solid var(--ag-line-1);
}

.ag-footer h2 {
	font-size: 0.85rem !important;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ag-fg-faint);
}

.ag-footer .wp-block-list {
	list-style: none;
	padding-left: 0;
}

.ag-footer .wp-block-list li a {
	display: inline-block;
	padding-block: 0.7rem;
	margin-block: -0.4rem;
	color: var(--ag-fg-muted);
	text-decoration: none;
	transition: color var(--ag-dur) ease;
}

.ag-footer .wp-block-list li a:hover {
	color: var(--ag-accent-text);
}

/* ============================================================
   12. ABOUT PAGE

   Sections that only the About template composes: the page hero's
   breadcrumb + pull quote, the drawn figure that stands in for the
   reference layout's photography, the capability band, and the
   half-panel split.
   ============================================================ */

.ag-crumbs {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.55rem;
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: 0.85rem;
	color: var(--ag-fg-faint);
}

.ag-crumbs a {
	color: var(--ag-fg-faint);
	text-decoration: none;
	/* padding + negative margin buys the 44px target without moving the row,
	   the same trick the top bar and footer links use */
	padding-block: 0.8rem;
	margin-block: -0.8rem;
	transition: color var(--ag-dur) ease;
}

.ag-crumbs a:hover {
	color: var(--ag-accent-text);
}

.ag-crumbs [aria-current] {
	color: var(--ag-fg-muted);
}

.ag-crumbs__sep {
	color: var(--ag-line-3);
}

/* Same escape as h1.ag-display: as a direct child of the hero's constrained
   group the trail took max-width: 760px and auto margins, so on service and
   blog pages it sat centred under a left-aligned title. */
.is-layout-constrained > nav[aria-label="Breadcrumb"] {
	max-width: none !important;
}

/*
 * The reference sets its hero quote in quotation marks at body weight. Here it
 * carries the display face and an accent rule, because it is the only thing
 * occupying the right half of the hero and has to hold that space on its own.
 */
.ag-pullquote {
	max-width: 20ch;
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-size: clamp(1.15rem, 0.95rem + 0.9vw, 1.65rem);
	font-weight: 500;
	line-height: 1.35;
	letter-spacing: -0.01em;
	color: var(--ag-fg);
}

.ag-pullquote::before {
	content: "";
	display: block;
	width: 2.5rem;
	height: 2px;
	margin-bottom: 1.1rem;
	background: var(--ag-accent);
	border-radius: 2px;
}

/*
 * The first section after a page hero: every page with a breadcrumb. The
 * home hero has none, and overlaps its feature strip on purpose.
 *
 * The hero already ends in its own bottom padding and the section's top
 * border, so the section's top padding is the second half of one gap, not
 * a whole gap between two sections. Services, About and Contact used
 * spacing-70 there (123px at 1440) where Blog, the service pages and posts
 * used spacing-60 (80px), and the extra 43px read as an empty band under
 * the hero. One value on every page; !important because the patterns set
 * their padding inline.
 */
main > .ag-mesh:has(.ag-crumbs) + * {
	padding-top: var(--wp--preset--spacing--60) !important;
}

/*
 * Below 782px the hero's two columns stack. The quote exists to hold the
 * right half of a wide hero; stacked under the title it is just the same
 * line again on About, Services and Contact, ~90px tall, so it goes. The
 * first section then takes the hero's own bottom padding, so breadcrumb to
 * next eyebrow is one ordinary gap.
 */
@media (max-width: 781.98px) {
	.ag-mesh .wp-block-column:has(> .ag-pullquote) {
		display: none;
	}

	main > .ag-mesh:has(.ag-crumbs) + * {
		padding-top: var(--wp--preset--spacing--50) !important;
	}
}

/* ------------------------------------------------------------------
   Drawn figure.

   The reference fills these slots with stock photography of a SOC. This
   theme ships no raster assets at all — the post-card placeholder is
   drawn from gradients for the same reason — so the composition is kept
   (two staggered tiles, one stat card straddling them) and the contents
   are drawn.
   ------------------------------------------------------------------ */
.ag-figure {
	position: relative;
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 1.25rem;
}

.ag-figure__tile {
	position: relative;
	overflow: hidden;
	aspect-ratio: 3 / 4;
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius-lg);
	box-shadow: var(--ag-inset-hi), var(--ag-shadow-1);
	background-color: var(--ag-surface);
	background-image:
		radial-gradient(ellipse 80% 60% at 50% 0%, rgba(63, 114, 192, 0.28), transparent 72%),
		linear-gradient(rgba(148, 156, 200, 0.07) 1px, transparent 1px),
		linear-gradient(90deg, rgba(148, 156, 200, 0.07) 1px, transparent 1px);
	background-size: auto, 26px 26px, 26px 26px;
}

/*
 * A grid on its own reads as an image that failed to load. Each tile carries a
 * distinct drawn motif so the composition is legibly a graphic: sweep rings on
 * the first, a redacted log block on the second.
 */
.ag-figure__tile::after {
	content: "";
	position: absolute;
	pointer-events: none;
}

.ag-figure__tile:nth-child(1)::after {
	inset: 0;
	background-image: repeating-radial-gradient(circle at 50% 46%,
		rgba(111, 163, 234, 0.30) 0 1px, transparent 1px 24px);
	-webkit-mask-image: radial-gradient(circle at 50% 46%, black 20%, transparent 74%);
	mask-image: radial-gradient(circle at 50% 46%, black 20%, transparent 74%);
}

.ag-figure__tile:nth-child(2)::after {
	inset: 14% 16%;
	background-image: repeating-linear-gradient(180deg,
		rgba(148, 156, 200, 0.28) 0 5px, transparent 5px 22px);
	-webkit-mask-image: linear-gradient(90deg, black 0 48%, transparent 88%);
	mask-image: linear-gradient(90deg, black 0 48%, transparent 88%);
}

/* Same rule as .ag-card: on a .ag-slab section the ground is --ag-surface, so
   a tile painted --ag-surface measures 1.00:1 against it. */
.ag-slab .ag-figure__tile {
	background-color: var(--ag-surface-2);
}

/* The stagger of the reference's collage — the second frame hangs lower */
.ag-figure__tile:nth-child(2) {
	margin-top: clamp(1.5rem, 4vw, 3rem);
}

/*
 * Panel blue, not another grey surface: the card has to separate from the tile
 * it overlaps on both grounds this figure appears on (base and .ag-slab), and
 * on the slab the tile is already --ag-surface-2. It is also the one saturated
 * element in the composition, which is what makes the number read first.
 */
.ag-figure__stat {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: min(66%, 15.5rem);
	padding: 1.35rem 1.5rem;
	text-align: center;
	background: var(--ag-panel);
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius-lg);
	box-shadow: var(--ag-shadow-2);
}

/* .ag-figure__num / .ag-figure__label are typeset with .ag-stat in §8 — one
   stat figure, two placements. Muted measures 5.24:1 on the panel. */

/*
 * Below 600px the tiles are ~140px wide and a card centred over both of them
 * covers almost all of the figure. Drop it out of the overlay and let it read
 * as the third row of the composition.
 */
@media (max-width: 599.98px) {
	.ag-figure__stat {
		position: static;
		transform: none;
		width: auto;
		grid-column: 1 / -1;
	}
}

/* ------------------------------------------------------------------
   Capability band — the reference scrolls these six labels past on an
   infinite marquee. It is decorative, auto-starting and runs longer
   than five seconds, which is exactly what WCAG 2.2.2 asks for a pause
   control on; a static row carries the same information and needs no
   exemption. They are links here rather than plain text, so the band
   also does something.
   ------------------------------------------------------------------ */
.ag-band {
	background: var(--ag-panel);
	border-top: 1px solid var(--ag-line-1);
	border-bottom: 1px solid var(--ag-line-1);
}

.ag-band__list {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	/* tuned so all six labels hold one row from 1280px up — the reference runs
	   them past on a single marquee line and a lone orphan on row two loses
	   that reading */
	gap: 0 clamp(0.8rem, 1.7vw, 1.6rem);
	margin: 0;
	padding: 0;
	list-style: none;
}

.ag-band__list li {
	display: flex;
	align-items: center;
	gap: clamp(0.8rem, 1.7vw, 1.6rem);
}

.ag-band__list li + li::before {
	content: "/";
	color: rgba(255, 255, 255, 0.38);
	font-size: 1.1rem;
}

.ag-band__list a {
	display: inline-block;
	padding-block: 0.75rem;
	font-family: var(--wp--preset--font-family--display);
	font-size: clamp(1rem, 0.85rem + 0.5vw, 1.3rem);
	font-weight: 600;
	letter-spacing: -0.01em;
	color: #fff; /* 12.72:1 on the panel */
	text-decoration: none;
	transition: opacity var(--ag-dur) ease;
}

/*
 * Hover moves the underline, not the colour: every alternative that reads as
 * "link" on this panel (accent-text included) is dimmer than the resting
 * white, so a colour hover would lower contrast on interaction.
 */
.ag-band__list a:hover {
	text-decoration: underline;
	text-underline-offset: 0.3em;
	text-decoration-thickness: 2px;
}

/*
 * Wrapped rows each opened with a slash — `li + li::before` cannot know it is
 * the first item on a new line, so the separator that reads as "between" on
 * one row reads as a stray mark at the start of the next two. Below the
 * breakpoint the row becomes a two-column grid and the separators go away.
 */
@media (max-width: 781.98px) {
	.ag-band__list {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 0 1.5rem;
		max-width: 30rem;
		margin-inline: auto;
	}

	.ag-band__list li {
		gap: 0;
	}

	.ag-band__list li + li::before {
		content: none;
	}
}

/*
 * Columns stretch, the card inside one does not — four cards of unequal copy
 * length left four different bottom edges under a single rule of numbers.
 */
.ag-card-row .wp-block-column > .ag-card {
	height: 100%;
}

/* ------------------------------------------------------------------
   Stat tile grid (2x2 on the About sections).

   Two defects it replaces:
   1. The tiles were plain children of two stacked columns, so each one
      sized to its own label. "Security assessments delivered" wrapped to
      two lines at 1440px and its tile rendered 182px against its
      neighbours' 156px — four numbers meant to be read against each
      other, on four different baselines, with the two columns ending
      15px apart.
   2. The second column carried an inline `margin-top: 2.5rem` for the
      collage stagger. Below 782px WP stacks the columns, and that offset
      survived as a stray 60px hole between tiles two and three in an
      otherwise 20px-gapped stack.

   Both go away if the four tiles become one 2x2 grid rather than two
   independent stacks. `display: contents` dissolves the wp:column
   wrappers so all four tiles are direct grid items — `grid-auto-rows:
   1fr` then gives every tile the same box whatever its label does, and
   reading order becomes left-to-right instead of down-then-across,
   which is how a row of comparable figures is read anyway.

   The stagger is dropped with it. It buys nothing on a 2x2 of numbers —
   unlike the photo collage it was copied from, these four want a common
   baseline so they compare.
   ------------------------------------------------------------------ */
.wp-block-columns.ag-stat-grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	grid-auto-rows: 1fr;
	gap: 1.25rem;
}

.ag-stat-grid > .wp-block-column {
	display: contents;
}

.ag-stat-grid .ag-card {
	/* the constrained-layout auto margins again, plus any leftover stagger */
	margin-top: 0 !important;
}

/* One column below the breakpoint WP would have stacked the columns at;
   equal-height rows stop being a virtue once there is one tile per row. */
@media (max-width: 781.98px) {
	.wp-block-columns.ag-stat-grid {
		grid-template-columns: minmax(0, 1fr);
		grid-auto-rows: auto;
	}
}

/* ------------------------------------------------------------------
   Half-panel split. Copy sits on the blue slab, flush with the 1290px
   container's left edge while the slab itself bleeds to the viewport;
   the drawn panel takes the other half.
   ------------------------------------------------------------------ */
.ag-split {
	display: grid;
}

.ag-split__copy {
	background: var(--ag-panel);
	padding-block: var(--wp--preset--spacing--70);
	padding-inline: var(--ag-edge) clamp(1.5rem, 4vw, 4rem);
}

/*
 * The copy half is a constrained group, so WP emits `margin-left/right: auto
 * !important` for every child and the 560px column centred itself inside the
 * half — leaving the eyebrow floating well right of the container edge the
 * section's whole point is to sit on. The core rule is :where()-wrapped at
 * (0,0,0); a plain class selector outranks it.
 */
.ag-split__copy > * {
	margin-inline: 0 !important;
}

.ag-split__figure {
	min-height: clamp(16rem, 38vw, 30rem);
	display: grid;
	place-items: center;
	background-color: var(--ag-surface);
	background-image:
		radial-gradient(ellipse 60% 70% at 40% 30%, rgba(63, 114, 192, 0.30), transparent 70%),
		linear-gradient(rgba(148, 156, 200, 0.06) 1px, transparent 1px),
		linear-gradient(90deg, rgba(148, 156, 200, 0.06) 1px, transparent 1px);
	background-size: auto, 32px 32px, 32px 32px;
}

.ag-split__figure svg {
	width: clamp(5rem, 12vw, 9rem);
	height: auto;
	color: rgba(111, 163, 234, 0.42);
}

@media (min-width: 782px) {
	.ag-split {
		grid-template-columns: 1fr 1fr;
	}
}

@media (max-width: 781.98px) {
	/* single column: the copy no longer aligns to a container edge, so it
	   takes the ordinary root padding on both sides */
	.ag-split__copy {
		padding-inline: clamp(1rem, 0.5rem + 2vw, 2.25rem);
	}
}

/* Ticks inherit the pricing list, but on the panel the accent needs the
   brighter text blue to stay legible against #222E66. */
.ag-split__copy .ag-ticks li {
	color: #D4D6E6;
	font-size: var(--wp--preset--font-size--medium);
}

/* ============================================================
   13. SERVICES PAGE
   ============================================================ */

/*
 * Filled tile rather than the outlined circle .ag-icon draws. The circle is
 * the quiet variant used inside prose-led sections (feature strip, About
 * capabilities); a catalogue of twelve cards needs each card to announce
 * itself from a scroll, and the tile is the reference layout's own answer.
 */
.ag-icon--tile {
	width: 54px;
	height: 54px;
	border: 0;
	border-radius: 14px;
	background: linear-gradient(140deg, var(--ag-accent), #6FA3EA);
	box-shadow: var(--ag-glow-accent);
}

.ag-icon--tile svg {
	width: 26px;
	height: 26px;
	color: #fff;
}

/*
 * The whole card is the target, but only the heading is a link.
 *
 * The reference puts a "Learn More" button in every card, which at twelve
 * cards means twelve more tab stops all pointing at destinations the heading
 * above them already names. This inverts it: one link per card, stretched to
 * the card's own box, so the pointer target is the full card while the
 * accessibility tree stays one link deep. .ag-card is already positioned.
 */
.ag-svc-card h3 a::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: var(--ag-radius-lg);
}

/*
 * Grid items stretch, so the twelve cards already share a row height — but
 * their contents did not, and a two-line title ("Application Security &
 * DevSecOps") pushed its cue 22px below the two beside it. Flex column plus
 * `margin-top: auto` parks every cue on the card's floor instead.
 *
 * The margin-inline reset is mandatory once this is a flex container: WP emits
 * `margin-left/right: auto !important` for children of a constrained layout,
 * which resolve to 0 on a block container but absorb free space on a flex one
 * — the same failure the pricing card was annotated for. The icon tile keeps
 * its own width, so cross-axis stretch leaves it alone.
 */
.ag-svc-card {
	display: flex;
	flex-direction: column;
}

.ag-svc-card > * {
	margin-inline: 0 !important;
}

.ag-svc-points {
	list-style: none;
	/* constrained-layout auto margins, same as .ag-ticks */
	margin: 0.9rem 0 0 !important;
	padding: 0;
}

.ag-svc-points li {
	position: relative;
	padding-left: 1.1rem;
	margin-block: 0.4rem;
	font-size: 0.9375rem;
	line-height: 1.45;
	color: var(--ag-fg-muted);
}

.ag-svc-points li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.62em;
	width: 5px;
	height: 5px;
	border-radius: 50%;
	background: var(--ag-accent-text);
}

/*
 * Affordance cue, not a second link — the heading link above already carries
 * the destination and the stretched ::after already makes this row clickable.
 * Marked aria-hidden in the markup so it is not read as duplicate link text.
 */
.ag-svc-more {
	display: flex;
	align-items: center;
	gap: 0.45rem;
	/* auto, not a fixed gap — see the flex note above */
	margin-top: auto !important;
	padding-top: 1.15rem;
	font-size: 0.9375rem;
	font-weight: 600;
	color: var(--ag-accent-text);
}

.ag-svc-more::after {
	content: "→";
	transition: transform var(--ag-dur) var(--ag-ease);
}

.ag-card:hover .ag-svc-more::after {
	transform: translateX(3px);
}

/* ------------------------------------------------------------------
   Why-choose-us cards. The reference fills the right half of each with
   a stock render; here the oversized outlined numeral does that work,
   which is the treatment the service cards on the front page already
   use for their index numbers.
   ------------------------------------------------------------------ */
.ag-why-card {
	display: grid;
	grid-template-columns: 1fr auto;
	align-items: center;
	gap: clamp(1rem, 3vw, 2.5rem);
}

.ag-why-card > * {
	margin-inline: 0 !important;
}

.ag-num--xl {
	font-size: clamp(3rem, 2rem + 5vw, 5.5rem);
	-webkit-text-stroke-width: 2px;
}

/* Below the breakpoint the numeral stops being a counterweight and starts
   being a second column squeezing the copy — it moves above it. */
@media (max-width: 599.98px) {
	.ag-why-card {
		grid-template-columns: 1fr;
	}

	.ag-why-card .ag-num--xl {
		grid-row: 1;
	}
}

/* ============================================================
   14. SERVICE DETAIL PAGE
   ============================================================ */

/*
 * Sidebar + article. The reference pins a list of its six sibling services
 * down the left of every detail page; with twelve the list is the length of
 * a short section, so it only becomes a column where there is room for one.
 */
.ag-svc-layout {
	display: grid;
	/*
	 * minmax(0, 1fr), not the implicit `auto`. A grid item's automatic minimum
	 * size is its content, so the nav strip — which is meant to scroll inside
	 * its own box below 1024px — instead stretched its track to the full
	 * width of twelve untruncated labels: a 2631px column in a 390px viewport.
	 * `overflow-x: clip` on the body meant this never showed up as a page
	 * scrollbar; it just silently pushed the article off-screen.
	 */
	grid-template-columns: minmax(0, 1fr);
	gap: var(--wp--preset--spacing--50);
}

@media (min-width: 1024px) {
	.ag-svc-layout {
		grid-template-columns: 290px minmax(0, 1fr);
		gap: var(--wp--preset--spacing--60);
		align-items: start;
	}

	.ag-svc-nav {
		position: sticky;
		/* clears the sticky header, which measures 77-84px */
		top: 6rem;
	}
}

.ag-svc-nav__title {
	margin: 0 0 0.9rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--ag-fg-faint);
}

.ag-svc-nav__list {
	list-style: none;
	margin: 0;
	padding: 0;
}

/* "Also available" heads the second list */
.ag-svc-nav__list + .ag-svc-nav__title {
	margin-top: 1.5rem;
}

/* Services page: the on-request group sits a section's gap below the core */
.ag-svc-group--more {
	margin-top: var(--wp--preset--spacing--70);
}

.ag-svc-nav__list a {
	display: block;
	padding: 0.85rem 1rem;
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius);
	background: var(--ag-surface);
	color: var(--ag-fg-muted);
	font-size: 0.9375rem;
	font-weight: 500;
	line-height: 1.35;
	text-decoration: none;
	transition: border-color var(--ag-dur) ease, color var(--ag-dur) ease,
	            background-color var(--ag-dur) ease;
}

.ag-svc-nav__list li + li {
	margin-top: 0.5rem;
}

.ag-svc-nav__list a:hover {
	border-color: var(--ag-line-3);
	color: var(--ag-fg);
	text-decoration: none;
}

/*
 * The current page is emitted as a <span>, not a link — a link to the page
 * you are already on is a dead control. aria-current on a non-link carries
 * the state, and the fill carries it visually.
 */
.ag-svc-nav__list [aria-current] {
	display: block;
	padding: 0.85rem 1rem;
	border: 1px solid var(--ag-accent);
	border-radius: var(--ag-radius);
	background: var(--ag-accent);
	color: #fff; /* 5.72:1 on the accent fill */
	font-size: 0.9375rem;
	font-weight: 600;
	line-height: 1.35;
}

/*
 * Below the column breakpoint the list becomes a scrolling strip. The scroll
 * is inside this container only — the page body never scrolls sideways.
 */
@media (max-width: 1023.98px) {
	.ag-svc-nav__list {
		display: flex;
		gap: 0.5rem;
		overflow-x: auto;
		padding-bottom: 0.5rem;
		/* the strip is a scroll container, so it is focusable; keep the ring */
		scrollbar-width: thin;
		-webkit-overflow-scrolling: touch;
	}

	.ag-svc-nav__list li + li {
		margin-top: 0;
	}

	.ag-svc-nav__list li {
		flex: 0 0 auto;
	}

	.ag-svc-nav__list a,
	.ag-svc-nav__list [aria-current] {
		white-space: nowrap;
		padding: 0.7rem 1rem;
		border-radius: 999px;
	}
}

/* ------------------------------------------------------------------
   Article body.

   The service copy is genuinely long-form — five to seven sections of
   prose, lists and steps — so it gets a measure and a vertical rhythm
   rather than the card treatment used everywhere else on the site.
   ------------------------------------------------------------------ */
.ag-article > * {
	margin-inline: 0 !important;
}

.ag-article h2 {
	margin-top: var(--wp--preset--spacing--60);
	margin-bottom: 1.1rem;
	font-size: clamp(1.5rem, 1.25rem + 1.1vw, 2.1rem);
}

/*
 * Only the opening paragraph loses the top margin.
 *
 * This was `.ag-article > :first-child`, which looks right and is wrong: the
 * article is not one element. Card grids are emitted as sibling wp:group
 * blocks between the prose runs, so a page is several .ag-article divs, and
 * every one of them after the first opens on an h2 — which the rule then
 * stripped, welding "Our process" and "Why choose Secure Debug" to the
 * bottom edge of the card grid above them.
 */
.ag-article > .ag-lead:first-child {
	margin-top: 0;
}

.ag-article p {
	max-width: 70ch;
}

/* ------------------------------------------------------------------
   Numbered process steps. A rail runs behind the markers so the list
   reads as a sequence rather than as six unrelated blocks.
   ------------------------------------------------------------------ */
.ag-steps {
	list-style: none;
	margin: 0 !important;
	padding: 0;
	counter-reset: ag-step;
}

.ag-steps li {
	position: relative;
	counter-increment: ag-step;
	padding-left: 3.5rem;
	padding-bottom: 1.75rem;
}

.ag-steps li:last-child {
	padding-bottom: 0;
}

.ag-steps li::before {
	content: counter(ag-step, decimal-leading-zero);
	position: absolute;
	left: 0;
	top: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.4rem;
	height: 2.4rem;
	border: 1px solid var(--ag-line-3);
	border-radius: 50%;
	background: var(--ag-surface);
	font-family: var(--wp--preset--font-family--display);
	font-size: 0.85rem;
	font-weight: 700;
	color: var(--ag-accent-text);
}

/* The rail. Stops at the last marker rather than trailing past it. */
.ag-steps li:not(:last-child)::after {
	content: "";
	position: absolute;
	left: 1.2rem;
	top: 2.4rem;
	bottom: 0;
	width: 1px;
	background: var(--ag-line-2);
}

.ag-steps strong {
	display: block;
	margin-bottom: 0.3rem;
	color: var(--ag-fg);
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.0625rem;
	font-weight: 600;
}

/* Standards / framework tags */
.ag-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
	list-style: none;
	margin: 0 !important;
	padding: 0;
}

.ag-chips li {
	padding: 0.45rem 0.9rem;
	border: 1px solid var(--ag-line-2);
	border-radius: 999px;
	background: var(--ag-surface);
	font-size: 0.85rem;
	font-weight: 600;
	color: var(--ag-fg-muted);
}

/* ------------------------------------------------------------------
   Sales sections — key facts, action rows, the mid-page CTA, the
   sample report and the FAQ. Generated from tools/services_sales.py.
   ------------------------------------------------------------------ */

/*
 * Key facts. The 1px grid gap over a line-coloured ground draws the
 * dividers, so they stay correct however many columns the row wraps to.
 */
.ag-facts {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
	gap: 1px;
	margin: var(--wp--preset--spacing--50) 0 0;
	overflow: hidden;
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius-lg);
	background: var(--ag-line-2);
}

.ag-facts__item {
	padding: 1.1rem 1.25rem;
	background: var(--ag-surface-2);
}

.ag-facts dt {
	font-size: 0.75rem;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ag-fg-faint);
}

.ag-facts dd {
	margin: 0.35rem 0 0;
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.125rem;
	font-weight: 600;
	line-height: 1.3;
	color: var(--ag-fg);
}

.ag-article .ag-facts__note {
	margin: 0.75rem 0 0;
	font-size: 0.8125rem;
	color: var(--ag-fg-faint);
}

/* Buttons emitted inside wp:html get no block-layout classes, so the row
   lays itself out. */
.ag-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
	margin-top: var(--wp--preset--spacing--40);
}

/* Mid-page CTA: sits straight after the process rail. */
.ag-cta-inline {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1.25rem 2rem;
	margin-top: var(--wp--preset--spacing--60);
	padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
	border: 1px solid rgba(255, 255, 255, 0.12);
	border-radius: var(--ag-radius-lg);
	background: var(--ag-panel);
	box-shadow: var(--ag-inset-hi), var(--ag-shadow-1);
}

.ag-cta-inline__copy {
	flex: 1 1 22rem;
}

.ag-article .ag-cta-inline__title {
	margin: 0 0 0.5rem;
	font-size: clamp(1.3rem, 1.1rem + 0.8vw, 1.7rem);
}

.ag-cta-inline p {
	margin: 0;
	color: var(--ag-fg-muted); /* 5.24:1 on the panel */
}

.ag-cta-inline .ag-actions {
	margin-top: 0;
}

/* Sample report: what it contains, beside a drawn redacted page. */
.ag-report {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
	gap: var(--wp--preset--spacing--50);
	align-items: center;
}

.ag-report__copy > p:first-child {
	margin-top: 0;
}

.ag-report__doc {
	/* border-box: as content-box, width:100% plus the padding ran 50px past
	   the column at 375px wide, and the tilt pushed it off both edges */
	box-sizing: border-box;
	justify-self: center;
	width: 100%;
	max-width: 380px;
	padding: 1.5rem 1.5rem 1.75rem;
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius);
	background: linear-gradient(180deg, var(--ag-surface-2), var(--ag-surface));
	box-shadow: var(--ag-inset-hi), var(--ag-shadow-2);
	transform: rotate(-1.5deg);
}

.ag-report__head {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	font-size: 0.7rem;
	font-weight: 700;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--ag-fg-faint);
}

.ag-report__tag {
	color: var(--ag-gold);
}

.ag-article .ag-report__title {
	margin: 1.25rem 0 1rem;
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.25rem;
	font-weight: 700;
	line-height: 1.25;
	color: var(--ag-fg);
}

/* Severity split — decorative, so the hues are not palette presets. */
.ag-report__bars {
	display: flex;
	gap: 4px;
	height: 8px;
	margin-bottom: 1.25rem;
}

.ag-report__bars span {
	border-radius: 999px;
}

.ag-report__bars span:nth-child(1) { flex: 1; background: #E5484D; }
.ag-report__bars span:nth-child(2) { flex: 2; background: #F2884B; }
.ag-report__bars span:nth-child(3) { flex: 3; background: var(--ag-gold); }
.ag-report__bars span:nth-child(4) { flex: 4; background: var(--ag-accent-text); }

.ag-report__line {
	display: block;
	height: 8px;
	margin-top: 0.7rem;
	border-radius: 999px;
	background: rgba(148, 156, 200, 0.18);
}

.ag-report__line:nth-of-type(2) { width: 86%; }
.ag-report__line:nth-of-type(3) { width: 94%; }
.ag-report__line:nth-of-type(4) { width: 58%; }
.ag-report__line:nth-of-type(5) { width: 90%; }
.ag-report__line:nth-of-type(6) { width: 72%; }

/* FAQ — native <details>, so it works without script. */
.ag-faq {
	max-width: 860px;
	border-top: 1px solid var(--ag-line-2);
}

.ag-faq__item {
	border-bottom: 1px solid var(--ag-line-2);
}

.ag-faq__item summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 1.15rem 0.25rem;
	list-style: none;
	cursor: pointer;
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.0625rem;
	font-weight: 600;
	line-height: 1.4;
	color: var(--ag-fg);
	transition: color var(--ag-dur) ease;
}

.ag-faq__item summary::-webkit-details-marker {
	display: none;
}

/* Chevron: points down closed, up open. */
.ag-faq__item summary::after {
	content: "";
	flex: 0 0 auto;
	width: 0.55rem;
	height: 0.55rem;
	margin-right: 0.35rem;
	border-right: 2px solid var(--ag-accent-text);
	border-bottom: 2px solid var(--ag-accent-text);
	transform: translateY(-2px) rotate(45deg);
	transition: transform var(--ag-dur) var(--ag-ease);
}

.ag-faq__item[open] summary::after {
	transform: translateY(2px) rotate(-135deg);
}

.ag-faq__item summary:hover {
	color: var(--ag-accent-text);
}

/* summary is not in the global focus-ring selector */
.ag-faq__item summary:focus-visible {
	outline: 2px solid var(--ag-accent-text);
	outline-offset: 2px;
	border-radius: 4px;
}

.ag-faq__answer {
	padding: 0 0.25rem 1.25rem;
}

.ag-faq__answer p {
	margin: 0;
	color: var(--ag-fg-muted);
}

@media (prefers-reduced-motion: reduce) {
	.ag-faq__item summary::after {
		transition: none;
	}
}

/* ============================================================
   15. CONTACT PAGE
   ============================================================ */

.ag-contact-grid {
	display: grid;
	/* minmax(0,1fr) for the same reason as .ag-svc-layout: a grid item's
	   automatic minimum is its content, and a textarea does not shrink. */
	grid-template-columns: minmax(0, 1fr);
	gap: var(--wp--preset--spacing--50);
}

@media (min-width: 900px) {
	.ag-contact-grid {
		/* The form is the primary action and takes the larger track. The
		   reference gives the two halves equal width, which reads as two
		   equally weighted choices rather than one route with a fallback. */
		grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
		gap: var(--wp--preset--spacing--60);
	}
}

/* ------------------------------------------------------------------
   Contact channels. The email address is a real link, so nobody has
   to retype it.
   ------------------------------------------------------------------ */
.ag-channels {
	list-style: none;
	margin: 0;
	padding: 0;
}

.ag-channels li + li {
	margin-top: 1.5rem;
	padding-top: 1.5rem;
	border-top: 1px solid var(--ag-line-2);
}

.ag-channel {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 1rem;
	align-items: start;
}

.ag-channel__label {
	display: block;
	margin-bottom: 0.2rem;
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ag-fg-faint);
}

.ag-channel__value {
	display: block;
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.0625rem;
	font-weight: 600;
	line-height: 1.4;
	color: var(--ag-fg);
}

.ag-channel a.ag-channel__value {
	color: var(--ag-accent-text);
	text-decoration: none;
	/* padding + negative margin buys the 44px target without moving the row */
	padding-block: 0.6rem;
	margin-block: -0.6rem;
}

.ag-channel a.ag-channel__value:hover {
	text-decoration: underline;
	text-underline-offset: 0.25em;
}

.ag-channel__note {
	display: block;
	margin-top: 0.25rem;
	font-size: 0.9375rem;
	color: var(--ag-fg-muted);
}

/* ------------------------------------------------------------------
   Form.

   Labels are visible and bound with `for`, not placeholders. The
   reference ships placeholder-only fields: the label vanishes the
   moment you start typing, which leaves anyone who is interrupted
   mid-form — or using speech input, or magnification — guessing what
   the box under the cursor was for.
   ------------------------------------------------------------------ */
.ag-form {
	display: grid;
	gap: 1.15rem;
}

/* Shown when the visitor arrived from a service page's CTA. */
.ag-form__topic {
	justify-self: start;
	margin: 0;
	padding: 0.45rem 0.9rem;
	border: 1px solid var(--ag-line-2);
	border-radius: 999px;
	font-size: 0.875rem;
	color: var(--ag-fg-muted);
}

.ag-form__topic strong {
	color: var(--ag-fg);
}

@media (min-width: 560px) {
	.ag-form__row {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 1.15rem;
	}
}

.ag-field {
	display: grid;
	gap: 0.45rem;
}

.ag-field label {
	font-size: 0.9375rem;
	font-weight: 600;
	color: var(--ag-fg);
}

.ag-field__opt {
	font-weight: 400;
	color: var(--ag-fg-faint);
}

.ag-field input,
.ag-field textarea {
	width: 100%;
	padding: 0.85rem 1rem;
	font-family: var(--wp--preset--font-family--body);
	/* 16px floor: iOS Safari zooms the viewport on focus for anything
	   smaller, and the page does not zoom back out afterwards. */
	font-size: 1rem;
	color: var(--ag-fg);
	background: var(--ag-surface-2);
	/* the only visual boundary of a control — WCAG 1.4.11's 3:1 applies,
	   see --ag-line-control */
	border: 1px solid var(--ag-line-control);
	border-radius: var(--ag-radius);
	transition: border-color var(--ag-dur) ease, background-color var(--ag-dur) ease;
}

.ag-field textarea {
	min-height: 9rem;
	resize: vertical;
}

.ag-field input::placeholder,
.ag-field textarea::placeholder {
	color: var(--ag-fg-faint);
	opacity: 1; /* Firefox dims placeholders by default */
}

.ag-field input:hover,
.ag-field textarea:hover {
	border-color: var(--ag-accent-text);
}

.ag-field :is(input, textarea):focus-visible {
	border-color: var(--ag-accent-text);
	background: var(--ag-surface);
}

/* Server-side validation marks the offending field, and the message sits
   with it rather than only in a summary at the top. */
.ag-field[data-invalid] :is(input, textarea) {
	border-color: #FF8A8A; /* 6.1:1 on --ag-surface-2 */
}

.ag-field__error {
	font-size: 0.9375rem;
	color: #FF8A8A;
}

/* Honeypot: off-screen rather than display:none, so a bot filling every
   field it can see still trips it. Never shown, never focusable. */
.ag-hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.ag-form__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem;
	margin-top: 0.35rem;
}

.ag-form__submit {
	padding: 0.95rem 1.8rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: 1rem;
	font-weight: 600;
	color: var(--ag-fg);
	background: var(--ag-accent);
	border: 0;
	border-radius: 6px;
	cursor: pointer;
	transition: background-color var(--ag-dur) ease, box-shadow var(--ag-dur) ease,
	            transform var(--ag-dur) var(--ag-ease);
}

.ag-form__submit:hover {
	background: #3768B4;
	box-shadow: var(--ag-glow-accent);
	transform: translateY(-2px);
}

.ag-form__hint {
	font-size: 0.9375rem;
	color: var(--ag-fg-muted);
}

/* UK GDPR transparency at the point of collection: the form takes a name,
   an email and a phone number, so it says what they are for. */
.ag-form__privacy {
	margin: 0;
	font-size: 0.875rem;
	color: var(--ag-fg-muted);
}

.ag-form__privacy a {
	color: var(--ag-accent-text);
}

/* ------------------------------------------------------------------
   Submission result. Rendered server-side after the redirect, so it
   survives the page load that follows the POST; role="status" means a
   screen reader hears it without the focus having to move.
   ------------------------------------------------------------------ */
.ag-notice {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 0.85rem;
	align-items: start;
	padding: 1.1rem 1.25rem;
	border-radius: var(--ag-radius);
	border: 1px solid;
	font-size: 0.9375rem;
}

.ag-notice--ok {
	border-color: rgba(126, 217, 168, 0.5);
	background: rgba(126, 217, 168, 0.10);
	color: #A8E6C4; /* 8.9:1 on the composited background */
}

.ag-notice--err {
	border-color: rgba(255, 138, 138, 0.5);
	background: rgba(255, 138, 138, 0.10);
	color: #FFB3B3;
}

.ag-notice strong {
	display: block;
	margin-bottom: 0.2rem;
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.0625rem;
}

.ag-notice svg {
	width: 22px;
	height: 22px;
	margin-top: 0.1rem;
	flex-shrink: 0;
}

/* ============================================================
   16. BLOG GRID

   Stacked card: a 16:9 image on top, then the category, the headline
   and the date. The tile used to lay the text over a full-bleed,
   portrait-cropped image. The covers are illustrations with their own
   titles in them, so the crop cut those titles off at both sides and
   the overlaid headline sat on top of the image's own lettering.
   Every featured image is now 16:9 or close to it (square originals
   were re-framed on a blurred backdrop), so the frame below crops
   almost nothing.
   ============================================================ */

.ag-post-tile {
	--ag-tile-pad: clamp(1.1rem, 0.95rem + 0.5vw, 1.4rem);
	position: relative;
	isolation: isolate;
	display: flex;
	flex-direction: column;
	/* Fill the grid row so every card in it ends on the same line; the
	   date is pushed to the bottom with margin-top:auto. */
	height: 100%;
	/*
	 * A single-result archive (one post in a category, one search hit)
	 * gets a full-width track. 430px is what three tiles measure in the
	 * 1290px container, so a lone tile matches its neighbours on the
	 * pages where it has some. Capping the tile rather than the track
	 * keeps the column count honest.
	 */
	max-width: 430px;
	padding: 0 !important;
	overflow: hidden;
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius-lg);
	background: var(--ag-surface-2);
	box-shadow: var(--ag-inset-hi), var(--ag-shadow-1);
	transition: border-color var(--ag-dur) ease, box-shadow var(--ag-dur) ease,
	            transform var(--ag-dur) var(--ag-ease);
}

.ag-post-tile > * {
	/* constrained-layout auto margins and block gap */
	margin: 0 !important;
	max-width: none !important;
}

.ag-post-tile:hover {
	border-color: var(--ag-line-3);
	box-shadow: var(--ag-inset-hi), var(--ag-shadow-2);
	transform: translateY(-4px);
}

/* The box is fixed before the image arrives, so nothing shifts as it loads. */
.ag-tile-media {
	position: relative;
	aspect-ratio: 16 / 9;
	overflow: hidden;
	border-radius: 0;
	background: var(--ag-panel);
	border-bottom: 1px solid var(--ag-line-2);
}

/* The drawn placeholder shares .ag-card-media--empty with the stacked
   card; inside the tile it keeps the 16:9 box and square corners. */
.ag-tile-media.ag-card-media--empty {
	aspect-ratio: 16 / 9;
	height: auto;
	border-radius: 0;
}

.ag-tile-media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.5s var(--ag-ease);
}

.ag-post-tile:hover .ag-tile-media img {
	transform: scale(1.04);
}

/* Category pill. On a card it is a plain label under the stretched link,
   so a click on it opens the post (see the post-terms filter). */
.ag-tile-terms {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem;
	padding: var(--ag-tile-pad) var(--ag-tile-pad) 0;
	font-size: 0;
}

/* One pill per card. A second category wrapped onto a new line and
   pushed that card's headline below its neighbours'. The filter already
   prints just one; this covers markup that skips it. */
.ag-post-tile .ag-tile-terms > * + * {
	display: none;
}

.ag-tile-terms a,
.ag-tile-term {
	display: inline-block;
	padding: 0.4rem 0.85rem;
	border-radius: 999px;
	background: var(--ag-accent);
	color: #fff; /* 5.72:1 on the accent fill */
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	text-decoration: none;
	transition: background-color var(--ag-dur) ease;
}

.ag-tile-terms a:hover {
	background: #3768B4;
	text-decoration: none;
}

/* single.html reuses the pills above the post title, with no padding. */
.ag-mesh .ag-tile-terms {
	padding: 0;
}

/* Core prints a separator between terms; the pills carry the separation. */
.ag-tile-terms .wp-block-post-terms__separator {
	display: none;
}

.ag-post-tile h2 {
	margin: 0.85rem var(--ag-tile-pad) 0 !important;
	font-size: clamp(1.1rem, 1rem + 0.4vw, 1.3rem);
	line-height: 1.3;
	text-wrap: balance;
}

/*
 * One link per tile, stretched over the whole box, the same treatment
 * the service cards use. Nothing in the tile sits above it, so a click
 * anywhere (image, pill, date) opens the post.
 */
.ag-post-tile h2 a {
	color: var(--ag-fg);
	text-decoration: none;
}

.ag-post-tile h2 a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	border-radius: var(--ag-radius-lg);
}

.ag-post-tile h2 a:hover {
	color: var(--ag-accent-text);
	text-decoration: none;
}

.ag-tile-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem 0.85rem;
	margin-top: auto !important;
	padding: 1rem var(--ag-tile-pad) var(--ag-tile-pad);
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.8rem;
	color: #C2C4D6; /* 9.3:1 on --ag-surface-2 */
}

.ag-tile-meta .wp-block-post-author-name,
.ag-tile-meta .wp-block-post-date {
	margin: 0 !important;
}

/* The date link is redundant with the tile link and adds a tab stop per
   card; core renders it as plain text when isLink is false, this only
   covers the archive-link case. */
.ag-tile-meta a {
	color: inherit;
	text-decoration: none;
}

/*
 * Core emits `repeat(auto-fill, minmax(<minimumColumnWidth>, 1fr))` for a
 * grid-layout post template. auto-fill keeps the empty tracks: with three
 * posts in a 1290px container it laid out four 304px columns and left the
 * three tiles huddled against the left edge under a full-width heading,
 * with 370px of dead space beside them. auto-fit collapses the empty
 * tracks so the tiles share the row — which is what the reference does,
 * and what the grid does anyway once there are more posts than columns.
 *
 * The ceiling that stops a single-result archive stretching one tile
 * across the whole container belongs on the tile, not on the track: a
 * `minmax(290px, 430px)` track is *counted* at its maximum, so the grid
 * fitted two 430px columns into 1290px instead of three 414px ones and
 * the blog dropped to two-up. See .ag-post-tile's max-width.
 */
.wp-block-post-template.is-layout-grid {
	grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
}

.ag-blog-intro {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: 1.5rem;
}

@media (prefers-reduced-motion: reduce) {
	.ag-post-tile,
	.ag-tile-media img,
	.ag-tile-terms a {
		transition: none !important;
	}

	.ag-post-tile:hover,
	.ag-post-tile:hover .ag-tile-media img {
		transform: none !important;
	}
}

/* ============================================================
   17. SINGLE POST

   Article + right sidebar, following the reference layout. Until
   now the post body inherited nothing but theme.json's global
   heading style: no measure, no vertical rhythm, and no rules at
   all for lists, quotes, code or tables. Every post on the site
   was set at whatever width its container happened to be.
   ============================================================ */

.ag-post-layout {
	display: grid;
	/* minmax(0,1fr): a code block or a wide table would otherwise blow the
	   track out to its own intrinsic width. */
	grid-template-columns: minmax(0, 1fr);
	gap: var(--wp--preset--spacing--60);
}

@media (min-width: 1024px) {
	.ag-post-layout {
		grid-template-columns: minmax(0, 1fr) 320px;
		align-items: start;
	}
}

/* ------------------------------------------------------------------
   Article body.
   ------------------------------------------------------------------ */
.ag-prose {
	/*
	 * Measured, not assumed. `ch` is the width of "0", which in a
	 * proportional face is far wider than the average letter — 68ch
	 * rendered 794px and fitted 94 characters to the line, well past the
	 * 65-75 band it was meant to hit. 52ch measures ~72 characters in DM
	 * Sans at this size.
	 *
	 * The measure sits on the text, not on the container. As a cap on the
	 * whole body it held code samples, tables and diagrams to 607px too: on
	 * a 1440px screen a code block showed 62 characters and scrolled while
	 * the 280px of column beside it stood empty, yet 95% of the code lines
	 * in our posts are under 80 characters. Text blocks take the measure
	 * below; code, tables and wide blocks span the column.
	 *
	 * 38rem rather than 52ch: ch resolves against each element's own font,
	 * so on an h2 it would be nearly twice the paragraph's width. 38rem is
	 * what 52ch came to at the body size, so headings and paragraphs still
	 * share one edge.
	 */
	--ag-measure: 38rem;
	font-size: 1.0625rem;
	line-height: 1.75;
	color: var(--ag-fg-muted);
	/*
	 * The third time this bites (see .ag-section-title and .ag-lead): a
	 * max-width inside a constrained layout inherits WP's
	 * `margin-left: auto !important` and centres itself. Here it put the
	 * body copy 117px right of the featured image directly above it.
	 */
	margin-inline: 0 !important;
}

/* Three classes deep on purpose: the post-content block's constrained
   layout sets its children's max-width with two-class selectors, and this
   has to win whichever of the two stylesheets prints last. */
.ag-post-layout .ag-prose > * {
	max-width: var(--ag-measure);
	margin-inline: 0 !important;
}

.ag-post-layout .ag-prose > :is(pre, .wp-block-code, .wp-block-preformatted, .wp-block-table, table, .alignwide, .alignfull) {
	max-width: none;
}

.ag-prose p {
	margin-block: 0 1.4em;
}

.ag-prose :is(h2, h3, h4) {
	margin-block: 2.2em 0.6em;
	text-wrap: balance;
}

.ag-prose h2 {
	font-size: clamp(1.45rem, 1.2rem + 1.1vw, 1.95rem);
}

.ag-prose h3 {
	font-size: clamp(1.2rem, 1.05rem + 0.6vw, 1.4rem);
}

.ag-prose h4 {
	font-size: 1.0625rem;
	letter-spacing: 0.01em;
}

.ag-prose > :first-child {
	margin-top: 0;
}

/*
 * The opening paragraph is the one every reader reads. Setting it a step
 * up gives the article an entry point without needing a separate "lead"
 * class the editor would have to remember to apply.
 */
.ag-prose > p:first-child {
	font-size: 1.1875rem;
	line-height: 1.65;
	color: #C2C4D6;
}

/*
 * Desktop: one edge for text and code. With code across the 890px column
 * and paragraphs held to the 608px measure, every paragraph stopped 280px
 * short of the code block under it and read as cut off. From 1280px the
 * article column is capped at 50rem and text fills it, set up to 18px so
 * the line stays readable (~85 characters, the long end of comfortable
 * for technical writing); code there shows ~89 characters, which all but
 * a few lines fit. The sidebar keeps its 320px and the rest is gutter.
 */
@media (min-width: 1280px) {
	.ag-post-layout {
		grid-template-columns: minmax(0, 50rem) 320px;
		justify-content: space-between;
	}

	.ag-prose {
		--ag-measure: none;
		font-size: 1.125rem;
	}

	.ag-prose > p:first-child {
		font-size: 1.25rem;
	}

	/* h4 was 17px, the old body size; it must not drop below the text. */
	.ag-prose h4 {
		font-size: 1.125rem;
	}
}

.ag-prose :is(ul, ol) {
	margin-block: 0 1.4em;
	padding-left: 1.4em;
}

.ag-prose li {
	margin-block: 0.5em;
}

.ag-prose li::marker {
	color: var(--ag-accent-text);
}

.ag-prose a {
	color: var(--ag-accent-text);
	text-decoration: underline;
	text-underline-offset: 0.2em;
	text-decoration-thickness: 1px;
}

.ag-prose a:hover {
	text-decoration-thickness: 2px;
}

.ag-prose strong {
	color: var(--ag-fg);
	font-weight: 600;
}

.ag-prose blockquote {
	margin: 2em 0;
	padding: 0.2em 0 0.2em 1.5em;
	border-left: 3px solid var(--ag-accent);
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.2rem;
	line-height: 1.5;
	color: var(--ag-fg);
}

.ag-prose blockquote p:last-child {
	margin-bottom: 0;
}

.ag-prose :is(code, kbd, samp) {
	padding: 0.15em 0.4em;
	border: 1px solid var(--ag-line-2);
	border-radius: 5px;
	background: var(--ag-surface-2);
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.9em;
	color: #D4D6E6;
}

.ag-prose pre {
	margin-block: 0 1.6em;
	padding: 1.15rem 1.25rem;
	/* A code sample is the one thing on the page that must not wrap, so it
	   scrolls inside its own box rather than widening the page. */
	overflow-x: auto;
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius);
	/*
	 * Scroll shadows, CSS only. The two `local` covers move with the code
	 * and hide the edge shadow when you are already at that end; the two
	 * `scroll` shadows stay pinned to the box. A shadow therefore shows only
	 * on a side with more code to scroll to — the cue macOS's hidden
	 * scrollbars removed, which made long lines look cut off.
	 */
	background:
		linear-gradient(to right, var(--ag-surface-2) 40%, rgba(31, 33, 66, 0)) left / 3rem 100% no-repeat local,
		linear-gradient(to left, var(--ag-surface-2) 40%, rgba(31, 33, 66, 0)) right / 3rem 100% no-repeat local,
		radial-gradient(farthest-side at 0 50%, rgba(5, 6, 20, 0.9), rgba(5, 6, 20, 0)) left / 1.25rem 100% no-repeat scroll,
		radial-gradient(farthest-side at 100% 50%, rgba(5, 6, 20, 0.9), rgba(5, 6, 20, 0)) right / 1.25rem 100% no-repeat scroll,
		var(--ag-surface-2);
	/* Code, not prose: smaller and tighter than the 17px / 1.75 body, so a
	   desktop column holds ~100 characters to the line. */
	font-size: 0.875rem;
	line-height: 1.55;
	tab-size: 4;
	/* Thin, but there before you start scrolling rather than only during. */
	scrollbar-width: thin;
	scrollbar-color: var(--ag-line-3) transparent;
}

.ag-prose pre code {
	padding: 0;
	border: 0;
	background: none;
	font-size: inherit;
}

.ag-prose img,
.ag-prose .wp-block-image img {
	border-radius: var(--ag-radius);
}

.ag-prose figcaption {
	margin-top: 0.6rem;
	font-size: 0.9rem;
	color: var(--ag-fg-faint);
}

.ag-prose hr {
	margin-block: 2.5em;
	border: 0;
	border-top: 1px solid var(--ag-line-2);
}

.ag-prose :is(table) {
	width: 100%;
	margin-bottom: 1.6em;
	border-collapse: collapse;
	font-size: 0.9375rem;
}

.ag-prose :is(th, td) {
	padding: 0.7rem 0.9rem;
	border: 1px solid var(--ag-line-2);
	text-align: left;
}

.ag-prose th {
	background: var(--ag-surface-2);
	color: var(--ag-fg);
	font-weight: 600;
}

/* Wide/full blocks inside the article escape the measure deliberately. */
.ag-prose .alignwide,
.ag-prose .alignfull {
	max-width: none;
	width: auto;
}

/* ------------------------------------------------------------------
   Article footer: tags and prev/next.
   ------------------------------------------------------------------ */
.ag-post-tags {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.6rem;
	margin-top: var(--wp--preset--spacing--50);
	padding-top: var(--wp--preset--spacing--40);
	border-top: 1px solid var(--ag-line-2);
}

.ag-post-tags .wp-block-post-terms__separator {
	display: none;
}

.ag-post-tags a {
	display: inline-block;
	padding: 0.45rem 0.9rem;
	border: 1px solid var(--ag-line-2);
	border-radius: 999px;
	background: var(--ag-surface-2);
	color: var(--ag-fg-muted);
	font-size: 0.85rem;
	font-weight: 600;
	text-decoration: none;
	transition: border-color var(--ag-dur) ease, color var(--ag-dur) ease;
}

.ag-post-tags a:hover {
	border-color: var(--ag-accent-text);
	color: var(--ag-fg);
	text-decoration: none;
}

/* ------------------------------------------------------------------
   Jetpack share buttons, drawn as the tag chips below them.

   Jetpack's own sheet paints white buttons in Open Sans, and its icons
   come from a data: font that the CSP (font-src 'self') blocks for every
   visitor who is not logged in. The icons here are CSS masks instead:
   data: images are allowed, and the glyph takes the text colour.
   Selectors carry .ag-prose to outrank Jetpack's (0,3,3) rules.
   ------------------------------------------------------------------ */
.ag-prose .sharedaddy {
	margin-top: var(--wp--preset--spacing--50);
}

.ag-prose .sharedaddy .sd-block {
	margin: 0;
	padding: 0;
	border: 0;
}

.ag-prose .sharedaddy .sd-title {
	margin: 0 0 0.75rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: 0.78rem;
	font-weight: 600;
	line-height: 1.4;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ag-fg-faint);
}

.ag-prose .sharedaddy .sd-title::before {
	display: none;
}

.ag-prose .sharedaddy .sd-content ul {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.ag-prose .sharedaddy .sd-content ul li {
	margin: 0;
	padding: 0;
}

.ag-prose .sharedaddy .sd-content ul li.share-end {
	display: none;
}

.ag-prose .sharedaddy .sd-content ul li a.sd-button {
	--ag-share-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='18' cy='5' r='3'/%3E%3Ccircle cx='6' cy='12' r='3'/%3E%3Ccircle cx='18' cy='19' r='3'/%3E%3Cpath d='m8.6 13.5 6.8 4M15.4 6.5l-6.8 4'/%3E%3C/svg%3E");
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	margin: 0;
	padding: 0.45rem 0.95rem;
	border: 1px solid var(--ag-line-2);
	border-radius: 999px;
	background: var(--ag-surface-2);
	box-shadow: none;
	color: var(--ag-fg-muted) !important; /* Jetpack sets #2c3338 !important */
	font-family: var(--wp--preset--font-family--body);
	font-size: 0.85rem;
	font-weight: 600;
	line-height: 1.4;
	text-decoration: none;
	text-shadow: none;
	transition: border-color var(--ag-dur) ease, color var(--ag-dur) ease;
}

.ag-prose .sharedaddy .sd-content ul li a.sd-button:hover {
	border-color: var(--ag-accent-text);
	color: var(--ag-fg) !important;
	box-shadow: none;
}

.ag-prose .sharedaddy .sd-content ul li a.sd-button::before {
	content: "";
	position: static;
	display: block;
	flex: none;
	width: 0.95rem;
	height: 0.95rem;
	margin: 0;
	font: 0/0 a;
	background: currentColor;
	-webkit-mask: var(--ag-share-icon) center / contain no-repeat;
	mask: var(--ag-share-icon) center / contain no-repeat;
}

.ag-prose .sharedaddy .sd-content ul li a.sd-button > span {
	margin: 0;
	line-height: inherit;
}

.ag-prose .sharedaddy .sd-content ul li a.share-twitter {
	--ag-share-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z'/%3E%3C/svg%3E");
}

.ag-prose .sharedaddy .sd-content ul li a.share-facebook {
	--ag-share-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z'/%3E%3C/svg%3E");
}

.ag-prose .sharedaddy .sd-content ul li a.share-linkedin {
	--ag-share-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-4 0v7h-4v-7a6 6 0 0 1 6-6z'/%3E%3Crect width='4' height='12' x='2' y='9'/%3E%3Ccircle cx='4' cy='4' r='2'/%3E%3C/svg%3E");
}

.ag-prose .sharedaddy .sd-content ul li a.share-email {
	--ag-share-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='20' height='16' x='2' y='4' rx='2'/%3E%3Cpath d='m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7'/%3E%3C/svg%3E");
}

/* Admin-only link Jetpack adds under the buttons. */
.ag-prose .sharedaddy .share-customize-link {
	margin: 0.75rem 0 0;
	font-size: 0.8rem;
}

.ag-prose .sharedaddy .share-customize-link a {
	color: var(--ag-fg-faint);
}

@media (prefers-reduced-motion: reduce) {
	.ag-prose .sharedaddy .sd-content ul li a.sd-button {
		transition: none;
	}
}

.ag-post-nav {
	display: grid;
	gap: 1rem;
	margin-top: var(--wp--preset--spacing--50);
}

@media (min-width: 700px) {
	.ag-post-nav {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	/* Next stays on the right when there is no previous post, and mirrors
	   Previous by aligning its text to the right edge. */
	.ag-post-nav .post-navigation-link-next {
		grid-column: 2;
		text-align: right;
	}
}

/* Core prints an empty wrapper on the newest and oldest post, which drew
   as a blank card beside the real one. */
.ag-post-nav .wp-block-post-navigation-link:empty {
	display: none;
}

.ag-post-nav > .wp-block-post-navigation-link {
	/* The group is a flow layout, whose 1rem block gap gave the second card
	   a top margin: Next sat 16px lower and 16px shorter than Previous.
	   The grid gap does the spacing here. */
	margin: 0;
}

.ag-post-nav .wp-block-post-navigation-link {
	position: relative;
	padding: 1.15rem 1.35rem;
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius);
	background: var(--ag-surface-2);
	transition: border-color var(--ag-dur) ease, transform var(--ag-dur) var(--ag-ease);
}

.ag-post-nav .wp-block-post-navigation-link:hover {
	border-color: var(--ag-line-3);
	transform: translateY(-2px);
}

/* The "Previous:" / "Next:" prefix is the label; the title is the link. */
.ag-post-nav .post-navigation-link__label {
	display: block;
	margin-bottom: 0.35rem;
	font-size: 0.78rem;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ag-fg-faint);
}

.ag-post-nav a {
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.0625rem;
	font-weight: 600;
	line-height: 1.35;
	color: var(--ag-fg);
	text-decoration: none;
}

/* The whole card is the target: it lifts on hover like a link does, so a
   click anywhere on it should follow the link, not only on the title. */
.ag-post-nav a::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: var(--ag-radius);
}

.ag-post-nav a:hover {
	color: var(--ag-accent-text);
	text-decoration: none;
}

.ag-post-nav .wp-block-post-navigation-link:hover a {
	color: var(--ag-accent-text);
}

/* ------------------------------------------------------------------
   Sidebar.
   ------------------------------------------------------------------ */
@media (min-width: 1024px) {
	.ag-sidebar {
		position: sticky;
		top: 6rem; /* clears the sticky header */
	}
}

.ag-sidebar__block + .ag-sidebar__block {
	margin-top: var(--wp--preset--spacing--50);
}

.ag-sidebar__title {
	margin: 0 0 1.1rem;
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.15rem;
	font-weight: 600;
	color: var(--ag-fg);
}

/*
 * Core's latest-posts list. Its own markup is an <ul> of <li>s with the
 * featured image, title and date as siblings; this turns each row into a
 * thumbnail + text grid, as in the reference.
 */
.ag-sidebar .wp-block-latest-posts__list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.ag-sidebar .wp-block-latest-posts__list li {
	display: grid;
	grid-template-columns: 76px minmax(0, 1fr);
	gap: 0.9rem;
	align-items: start;
	padding-bottom: 1.1rem;
}

/*
 * Not every post has a featured image, and core simply omits the element
 * rather than leaving a placeholder — so the title fell into the 76px
 * thumbnail column and wrapped one word per line while the date sat in
 * the empty column beside it. Without an image there is no thumbnail
 * column to reserve.
 */
.ag-sidebar .wp-block-latest-posts__list li:not(:has(.wp-block-latest-posts__featured-image)) {
	grid-template-columns: minmax(0, 1fr);
}

.ag-sidebar .wp-block-latest-posts__list li + li {
	margin-top: 1.1rem;
	border-top: 1px solid var(--ag-line-2);
	padding-top: 1.1rem;
}

.ag-sidebar .wp-block-latest-posts__featured-image {
	grid-row: span 2;
	margin: 0;
}

.ag-sidebar .wp-block-latest-posts__featured-image img {
	width: 76px;
	height: 76px;
	object-fit: cover;
	border-radius: 8px;
	background: var(--ag-surface-2);
}

.ag-sidebar .wp-block-latest-posts__post-title {
	display: block;
	font-family: var(--wp--preset--font-family--display);
	font-size: 0.9375rem;
	font-weight: 600;
	line-height: 1.4;
	color: var(--ag-fg);
	text-decoration: none;
}

.ag-sidebar .wp-block-latest-posts__post-title:hover {
	color: var(--ag-accent-text);
	text-decoration: none;
}

.ag-sidebar .wp-block-latest-posts__post-date {
	display: block;
	margin-top: 0.3rem;
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.78rem;
	color: var(--ag-fg-faint);
}

/* Category / tag cloud */
.ag-sidebar .wp-block-categories-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.ag-sidebar .wp-block-categories-list li + li {
	margin-top: 0.15rem;
}

.ag-sidebar .wp-block-categories-list a {
	display: block;
	padding: 0.6rem 0;
	color: var(--ag-fg-muted);
	font-size: 0.9375rem;
	text-decoration: none;
	transition: color var(--ag-dur) ease;
}

.ag-sidebar .wp-block-categories-list a:hover {
	color: var(--ag-accent-text);
}

.ag-sidebar .wp-block-tag-cloud a {
	display: inline-block;
	margin: 0 0.35rem 0.5rem 0;
	padding: 0.4rem 0.8rem;
	border: 1px solid var(--ag-line-2);
	border-radius: 999px;
	background: var(--ag-surface-2);
	/* Core sizes tag-cloud links by post count, down to 8pt. */
	font-size: 0.85rem !important;
	color: var(--ag-fg-muted);
	text-decoration: none;
	transition: border-color var(--ag-dur) ease, color var(--ag-dur) ease;
}

.ag-sidebar .wp-block-tag-cloud a:hover {
	border-color: var(--ag-accent-text);
	color: var(--ag-fg);
	text-decoration: none;
}

/* Sidebar CTA */
.ag-sidebar__cta {
	padding: var(--wp--preset--spacing--40);
	border: 1px solid var(--ag-line-2);
	border-radius: var(--ag-radius-lg);
	background: var(--ag-panel);
}

.ag-sidebar__cta p {
	margin: 0 0 1.1rem;
	font-size: 0.9375rem;
	line-height: 1.6;
	color: #D4D6E6;
}

@media (prefers-reduced-motion: reduce) {
	.ag-post-nav .wp-block-post-navigation-link {
		transition: none !important;
	}

	.ag-post-nav .wp-block-post-navigation-link:hover {
		transform: none !important;
	}
}

/* ============================================================
   18. MOTION — every animation gated behind no-preference
   ============================================================ */
/*
 * `screen` matters: in the paged medium a view() timeline never advances, so
 * `animation-fill-mode: both` pins every .ag-rise element in its `from`
 * keyframe — opacity 0. Printing the front page produced 7 pages containing
 * only the section headings; every card, tier and testimonial was blank.
 */
@media screen and (prefers-reduced-motion: no-preference) {
	@keyframes ag-rise {
		from { opacity: 0; transform: translateY(14px); }
		to   { opacity: 1; transform: none; }
	}

	/*
	 * Entrance stagger. animation-timeline: view() only runs where supported;
	 * everywhere else the rule is dropped and content is simply visible.
	 * The resting state is never opacity:0 in the base stylesheet, so content
	 * can never get stuck invisible — a scroll animation must not be the
	 * thing that makes text appear.
	 */
	@supports (animation-timeline: view()) {
		.ag-rise {
			animation: ag-rise linear both;
			animation-timeline: view();
			animation-range: entry 10% cover 26%;
		}
	}

	/* Hero radar: one turn every 8s; each blip flashes as the edge reaches it
	   (a negative delay of 8s × (1 − --at) puts its 0% keyframe there). */
	@keyframes ag-radar-sweep {
		from { transform: rotate(0deg); }
		to   { transform: rotate(360deg); }
	}

	@keyframes ag-radar-ping {
		0%, 8% { opacity: 1; }
		55%, 100% { opacity: 0.45; }
	}

	.ag-radar__sweep {
		animation: ag-radar-sweep 8s linear infinite;
	}

	.ag-radar__blip {
		animation: ag-radar-ping 8s linear infinite;
		animation-delay: calc((var(--at) - 1) * 8s);
	}
}

@media print {
	.ag-rise {
		animation: none !important;
		opacity: 1 !important;
		transform: none !important;
	}
}

@media (prefers-reduced-motion: reduce) {
	.ag-card,
	.wp-block-button__link,
	.ag-header .wp-block-navigation a,
	.ag-footer .wp-block-list li a,
	.ag-card :is(h2, h3) a,
	.ag-card-media img,
	.ag-svc-more::after {
		transition: none !important;
	}

	.ag-card:hover,
	.wp-block-button__link:hover,
	.ag-card:hover .ag-card-media img,
	.ag-card:hover .ag-svc-more::after {
		transform: none !important;
	}
}

/* ============================================================
   19. RESPONSIVE

   max-width queries use the .98 fraction so they never overlap the matching
   min-width query at the boundary.
   ============================================================ */

/* Collapse the nav to a hamburger below 782px (WP handles <600px itself) */
@media (min-width: 600px) and (max-width: 781.98px) {
	.ag-header .wp-block-navigation__responsive-container-open:not(.always-shown) {
		display: flex;
	}

	.ag-header .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) {
		display: none;
	}
}

/* Mobile nav overlay */
/* Core's `:not(.disable-default-overlay)` rule is (0,3,0) and beat the old
   `body .…is-menu-open` (0,2,1). Worse, its padding clamp references the
   unitless root padding token, so the computed top padding collapsed to 0
   and the close button sat flush against the viewport edge. */
.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay),
body .wp-block-navigation__responsive-container.is-menu-open {
	background: var(--ag-bg) !important;
	padding: 1.5rem !important;
}

body .wp-block-navigation__responsive-container.is-menu-open a {
	color: var(--ag-fg) !important;
	font-size: 1.25rem;
	font-family: var(--wp--preset--font-family--display);
	/* the overlay is the only navigation a phone user has; 34px was the one
	   place in the theme that missed the 44px floor */
	padding-block: 0.6rem;
}

@media (max-width: 1023.98px) {
	/* the hero's second line only offsets where there is room for it */
	.ag-display--offset {
		margin-left: 0;
	}
}

@media (max-width: 781.98px) {
	.ag-feature-strip .wp-block-column {
		border-left: 0;
		border-top: 1px solid rgba(255, 255, 255, 0.10);
	}

	.ag-feature-strip .wp-block-column:first-child {
		border-top: 0;
	}
}

@media (max-width: 599.98px) {
	.ag-topbar {
		display: none; /* the address and the email do not fit one row this narrow */
	}
}

/* "Get a Quote" wrapped onto two lines and doubled the sticky header's
   height on 394–420px phones; one line or nothing. */
.ag-header .wp-block-button__link {
	white-space: nowrap;
}

@media (max-width: 419.98px) {
	/* Logo, menu and button need ~377px; below that the logo and the menu win. */
	.ag-header .wp-block-buttons {
		display: none;
	}
}
