/* ============================================================
   Header, footer, and the section library.

   Loaded after main.css. Split out because main.css is the token and
   primitive layer — the thing you read to understand the design system — and
   this is the thing you read to understand a page.
   ============================================================ */

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

/*
 * Two geometries, one bar. See the comment in header.php: over a dark hero the
 * header is fixed and paints nothing, over a light first section it stays a
 * normal sticky bar with a real background. Both become the same dark glass
 * once you scroll past the hero.
 */
.site-header {
	position: sticky;
	top: 0;
	z-index: 50;
	/* background + colour come from the surface class PHP puts on it. */
	transition: background-color .25s ease, color .25s ease, box-shadow .25s ease;
}
.site-header--overlay {
	position: fixed;
	inset-inline: 0;
	background-color: transparent;
}
/*
 * The fixed bar is out of flow, so the first section has to open up enough room
 * for it — otherwise the hero kicker starts underneath the logo.
 */
.header-overlay #main > .section:first-child {
	padding-block-start: calc(var(--space-section) + var(--header-h));
}

/*
 * Scrolled: brown at 96% with a blur behind it, and a hairline of light along
 * the top edge so the bar has an actual edge rather than fading out.
 *
 * The surface tokens flip with it. A cream-first page starts with a light
 * header and ends up dark, and the link and focus colours have to travel with
 * the background or the active item goes from 5.65:1 to 3.1:1 mid-scroll.
 */
.site-header.is-scrolled {
	background-color: color-mix(in oklab, var(--color-brown-900) 96%, transparent);
	box-shadow: 0 1px 0 oklch(100% 0 0 / 0.08), 0 10px 30px -18px oklch(0% 0 0 / 0.7);
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
	color: var(--color-paper);
	--surface-ink: var(--color-paper);
	--surface-ink-soft: var(--color-paper-soft);
	--surface-link: var(--color-teal-lift);
	--surface-kicker: var(--color-teal-lift);
	--surface-focus: var(--color-teal-lift);
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	min-block-size: var(--header-h);
}

/*
 * Padding, not just line-height. The brand link measured 211x20px and failed
 * WCAG 2.2 SC 2.5.8 (target size) — a 20px-tall target is under the 24px
 * minimum, and it had no clear space around it either.
 */
.site-header__brand {
	display: inline-flex;
	align-items: center;
	/* The wordmark leads, the icon follows, with a small gap between them. */
	gap: 0.5rem;
	padding-block: 0.5rem;
	padding-inline-end: 0.75rem;
	text-decoration: none;
	color: inherit;
}

/*
 * The wordmark is the finished brand SVG, shipped as two fixed-colour variants:
 * the DARK mark (ink + berry) for a light header, and the LIGHT mark for the
 * dark/overlay bar. The header state picks which shows; the other is hidden.
 * This is the stacked (vertical) mark — EMPOWER over YOUth — so it is sized by
 * height to nearly the icon's height, letting both lines stay legible; capped by
 * width so it never crowds the burger.
 */
.site-header__wordmark {
	display: block;
	/* A fixed height, well under the icon (56px): the mark leads, the words
	   support it. Fixed — not vw-scaled — because the icon partner is a fixed
	   height at every width, so a scaling wordmark would collapse the lockup's
	   proportion on narrow screens. */
	block-size: calc(var(--header-h) - 2.25rem);
	inline-size: auto;
	max-inline-size: min(42vw, 128px);
}
.site-header__wordmark--light { display: none; }
.site-header--overlay .site-header__wordmark--dark,
.site-header.is-scrolled .site-header__wordmark--dark,
.site-header.is-brown .site-header__wordmark--dark,
.site-header.is-dark .site-header__wordmark--dark,
.site-header.is-berry .site-header__wordmark--dark,
.site-header.is-teal .site-header__wordmark--dark { display: none; }
.site-header--overlay .site-header__wordmark--light,
.site-header.is-scrolled .site-header__wordmark--light,
.site-header.is-brown .site-header__wordmark--light,
.site-header.is-dark .site-header__wordmark--light { display: block; }
/*
 * Berry and teal grounds take the ACCENT wordmark, not the light one: the light
 * mark's "YOUth" line is berry, which sinks tone-on-tone into a berry ground, so
 * that line lifts to pale berry (berry-lift) in this variant. These rules sit
 * AFTER the is-scrolled group on purpose — a berry-first page keeps .is-berry when
 * .is-scrolled is added, and the two selectors are the same specificity, so source
 * order has to hide the light mark and hold the accent through the scrolled brown
 * bar (berry-lift is 4.66:1 there, so it needs no further swap).
 */
.site-header__wordmark--accent { display: none; }
.site-header.is-berry .site-header__wordmark--light,
.site-header.is-teal .site-header__wordmark--light { display: none; }
.site-header.is-berry .site-header__wordmark--accent,
.site-header.is-teal .site-header__wordmark--accent { display: block; }

/* The brand icon leads the lockup. Square (1:1), sized a touch taller than the
   wordmark so the seed mark holds its own; same two fixed-colour variants,
   toggled with the same header-state rules. */
.site-header__brandmark {
	display: block;
	/* Fill the bar: the mark stands as tall as the header allows, with only the
	   brand link's padding as breathing room. */
	block-size: calc(var(--header-h) - 1rem);
	inline-size: auto;
	flex: none;
}
.site-header__brandmark--light { display: none; }
.site-header--overlay .site-header__brandmark--dark,
.site-header.is-scrolled .site-header__brandmark--dark,
.site-header.is-brown .site-header__brandmark--dark,
.site-header.is-dark .site-header__brandmark--dark,
.site-header.is-berry .site-header__brandmark--dark,
.site-header.is-teal .site-header__brandmark--dark { display: none; }
.site-header--overlay .site-header__brandmark--light,
.site-header.is-scrolled .site-header__brandmark--light,
.site-header.is-brown .site-header__brandmark--light,
.site-header.is-dark .site-header__brandmark--light,
.site-header.is-berry .site-header__brandmark--light,
.site-header.is-teal .site-header__brandmark--light { display: block; }
.site-header--overlay .site-header__brandmark,
.site-header.is-scrolled .site-header__brandmark {
	filter: drop-shadow(0 2px 8px oklch(0% 0 0 / 0.4));
}

.site-header__logo { max-block-size: clamp(32px, 7vw, 40px); inline-size: auto; }
/* The shadow is for the overlay bar only, where the mark sits on a photo hero
   and needs separating from it. On a solid cream header it is a grey halo
   around a logo that already has all the contrast it needs. */
.site-header--overlay .site-header__logo,
.site-header.is-scrolled .site-header__logo {
	filter: drop-shadow(0 2px 8px oklch(0% 0 0 / 0.4));
}

.site-nav__list {
	display: flex;
	align-items: center;
	gap: 0.15rem;
	list-style: none;
	margin: 0;
	padding: 0;
}
/*
 * Flex, not list-item. A parent item is TWO controls on one line — the link and
 * its disclosure — and as inline boxes they sat on a shared baseline rather
 * than a shared centre: measured 4px apart vertically, which put the link's
 * rounded hover highlight visibly out of line with the chevron next to it.
 */
.site-nav__list > li {
	position: relative;
	display: flex;
	align-items: center;
	/* Matches the link's own radius, so the pair reads as one rounded control
	   rather than a box with a corner sticking out of it. */
	border-radius: 0.5rem;
	transition: background-color .16s ease;
}

/* The disclosure sits in the space the label would otherwise pad out, so the
   pair reads as one item rather than a link with a button stuck to it. */
.site-nav__list > li.menu-item-has-children > .site-nav__link { padding-inline-end: 0.25rem; }

.site-nav__link {
	display: inline-flex;
	align-items: center;
	/* "Referrals & Visiting" is one label. Left to wrap it became three lines
	   in a bar built for one. */
	white-space: nowrap;
	/* 44px tall, comfortably over the 24px floor and matching the buttons. */
	min-block-size: 44px;
	padding-inline: 0.8rem;
	/* Nav links wear the body font (Figtree), not the serif display face. */
	font-family: var(--font-sans);
	font-weight: 500;
	font-size: 0.9rem;
	color: inherit;
	text-decoration: none;
	border-radius: 0.5rem;
	transition: color .16s ease, background-color .16s ease;
}
/*
 * The page you are on is green. --surface-link is the text-safe accent for
 * whichever surface the bar is currently wearing — neon on dark (8.74:1),
 * neon-ink on cream (5.65:1). Naming the raw token here would be 1.49:1 on a
 * light header, which the token file itself warns about.
 *
 * Colour is not the only signal: aria-current="page" is on the link, so the
 * state is announced as well as shown.
 */
.site-nav__list > li.is-current > .site-nav__link,
.site-nav__list > li.is-current > .site-nav__disclosure {
	color: var(--surface-link);
}
/*
 * Top bar: the hover is the heading-accent pink as TEXT — no box. Which pink is
 * in force follows the ground (the saturated berry on the light bar, the lighter
 * berry-lift on the dark / overlaid / scrolled bar, where the heading accent is
 * itself berry-lift), each cleared for text on its ground. The panels below keep
 * a pink fill; only the bar drops the box, the way the client asked.
 */
.site-header { --nav-hover: var(--color-berry); }
.site-header.is-brown,
.site-header.is-dark,
.site-header.is-berry,
.site-header.is-teal,
.site-header--overlay,
.site-header.is-scrolled { --nav-hover: var(--color-berry-lift); }

/*
 * The colour belongs to the ITEM, not one control.
 *
 * A parent is two controls — the link and its disclosure — so hovering either
 * half turns the pair pink (label AND chevron), and it stays pink while the
 * pointer is in the open panel, so it is clear which item the panel belongs to.
 * Text only, no fill. Wins over the green is-current rule on specificity, so
 * hovering the page you are on shows pink rather than staying green.
 */
.site-nav__list > li:not(.menu-item-has-children) > .site-nav__link:hover { color: var(--nav-hover); }
.site-nav__list > li.menu-item-has-children:hover > .site-nav__link,
.site-nav__list > li.menu-item-has-children:hover > .site-nav__disclosure { color: var(--nav-hover); }

.site-nav { display: flex; align-items: center; gap: 0.75rem; }
.site-nav__cta { margin-inline-start: 0.5rem; white-space: nowrap; }
/*
 * The secondary button (Support Us) is a berry outline, wherever the header sits.
 * The header is in no section, so it inherits no --section-accent; the outline
 * reads berry on the light bar and berry-lift once the bar goes dark on scroll or
 * floats over a hero. .btn-outline draws its border and label from -ink.
 */
.site-nav__cta--support {
	--section-accent: var(--color-berry);
	--section-accent-ink: var(--color-berry);
	--section-accent-pale: var(--color-berry);
}
.site-header.is-scrolled .site-nav__cta--support,
.site-header--overlay .site-nav__cta--support,
.site-header.is-brown .site-nav__cta--support,
.site-header.is-dark .site-nav__cta--support { --section-accent-ink: var(--color-berry-lift); }
/*
 * Berry and teal grounds are the exception: berry-lift is tuned for the brown
 * bands (4.66:1 there) and only reaches 2.59:1 on the berry ground itself, so the
 * Support Us outline label fails AA. On these two coloured-dark grounds the outline
 * takes paper instead — 6.88:1 on berry, 6.86:1 on teal — the same light treatment
 * a berry/teal SECTION already gives its outline buttons.
 */
.site-header.is-berry .site-nav__cta--support,
.site-header.is-teal .site-nav__cta--support { --section-accent-ink: var(--color-paper); }

/* ---- Programmes dropdown ---- */

/*
 * The parent link still goes to the parent page; this button is the only thing
 * that opens the panel. Keeping them separate means the section is reachable
 * without a pointer, which a link-that-is-also-a-toggle never is.
 */
.site-nav__disclosure {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 28px;
	min-block-size: 44px;
	padding: 0;
	border: 0;
	background: transparent;
	color: inherit;
	cursor: pointer;
	border-radius: 0.5rem;
}
.site-nav__chevron {
	inline-size: 12px;
	block-size: 12px;
	transition: transform .18s ease;
}
/* The chevron follows the panel, whichever way it was opened — hover flips it
   too, or a mouse user gets an arrow pointing down at an open menu. */
.site-nav__list > li.is-open > .site-nav__disclosure .site-nav__chevron { transform: rotate(180deg); }
@media (hover: hover) and (min-width: 1200px) {
	.site-nav__list > li:hover > .site-nav__disclosure .site-nav__chevron { transform: rotate(180deg); }
}

.site-nav__submenu {
	list-style: none;
	margin: 0;
	padding: 0.4rem;
	/* Wide enough for "Training for schools & professionals" on one line. The
	   programmes are named, not labelled, and truncating a name is worse than
	   a wide panel. */
	min-inline-size: 19rem;
}

@media (min-width: 1200px) {
	.site-nav__submenu {
		position: absolute;
		top: 100%;
		inset-inline-start: 0;
		/* Opaque: the wide mega panel sits over the hero's bright shapes and text,
		   and any translucency let them bleed through and wash out the links. */
		background-color: var(--color-brown-900);
		border: 1px solid oklch(100% 0 0 / 0.12);
		/* A panel radius, not the button pill: --radius-btn is 999px, which rounds
		   a tall, narrow menu into a blob. */
		border-radius: var(--radius-card);
		box-shadow: 0 18px 40px -20px oklch(0% 0 0 / 0.8);
		/* The panel has its own dark ground, so its ink is light whatever the
		   bar above it is currently wearing. */
		color: var(--color-paper);
		--surface-link: var(--color-teal-lift);
		/* Hidden means hidden: visibility, not opacity alone, or the links stay
		   in the tab order while invisible. */
		opacity: 0;
		visibility: hidden;
		transform: translateY(-0.35rem);
		transition: opacity .16s ease, transform .16s ease, visibility .16s;
	}
	/* Hover is a pointer affordance; the button is the real control. */
	.site-nav__list > li:hover > .site-nav__submenu,
	.site-nav__list > li.is-open > .site-nav__submenu {
		opacity: 1;
		visibility: visible;
		transform: none;
	}
	.site-nav__submenu .site-nav__link {
		display: flex;
		inline-size: 100%;
		/* Padding rather than a fixed height, so a name that does wrap keeps
		   the same breathing room as one that does not. A touch more than before
		   — the client asked for a bit more air between the items. */
		padding: 0.6rem 0.8rem;
		min-block-size: 3rem;
		font-size: 0.9rem;
		line-height: 1.35;
		/* Rounded so the pink hover fill reads as a clean pill inside the panel.
		   Concentric with the panel: the item is inset by the panel's 0.6rem
		   padding, so radius-card − 0.5rem echoes the panel corner rather than
		   fighting it — the same nested-radius the cards use (.card__inner). */
		border-radius: calc(var(--radius-card) - 0.5rem);
	}
	/* The panel is always the dark brown-900 ground, so the hover pink is the
	   berry-lift the heading accent uses there and the label flips to ink — the
	   same treatment the top bar gets, kept consistent inside the panel. */
	.site-nav__submenu .site-nav__link:hover { background: var(--color-berry-lift); color: var(--color-ink); }
	.site-nav__submenu .site-nav__link:hover :is(.site-nav__sublabel, .site-nav__desc) { color: var(--color-ink); }
	/* The pipe separator carries its own dimmed colour, so flip it to a dimmed
	   ink on the pink too rather than leaving a faint white glyph on the fill. */
	.site-nav__submenu .site-nav__link:hover .site-nav__sublabel::before { color: oklch(0% 0 0 / 0.45); }
}

@media (prefers-reduced-motion: reduce) {
	.site-nav__submenu,
	.site-nav__chevron,
	.site-header { transition-duration: .01ms; }
}

/* ---- Rich sub-navigation: group headings, descriptions, the Programmes mega panel ---- */

.site-nav__group-title {
	margin: 0.55rem 0 0.1rem;
	padding-inline: 0.7rem;
	font-family: var(--font-ui, inherit);
	font-size: 0.68rem;
	font-weight: 700;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	color: var(--color-teal-lift);
}
.site-nav__group-list { list-style: none; margin: 0; padding: 0; }

/*
 * A panel link carries a label and, sometimes, a description. It never sits in
 * the flex row the plain submenu links use, so the label and description can
 * stack (Community) or read inline (the mega Programmes column).
 */
.site-nav__submenu .site-nav__sublink { display: block; min-block-size: 0; }
.site-nav__sublabel { display: block; }
.site-nav__desc { color: oklch(100% 0 0 / 0.66); }

/* Community: the description is a full sentence under a slightly stronger label. */
.site-nav__submenu:not(.site-nav__submenu--mega) .site-nav__sublink--rich .site-nav__sublabel { font-weight: 600; }
.site-nav__submenu:not(.site-nav__submenu--mega) .site-nav__desc {
	display: block;
	margin-block-start: 0.1rem;
	font-size: 0.8rem;
	line-height: 1.3;
}

/* Programmes: lead with the acronym (primary), then the plain-English name
   (secondary) after a pipe — "H.O.M.E. | In-home family support". The walker
   emits the acronym first, so the pipe leads the name here (a ::before on the
   name). The acronym takes the full ink and the name the muted tone, the reverse
   of the old label-first order, so the eye lands on the acronym the design leads
   with. */
.site-nav__submenu--mega .site-nav__sublabel { display: inline; color: oklch(100% 0 0 / 0.72); }
.site-nav__submenu--mega .site-nav__desc { display: inline; white-space: nowrap; }
/* A pipe rather than an em dash (the client's call), dimmed a shade below the
   name so it reads as a separator, not a glyph. It leads the plain-English name
   rather than trailing the acronym, so when the pair wraps on a narrow phone the
   pipe goes to the new line with the name ("H.O.M.E." / "| In-home family
   support") instead of dangling at the end of the acronym's line. */
.site-nav__submenu--mega .site-nav__sublabel::before { content: "| "; color: oklch(100% 0 0 / 0.4); margin-inline: 0.15rem; }

