/*
Theme Name:   CDF
Theme URI:    https://cdf-sd.org
Description:  Child Development Foundation, child theme. All CDF customizations live here (theme.json, templates, parts, patterns, CSS); the parent receives upstream updates and is never edited. Implements direction B, Beacon (المنارة), per ADR-0011 and ADR-0012.
Template:     twentytwentyfive
Author:       Gearbox Integrated Solutions
Version:      0.10.4
Requires at least: 7.0
Requires PHP: 8.1
License:      GNU General Public License v2 or later
License URI:  https://www.gnu.org/licenses/gpl-2.0.html
Text Domain:  cdf
*/

/* ============================================================================
   Token bridge
   ============================================================================

   theme.json emits --wp--preset--color--primary and --wp--custom--radius--sm.
   The mockups, which are the reference implementation this theme transcribes
   (ADR-0011), say --color-primary and --radius-sm. Rather than rewrite every
   declaration on the way across, and risk a typo in each one, the short names
   are re-declared here as aliases.

   So the rule "components reference tokens only, never raw values" still holds
   exactly: this block is the only place the two vocabularies meet, and it is
   the only place in the theme's CSS where a --wp--* name appears. Everything
   below reads like the mockup because it is the mockup.

   Nothing is invented here. Every alias resolves to a value generated from
   design/tokens.json by tools/build-tokens.mjs. */

:root {
	/* Colour: the nine in the client's picker. */
	--color-base: var(--wp--preset--color--base);
	--color-surface: var(--wp--preset--color--surface);
	--color-contrast: var(--wp--preset--color--contrast);
	--color-muted: var(--wp--preset--color--muted);
	--color-primary: var(--wp--preset--color--primary);
	--color-link: var(--wp--preset--color--link);
	--color-accent: var(--wp--preset--color--accent);
	--color-cta: var(--wp--preset--color--cta);
	--color-spot: var(--wp--preset--color--spot);

	/* Colour: held out of the picker on purpose (charter D2). primary-deep is
	   barred from backgrounds by ADR-0012 decision 2, and the on-* trio are
	   pairing tokens, not choices. */
	--color-primary-deep: var(--wp--custom--color--primary-deep);
	--color-on-brand: var(--wp--custom--color--on-brand);
	--color-on-accent: var(--wp--custom--color--on-accent);
	--color-on-cta: var(--wp--custom--color--on-cta);

	/* The two light fields Beacon is built on. Mixes, not colours. */
	--field-wash: var(--wp--custom--field--wash);
	--field-sky: var(--wp--custom--field--sky);

	/* Type. */
	--font-heading: var(--wp--preset--font-family--heading);
	--font-body: var(--wp--preset--font-family--body);
	--weight-heading: var(--wp--custom--weight--heading);
	--weight-body: var(--wp--custom--weight--body);
	--weight-bold: var(--wp--custom--weight--bold);

	--size-tiny: var(--wp--preset--font-size--tiny);
	--size-small: var(--wp--preset--font-size--small);
	--size-body: var(--wp--preset--font-size--body);
	--size-large: var(--wp--preset--font-size--large);
	--size-lead: var(--wp--preset--font-size--lead);
	/* WordPress kebab-cases preset slugs on the way out, so the slug `h1`
	   is served as `h-1` and `2xs` as `2-xs`. Not a typo, and not ours to
	   normalise: tools/check-preset-refs.mjs holds these to what core emits. */
	--size-h3: var(--wp--preset--font-size--h-3);
	--size-h2: var(--wp--preset--font-size--h-2);
	--size-h1: var(--wp--preset--font-size--h-1);
	--size-hero: var(--wp--preset--font-size--hero);
	--size-stat: var(--wp--preset--font-size--stat);

	--line-body: var(--wp--custom--line--body);
	--line-lead: var(--wp--custom--line--lead);
	--line-heading: var(--wp--custom--line--heading);
	--line-tight: var(--wp--custom--line--tight);

	/* Space and layout. */
	--space-3xs: var(--wp--preset--spacing--3-xs);
	--space-2xs: var(--wp--preset--spacing--2-xs);
	--space-xs: var(--wp--preset--spacing--xs);
	--space-s: var(--wp--preset--spacing--s);
	--space-m: var(--wp--preset--spacing--m);
	--space-l: var(--wp--preset--spacing--l);
	--space-xl: var(--wp--preset--spacing--xl);
	--space-section: var(--wp--preset--spacing--section);

	--layout-gutter: var(--wp--custom--layout--gutter);
	--layout-content: var(--wp--style--global--content-size);
	--layout-wide: var(--wp--style--global--wide-size);

	/* Geometry. Beacon is near-square with heavy rules. */
	--radius-sm: var(--wp--custom--radius--sm);
	--radius-md: var(--wp--custom--radius--md);
	--radius-lg: var(--wp--custom--radius--lg);
	--rule-weight: var(--wp--custom--rule-weight);
	--icon-stroke: var(--wp--custom--icon-stroke);
	--focus-width: var(--wp--custom--focus--width);
	--focus-offset: var(--wp--custom--focus--offset);
	--underline-thin: var(--wp--custom--underline--thin);
	--underline-thick: var(--wp--custom--underline--thick);
	--underline-offset: var(--wp--custom--underline--offset);
	--border-card: var(--wp--custom--border--card);

	/* Motion (ADR-0007) and elevation. */
	--motion-distance: var(--wp--custom--motion--distance);
	--motion-duration-fast: var(--wp--custom--motion--duration-fast);
	--motion-duration-base: var(--wp--custom--motion--duration-base);
	--motion-duration-slow: var(--wp--custom--motion--duration-slow);
	--motion-duration-loop: var(--wp--custom--motion--duration-loop);
	--motion-ease-standard: var(--wp--custom--motion--ease-standard);
	--motion-ease-enter: var(--wp--custom--motion--ease-enter);
	--motion-ease-exit: var(--wp--custom--motion--ease-exit);
	--motion-ease-spring: var(--wp--custom--motion--ease-spring);
	--motion-scale-hover: var(--wp--custom--motion--scale-hover);
	--motion-scale-press: var(--wp--custom--motion--scale-press);
	--shadow-1: var(--wp--custom--shadow--1);
	--shadow-2: var(--wp--custom--shadow--2);
	--shadow-3: var(--wp--custom--shadow--3);
	--lift-1: var(--wp--custom--lift--1);
	--lift-2: var(--wp--custom--lift--2);
	--lift-3: var(--wp--custom--lift--3);
	--raise-1: var(--wp--custom--raise--1);
	--raise-2: var(--wp--custom--raise--2);
	--raise-3: var(--wp--custom--raise--3);

	/* The header's height. theme.js overwrites this with the measured value;
	   this is the one-row header for readers without JavaScript. */
	--header-block-size: calc(52px + 2 * var(--space-s));

	/* The categorical chart scale (tokens.json chart_categorical), used by
	   the projects dashboard only. Swapped for the dark set below. */
	--cat-1: var(--wp--custom--cat--light--1);
	--cat-2: var(--wp--custom--cat--light--2);
	--cat-3: var(--wp--custom--cat--light--3);
	--cat-4: var(--wp--custom--cat--light--4);
	--cat-5: var(--wp--custom--cat--light--5);
	--cat-6: var(--wp--custom--cat--light--6);
	--cat-7: var(--wp--custom--cat--light--7);
	--cat-8: var(--wp--custom--cat--light--8);
	--cat-9: var(--wp--custom--cat--light--9);
	--cat-10: var(--wp--custom--cat--light--10);

	/* RTL-aware sign: flip inline-direction motion without forking on [dir]. */
	--inline: 1;
}

/* ============================================================================
   The dark theme (ADR-0017 decision 1)
   ============================================================================

   Seven values, one selector, and it re-points the PRESET properties rather
   than the --color-* aliases above.

   That distinction is the whole decision and it is not stylistic. theme.json's
   own `styles.color` sets the page ground and ink from
   var(--wp--preset--color--base) and --contrast; twelve more preset references
   sit inside `styles`; and --wp--custom--field--wash/--sky are color-mix() on
   the presets. None of those reads the bridge. A block that re-declared the
   aliases was built and injected into the running site on 2026-08-15 and left
   the page white. This one moved the ground, the ink and the header at once.

   Six slots deliberately do NOT change: primary, primary-deep, accent, cta,
   on-brand and on-cta. ADR-0013 decision 4 changes primary's JOB here (field,
   never ink) and not its colour.

   The selector is html[...] and not :root[...] on purpose: (0,1,1) beats the
   (0,1,0) of the :root block WordPress prints inline in <head>, so this wins
   on specificity and never on source order.

   The attribute is stamped by the boot snippet in functions.php before first
   paint. With JavaScript off there is no attribute and the page is light,
   which is ADR-0013 decision 5's recorded trade. */
html[data-theme='dark'] {
	--wp--preset--color--base: var(--wp--custom--dark--base);
	--wp--preset--color--surface: var(--wp--custom--dark--surface);
	--wp--preset--color--contrast: var(--wp--custom--dark--contrast);
	--wp--preset--color--muted: var(--wp--custom--dark--muted);
	--wp--preset--color--link: var(--wp--custom--dark--link);
	--wp--preset--color--spot: var(--wp--custom--dark--spot);

	/* on-accent is held out of the picker, so it is a custom property and not
	   a preset one. Same idea, different namespace. */
	--wp--custom--color--on-accent: var(--wp--custom--dark--on-accent);

	/* The shadow levels read their ink through var() on this element, so
	   re-pointing the ink moves all three. Elevation in dark is mostly the
	   raise (tokens.json elevation), since a shadow barely shows here. */
	--wp--custom--shadow--ink: var(--wp--custom--shadow--ink-dark);

	/* The chart scale is read only by our own CSS and dashboard.js, never by
	   theme.json's styles, so the alias itself can be swapped here. */
	--cat-1: var(--wp--custom--cat--dark--1);
	--cat-2: var(--wp--custom--cat--dark--2);
	--cat-3: var(--wp--custom--cat--dark--3);
	--cat-4: var(--wp--custom--cat--dark--4);
	--cat-5: var(--wp--custom--cat--dark--5);
	--cat-6: var(--wp--custom--cat--dark--6);
	--cat-7: var(--wp--custom--cat--dark--7);
	--cat-8: var(--wp--custom--cat--dark--8);
	--cat-9: var(--wp--custom--cat--dark--9);
	--cat-10: var(--wp--custom--cat--dark--10);

	/* Form controls, the <select> popup and any scrollbar still on `auto`.
	   No gate covers this; keyboard-pass only fails a user-agent DEFAULT, and
	   the four surfaces below are declared in tokens. check-theme-boot.mjs
	   carries the assertion instead (ADR-0017 decision 5). */
	color-scheme: dark;
}

html:not([data-theme='dark']) {
	color-scheme: light;
}

/* The mockups say `[dir='ltr']`, because their <html> carries an explicit dir
   in both languages. WordPress does not: it emits dir="rtl" for an RTL locale
   and NOTHING at all for an LTR one, so `[dir='ltr']` never matches the root
   of the English site and every inline-direction reveal would animate from the
   wrong edge. Inverted to match the absence instead. */
html:not([dir='rtl']) {
	--inline: -1;
}

/* ============================================================================
   Shared component layer, ported from dist/assets/css/base.css
   ============================================================================

   RTL is the native case. Every inset, margin and padding is a logical
   property, so the English LTR page is the same CSS with no forks and no
   [dir] selectors (hard rule 6). */

/* ---- layout primitives ---- */

.wrap {
	inline-size: min(100% - (var(--layout-gutter) * 2), var(--layout-wide));
	margin-inline: auto;
}

/* ---- focus, and the skip link ----

   WordPress injects its own skip link ahead of the header and styles it with
   `left`, a physical property that puts it off the start edge of an RTL page.
   Restyled here on the logical axis so it lands inside the page in either
   direction, which is what dist/assets/css/base.css does for .skip. */

/* The only :focus-visible in the theme. Change these lines, never add a second
   rule (docs/meetings/2026-08-14-design-token-review.md, F2). */
:focus-visible {
	outline: var(--focus-width) solid var(--color-link);
	outline-offset: var(--focus-offset);
	border-radius: var(--radius-sm);
}

/* The ring is drawn outside the border box, and the browser scrolls the border
   box into view without knowing that. So a link that lands flush against the
   bottom of the viewport gets a ring the reader cannot see, which was true of
   five stops in the tab order on 2026-08-14. `scroll-margin` is the only thing
   that reaches the browser's own scrolling. Verified by
   tools/visual/keyboard-pass.mjs, which measures the ring's box rather than
   the element's. */
:where(a, button, input, textarea, select, summary, iframe, [tabindex], [contenteditable]) {
	scroll-margin-block: calc(var(--focus-width) + var(--focus-offset) + var(--space-3xs));
}

/* ---- the surfaces the browser draws, which a design that ignores them still
   ships, in the user agent's colours rather than ours. These four declarations
   have one home each, here, and the mockups carry the same four in
   dist/assets/css/base.css. Reasoning: docs/CDF_Design_System.md, "The surfaces
   the browser draws". ---- */

/* Selection is the accent slot's one job done literally: an opaque fill with
   dark ink. Opaque matters, because a reader can select text on the footer and
   the impact band as easily as on the page ground, and a tint of the ink would
   vanish on one of them. The accent/on-accent pair is gated in both themes. */
::selection {
	background: var(--color-accent);
	color: var(--color-on-accent);
}

/* The caret and the native control marks follow the interactive blue, so a form
   does not answer in the operating system's accent colour. */
input,
textarea,
select {
	caret-color: var(--color-link);
}

:root {
	accent-color: var(--color-link);
	/* Thumb, then track. The width stays at the user agent's default: a thin
	   scrollbar is a smaller target, and this is a pointer control before it is
	   a decoration. */
	scrollbar-color: color-mix(in srgb, var(--color-primary) 35%, var(--color-base)) transparent;
}

/* The mockup's link underline. It lives here, not in theme.json: WordPress 7.0.4
   does not implement textDecorationThickness, and the parent sets it with
   !important (meeting record, F7). */
a:where(:not(.wp-element-button)) {
	text-decoration-thickness: var(--underline-thin) !important;
	text-underline-offset: var(--underline-offset);
}

a:where(:not(.wp-element-button)):hover {
	text-decoration-thickness: var(--underline-thick) !important;
}

.skip-link:focus {
	/* Order matters and this rule had it backwards until 2026-08-14. A physical
	   property beats a logical one that was declared BEFORE it, so `left: auto`
	   and `right: auto` were cancelling the inset above them: the link landed
	   at its static position, flush with the edge and one pixel past it, with
	   its focus ring clipped. The autos clear the parent's own `left`; the
	   logical inset then has to come last to win. Found by
	   tools/visual/keyboard-pass.mjs, on the first tab stop of every page. */
	left: auto !important;
	right: auto !important;
	inset-block-start: var(--space-2xs) !important;
	inset-inline-start: var(--space-2xs) !important;
	z-index: 100;
	padding: var(--space-2xs) var(--space-s) !important;
	background: var(--color-primary) !important;
	color: var(--color-on-brand) !important;
	font-family: var(--font-body) !important;
	font-size: var(--size-small) !important;
	font-weight: var(--weight-bold);
	border-radius: var(--radius-sm);
	text-decoration: none;
}

/* ---- screen-reader-only text ----
   Not `display: none` and not `visibility: hidden`: both remove the element
   from the accessibility tree, which is the opposite of the point. */