@media (min-width: 1200px) {
	/*
	 * The six named programmes in one column, each on its own line so the acronym
	 * and its plain-English name ("H.O.M.E. | In-home family support") stay
	 * together. An EXPLICIT width gives the longest pair ("R.I.S.E. | Mental
	 * health & psychosocial support") room on one line; a name that does wrap
	 * keeps the panel from clamping to its content and spilling onto the page.
	 *
	 * Panel padding and item rhythm are kept in step with the plain Charity /
	 * About dropdowns below, so the three read as one family — the client asked
	 * for consistency across them.
	 */
	.site-nav__submenu--mega {
		display: grid;
		inline-size: 25rem;
		grid-template-columns: minmax(0, 1fr);
		gap: 0.2rem;
		padding: 0.6rem;
	}
	.site-nav__submenu--mega > li { min-inline-size: 0; }
	.site-nav__submenu--mega .site-nav__sublink { line-height: 1.3; }
	/* The plain dropdowns match the mega's panel padding, so all three panels
	   inset their items by the same amount. */
	.site-nav__submenu:not(.site-nav__submenu--mega) { min-inline-size: 20rem; padding: 0.6rem; }
	.site-nav__submenu .site-nav__sublink { padding-block: 0.6rem; }
}

.site-header__toggle {
	display: none;
	align-items: center;
	justify-content: center;
	inline-size: 44px;
	block-size: 44px;
	padding: 0;
	border: 0;
	border-radius: 0.5rem;
	background: transparent;
	color: inherit;
	cursor: pointer;
	font-size: 1.5rem;
}
.site-header__toggle-close { display: none; }
[aria-expanded="true"] .site-header__toggle-open { display: none; }
[aria-expanded="true"] .site-header__toggle-close { display: inline-block; }

/*
 * 1200px, not 900px.
 *
 * Measured rather than picked: brand 265px + the six items 648px + the button
 * 174px + gaps is 1123px of content, and the container adds 64px of padding —
 * so the bar needs 1187px to sit on one line. Below 1200px it was still trying,
 * and "Referrals & Visiting" became three lines while the button became four.
 *
 * Re-measure if a seventh item is ever added: the number is the content's, not
 * a device's.
 */
@media (max-width: 1199px) {
	.site-header__toggle { display: inline-flex; }
	.site-nav {
		position: absolute;
		inset-inline: 0;
		top: 100%;
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		padding: 1rem 1.5rem 1.5rem;
		background-color: color-mix(in oklab, var(--color-brown-900) 98%, transparent);
		-webkit-backdrop-filter: blur(8px);
		backdrop-filter: blur(8px);
		border-block-start: 1px solid oklch(100% 0 0 / 0.10);
		/* display:none, not visibility — a hidden panel must be out of the tab
		   order entirely, or keyboard focus walks into an invisible menu. */
		display: none;
	}
	/* The panel has its own brown background, so its ink is light regardless
	   of what the header is floating over. */
	.nav-open .site-nav {
		display: flex;
		color: var(--color-paper);
		--surface-link: var(--color-teal-lift);
		/*
		 * Six sections, six programmes and a button is taller than a phone in
		 * landscape. Without this the last items are simply unreachable —
		 * the panel hangs off a fixed header, so the page behind it cannot
		 * scroll to them. dvh, not vh: the mobile URL bar moves.
		 */
		max-block-size: calc(100dvh - var(--header-h));
		overflow-y: auto;
		overscroll-behavior: contain;
	}
	.site-nav__list { flex-direction: column; align-items: stretch; }
	.site-nav__cta { margin: 0.75rem 0 0; justify-content: center; }
	/*
	 * Collapsible on a phone: each parent is a link plus its disclosure on one
	 * row, and its children stay hidden until the disclosure is tapped. The
	 * shared disclosure JS already toggles .is-open here; the desktop version
	 * hid the button and left every submenu open, which made the panel long
	 * enough to bury the last items. The row wraps so the panel drops full-width
	 * below the pair; the link fills the row so the disclosure sits at its end.
	 */
	.site-nav__list > li { flex-wrap: wrap; }
	.site-nav__list > li > .site-nav__link { flex: 1 1 auto; }
	.site-nav__disclosure { display: inline-flex; inline-size: 44px; }
	.site-nav__submenu {
		flex-basis: 100%;
		padding: 0 0 0 1rem;
		display: none;
	}
	.site-nav__list > li.is-open > .site-nav__submenu { display: block; }
	/*
	 * Touch height. The submenu links carry min-block-size:0 (set for the desktop
	 * dropdown, where a mouse doesn't need the room); on a phone that left them
	 * ~28px, under every touch-target guideline. 44px is the recognised minimum
	 * (Apple HIG / WCAG 2.5.5) and matches the top-level links. Community's
	 * two-line items are already taller and keep their block flow; the Programmes
	 * one-liners centre within the 44px, wrapping the name below if it is long.
	 */
	/* white-space:normal so long names wrap instead of overflowing — the bar's
	   links are nowrap by design ("Referrals & Visiting" must stay one line), and
	   the sublinks inherit that; on a phone the panel is narrow and they must be
	   free to wrap. The acronym keeps its own nowrap (the mega --desc rule) so
	   "R.I.S.E." never breaks mid-token. */
	.site-nav__submenu .site-nav__link { font-size: 0.9rem; opacity: 0.9; min-block-size: 44px; white-space: normal; }
	/* Programmes' items keep their inline flow (acronym–dash–name reads and wraps
	   as running text); a little block padding sits the text off the top of the
	   44px so it reads centred. Community's items are already two lines. */
	.site-nav__submenu--mega .site-nav__sublink { padding-block: 0.55rem; }
}

/* ---------------- Sections ---------------- */

.section {
	position: relative;
	/*
	 * The splatter is deliberately positioned outside the section's box, and
	 * without this it widens the document: measured at 1498px against a 1440px
	 * viewport, i.e. a horizontal scrollbar on every page carrying one.
	 * `clip` rather than `hidden` so the section never becomes a scroll
	 * container and quietly breaks position:sticky on anything inside it.
	 */
	overflow: clip;
}
.section__inner { position: relative; z-index: 1; }

/*
 * A section you can link to has to stop clear of the header. On pages where
 * the bar floats over the hero it is position:fixed, so #privacy would land
 * with the heading tucked underneath it.
 */
.section[id] { scroll-margin-block-start: calc(var(--header-h) + 1rem); }

/*
 * The bottom border sits on the section's bottom edge. The
 * section is position:relative and clips, so it cannot escape.
 */
.section__rule {
	position: absolute;
	inset-inline: 0;
	inset-block-end: 0;
	z-index: 2;
	margin: 0;
}
.section__head { max-inline-size: 46rem; margin-block-end: 2.5rem; }
.section__head--center { margin-inline: auto; text-align: center; }
.section__title { margin: 0.5rem 0 0; }
/*
 * Text and photo prints a bare .section__title straight into .prose, where
 * .section__head would normally have put a .section__intro carrying its own
 * 1rem. The title has no bottom margin and .prose zeroes its first child top
 * margin, so the two conventions met at zero and the words touched. 1rem is
 * the gap .section__head already produces, not a third value.
 */
.textmedia__body > .section__title { margin-block-end: 1rem; }
/* 60-75 characters is the readable measure; wider and the eye loses the line. */
.section__intro { margin-block-start: 1rem; max-inline-size: 62ch; }
/*
 * Centre the intro's BOX, not just the text inside it.
 *
 * text-align on the head centres the words; it does not move the block they
 * sit in. The intro caps at 62ch inside a 46rem head, so its box was 110px
 * narrower and pinned to the left edge — measured 626px wide in a 736px head,
 * putting its centre 55px left of the heading and the buttons, both of which
 * genuinely were centred. It read as a wonky CTA and was one missing margin.
 */
.section__head--center .section__intro { margin-inline: auto; }
.section__intro p:last-child { margin-block-end: 0; }
.section__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-block-start: 1.75rem; }
.section__head--center + .section__actions { justify-content: center; }

/*
 * The large watermark of the brand icon, tinted to the section's accent.
 *
 * The shape is the icon SVG's alpha, taken as a mask; the colour is a flat fill
 * in the section's accent behind it — the same masked-element idiom as .halftone.
 * Because the fill is --section-accent, the watermark takes each section's accent
 * and its dark-ground -lift automatically (that token already swaps on .is-brown /
 * .is-dark), so one rule tints it in every section. Unlike the old corner
 * flourish it is a WASH, not a mark: opacity 0.1 keeps it faint enough that body
 * copy sitting over it still clears AA (accent at ~10% over the ground moves the
 * blended colour by too little to matter), and .section__inner above it at
 * z-index 1 keeps the text on top. Pinned to the bottom of one side and sized
 * large, but CONTAINED: block-size is capped at 100% of the section (which is
 * position:relative and clips), so on a tall viewport with a short section the
 * mark shrinks to fit the section height instead of overshooting it and being
 * cut off top and bottom. It sits flush on the section's bottom edge — no
 * deliberate overhang. The side inset is 0, not negative, so the mark sits flush
 * INSIDE the section and is never cut off left or right; the icon is square, so
 * one dimension sets both.
 */
.section__watermark {
	position: absolute;
	z-index: 0;
	inset-block-end: 0;
	aspect-ratio: 1 / 1;
	block-size: min(clamp(360px, 66vh, 760px), 100%);
	inline-size: auto;
	opacity: 0.1;
	pointer-events: none;
	background-color: var(--section-accent, var(--color-teal));
	-webkit-mask: url("../img/logo/icon-light.svg") center / contain no-repeat;
	        mask: url("../img/logo/icon-light.svg") center / contain no-repeat;
}
.section__watermark--left  { inset-inline-start: 0; }
.section__watermark--right { inset-inline-end: 0; }
/*
 * A large watermark and the copy compete for the same space on a phone, and
 * decoration must never win that. Below 700px it goes.
 */
@media (max-width: 700px) { .section__watermark { display: none; } }

/*
 * Measure. Body copy set to the full 1200px container runs to roughly 160
 * characters a line, and the eye loses its place returning to the left edge.
 * 68ch sits in the 60-75 range that reads comfortably.
 *
 * Not applied to .section__intro, which sets its own slightly narrower measure,
 * nor to grid children, whose column already constrains them.
 */
.prose { max-inline-size: 68ch; }
.card .prose,
.event__detail,
.faq__a { max-inline-size: none; }

.prose > :first-child { margin-block-start: 0; }
.prose > :last-child { margin-block-end: 0; }
.prose a {
	color: var(--surface-link);
	text-decoration: underline;
	/* Hairline, clearing the descenders — see .link in main.css. */
	text-decoration-thickness: 1px;
	text-underline-offset: 0.35em;
	text-decoration-color: color-mix(in oklab, currentColor 55%, transparent);
	transition: color .14s ease;
}
/* Hover shifts the colour, not the underline — the rest-state hairline stays as
   the in-prose link cue (F73); see .link in main.css. */
.prose a:hover { color: color-mix(in oklch, var(--surface-link), var(--link-hover-toward) var(--link-hover-amt)); }

.notice-inline {
	padding: 0.75rem 1rem;
	border-radius: 0.6rem;
	background: var(--color-cream-200);
	color: var(--color-ink-soft);
	font-size: 0.9rem;
}

/* ---------------- Cards ---------------- */

.cards {
	display: grid;
	gap: 1.25rem;
	list-style: none;
	margin: 0;
	padding: 0;
	/* Columns come from the content: cards stay at least 17rem wide and wrap
	   when they cannot, rather than snapping at invented breakpoints. */
	grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
}
/*
 * One column. Cards stay cards rather than becoming full-width bands, because
 * a 1152px-wide card with three lines in it is not a card.
 *
 * AND THE TRACK IS CENTRED, which the cap on its own forgot to say. A 34rem
 * track in a 1152px row is 608px of nothing, and grid puts it all on one side:
 * the head above it centred, the buttons below it centred, and the card itself
 * jammed against the left margin. Nothing chose that — it is what
 * `justify-content` does when no one sets it.
 *
 * Safe on every other arrangement, because it only has an effect when there is
 * room spare. Beside the words (Layout: text left or right) the grid sits in
 * the narrower column, min() resolves to 100%, the track fills it and there is
 * nothing left to centre.
 */
.cards--1 {
	grid-template-columns: minmax(0, min(100%, 34rem));
	justify-content: center;
}
/*
 * A horizontal single card gets the gutter back on top of that.
 *
 * The media sits in an 11rem padded gutter cut out of the card's own width, so
 * at the vertical cap the words had 344px to run in — 48 characters, which is
 * under the 45–75 a line of prose wants and reads as a column of two- and
 * three-word lines. Widening is scoped to this case for that reason: the
 * VERTICAL card keeps 34rem, where the same words already get a full measure
 * and more width would only push them past it.
 */
.cards--1.cards--horizontal { grid-template-columns: minmax(0, min(100%, 47rem)); }
/*
 * Two and four were minimum WIDTHS, not counts: --2 said "at least 22rem" and
 * fitted three on a wide row, which made a Columns control that says Two and
 * means three. They use the same capped model --3 and --5 already used, so the
 * number an editor picks is the number they get, and they still wrap when the
 * row cannot hold them.
 */
.cards--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, max(22rem, (100% - 1 * 1.25rem) / 2)), 1fr)); }
/*
 * There was no .cards--3 rule at all, so a grid asking for three columns fell
 * through to the 17rem default and laid out FOUR on a wide screen — the class
 * name was describing something nobody had written. This caps the count while
 * keeping the no-breakpoint approach above: past the width where a third of
 * the row exceeds 17rem, a third of the row becomes the minimum track and
 * three is all that fits; below it, 17rem wins again and they wrap to two,
 * then one.
 */
.cards--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, max(17rem, (100% - 2 * 1.25rem) / 3)), 1fr)); }
.cards--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, max(14rem, (100% - 3 * 1.25rem) / 4)), 1fr)); }
/* Five short cards in a row. Below the width where a fifth of the row clears
   11rem they wrap, same no-invented-breakpoints approach as .cards--3. */
.cards--5 { grid-template-columns: repeat(auto-fit, minmax(min(100%, max(11rem, (100% - 4 * 1.25rem) / 5)), 1fr)); }

/*
 * The Cards section's own modifiers.
 *
 * Team, Sponsors and Testimonials each had their own grid before they became
 * sources on one section — identical rules to .cards, differing only in the
 * width a card had to keep. That width is what the Columns control now says
 * out loud, so the three rules collapse into these two lines and the sources
 * keep only what is actually theirs: how a card looks, not how many fit.
 */
.cards--sponsor .card,
.cards--testimonial .card { height: 100%; }

/*
 * Horizontal cards: the media beside the text rather than above it. Grid with
 * an auto first track, so a card with no image is not left holding a gap where
 * one would have gone.
 */
/*
 * Horizontal: the media in a gutter, the rest of the card unchanged.
 *
 * Grid was the obvious way and the wrong one. Spanning the media down the card
 * needs a row count nobody knows in advance, and `span 99` invents 99 tracks —
 * the card came out 590px tall around 390px of content, because 95 empty rows
 * still take their row-gap. A padded gutter with the media placed in it works
 * for a person card, a programme card and a hand-typed one alike, and needs no
 * wrapper element that only two of the five partials have.
 */
/*
 * 11rem, not 9.75rem: the gutter holds a 1.5rem inset and an 8rem tile, so the
 * old value left 4px between the picture and the first word — the text read as
 * if it were touching the tile rather than sitting beside it. 11rem is a 1.5rem
 * gap, the same step the inset uses.
 */
.cards--horizontal > .card {
	padding-inline-start: 11rem;
	min-block-size: 11rem;
}
.cards--horizontal > .card > :is(.card__icontile, .card__icon, .card__illo, .card__media, .person__photo, .person__photo--placeholder, .sponsor__logo, .card__pip) {
	position: absolute;
	inset-inline-start: 1.5rem;
	inset-block-start: 1.5rem;
	inline-size: 8rem;
	block-size: auto;
	margin: 0;
}
/* The person card is a flex column; its body must not keep the photo's slot. */
/* The gutter sets block-size: auto, which is right for a portrait and wrong for
   a tile — a rounded square only as tall as its drawing is not a square. The
   tiles restate their size instead, and the image follows it. */
.cards--horizontal > .card > :is(.card__icontile, .card__icon, .card__pip) {
	--tile-size: 8rem;
	block-size: var(--tile-size);
}
.cards--horizontal > .card > .person__body { inline-size: 100%; }

/*
 * Border. `top` is the accent bar these cards have always drawn, so it is the
 * default and the control names the existing look rather than adding one. The
 * other three sides are the same rule rotated; none removes it.
 *
 * It is the BAR on all four sides, not a border on three of them.
 *
 * There were two mechanisms drawing this line and they did not agree. The bar
 * was a <span> hardcoded into the programme card — 4px, always at the top, in
 * the CARD's own cycling colour — and the Border control was a real border, 3px,
 * in the SECTION's colour, because --card-bar was only ever set under
 * .cards--barred and nothing has emitted that class since Cards replaced the
 * five layouts. So a row of magenta, blue and violet cards came out with four
 * identical green edges, against the rule the rest of the build follows: a run
 * of cards is told apart by colour.
 *
 * Worse, the two could both draw. The bar was suppressed only for `top`, so
 * Bottom, Left and Right kept it AND added the border — two lines on one card —
 * and None removed the border the editor could see while leaving the bar it
 * could not name.
 *
 * One mechanism now, and it is the bar, because that is the look. A border is
 * part of the box, so it pushes content and paints as a stroke around the
 * corner radius; this is a block laid ON the card, clipped by the radius the
 * card already has, which is what gives the top edge its solid rounded cap.
 * None means none, on every source.
 */
.cards--border-top > .card::before,
.cards--border-bottom > .card::before,
.cards--border-left > .card::before,
.cards--border-right > .card::before {
	content: "";
	position: absolute;
	/*
	 * Whichever token this card's family used to name its colour. The manual,
	 * programme, sponsor and testimonial cards carry .card--accent-* and set
	 * --card-accent; the person card carries .person--accent-* and sets
	 * --accent-icon, and that one already holds the violet and magenta lifts
	 * for the dark grounds. Falling through to it is what keeps a team grid
	 * cycling instead of turning five people green.
	 */
	background: var(--card-accent, var(--accent-icon, var(--color-teal)));
	pointer-events: none;
}
/* A vacancy has no accent to announce; a grey bar keeps the card the same
   shape without claiming a colour it has not earned. */
.cards--border-top > .person--vacant::before,
.cards--border-bottom > .person--vacant::before,
.cards--border-left > .person--vacant::before,
.cards--border-right > .person--vacant::before { background: var(--surface-card-border); }
.cards--border-top > .card::before    { inset-block-start: 0; inset-inline: 0; block-size: 4px; }
.cards--border-bottom > .card::before { inset-block-end: 0;   inset-inline: 0; block-size: 4px; }
.cards--border-left > .card::before   { inset-inline-start: 0; inset-block: 0; inline-size: 4px; }
.cards--border-right > .card::before  { inset-inline-end: 0;  inset-block: 0; inline-size: 4px; }
/*
 * The pathway accents at full strength are too dark to register on the brown
 * bands — the bar is decorative (4px, no text), but a bar you cannot see is not
 * decoration either. Each takes the same on-dark lift the pip and outline take.
 */
.is-brown .card--accent-teal,
.is-dark .card--accent-teal,
.is-berry .card--accent-teal,
.is-teal .card--accent-teal { --card-accent: var(--color-teal-lift); --card-accent-hi: var(--color-teal-splash); --card-accent-lo: var(--color-teal); }
.is-brown .card--accent-berry,
.is-dark .card--accent-berry,
.is-berry .card--accent-berry,
.is-teal .card--accent-berry { --card-accent: var(--color-berry-lift); --card-accent-hi: var(--color-berry-splash); --card-accent-lo: var(--color-berry); }

/*
 * Checkerboard on a two-column grid — done here, not in the PHP accent index,
 * so the single-column phone layout keeps the plain berry/teal/berry/teal
 * alternation it needs. The cards render in that linear cycle; only once the
 * grid is genuinely two columns wide do the two off-diagonal cards of each 2×2
 * block swap colour, so the pattern reads berry/teal · teal/berry with no two
 * neighbours alike. Baking it into the index instead left the stacked phone
 * layout as berry, teal, teal, berry — two teals touching in the middle.
 *
 * A CONTAINER query, so CSS and the grid agree about when there are two
 * columns: the threshold is the width .cards--2 actually needs for two tracks
 * (2 × 22rem min + 1.25rem gap ≈ 45.25rem). A viewport media query would only
 * guess, and misfire in a narrow column. Emitted only for the default
 * two-colour cycle (see cards.php) — Single has nothing to alternate and a
 * Custom order is the editor's to keep.
 *
 * Only --card-accent (+ its on-dark -hi/-lo siblings) carries the look: the
 * bar, the pip, the illustration's two tones and the solid button all read it,
 * and --card-accent-on is the same on either accent. nth-child(4n+3) is a berry
 * card in the linear cycle → read teal; nth-child(4n+4) is teal → read berry.
 */
.cards--checker { container-type: inline-size; }
@container (min-width: 45.25rem) {
	.cards--checker > .card:nth-child(4n + 3) { --card-accent: var(--color-teal); }
	.cards--checker > .card:nth-child(4n + 4) { --card-accent: var(--color-berry); }
	/* On the dark bands, the lifted set the accent classes use there — mirrors
	   the four --card-accent rules just above, so a swapped card's bar still
	   registers and its illustration keeps both tones. */
	:is(.is-brown, .is-dark, .is-berry, .is-teal) .cards--checker > .card:nth-child(4n + 3) {
		--card-accent: var(--color-teal-lift); --card-accent-hi: var(--color-teal-splash); --card-accent-lo: var(--color-teal);
	}
	:is(.is-brown, .is-dark, .is-berry, .is-teal) .cards--checker > .card:nth-child(4n + 4) {
		--card-accent: var(--color-berry-lift); --card-accent-hi: var(--color-berry-splash); --card-accent-lo: var(--color-berry);
	}
}

/* The Cards section centres its buttons under the grid, whatever the head
   above them is doing. */
.section--cardset .section__actions { justify-content: center; }

/*
 * Show text — the head and buttons beside the grid rather than over it.
 *
 * This is what Featured profile and Text with a card were: a block of words on
 * one edge, a card on the other. 1.15/0.85 rather than an even split, because
 * the text leads and the cards support it — the ratio Text with a card used
 * for the same arrangement, and four of the five sections folding in here came
 * from it.
 *
 * 900px is where two columns stop fitting, not a device width. Under it the
 * text stacks over the grid, which is `above`, which already works.
 */
.cardset { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 900px) {
	.cardset--text-left  { grid-template-columns: 1.15fr 0.85fr; }
	.cardset--text-right { grid-template-columns: 0.85fr 1.15fr; }
	/* Source order is text-then-cards on both sides — the words say what the
	   cards are, so that is the reading order. Only the painting order moves,
	   which is the same trade the retired section made. */
	.cardset--text-right .cardset__text { order: 2; }
}
.cardset__text > .section__head { margin-block-end: 1.5rem; }
/*
 * Left-aligned beside the grid, against the centred buttons every other
 * cardset gets. Scoped to .cardset — which only exists for left and right —
 * so `below` keeps the centring it is supposed to have, and three classes so
 * it wins on specificity rather than on being further down the file.
 */
.cardset .cardset__text .section__actions { justify-content: flex-start; }
/* Below: the words answer the cards rather than introduce them, and want the
   same air between as a head above one. */
.section--text-below .cardset__text { margin-block-start: 2.5rem; }

.card {
	position: relative;
	overflow: hidden;
	padding: 1.5rem;
	display: flex;
	flex-direction: column;
	gap: 0.6rem;
}
/* The card bar is decorative (4px, no text on it), so it takes each pathway's
   brand value at full strength. */
.card--accent-teal   { --card-accent: var(--color-teal); }
.card--accent-berry  { --card-accent: var(--color-berry); }

/*
 * Card buttons and icons take the CARD's colour, not the section's.
 *
 * --card-accent is the fill (the bar already uses it); --card-accent-on is the
 * label a SOLID card button wears on that fill — its sibling. White on both
 * dark accents on a light ground; ink on every lift on the brown bands, which
 * are light enough to carry dark text. Outline/ghost card buttons and the
 * link-style ones run on --accent-ink, which is already defined per accent. So
 * only the solid button needs a new token, and only .card__btn reads any of
 * this — the section's own buttons, eyebrow and links keep --section-accent.
 */