.u-sr-only {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ---- bidi isolation ----
   Any Latin run inside Arabic copy (a phone number, a URL, a version string)
   needs dir="ltr" on the element or the bidi algorithm reorders it. */

[dir='ltr'] {
	unicode-bidi: isolate;
}

/* ---- buttons ---- */

.btn,
.wp-block-button__link {
	display: inline-block;
	padding: 0.85em 1.9em;
	border: 0;
	border-radius: var(--radius-md);
	background: var(--color-primary);
	color: var(--color-on-brand);
	font-family: var(--font-body);
	font-size: var(--size-body);
	font-weight: var(--weight-bold);
	line-height: var(--line-tight);
	text-decoration: none;
	cursor: pointer;
}

/* The state, which everyone gets. The lift and the transition are motion and
   live in assets/css/motion.css, behind prefers-reduced-motion (meeting record,
   F5). Nothing here may declare transform or transition. */
.btn:hover,
.wp-block-button__link:hover {
	background: var(--color-primary-deep);
	box-shadow: var(--shadow-1);
}

/* The give button. This is the ONLY place --color-cta appears anywhere on the
   site: the moment it turns up a second time it stops meaning "give". */

.btn--give,
.is-style-give .wp-block-button__link {
	background: var(--color-cta);
	color: var(--color-on-cta);
}

.btn--give:hover,
.is-style-give .wp-block-button__link:hover {
	background: var(--color-cta);
	filter: brightness(0.9);
}

.btn--ghost,
.is-style-ghost .wp-block-button__link {
	background: transparent;
	color: var(--color-primary);
	box-shadow: inset 0 0 0 2px currentColor;
}

.btn--ghost:hover,
.is-style-ghost .wp-block-button__link:hover {
	background: var(--color-primary);
	color: var(--color-on-brand);
}

/* ============================================================================
   Header
   ============================================================================

   Sticky from the first scroll, with the small elevation token underneath it.
   Requested by the client on 2026-07-28. The shadow is --shadow-1 and not a
   hand-rolled rgba: the header is the one element guaranteed to sit over
   arbitrary content, so if the elevation scale is retuned this moves with it.

   The ground is --field-wash, the 6% mix. It cannot be transparent: a sticky
   bar with no ground shows the page sliding through it. */

.header {
	position: sticky;
	inset-block-start: 0;
	z-index: 20;
	padding-block: var(--space-s);
	box-shadow: var(--shadow-1);
	background: var(--field-wash);
	color: var(--color-contrast);
}

/* A sticky element sticks only inside its parent, and core wraps the header
   part in a div exactly as tall as the header, so until 2026-09-18 the header
   scrolled away with the page. Without the box, .wp-site-blocks is the parent. */
.wp-site-blocks > .wp-block-template-part:has(> .header) {
	display: contents;
}

/* Every jump (an anchor, a skip link, a focused control scrolled into view)
   stops below the header, not under it. */
html {
	scroll-padding-block-start: calc(var(--header-block-size) + var(--space-3xs));
}

.header__inner {
	display: flex;
	align-items: center;
	gap: var(--space-m);
	flex-wrap: wrap;
}

.header__brand {
	display: flex;
	align-items: center;
	gap: var(--space-2xs);
	text-decoration: none;
	color: inherit;
	/* min-inline-size:0 lets the wordmark wrap inside the flex header. Without
	   it a long name (the English one is far longer than the Arabic) refuses to
	   break and pushes the page wider than a 320px screen. */
	min-inline-size: 0;
	flex: 1 1 auto;
}

/* The mark never shrinks; the wordmark beside it does. */
.header__brand .wp-block-site-logo img {
	inline-size: 52px;
	block-size: 52px;
	object-fit: contain;
	flex: none;
}

/* The stacked bilingual wordmark (Samir, 2026-07-31). Arabic on top, English
   below, in both languages. These three rules are the successors of
   .wp-block-site-title and .wp-block-site-tagline, which were the only rules
   giving those two lines their family, size, weight, leading and colour; the
   markup moved, so the styling moved with it in the same change set rather than
   being orphaned and letting the second line revert to body defaults in
   silence (ratification Q4). tools/check-i18n.php check 9 fails if a selector
   for either line goes missing.

   align-items:flex-start is load-bearing. The lines carry opposite dir values,
   so stretched flex items would right-align the Arabic against a left-aligned
   English on the LTR page. Shrink-to-fit makes both start at the header's own
   start edge, which is the right edge in RTL and the left in LTR. */
.header__brand .header__wordmark {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	min-inline-size: 0;
	text-decoration: none;
	color: inherit;
}

.header__brand .header__wordmark__ar {
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	font-size: var(--size-large);
	line-height: var(--line-tight);
	overflow-wrap: break-word;
	color: var(--color-primary);
}

.header__brand .header__wordmark__en {
	font-family: var(--font-body);
	font-size: var(--size-small);
	font-weight: var(--weight-body);
	line-height: var(--line-tight);
	overflow-wrap: break-word;
	color: var(--color-muted);
}

/* ---- navigation ----

   Core Navigation block, per the Component Inventory's route for #1. Its own
   markup, not the mockup's: the mockup's mobile menu button is scaffolding
   ("a non-functional disclosure", inventory #1) and core's overlay is the real
   implementation. What transcribes is the design, not the DOM. */

.header__nav {
	margin-inline-start: auto;
	font-size: var(--size-body);
}

.header__nav .wp-block-navigation__container {
	gap: var(--space-m);
	align-items: center;
}

/* :not() is load-bearing, not tidiness. An unscoped `.header__nav a` rule
   outweighs .btn--give and repaints the give button's label, so the button in
   the header stops matching the same button in the page body. That is a
   different colour pair AND a different WCAG threshold from the same control.
   Found twice in the mockups, both times by eye. Any new rule here that sets a
   colour needs the same exclusion.

   `.wp-block-button__link` is the third time, and it is the one the mockups
   could not have caught: a core button block carries that class and NOT `.btn`,
   so the exclusion ported across intact and stopped excluding anything. The
   header give button rendered #101538 on #CF4600, 3.82:1, while the identical
   button in the page body was white on the same orange at 4.64:1. Found by
   ./cdf.sh contrast against the live URL, which is the whole point of teaching
   it to read one. Any class core might put on a button belongs in this list. */
.header__nav a:not(.btn):not(.wp-block-button__link) {
	color: inherit;
	text-decoration: none;
	padding-block: var(--space-3xs);
}

.header__nav a:not(.btn):not(.wp-block-button__link):hover {
	color: var(--color-primary);
	text-decoration: none;
}

/* The current-page marker was a yellow underline in the pre-2026-07-28 mockup,
   which on this wash is the 1.05:1 problem in its worst form: a 3px line is
   all the mark there is. */
.header__nav .current-menu-item > a,
.header__nav [aria-current='page'] {
	box-shadow: inset 0 calc(-1 * var(--rule-weight)) 0 var(--color-primary);
}

/* The language switcher. Polylang, ADR-0005.

   A badge, not a nav link. `hide_current` means only the other language is ever
   shown, so the control says where you can go, not where you are, and reading
   it as a seventh menu item was the confusion: "English" sat in the Arabic menu
   looking like a page called English. Two characters inside a ring cannot be
   read as a page name. The label comes from wp/mu-plugins/cdf-language-badge.php.

   A badge, not a nav link: `hide_current` means only the other language shows.
   Shape is one stroked SVG path from wp/mu-plugins/cdf-language-badge.php.
   Fixed size and (0,5,1) specificity are both load-bearing.
   CDF_Design_System.md, RN2. */
.header__nav .wp-block-polylang-navigation-language-switcher.lang-item a.wp-block-navigation-item__content {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 2.5rem;
	block-size: 2.375rem;
	/* The tail is the bottom 24% of the canvas, so reserving it as padding is
	   what centres the label in the bubble's body instead of in its box. */
	padding: 0 0 24%;
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
	line-height: var(--line-body);
	color: var(--color-primary);
}

/* Positioned so it paints above the absolute SVG. Without this the bubble is a
   positioned element and the label is not, so the shape covers the text. */
.header__nav .wp-block-polylang-navigation-language-switcher .wp-block-navigation-item__label {
	position: relative;
}

/* Behind the label. `--inline` is the bridge's RTL sign, so the tail follows
   the page's direction; the path is drawn tail-left and never mirrored in the
   markup. CDF_Design_System.md, RN2. */
.header__nav .cdf-lang-badge__bubble {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	transform: scaleX(var(--inline));
	fill: transparent;
	stroke: currentColor;
	stroke-width: var(--icon-stroke);
	stroke-linejoin: round;
	transition: fill var(--motion-duration-fast) var(--motion-ease-standard);
}

/* Hover fills the bubble, so the label must leave `currentColor` with it:
   stroke and fill are the same ink. CDF_Design_System.md, RN2. */
.header__nav .wp-block-polylang-navigation-language-switcher.lang-item a.wp-block-navigation-item__content:hover .cdf-lang-badge__bubble {
	fill: var(--color-primary);
}

.header__nav .wp-block-polylang-navigation-language-switcher.lang-item a.wp-block-navigation-item__content:hover .wp-block-navigation-item__label {
	color: var(--color-on-brand);
}

/* The submenus (News, Reports; cdf_nav_tree()). Core's markup and its
   hover, focus and toggle behaviour; only the panel is ours. In the phone
   overlay core shows them open and indented, and these rules stay off it. */
.header__nav .wp-block-navigation__submenu-icon {
	color: inherit;
}

.header__nav .wp-block-navigation__responsive-container:not(.is-menu-open) .wp-block-navigation__submenu-container {
	min-inline-size: 13rem;
	padding: var(--space-2xs);
	border: 0;
	border-radius: var(--radius-md);
	background: var(--color-base);
	color: var(--color-contrast);
	box-shadow: var(--border-card), var(--shadow-2);
}

.header__nav .wp-block-navigation__responsive-container:not(.is-menu-open) .wp-block-navigation__submenu-container a:not(.btn):not(.wp-block-button__link) {
	padding: var(--space-2xs) var(--space-s);
	border-radius: var(--radius-sm);
	white-space: nowrap;
}

.header__nav .wp-block-navigation__responsive-container:not(.is-menu-open) .wp-block-navigation__submenu-container a:not(.btn):not(.wp-block-button__link):hover {
	background: var(--color-surface);
}

/* In the overlay a child link is a row like its parent, one step in from the
   start edge. Core indents the list 2rem on BOTH sides and then pads each
   child link 0.5em 1em at (0,5,0), which beats its own overlay reset, so the
   children sat 48px in and 16px taller than every other row. Core's block CSS
   prints after this file, so a tie goes to core: hence the `.header` prefix. */
.header .header__nav .wp-block-navigation__responsive-container.is-menu-open .has-child .wp-block-navigation__submenu-container {
	gap: var(--space-xs);
	padding-block: var(--space-xs) 0;
	padding-inline: var(--space-m) 0;
}

.header .header__nav.wp-block-navigation .is-menu-open .has-child .wp-block-navigation__submenu-container > .wp-block-navigation-item {
	margin: 0;
}

.header .header__nav.wp-block-navigation .is-menu-open .has-child .wp-block-navigation__submenu-container > .wp-block-navigation-item > .wp-block-navigation-item__content {
	padding: 0;
}

/* Indentation alone did not read as hierarchy on a phone (Samir, 2026-10-11).
   Core hides its submenu toggle in the overlay because the children are always
   open, so the parent carries a static down chevron and the children sit behind
   a guide rule. Neither adds height (RN3). CDF_Design_System.md, RN6. */
.header .header__nav .is-menu-open .has-child > .wp-block-navigation-item__content::after {
	content: "\ea5f";
	content: "\ea5f" / "";
	display: inline-block;
	margin-inline-start: var(--space-2xs);
	vertical-align: -0.15em;
	font-family: 'CDF Icons';
	font-size: 1.125rem;
	font-weight: normal;
	line-height: 1;
	color: var(--color-primary);
}

.header .header__nav.wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open .has-child .wp-block-navigation__submenu-container {
	margin-inline-start: var(--space-3xs);
	padding-inline-start: var(--space-s);
	border-inline-start: var(--rule-weight) solid var(--color-primary);
}

.header__nav .wp-block-navigation__responsive-container-open,
.header__nav .wp-block-navigation__responsive-container-close {
	color: var(--color-primary);
}

/* The overlay is a light field like every other field on this direction
   (ADR-0012 decision 2), not core's default white.

   The specificity is load-bearing. Core paints the overlay white and its ink
   black at (0,5,0), `.wp-block-navigation:not(.has-background) ...
   .is-menu-open:not(.disable-default-overlay)`, so the old (0,3,0) rule lost
   and the panel was white in both themes. In dark the badge and the close
   button kept their light ink on that white: invisible (Maab, 2026-09-19).
   keyboard-pass.mjs now measures the ink of every control in the open menu
   against the ground it actually sits on. CDF_Design_System.md, RN3. */
.header .header__nav.wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) {
	background: var(--field-wash);
	color: var(--color-contrast);
	padding: var(--space-l) var(--layout-gutter);
}

/* The overlay has to FIT a phone, not merely scroll on one: at 390x560 the
   give button used to open cut in half. The top padding is a clearance for the
   absolutely positioned close button, not a rhythm. CDF_Design_System.md, RN3. */
.header__nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-container-content {
	padding-block-start: var(--space-l);
}

.header__nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	gap: var(--space-xs);
}

/* A phone menu is a scroll container over the page. Without this, flicking past
   its end scrolls the document underneath it.

   The row is justified "right", and core aligns the overlay's items with the
   same setting as flex-end: the left edge in Arabic. The overlay reads start
   in both languages. The list carries the nav's own classes, so core sets the
   variable again on it. keyboard-pass.mjs fails if an item leaves the start
   edge. */
.header__nav .wp-block-navigation__responsive-container.is-menu-open {
	overscroll-behavior: contain;
}

.header__nav .wp-block-navigation__responsive-container.is-menu-open,
.header__nav .is-menu-open .wp-block-navigation__container {
	--navigation-layout-justification-setting: flex-start;
}

/* ---- the nav breakpoint ----

   Core collapses the navigation to its overlay below 600px. That is far too
   low here. The Arabic row is seven items plus a language switcher plus the
   give button, and it needs about 1132px; `.wrap` only reaches that at roughly
   1191px. Left at 600px, everything between a small tablet and a laptop
   renders a two-line header, which is the state the mockups already went
   through and rejected: dist/assets/css/base.css moved the same breakpoint
   from 60rem to 78rem on 2026-07-30 for exactly this reason.

   So the band from core's 600px up to 78rem is handed back to the overlay.
   Both halves are needed, because core turns the button off and the row on in
   the same media query. `:not(.is-menu-open)` matters: without it, opening the
   menu inside this band hides the panel it just opened. */

/* One row on a phone. The theme switcher (2026-08-15) made the row 19px too
   wide at 360px, so it wrapped to a second row and the header took a quarter
   of a 360x640 screen. That was a cost at the top of the page until the header
   stayed on screen (2026-09-18). Here the English name wraps inside the brand
   instead. Measured 2026-09-18: 94px at 360px (was 153px); at 320px both
   names wrap and the header is 133px (was 153px). */
@media (max-width: 26rem) {
	.header__inner {
		flex-wrap: nowrap;
		gap: var(--space-xs);
	}
}

@media (min-width: 600px) and (max-width: 78rem) {
	.header__nav .wp-block-navigation__responsive-container-open:not(.always-shown) {
		display: flex;
	}

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

/* ============================================================================
   Footer
   ============================================================================

   The second of Beacon's two solid fields, and since 2026-08-14 the deeper of
   them. It was --color-primary from the 2026-07-28 client review until Samir
   confirmed the footer as a named --color-primary-deep surface, which is item 2
   of the list in ADR-0012 decision 2. The point is a closing anchor the page
   does not confuse with the impact band under the hero.

   Deepening it answers the objection that produced the original bar rather than
   reopening it: the older staff at that review found dark fields hard to read,
   and every ratio here goes up. White ink 10.23:1 -> 16.09:1, the accent column
   titles 7.64:1 -> 12.02:1. The titles stay accent, which is legal on this
   field for the same reason it is legal on the impact band and nowhere else on
   the page.

   What is NOT settled, and is Samir's to close with the client: .footer__intro
   is a 34-word sentence, and ADR-0012 decision 2 says this colour never sits
   behind running prose. He named the footer anyway, so it is built; the tension
   is recorded here rather than resolved by a session. */

.footer {
	background: var(--color-primary-deep);
	color: var(--color-on-brand);
	padding-block: var(--space-xl) var(--space-l);
}

.footer a {
	color: var(--color-on-brand);
	text-decoration: none;
	opacity: 0.85;
}

.footer a:hover {
	opacity: 1;
	text-decoration: underline;
}

.footer__grid {
	display: grid;
	gap: var(--space-l);
	grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

/* The chip restores the light ground the mark was drawn for; on the navy
   footer its wordmark is invisible without it. CDF_Design_System.md, RN4. */
/* The mark anchors the closing line, not the first column, where it broke the
   row the four yellow titles share. CDF_Design_System.md, RN4. */
.footer__logo {
	margin: 0;
	line-height: 0;
}

.footer__logo img {
	display: block;
	background: var(--color-base);
	border-radius: 50%;
	padding: var(--space-3xs);
}

.footer__title {
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	font-size: var(--size-body);
	margin-block: 0 var(--space-2xs);
	color: var(--color-accent);
}

.footer__intro {
	margin-block: 0;
	opacity: 0.85;
}

.footer__links {
	list-style: none;
	padding: 0;
	margin: 0;
	display: grid;
	gap: var(--space-2xs);
}

/* "View all partners", under the six (Malk, 2026-10-13). */
.footer__more {
	margin-block: var(--space-xs) 0;
	font-weight: var(--weight-heading);
}

.footer__bottom {
	margin-block-start: var(--space-l);
	padding-block-start: var(--space-m);
	/* The footer sets color: var(--color-on-brand) on itself, so currentColor
	   is that token. Mixing it down to 18% keeps the hairline correct if the
	   ink ever stops being pure white, which a raw rgb() could not. The 1px is
	   deliberate and not --rule-weight: this is a divider inside a panel, not
	   one of the direction's structural rules, and 3px would read as a stripe
	   across the footer. */
	border-block-start: 1px solid color-mix(in srgb, currentColor 18%, transparent);
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2xs) var(--space-m);
	/* Centred, not `space-between`, which threw the two languages to opposite
	   ends of a 1200px rule. CDF_Design_System.md, RN4. */
	justify-content: center;
	text-align: center;
	font-size: var(--size-small);
	opacity: 0.8;
}

/* ============================================================================
   Layout primitives and typography, ported from base.css
   ============================================================================ */

h1,
h2,
h3,
h4 {
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	line-height: var(--line-heading);
}

/* Tracking is a Latin-only adjustment. Arabic is a connected script: letters
   join along a baseline, and moving them apart or together fights the joins
   instead of tuning the fit. Scoped to :lang(en) so the derived English site
   gets the tighter display fit and the Arabic primary never does. */
h1:lang(en),
h2:lang(en),
h3:lang(en),
h4:lang(en) {
	letter-spacing: -0.01em;
}

.prose {
	inline-size: min(100%, var(--layout-content));
}

/* Every band contains its own horizontal axis. The reveal animations park a
   block 8px outside its column until it scrolls into view, and on a narrow
   column that overshoot lands past the edge of the page and makes the document
   wider than the screen. In RTL that is not cosmetic: Chrome on Android opens
   an over-wide RTL page at the far end of the overflow, so the reader is handed
   a homepage with the logo, the headline and both buttons sliced off the side.
   (iOS Safari holds the start edge, which is why the same page looked correct
   on an iPhone and in a desktop window.)

   clip, not hidden, because clip leaves the vertical axis visible: the yellow
   anchor blocks hang below their photograph and must not be cut. */
.section,
.band--tint,
.band--sky,
.band--figures {
	overflow-x: clip;
}

/* Core's blockGap lands on the containers that stack the page, putting body
   colour between full-bleed bands. Specificity is counted, not guessed.
   CDF_Design_System.md, RN1. */
:root :is(.wp-site-blocks, main.is-layout-flow) > * {
	margin-block-start: 0;
}

/* Same collision inside anything a theme class turns into a grid or flex box:
   these already space their children with `gap`. Keep the list in step with
   the flex and grid primitives. CDF_Design_System.md, RN1. */
:root :is(
	.grid, .stats, .areas, .involve, .footer__grid, .footer__links,
	.hero__inner, .hero__actions, .header__inner, .section__head--split,
	.card, .card__body, .area, .area__body, .area__figures,
	.story, .story__body, .give .entry-content, .give-route,
	.post__head, .post-nav, .about-profile, .about-profile__copy, .about-statements, .about-values, .about-statement, .about-objectives,
	.about-objectives-band, .about-values-band,
	.programs-intro, .programs-index, .program-sector, .program-sector__intro, .program-areas,
	.home-about
) > * {
	margin-block-start: 0;
}

/* Justified text (#98): running body text, only where its own column is at
   least 36em wide, so never on a portrait phone. Each wrapper is the box the
   query measures. A paragraph an editor aligned by hand keeps its alignment.
   The leads of the About profile and the Programs intro are the one named
   exception. Measurements and the gate: CDF_Design_System.md, RN7. */
.prose, .stories .entry-content, .story__body, .about-profile__copy, .programs-intro,
.program-sector__intro {
	container-type: inline-size;
}

@container (min-width: 36em) {
	:is(.prose .entry-content, .stories .entry-content, .story__body, .about-profile__copy, .programs-intro, .program-sector__intro)
		> p:not(.lead, .story__meta, [class*='has-text-align-']),
	:is(.about-profile__copy, .programs-intro) > p.lead:not([class*='has-text-align-']) {
		text-align: justify;
	}

	:is(.prose .entry-content, .stories .entry-content, .story__body, .about-profile__copy, .programs-intro, .program-sector__intro)
		> p:lang(en) {
		hyphens: auto;
	}
}

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

.section--tight {
	padding-block: var(--space-xl);
}

.stack > * + * {
	margin-block-start: var(--space-m);
}

.stack-s > * + * {
	margin-block-start: var(--space-2xs);
}

/* Band: a full-width horizontal field. Beacon lights all of them except two
   (ADR-0012 decision 2): the impact band and the footer. */
.band--tint {
	background: var(--color-surface);
}

.band--sky {
	background: var(--field-sky);
}

.lead {
	font-size: var(--size-lead);
	line-height: var(--line-lead);
}

.muted {
	color: var(--color-muted);
}

/* Underline that sweeps in from the inline start. Direction-aware without a
   fork: the LTR case only moves the origin. */
.link-sweep {
	background-image: linear-gradient(currentColor, currentColor);
	background-repeat: no-repeat;
	background-size: 0% var(--underline-thick);
	background-position: right 100%;
	text-decoration: none;
	transition: background-size var(--motion-duration-base) var(--motion-ease-standard);
}

html:not([dir='rtl']) .link-sweep {
	background-position: left 100%;
}

.link-sweep:hover {
	background-size: 100% var(--underline-thick);
}

/* ---- section headings: Beacon's heavy rule, above the title ---- */

.section__head {
	margin-block-end: var(--space-l);
	max-inline-size: 48rem;
}

/* The rule was yellow until 2026-07-28 and is now primary, because yellow on a
   light band is 1.05:1: not a quiet accent, an absent one. */
.section__head::before {
	content: '';
	display: block;
	inline-size: 3.5rem;
	block-size: var(--rule-weight);
	margin-block-end: var(--space-s);
	background: var(--color-primary);
}

.section__head h2 {
	color: var(--color-primary);
}

.section__head p {
	margin-block-start: var(--space-s);
	color: var(--color-muted);
}

/* Title on one side, the standfirst and its call to action on the other.

   Two things here are load-bearing and both were bugs first. The selector is
   doubled to out-specify `.section__head { max-inline-size: 48rem }` above,
   which would otherwise hold the head at 48rem while the cards below it run
   the full width. And `::before` is a generated GRID ITEM once the head
   becomes a grid: left alone it takes the first cell and pushes the title into
   the wrong column, so it gets its own full-width row. */
.section__head.section__head--split {
	max-inline-size: none;
	display: grid;
	align-items: start;
	row-gap: var(--space-s);
	column-gap: var(--space-m);
}

.section__head--split::before {
	grid-column: 1 / -1;
	margin-block-end: 0;
}

@media (min-width: 52rem) {
	.section__head.section__head--split {
		grid-template-columns: minmax(0, 1fr) minmax(0, 27rem);
		column-gap: var(--space-xl);
		align-items: end;
	}
}

.section__head--split h2 {
	max-inline-size: 16ch;
}

.section__head-aside p {
	margin-block-start: 0;
}

.section__head-aside .btn {
	margin-block-start: var(--space-m);
}

/* ============================================================================
   Homepage sections
   ============================================================================ */

/* ---- hero: a pale blue field, photograph inset beside the type ----
   It was a solid deep-blue field until 2026-07-28. */

.hero {
	background: var(--field-sky);
	color: var(--color-contrast);
	padding-block: var(--space-xl) var(--space-section);
	overflow: hidden;
}

.hero__inner {
	display: grid;
	gap: var(--space-xl);
	align-items: center;
	grid-template-columns: 1fr;
}

@media (min-width: 56rem) {
	.hero__inner {
		grid-template-columns: 1.05fr 0.95fr;
	}
}

.hero__title {
	font-size: var(--size-hero);
	color: var(--color-primary);
}

/* The headline device: a heavy rule set hard against the type. This is the
   element that makes the direction recognisable, so it repeats at every
   section heading rather than appearing once. */
.hero__title::after {
	content: '';
	display: block;
	inline-size: 7rem;
	block-size: var(--rule-weight);
	margin-block-start: var(--space-m);
	background: var(--color-primary);
}

.hero__sub {
	margin-block-start: var(--space-m);
	color: var(--color-muted);
	max-inline-size: 38ch;
}

.hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-s);
	margin-block-start: var(--space-l);
}