.card--accent-teal,
.card--accent-berry { --card-accent-on: #fff; }
.is-brown :is(.card--accent-teal, .card--accent-berry),
.is-dark  :is(.card--accent-teal, .card--accent-berry),
.is-berry :is(.card--accent-teal, .card--accent-berry),
.is-teal :is(.card--accent-teal, .card--accent-berry) {
	--card-accent-on: var(--color-ink);
}

/* The solid card button — the card's fill, its on-label, and the same lifted
   shadow the section button draws, restated off --card-accent so it tints too. */
.card__btn.btn-primary {
	background: var(--card-accent, var(--color-berry));
	color: var(--card-accent-on, #fff);
	box-shadow: 0 8px 22px -6px color-mix(in oklch, var(--card-accent, var(--color-berry)) 50%, transparent);
}
.card__btn.btn-primary:hover {
	box-shadow: 0 14px 30px -8px color-mix(in oklch, var(--card-accent, var(--color-berry)) 60%, transparent);
}
/* Outline / ghost card buttons — the card's text-safe accent for label and
   edge, the same 76% the section outline uses. --accent-ink is already the
   legible-per-ground value, so nothing new is needed. */
.card__btn:is(.btn-outline, .btn-ghost) {
	color: var(--accent-ink, var(--color-berry-ink));
	border-color: color-mix(in oklch, var(--accent-ink, var(--color-berry-ink)) 76%, transparent);
}

.card__title { margin: 0; font-size: 1.15rem; }
.card__titlelink { text-decoration: none; transition: color .14s ease; }
/* Card titles rest in the ink colour; hover takes the surface's accent link colour
   (contrast-safe on every ground) rather than sprouting an underline. */
.card__titlelink:hover { color: var(--surface-link); }
.card__meta { margin: 0; font-size: 0.9rem; color: var(--surface-ink-soft); }
/* Same 14px floor as .event__detail, and the same reasoning. */
.card__text { margin: 0; font-size: 0.875rem; color: var(--surface-ink-soft); }
/* A heading written into a card's text is a step down from the section's — the
   card is an aside, and matching the section title would make it compete. It
   takes the surface's own ink: the text around it is deliberately softened, and
   a heading softened as well stops reading as one. */
.card__text :is(h2, h3, h4) {
	margin-block: 1.25rem 0.35rem;
	font-size: 1.05rem;
	color: var(--surface-ink);
}
.card__text > :first-child { margin-block-start: 0; }
.card__badges { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0; }
/*
 * The card's text link takes the card's own colour, so a row of cards reads as
 * a set the way its bar and its kicker already do rather than four identical
 * green links under four different accents.
 *
 * --accent-ink, never --card-accent: this is TEXT. The fill is the bright
 * value and magenta measures 2.83:1 on white; -ink is the readable face of the
 * same accent, and its .is-brown/.is-dark rules already swap magenta and violet
 * for their lifts. --card-ink was a second, incomplete copy of this token with
 * no dark-ground rules and nothing reading it, and it is gone.
 */
.card__link {
	margin-block-start: auto;
	padding-block: 0.35rem;
	color: var(--accent-ink, var(--surface-link));
}

/*
 * Clickable card — the whole surface, not just the link.
 *
 * The card's one primary link is stretched over the card with a ::after overlay.
 * The card is already position:relative and the link stays static, so the
 * overlay's containing block is the CARD and it covers the whole thing. Exactly
 * one real <a> remains: a screen reader announces one link (named by the title),
 * the keyboard focuses it normally, and the rest of the card is target area
 * rather than content to read out.
 *
 * The primary link per card is the programme title, the sponsor's website link,
 * and a manual card's button WHEN it is the only one — a card with two actions
 * keeps them apart rather than guessing which one is the card. Testimonials have
 * no link and stay inert.
 */
.card__titlelink::after,
.sponsor__link::after,
.card__actions > a:only-child::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}
/* A link written into the card's text is not the card's link: it sits above the
   overlay so it stays individually clickable (and its text stays selectable). */
.card__text a { position: relative; z-index: 2; }

/*
 * The affordance: a card carrying a stretched link lifts a little on hover and
 * on keyboard focus, and the focus ring moves from the small title onto the
 * whole card so it frames what a press will open. A secondary button or an
 * in-text link is left alone — the :only-child / :has scoping never matches it,
 * so it keeps its own ring.
 */
.card:has(.card__titlelink),
.card:has(.sponsor__link),
.card:has(.card__actions > a:only-child) {
	transition: transform .18s ease, box-shadow .18s ease;
}
.card:has(.card__titlelink):hover,
.card:has(.sponsor__link):hover,
.card:has(.card__actions > a:only-child):hover,
.card:has(.card__titlelink):focus-within,
.card:has(.sponsor__link):focus-within,
.card:has(.card__actions > a:only-child):focus-within {
	transform: translateY(-3px);
	box-shadow: 0 14px 30px -14px color-mix(in oklch, var(--color-ink) 34%, transparent);
}
.card:has(.card__titlelink:focus-visible),
.card:has(.sponsor__link:focus-visible),
.card:has(.card__actions > a:only-child:focus-visible) {
	outline: 3px solid var(--surface-focus, var(--color-teal-ink));
	outline-offset: 3px;
	border-radius: 5px;
}
.card__titlelink:focus-visible,
.sponsor__link:focus-visible,
.card__actions > a:only-child:focus-visible { outline: none; }

@media (prefers-reduced-motion: reduce) {
	.card:has(.card__titlelink),
	.card:has(.sponsor__link),
	.card:has(.card__actions > a:only-child) { transition: none; }
	.card:has(.card__titlelink):hover,
	.card:has(.sponsor__link):hover,
	.card:has(.card__actions > a:only-child):hover,
	.card:has(.card__titlelink):focus-within,
	.card:has(.sponsor__link):focus-within,
	.card:has(.card__actions > a:only-child):focus-within { transform: none; }
}

/* ---------------- Pills / checklist ---------------- */

.pills { display: flex; flex-wrap: wrap; gap: 0.6rem; list-style: none; margin: 0; padding: 0; }
.pill {
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
	padding: 0.5rem 0.95rem;
	border-radius: 999px;
	background: var(--surface-card-bg);
	border: 1px solid var(--surface-card-border);
	font-size: 0.95rem;
}
/*
 * ARRANGEMENT — how the list runs. Stacked, inline or as pills.
 *
 * This used to be tangled with the mark: Checklist meant "stacked AND ticked",
 * so a stacked list of numbers had no way to be asked for. The two are separate
 * controls now and separate classes, which is what makes the nine combinations
 * fall out of six buttons.
 */
.pills--stacked { flex-direction: column; align-items: stretch; gap: 0.5rem; }
/*
 * The mark sits on the FIRST line, not in the middle of the block.
 *
 * .pill centres its children, which is right for a one-line label and wrong the
 * moment one wraps: "A case manager or support coordinator" runs to two lines
 * and the tick floated between them, pointing at nothing.
 */
.pills--stacked .pill { border-radius: 0.6rem; justify-content: flex-start; align-items: flex-start; }

/*
 * MARKS — what each item carries.
 *
 * A bullet and a number take the accent's FILL, not its -ink, and the
 * section's rather than a hardcoded green: a lavender section had green marks.
 * The bright value is deliberate and carries no contrast bar of its own,
 * because every item has a mark and it therefore distinguishes nothing between
 * them — the words beside it are what say what the item is. It is a texture on
 * a list, not a status on an item. -ink is for the accent when it has to be
 * read.
 *
 * The tick is the exception: not a bare mark but a filled disc — the accent's
 * FILL as the disc, the accent's -on colour as the tick — so it reads as a
 * completed checkbox rather than a coloured squiggle. -on is the token held to
 * 4.5:1 ON the fill, which is why the tick is white on the light bands and
 * flips to ink on the dark ones by construction, never by hand.
 *
 * The offsets are optical rather than geometric: flex-start aligns the mark's
 * box to the text's box, and a text box starts above the cap height by half its
 * leading.
 *
 * WHICH IS WHY AN OFFSET BELONGS TO THE ARRANGEMENT AND NOT TO THE MARK.
 * Only `stacked` sets flex-start. Inline keeps .pill's own `align-items:
 * center`, which already puts the mark on the middle of a one-line label, and
 * nudging it down as well dropped the dot onto the baseline — the row of
 * coloured dots under the hero sat low against its own words. (Pills never
 * reach here: the template emits no mark class for that arrangement, because a
 * pill's shape is its mark.)
 */
/*
 * A ticked list carries no card of its own — the disc is mark enough, and a
 * box around every line fought the button below it for the left edge and read
 * as busy. Strip the pill's ground, border and padding so the tick sits on the
 * section's own gutter; the wider gap gives the disc room off the words.
 */
.pills--checks .pill {
	background: none;
	border: 0;
	padding: 0;
	gap: 0.7rem;
}
.pill__check {
	flex: none;
	display: inline-grid;
	place-items: center;
	inline-size: 1.5rem;
	block-size: 1.5rem;
	border-radius: 999px;
	background: var(--section-accent, var(--color-teal));
	color: var(--section-accent-on, #fff);
}
.pill__check .icon { inline-size: 0.9rem; block-size: 0.9rem; }
/*
 * Stacked lines wrap, so the disc rides the FIRST line (see the arrangement
 * note above) — centred on that line's box, not floated between two. Inline
 * stays vertically centred by .pill and needs no nudge.
 */
.pills--stacked.pills--checks .pill__check { margin-block-start: calc((1lh - 1.5rem) / 2); }
.pills--bullets .pill::before {
	content: "";
	flex: none;
	inline-size: 7px;
	block-size: 7px;
	border-radius: 999px;
	background: var(--section-accent, var(--color-teal));
}
.pills--stacked.pills--bullets .pill::before { margin-block-start: 0.55em; }
/*
 * A CSS counter rather than an <ol>. The number is decoration in exactly the
 * way the bullet and the tick are — it is generated content, so it stays out of
 * the accessibility tree, and the list is still a list of items rather than a
 * sequence somebody has to follow in order. A numbered SEQUENCE is what the
 * Cards section says now, with a numeral tile per card.
 */
.pills--numbers { counter-reset: ey-item; }
.pills--numbers .pill { counter-increment: ey-item; }
.pills--numbers .pill::before {
	content: counter(ey-item) ".";
	flex: none;
	min-inline-size: 1.15em;
	font-family: var(--font-display);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	color: var(--section-accent, var(--color-teal));
}

/* ---------------- Events ----------------
   A bright accent header over a plain white card. The accents run through the
   palette in order, so a row alternates rather than repeating one colour. */

.events {
	display: grid;
	gap: 1.25rem;
	grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
	list-style: none;
	margin: 0;
	padding: 0;
}

/* Padding moves onto the two halves: the bar has to reach the card edge, and
   the card's own 0.6rem gap would otherwise float it below the top border.
   .card already clips to its radius, so the bar's corners follow the card's. */
.events > .event {
	padding: 0;
	gap: 0;
}

/*
 * Cards in a row match height, and so do their bars.
 *
 * Equal card height alone is not enough here: titles run from two words to a
 * full sentence, so the accent bars would end at four different depths across
 * a row and the colour blocks would read as ragged even though the cards were
 * level. Subgrid puts every bar in one shared row track and every body in the
 * next, so both edges line up.
 *
 * Behind @supports because the fallback is the honest one — without subgrid
 * the cards still stretch to equal height, they just have their own bar
 * depths, which is what every browser did before subgrid shipped.
 */
@supports (grid-template-rows: subgrid) {
	.events > .event {
		display: grid;
		grid-row: span 2;
		grid-template-rows: subgrid;
	}
}

/*
 * Each accent is THREE values decided together and measured together, never
 * mixed and matched:
 *
 *   --bar / --bar-ink   the header fill and the text that sits on it
 *   --accent-ink        the readable shade, for icons and the small button
 *   --accent-tint       a wash of the accent, only ever behind --accent-ink
 *
 * Text on the bar (AA needs 4.5:1 at this size). The three light accents take
 * INK; the two dark ones take WHITE, and ink on them fails at 3.23 and 3.27:
 *
 *   neon    #7FE069 + ink #241D18 = 10.10:1
 *   orange  #FBA32C + ink #241D18 =  8.21:1
 *   blue    #00D3EF + ink #241D18 =  9.15:1
 *   violet  #6E4AFF + white       =  5.14:1
 *   magenta #CB297A + white       =  5.08:1
 *
 * The bar is the brand value for all five. Violet and magenta are wired
 * identically here — neither is a special case any more.
 *
 * --accent-ink carries the register link, which is text, so it needs the full
 * 4.5:1 on the white card: neon 6.25 · orange 6.28 · blue 6.25 · violet 5.14
 * · magenta 5.08. On --accent-tint: 5.87 · 5.78 · 5.74 · 4.61 · 4.64.
 *
 * The two dark accents need lighter washes than the three light ones — violet
 * 8%, magenta 6%, against 12% for the rest. At 12% their own value lands on
 * 4.37 and 4.25 and fails. The wash is lighter precisely because the ink on it
 * is darker; these are measured, not picked.
 *
 * --accent-icon is the bright value instead, and it is deliberately allowed to
 * be faint: neon 1.64:1, blue 1.82:1 and orange 2.02:1 on white. That is a
 * decision, not an oversight. These icons are aria-hidden and sit against text
 * that says the same thing — a clock beside "9am – 10am" — so SC 1.4.11 does
 * not reach them: it covers graphics REQUIRED to understand the content, and
 * nothing here is.
 *
 * axe reports layered translucent backgrounds as "incomplete" rather than
 * pass or fail, so the tint numbers in particular are measured here, not
 * caught by the gate.
 */
.event--accent-teal {
	--bar: var(--color-teal);
	--bar-ink: #fff;
	--accent-ink: var(--color-teal-ink);
	--accent-tint: color-mix(in oklch, var(--color-teal) 12%, transparent);
}
.event--accent-berry {
	--bar: var(--color-berry);
	--bar-ink: #fff;
	--accent-ink: var(--color-berry);
	--accent-tint: color-mix(in oklch, var(--color-berry) 6%, transparent);
}

/* On a dark card the readable shade goes the other way: --accent-ink is a
   dark colour and would vanish, so the accent moves to its bright -lift. */
.is-brown .event, .is-dark .event, .is-berry .event, .is-teal .event { --accent-ink: var(--bar); --accent-tint: oklch(100% 0 0 / 0.10); }
/*
 * Base berry fails to carry the register link on a dark card (3.14:1 on the
 * glass). It has a -lift for exactly this; teal is wired the same way.
 *
 * This was live and the gate could not see it — a dark card's background is a
 * gradient, and axe returns "incomplete" for text over a gradient rather than
 * a violation, so --fail-on AA had nothing to fail on. Found by measuring.
 *
 * The ICON is a separate value and stays bright, because it is decoration
 * beside its own label.
 */
.is-brown .event--accent-teal, .is-dark .event--accent-teal, .is-berry .event--accent-teal, .is-teal .event--accent-teal {
	--accent-ink: var(--color-teal-lift);
	--accent-icon: var(--color-teal-lift);
}
.is-brown .event--accent-berry, .is-dark .event--accent-berry, .is-berry .event--accent-berry, .is-teal .event--accent-berry {
	--accent-ink: var(--color-berry-lift);
	--accent-icon: var(--color-berry-lift);
}

.event__bar {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 0.3rem;
	padding: 1rem 1.25rem 1.1rem;
	background: var(--bar);
	color: var(--bar-ink);
}
.event__date {
	display: flex;
	align-items: center;
	gap: 0.45rem;
	margin: 0;
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 0.8rem;
	letter-spacing: 0.08em;
	/* Uppercased here rather than in the string: "Sat" survives a screen
	   reader as a word, "SAT" is a coin toss between a word and three letters
	   — and this is the first thing announced on every event.
	   No opacity on it either. Dimming the date would quietly undo the
	   measured contrast above. */
	text-transform: uppercase;
	font-variant-numeric: tabular-nums;
}
.event__title {
	margin: 0;
	/* Smaller than the h3 scale on purpose — a card title competing with the
	   section heading is what makes a grid of them read as noise. */
	font-size: 1.15rem;
	line-height: 1.2;
	letter-spacing: -0.01em;
	/* Workshop titles run long: "Parent Workshop: Understanding Alcohol and
	   Other Drug Use in Adolescence" has to wrap inside the bar at 390px. */
	text-wrap: balance;
	overflow-wrap: anywhere;
}

.event__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: 0.55rem;
	padding: 1.1rem 1.25rem 1.25rem;
}

/* Time and place are the same kind of fact, so they get the same treatment
   and differ only by their icon. The icon carries the accent; the words stay
   in the surface's own ink, because coloured body text is how a card starts
   competing with itself. */
.event__meta {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin: 0;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 0.875rem;
	color: var(--surface-ink-soft);
}
/* Solid, and a size up: a filled 14px glyph beside 14px display type reads as
   a pair, where the outline version reads as a smudge.
   Bright accent, not the readable shade — see --accent-icon above. */
.event__meta .icon { flex: none; inline-size: 1.15em; block-size: 1.15em; color: var(--accent-icon, var(--bar)); }

.event__detail {
	/*
	 * 14px. WCAG sets no minimum text size — the only size-dependent rule is
	 * the large-text contrast exemption, which cuts the other way — so nothing
	 * in the gate stops this going smaller. 14px is where legibility stops it
	 * instead, for an audience that includes young people and parents reading
	 * on a phone. The contrast here is 9.15:1, nowhere near the constraint.
	 */
	font-size: 0.875rem;
	color: var(--surface-ink-soft);
	/*
	 * Calendar descriptions are pasted by hand and routinely contain a bare
	 * URL. `anywhere` breaks it instead of letting it push the card wide.
	 */
	overflow-wrap: anywhere;
}
.event__detail p { margin: 0; }

/* Register is a text link, not a button. Twenty filled buttons down a page is
   twenty things shouting the same volume; the accent already carries the card. */
.event__action { margin: 0; padding-block-start: 0.15rem; }
.event__link {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 0.875rem;
	color: var(--accent-ink);
	text-decoration: underline;
	/* Hairline, and far enough off the baseline to clear the descenders in
	   "Register" and "Just" rather than cutting through them. from-font is not
	   used on purpose: Space Grotesk's own metrics put it back where it was. */
	text-decoration-thickness: 1px;
	text-underline-offset: 0.35em;
	text-decoration-color: color-mix(in oklab, currentColor 55%, transparent);
	/* 44px of target without 44px of ink, so the tap area meets 2.5.8 while
	   the link still reads as a line of text. */
	min-block-size: 44px;
	transition: color .14s ease;
}
/* Hover shifts the colour, not the underline; the rest-state hairline stays. */
.event__link:hover { color: color-mix(in oklch, var(--accent-ink), var(--link-hover-toward) var(--link-hover-amt)); }
.event__link .icon { flex: none; }

/* Add to calendar: a square with a plus in it, pinned bottom-right on its own
   row, that opens leftwards onto its label on hover or focus.
   0fr -> 1fr animates a real width without anyone having to know one. */
.event__foot { display: flex; justify-content: flex-end; margin: 0; margin-block-start: auto; padding-block-start: 0.6rem; }
.event__add {
	display: grid;
	grid-template-columns: 0fr auto;
	align-items: center;
	border-radius: 0.85rem;
	background: var(--accent-tint);
	color: var(--accent-ink);
	text-decoration: none;
	transition: grid-template-columns .28s ease, background-color .2s ease, color .2s ease;
}
.event__add__label {
	overflow: hidden;
	min-inline-size: 0;
	white-space: nowrap;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 0.85rem;
}
/* The padding lives on the inner span so the closed state is a true square —
   padding on the clipped element itself would still take up room at 0fr. */
.event__add__label > span { display: block; padding-inline-start: 0.9rem; }
.event__add__icon {
	display: grid;
	place-items: center;
	inline-size: 2.75rem;
	block-size: 2.75rem;
}
.event__add:hover,
.event__add:focus-visible {
	grid-template-columns: 1fr auto;
	background: var(--bar);
	color: var(--bar-ink);
}
/* No hover means no reveal, so the label just stays open. A bare plus on a
   phone is a guess, and the label is the whole explanation. */
@media (hover: none) {
	.event__add { grid-template-columns: 1fr auto; }
}

.events__empty { margin: 0; color: var(--surface-ink-soft); }

/* ---------------- Sponsors ---------------- */

.sponsors {
	display: grid;
	gap: 1.25rem;
	grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
	list-style: none;
	margin: 0;
	padding: 0;
}
.sponsor { align-items: flex-start; text-align: start; }
.sponsor__logo { max-block-size: 72px; inline-size: auto; object-fit: contain; }
.sponsor__name { margin: 0; font-family: var(--font-display); font-weight: 700; }
.sponsor__note { margin: 0; font-size: 0.9rem; color: var(--surface-ink-soft); }

/* ---------------- Testimonials ---------------- */

.testimonials {
	display: grid;
	gap: 1.25rem;
	grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
	list-style: none;
	margin: 0;
	padding: 0;
}
.testimonial__rating { display: flex; gap: 0.15rem; margin: 0; color: var(--surface-link, var(--color-teal-ink)); }
.testimonial__quote { margin: 0; font-size: 1.05rem; }
.testimonial__quote p { margin: 0; }
.testimonial__by { margin: 0; font-family: var(--font-display); font-weight: 600; }
.testimonial__role { display: block; font-weight: 400; font-size: 0.9rem; color: var(--surface-ink-soft); }

/* ---------------- FAQs ---------------- */

.faqs { display: flex; flex-direction: column; gap: 0.5rem; max-inline-size: 52rem; }
.faq { border-block-end: 1px solid var(--surface-card-border); }
.faq__q {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	/* 44px of height via padding, so the summary is a comfortable target. */
	padding-block: 1rem;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 1.05rem;
	cursor: pointer;
	list-style: none;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__chev { transition: transform .18s ease; flex: none; }
.faq[open] .faq__chev { transform: rotate(180deg); }
.faq__a { padding-block-end: 1rem; color: var(--surface-ink-soft); }

/* ---------------- Gallery ---------------- */

.gallery {
	display: grid;
	gap: 0.75rem;
	grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
	list-style: none;
	margin: 0;
	padding: 0;
}
.gallery__img { inline-size: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 0.8rem; }

/*
 * First one large: the lead photo takes a full row of its own and the rest
 * share the row beneath. Two columns rather than three, because the pair
 * underneath should read as a pair — a third would make the lead look like a
 * mistake rather than a choice.
 *
 * Only from 700px. Below that every photo is full width anyway and the whole
 * arrangement collapses to a stack, which is the right answer on a phone.
 */
@media (min-width: 700px) {
	.gallery--feature { grid-template-columns: 1fr 1fr; }
	.gallery--feature > .gallery__item:first-child { grid-column: 1 / -1; }
	.gallery--feature > .gallery__item:first-child .gallery__img { aspect-ratio: 16 / 9; }
}

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

.map { border-radius: var(--radius-card); overflow: hidden; }
.map iframe { display: block; }

/* ---------------- Placeholders ---------------- */

.img-placeholder {
	display: grid;
	place-items: center;
	aspect-ratio: 16 / 9;
	border: 2px dashed var(--surface-card-border);
	border-radius: var(--radius-card);
	background-image: repeating-linear-gradient(45deg, color-mix(in oklch, var(--color-ink) 3%, transparent) 0 10px, transparent 10px 20px);
	color: var(--surface-ink-soft);
	font-size: 0.9rem;
	text-align: center;
	padding: 1rem;
}

/* ---------------- Footer ---------------- */

/* ---------------- Footer ----------------
   Three columns of unequal weight — the brand needs room to breathe, the site
   map is a narrow list, contact sits between the two — then two full-width
   bands under them. */

.site-footer { padding-block-start: 3.5rem; }
.site-footer__inner {
	display: grid;
	gap: 2.5rem;
	padding-block-end: 3.5rem;
	grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
}
@media (min-width: 900px) {
	/* Three columns: brand and contact stack down the left; the four audience
	   menus pair up on the right — Support over Community, Referrers over About. */
	.site-footer__inner { grid-template-columns: 1.5fr 2fr; gap: 2.5rem 1.75rem; align-items: start; }
	.site-footer__brand { grid-column: 1; grid-row: 1; }
	.site-footer__visit { grid-column: 1; grid-row: 2; }
	.site-footer__cols { grid-column: 2; grid-row: 1 / span 2; }
}

/* The audience menus, paired into two columns. Row-major flow stacks the second
   pair under the first: Support / Referrers on top, Community / About beneath. */
.site-footer__cols {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 2.5rem 1.75rem;
	align-content: start;
}
/* On a narrow phone two menu columns get cramped, so the pair stacks to one. */
@media (max-width: 479px) {
	.site-footer__cols { grid-template-columns: 1fr; }
}

/* The footer wears the full brand lockup — icon, wordmark and tagline as one
   finished SVG (the light mark, for the dark band). It fills the brand column up
   to a cap, so it stays a footer mark rather than a hero. */
.site-footer__home { display: inline-block; }
.site-footer__lockup { display: block; inline-size: 100%; max-inline-size: 240px; block-size: auto; }

.site-footer__tagline {
	margin: 1rem 0 0;
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 1.1rem;
	color: var(--surface-ink);
}
.site-footer__blurb {
	margin: 0.5rem 0 0;
	max-inline-size: 26rem;
	color: var(--surface-ink-soft);
	line-height: 1.6;
}
.site-footer__parent { margin: 1.25rem 0 0; font-size: 0.9rem; color: var(--surface-ink-soft); }
.site-footer__parent-intro { display: block; margin-block-end: 0.15rem; }

/* Footer links carry no rest-state underline: the dark band is dense with links
   and the link colour + weight already read as links here. On hover/focus the
   colour lifts (no underline) so the interaction shows itself — matching the
   sitewide link-hover behaviour; the global .link keeps its rest-state underline
   everywhere else on the site, and :focus-visible still gets the global ring. */
.site-footer .link { text-decoration: none; transition: color .14s ease; }
.site-footer .link:hover,
.site-footer .link:focus-visible {
	color: color-mix(in oklch, var(--surface-link), var(--link-hover-toward) var(--link-hover-amt));
}

/*
 * Footer column titles read as eyebrows, not headings: the same sans, weight,
 * tracking and case as .kicker, in the accent rather than plain ink. The footer
 * sits on the dark brown ground, so the default berry accent resolves to
 * berry-lift here (4.66:1) — the exact colour a default section eyebrow takes on
 * the dark bands.
 */
.site-footer__heading {
	margin: 0;
	font-family: var(--font-sans);
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-berry-lift);
}

.site-footer__list {
	list-style: none;
	margin: 0.5rem 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
}
.site-footer__list a {
	display: inline-flex;
	align-items: center;
	/* A snug target: taller than the text for a comfortable tap, but tightened
	   from 44px so the links sit closer together (still clears the 24px AA floor).
	   The title-to-first-link gap lives on .site-footer__list, not here. */
	min-block-size: 32px;
	/* A label that wraps to two lines is still one item — tight leading keeps its
	   two lines together inside the 44px row instead of reading as two rows. */
	line-height: 1.3;
	color: var(--surface-ink-soft);
	text-decoration: none;
	transition: color .16s ease;
}
.site-footer__list a:hover { color: var(--surface-link); }
/* The policy links are a second group, not a continuation of the first — the
   rule is what says so. */
.site-footer__list--policies {
	margin-block-start: 0.75rem;
	padding-block-start: 0.5rem;
	border-block-start: 1px solid oklch(100% 0 0 / 0.10);
	font-size: 0.9rem;
}

.site-footer__contact {
	font-style: normal;
	margin-block-start: 0.5rem;
	display: flex;
	flex-direction: column;
}
.site-footer__contact p { margin: 0; }
.site-footer__contact-name { display: block; color: var(--surface-ink); }
.site-footer__address { display: block; line-height: 1.6; color: var(--surface-ink-soft); }
.site-footer__contact a {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	min-block-size: 44px;
}
/* Bright, not the readable shade. These are aria-hidden marks sitting beside
   text that already says "phone" and "email", so SC 1.4.11 does not reach
   them — see the icon note in inc/icons.php. */
.site-footer__contact .icon { color: var(--surface-link, var(--color-teal-ink)); font-size: 1.05em; }

.site-footer__note {
	margin: 1rem 0 0;
	font-size: 0.85rem;
	line-height: 1.6;
	color: var(--surface-ink-soft);
}

/* Footer links resolve to BERRY, not the dark-band default teal — the footer's
   own accent, matching the berry-lift eyebrows already used for the column
   headings. berry-lift clears AA on the brown-950 ground (4.66:1 on brown-800,
   5.85:1 here). The focus ring moves with it so a berry link never draws a teal
   ring. The crisis 000 link keeps its paper-bold safety contrast on purpose. */
.site-footer {
	--surface-link: var(--color-berry-lift);
	--surface-focus: var(--color-berry-lift);
}
.site-footer__band { border-block-start: 1px solid oklch(100% 0 0 / 0.10); }
.site-footer__band > .container-lab { padding-block: 1.5rem; }
/* The two flags sit alongside the Acknowledgement, top-aligned to the first line. */
.site-footer__aoc-wrap { display: flex; align-items: flex-start; gap: 0.9rem; }
.site-footer__flags { display: flex; gap: 0.45rem; flex: none; padding-block-start: 0.14rem; }
/* The flags match the height of the acknowledgement text, centred on its first
   line (first line ≈ 0.85rem × 1.4 line-height ≈ 1.19rem; a 0.9rem flag centres
   with ~0.14rem of top padding on the container). */
.site-footer__flag {
	display: block;
	block-size: 0.9rem;
	inline-size: auto;
	border-radius: 2px;
	box-shadow: 0 0 0 1px oklch(100% 0 0 / 0.14);
}
.site-footer__aoc {
	margin: 0;
	max-inline-size: 58rem;
	font-size: 0.85rem;
	line-height: 1.4;
	color: var(--surface-ink-soft);
}
.site-footer__legal {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem 2rem;
	justify-content: space-between;
	align-items: center;
	font-size: 0.8rem;
	color: var(--surface-ink-soft);
}
.site-footer__legal p { margin: 0; }
.site-footer__legal p + p { margin-block-start: 0.25rem; }
.site-footer__credit a { color: var(--surface-ink); font-weight: 600; text-decoration: none; }
.site-footer__credit a:hover { color: var(--surface-link); }

/* Contact: the charity/partnership line and the address, each with its mark. */
.site-footer__contact-alt { margin-block-start: 0.75rem; }
.site-footer__contact-label { display: block; color: var(--surface-ink); font-size: 0.85rem; line-height: 1.3; }
/* Sit the labelled email directly under its label. The icon stays centred on the
   text (align-items:center, matching the phone row), and a negative top margin
   lifts the whole 44px link up so its text tucks under the label — the hit area
   is untouched, it just overlaps the label's non-interactive whitespace above. */
.site-footer__contact .site-footer__contact-alt a { align-items: center; margin-block-start: -0.7rem; }
.site-footer__address { display: flex; align-items: flex-start; gap: 0.5rem; margin-block-start: 0.75rem; line-height: 1.6; color: var(--surface-ink-soft); }
.site-footer__address .icon { margin-block-start: 0.2rem; flex: none; color: var(--surface-link, var(--color-teal-ink)); }

/* Compliance band: the marks, then the two legal entities and recognition side
   by side, then the charity/crisis lines, then copyright and credit. The layout
   is the flex column defined below (this block used to declare a competing grid;
   removed so the gap is edited in one place). */
.site-footer__reg { display: grid; gap: 1.25rem 2rem; grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr)); }
/* Each registered entity: its trust mark to the left of the legal text. */
.site-footer__reg-item { display: flex; align-items: center; gap: 1.25rem; }
.site-footer__reg-item .site-footer__mark { flex: none; }
.site-footer__reg-text { min-inline-size: 0; }
.site-footer__reg-role { margin: 0; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--surface-ink); }
.site-footer__reg-name { margin: 0.2rem 0 0; color: var(--surface-ink); }
.site-footer__reg-id { margin: 0.1rem 0 0; color: var(--surface-ink-soft); font-size: 0.85rem; }
.site-footer__charity { margin: 0; max-inline-size: 54rem; font-size: 0.85rem; line-height: 1.6; color: var(--surface-ink-soft); }
.site-footer__crisis { margin: 0; font-size: 0.85rem; color: var(--surface-ink-soft); }
.site-footer__crisis a { color: var(--surface-ink); font-weight: 600; }

/*
 * The compliance block: the trust marks stacked over the legal identifiers.
 * It is the left child of the space-between legal row, so it keeps its own
 * left edge while the Noknok credit sits opposite.
 */
/* The gap is tight so the three closing lines (charity / crisis / copyright)
   read as one block. The reg block keeps its own separation via its own
   margin-block-end, so this stays robust even if the charity line is empty. */
.site-footer__compliance { display: flex; flex-direction: column; gap: 0.15rem; }
.site-footer__compliance > .site-footer__reg { margin-block-end: 0.95rem; }
.site-footer__marks { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
/* An uploaded logo, capped so a tall or wide file lands at the same optical size. */
.site-footer__mark { display: block; block-size: auto; max-block-size: 64px; inline-size: auto; }
/* The fallback while no logo is loaded: the claim as an understated outlined pill,
   never a coloured badge that would ape the official mark it stands in for. */
.site-footer__mark--badge {
	display: inline-flex;
	align-items: center;
	padding: 0.3rem 0.7rem;
	border: 1px solid oklch(100% 0 0 / 0.22);
	border-radius: var(--radius-btn);
	font-size: 0.72rem;
	font-weight: 600;
	letter-spacing: 0.01em;
	color: var(--surface-ink);
}
.site-footer__ids { display: flex; flex-wrap: wrap; gap: 0 0.5rem; align-items: baseline; }
/* The identifiers can wrap as a group under the copyright on a narrow screen
   without breaking the "© year entity" phrase apart. */
.site-footer__idlist { color: var(--surface-ink-soft); }
.site-footer__charity { max-inline-size: 46rem; }

/* Social. Icon-only links under the brand blurb; empty fields render nothing.
   Each is a round hit area of 2.5rem (40px, well past the 24px WCAG target-size
   floor) so the small marks stay easy to hit; the icon takes currentColor and
   lifts from soft ink to the link teal on hover/focus, with a faint disc behind
   it — colour and motion, never motion alone. */
.site-footer__socials {
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem;
	margin: 0.75rem 0 0;
	margin-inline-start: -0.5rem; /* pull the round targets back to the text edge */
	padding: 0;
	list-style: none;
}
.site-footer__social {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 2.5rem;
	block-size: 2.5rem;
	border-radius: 999px;
	color: var(--surface-ink-soft);
	text-decoration: none;
	transition: color .14s ease, background-color .14s ease;
}
.site-footer__social .icon { inline-size: 1.4rem; block-size: 1.4rem; }
.site-footer__social:hover {
	color: var(--surface-link, var(--color-teal-ink));
	background-color: color-mix(in oklab, currentColor 14%, transparent);
}

/* ---------------- Programme single ---------------- */

.detail-list { display: grid; gap: 1rem; margin: 0; max-inline-size: 46rem; }
.detail-list dt { font-family: var(--font-display); font-weight: 700; }
.detail-list dd { margin: 0.25rem 0 0; color: var(--surface-ink-soft); }
.programme-hero .card__badges { margin-block-start: 1.25rem; }

/* ---------------- Team ---------------- */

.team {
	display: grid;
	gap: 1.25rem;
	grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
	list-style: none;
	margin: 0;
	padding: 0;
}
/*
 * Two up, and only two.
 *
 * .team's auto-fit is right for the small grid cards and wrong for featured
 * ones: those are photo-beside-text already, so a third column takes the text
 * down to a couple of words a line. One column until there is genuinely room
 * for two of them side by side.
 */
/*
 * Cards hug their content instead of stretching to the tallest in the row.
 * Bios are optional and wildly uneven — one person with a paragraph and one
 * with none put a 660px card of blank next to a full one. A short card with
 * space under it reads as a short card; a tall empty one reads as broken.
 */
.team--two { grid-template-columns: minmax(0, 1fr); align-items: start; }
/*
 * 1200px, and the number is derived rather than picked.
 *
 * Two columns cost the text half its measure: at 1024 the text column works
 * out at 420 - 144 - 20 = 256px, about 34 characters a line, which is well
 * inside the range where the eye starts losing its place. The wrap caps at
 * 76rem, so the widest this ever gets is a 352px column — 46 characters. 1200
 * is where it first clears 45.
 *
 * Held at 1200 even though the programme page passes bio => false, because
 * qualifications and the likes/doesn't-like pair still run long enough to
 * care, and this modifier is not promised to that one caller.
 */
@media (min-width: 1200px) {
	.team--two { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; }
	/* The portrait gives ground back. At half the container the 13rem column a
	   lone featured card uses leaves the bio too little to read. */
	.team--two .person--featured {
		grid-template-columns: minmax(0, 9rem) minmax(0, 1fr);
		gap: 1.25rem;
	}
}

/* A lone card in the grid still gets a card's width rather than the whole
   container — auto-fit would otherwise stretch it. The featured profile has
   its own section for this job; this is the safety net. */
.team:has(> :only-child) { grid-template-columns: minmax(0, 22rem); }
/*
 * ...but 22rem is a SMALL card's width. A featured one is photo-beside-text
 * inside that, so the same rule leaves the text about 160px and puts the
 * person's name on two lines. Roughly what one column of the two-up grid
 * gives it, so a programme run by one person looks like the others.
 *
 * After the rule above, not before: same specificity, so order decides.
 */
.team--two:has(> :only-child) { grid-template-columns: minmax(0, 34rem); }
.person__photo {
	inline-size: 100%;
	aspect-ratio: 1;
	object-fit: cover;
	border-radius: 0.9rem;
}
.person__photo--placeholder {
	display: grid;
	place-items: center;
	background: var(--surface-card-border);
	color: var(--surface-ink-soft);
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 2.5rem;
	/* The tile is a stand-in for a photo, so it holds the photo's box exactly —
	   that is the whole job. */
	line-height: 1;
}
/*
 * The role takes the card's accent, cycling through the palette in order, so a
 * grid of thirteen reads as a group rather than thirteen repetitions of one
 * green line. Same --accent-ink vocabulary as the event cards: the readable
 * shade, never the bright one, because this is text.
 */
.person--accent-teal   { --accent-ink: var(--color-teal-ink); --accent-icon: var(--color-teal); }
.person--accent-berry  { --accent-ink: var(--color-berry);    --accent-icon: var(--color-berry); }
/* On a dark surface the dark readable shade vanishes into the ground, so each
   pathway accent switches to its on-dark lift — the value made to carry text. */
.is-brown .person--accent-teal,   .is-dark .person--accent-teal,   .is-berry .person--accent-teal, .is-teal .person--accent-teal   { --accent-ink: var(--color-teal-lift); }
.is-brown .person--accent-berry, .is-dark .person--accent-berry, .is-berry .person--accent-berry, .is-teal .person--accent-berry {
	--accent-ink: var(--color-berry-lift);
	--accent-icon: var(--color-berry-lift);
}

/*
 * The room the bar sits in. The bar itself is the Border control's now — this
 * card drew its own along the bottom edge unconditionally, which meant a team
 * grid set to Border = Top came out with two of them, one at each end, and a
 * grid set to None still had one nobody had asked for. Same span-versus-control
 * split the programme card had, one card family over.
 */
.person { padding-block-end: 1.6rem; }

.person__name { margin: 0.4rem 0 0; font-size: 1.1rem; }
/*
 * The role is a label, not prose. It was inheriting the body's 1.6, which at
 * 0.9rem left 23px of leading — enough that "Community Practice & / Resources
 * Leader" read as two separate lines rather than one job title that wrapped.
 * 1.3 keeps the pair together while still clearing the descenders.
 *
 * balance over the inherited `pretty`: on two lines pretty only guards the last
 * one against an orphan, where balance evens both, so "Work, Health & Safety /
 * Coordinator" stops hanging a single word underneath.
 */
.person__role {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 0.9rem;
	line-height: 1.3;
	text-wrap: balance;
	color: var(--accent-ink, var(--surface-link));
}
.person--vacant .person__role { color: var(--surface-ink-soft); }
.person__vacant { margin: 0; font-size: 0.9rem; font-style: italic; color: var(--surface-ink-soft); }
.person__bio { margin: 0; font-size: 0.95rem; color: var(--surface-ink-soft); }

/* Italic, no marker. Still a <ul> — see the note in team.php. */
.person__quals {
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: 0.85rem;
	font-style: italic;
	line-height: 1.45;
	color: var(--surface-ink-soft);
}

/*
 * A programme's photo beside its description.
 *
 * Only .progintro--split is a grid. Without a photo the wrapper is a plain
 * block and the .prose inside keeps its own 68ch measure — byte-for-byte the
 * layout this section had before the photo existed, which is still what most
 * programmes render.
 */
.progintro--split { display: grid; gap: 2rem; align-items: start; }
@media (min-width: 900px) {
	/*
	 * The picture's column is `auto` — it takes the width the picture actually
	 * ends up, rather than a fixed share of the row. A portrait bounded by its
	 * height comes out narrow, and the text gets the difference instead of a
	 * column of blank sitting next to it.
	 */
	.progintro--split { grid-template-columns: auto minmax(0, 1fr); gap: 2.5rem; }
}

/*
 * The frame is the picture, so it has to be the picture's shape.
 *
 * Both axes auto and both bounded: the image scales down to fit inside the
 * box and keeps its own ratio doing it. Nothing is cropped in CSS either —
 * no object-fit, no aspect-ratio, no fixed side. A landscape hits the width
 * first and a portrait hits the height, which is the whole point.
 */

/*
 * The text half of the description band: the place, the headline, the chips,
 * the description, then Register. A flex column so one gap rule spaces five
 * different blocks instead of five margin rules arguing with each other.
 *
 * Stretch, not align-items:start — a start-aligned flex item shrinks to its
 * max-content width, and for a paragraph that is the whole text on one line
 * before it is clamped, which is not a width anyone chose. The button does not
 * stretch because .section__actions is itself a flex row.
 */
.progintro__body {
	display: flex;
	flex-direction: column;
	gap: 1.1rem;
}
/* The column owns the spacing now, so the blocks give up their own margins. */
.progintro__body > .kicker,
.progintro__body > .section__title,
.progintro__body > .card__badges { margin: 0; }
/* A shade more air before the button than between the blocks above it. */
.progintro__body > .section__actions { margin-block-start: 0.4rem; }

.progintro__photo {
	display: block;
	inline-size: auto;
	block-size: auto;
	max-inline-size: 100%;
	border-radius: var(--radius-card);
}
@media (min-width: 900px) {
	/* 30rem of height is about as tall as a portrait can go before it pushes
	   the description off the screen on a laptop. */
	.progintro__photo { max-inline-size: 26rem; max-block-size: 30rem; }
}

/*
 * A resource's media beside its context — the same shape as a programme's
 * description band. The media is a cover image or an embedded video; the body is
 * the "why this helps" note, the citation, and the action button for a reader
 * who has read that far.
 *
 * Two equal columns rather than the programme's `auto`: a 16:9 video has no
 * natural narrow width to fall back to, so a fixed share keeps it from taking the
 * whole row. Without media the wrapper is a plain block and the prose keeps its
 * own measure.
 */
.resource--split { display: grid; gap: 2rem; align-items: start; }
@media (min-width: 900px) {
	.resource--split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 2.5rem; }
}

.resource__body {
	display: flex;
	flex-direction: column;
	gap: 1.1rem;
}
.resource__body > .prose { margin: 0; }
/* A shade more air before the button than between the blocks above it. */
.resource__body > .section__actions { margin-block-start: 0.4rem; }

.resource__photo {
	display: block;
	inline-size: auto;
	block-size: auto;
	max-inline-size: 100%;
	border-radius: var(--radius-card);
}
@media (min-width: 900px) {
	.resource__photo { max-inline-size: 30rem; max-block-size: 32rem; }
}

/*
 * The video frame: 16:9, filling its column, the iframe pinned to fill it. A
 * dark ground shows for the instant before the player paints rather than a flash
 * of the cream band through it.
 */
.resource__video {
	position: relative;
	inline-size: 100%;
	aspect-ratio: 16 / 9;
	border-radius: var(--radius-card);
	overflow: hidden;
	background: var(--color-ink);
}
.resource__video iframe {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	border: 0;
}

.resource__cite { margin: 0; font-size: 0.875rem; color: var(--surface-ink-soft); }

/*
 * What a person runs. Featured cards only — see the note in person-card.php.
 */
.person__programmes { margin-block-start: 0.15rem; }
.person__programmes-label {
	margin: 0 0 0.4rem;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: 0.75rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--surface-ink-soft);
}

.pills--person { gap: 0.4rem; }

/*
 * Smaller than a standalone .pill, and on its own ground: the base pill fills
 * with --surface-card-bg, which IS the card these sit inside, so it would
 * vanish against it.
 *
 * The label stays --surface-ink — the surface's own tested body colour — rather
 * than taking the accent. Five programme colours across four surfaces is twenty
 * pairs to measure otherwise, and the colour has somewhere better to be: the
 * dot. That dot is decorative, and can be, because the programme's name is in
 * the pill beside it.
 *
 * No underline. The pill's ground and border are the affordance, the same way
 * .btn works — this is not a link sitting in a sentence.
 */
.pill--prog {
	gap: 0.4rem;
	/* Clears the 24px minimum target of SC 2.5.8 with room to spare. */
	min-block-size: 1.75rem;
	padding: 0.25rem 0.7rem;
	font-size: 0.8rem;
	line-height: 1.3;
	background: color-mix(in oklch, var(--surface-ink) 5%, transparent);
	border-color: color-mix(in oklch, var(--surface-ink) 16%, transparent);
	color: var(--surface-ink);
	text-decoration: none;
	transition: background-color 120ms ease, border-color 120ms ease;
}
.pill--prog::before {
	content: "";
	flex: none;
	inline-size: 0.5rem;
	block-size: 0.5rem;
	border-radius: 50%;
	background: var(--pill-accent, var(--surface-link));
}
.pill--prog:hover {
	background: color-mix(in oklch, var(--pill-accent, var(--surface-link)) 12%, transparent);
	border-color: color-mix(in oklch, var(--pill-accent, var(--surface-link)) 45%, transparent);
}

/*
 * The programme's own colour, so it is the same wherever the programme appears.
 *
 * Descendant selectors, not the compound ones the section accents use: the
 * surface class is on the <section> and the pill is well inside it, which is
 * the exact inversion of the case noted beside .is-brown.section--accent-teal
 * in main.css. Same token pairs as there, including the berry lift it needs
 * before it reads on brown at all.
 */
.pill--accent-teal   { --pill-accent: var(--color-teal-ink); }
.pill--accent-berry  { --pill-accent: var(--color-berry); }

.is-brown .pill--accent-teal,   .is-dark .pill--accent-teal,   .is-berry .pill--accent-teal, .is-teal .pill--accent-teal   { --pill-accent: var(--color-teal-lift); }
.is-brown .pill--accent-berry, .is-dark .pill--accent-berry, .is-berry .pill--accent-berry, .is-teal .pill--accent-berry { --pill-accent: var(--color-berry-lift); }