/* The ghost button inverts on dark fields. Beacon's hero is not one any more,
   and white on a pale wash is the button vanishing entirely, so it inverts
   back. */
.hero .btn--ghost,
.hero .is-style-ghost .wp-block-button__link {
	color: var(--color-primary);
}

.hero .btn--ghost:hover,
.hero .is-style-ghost .wp-block-button__link:hover {
	background: var(--color-primary);
	color: var(--color-on-brand);
}

/* The photograph is a panel, deliberately not full bleed: it is supporting
   evidence rather than the emotional load, which is what lets this direction
   work with the documentary group shots CDF actually has.

   `isolation: isolate` is load-bearing and is the one thing this port adds to
   the mockup's rule. The yellow anchor below is a z-index:-1 pseudo-element,
   and a negative-z-index child paints into the nearest ancestor STACKING
   CONTEXT, not the nearest positioned ancestor. Without an isolate here the
   anchor lands behind .hero's own background and is invisible. The mockups get
   away without it because their hero markup is one div shallower; WordPress
   wraps the image in a block group, which changes what paints where. */
.hero__media {
	position: relative;
	isolation: isolate;
}

.hero__media img {
	inline-size: 100%;
	border-radius: var(--radius-lg);
}

/* ---- the yellow anchor ----
   Yellow's one job on a light ground, and the whole of it. A 6rem block set
   behind a large panel, offset so an L of it shows, carrying no text and with
   no text near it. Its luminance contrast against the wash is 1.05:1 and that
   is fine precisely because there is nothing to read: what separates it is
   chroma, not brightness.

   Two instances, bookending the page: the hero photograph and the
   get-involved photograph. Do not add a third, do not put text or an icon on
   one, and do not place one where a give button can sit beside it, because
   brand yellow against --color-cta is 2.26:1 and the two shapes merge. */
.hero__media::after,
.involve__media::after {
	content: '';
	position: absolute;
	inline-size: 6rem;
	block-size: 6rem;
	background: var(--color-accent);
	z-index: 0;
}

/* The panel is lifted above the anchor explicitly (figure z-index 1, anchor 0)
   rather than the anchor being pushed below it with the mockups' z-index:-1.
   Both work. This one is stated in two positive layers inside a stacking
   context this file creates, so it does not depend on how a negative-z-index
   pseudo-element interacts with the extra <figure> WordPress wraps the image
   in. Verified by sampling three pixels: behind the photo, past the inline
   edge, and below it. The anchor must read as an L, never as a full square. */
.hero__media .wp-block-image,
.involve__media .wp-block-image {
	position: relative;
	z-index: 1;
	margin: 0;
}

.hero__media::after {
	inset-block-end: calc(var(--space-m) * -1);
	inset-inline-start: calc(var(--space-m) * -1);
}

/* ---- impact band: one of the two fields that stays solid ----
   Client request 2026-07-28, amended 2026-07-30 one step darker: the band
   takes --color-primary-deep and keeps the accent numerals.

   This is the ONE background primary-deep is allowed. ADR-0012 barred it from
   all of them; the 2026-07-30 amendment narrows the bar rather than lifting
   it, on the ground that this band carries six words and no running prose.
   Everything the 2026-07-28 meeting said about solid dark fields being hard
   for the older staff to read still holds for fields that carry paragraphs.
   Adding a second primary-deep background is a change of position, not a
   change of styling. */

.band--figures {
	background: var(--color-primary-deep);
	color: var(--color-on-brand);
	padding-block: var(--space-xl);
}

.stats {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
	gap: var(--space-l) var(--space-m);
	text-align: center;
}

/* An icon above each figure, client request 2026-07-28. Decorative in the
   strict sense: every stat already says in words what it counts, so the glyphs
   carry aria-hidden and a screen reader loses nothing. Sized off the numeral
   rather than a fixed px so it keeps proportion as the fluid scale grows. */
.stat__icon {
	display: block;
	margin-block-end: var(--space-2xs);
	inline-size: 2.25rem;
	block-size: 2.25rem;
	margin-inline: auto;
}

.stat__icon svg {
	inline-size: 100%;
	block-size: 100%;
	fill: none;
	stroke: currentColor;
	stroke-width: var(--icon-stroke);
	stroke-linecap: round;
	stroke-linejoin: round;
}

.stat__num {
	display: block;
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	font-size: var(--size-stat);
	line-height: 1.05;
	/* Tabular figures stop the count-up animation reflowing the row on every
	   frame as digit widths change. */
	font-variant-numeric: tabular-nums;
}

.stat__icon,
.stat__num {
	color: var(--color-accent);
}

.stat__label {
	display: block;
	margin-block-start: var(--space-3xs);
	font-size: var(--size-small);
	line-height: 1.4;
	color: var(--color-on-brand);
	opacity: 0.88;
}

/* ---- get-involved ---- */

.involve {
	display: grid;
	gap: var(--space-xl);
	align-items: center;
	grid-template-columns: 1fr;
}

@media (min-width: 52rem) {
	.involve {
		grid-template-columns: 1fr 1fr;
	}
}

.involve__media {
	position: relative;
}

.involve__media img {
	inline-size: 100%;
	border-radius: var(--radius-lg);
}

/* Same isolate as .hero__media, same reason. */
.involve__media {
	isolation: isolate;
}

/* On the OUTER edge, not the one facing the copy: the give button lives in the
   other half of this section, and yellow beside --color-cta is 2.26:1. */
.involve__media::after {
	inset-block-end: calc(var(--space-m) * -1);
	inset-inline-end: 0;
}

/* The sideways overhang only exists in the two-column layout, where the media
   has half a section beside it to hang into. In one column the photograph runs
   the full width of the wrap, so --space-m of overhang lands --space-m past the
   edge of the page and the document gets 24px wider than the screen. */
@media (min-width: 52rem) {
	.involve__media::after {
		inset-inline-end: calc(var(--space-m) * -1);
	}
}

/* ---- the give button's Beacon hover ----
   Fill and label come from the shared layer; only the inversion is Beacon's.
   `filter: none` cancels the shared brightness(0.9), which existed to darken an
   orange that is no longer orange on hover.

   The accent ring is not decoration, it is the button's edge. primary-deep
   carries the accent label at 12.02:1, the best pairing on the site, but a
   control also owes 3:1 against what surrounds it, and on a dark ground
   primary-deep is 1.11:1. Without the ring the hovered button would keep its
   label and lose its shape: yellow words floating with no button under them. */
.btn--give:hover,
.is-style-give .wp-block-button__link:hover {
	background: var(--color-primary-deep);
	color: var(--color-accent);
	/* --shadow-1 is repeated rather than inherited: this replaces the shared
	   .btn:hover declaration, and dropping it takes the lift off the one button
	   that most needs to feel pressable. */
	box-shadow: inset 0 0 0 2px var(--color-accent), var(--shadow-1);
	filter: none;
}

/* ============================================================================
   Cards, chips and programme areas
   ============================================================================ */

.grid {
	display: grid;
	gap: var(--space-m);
	grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
}

/* A post list keeps its empty columns (auto-fill), so one or two posts stay
   at the card width of a full row instead of stretching across it (Samir,
   2026-10-14: a lone story filled the screen and its photograph with it). */
.wp-block-post-template.grid {
	grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
}

/* In a query loop each card sits in a list item. The item stretches to the
   row; the card must fill it, or cards in one row end at different heights. */
.grid > li {
	display: flex;
}

.grid > li > .card {
	flex: 1;
}

.grid--2 {
	grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

.card {
	display: flex;
	flex-direction: column;
	background: var(--color-base);
	border-radius: var(--radius-md);
	overflow: hidden;
	position: relative;
	box-shadow: var(--border-card);
	transition:
		transform var(--motion-duration-base) var(--motion-ease-standard),
		box-shadow var(--motion-duration-base) var(--motion-ease-standard),
		background-color var(--motion-duration-base) var(--motion-ease-standard);
}

.card:hover {
	transform: translateY(calc(-1 * var(--lift-2)));
	box-shadow: var(--shadow-2);
}

/* Beacon's card signature: a heavy rule that wipes in along the bottom edge. */
.card::after {
	content: '';
	position: absolute;
	inset-block-end: 0;
	inset-inline: 0;
	block-size: var(--rule-weight);
	background: var(--color-primary);
	transform: scaleX(0);
	transform-origin: inline-start;
	transition: transform var(--motion-duration-base) var(--motion-ease-standard);
}

.card:hover::after {
	transform: scaleX(1);
}

.card__media {
	position: relative;
	aspect-ratio: 4 / 3;
	overflow: hidden;
	background: var(--color-surface);
}

/* Positioned rather than sized at block-size:100%. A percentage block size has
   nothing to resolve against when the parent's height comes from aspect-ratio,
   so it falls back to auto and every photograph sits short of its frame with a
   strip of surface grey underneath. Absolute inset gives the img a definite
   box. The same applies to .area__media img below. */
.card__media img {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	transition: transform var(--motion-duration-slow) var(--motion-ease-standard);
}

.card:hover .card__media img {
	transform: scale(var(--motion-scale-hover));
}

.card__body {
	flex: 1;
	padding: var(--space-m);
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
}

.card__title {
	font-size: var(--size-h3);
	margin: 0;
}

/* News cards are one size (Samir, 2026-09-16): the title takes exactly three
   lines, and a longer one ends in an ellipsis. The full title stays in the
   markup for screen readers and in the link it opens. */
.wp-block-post-template .card__title {
	min-block-size: 3lh;
}

.wp-block-post-template .card__title a {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	line-clamp: 3;
	overflow: hidden;
}

/* The category chip stays on one line. */
.wp-block-post-template .chip--news {
	max-inline-size: 100%;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* The news page leads with one featured article (Samir, 2026-09-17): the
   first card on page 1 spans the row, photograph beside the text on wider
   screens. A sticky post comes first, so it is the one featured. A list of
   one, which a filter can give, is not featured: the card keeps its grid
   width (Samir, 2026-10-14). */
.blog:not(.paged) .news-grid > li:first-child:not(:only-child) {
	grid-column: 1 / -1;
}

.blog:not(.paged) .news-grid > li:first-child:not(:only-child) .card__title {
	font-size: var(--size-h2);
}

@media (min-width: 48rem) {
	.blog:not(.paged) .news-grid > li:first-child:not(:only-child) > .card {
		flex-direction: row;
	}

	.blog:not(.paged) .news-grid > li:first-child:not(:only-child) .card__media {
		flex: 3 1 0;
		aspect-ratio: auto;
		min-block-size: 20rem;
	}

	.blog:not(.paged) .news-grid > li:first-child:not(:only-child) .card__body {
		flex: 2 1 0;
		justify-content: center;
		padding: var(--space-l);
	}
}

/* A single post's head (Samir, 2026-10-13): from 56rem the image and the
   title share one row, image 60 and text 40, the image on the start side; the
   date sits under the row. The source order stays title first, so a phone
   reads title, image, date. A post with no image keeps the title alone.
   CDF_Design_System.md, RN8. */
.post__head {
	display: grid;
	gap: var(--space-l) var(--space-xl);
	grid-template-columns: minmax(0, 1fr);
}

.post__head > .section__head {
	margin-block-end: 0;
}

.post__date {
	margin-block-start: var(--space-s);
}

@media (min-width: 56rem) {
	.post__head:has(> .post__media) {
		grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
		align-items: center;
	}

	.post__head > .post__media {
		grid-column: 1;
		grid-row: 1;
	}

	/* One frame for every post, so a portrait photograph does not make the
	   row taller than the screen. The crop keeps the top of the photograph,
	   where the heads are (Samir, 2026-10-13). */
	.post__head > .post__media img {
		aspect-ratio: 4 / 3;
		object-fit: cover;
		object-position: 50% 0;
	}

	.post__head > .section__head {
		grid-column: 2;
		grid-row: 1;
	}
}

/* A post with no featured image: the newspaper icon on the media box. */
.card__media--empty {
	display: grid;
	place-items: center;
	margin: 0;
}

.card__media--empty::before {
	content: "\eafd";
	content: "\eafd" / "";
	color: var(--color-primary);
	font-family: 'CDF Icons';
	font-size: 4rem;
	line-height: 1;
	opacity: 0.55;
}

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

.card__meta {
	font-size: var(--size-small);
	color: var(--color-muted);
}

.chip {
	display: inline-block;
	align-self: flex-start;
	padding: 0.25em 0.85em;
	border-radius: var(--radius-sm);
	background: var(--color-spot);
	color: var(--color-on-brand);
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
	line-height: 1.6;
	text-decoration: none;
}

.chip--news {
	background: var(--color-primary);
}

/* The chip is a fill carrying reversed ink. Core renders the category as a
   LINK inside it, which picks up the global link colour and puts --color-link
   on --color-primary: 1.3:1, the term unreadable on its own chip. The chip owns
   its ink, so the link inherits it. */
.chip a {
	color: inherit;
	text-decoration: none;
}

.chip a:hover {
	text-decoration: underline;
}

/* ---- news filter and search (inventory #24, #26) ---- */

/* The type tabs, one row of search, sector and state, and the filters that
   are open. Links and a GET form, no JS (patterns/news-filter.php). Design
   and the options it beat: docs/meetings/2026-10-14-news-filter.md.

   The head's 48rem measure is for the title and the lead. The filter takes
   the full width, so the search and both selects fit one line on a desktop.
   Without :has() it keeps the measure and wraps, which still works. */
.section__head:has(> .news-filter) {
	max-inline-size: none;
}

.section__head:has(> .news-filter) > :not(.news-filter) {
	max-inline-size: 48rem;
}

.news-filter {
	display: grid;
	gap: var(--space-s);
	margin-block-start: var(--space-m);
}

/* The tabs: text, not chips, so a category reads as a place to go and the
   chip on a card stays a label. The open one carries the header's underline
   and aria-current. 44px tall, like the pagination tiles. */
.news-tabs {
	display: flex;
	flex-wrap: wrap;
	column-gap: var(--space-m);
	margin: 0;
	padding: 0;
	list-style: none;
	box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--color-primary) 20%, transparent);
}

.news-tabs li {
	margin: 0;
}

.news-tabs__link {
	display: flex;
	align-items: center;
	min-block-size: 2.75rem;
	color: var(--color-muted);
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
	text-decoration: none;
}

.news-tabs__link:hover {
	color: var(--color-primary);
}

.news-tabs__link[aria-current] {
	color: var(--color-contrast);
	box-shadow: inset 0 calc(-1 * var(--rule-weight)) 0 var(--color-primary);
}

/* One form. A phone stacks it: the search, then the sector (its longest
   name needs the width), then the state beside the button. */
.news-refine {
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	grid-template-areas:
		'search search'
		'sector sector'
		'state submit';
	gap: var(--space-2xs);
}

@media (min-width: 40rem) {
	.news-refine {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
		grid-template-areas:
			'search search search'
			'sector state submit';
	}
}

@media (min-width: 64rem) {
	.news-refine {
		grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) auto;
		grid-template-areas: 'search sector state submit';
	}
}