/* ---------------- Find us ---------------- */

.findus--split {
	display: grid;
	gap: 2rem;
	grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
	align-items: start;
}
.findus__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; }
.findus__list li { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 0.6rem; align-items: start; }
.findus__list .icon { grid-row: span 2; margin-block-start: 0.15rem; color: var(--surface-link); }
.findus__label { font-family: var(--font-display); font-weight: 700; font-size: 0.85rem; }
.findus__list a { min-block-size: 24px; display: inline-flex; align-items: center; gap: 0.35rem; }
.findus__directions { grid-template-columns: 1fr; }

/* ---------------- Programme spotlight ---------------- */

.spotlight {
	display: grid;
	gap: 1.5rem;
	grid-template-columns: auto 1fr;
	align-items: start;
	padding: 2rem;
}
@media (max-width: 640px) { .spotlight { grid-template-columns: 1fr; } }
.spotlight__icon { display: inline-flex; inline-size: 84px; block-size: 84px; }
.spotlight__icon img { inline-size: 100%; block-size: 100%; object-fit: contain; }
.spotlight__body { display: grid; gap: 0.6rem; }
.spotlight__title { margin: 0; font-size: clamp(1.35rem, 1.1rem + 1vw, 1.75rem); }
.spotlight__text { margin: 0; color: var(--surface-ink-soft); max-inline-size: 62ch; }
.spotlight .section__actions { margin-block-start: 0.5rem; }

/* ---------------- Hero with artwork ---------------- */

.hero { display: grid; gap: 2rem; align-items: center; }
.section--hero-art .hero { grid-template-columns: 1.15fr 1fr; }
@media (max-width: 860px) { .section--hero-art .hero { grid-template-columns: 1fr; } }
/*
 * Text right: the ratio turns over with it, so the words keep the larger
 * column whichever edge they are on.
 *
 * Only above the breakpoint. Stacked, the words go first regardless — the
 * heading is the page's h1 and the artwork is decorative and aria-hidden, so
 * leading with the picture would put the one thing that says what the page is
 * below a screenful of drawing. That is the opposite of the call Text and
 * photo makes, and deliberately: there the photo is content.
 */
@media (min-width: 861px) {
	.section--hero-art .hero--text-right { grid-template-columns: 1fr 1.15fr; }
	.section--hero-art .hero--text-right .hero__body { order: 2; }
}
.hero__body .section__head { margin-block-end: 0; }
.hero__art img { inline-size: 100%; block-size: auto; }

/*
 * One heading size for every hero, whatever the Artwork option is and wherever
 * the hero sits on the page.
 *
 * The section heading is an h1 on the first section with a heading and an h2 on
 * every section after it (see ey_heading_level), so two heroes on one page — or
 * a hero that is not the page's first section — would otherwise render at the h1
 * clamp (up to 3.4rem) and the h2 clamp (up to 2.25rem), which read as two
 * different heroes. This pins them both to a single size between the two so the
 * three artwork options, and stacked heroes, always match. The heading LEVEL is
 * untouched — assistive tech still gets one h1 per page; only the visual size is
 * fixed here.
 */
.section--hero .section__title { font-size: clamp(2rem, 1.4rem + 2.5vw, 2.8rem); }
.hero__note {
	display: flex;
	align-items: center;
	gap: 0.45rem;
	margin-block-start: 1.5rem;
	font-size: 0.9rem;
	color: var(--surface-ink-soft);
}

/* ---------------- Text with a photo beside it ---------------- */

.textmedia { display: grid; gap: 2.5rem; align-items: center; }
.section--text-media .textmedia { grid-template-columns: 1fr 1fr; }
@media (max-width: 860px) { .section--text-media .textmedia { grid-template-columns: 1fr; } }
/* The photo leads on a phone regardless of the side chosen: a picture is a
   faster way into a section than a paragraph is. */
@media (max-width: 860px) { .textmedia--left .textmedia__figure { order: -1; } }
@media (min-width: 861px) { .textmedia--left .textmedia__figure { order: -1; } }
.textmedia__figure { margin: 0; }
.textmedia__img {
	inline-size: 100%;
	aspect-ratio: 1;
	object-fit: cover;
	border-radius: var(--radius-card);
	/* A hairline so a light photograph does not bleed into a cream section. */
	outline: 1px solid var(--surface-card-border);
	outline-offset: -1px;
}


/* Inline points — the strip that runs under a hero. Tight vertical rhythm,
   because it is a continuation of the hero rather than a section of its own,
   and centred so it reads as one line rather than a left-aligned list. */
.section--list-inline { padding-block: 1.25rem; }
.pills--inline {
	gap: 0.4rem 2rem;
	justify-content: center;
	text-align: center;
}
.pills--inline .pill {
	background: transparent;
	border: 0;
	padding: 0.2rem 0;
	gap: 0.65rem;
	font-size: 0.875rem;
	color: var(--surface-ink-soft);
}

/*
 * A dot, not a tick. Four points all wearing the same green tick read as a
 * checklist you are meant to verify; four coloured dots read as a list, which
 * is what this is. The colours run through the palette in order so the strip
 * carries the brand rather than repeating one accent.
 *
 * Decoration only — aria-hidden is not needed because ::before is never in the
 * accessibility tree, and nothing here is carried by colour alone: the point is
 * the words next to it.
 */
/*
 * Scoped to the bullets mark. It used to apply to every inline item, which was
 * fine while inline could only ever be ticks-that-were-really-dots; now that
 * the strip can carry numbers or ticks, an unconditional `content: ""` here
 * would blank whichever mark was chosen.
 */
.pills--inline.pills--bullets .pill::before {
	content: "";
	flex: none;
	inline-size: 8px;
	block-size: 8px;
	border-radius: 999px;
	background: var(--pill-dot, var(--color-teal));
}
/* The dots alternate the duotone: teal, then berry. Berry base disappears on a
   dark band, so it lifts there; on cream/white it goes back to the base. */
.pills--inline .pill:nth-child(2n + 1) { --pill-dot: var(--color-teal); }
.pills--inline .pill:nth-child(2n + 2) { --pill-dot: var(--color-berry-lift); }
.is-cream .pills--inline .pill:nth-child(2n + 2),
.is-white .pills--inline .pill:nth-child(2n + 2) { --pill-dot: var(--color-berry); }

/* Testimonial avatar — the initial, as a disc. */
.testimonial__by { display: flex; align-items: center; gap: 0.7rem; }
.testimonial__avatar {
	display: grid;
	place-items: center;
	inline-size: 2.5rem;
	block-size: 2.5rem;
	flex: none;
	border-radius: 50%;
	background: var(--color-teal);
	color: var(--color-brown-900);
	font-family: var(--font-display);
	font-weight: 700;
}

.section--spotlight .section__title { margin-block-end: 1.5rem; }

/* ---------------- Icons ----------------
   Brand icons sit directly on the card, with no square behind them, and they
   take the CARD's colour rather than the one baked into the artwork.

   The art is a single-colour line drawing on a transparent ground, so it is
   used as a MASK — its alpha is the shape — over a fill of --card-accent. That
   is what recolours it per card: a teal hand on a berry card comes out berry,
   without a second copy of every icon. --icon-src is the image URL, set inline
   per card (see manual.php / programme.php). No src, no paint — an unset mask
   would leave the fill as a bare accent square, which is the wash we removed
   back in colour, so the templates only ever emit this element with it set.

   Sized in ABSOLUTE units off --tile-size, never percentages: the element wraps
   the icon at a definite size, so it does not stretch to the card width, and
   the card's own alignment then places it — flush to the content edge on a
   left-aligned card, centred on a .cards--center one. `contain` keeps the
   drawing's proportions; the horizontal card restates a larger --tile-size. */
.card__icontile,
.card__icon {
	--tile-size: 4rem;
	inline-size: calc(var(--tile-size) * 0.7);
	block-size: calc(var(--tile-size) * 0.7);
	background-color: var(--card-accent, var(--color-teal));
	-webkit-mask: var(--icon-src) center / contain no-repeat;
	        mask: var(--icon-src) center / contain no-repeat;
}

/* A card with no accent bar needs its top padding back. */
.card--plain { padding-block-start: 1.75rem; }

/*
 * A programme's photo on its card, FULL-BLEED. 4:3 to match ey_card, so the
 * ratio the layout reserves is the ratio the file already is and nothing is
 * cropped twice. It runs to the top and side edges of the card: negative margins
 * cancel the card's 1.5rem padding, and the card's own overflow:hidden +
 * border-radius round the top corners, so the image needs no radius of its own.
 * (.card__media is the manual card's own image — an inset rounded-square
 * thumbnail, not this full-bleed top strip.)
 */
.card__img {
	inline-size: calc(100% + 3rem);
	max-inline-size: none;
	margin-block-start: -1.5rem;
	margin-inline: -1.5rem;
	margin-block-end: 0.9rem;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: 0;
	display: block;
}
/* Horizontal cards put their media in a side gutter, not across the top, so the
   photo keeps the inset, contained treatment there. */
.cards--horizontal > .card > .card__img {
	inline-size: 100%;
	max-inline-size: 100%;
	margin: 0 0 0.35rem;
	border-radius: calc(var(--radius-card) - 0.6rem);
}

/*
 * A book cover is portrait; the 4:3 card crop would slice its top and bottom.
 * Contain it instead, on a faint wash of the card's own accent so the letterbox
 * either side reads as a deliberate panel rather than a gap. Resources only.
 */
.card--format-book > .card__img {
	object-fit: contain;
	/*
	 * Transparent letterbox, not a coloured panel: the bars either side of a
	 * portrait cover show the card's own background through them, so a light
	 * cover's white edges melt into the card instead of sitting on pink. It
	 * follows whatever ground the card is on — the surface on a light section,
	 * the glass on a dark one — with nothing to re-tune per background.
	 */
	background: transparent;
}

/*
 * The stand-in for a resource with no cover — the picture slot filled with a
 * diagonal wash of the card's accent, same 4:3 box and edge bleed as .card__img,
 * so a coverless card keeps the grid even instead of starting at its title.
 */
.card__blank {
	inline-size: calc(100% + 3rem);
	margin-block-start: -1.5rem;
	margin-inline: -1.5rem;
	margin-block-end: 0.9rem;
	aspect-ratio: 4 / 3;
	background: linear-gradient(
		135deg,
		color-mix(in oklch, var(--card-accent, var(--color-teal)) 22%, white),
		color-mix(in oklch, var(--card-accent, var(--color-teal)) 7%, white)
	);
}

/*
 * The mark an icon-less card wears in place of an icon: a single dandelion
 * seed, not a bar. Same recolouring trick as .card__icontile — the seed art is
 * an alpha MASK over a fill, and that fill is --card-accent, the SAME token the
 * accent bar uses. So the seed is always the exact colour of the card's border,
 * including the per-band lift (teal/berry both go to their -lift on the brown
 * and dark grounds — see the --card-accent rules up in the border block). It
 * used to carry its own --pip token with a hand-kept lift matrix, which drifted:
 * the teal seed missed the lift on the brown band and sat a shade darker than
 * its border. One token, no drift. Sized to the icontile's box and with no
 * margin of its own, so a seed card and an icon card in one row drop their
 * titles onto the same line. Decoration: the card's meaning is its heading.
 */
.card__pip {
	inline-size: calc(4rem * 0.7);
	block-size: calc(4rem * 0.7);
	background-color: var(--card-accent, var(--color-teal));
	-webkit-mask: url("../img/decor/seed-18.svg") center / contain no-repeat;
	        mask: url("../img/decor/seed-18.svg") center / contain no-repeat;
}

/* Links that point somewhere carry an arrow. Generated content, so it is not
   read out as a word — the link text already says where it goes. */
.arrowed::after { content: " \2192"; }

/* Logos keep their own proportions. */
.sponsor__logo { object-fit: contain; object-position: left center; }

/* ---------------- Contact: details and form ----------------
   0.85 / 1.15 rather than an even split: the form is the thing most people
   came to use, and the details column is a list of short lines that would
   only stretch. */

.contact-split { display: grid; gap: 2.5rem; }
@media (min-width: 900px) {
	.contact-split { grid-template-columns: 0.85fr 1.15fr; gap: 3.5rem; }
	/* Without the form there is no second column, and the details should not
	   sit in the narrow 0.85fr track next to a hole. */
	.contact-split--solo { grid-template-columns: minmax(0, 1fr); }
}
.contact-split .section__head { margin-block-end: 1.5rem; }
.contact-split .section__title { font-size: clamp(1.5rem, 1.2rem + 1vw, 1.9rem); }

.contact-card-list {
	list-style: none;
	margin: 1.5rem 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}
.contact-card {
	display: flex;
	align-items: flex-start;
	gap: 0.75rem;
	padding: 1rem;
	border-radius: 0.75rem;
	background: #fff;
	border: 1px solid var(--surface-card-border);
}
/* Bright, not the readable shade: these are aria-hidden marks sitting beside
   a label that already says "Phone" — see the icon note in inc/icons.php. */
.contact-card > .icon { color: var(--surface-link, var(--color-teal-ink)); font-size: 1.15rem; margin-block-start: 0.1rem; }
.contact-card__label {
	display: block;
	font-size: 0.8rem;
	color: var(--color-ink-soft);
}
.contact-card__value {
	font-family: var(--font-display);
	font-weight: 700;
	color: var(--color-ink);
	text-decoration: none;
	/* An address is one long unbroken token on a narrow column. */
	overflow-wrap: anywhere;
}
a.contact-card__value:hover,
.contact-card__value:hover { color: var(--color-teal-ink); }

.contact-notice {
	margin: 1.25rem 0 0;
	padding: 1rem;
	border-radius: 0.75rem;
	/* neon at a tenth on cream is a tint, not a fill; the ink on it is still
	   the page's own 15:1 ink. */
	background: color-mix(in oklch, var(--color-teal) 12%, transparent);
	border: 1px solid color-mix(in oklch, var(--color-teal-ink) 25%, transparent);
	color: var(--color-ink);
	font-size: 0.9rem;
	line-height: 1.6;
}

.contact-split__map { margin-block-start: 1.5rem; }
.contact-split__map-label {
	margin: 0 0 0.5rem;
	font-family: var(--font-display);
	font-size: 0.85rem;
	font-weight: 600;
	color: var(--color-ink);
}

.contact-form-card {
	padding: 1.5rem;
	border-radius: var(--radius-card);
	background: #fff;
	border: 1px solid var(--surface-card-border);
	box-shadow: 0 18px 44px -28px color-mix(in oklch, var(--color-ink) 35%, transparent);
}
@media (max-width: 560px) {
	.contact-form-card { padding: 1.15rem; }
}

/* ---------------- Fluent Forms ----------------
   The submit button already carries .btn .btn-primary from the form config,
   but the plugin ships its own skin (.ff-default .ff_btn_style: 7px corners,
   its own padding and weight) AND writes the form's colour into an inline
   <style> in the body — so it beats the theme on both specificity and order.
   The result was a darker green rectangle with the wrong corners sitting in a
   card full of correctly-shaped buttons.

   Three classes puts the theme's own values back on top: enough to outrank
   `form.fluent_form_3 .ff-btn-submit` without reaching for !important, which
   would take the styling away from the client's form panel for good.

   The colours are .btn-primary's: the berry fill with a white label at 7.87:1,
   where the plugin was painting its own darker green. */
.ff-btn-submit.btn.btn-primary {
	background: var(--color-berry);
	border-color: transparent;
	color: #fff;
	border-radius: var(--radius-btn);
	padding: 0.9rem 1.5rem;
	min-height: 48px;
	font-family: var(--font-display);
	font-size: 0.975rem;
	font-weight: 600;
	line-height: 1;
	box-shadow: 0 8px 22px -6px color-mix(in oklch, var(--color-berry) 50%, transparent);
}
.ff-btn-submit.btn.btn-primary:hover,
.ff-btn-submit.btn.btn-primary:focus {
	background: var(--color-berry);
	color: #fff;
	transform: translateY(-2px);
	box-shadow: 0 14px 30px -8px color-mix(in oklch, var(--color-berry) 60%, transparent);
}
.ff-btn-submit.btn.btn-primary:active { transform: translateY(0) scale(.985); }

/* The plugin skins its same-page confirmation as a bordered, shadowed box
   (.ff-message-success: 1px #ced4da border + drop shadow + 15px padding), which
   drew a second grey frame inside the white .contact-form-card. The card is the
   container; the confirmation is its content — so drop the plugin's box and let
   the message sit as text in the card. Scoped through .contact-form-card to
   outrank the plugin's single-class rule without !important, the same reason the
   submit button above needs three classes. Error styling is untouched: it lives
   on .ff-el-is-error / .ff-errors-in-stack / .text-danger, not this class. */
.contact-form-card .ff-message-success {
	border: 0;
	box-shadow: none;
	padding: 0;
	margin-top: 0;
}

/* ---------------- The featured person card ----------------
   The Featured profile SECTION is retired — Cards with Show text says it now —
   but the card treatment it used is not: single-programme.php still asks
   person-card for it, so a programme run by one person shows them this way. */

/*
 * The card itself goes two-column: photo left, everything else right. A square
 * portrait at full card width pushed the name below the fold on a phone-sized
 * column, and at this size the face is an identifier rather than the content.
 */
@media (min-width: 560px) {
	.person--featured {
		display: grid;
		grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
		align-items: start;
		gap: 1.5rem;
	}
	.person--featured .person__photo { margin-block-end: 0; }
}
/* .person is a flex column, so its children stack; the featured card moves the
   text into its own box and lets the grid place the two. */
.person__body { display: flex; flex-direction: column; gap: 0.6rem; }
.person--featured .person__name { font-size: 1.35rem; }
.person__bio { line-height: 1.6; }

/* ---------------- Split list ----------------
   Heading in its own column, list beside it. The head is the narrower half:
   it is two or three lines, and the list is the substance. */