.news-refine__search {
	grid-area: search;
	max-inline-size: none;
}

.news-refine__select--cdf_sector {
	grid-area: sector;
}

.news-refine__select--cdf_state {
	grid-area: state;
}

.news-refine__submit {
	grid-area: submit;
}

/* The select is the search field's box with the theme's chevron, so the row
   reads as one control set. */
.news-refine__select {
	position: relative;
}

.news-refine__select::after {
	content: "\ea5f";
	content: "\ea5f" / "";
	position: absolute;
	inset-block: 0;
	inset-inline-end: var(--space-s);
	display: grid;
	place-items: center;
	color: var(--color-muted);
	font-family: 'CDF Icons';
	font-size: 1.25rem;
	pointer-events: none;
}

.news-refine__select select {
	box-sizing: border-box;
	inline-size: 100%;
	padding: 0.7em var(--space-s);
	padding-inline-end: calc(var(--space-s) * 2 + 1.25rem);
	border: 1px solid color-mix(in srgb, var(--color-primary) 30%, transparent);
	border-radius: var(--radius-md);
	background: var(--color-base);
	color: var(--color-contrast);
	font: inherit;
	text-overflow: ellipsis;
	appearance: none;
	cursor: pointer;
}

/* The filters that are open, each a link that removes it. */
.news-open {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2xs) var(--space-s);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--size-small);
}

.news-open li {
	margin: 0;
}

.news-open__item {
	display: inline-flex;
	align-items: center;
	gap: var(--space-xs);
	min-block-size: 2.75rem;
	padding-inline: var(--space-s) var(--space-xs);
	border-radius: var(--radius-sm);
	background: var(--color-surface);
	color: var(--color-contrast);
	font-weight: var(--weight-bold);
	text-decoration: none;
}

.news-open__item::after {
	content: "\eb55";
	content: "\eb55" / "";
	color: var(--color-muted);
	font-family: 'CDF Icons';
	font-size: 1.15em;
	font-weight: normal;
}

.news-open__item:hover::after {
	color: var(--color-primary);
}

/* ---- news and archive search (inventory #26) ---- */

/* The Reports field (.reports-search) with a submit button, because this
   one sends a GET request rather than filtering in place. Detail:
   docs/CDF_Search.md. */
.listing-search {
	display: flex;
	gap: var(--space-2xs);
	max-inline-size: 36rem;
	margin-block-start: var(--space-s);
}

.listing-search__field {
	flex: 1;
	min-inline-size: 0;
}

.listing-search__submit {
	flex: none;
	padding-inline: 1.4em;
}

.search-count {
	margin-block: var(--space-xs) 0;
	color: var(--color-muted);
	font-size: var(--size-small);
}

/* ---- pagination (inventory #23) ---- */

/* Every Query Loop's pagination, so a new listing gets it with no class to
   remember. The page numbers are the filter chip as a 44px tile; previous and
   next hold the ends of a three-column grid, so the numbers stay centred on
   the first and last page, where core leaves one end out. On a phone the
   numbers take the first row. Detail: docs/CDF_Design_System.md, "Pagination". */
.wp-block-query-pagination.wp-block-query-pagination {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: var(--space-s);
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
}

.wp-block-query-pagination-numbers {
	grid-column: 2;
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--space-3xs);
}

.wp-block-query-pagination-previous {
	grid-column: 1;
	justify-self: start;
}

.wp-block-query-pagination-next {
	grid-column: 3;
	justify-self: end;
}

.wp-block-query-pagination .page-numbers,
.wp-block-query-pagination-previous,
.wp-block-query-pagination-next,
.post-nav a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	min-block-size: 2.75rem;
	border-radius: var(--radius-sm);
	line-height: 1;
	font-variant-numeric: tabular-nums;
	text-decoration: none;
}

.wp-block-query-pagination .page-numbers {
	min-inline-size: 2.75rem;
	padding-inline: var(--space-2xs);
}

.wp-block-query-pagination-previous,
.wp-block-query-pagination-next,
.post-nav a {
	gap: var(--space-2xs);
	padding-inline: var(--space-s);
}

.wp-block-query-pagination a.page-numbers,
.wp-block-query-pagination-previous,
.wp-block-query-pagination-next,
.post-nav a {
	background: var(--color-surface);
	color: var(--color-primary);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 30%, transparent);
	transition: background-color var(--motion-duration-base) var(--motion-ease-standard),
		color var(--motion-duration-base) var(--motion-ease-standard);
}

.wp-block-query-pagination a.page-numbers:hover,
.wp-block-query-pagination-previous:hover,
.wp-block-query-pagination-next:hover,
.post-nav a:hover,
.wp-block-query-pagination .page-numbers.current {
	background: var(--color-primary);
	color: var(--color-on-brand);
}

/* Not a link: aria-current names it, the fill shows it. */
.wp-block-query-pagination .page-numbers.dots {
	min-inline-size: 1.5rem;
	padding-inline: 0;
	color: var(--color-muted);
}

/* Chevrons from the icon font. --inline is 1 in RTL, so the glyphs are
   the Arabic ones and English flips them, as on .branches__step. */
.wp-block-query-pagination-previous::before,
.wp-block-query-pagination-next::after,
.post-navigation-link-previous a::before,
.post-navigation-link-next a::after {
	display: inline-block;
	font-family: 'CDF Icons';
	font-size: 1.25rem;
	font-weight: normal;
	line-height: 1;
	transform: scaleX(var(--inline));
}

.wp-block-query-pagination-previous::before, .post-navigation-link-previous a::before { content: "\ea61"; content: "\ea61" / ""; }
.wp-block-query-pagination-next::after, .post-navigation-link-next a::after { content: "\ea60"; content: "\ea60" / ""; }

/* An article's previous and next links take the same tiles, at the two ends
   of one row (Samir, 2026-10-13). On the newest or oldest post core renders
   an empty box for the missing link, so each end keeps its side. */
.post-nav {
	display: grid;
	grid-template-columns: 1fr 1fr;
	align-items: center;
	gap: var(--space-s);
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
}

.post-navigation-link-previous {
	grid-column: 1;
	justify-self: start;
}

.post-navigation-link-next {
	grid-column: 2;
	justify-self: end;
}

@media (max-width: 40rem) {
	.wp-block-query-pagination.wp-block-query-pagination {
		grid-template-columns: 1fr 1fr;
		row-gap: var(--space-xs);
	}

	.wp-block-query-pagination-numbers {
		grid-column: 1 / -1;
		grid-row: 1;
	}

	.wp-block-query-pagination-previous {
		grid-column: 1;
		grid-row: 2;
	}

	.wp-block-query-pagination-next {
		grid-column: 2;
		grid-row: 2;
	}
}

/* ---- programme areas ---- */

.areas {
	display: grid;
	gap: var(--space-l);
	grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

.area {
	display: flex;
	flex-direction: column;
	background: var(--color-base);
	border-radius: var(--radius-md);
	border-block-end: var(--rule-weight) solid var(--color-primary);
	box-shadow: var(--border-card);
	transition:
		transform var(--motion-duration-base) var(--motion-ease-standard),
		box-shadow var(--motion-duration-base) var(--motion-ease-standard),
		background-color var(--motion-duration-base) var(--motion-ease-standard);
}

.area:hover {
	transform: translateY(calc(-1 * var(--lift-2)));
	box-shadow: var(--shadow-2);
}

/* The media is the positioning context for the icon badge, which hangs out of
   it, so the media cannot clip. The photograph gets its own clipping frame, so
   the hover zoom stays inside the card. */
.area__media {
	position: relative;
	aspect-ratio: 4 / 3;
}

.area__photo {
	position: absolute;
	inset: 0;
	overflow: hidden;
	border-start-start-radius: var(--radius-md);
	border-start-end-radius: var(--radius-md);
}

.area__media img {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	transition: transform var(--motion-duration-slow) var(--motion-ease-standard);
}

.area:hover .area__media img {
	transform: scale(var(--motion-scale-hover));
}

/* The badge straddles the seam between photograph and body rather than the top
   edge of the card. Same effect as the CRS reference, and it does not need the
   grid to reserve space above the first row for a shape that hangs outside the
   card. Placed at the inline start, so it sits right in Arabic and left in
   English with no fork. */
.area__badge {
	--icon-size: 3.25rem;
	position: absolute;
	inset-block-end: calc(var(--icon-size) / -2);
	inset-inline-start: var(--space-m);
	inline-size: var(--icon-size);
	block-size: var(--icon-size);
	display: grid;
	place-items: center;
	border-radius: 50%;
	background: var(--color-primary);
	color: var(--color-on-brand);
}

/* The sector's glyph from the icon font, as on the Programs headings (#80). */
.area__badge::before {
	font-family: 'CDF Icons';
	font-size: 1.75rem;
	font-weight: normal;
	line-height: 1;
}

.area__body {
	flex: 1;
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
	/* Clears the half of the badge that hangs into this box. */
	padding: calc(var(--space-m) + 1.75rem) var(--space-m) var(--space-m);
}

.area__name {
	margin: 0;
	font-size: var(--size-h3);
	color: var(--color-primary);
}

/* On Programs each name links to its sector below (#80). */
.area__name a {
	color: inherit;
	text-decoration: none;
}

.area__name a:hover {
	text-decoration: underline;
}

/* All six sectors (Programs): three columns at full width, not four and two. */
.areas--all {
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
	margin-block-end: var(--space-xl);
}

.area__desc {
	margin: 0;
	color: var(--color-muted);
	font-size: var(--size-small);
	line-height: 1.6;
}

/* Pushed to the bottom so the figure rows line up across the row however
   unequal the descriptions are. */
.area__figures {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--space-3xs);
	padding-block-start: var(--space-s);
}

/* The figures sit at the foot of every card in a row. The RN1 margin reset
   above is 0,3,0 (:is takes its most specific argument), so this must be too. */
:root .area__body > .area__figures {
	margin-block-start: auto;
}

/* Solid primary chips with accent numerals, client request 2026-07-30. This is
   the same move as the impact band one scale down, and it is the reason the
   band had to go deeper: three blue chips inside a white card, under a heading
   already set in the same blue, is a lot of one value in a small space. */
.area__figure {
	padding: var(--space-2xs) var(--space-3xs);
	border-radius: var(--radius-sm);
	background: var(--color-primary);
	text-align: center;
}

.area__figure-num {
	display: block;
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	font-size: var(--size-h3);
	line-height: 1.1;
	font-variant-numeric: tabular-nums;
	color: var(--color-accent);
}

/* The label stays white rather than yellow. Yellow on this blue is legal at
   7.65:1, but spending it on both the numeral and its caption removes the
   distinction the pair is there to draw, and the caption is --size-tiny, which
   is where yellow-on-blue starts to vibrate. */
.area__figure-label {
	display: block;
	margin-block-start: var(--space-3xs);
	font-size: var(--size-tiny);
	color: var(--color-on-brand);
	opacity: 0.88;
}

.area__more {
	margin: 0;
	padding-block-start: var(--space-s);
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
}

/* --color-link, not --color-primary. This is a link, and link is the slot a
   dark theme re-lights; primary deliberately does not move. The two blues are
   within a hair of each other in the light theme, so nothing visible changes
   here, but the wrong token would render at 1.78:1 the day dark mode lands. */
.area__more a {
	color: var(--color-link);
	text-decoration: none;
}

/* ============================================================================
   Videos and success stories (inventory #12, phased build 3.3)
   ============================================================================

   One story is a pair: the video on one side, the written half on the other,
   which is what the Agreement sells and what the page is for. The video slot is
   the core embed block, turned into the click-to-load facade by the render
   filter in functions.php; its own rules live in assets/css/video.css and load
   only on a page that has an embed on it.

   The written half is the reason the page cannot use `.prose`: at a 42rem
   measure the pair has nowhere to sit side by side. core/post-content renders
   in FLOW layout here, not constrained, so nothing caps a measure on this page
   and the intro paragraph would otherwise run the full 75rem of the wrap. The
   cap goes on everything that is not a story instead, so a paragraph the client
   types stays readable and the pair still gets the whole column. */

.stories .entry-content > :not(.story) {
	max-inline-size: var(--layout-content);
}

.story {
	display: grid;
	gap: var(--space-l);
	grid-template-columns: 1fr;
	align-items: start;
}

@media (min-width: 52rem) {
	.story {
		grid-template-columns: 1fr 1fr;
	}
}

/* A story with no video yet: one column at the reading width, so it does not
   leave an empty half (#71). The editor removes the class with the video. */
.story.story--text {
	grid-template-columns: 1fr;
	max-inline-size: var(--layout-content);
}

/* The intro and the first story are separate groups. */
.stories .entry-content > :not(.story) + .story {
	margin-block-start: var(--space-xl);
}

/* Two stories need more air between them than core's block gap gives, or the
   second one reads as a continuation of the first. */
.stories .story + .story {
	margin-block-start: var(--space-section);
}

/* The figure core wraps an embed in carries the browser's default margins. */
.story__media {
	margin: 0;
}

.story__body {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
}

.story__meta {
	font-size: var(--size-small);
	color: var(--color-muted);
}

/* The empty state, and it is deliberately not silent: until CDF sends the
   YouTube links (input #8) the media half says so, in the reader's language,
   at the size and shape the video will take. An empty grid cell would look
   like a broken page instead of a page waiting for content. Only the shape is
   here; the dashed marker is shared with the giving routes and is declared
   once, in the section below. */
.story__awaiting {
	display: grid;
	place-items: center;
	aspect-ratio: 16 / 9;
}

/* ============================================================================
   Ways to give (inventory #21, phased build 3.4)
   ============================================================================

   Presentational only, and that is a hard rule rather than a phase (hard rule
   3): a route is a description and a person to ask, never a gateway, a form or
   a processor. tools/check-php-notices.mjs renders this page and fails on a
   form, an iframe or a payment host appearing in it.

   One grid holds the title, the page content and the routes (Samir,
   2026-09-19). `.entry-content` is display: contents, so every block the Comms
   Office adds is a grid item and lands in the right place without a wrapper to
   keep intact. From 56rem: the title and the lead on the start side, the
   photograph on the end side, the bank transfer across the row, then the other
   routes two to a row. Anything else spans the row at a readable measure. */

.give {
	display: grid;
	gap: var(--space-m);
	grid-template-columns: minmax(0, 1fr);
}

.give .entry-content {
	display: contents;
}

.give .entry-content > :where(:not(.give-route):not(.give-photo)) {
	grid-column: 1 / -1;
	max-inline-size: var(--layout-content);
	margin: 0;
}

.give > .section__head {
	margin-block-end: 0;
}

.give-photo {
	margin: 0;
}

.give-photo img {
	display: block;
	inline-size: 100%;
	block-size: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: var(--radius-md);
}

@media (min-width: 56rem) {
	.give {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: var(--space-xl);
	}

	.give > .section__head {
		grid-column: 1;
		grid-row: 1;
		align-self: end;
	}

	.give .entry-content > .lead {
		grid-column: 1;
		grid-row: 2;
		align-self: start;
	}

	.give-photo {
		grid-column: 2;
		grid-row: 1 / span 2;
		align-self: center;
	}
}

.give-route--bank {
	grid-column: 1 / -1;
}

/* Beacon's heavy rule, static. The card signature elsewhere wipes in on hover,
   which says "this is a link"; a route is text, and nothing here is clickable
   except the contact link inside it. */
.give-route {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
	padding: var(--space-m);
	background: var(--color-base);
	border-block-start: var(--rule-weight) solid var(--color-primary);
	border-radius: var(--radius-md);
	box-shadow: var(--border-card);
}

.give-route__title {
	font-size: var(--size-h3);
	margin: 0;
}

.give-route__detail {
	font-size: var(--size-small);
	color: var(--color-muted);
}

/* The marked empty state, shared with the stories page (#12): a slot CDF has
   not filled yet says so, in the reader's language, in the shape the real
   thing will take. Silence would read as a finished page. */
.story__awaiting,
.give-route__awaiting {
	margin: 0;
	padding: var(--space-m);
	text-align: center;
	background: var(--color-surface);
	border: var(--rule-weight) dashed var(--color-primary);
	border-radius: var(--radius-lg);
	font-size: var(--size-small);
}

/* Pushed to the bottom so the marker, or the detail that replaces it, lines up
   across a row of routes however long the descriptions are. It follows the
   shared rule above, whose `margin` shorthand would otherwise reset it. */
.give-route__awaiting,
.give-route__detail {
	margin-block-start: auto;
}

/* The bank transfer route (Samir, 2026-09-19): the bank and the shared
   details, then one card per account, three to a row from 48rem. The emblem
   is El Nilein Bank's own, cut from https://nileinuae.com (2026-09-19) without
   the "Abu Dhabi" wordmark, because CDF banks at the Amarat branch in
   Khartoum. The name beside it is text, so the image is decoration. It sits
   on an on-brand disc, which keeps the bank's blue readable in dark. */
.give-bank {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
	margin-block-start: var(--space-xs);
}

.give-bank__name {
	display: flex;
	align-items: center;
	gap: var(--space-s);
	margin: 0;
	font-size: var(--size-large);
	font-weight: var(--weight-bold);
}

.give-bank__name::before {
	content: '';
	flex: none;
	inline-size: 3.5rem;
	block-size: 3.5rem;
	border-radius: 50%;
	background: var(--color-on-brand) url('assets/img/nilein-emblem.webp') center / 80% no-repeat;
	box-shadow: var(--border-card);
}

.give-bank__details {
	display: grid;
	gap: var(--space-3xs);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--size-small);
	color: var(--color-muted);
}

.give-bank__details strong {
	color: var(--color-contrast);
}

.give-accounts {
	display: grid;
	gap: var(--space-s);
	grid-template-columns: minmax(0, 1fr);
	margin-block-start: var(--space-xs);
}

@media (min-width: 48rem) {
	.give-accounts {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

/* Grid gaps do the spacing here, not the block gap's top margins. */
.give-accounts > *,
.give-bank > *,
.give-bank__details > li {
	margin: 0;
}

.give-account {
	display: flex;
	flex-direction: column;
	gap: var(--space-3xs);
	padding: var(--space-s);
	background: var(--color-surface);
	border-radius: var(--radius-md);
}

.give-account__currency {
	margin: 0;
	font-size: var(--size-body);
}

.give-account__label {
	margin: 0;
	font-size: var(--size-tiny);
	color: var(--color-muted);
}

/* One click selects the whole number, so it copies without a stray space. */
.give-account__iban {
	margin: 0;
	font-size: var(--size-large);
	font-weight: var(--weight-bold);
	font-variant-numeric: tabular-nums;
	/* The number is LTR in an RTL card: sit it on the card's start edge. */
	align-self: start;
	max-inline-size: 100%;
	user-select: all;
	overflow-wrap: anywhere;
}

/* ============================================================================
   Programs (the Projects page, slug `projects`)
   ============================================================================

   CDF's approved programmes copy is page content (tools/content/README.md),
   so these rules sit on the classes in the content, as on the About page.
   Order: the introduction on the page ground, the list of the six sectors,
   then the sectors, which alternate tinted band and page ground so the page
   is not one long field. The sub-areas are core Details blocks: no script, and
   the browser gives them keyboard and screen reader behaviour for free. */

.programs .entry-content > * {
	max-inline-size: var(--layout-content);
}

.programs .entry-content > :is(.programs-index, .program-sector) {
	max-inline-size: none;
}

.programs-intro {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
}

.programs-intro > p {
	margin: 0;
}

/* ---- the sector list: six jump links ---- */
.programs-index {
	display: grid;
	gap: var(--space-s);
	grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
	margin: var(--space-l) 0 0;
	padding: 0 0 var(--space-section);
	list-style: none;
}

/* From 64rem the list moves beside the introduction, one link per row, so
   the six shortcuts sit in view next to the copy (Samir, 2026-09-18). */
@media (min-width: 64rem) {
	.programs .entry-content {
		display: grid;
		grid-template-columns: minmax(0, var(--layout-content)) minmax(0, 1fr);
		column-gap: var(--space-xl);
		align-items: start;
	}

	.programs .entry-content > * {
		grid-column: 1 / -1;
	}

	.programs .entry-content > .programs-intro {
		grid-column: 1;
		padding-block-end: var(--space-section);
	}

	.programs .entry-content > .programs-index {
		grid-column: 2;
		grid-template-columns: minmax(0, 1fr);
		margin: 0;
	}
}

.programs-index a {
	display: flex;
	align-items: center;
	gap: var(--space-s);
	block-size: 100%;
	box-sizing: border-box;
	padding: var(--space-s) var(--space-m);
	background: var(--color-surface);
	border-radius: var(--radius-md);
	color: var(--color-primary);
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	line-height: var(--line-tight);
	text-decoration: none;
}

.programs-index a:hover {
	background: var(--color-primary);
	color: var(--color-on-brand);
}

/* ---- the sector icons ----
   The same badge as the About headings, from the subset icon font. One glyph
   per sector, keyed on the sector class the content carries, so the list and
   the section heading always show the same icon. */
.programs-index a::before,
.program-sector > h2::before {
	--icon-size: 3.25rem;
	display: grid;
	place-items: center;
	flex: none;
	inline-size: var(--icon-size);
	block-size: var(--icon-size);
	border-radius: 50%;
	background: var(--color-primary);
	color: var(--color-on-brand);
	font-family: 'CDF Icons';
	font-size: 1.75rem;
	font-weight: normal;
	line-height: 1;
}

.programs-index a::before {
	--icon-size: 2.5rem;
	font-size: 1.35rem;
}

.programs-index a:hover::before {
	background: var(--color-accent);
	color: var(--color-on-accent);
}

.programs-index__item--health a::before, .program-sector--health > h2::before, .sector-nav__item--health a::before, .area__badge--health::before { content: "\edbe"; content: "\edbe" / ""; }
.programs-index__item--food a::before, .program-sector--food > h2::before, .sector-nav__item--food a::before, .area__badge--food::before { content: "\ed51"; content: "\ed51" / ""; }
.programs-index__item--education a::before, .program-sector--education > h2::before, .sector-nav__item--education a::before, .area__badge--education::before { content: "\ecf7"; content: "\ecf7" / ""; }
.programs-index__item--protection a::before, .program-sector--protection > h2::before, .sector-nav__item--protection a::before, .area__badge--protection::before { content: "\f9c7"; content: "\f9c7" / ""; }
.programs-index__item--wash a::before, .program-sector--wash > h2::before, .sector-nav__item--wash a::before, .area__badge--wash::before { content: "\ea97"; content: "\ea97" / ""; }
.programs-index__item--eore a::before, .program-sector--eore > h2::before, .sector-nav__item--eore a::before, .area__badge--eore::before { content: "\ea06"; content: "\ea06" / ""; }

/* ---- a sector: heading, introduction, sub-areas ----
   Two columns from 64rem: the introduction at a reading measure, the
   sub-areas beside it. Below that they stack. */
.program-sector {
	display: grid;
	gap: var(--space-m) var(--space-xl);
	grid-template-columns: minmax(0, 1fr);
	align-items: start;
	padding-block: var(--space-section);
}

@media (min-width: 64rem) {
	.program-sector {
		grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
	}
}

.program-sector > h2 {
	grid-column: 1 / -1;
	display: flex;
	align-items: center;
	gap: var(--space-s);
	margin: 0;
	color: var(--color-primary);
	font-size: var(--size-h2);
	/* A jump from the sector list lands at the top of the sector's band. The
	   header's height is already in the page's scroll-padding. */
	scroll-margin-block-start: var(--space-section);
}

.program-sector__intro {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
}

.program-sector__intro > p {
	margin: 0;
}

/* The tinted bands: the first, third and fifth sector, counted among sectors
   only, so a block the Comms Office adds above them does not shift the
   pattern. Same field technique as "the About bands". */
.program-sector:nth-child(odd of .program-sector) {
	background: var(--color-surface);
	border-image: conic-gradient(var(--color-surface) 0 0) fill 0 / 0 / 0 100vmax;
}

/* ---- the sub-areas ----
   A row is a card of the other ground: base on a tinted band, surface on the
   page ground, so it reads as a row on both. */
.program-areas {
	display: flex;
	flex-direction: column;
	gap: var(--space-xs);
}

.program-area {
	background: var(--color-surface);
	border-radius: var(--radius-md);
}

.program-sector:nth-child(odd of .program-sector) .program-area {
	background: var(--color-base);
	box-shadow: var(--border-card);
}

.program-area > summary {
	display: flex;
	align-items: center;
	gap: var(--space-s);
	padding: var(--space-s) var(--space-m);
	list-style: none;
	cursor: pointer;
	color: var(--color-primary);
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	line-height: var(--line-tight);
}

.program-area > summary::-webkit-details-marker {
	display: none;
}

.program-area > summary::after {
	content: "\ea5f";
	content: "\ea5f" / "";
	margin-inline-start: auto;
	font-family: 'CDF Icons';
	font-weight: normal;
	font-size: 1.25em;
	line-height: 1;
}

.program-area[open] > summary::after {
	transform: rotate(180deg);
}

.program-area > p {
	margin: 0;
	padding: 0 var(--space-m) var(--space-m);
}

/* ---- the 2025 projects of a sector ----
   Built from tools/content/projects-2025.json. Each item is a plain list item,
   so the Comms Office edits it in the block editor: the title in bold, then
   state, donor and months. Cards take the other ground, as the sub-areas do. */
.program-projects {
	grid-column: 1 / -1;
}

.program-projects > h3 {
	margin: 0 0 var(--space-s);
	font-size: var(--size-h3);
}

.program-projects__list {
	display: grid;
	gap: var(--space-xs);
	grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

.program-projects__list > li {
	margin: 0;
	padding: var(--space-s) var(--space-m);
	background: var(--color-surface);
	border-radius: var(--radius-md);
	color: var(--color-muted);
	font-size: var(--size-small);
}

.program-sector:nth-child(odd of .program-sector) .program-projects__list > li {
	background: var(--color-base);
	box-shadow: var(--border-card);
}

.program-projects__list strong {
	display: block;
	margin-block-end: var(--space-2xs);
	color: var(--color-contrast);
	font-size: var(--size-body);
	font-weight: var(--weight-bold);
	line-height: var(--line-tight);
}

.program-projects__list br {
	display: none;
}

/* ============================================================================
   Reports & Research (phased build 3.9)
   ============================================================================

   templates/reports.html and patterns/reports-list.php. The tabs and the
   search stay hidden until reports.js runs, so without scripts the page is a
   plain list of every publication. The buttons borrow the share button. */

.reports-tools {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
	margin-block-end: var(--space-l);
}

.reports-tabs {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2xs);
}

.reports-tab {
	padding: 0.35em 0.9em;
	border: 0;
	border-radius: var(--radius-sm);
	background: var(--color-surface);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 30%, transparent);
	color: var(--color-primary);
	font: inherit;
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
	line-height: 1.6;
	cursor: pointer;
}

.reports-tab:hover,
.reports-tab[aria-pressed='true'] {
	background: var(--color-primary);
	color: var(--color-on-brand);
}

.reports-search {
	position: relative;
	max-inline-size: 36rem;
}

.reports-search::before {
	content: "\eb1c";
	content: "\eb1c" / "";
	position: absolute;
	inset-block: 0;
	inset-inline-start: var(--space-s);
	display: grid;
	place-items: center;
	color: var(--color-muted);
	font-family: 'CDF Icons';
	font-size: 1.25rem;
	pointer-events: none;
}

.reports-search input {
	box-sizing: border-box;
	inline-size: 100%;
	padding: 0.7em var(--space-s);
	padding-inline-start: calc(var(--space-s) * 2 + 1.25rem);
	border: 1px solid color-mix(in srgb, var(--color-primary) 30%, transparent);
	border-radius: var(--radius-md);
	background: var(--color-base);
	color: var(--color-contrast);
	font: inherit;
}

.reports-search input::placeholder {
	color: var(--color-muted);
	opacity: 1;
}

.reports-count {
	margin: 0;
	color: var(--color-muted);
	font-size: var(--size-small);
}

.reports {
	display: grid;
	gap: var(--space-m);
	grid-template-columns: repeat(auto-fill, minmax(min(34rem, 100%), 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

.report {
	display: grid;
	grid-template-columns: clamp(4.5rem, 22%, 9rem) minmax(0, 1fr);
	gap: var(--space-m);
	margin: 0;
	padding: var(--space-m);
	background: var(--color-base);
	border-radius: var(--radius-md);
	box-shadow: var(--border-card);
}

.report[hidden] {
	display: none;
}

/* A4 portrait. A publication without a cover shows the document icon. */
.report__cover {
	display: grid;
	place-items: center;
	align-self: start;
	aspect-ratio: 210 / 297;
	overflow: hidden;
	background: var(--color-surface);
	border-radius: var(--radius-sm);
	box-shadow: var(--border-card);
}

.report__cover:empty::before {
	content: "\eaa2";
	content: "\eaa2" / "";
	color: var(--color-primary);
	font-family: 'CDF Icons';
	font-size: 2.5rem;
}

.report__cover img {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
}

.report__body {
	display: flex;
	flex-direction: column;
	gap: var(--space-xs);
	min-inline-size: 0;
}

.report__title {
	margin: 0;
	color: var(--color-primary);
	font-size: var(--size-h3);
	line-height: var(--line-tight);
}

.report__summary {
	margin: 0;
	font-size: var(--size-small);
}

.report__meta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-3xs) var(--space-xs);
	margin: 0;
	padding: 0;
	list-style: none;
	color: var(--color-muted);
	font-size: var(--size-tiny);
	font-weight: var(--weight-bold);
}

.report__meta li {
	margin: 0;
}

.report__meta li:not(:last-child)::after {
	content: "·";
	margin-inline-start: var(--space-xs);
}

.report__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-xs);
	margin-block-start: auto;
	padding-block-start: var(--space-2xs);
}

.report__link--download::before { content: "\ea96"; content: "\ea96" / ""; }
.report__link--view::before { content: "\ea99"; content: "\ea99" / ""; }

.report__soon {
	align-self: flex-start;
	margin: auto 0 0;
	padding: 0.25em 0.85em;
	border-radius: var(--radius-sm);
	background: var(--color-surface);
	color: var(--color-muted);
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
}

.reports-empty {
	color: var(--color-muted);
}

/* ============================================================================
   Photo gallery (phased build 3.10)
   ============================================================================

   templates/gallery.html and assets/js/gallery.js. Without the script the
   core Gallery block keeps its own grid. With it, the block becomes a strip of
   square thumbnails under a stage. The lightbox is PhotoSwipe's own stylesheet,
   loaded on the first open; only the caption below is ours. */

.gallery-stage {
	display: flex;
	flex-direction: column;
	gap: var(--space-xs);
	margin: 0 0 var(--space-s);
}

.gallery-stage__open {
	display: block;
	padding: 0;
	border: 0;
	border-radius: var(--radius-md);
	overflow: hidden;
	background: var(--color-surface);
	cursor: zoom-in;
}

.gallery-stage__open img {
	display: block;
	inline-size: 100%;
	block-size: auto;
	max-block-size: 70vh;
	aspect-ratio: 16 / 10;
	object-fit: contain;
}

.gallery-stage__caption {
	min-block-size: calc(1em * var(--line-body));
	color: var(--color-muted);
	font-size: var(--size-small);
}

/* No visible title: the stage and the strip fill the window (Samir,
   2026-09-18). gallery.js measures the height left under the sticky header
   and sets --gallery-fit; without the script the core grid shows instead. */
.gallery-section {
	padding-block: var(--space-m);
}

/* The 16:10 ratio stays, so a phone's tall stage does not letterbox a
   landscape photograph; the fit caps it where the window is the limit. */
.gallery-stage.is-fitted .gallery-stage__open img {
	max-block-size: var(--gallery-fit);
}

.gallery-page .wp-block-gallery.is-strip {
	display: flex;
	flex-wrap: nowrap;
	gap: var(--space-2xs);
	margin: 0;
	padding-block-end: var(--space-2xs);
	overflow-x: auto;
	scroll-snap-type: x proximity;
	scrollbar-width: thin;
}

/* Core sizes gallery images with figure.wp-block-image:not(#individual-image),
   an ID in the specificity, so this rule carries the same :not(). */
.gallery-page .wp-block-gallery.is-strip > figure.wp-block-image:not(#individual-image) {
	flex: 0 0 auto;
	inline-size: 5.5rem;
	margin: 0;
	scroll-snap-align: start;
}

.gallery-page .wp-block-gallery.is-strip figcaption {
	display: none;
}

.gallery-page .wp-block-gallery.is-strip a {
	display: block;
	border-radius: var(--radius-sm);
	overflow: hidden;
	opacity: 0.65;
	outline-offset: -3px;
}

.gallery-page .wp-block-gallery.is-strip a:hover,
.gallery-page .wp-block-gallery.is-strip a[aria-current='true'] {
	opacity: 1;
}

.gallery-page .wp-block-gallery.is-strip a[aria-current='true'] {
	box-shadow: inset 0 0 0 3px var(--color-accent);
}

.gallery-page .wp-block-gallery.is-strip img {
	display: block;
	inline-size: 100%;
	block-size: auto;
	aspect-ratio: 1;
	object-fit: cover;
}

/* The inset ring must sit over the photograph, not under it. */
.gallery-page .wp-block-gallery.is-strip a[aria-current='true'] img {
	position: relative;
	z-index: -1;
}

.pswp__cdf-caption {
	position: absolute;
	inset-inline: 0;
	inset-block-end: 0;
	padding: var(--space-s) var(--space-m);
	background: rgb(0 0 0 / 0.6);
	color: #fff;
	font-size: var(--size-small);
	text-align: center;
}

/* ============================================================================
   Contact us (phased build 3.11)
   ============================================================================

   templates/contact.html: the contact details and map (patterns/
   contact-location.php) beside the form (patterns/contact-form.php) from
   56rem, stacked below it. The map is OpenStreetMap's own embed, lazy-loaded,
   so it cannot take the theme's colours. docs/CDF_Contact_Page.md. */

.contact-page__grid {
	display: grid;
	gap: var(--space-xl);
	grid-template-columns: minmax(0, 1fr);
	align-items: start;
}

/* Core's flow layout puts a block gap on the second child; the grid's own gap
   replaces it, so the two columns start level. */
.contact-page__grid > * {
	margin-block: 0;
}

@media (min-width: 56rem) {
	.contact-page__grid {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	}
}

.contact-location,
.contact-form {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: var(--space-m);
}

.contact-location__title,
.contact-form__title {
	margin: 0;
	color: var(--color-primary);
	font-family: var(--font-heading);
	font-size: var(--size-h3);
	font-weight: var(--weight-heading);
	line-height: var(--line-tight);
}

.contact-location__address {
	font-style: normal;
}

.contact-location__list {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
	margin: 0;
}

/* Icon in the start column, label over value beside it. */
.contact-location__item {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	column-gap: var(--space-xs);
}

.contact-location__item::before {
	grid-row: span 2;
	color: var(--color-primary);
	font-family: 'CDF Icons';
	font-size: 1.4em;
	line-height: var(--line-tight);
}

.contact-location__item--address::before { content: "\eae8"; content: "\eae8" / ""; }
.contact-location__item--phone::before { content: "\eb09"; content: "\eb09" / ""; }
.contact-location__item--email::before { content: "\eae5"; content: "\eae5" / ""; }

.contact-location__item dt {
	color: var(--color-muted);
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
}

.contact-location__item dd {
	margin: 0;
}

.contact-location__link {
	align-self: flex-start;
}

.contact-location__link::before { content: "\eae8"; content: "\eae8" / ""; }

/* 4:3 on a phone, 2:1 beside the form, which levels the two columns. */
.contact-location__map {
	margin: 0;
	aspect-ratio: 4 / 3;
	overflow: hidden;
	border-radius: var(--radius-md);
	background: var(--color-surface);
	box-shadow: var(--border-card);
}

.contact-location__map iframe {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	border: 0;
}

@media (min-width: 56rem) {
	.contact-location__map {
		aspect-ratio: 2 / 1;
	}
}

/* ---- the state offices: the branch map in contact mode
   (patterns/contact-branches.php, about-branches.php) ---- */

.branch__note {
	margin: 0;
	color: var(--color-muted);
}

/* dir="ltr" on a block box would push the number to the left edge in RTL. */
.branch__phone {
	display: block;
	inline-size: fit-content;
}

/* ---- the form ---- */

.contact-form {
	padding: var(--space-l);
	border-radius: var(--radius-md);
	background: var(--color-surface);
	box-shadow: var(--border-card);
}

.contact-form__form {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
}

/* Name and email side by side when the card is wide enough for both. */
.contact-form__row {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
	gap: var(--space-s);
}

.contact-form__field {
	display: flex;
	flex-direction: column;
	gap: var(--space-3xs);
	margin: 0;
}

.contact-form__field label {
	font-weight: var(--weight-bold);
}

/* The border is the muted text colour, so the field's edge keeps the 3:1 a
   control boundary needs (WCAG 1.4.11) in both themes. */
.contact-form__field input,
.contact-form__field textarea {
	box-sizing: border-box;
	inline-size: 100%;
	padding: 0.7em 0.9em;
	border: 1px solid var(--color-muted);
	border-radius: var(--radius-sm);
	background: var(--color-base);
	color: var(--color-contrast);
	font: inherit;
	line-height: var(--line-body);
}

.contact-form__field textarea {
	resize: vertical;
	min-block-size: 9em;
}

.contact-form__submit {
	align-self: flex-start;
}