/*
 * A list beside its heading wants the opposite ratio to cards beside theirs.
 *
 * .cardset gives the words 1.15 against 0.85, because a row of cards is a
 * supporting exhibit. A list IS the section — phrases that each want to stay on
 * one line — so it keeps the 0.8/1.2 and the wider gap it had while this was
 * .listsplit and a tickbox called "Heading beside the list". That ratio is the
 * only thing about the arrangement that was ever specific to a list;
 * everything else is the shared one now.
 */
@media (min-width: 900px) {
	.section--list .cardset--text-left  { grid-template-columns: 0.8fr 1.2fr; gap: 3.5rem; }
	.section--list .cardset--text-right { grid-template-columns: 1.2fr 0.8fr; gap: 3.5rem; }
}

/* Two columns of short points. Below 640px they stack: a checklist item is a
   phrase, and half a phone width turns each one into three lines.

   The two cards in a row match heights — grid's own default, and `align-items:
   start` was overriding it. A row is only as tall as its tallest card, so when
   "A case manager or support coordinator" wrapped to two lines the card beside
   it stayed at one line and left the difference as a hole in the column — a
   gap that read as a missing item rather than as a wrapped neighbour. */
@media (min-width: 640px) {
	.pills--stacked.pills--two {
		display: grid;
		grid-template-columns: 1fr 1fr;
	}
	/* A boxed list holds its own column edge; a ticked one has none, so its two
	   columns need real space between them or they read as one ragged block. */
	.pills--stacked.pills--two.pills--checks { column-gap: 2rem; }
}

/* ---------------- Outlined cards ----------------
   The border carries the accent instead of a bar. For cards that have to be
   told apart at a glance — a safety note beside a funding note — rather than
   just coloured for variety. */

/*
 * The shorthand, not just width and colour: .card carries no border of its own
 * any more, so border-style would resolve to none and this would draw nothing.
 * 1px rather than 1.5 because it is the only line on the card now — at 1.5 it
 * read as a frame around the card rather than an edge to it.
 */
.cards--outlined > .card { border: 1px solid var(--card-line, var(--surface-card-border)); }
.cards--outlined > .card--accent-teal   { --card-line: color-mix(in oklch, var(--color-teal-ink) 45%, transparent); }
.cards--outlined > .card--accent-berry  { --card-line: color-mix(in oklch, var(--color-berry) 45%, transparent); }
/* On dark the -ink shades disappear into the ground, so each pathway's on-dark
   lift draws the line instead — a border is not text and clears 3:1. */
.is-brown .cards--outlined > .card--accent-teal,
.is-dark .cards--outlined > .card--accent-teal,
.is-berry .cards--outlined > .card--accent-teal,
.is-teal .cards--outlined > .card--accent-teal   { --card-line: color-mix(in oklch, var(--color-teal-lift) 60%, transparent); }
.is-brown .cards--outlined > .card--accent-berry,
.is-dark .cards--outlined > .card--accent-berry,
.is-berry .cards--outlined > .card--accent-berry,
.is-teal .cards--outlined > .card--accent-berry  { --card-line: color-mix(in oklch, var(--color-berry-lift) 60%, transparent); }

/* A card's kicker sits tighter to its title than a section's does, and takes
   the card's own accent rather than the surface default. */
.card__kicker { margin: 0; font-size: 0.72rem; color: var(--accent-ink, var(--surface-kicker)); }
.card--accent-teal   { --accent-ink: var(--color-teal-ink); }
.card--accent-berry  { --accent-ink: var(--color-berry); }
.is-brown .card--accent-teal,   .is-dark .card--accent-teal,   .is-berry .card--accent-teal, .is-teal .card--accent-teal   { --accent-ink: var(--color-teal-lift); }
.is-brown .card--accent-berry, .is-dark .card--accent-berry, .is-berry .card--accent-berry, .is-teal .card--accent-berry { --accent-ink: var(--color-berry-lift); }

/*
 * A list written into a section's intro gets accent markers rather than the
 * browser's bullets, matching the checklists elsewhere.
 *
 * .section__intro belongs to every layout, and this selector is exactly as
 * broad as the capability: Cards is the only one whose Intro is a wysiwyg, so
 * it is the only one that can hold a list at all. The enquiry list on
 * Referrals is the case.
 */
.section__intro ul {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 0.55rem;
}
.section__intro ul li {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0.65rem;
	align-items: start;
	font-size: 0.95rem;
	color: var(--surface-ink-soft);
}
.section__intro ul li::before {
	content: "";
	inline-size: 7px;
	block-size: 7px;
	margin-block-start: 0.5em;
	border-radius: 999px;
	/* The accent's fill, like the checklist ticks — a marker on every row
	   distinguishes none of them, so it is texture rather than text. */
	background: var(--section-accent, var(--color-teal));
}

/* ---------------- Card labels, notes and actions ---------------- */

.card__labels {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem;
	list-style: none;
	margin: 0.35rem 0 0;
	padding: 0;
}
.card__labels li {
	padding: 0.3rem 0.7rem;
	border-radius: 999px;
	background: var(--surface-card-border);
	color: var(--surface-ink-soft);
	font-size: 0.8rem;
}
.is-brown .card__labels li,
.is-dark .card__labels li,
.is-berry .card__labels li,
.is-teal .card__labels li { background: oklch(100% 0 0 / 0.08); }

.card__note { margin: 0.35rem 0 0; font-size: 0.8rem; line-height: 1.5; color: var(--surface-ink-soft); }

/* The action sits at the bottom of the card whatever length the text above it
   runs to, so a row of cards lines its buttons up. */
.card__btn { margin-block-start: auto; align-self: flex-start; }
/*
 * The card's actions, wrapped, because a card can carry more than one button
 * now — the visiting card on Referrals has two.
 *
 * The wrapper is what takes the auto margin that pins them to the foot of the
 * card. It cannot stay on the buttons: two flex items each asking for `margin-
 * block-start: auto` split the free space between them and end up at opposite
 * ends of the card rather than together at the bottom.
 */
.card__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.6rem;
	margin: auto 0 0;
}
.card__actions > .card__link,
.card__actions > .card__btn { margin-block-start: 0; }
.card__labels + .card__actions,
.card__note + .card__actions { padding-block-start: 1rem; }

/*
 * The card's own picture, which is not .card__img.
 *
 * .card__img is a programme's photograph, full-bleed across the top of its
 * card. This is the manual card's own image, and it reads as a small block
 * pinned to the leading edge — so it lines up with the heading and body beneath
 * it rather than floating centred over them.
 *
 * CONTAINED, never cropped — because this field is for logos and drawings, which
 * is what manual.php declares at the point it renders the image ("these are
 * logos and drawings, and a 4:3 crop takes the top off one"). An earlier
 * aspect-ratio: 1 + object-fit: cover overrode that and square-cropped whatever
 * was dropped in: a line-drawing lost its top and bottom, a wide scene its
 * sides. The template's intent wins — natural ratio inside an 8rem box, capped
 * on both axes so a tall drawing can't run the card and a wide one can't overrun
 * it. The lone place the field held a portrait photo (a team headshot) now
 * letterboxes rather than crops, which is the acceptable cost of honouring the
 * common case; a portrait belongs on a person card, not this field.
 *
 * No object-fit crop and no forced ratio, so a transparent line-drawing needs
 * no radius and no image outline (better-ui) — an outline would trace the
 * transparent bounding box and read as a stray frame. `align-self` sets the
 * leading edge whatever the card's align-items is — Centre contents is the one
 * control that overrides it (see .cards--center).
 */
.card__media {
	display: block;
	align-self: flex-start;
	inline-size: 8rem;
	max-inline-size: 100%;
	block-size: auto;
	max-block-size: 8rem;
	object-fit: contain;
	margin-block-end: 0.5rem;
}

/*
 * The card's illustration — the full-scene brand drawing, inlined by
 * ey_illustration_svg() so it can recolour to the card it sits on.
 *
 * Unlike the icon (a single-colour mask) this is a two-tone SVG, and the two
 * tones read straight off the card's tokens: the outline is currentColor, so it
 * takes the surface ink and flips light on the dark bands; the accent is
 * --ey-illo-accent, bound here to --card-accent, so a berry card's illustration
 * comes out berry and a teal card's teal, each lifting on the brown/teal/berry
 * grounds exactly as the accent bar and icon already do. No second copy of any
 * drawing, and nothing baked into the file — the same recolour trick the icon
 * mask uses, one tone further.
 *
 * A leading-pinned block across the card, sized to the card width rather than a
 * fixed tile: these are 4:3 scenes, not marks, so they lead the card the way a
 * programme photo does. contain via width + auto height off the file's viewBox.
 */
.card__illo {
	--ey-illo-accent: var(--card-accent, var(--color-berry));
	display: block;
	align-self: flex-start;
	inline-size: 100%;
	color: var(--surface-ink);
	margin-block-end: 0.75rem;
}
.card__illo svg {
	display: block;
	inline-size: 100%;
	block-size: auto;
}
/*
 * On the dark grounds the two tones flip off the cream ink into the card's own
 * colour. The linework takes the LIGHTER tone — the lifted --card-accent-hi, the
 * brightest step, so the fine strokes read most clearly on the dark ground; the
 * splash drops to the DARKEST tone, --card-accent-lo (the full-strength brand
 * teal / berry), a deep saturated fill held inside the bright outline. Scoped to
 * the brown / near-black bands only — on a teal or berry GROUND a same-hue line
 * would sink into it, so those keep the cream linework this rule leaves be.
 */
.is-brown .card__illo,
.is-dark .card__illo {
	color: var(--card-accent-hi, var(--surface-ink));
	--ey-illo-accent: var(--card-accent-lo, var(--card-accent, var(--color-berry)));
}
/* Centre contents pulls the drawing back to the middle, as it does the photo. */
.cards--center > .card > .card__illo { align-self: center; }

/*
 * A standalone brand illustration in a SECTION image slot (hero / Text-and-photo),
 * inlined by ey_media_composition() so it recolours — the same two-tone trick the
 * card illustration uses, but read off the SECTION rather than a card. The linework
 * takes --surface-ink (so it flips light on the dark / berry / teal bands and dark
 * on cream, always legible on the ground) and the accent splash takes the section
 * accent. No card here to borrow --card-accent from, so no -hi/-lo lift is needed:
 * surface-ink is already the right-for-this-ground tone.
 *
 * Sized to fill its column, contained on the file's own 4:3 viewBox — these are
 * scenes, not marks — with no radius or image outline, because a transparent line
 * drawing would only get a stray frame traced round its bounding box (better-ui).
 */
.ey-illo {
	--ey-illo-accent: var(--section-accent, var(--color-berry));
	display: block;
	inline-size: 100%;
	color: var(--surface-ink);
}
.ey-illo svg { display: block; inline-size: 100%; block-size: auto; }

/*
 * On a berry or teal GROUND the section illustration keeps its BLACK linework
 * (Nikki: don't flip the drawing to the cream surface ink here) and carries the
 * colour in the splash instead: the dark pink on berry, the lifted teal on teal.
 * The figure reads as a bold ink silhouette with one coloured garment — the
 * duotone the bands already run, one tone further — rather than a flat cream
 * cut-out that loses its line. Ground-scoped, so cream / brown bands keep the
 * surface-ink linework + section-accent splash the base rule gives them.
 *
 * --color-ink is a warm near-black (#241D18); as a decorative line drawing over
 * the deep berry / teal grounds it reads as shape and texture, not body copy, so
 * the low line-to-ground ratio is intended, not a contrast failure.
 */
.is-berry .ey-illo {
	color: var(--color-ink);
	--ey-illo-accent: var(--color-pink-ink);   /* #D52D5C — the dark display pink */
}
.is-teal .ey-illo {
	color: var(--color-ink);
	--ey-illo-accent: var(--color-teal-lift);  /* #53A296 — lifted teal */
}

/*
 * Centre card contents — ALL of it.
 *
 * text-align was never going to do this on its own. A card is a flex column
 * with `align-items: flex-start`, so every child is shrink-wrapped and pinned
 * to the left edge, and text-align then centres the words INSIDE each of those
 * boxes. A note long enough to fill the card came out looking centred; a
 * heading, a logo or a link did not — each was a narrow box against the left
 * margin with its text neatly centred inside it. Measured on the sponsor
 * cards: the note spanned the full 273px, the name stopped 127px short of the
 * right edge and the link 150px short, all three flush left.
 *
 * `align-items: center` is the fix, and it is deliberately generic — it
 * centres every direct child a card has rather than a list of class names that
 * the next card type would not be on. The list that used to be here was that
 * list, and it is why logos and links were missed.
 *
 * Safe on a horizontal card: that is the same flex column with a padding
 * gutter, and its media is absolutely positioned into the gutter where
 * align-items cannot reach it.
 */
.cards--center > .card {
	text-align: center;
	align-items: center;
}
/* The children that are flex rows in their own right. align-items placed the
   row in the card; this places the items in the row. */
.cards--center > .card > :is(.card__labels, .card__actions, .card__badges, .testimonial__by) {
	justify-content: center;
}
/*
 * The logo pins its artwork to the left of its own box so a row of odd-sized
 * logos lines up on one edge — right until the card asks for the opposite.
 */
.cards--center > .card > .sponsor__logo { object-position: center; }
/* The card image is leading-pinned by default (align-self on .card__media);
   Centre contents is the one control that pulls it back to the middle. */
.cards--center > .card > .card__media { align-self: center; }

/* A list written into a card's text gets the same accent markers as the column
   beside it, and two columns once there is room for them. "Funds may support"
   on Support us is the case. */
.card__text ul {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 0.5rem;
}
@media (min-width: 480px) {
	.card__text ul { grid-template-columns: 1fr 1fr; column-gap: 1.25rem; }
}
.card__text ul li {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0.6rem;
	align-items: start;
	font-size: 0.9rem;
	color: var(--surface-ink-soft);
}
.card__text ul li::before {
	content: "";
	inline-size: 6px;
	block-size: 6px;
	margin-block-start: 0.5em;
	border-radius: 999px;
	/* The CARD's fill first: a list inside a cycling card belongs to the card,
	   the same way its bar and its link now do. */
	background: var(--card-accent, var(--section-accent, var(--color-teal)));
}

/* ================================================================
 * Photo-artwork shapes (Shapes-02 / Shapes-03) and the sections
 * built on them. The SVGs are square (1080×1080); they scale to the
 * width of their slot and keep their own delivered colours.
 * ================================================================ */
.ey-shape { display: block; inline-size: 100%; block-size: auto; }

/* ---------------- Hero with photos (Shapes-02, full-bleed) ------- */
/*
 * The one section that leaves the container: text on the left aligned to the
 * site's content edge, the artwork running to the right viewport edge. It defeats
 * .container-lab for itself alone, then re-establishes the left inset with padding
 * so the words still line up with every other section above and below.
 */
.section--hero-photos .section__inner { max-width: none; padding-inline: 0; }
.hero--photos { display: grid; gap: 1.5rem 2.5rem; align-items: center; }
.hero--photos .hero__body { padding-inline: 1.5rem; }
.hero__photos { min-inline-size: 0; }
.hero__photos .ey-shape { margin-inline: auto; max-inline-size: 34rem; }

@media (min-width: 861px) {
	/* The section zeroes its own vertical padding so the words centre clear of the
	   header (align-items: center on the grid). The photo composition sizes to its
	   column and stays CONTAINED — square and fully visible — rather than stretching
	   to the full hero height and covering, which cropped its sides on tall columns. */
	.section--hero-photos { padding-block: 0; }
	/* Words fill to the page centre; both photo heroes share the measure so their
	   text lines up. */
	.hero--photos { grid-template-columns: minmax(0, 50vw) 1fr; gap: 2.5rem; }
	.hero--photos .hero__body {
		/* Align the text's leading edge with .container-lab's content edge. */
		padding-inline-start: max(1.5rem, calc((100vw - var(--wrap)) / 2 + 1.5rem));
		padding-inline-end: 0;
	}
	/* A touch larger than the base cap on desktop, centred in the artwork column
	   (base .ey-shape keeps margin-inline: auto and the square, contained aspect). */
	.hero__photos .ey-shape { max-inline-size: 38rem; }
	/* Hold the composition off the trailing glass on narrow desktops, where the
	   artwork column can be as slim as the shape itself. Mirrored for Text right. */
	.hero--photos .hero__photos { padding-inline-end: 1.5rem; }
	/* And a vertical gutter so the photo sits OFF the section's top and bottom edges.
	   The section zeroes its own block padding here (above) so the words centre clear
	   of the header; that leaves the contained, square photo — the tallest grid item —
	   running flush to both band edges. Insetting the photo COLUMN (not the section)
	   lifts it off the edges while the words stay centred and header clearance is
	   untouched. */
	.hero--photos .hero__photos { padding-block: 2.5rem; }
	.hero--photos.hero--text-right .hero__photos { padding-inline-end: 0; padding-inline-start: 1.5rem; }
}
/* On a phone the artwork stacks under the words, capped so it is not a screenful
   and held off both edges by the layout gutter. */
@media (max-width: 860px) {
	.hero__photos .ey-shape { max-inline-size: 26rem; }
}

/* ---------------- Text + shape photo (Shapes-03) ---------------- */
.textmedia__shape { margin: 0; min-inline-size: 0; }
.textmedia__shape .ey-shape { max-inline-size: 30rem; margin-inline: auto; }
.textmedia--left .textmedia__shape { order: -1; }

/* ---------------- Duo Tone (Hero + Text and photo) -------------- */
/*
 * A two-band ground with the artwork centred on the seam: deep brown above,
 * berry below. Above the seam sits the eyebrow and heading (hugging it from
 * above); everything else sits below it (hugging from below). The colour tokens
 * for each band come from main.css (.is-duotone for the brown band; .duotone__below
 * re-scopes the berry set).
 *
 * The seam is painted by a full-bleed ::before pinned to the GRID's box, never
 * the section's — the grid carries symmetric block padding, so its 50% is exactly
 * the 1fr/1fr row boundary the heading, body and image all reference. Tying it to
 * the section instead would drift the moment the first section opens up room for
 * the fixed header. The section keeps no vertical padding of its own; its
 * --surface-bg is the top band's brown, so a first section's header-clearance
 * strip reads as more of the top band.
 */