/* The trap field. Hidden from everyone, including screen readers; a person
   never fills it and most bots do. */
.contact-form__trap {
	display: none;
}

.contact-form__status {
	margin: 0;
	padding: var(--space-s) var(--space-m);
	border-inline-start: 4px solid var(--color-primary);
	border-radius: var(--radius-sm);
	background: var(--color-base);
	font-weight: var(--weight-bold);
}

/* ============================================================================
   About (phased build 3.8)
   ============================================================================

   CDF's approved copy is page content, so these rules sit on the content
   rather than on a wrapper the client must keep intact. core/post-content
   renders in FLOW layout, so prose gets the reading measure here, and the
   statements, the objectives and the value groups get the whole column.
   Section order and sources: templates/about.html. The hero is the homepage
   hero (cdf/about-hero), so it carries no rules of its own here. */

/* The subset icon font: thirty-one Tabler glyphs, 5.3 KB (ADR-0010 amendment of
   2026-09-16). tools/build-icon-font.py writes the unicode-range, and --check
   fails when a codepoint below is not in the font. `block` because an icon that swaps in late is a jump,
   and the fallback for a font that never arrives is the empty badge, not a
   letter from the body face. Every `content` is written twice: the first for
   browsers that cannot read the `/ ""` alt text, which hides the glyph from
   screen readers in the ones that can. The badge is decoration, and the
   heading beside it already says what it is. */
@font-face {
	font-family: 'CDF Icons';
	src: url('assets/fonts/cdf-tabler-icons.woff2') format('woff2');
	font-display: block;
	unicode-range: U+EA06, U+EA5F, U+EA60, U+EA61, U+EA7A, U+EA96, U+EA97, U+EA99, U+EA9A, U+EAA2, U+EAE5, U+EAE8, U+EAFD, U+EB09, U+EB1C, U+EB21, U+EB54, U+EB55, U+EB6A, U+EBC2, U+EC1A, U+EC20, U+EC74, U+EC8C, U+ECF7, U+ED51, U+EDBE, U+F0F3, U+F51A, U+F7CD, U+F9C7;
}

.about .entry-content > * {
	max-inline-size: var(--layout-content);
}

.about .entry-content > :is(.about-profile, .about-statements, .about-objectives-band, .about-values-band) {
	max-inline-size: none;
}

/* ---- the profile: CDF's copy in the first column, the mark in the second ----
   The copy wrapper and the mark are added at render time
   (cdf_about_profile_mark), so the editor sees three plain paragraphs in a
   group. Below 56rem it stacks under the copy.
   The disc is transparent in light, where the mark sits on the white it was
   drawn for, and on-brand in dark (see the dark block). */
.about-profile {
	display: grid;
	gap: var(--space-m) var(--space-xl);
	align-items: center;
	grid-template-columns: 1fr;
}

.about-profile__copy {
	display: flex;
	flex-direction: column;
	gap: var(--space-m);
	max-inline-size: var(--layout-content);
	grid-column: 1;
}

.about-profile__copy > p {
	margin: 0;
}

@media (min-width: 56rem) {
	.about-profile {
		grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
	}

	.about-profile__mark {
		grid-column: 2;
		grid-row: 1;
	}
}

.about-profile__mark {
	justify-self: center;
	display: grid;
	place-items: center;
	inline-size: 14rem;
	aspect-ratio: 1;
	border-radius: 50%;
}

.about-profile__logo {
	inline-size: 10rem;
	block-size: auto;
}

.about .entry-content > h2 {
	margin-block-start: var(--space-section);
}

.about-statement > h2::before,
.about-objectives-title::before {
	--icon-size: 3.25rem;
	display: grid;
	place-items: center;
	flex: none;
	inline-size: var(--icon-size);
	block-size: var(--icon-size);
	border-radius: 50%;
	background: var(--color-primary);
	color: var(--color-on-brand);
	font-family: 'CDF Icons';
	font-size: 1.75rem;
	font-weight: normal;
	line-height: 1;
}

.about-statement--vision > h2::before { content: "\ea9a"; content: "\ea9a" / ""; }
.about-statement--mission > h2::before { content: "\f51a"; content: "\f51a" / ""; }
.about-objectives-title::before { content: "\eb6a"; content: "\eb6a" / ""; }

.about-statement > h2,
.about-objectives-title {
	display: flex;
	align-items: center;
	gap: var(--space-s);
	margin: 0;
	color: var(--color-primary);
}

/* ---- vision and mission: open statements under Beacon's heavy rule ---- */

.about-statements {
	display: grid;
	gap: var(--space-xl);
	grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
	margin-block-start: var(--space-section);
	padding-block: var(--space-section);
}

/* ---- the About bands ----
   The content below the hero alternates page ground and tinted band, so the
   page is not one long field (Samir, 2026-09-16): profile on the ground,
   vision and mission tinted, objectives on the ground, values tinted, share
   and structure on the ground, branches tinted. The branches stay tinted
   because their cards and map panel are base-coloured and need the tint to
   read as cards.

   Surface is the tinted section band's one job. primary-deep is not used,
   because ADR-0012 decision 2 keeps it from backgrounds behind running prose;
   in the dark theme surface is #1A2152 against the ground's #0D1233.

   Each band stays inside the content column in the markup and paints its field
   out to the viewport edges with border-image, which adds no width, so the
   page cannot scroll sideways; .section clips the rest (overflow-x: clip). */
.about-statements,
.about-values-band {
	background: var(--color-surface);
	border-image: conic-gradient(var(--color-surface) 0 0) fill 0 / 0 / 0 100vmax;
}

.about-statement {
	display: flex;
	flex-direction: column;
	gap: var(--space-m);
	padding-block-start: var(--space-l);
	border-block-start: var(--rule-weight) solid var(--color-primary);
}

.about-statement > h2 {
	font-size: var(--size-h3);
}

.about-statement p {
	font-family: var(--font-heading);
	font-size: var(--size-lead);
	line-height: var(--line-lead);
	color: var(--color-contrast);
}

/* ---- objectives: the order is CDF's, so the numerals carry it ---- */

/* On the page ground, between the two tinted bands (see "the About bands"). */
.about-objectives-band {
	display: flex;
	flex-direction: column;
	gap: var(--space-m);
	padding-block: var(--space-section);
}

.about-objectives-band > p {
	max-inline-size: var(--layout-content);
	margin: 0;
}

.about-objectives-title {
	font-size: var(--size-h2);
}

.about-objectives {
	counter-reset: objective;
	list-style: none;
	padding: 0;
	display: grid;
	gap: var(--space-m) var(--space-xl);
	grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
}

/* The numeral is positioned rather than a grid column: an Arabic objective is
   a <strong> title and a bare text node, and a grid would give each its own
   cell. */
.about-objectives > li {
	counter-increment: objective;
	position: relative;
	padding-block-start: var(--space-s);
	padding-inline-start: calc(var(--size-h2) * 1.4);
	border-block-start: 1px solid color-mix(in srgb, var(--color-primary) 18%, transparent);
}

.about-objectives > li::before {
	content: counter(objective);
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	font-size: var(--size-h2);
	line-height: 1;
	color: var(--color-primary);
	position: absolute;
	inset-block-start: var(--space-s);
	inset-inline-start: 0;
}

.about-objectives strong {
	display: block;
	color: var(--color-primary);
}

/* ---- values: a tinted band, a numbered grid, no card boxes ---- */

.about-values-band {
	display: flex;
	flex-direction: column;
	gap: var(--space-m);
	padding-block: var(--space-section);
}

.about-values-band > p {
	max-inline-size: var(--layout-content);
	margin: 0;
}

.about-values {
	counter-reset: value;
	list-style: none;
	padding: 0;
	margin-block-start: var(--space-s);
	display: grid;
	gap: var(--space-m) var(--space-xl);
	grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
}

/* Six names and no descriptions since 2026-10-12, numbered in CDF's order,
   the same numeral as the objectives above. */
.about-values > li {
	counter-increment: value;
	display: flex;
	align-items: baseline;
	gap: var(--space-s);
	padding-block-start: var(--space-s);
	border-block-start: 1px solid color-mix(in srgb, var(--color-primary) 18%, transparent);
	font-family: var(--font-heading);
	font-weight: var(--weight-heading);
	font-size: var(--size-h3);
	color: var(--color-primary);
}

.about-values > li::before {
	content: counter(value);
	flex: none;
	min-inline-size: 0.75em;
	font-size: var(--size-h2);
	font-variant-numeric: tabular-nums;
	line-height: 1;
}

/* ---- the video slot (Q9): first block of the page content ----
   Until CDF sends the link, a photograph and a note hold the video's place.
   The Comms Office adds a YouTube embed block to the group, and :has() then
   hides the placeholder, so a note left behind cannot sit under the video. */
.about .entry-content > .about-video {
	max-inline-size: 56rem;
	margin-inline: auto;
	margin-block-end: var(--space-section);
}

.about-video .wp-block-image {
	margin: 0;
}

.about-video .wp-block-image img {
	display: block;
	inline-size: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
	border-radius: var(--radius-lg);
}

.about-video__awaiting {
	margin: var(--space-xs) 0 0;
	text-align: center;
	font-size: var(--size-small);
	color: var(--color-muted);
}

.about-video:has(.wp-block-embed) > :is(.wp-block-image, .about-video__awaiting) {
	display: none;
}

/* ---- share (Q14): after the copy ---- */

.about-share {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-s) var(--space-m);
	margin-block-start: var(--space-section);
	padding-block-start: var(--space-m);
	border-block-start: 1px solid color-mix(in srgb, var(--color-primary) 18%, transparent);
}

.about-share__label {
	margin: 0;
	font-weight: var(--weight-bold);
}

.about-share__list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-xs);
	margin: 0;
	padding: 0;
	list-style: none;
}

.about-share__link {
	display: inline-flex;
	align-items: center;
	gap: 0.5em;
	padding: 0.5em 1em;
	border: 0;
	border-radius: var(--radius-md);
	background: transparent;
	box-shadow: inset 0 0 0 2px currentColor;
	color: var(--color-primary);
	font: inherit;
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
	line-height: var(--line-tight);
	text-decoration: none;
	cursor: pointer;
}

.about-share__link:hover {
	background: var(--color-primary);
	color: var(--color-on-brand);
}

.about-share__link::before {
	font-family: 'CDF Icons';
	font-weight: normal;
	font-size: 1.25em;
	line-height: 1;
}

.about-share__link--facebook::before { content: "\ec1a"; content: "\ec1a" / ""; }
.about-share__link--linkedin::before { content: "\ec8c"; content: "\ec8c" / ""; }
.about-share__link--whatsapp::before { content: "\ec74"; content: "\ec74" / ""; }
.about-share__link--instagram::before { content: "\ec20"; content: "\ec20" / ""; }

.about-share__link--copy::before { content: "\ea7a"; content: "\ea7a" / ""; }

/* ---- homepage about: the prose and the silver jubilee logo, 60:40 (#82) ----
   The logo sits on white in both themes: its silver parts vanish on the dark
   ground, as the partner logos and the organogram would. */
.home-about {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--space-l);
	align-items: start;
}

/* The grid gap spaces the heading; its own margin would double it. */
.home-about > .section__head {
	margin-block-end: 0;
}

@media (min-width: 52rem) {
	.home-about {
		grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
		grid-template-rows: auto 1fr; /* the logo's extra height goes under the prose */
		column-gap: var(--space-xl);
	}

	/* The logo spans the heading and the prose, so its top meets the rule. */
	.home-about > .jubilee {
		grid-column: 2;
		grid-row: 1 / span 2;
	}
}

.jubilee__tile {
	display: block;
	padding: var(--space-m);
	background: #fff; /* the logo is drawn on white in both themes */
	border: 1px solid color-mix(in srgb, var(--color-primary) 18%, transparent);
	border-radius: var(--radius-md);
}

.jubilee__tile img {
	display: block;
	inline-size: 100%;
	block-size: auto;
}

/* ---- organizational structure (Q12) ----
   Built by tools/build-organogram.py. The links borrow the share button. */

.organogram {
	display: grid;
	gap: var(--space-m);
	grid-template-columns: repeat(auto-fit, minmax(min(30rem, 100%), 1fr));
}

.organogram__page {
	margin: 0;
}

.organogram__caption {
	margin-block-start: var(--space-2xs);
	font-size: var(--size-tiny);
	color: var(--color-muted);
}

.organogram__image {
	display: block;
	border: 1px solid color-mix(in srgb, var(--color-primary) 18%, transparent);
	border-radius: var(--radius-md);
	overflow: hidden;
	background: #fff; /* the chart is drawn on white in both themes */
}

.organogram__image img {
	display: block;
	inline-size: 100%;
	block-size: auto;
}

.organogram__actions {
	grid-column: 1 / -1;
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-xs);
	margin: 0;
}

/* ---- partnerships (Q5): the last section, on the page ground ----
   Each logo sits on a white tile in both themes, because the marks were drawn
   for white, as the organogram is. The name under it is the text a reader and
   a screen reader get; the logo has empty alt text. */
.partners {
	display: grid;
	gap: var(--space-m);
	grid-template-columns: repeat(auto-fill, minmax(min(10rem, 100%), 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

.partner {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
}

.partner__logo {
	display: flex;
	align-items: center;
	justify-content: center;
	block-size: 6rem;
	padding: var(--space-s);
	background: #fff; /* the marks are drawn on white in both themes */
	border: 1px solid color-mix(in srgb, var(--color-primary) 18%, transparent);
	border-radius: var(--radius-md);
}

.partner__logo img {
	display: block;
	inline-size: auto;
	block-size: auto;
	max-inline-size: 100%;
	max-block-size: 100%;
}

.partner__name {
	font-size: var(--size-small);
	line-height: 1.4;
	color: var(--color-muted);
}

/* ---- About contact (patterns/about-contact.php, Malk 2026-10-14) ----
   The head office in a primary panel at the start, three cards at the end;
   stacked below 56rem. The panel is a card, not a field: ADR-0012 decision 2
   keeps the page's solid fields to two. Yellow sits on it only as the rule,
   because accent is never ink. */
.about-contact__grid {
	display: grid;
	gap: var(--space-m);
	grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 56rem) {
	.about-contact__grid {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	}
}

.about-contact__office {
	padding: var(--space-l);
	border-radius: var(--radius-md);
	background: var(--color-primary);
	color: var(--color-on-brand);
	font-style: normal;
}

.about-contact__office::before {
	content: '';
	display: block;
	inline-size: 3.5rem;
	block-size: var(--rule-weight);
	margin-block-end: var(--space-s);
	background: var(--color-accent);
}

.about-contact__office-title {
	margin: 0 0 var(--space-xs);
	color: var(--color-on-brand);
	font-size: var(--size-h3);
}

.about-contact__office p {
	margin: 0;
}

.about-contact__list {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Icon tile at the start, label over value beside it. */
.about-contact__item {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	align-items: center;
	column-gap: var(--space-s);
	padding: var(--space-s) var(--space-m);
	border-radius: var(--radius-md);
	background: var(--color-surface);
	box-shadow: var(--border-card);
}

.about-contact__item::before {
	grid-row: span 2;
	display: grid;
	place-items: center;
	inline-size: 2.75rem;
	block-size: 2.75rem;
	border-radius: var(--radius-sm);
	background: color-mix(in srgb, var(--color-primary) 10%, var(--color-base));
	color: var(--color-primary);
	font-family: 'CDF Icons';
	font-size: 1.3rem;
	line-height: 1;
}

.about-contact__item--email::before { content: "\eae5"; content: "\eae5" / ""; }
.about-contact__item--phone::before { content: "\eb09"; content: "\eb09" / ""; }
.about-contact__item--web::before { content: "\eb54"; content: "\eb54" / ""; }

.about-contact__label {
	color: var(--color-muted);
	font-size: var(--size-small);
}

/* dir="ltr" on the link keeps the digits in order; justify-self keeps the
   value at the start edge in RTL. */
.about-contact__value {
	justify-self: start;
	color: var(--color-contrast);
	font-weight: var(--weight-bold);
	text-decoration: none;
}

.about-contact__value:hover {
	color: var(--color-link);
	text-decoration: underline;
}

/* ---- homepage partner strip ----
   The About tiles, smaller, in one row that loops. The only loop on the site
   (ADR-0007 amendment 2026-10-07). Under reduced motion it is a still,
   wrapped grid with no second copy, declared in its own query so the
   collision gate sees two chosen layouts. The item margin, not a flex gap,
   spaces the tiles, so the two copies are exactly half the track each and the
   loop has no seam. Detail: docs/CDF_Design_System.md, "Partner strip". */
.partner-strip__viewport {
	display: block;
	text-decoration: none;
}

.partner-strip__track {
	display: flex;
	margin: 0;
	padding: 0;
	list-style: none;
}

.partner-strip__item {
	display: flex;
	flex: none;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	inline-size: 9rem;
	block-size: 4.5rem;
	padding: var(--space-xs);
	background: #fff; /* the marks are drawn on white in both themes */
	border: 1px solid color-mix(in srgb, var(--color-primary) 18%, transparent);
	border-radius: var(--radius-md);
}


.partner-strip__item img {
	display: block;
	inline-size: auto;
	block-size: auto;
	max-inline-size: 100%;
	max-block-size: 100%;
}

.partner-strip__bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s);
	margin-block-start: var(--space-m);
}

.partner-strip__more {
	font-weight: var(--weight-bold);
}

/* inline-block, so the link's underline does not run under the chevron. */
.partner-strip__more::after {
	content: "\ea60";
	content: "\ea60" / "";
	display: inline-block;
	margin-inline-start: var(--space-2xs);
	vertical-align: -0.2em;
	font-family: 'CDF Icons';
	font-size: 1.25rem;
	font-weight: normal;
	line-height: 1;
	transform: scaleX(var(--inline));
}

.partner-strip__toggle {
	display: inline-grid;
	place-items: center;
	inline-size: 2.75rem;
	block-size: 2.75rem;
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm);
	background: var(--color-surface);
	color: var(--color-primary);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 30%, transparent);
	cursor: pointer;
}

.partner-strip__toggle[hidden] {
	display: none;
}

.partner-strip__icon {
	inline-size: 1.25rem;
	block-size: 1.25rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 2.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.partner-strip__icon--play,
.partner-strip.is-paused .partner-strip__icon--pause {
	display: none;
}

.partner-strip.is-paused .partner-strip__icon--play {
	display: block;
}

@media (prefers-reduced-motion: no-preference) {
	.partner-strip__viewport {
		overflow: hidden;
		mask-image: linear-gradient(to right, transparent, #000 3rem, #000 calc(100% - 3rem), transparent);
	}

	.partner-strip__track {
		flex-wrap: nowrap;
		justify-content: flex-start;
		gap: 0;
		inline-size: max-content;
		animation: cdf-partner-loop var(--motion-duration-loop) linear infinite;
	}

	.partner-strip__item {
		margin-inline-end: var(--space-s);
	}

	.partner-strip__viewport:hover .partner-strip__track,
	.partner-strip.is-paused .partner-strip__track {
		animation-play-state: paused;
	}
}

/* The still form: one copy, wrapped and centred. */
@media (prefers-reduced-motion: reduce) {
	.partner-strip__track {
		flex-wrap: wrap;
		justify-content: center;
		gap: var(--space-s);
	}

	.partner-strip__item.is-copy {
		display: none;
	}
}

/* Half the track is one copy. --inline is 1 in RTL, where the track hangs
   from the right edge, so it moves right; English moves left. */
@keyframes cdf-partner-loop {
	to {
		transform: translateX(calc(var(--inline) * 50%));
	}
}

/* ---- quote sharing: the mission and each objective ----
   A closed <details>, so six quotes add six small toggles rather than six rows
   of buttons, and the control works from the keyboard without a script. */
.quote-share {
	margin-block-start: var(--space-xs);
	font-family: var(--font-body);
	font-size: var(--size-small);
	line-height: var(--line-tight);
}

.quote-share__toggle {
	display: inline-flex;
	align-items: center;
	gap: 0.4em;
	list-style: none;
	cursor: pointer;
	color: var(--color-link);
	font-weight: var(--weight-bold);
}

.quote-share__toggle::-webkit-details-marker {
	display: none;
}

.quote-share__toggle::before {
	content: "\eb21";
	content: "\eb21" / "";
	font-family: 'CDF Icons';
	font-weight: normal;
	font-size: 1.2em;
	line-height: 1;
}

.quote-share__list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-xs);
	margin: var(--space-xs) 0 0;
	padding: 0;
	list-style: none;
}

.quote-share__status {
	margin: var(--space-2xs) 0 0;
	color: var(--color-muted);
}

.quote-share__status:empty {
	display: none;
}


/* ---- field presence and state branches (patterns/about-branches.php) ----
   CDF's section 4. The map is a static SVG (ADR-0005 amendment of
   2026-09-16); the cards reuse .area__figure, so the tiles match the
   programme areas. Without branches.js every card shows as a grid. With it,
   `.is-live` shows the selected card beside the map, and below 56rem a tap on
   the map opens that card over the page. The map ramp is set once in custom
   properties, so the dark block changes values, not rules. */

.branches {
	--branch-absent: color-mix(in srgb, var(--color-primary) 10%, var(--color-base));
	--branch-absent-edge: color-mix(in srgb, var(--color-primary) 22%, transparent);
	--branch-present: color-mix(in srgb, var(--color-primary) 62%, var(--color-base));
	--branch-present-hover: var(--color-primary);
	--branch-present-edge: var(--color-base);
	--branch-border: var(--color-primary);
	--branch-selected: var(--color-primary);
	--branch-control: color-mix(in srgb, var(--color-primary) 12%, transparent);
	--branch-control-hover: color-mix(in srgb, var(--color-primary) 22%, transparent);
	--branch-control-ink: var(--color-primary);
	display: grid;
	gap: var(--space-l);
	align-items: start;
}

@media (min-width: 56rem) {
	.branches {
		grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
	}
}

.branches [hidden] {
	display: none;
}

.branches__map {
	margin: 0;
	padding: var(--space-m);
	border-radius: var(--radius-md);
	background: var(--color-base);
	box-shadow: var(--border-card);
}

/* A map does not mirror in RTL. */
.branch-map__svg {
	display: block;
	inline-size: 100%;
	block-size: auto;
	direction: ltr;
}

.branch-map__state {
	fill: var(--branch-absent);
	stroke: var(--branch-absent-edge);
	stroke-width: 1;
	stroke-linejoin: round;
}

.branch-map__state.is-present {
	fill: var(--branch-present);
	stroke: var(--branch-present-edge);
	stroke-width: 1.2;
	transition: fill var(--motion-duration-fast) var(--motion-ease-standard);
}

.branches.is-live .branch-map__state.is-present {
	cursor: pointer;
}

.branches.is-live .branch-map__state.is-present:hover {
	fill: var(--branch-present-hover);
}

/* The national border (#78), drawn under the states: they cover the inner
   half, so 5 units show outside the country (tools/build-sudan-map.mjs). It
   takes no pointer events, so a click near the edge still reaches the state. */
.branch-map__border {
	fill: none;
	stroke: var(--branch-border);
	stroke-width: 10;
	stroke-linejoin: round;
	stroke-linecap: round;
	pointer-events: none;
}

/* State capital pins (#90). The name shows on hover and on the selected
   state; the cards carry it for the keyboard and screen readers. */
.branch-map__pin-hit {
	fill: transparent;
}

.branch-map__pin-dot {
	fill: var(--color-base);
	stroke: var(--branch-border);
	stroke-width: 3;
}

.branch-map__pin-name {
	visibility: hidden;
	font-family: inherit;
	font-size: 22px;
	font-weight: 700;
	text-anchor: middle;
	fill: var(--color-contrast);
	stroke: var(--color-base);
	stroke-width: 6;
	stroke-linejoin: round;
	paint-order: stroke;
	pointer-events: none;
}

.branches.is-live .branch-map__pin.is-linked {
	cursor: pointer;
}

.branch-map__pin.is-selected .branch-map__pin-dot {
	stroke: var(--color-accent);
}

.branch-map__pin:hover .branch-map__pin-name,
.branch-map__pin.is-selected .branch-map__pin-name {
	visibility: visible;
}

/* The map shrinks to about half its width on a phone. */
@media (max-width: 40rem) {
	.branch-map__pin-name {
		font-size: 36px;
		stroke-width: 9;
	}
}

.branch-map__state.is-present.is-selected {
	fill: var(--branch-selected);
	stroke: var(--color-accent);
	stroke-width: 2.5;
}

.branches__legend {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2xs) var(--space-m);
	margin: var(--space-s) 0 0;
	padding: 0;
	list-style: none;
	font-size: var(--size-small);
	color: var(--color-muted);
}

.branches__key {
	display: inline-flex;
	align-items: center;
	gap: 0.5em;
}

.branches__key::before {
	content: "";
	inline-size: 0.9em;
	block-size: 0.9em;
	border-radius: 2px;
	background: var(--branch-absent);
	box-shadow: inset 0 0 0 1px var(--branch-absent-edge);
}

.branches__key--present::before {
	background: var(--branch-present);
	box-shadow: none;
}

.branches__key--capital::before {
	inline-size: 0.6em;
	block-size: 0.6em;
	border-radius: 50%;
	background: var(--color-base);
	box-shadow: 0 0 0 2px var(--branch-border);
}

.branches__credit {
	margin-block-start: var(--space-2xs);
	font-size: var(--size-tiny);
	color: var(--color-muted);
}

.branches__panel,
.branches__box {
	display: grid;
	gap: var(--space-s);
}

.branches__hint {
	margin: 0;
	font-size: var(--size-small);
	color: var(--color-muted);
}

/* Beside the map, the hint sits on the subtitle's line in the head's bottom
   margin, so the card starts level with the map. The head is held to the map
   column so the two lines never meet (Samir, 2026-09-18). */
@media (min-width: 56rem) {
	.section__head:has(+ .branches) {
		max-inline-size: calc((100% - var(--space-l)) * 7 / 12);
	}

	.branches__panel {
		position: relative;
	}

	.branches__hint {
		position: absolute;
		inset-block-end: calc(100% + var(--space-l));
		inset-inline: 0;
	}
}

.branches__list {
	display: grid;
	gap: var(--space-s);
	grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

.branches.is-live .branches__list {
	grid-template-columns: minmax(0, 1fr);
}

/* The arrows keep one place on every state (Gitea #60): they sit below the
   scroll area, never inside it. About stacks its short cards in one cell, so
   the box is as tall as the tallest card. Programs' cards are long, so there
   the box takes the map's height and the card scrolls. branches.js makes the
   scroll area focusable only while it has something to scroll. */
.branches.is-live .branches__box {
	display: flex;
	flex-direction: column;
}

.branches.is-live .branches__scroll {
	flex: 1 1 auto;
	min-block-size: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-gutter: stable;
	/* Room for the card's 1px ring, which the scroll area would clip. */
	padding: 1px;
	padding-inline-end: var(--space-2xs);
}

.branches.is-live:not(.branches--projects) .branch {
	grid-area: 1 / 1;
}

.branches.is-live:not(.branches--projects) .branch[hidden] {
	display: flex;
	visibility: hidden;
}

@media (min-width: 56rem) {
	/* contain: size takes the panel out of the row's height, so the map sets it. */
	.branches--projects.is-live .branches__panel {
		align-self: stretch;
		contain: size;
		grid-template-rows: minmax(0, 1fr);
	}

	.branches--projects.is-live .branches__sheet {
		display: grid;
		grid-template-rows: minmax(0, 1fr);
	}
}

.branch {
	display: flex;
	flex-direction: column;
	gap: var(--space-3xs);
	padding: var(--space-m);
	border-radius: var(--radius-md);
	border-block-end: var(--rule-weight) solid var(--color-primary);
	background: var(--color-base);
	box-shadow: var(--border-card);
}

/* On the homepage the section sits on the page ground (mockup order, Samir
   2026-09-19), so the map panel and the cards take the surface instead.
   The dark theme already sets both to surface. The empty states go to base,
   because the ramp's 10% mix is almost the surface colour. */
.about-branches--ground :is(.branches__map, .branch) {
	background: var(--color-surface);
}

.about-branches--ground .branches {
	--branch-absent: var(--color-base);
}

.branch__name {
	margin: 0;
	font-size: var(--size-h3);
	color: var(--color-primary);
}

.branch__capital {
	margin: 0;
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
	color: var(--color-muted);
}

.branch__figures {
	grid-template-columns: repeat(auto-fit, minmax(min(8rem, 100%), 1fr));
}

/* Khartoum's two offices on one card (#66): a name over each figure row. */
.branch__office {
	margin: var(--space-s) 0 0;
	font-size: var(--size-body);
	font-weight: var(--weight-bold);
	color: var(--color-contrast);
}

.branch__office + .branch__figures {
	padding-block-start: var(--space-3xs);
}

/* The Programs page projects map (patterns/programs-map.php): the state's
   2025 projects, listed under its count. */
.branch__projects {
	display: grid;
	gap: var(--space-s);
	margin: var(--space-2xs) 0 0;
	padding: 0;
	list-style: none;
}

.branch__project {
	padding-block-start: var(--space-s);
	border-block-start: 1px solid color-mix(in srgb, var(--color-primary) 18%, transparent);
}

/* A library photograph of the project's sector (#74). */
.branch__project-photo {
	display: block;
	inline-size: 100%;
	block-size: auto;
	aspect-ratio: 16 / 9;
	object-fit: cover;
	border-radius: var(--radius-md);
	margin-block-end: var(--space-xs);
}

.branch__project-title {
	margin: 0;
	font-weight: var(--weight-bold);
	line-height: var(--line-tight);
}

.branch__project-meta {
	margin: var(--space-3xs) 0 0;
	font-size: var(--size-small);
	color: var(--color-muted);
}

.branches__nav {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s);
}

.branches__count {
	font-size: var(--size-small);
	color: var(--color-muted);
	font-variant-numeric: tabular-nums;
}

.branches__step,
.branches__close {
	display: inline-grid;
	place-items: center;
	inline-size: 2.75rem;
	block-size: 2.75rem;
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm);
	background: var(--branch-control);
	color: var(--branch-control-ink);
	font: inherit;
	cursor: pointer;
	transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
}

.branches__step:hover,
.branches__close:hover {
	background: var(--branch-control-hover);
}

.branches__step::before,
.branches__close::before {
	display: inline-block;
	font-family: 'CDF Icons';
	font-size: 1.4rem;
	line-height: 1;
}

/* One glyph per button, flipped by the --inline sign rather than forked on
   [dir]: "next" points left in Arabic and right in English. */
.branches__step--next::before { content: "\ea60"; content: "\ea60" / ""; transform: scaleX(var(--inline)); }
.branches__step--prev::before { content: "\ea61"; content: "\ea61" / ""; transform: scaleX(var(--inline)); }
.branches__close::before { content: "\eb55"; content: "\eb55" / ""; }

/* The box is a flex column whenever the close button can show. */
.branches__close {
	align-self: end;
}

@media (max-width: 55.99rem) {
	.branches__sheet.is-open {
		position: fixed;
		inset: 0;
		z-index: 60;
		display: grid;
		place-items: center;
		padding: var(--layout-gutter);
		background: color-mix(in srgb, var(--color-contrast) 60%, transparent);
	}

	.branches__sheet.is-open .branches__box {
		inline-size: 100%;
		max-inline-size: 26rem;
		max-block-size: 88svh;
		padding: var(--space-s);
		border-radius: var(--radius-md);
		background: var(--color-base);
		box-shadow: var(--shadow-3);
	}

	.branches__sheet.is-open .branch {
		box-shadow: none;
	}

	.about-branches--ground .branches__sheet.is-open .branch {
		background: var(--color-base);
	}

	/* A fixed height, or the centred sheet moves the arrows on each state. */
	.branches--projects .branches__sheet.is-open .branches__box {
		block-size: 88svh;
	}
}

/* ============================================================================
   Reduced motion, and print because funders print pages
   ============================================================================ */

/* Every selector that declares a transition outside a motion query belongs
   here. The list held four of seven until 2026-08-14, so the card and the
   section tile still animated for a reader who had asked for none, and a
   partial list reads exactly like a complete one. tools/check-css-collisions.mjs
   now fails when the two disagree (meeting record, blind spot B3). */
@media (prefers-reduced-motion: reduce) {
	.card,
	.card::after,
	.card__media img,
	.area,
	.area__media img,
	.header__nav .cdf-lang-badge__bubble,
	.link-sweep,
	.wp-block-query-pagination a.page-numbers,
	.wp-block-query-pagination-previous,
	.wp-block-query-pagination-next,
	.post-nav a,
	.branch-map__state.is-present,
	.branches__step,
	.branches__close {
		transition: none;
	}

	/* A hover lift is motion, like the button's (motion.css). The shadow
	   still rises, so the hover still shows. */
	.card:hover,
	.area:hover {
		transform: none;
	}
}

@media print {
	.header,
	.footer,
	.skip-link {
		display: none;
	}

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

/* ============================================================================
   The theme switcher's own control (ADR-0013 decision 5, ported by ADR-0017)
   ============================================================================

   Ported from dist/assets/css/base.css:1646-1694. It sits outside the
   navigation, so it survives the mobile overlay; see patterns/header.php. */

.theme-toggle {
	display: none;
	place-items: center;
	inline-size: 2.15rem;
	block-size: 2.15rem;
	flex: none;
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm);
	background: transparent;
	color: inherit;
	opacity: 0.7;
	cursor: pointer;
	box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 30%, transparent);
	transition: opacity var(--motion-duration-fast) var(--motion-ease-standard);
}

/* Shown only once the boot script has stamped a theme. With JavaScript off the
   control would do nothing, so it is not offered. */
html[data-theme] .theme-toggle {
	display: grid;
}

.theme-toggle:hover {
	opacity: 1;
	background: color-mix(in srgb, currentColor 10%, transparent);
}