.is-duotone.section { padding-block: 0; }

.hero--duo,
.textmedia--duo {
	position: relative;
	isolation: isolate;
	display: grid;
	gap: 0;
	padding-block: var(--space-section);   /* symmetric — keeps the seam at 50% */
}

/*
 * The hero carries no block padding of its own — every duotone hero version.
 * The seam gradient fills the box including padding, so any top/bottom padding
 * renders as a coloured band above and below the content; that band is removed
 * here. The seam still lands at 50% because the two rows stay 1fr/1fr (the
 * symmetric padding was one way to hold that; zero holds it too). The seam sits
 * at the content midline rather than floating on extra air. textmedia--duo,
 * which is body content and not the hero, keeps its breathing room above.
 */
.hero--duo { padding-block: 0; }

/*
 * A small amount of vertical breathing room on the hero, on request: a nudge at
 * the top, double that below, so the berry band gets more air under the content.
 * The bottom is expressed off the top var so the seam shift below can stay tied
 * to it exactly. Both well under --space-section.
 */
.hero--duo {
	--ey-hero-pt: clamp(1rem, 0.5rem + 1.25vw, 1.75rem);
	padding-block-start: var(--ey-hero-pt);
	padding-block-end: calc(var(--ey-hero-pt) * 2);
}

/* Phone: stacked full-bleed bands. textmedia--duo keeps its art in the middle,
   so the seam falls through the image (gradient band on the art). The hero
   stacks heading → words → photos (art last, see hero.php), so its seam is the
   hard edge between the brown heading band and the berry that carries the words
   AND the photos beneath them — the art band is berry, not the gradient. */
@media (max-width: 860px) {
	.hero--duo > *,
	.textmedia--duo > * { position: relative; isolation: isolate; }
	.duotone__above::before,
	.duotone__art::before,
	.duotone__below::before {
		content: "";
		position: absolute;
		z-index: -1;
		inset-block: 0;
		inset-inline: calc(50% - 50vw);
		inline-size: 100vw;
	}
	.duotone__above::before { background: var(--duo-top); }
	.duotone__art::before   { background: linear-gradient(var(--duo-top) 0 50%, var(--duo-bottom) 50% 100%); }
	.duotone__below::before { background: var(--duo-bottom); }
	/* Hero art is last and sits on the berry band, so it takes the flat bottom
	   colour rather than the middle-seam gradient. Hero only — textmedia--duo,
	   whose art is still the middle band, keeps the gradient above. */
	.hero--duo .duotone__art::before { background: var(--duo-bottom); }

	/* Kill the hero's own block padding on a phone. The seam is painted per-band by
	   the ::before elements above (each pinned to its own band with inset-block: 0),
	   so the pt/2pt padding on .hero--duo — meant for the desktop gradient, which
	   DOES fill its own padding — falls OUTSIDE every band here: above the brown
	   heading band (brown-on-brown, invisible) and, worse, below the last berry band,
	   where it shows the section's brown --surface-bg as a brown strip under the hero.
	   Zeroing it makes the phone stack clean edge-to-edge; the art band keeps its own
	   1.5rem of berry breathing room below the photos. */
	.hero--duo { padding-block: 0; }

	.duotone__above { padding-block-end: 1.25rem; }
	.duotone__below { padding-block-start: 1.5rem; }
	.duotone__art   { padding-block: 1.5rem; display: grid; place-items: center; }
	/* The section zeroes its inline padding for the desktop bleed, so on a phone
	   the stacked composition would run to both edges — give it the layout gutter
	   back. */
	.hero--duo-photos .duotone__art { padding-inline: 1.5rem; }
}

/* Desktop: two columns, two equal rows; the artwork spans both and centres on
   the seam, the words split across it. */
@media (min-width: 861px) {
	.hero--duo::before,
	.textmedia--duo::before {
		content: "";
		position: absolute;
		z-index: -1;
		inset-block: 0;
		inset-inline: calc(50% - 50vw);
		inline-size: 100vw;
		background: linear-gradient(var(--duo-top) 0 50%, var(--duo-bottom) 50% 100%);
	}

	/*
	 * The seam above sits at 50% of the box. The hero's bottom padding is double
	 * its top, so the box midline drops below the content split — the line the two
	 * word rows meet on. Shift the hero's OWN seam up by half the extra padding
	 * (the bottom-minus-top gap is one --ey-hero-pt, so half is pt/2) to put it
	 * back on the split. Hero only; textmedia--duo keeps symmetric padding and the
	 * plain 50% stop.
	 */
	.hero--duo::before {
		background: linear-gradient(
			var(--duo-top) 0 calc(50% - var(--ey-hero-pt) / 2),
			var(--duo-bottom) calc(50% - var(--ey-hero-pt) / 2) 100%
		);
	}

	.hero--duo,
	.textmedia--duo {
		grid-template-columns: 1fr 1fr;
		grid-template-rows: 1fr 1fr;
		column-gap: clamp(2rem, 5vw, 4rem);
		row-gap: 0;
	}

	/* Text left (default): words in column 1, artwork spanning column 2. */
	.duotone__above { grid-area: 1 / 1; align-self: end;   padding-block-end: 1.5rem; }
	.duotone__below { grid-area: 2 / 1; align-self: start; padding-block-start: 1.5rem; }
	.duotone__art   { grid-area: 1 / 2 / 3 / 3; align-self: center; }

	/* Text right: mirror the columns. The frame corner does NOT mirror. */
	.hero--text-right .duotone__above,
	.textmedia--right .duotone__above { grid-area: 1 / 2; }
	.hero--text-right .duotone__below,
	.textmedia--right .duotone__below { grid-area: 2 / 2; }
	.hero--text-right .duotone__art,
	.textmedia--right .duotone__art { grid-area: 1 / 1 / 3 / 2; }

	.duotone__art { display: grid; place-items: center; }
}

.duotone__art img,
.duotone__art .ey-shape {
	inline-size: 100%;
	block-size: auto;
	max-inline-size: 30rem;
	max-block-size: min(34rem, 70vh);
	object-fit: contain;
	margin-inline: auto;
}

/* No artwork: one column, so the words simply split across the seam. */
@media (min-width: 861px) {
	.hero--duo-solo,
	.textmedia--duo-solo { grid-template-columns: 1fr; }
}

/* The three-photo composition centres on the seam and stays CONTAINED — fully
   visible, never cover-cropped — the same as the Duo Tone Text-and-photo art
   (base .duotone__art .ey-shape keeps the square, contained aspect). */
.hero--duo-photos .duotone__art {
	max-inline-size: 34rem;
	margin-inline: auto;
	inline-size: 100%;
}

/*
 * The words re-establish the container's left inset.
 *
 * section--hero-photos zeroes the section's inline padding so the composition can
 * bleed to the viewport edge. The non-duo .hero--photos body re-adds the inset on
 * its own; the Duo Tone words live in .duotone__above/__below instead, so without
 * this they sit flush against the screen. The value matches .hero--photos exactly,
 * so a Duo Tone triptych's text lines up with every other section's. Mirrored for
 * Text right, where the artwork bleeds left and the words hold the right edge.
 */
.hero--duo-photos .duotone__above,
.hero--duo-photos .duotone__below { padding-inline: 1.5rem; }
@media (min-width: 861px) {
	/*
	 * The words fill the left half of the CONTENT area — from the content edge to
	 * the page centre (50vw) — and the artwork takes the rest and bleeds to the
	 * viewport edge. That is the same measure a single-photo hero's words get from
	 * its own column, so the heading and intro break against the same right edge
	 * whichever Artwork option the hero carries. No right padding: the column stops
	 * at the page centre, and the column gap holds the words off the artwork.
	 */
	.hero--duo-photos { grid-template-columns: minmax(0, 50vw) 1fr; }
	.hero--duo-photos.hero--text-right { grid-template-columns: 1fr minmax(0, 50vw); }

	.hero--duo-photos .duotone__above,
	.hero--duo-photos .duotone__below {
		padding-inline-start: max(1.5rem, calc((100vw - var(--wrap)) / 2 + 1.5rem));
		padding-inline-end: 0;
	}
	.hero--duo-photos.hero--text-right .duotone__above,
	.hero--duo-photos.hero--text-right .duotone__below {
		padding-inline-start: 0;
		padding-inline-end: max(1.5rem, calc((100vw - var(--wrap)) / 2 + 1.5rem));
	}
}

/* Plain photo under Duo Tone: the brand teardrop's corner signature — three
   round corners, a sharp bottom-left. Physical, so it never mirrors on flip. */
.duo-frame {
	border-radius: var(--radius-card);
	border-bottom-left-radius: 0;
	overflow: hidden;
}

/* ---------------- Numbered steps — the journey ----------------- */
/*
 * The process reads as a pathway. On desktop a single smooth line (drawn in
 * assets/js/steps-path.js) threads the numbered nodes, solid on the upper half
 * of each segment and dashed on the lower. The numbers sit ALIGNED on the line's
 * centre; the line does the weaving, bulging out to alternating sides BETWEEN
 * the numbers — so each number sits at a neck of the wave, not on its crest. A
 * large icon sits on the OUTER side of each number, its side alternating down
 * the list. The heading + a compact, tight-leading text box stay on the trailing
 * side throughout.
 *
 * The <ol> carries the true sequence for assistive tech; the line, the node
 * badges and the icons are decorative reinforcement (the SVG is aria-hidden, the
 * badge number is aria-hidden in markup, the icon is aria-hidden via ey_icon()).
 *
 * On mobile there is no curve and no icons: the numbers sit in a plain column
 * beside the text (a CSS-only downgrade — the script clears the line).
 */
.steps-journey {
	position: relative;
	/* Defined here, not on .steps, because the SVG is a sibling of the <ol> —
	   the line colour must inherit to it. --section-accent comes from the section. */
	--steps-line: color-mix(in oklch, var(--section-accent, var(--color-teal)) 62%, transparent);
}

.steps {
	--steps-node: 3rem;                    /* node diameter */
	--steps-gap: 2.5rem;                   /* vertical gap between rows — room for the wave to bulge */
	--steps-rail: 5.5rem;                  /* number zone (no icons) */
	--steps-icon: 3.25rem;                 /* the large step icon's box */
	--steps-a: var(--section-accent, var(--color-teal));   /* lead node colour */
	--steps-b: var(--section-accent, var(--color-teal));   /* alternating partner; set on light grounds below */
	list-style: none; margin: 2.25rem 0 0; padding: 0;
	display: grid; gap: var(--steps-gap);
}
/*
 * Duotone rhythm — nodes alternate the two brand colours, leading with the
 * section's own accent and alternating to its partner. Only on light grounds
 * (white / cream), where BOTH deep berry and deep teal clear AA behind white
 * numerals; on any other surface --steps-b stays the section accent, so an
 * unplanned placement can never drop a node below AA.
 */
.is-white.section--accent-berry .steps, .is-cream.section--accent-berry .steps { --steps-b: var(--color-teal); }
.is-white.section--accent-teal  .steps, .is-cream.section--accent-teal  .steps { --steps-b: var(--color-berry); }
/* Icons need room on the number's outer side; without them the rail is tighter. */
.steps--has-icons { --steps-rail: 10rem; }

/* The JS-drawn curve, threaded through the number centres. Desktop only; the
   script empties it below the breakpoint, where the line is hidden anyway. */
.steps__path {
	position: absolute; inset: 0; inline-size: 100%; block-size: 100%;
	pointer-events: none; overflow: visible; z-index: 0;
}
.steps__path-solid, .steps__path-dash {
	fill: none; stroke: var(--steps-line); stroke-width: 2.4;
	stroke-linecap: round; stroke-linejoin: round;
}
.steps__path-dash { stroke-dasharray: 3 7; }

.steps__item {
	position: relative; z-index: 1;
	display: grid; grid-template-columns: var(--steps-rail) 1fr;
	gap: 0 1.5rem; align-items: center;
}
/* The numbers sit ALIGNED on the rail's centre; the curve (JS) weaves between
   them, so a number lands at a neck of the wave rather than on its crest. */
.steps__num {
	position: relative; z-index: 2; justify-self: center;
	display: flex; align-items: center; justify-content: center;
	inline-size: var(--steps-node); block-size: var(--steps-node); flex: none;
	border-radius: 999px;
	background: var(--steps-a);
	color: var(--section-accent-on, #fff);   /* white reads on both deep berry and deep teal */
	font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; line-height: 1;
	box-shadow: 0 0 0 5px var(--surface-bg, transparent);   /* halo: clean gap where the line meets the node */
}
.steps__item:nth-child(even) .steps__num { background: var(--steps-b); }
/* The large icon rides on the OUTER side of the number, its side alternating. */
.steps__icon-wrap {
	position: absolute; z-index: 2; top: 50%; translate: 0 -50%;
	inline-size: var(--steps-icon); block-size: var(--steps-icon);
	display: flex; align-items: center; justify-content: center;
	color: var(--steps-a);
}
.steps__item:nth-child(odd)  .steps__icon-wrap { inset-inline-start: 0; }
.steps__item:nth-child(even) .steps__icon-wrap {
	inset-inline-start: calc(var(--steps-rail) - var(--steps-icon));
	color: var(--steps-b);
}
.steps__icon-wrap .icon { inline-size: 3rem; block-size: 3rem; stroke-width: 1.35; }
/* A compact, tight-leading text box on the trailing side. */
.steps__body { align-self: center; max-inline-size: 40ch; }
.steps__title { margin: 0 0 0.25rem; font-size: 1.1rem; line-height: 1.2; }
.steps__text { margin: 0; color: var(--surface-ink-soft); line-height: 1.35; }

/*
 * Mobile: no curve, no icons, no weave — a plain numbered column beside the
 * text. A CSS-only downgrade; the script leaves the (hidden) SVG empty. The
 * breakpoint is content-derived: below ~48em the icon + weave + a readable text
 * measure no longer fit three-abreast.
 */
@media (max-width: 47.99em) {
	.steps__path { display: none; }
	.steps__icon-wrap { display: none; }
	.steps__item {
		grid-template-columns: var(--steps-node) 1fr;
		gap: 0 1.1rem; align-items: start;
	}
	.steps__num { translate: none; }
	.steps__body { align-self: start; padding-block-start: 0.2rem; }
}

/* ---------------- Impact numbers -------------------------------- */
/*
 * Left-aligned to the section's leading edge, the same edge the head and the
 * status note already sit on — the figures are a group under the heading, not a
 * band spread edge-to-edge. max-content columns + justify-content:start pack
 * them left rather than stretching each across a third of the row (which
 * stranded "$420,000" in the middle of empty space). A section whose head is
 * centred (section__head--center) carries the figures back to centre with it,
 * so the two never disagree.
 */
.stats {
	display: grid; margin: 2rem 0 0; gap: 1.5rem 3rem;
	grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), max-content));
	justify-content: start;
}
.stats__item { text-align: start; }
.section:has(.section__head--center) .stats { justify-content: center; }
.section:has(.section__head--center) .stats__item { text-align: center; }
.stats__value {
	margin: 0; font-family: var(--font-display); font-weight: 600; line-height: 1;
	font-size: clamp(2.2rem, 5vw, 3.25rem);
	color: var(--section-accent-ink, var(--color-teal-ink));
}
.stats__label { margin: 0.45rem 0 0; color: var(--surface-ink-soft); font-size: 0.95rem; }
.stats__note { margin: 1.5rem 0 0; font-size: 0.8rem; color: var(--surface-ink-soft); }

/* ==================================================================
 * Programme restructure — the card's chips + meta stack, and the Header
 * illustration.
 *
 * A programme card's visual is its Illustration, its body the Card description,
 * "who it is for" a row of chips, and When / Where / Age / Cost an icon-led meta
 * stack. The single programme's Header renders through the hero block with the
 * illustration injected as its art (contained section--hero-art, or the Duo Tone
 * art band); a hero has no card to carry --card-accent, so it is bound here.
 * ================================================================== */

/* The Header illustration recolours off --card-accent (see .card__illo). Bind it
 * to the section's own accent, and size it for the hero column rather than a card
 * tile — the card's 0.75rem bottom margin is a card thing, not a hero one. */
.section--hero-art .hero__art,
.hero--duo-photos .duotone__art--illo {
	--card-accent: var(--section-accent, var(--color-teal));
}
.hero__art .card__illo,
.duotone__art--illo .card__illo {
	margin-block-end: 0;
	margin-inline: auto;
	max-inline-size: 24rem;
}

/* Who it is for → chips. Neutral pills on the badge's already-tested grounds, so
 * a row reads as tags without competing with the card's accent; they wrap, and
 * the gap between them is the card's own rhythm. */
.card__chips {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem;
	margin: 0;
}
.chip {
	display: inline-flex;
	align-items: center;
	border-radius: 999px;
	background: var(--color-cream-200);
	color: var(--color-ink-soft);
	font-size: 0.78rem;
	font-weight: 600;
	line-height: 1.2;
	padding: 0.28rem 0.7rem;
}
.is-brown .chip,
.is-dark .chip,
.is-berry .chip,
.is-teal .chip { background: oklch(100% 0 0 / 0.1); color: var(--color-paper); }

/* When / Where / Age / Cost → an icon-led meta stack: facts, stacked, in the
 * card's soft ink. currentColor keeps the icon the exact weight and colour of the
 * text beside it, so it can never fall off-palette or below contrast. The icon
 * holds the first line when the value wraps. */
.card__meta-stack {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
	margin: 0;
	padding: 0;
	list-style: none;
}
.card__meta-item {
	display: flex;
	align-items: flex-start;
	gap: 0.5rem;
	font-size: 0.85rem;
	line-height: 1.35;
	color: var(--surface-ink-soft);
}
.card__meta-item .icon {
	margin-block-start: 0.15em;
	color: var(--card-accent, var(--section-accent, var(--color-teal)));
}