.theme-toggle svg {
	grid-area: 1 / 1;
	inline-size: 1.05rem;
	block-size: 1.05rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.75;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* The glyph names the ACTION, like the label does: moon = click for dark. Kept
   in CSS off the painted theme, so it cannot drift and cannot flash. The JS
   owns the label text and this owns the glyph. */
html[data-theme='light'] .theme-toggle [data-icon='sun'],
html[data-theme='dark'] .theme-toggle [data-icon='moon'] {
	display: none;
}

@media (prefers-reduced-motion: reduce) {
	.theme-toggle {
		transition: none;
	}
}

/* ============================================================================
   Back-to-top button and the Programs sector menu (Samir, 2026-09-18)
   ============================================================================

   Both float over the page, below the header's layer so the mobile menu
   covers them. Both come in with JavaScript only: theme.js shows the button
   once the reader is a screen down a page taller than two screens, and
   sectors.js shows the menu while a sector is on screen. Both are round
   badges in the Programs page's colours, and each names itself in a label on
   hover and on keyboard focus. */

.float-tip {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	pointer-events: none;
}

:is(:hover, :focus-visible) > .float-tip {
	inline-size: auto;
	block-size: auto;
	overflow: visible;
	clip-path: none;
	padding: var(--space-3xs) var(--space-2xs);
	border-radius: var(--radius-sm);
	background: var(--color-contrast);
	color: var(--color-base);
	font-family: var(--font-body);
	font-size: var(--size-small);
	font-weight: var(--weight-bold);
	line-height: var(--line-tight);
}

.to-top,
.sector-nav {
	position: fixed;
	z-index: 15;
	transition:
		opacity var(--motion-duration-base) var(--motion-ease-standard),
		visibility var(--motion-duration-base) var(--motion-ease-standard);
}

/* They rest at elevation 2 and hover to 3. */
.to-top {
	transition:
		opacity var(--motion-duration-base) var(--motion-ease-standard),
		visibility var(--motion-duration-base) var(--motion-ease-standard),
		transform var(--motion-duration-fast) var(--motion-ease-standard),
		box-shadow var(--motion-duration-fast) var(--motion-ease-standard);
}

.sector-nav a {
	transition:
		transform var(--motion-duration-fast) var(--motion-ease-standard),
		box-shadow var(--motion-duration-fast) var(--motion-ease-standard);
}

.to-top:hover {
	box-shadow: var(--shadow-3);
}

@media (prefers-reduced-motion: no-preference) {
	.to-top:hover {
		transform: translateY(calc(var(--lift-2) - var(--lift-3)));
	}
}

.to-top:not([data-shown]),
.sector-nav:not([data-shown]) {
	visibility: hidden;
	opacity: 0;
}

.to-top,
.sector-nav a {
	display: grid;
	place-items: center;
	inline-size: 2.75rem;
	block-size: 2.75rem;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: var(--color-primary);
	color: var(--color-on-brand);
	box-shadow: var(--shadow-2);
	text-decoration: none;
	cursor: pointer;
}

.to-top:hover,
.sector-nav a:hover,
.sector-nav a[aria-current] {
	background: var(--color-accent);
	color: var(--color-on-accent);
}

/* ---- back to top: the bottom corner on the end side ---- */
.to-top {
	inset-block-end: var(--space-m);
	inset-inline-end: var(--layout-gutter);
}

.to-top[hidden] {
	display: none;
}

.to-top svg {
	grid-area: 1 / 1;
	inline-size: 1.35rem;
	block-size: 1.35rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.to-top .float-tip {
	inset-inline-end: calc(100% + var(--space-2xs));
	inset-block-start: 50%;
	translate: 0 -50%;
}

/* ---- the sector menu ----
   The icon is the one the sector's heading and list item carry (the three
   rules under "the sector icons" name this menu too). */
.sector-nav__list {
	display: flex;
	gap: var(--space-2xs);
	margin: 0;
	padding: 0;
	list-style: none;
}

.sector-nav a {
	position: relative;
}

.sector-nav a::before {
	font-family: 'CDF Icons';
	font-size: 1.35rem;
	font-weight: normal;
	line-height: 1;
}

/* A column on the start side where the margin beside the content holds it:
   1344px leaves 72px beside the 1200px content, and the column needs 60. */
@media (min-width: 84rem) {
	.sector-nav {
		inset-inline-start: var(--space-s);
		inset-block-start: calc(var(--header-block-size) + (100vh - var(--header-block-size)) / 2);
		translate: 0 -50%;
	}

	.sector-nav__list {
		flex-direction: column;
	}

	.sector-nav a:hover {
		box-shadow: var(--shadow-3);
	}

	.sector-nav .float-tip {
		inset-inline-start: calc(100% + var(--space-2xs));
		inset-block-start: 50%;
		translate: 0 -50%;
	}
}

/* Narrower than that, a bar along the bottom of the screen. Its height is
   kept out of the jump targets by the scroll-padding, and the back-to-top
   button moves up above it. */
@media (max-width: 83.99rem) {
	:root {
		--sector-bar-size: calc(2.5rem + 2 * var(--space-2xs));
	}

	.sector-nav {
		inset-inline: 0;
		inset-block-end: 0;
		padding-block: var(--space-2xs);
		padding-block-end: calc(var(--space-2xs) + env(safe-area-inset-bottom));
		background: var(--field-wash);
		box-shadow: 0 -1px 0 color-mix(in srgb, var(--color-contrast) 15%, transparent);
	}

	.sector-nav__list {
		justify-content: space-between;
		inline-size: min(100% - (var(--layout-gutter) * 2), 30rem);
		margin-inline: auto;
	}

	.sector-nav a {
		inline-size: 2.5rem;
		block-size: 2.5rem;
		box-shadow: none;
	}

	/* Above the icon, centred on it. --inline turns the one translate into
	   the right direction for both languages. The two end labels align with
	   their icon's outer edge, so neither runs off the screen. */
	.sector-nav .float-tip {
		inset-block-end: calc(100% + var(--space-2xs));
		inset-inline-start: 50%;
		translate: calc(50% * var(--inline)) 0;
	}

	.sector-nav__item:first-child .float-tip {
		inset-inline-start: 0;
		translate: none;
	}

	.sector-nav__item:last-child .float-tip {
		inset-inline-start: auto;
		inset-inline-end: 0;
		translate: none;
	}

	html:has(.sector-nav[data-shown]) {
		scroll-padding-block-end: calc(var(--sector-bar-size) + var(--space-3xs));
	}

	html:has(.sector-nav[data-shown]) .to-top {
		inset-block-end: calc(var(--sector-bar-size) + var(--space-s));
	}
}

@media (min-width: 84rem) and (prefers-reduced-motion: no-preference) {
	.sector-nav a:hover {
		transform: translateY(calc(var(--lift-2) - var(--lift-3)));
	}
}

@media (prefers-reduced-motion: reduce) {
	.to-top,
	.sector-nav,
	.sector-nav a {
		transition: none;
	}
}

/* ============================================================================
   The dark theme, component half (ADR-0017 decision 3)
   ============================================================================

   Ported from dist/assets/css/direction-b.css:521-794, minus the sixteen map
   and state-card rules, which land with the projects section. Selectors are
   rewritten against block markup: the mockup's .btn--ghost is the theme's
   .is-style-ghost .wp-block-button__link, and so on. Nothing here is new
   design; the reasoning for each choice is in the mockup file beside the rule.

   THE RULE THAT GOVERNS THIS WHOLE BLOCK (ADR-0013 decision 4): in the dark
   theme --color-primary is a FIELD colour and never ink. It is 1.78:1 on the
   dark ground. Anything that inks in primary in the light theme is re-inked
   here, and that includes borders, which no gate in this repo reads. */

/* ---- hero: a solid brand field rather than a pale wash ---- */
html[data-theme='dark'] .hero {
	background: var(--color-primary);
	color: var(--color-on-brand);
}

html[data-theme='dark'] .hero__title {
	color: var(--color-on-brand);
}

html[data-theme='dark'] .hero__title::after {
	background: var(--color-accent);
}

html[data-theme='dark'] .hero__sub {
	color: var(--color-on-brand);
	opacity: 0.9;
}

/* ---- buttons ----
   A primary fill is 1.9:1 on the ground and invisible on the primary hero, so
   the primary button inverts to a white fill with blue lettering, 9.5:1 either
   way. Not the archive's yellow fill: the page already carries two yellow
   anchor blocks and every section rule in yellow, and a third yellow shape
   that is also clickable makes the accent stop meaning anything. Yellow
   structures, white acts, and --color-cta still means give. */
html[data-theme='dark'] .btn:not(.btn--ghost):not(.btn--give),
html[data-theme='dark'] .wp-block-button:not(.is-style-ghost):not(.is-style-give) .wp-block-button__link {
	background: var(--color-on-brand);
	color: var(--color-primary);
}

html[data-theme='dark'] .btn:not(.btn--ghost):not(.btn--give):hover,
html[data-theme='dark'] .wp-block-button:not(.is-style-ghost):not(.is-style-give) .wp-block-button__link:hover {
	background: var(--color-on-brand);
	filter: brightness(0.9);
}

html[data-theme='dark'] .is-style-ghost .wp-block-button__link,
html[data-theme='dark'] .hero .is-style-ghost .wp-block-button__link {
	color: var(--color-contrast);
}

html[data-theme='dark'] .hero .is-style-ghost .wp-block-button__link:hover {
	background: var(--color-on-brand);
	color: var(--color-primary);
}

/* ---- header ----
   --field-wash needs no fork: it is primary mixed 6% into the ground, which
   over a dark ground is a slightly bluer dark rather than a pale one. The ink
   on it does. */
html[data-theme='dark'] .header__brand .header__wordmark__ar,
html[data-theme='dark'] .header__nav .wp-block-navigation__responsive-container-open,
html[data-theme='dark'] .header__nav .wp-block-navigation__responsive-container-close {
	color: var(--color-contrast);
}

html[data-theme='dark'] .header__nav a:not(.btn):not(.wp-block-button__link):hover {
	color: var(--color-accent);
}

html[data-theme='dark'] .header__nav [aria-current='page'] {
	box-shadow: inset 0 -3px 0 var(--color-accent);
}

/* The News filter (inventory #24): the header's tab marks, and its edges,
   which rendered-contrast.mjs does not read. Primary is never ink here, so
   the ghost button takes the dark .btn's inks. */
html[data-theme='dark'] .news-tabs__link:hover,
html[data-theme='dark'] .news-open__item:hover::after {
	color: var(--color-accent);
}

html[data-theme='dark'] .news-tabs__link[aria-current] {
	box-shadow: inset 0 calc(-1 * var(--rule-weight)) 0 var(--color-accent);
}

html[data-theme='dark'] .news-tabs {
	box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--color-on-brand) 20%, transparent);
}

html[data-theme='dark'] .news-refine__select select,
html[data-theme='dark'] .news-refine__search input {
	border-color: color-mix(in srgb, var(--color-on-brand) 30%, transparent);
}

html[data-theme='dark'] .news-refine__submit {
	color: var(--color-on-brand);
}

html[data-theme='dark'] .news-refine__submit:hover {
	background: var(--color-on-brand);
	color: var(--color-primary);
}

/* The overlay's hierarchy marks (RN6): the chevron is an icon, inked like the
   close button; the guide rule is a structural mark. */
html[data-theme='dark'] .header .header__nav .is-menu-open .has-child > .wp-block-navigation-item__content::after {
	color: var(--color-contrast);
}

html[data-theme='dark'] .header .header__nav.wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open .has-child .wp-block-navigation__submenu-container {
	border-inline-start-color: var(--color-accent);
}

/* The language badge label, one of the three ink declarations the mockup never
   had because the mockup has no Polylang switcher. */
html[data-theme='dark'] .header__nav .wp-block-polylang-navigation-language-switcher.lang-item a.wp-block-navigation-item__content {
	color: var(--color-contrast);
}

/* ---- section headings ---- */
html[data-theme='dark'] .section__head::before {
	background: var(--color-accent);
}

html[data-theme='dark'] .section__head h2,
html[data-theme='dark'] .involve h2,
html[data-theme='dark'] .area__name {
	color: var(--color-contrast);
}

/* ---- cards ----
   A card at --color-base IS the page ground here, so it stops being a card.
   --color-surface is the raised value, and the 1px primary edge goes with it:
   primary is 1.4:1 against that surface. */
html[data-theme='dark'] .card {
	background: var(--color-surface);
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-on-brand) 14%, transparent);
}

html[data-theme='dark'] .card::after {
	background: var(--color-accent);
}

html[data-theme='dark'] .area {
	border-block-end-color: var(--color-accent);
}

/* Elevation 2 on hover. The shadow barely shows on this ground, so the card
   also gets lighter: the level's raise, mixed into its own resting ground. */
html[data-theme='dark'] .card:hover {
	background: color-mix(in srgb, var(--color-on-brand) var(--raise-2), var(--color-surface));
}

html[data-theme='dark'] .area:hover {
	background: color-mix(in srgb, var(--color-on-brand) var(--raise-2), var(--color-base));
}

/* A blue disc on a blue card is 1.4:1. This is a badge carrying a glyph, not a
   button, which is exactly what the accent slot is for. */
html[data-theme='dark'] .area__badge {
	background: var(--color-accent);
	color: var(--color-on-accent);
}

/* The figure chips keep the client's primary fill and gain an edge they did
   not need on a white card: without it three chips read as one blurred band. */
html[data-theme='dark'] .area__figure {
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-on-brand) 16%, transparent);
}

/* ---- the numbers band ----
   primary-deep is 1.28:1 against this ground, which in the light theme would
   be a bug and here is the effect: the field recedes almost to the page and
   the yellow numerals sit on it at 10.4:1. It still owes an edge, or the
   sections above and below it run together. */
html[data-theme='dark'] .band--figures {
	border-block: 1px solid color-mix(in srgb, var(--color-on-brand) 12%, transparent);
}

/* ---- the eight cases no gate here reads (ADR-0017 decision 3) ----

   rendered-contrast.mjs resolves text nodes and SVG fills. It reads no
   border-color, box-shadow or outline, so these would ship present, invisible
   and green. The third ink declaration and the five edges. */

/* Found by ./cdf.sh contrast on the news archive, in both languages, after the
   port: 1.48:1. The pagination tiles and the story links copy the old filter
   chip and carry its primary ink on the same surface, so they share one rule. */
html[data-theme='dark'] .wp-block-query-pagination a.page-numbers,
html[data-theme='dark'] .wp-block-query-pagination-previous,
html[data-theme='dark'] .wp-block-query-pagination-next,
html[data-theme='dark'] .post-nav a {
	color: var(--color-contrast);
}

/* style.css:462, the sticky header's bottom rule. */
html[data-theme='dark'] .header {
	box-shadow: inset 0 calc(-1 * var(--rule-weight)) 0 color-mix(in srgb, var(--color-on-brand) 20%, transparent);
}

/* style.css:1315, the chip outline, and the pagination tiles that copy it. */
html[data-theme='dark'] .wp-block-query-pagination a.page-numbers,
html[data-theme='dark'] .wp-block-query-pagination-previous,
html[data-theme='dark'] .wp-block-query-pagination-next,
html[data-theme='dark'] .post-nav a {
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-on-brand) 30%, transparent);
}

/* style.css:1348, a structural rule. */
html[data-theme='dark'] .footer__bottom {
	border-color: color-mix(in srgb, var(--color-on-brand) 20%, transparent);
}

/* The give routes, which the mockup never had, so nothing about them was
   inherited. Found by looking rather than by a gate: the panel is
   --color-base, which IS the page ground here, and its top rule and hairline
   are both primary, so the card stopped being a card and kept a rule nobody
   could see. It takes .area's grammar rather than .card's, because that is the
   panel the client reviewed on 2026-07-30: it stays on the ground and the rule
   that separates it goes yellow. */
html[data-theme='dark'] .give-route {
	border-block-start-color: var(--color-accent);
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-on-brand) 14%, transparent);
}

/* style.css:1638, the dashed placeholder edge on content CDF has not sent. */
html[data-theme='dark'] .story__awaiting,
html[data-theme='dark'] .give-route__awaiting {
	border-color: color-mix(in srgb, var(--color-on-brand) 30%, transparent);
}

/* The About page (3.8). Every primary ink on it is re-inked here, in the
   grammar the page already uses elsewhere: headings and numerals go to
   contrast (.area__name), the icon badges go to the accent fill with
   on-accent ink (.area__badge), the heavy rule goes yellow (.give-route), and
   the hairlines become an on-brand mix. Found by Samir looking at the page on
   2026-09-16, after the light theme alone had been checked. */
html[data-theme='dark'] .about-statement > h2,
html[data-theme='dark'] .about-values > li,
html[data-theme='dark'] .about-objectives-title,
html[data-theme='dark'] .about-objectives > li::before,
html[data-theme='dark'] .about-objectives strong {
	color: var(--color-contrast);
}

html[data-theme='dark'] .about-statement > h2::before,
html[data-theme='dark'] .about-objectives-title::before {
	background: var(--color-accent);
	color: var(--color-on-accent);
}

html[data-theme='dark'] .about-statement {
	border-block-start-color: var(--color-accent);
}

/* The programs page, in the same grammar: primary ink goes to contrast, the
   icon badges to the accent fill. */
html[data-theme='dark'] .programs-index a,
html[data-theme='dark'] .program-sector > h2,
html[data-theme='dark'] .program-area > summary {
	color: var(--color-contrast);
}

html[data-theme='dark'] .programs-index a::before,
html[data-theme='dark'] .program-sector > h2::before {
	background: var(--color-accent);
	color: var(--color-on-accent);
}

html[data-theme='dark'] .programs-index a:hover {
	background: var(--color-on-brand);
	color: var(--color-primary);
}

/* The two floating controls follow the badges: accent at rest, the light
   disc when hovered or current. */
html[data-theme='dark'] .to-top,
html[data-theme='dark'] .sector-nav a {
	background: var(--color-accent);
	color: var(--color-on-accent);
}

html[data-theme='dark'] .to-top:hover,
html[data-theme='dark'] .sector-nav a:hover,
html[data-theme='dark'] .sector-nav a[aria-current] {
	background: var(--color-on-brand);
	color: var(--color-primary);
}

html[data-theme='dark'] .program-sector:nth-child(odd of .program-sector) .program-area {
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-on-brand) 14%, transparent);
}

/* The mark was drawn for a white ground. on-brand as a fill has one precedent,
   the dark primary button's white fill above, and this is the same move: a
   light disc the mark can sit on, on the dark page. */
html[data-theme='dark'] .about-profile__mark {
	background: var(--color-on-brand);
}

html[data-theme='dark'] .about-objectives > li,
html[data-theme='dark'] .about-values > li {
	border-block-start-color: color-mix(in srgb, var(--color-on-brand) 14%, transparent);
}

html[data-theme='dark'] .about-share {
	border-block-start-color: color-mix(in srgb, var(--color-on-brand) 14%, transparent);
}

html[data-theme='dark'] .partner__logo,
html[data-theme='dark'] .partner-strip__item {
	border-color: color-mix(in srgb, var(--color-on-brand) 14%, transparent);
}

html[data-theme='dark'] .partner-strip__toggle {
	color: var(--color-contrast);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-on-brand) 30%, transparent);
}

html[data-theme='dark'] .contact-location__title,
html[data-theme='dark'] .contact-form__title,
html[data-theme='dark'] .contact-location__item::before {
	color: var(--color-contrast);
}

html[data-theme='dark'] .about-contact__item {
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-on-brand) 14%, transparent);
}

html[data-theme='dark'] .about-contact__item::before {
	background: color-mix(in srgb, var(--color-on-brand) 8%, transparent);
	color: var(--color-contrast);
}

html[data-theme='dark'] .card__media--empty::before {
	color: var(--color-contrast);
}

/* Reports & Research: primary ink re-inks to contrast. */
html[data-theme='dark'] .reports-tab,
html[data-theme='dark'] .report__title {
	color: var(--color-contrast);
}

html[data-theme='dark'] .reports-tab:hover,
html[data-theme='dark'] .reports-tab[aria-pressed='true'] {
	background: var(--color-on-brand);
	color: var(--color-primary);
}

html[data-theme='dark'] .report__cover:empty::before {
	color: var(--color-contrast);
}

html[data-theme='dark'] .about-share__link,
html[data-theme='dark'] .quote-share__toggle {
	color: var(--color-contrast);
}

html[data-theme='dark'] .about-share__link:hover {
	background: var(--color-on-brand);
	color: var(--color-primary);
}

/* ---- field presence and state branches ----
   The map ramp moves from primary to link, because primary does not re-light
   and a navy state on a navy card disappears. Same ramp as the dark mockup
   map, where each step was measured (dist/assets/css/direction-b.css). */
html[data-theme='dark'] .branches {
	--branch-absent: color-mix(in srgb, var(--color-link) 24%, var(--color-base));
	--branch-absent-edge: color-mix(in srgb, var(--color-link) 55%, transparent);
	--branch-present: color-mix(in srgb, var(--color-link) 56%, var(--color-base));
	--branch-present-hover: color-mix(in srgb, var(--color-link) 68%, var(--color-base));
	--branch-present-edge: var(--color-surface);
	--branch-border: var(--color-link);
	--branch-selected: color-mix(in srgb, var(--color-link) 56%, var(--color-base));
	--branch-control: color-mix(in srgb, var(--color-link) 16%, transparent);
	--branch-control-hover: color-mix(in srgb, var(--color-link) 28%, transparent);
	--branch-control-ink: var(--color-link);
}

html[data-theme='dark'] .branches__map,
html[data-theme='dark'] .branch,
html[data-theme='dark'] .branches__sheet.is-open .branches__box {
	background: var(--color-surface);
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-on-brand) 14%, transparent);
}

/* The phone sheet is elevation 3. The card inside takes the same ground, or
   it reads as a hole in the sheet. */
html[data-theme='dark'] .branches__sheet.is-open :is(.branches__box, .branch) {
	background: color-mix(in srgb, var(--color-on-brand) var(--raise-3), var(--color-surface));
}

html[data-theme='dark'] .branch {
	border-block-end-color: var(--color-accent);
}

html[data-theme='dark'] .branch__name {
	color: var(--color-contrast);
}

html[data-theme='dark'] .branches__sheet.is-open {
	background: color-mix(in srgb, var(--color-base) 80%, transparent);
}
