/* ==========================================================================
   Al-Azhar Academy — base.css
   Reset, typography, layout primitives, site chrome (header/footer),
   buttons and form controls. Tokens come from theme.css.
   ========================================================================== */

/* -------------------------------------------------------------------------
   Reset
   ------------------------------------------------------------------------- */
*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	-webkit-text-size-adjust: 100%;
	text-size-adjust: 100%;
}

body {
	margin: 0;
	background-color: var(--bg);
	color: var(--text);
	font-family: var(--font-sans);
	font-size: var(--fs-400);
	font-weight: 400;
	line-height: var(--lh-normal);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* Script display for the top two levels, the rounded face for the rest. Both
   are effectively single-weight faces, so a `font-weight: 600` on the script
   would only buy a synthesised — and visibly smeared — bold. */
h1,
h2 {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 400;
	line-height: var(--lh-tight);
	letter-spacing: var(--tracking-tight);
	text-wrap: balance;
}

h3,
h4 {
	margin: 0;
	font-family: var(--font-heading);
	font-weight: 600;
	line-height: var(--lh-snug);
	letter-spacing: var(--tracking-heading);
	text-wrap: balance;
}

h1 {
	font-size: var(--fs-900);
}

h2 {
	font-size: var(--fs-800);
}

h3 {
	font-size: var(--fs-700);
	line-height: var(--lh-snug);
}

h4 {
	font-size: var(--fs-500);
	line-height: var(--lh-snug);
}

p {
	margin: 0;
	text-wrap: pretty;
}

/* Every link takes the hue of the section it sits in, and the underline fills
   in on hover instead of the colour shifting — that keeps the contrast that
   was measured for body copy. */
a {
	color: var(--tone);
	text-decoration-thickness: 1px;
	text-decoration-color: var(--tone-edge);
	text-underline-offset: 2px;
}

a:hover {
	text-decoration-color: currentColor;
}

ul,
ol {
	margin: 0;
	padding: 0;
}

img,
svg,
video {
	display: block;
	max-width: 100%;
}

img {
	height: auto;
}

button,
input,
select,
textarea {
	font: inherit;
	color: inherit;
}

strong,
b {
	font-weight: 600;
}

/* Visible, unanimated focus for keyboard users. */
:focus-visible {
	outline: 3px solid var(--tone);
	outline-offset: 2px;
	border-radius: var(--radius-xs);
}

:focus:not(:focus-visible) {
	outline: none;
}

.site-footer :focus-visible {
	outline-color: var(--joy-sun-fill);
}

/* Anchor targets sit below the sticky header. */
[id] {
	scroll-margin-top: calc(var(--header-h) + var(--space-5));
}

/* -------------------------------------------------------------------------
   Layout primitives
   ------------------------------------------------------------------------- */
.container {
	width: 100%;
	max-width: calc(var(--container) + var(--gutter) * 2);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.container--narrow {
	max-width: calc(var(--container-narrow) + var(--gutter) * 2);
}

.section {
	padding-block: var(--section-y);
}

.section--tight {
	padding-block: clamp(2.5rem, 5vw, 4rem);
}

.section--sunken {
	background-color: var(--surface-sunken);
}

/* The alternating band. It takes the section's hue and dissolves into the paper
   at both edges, so two colours never meet on a hard line — and it has no
   border, because a border is a hard line by definition. The fade is a share of
   the band's own height, and the section's own padding is deeper than the fade,
   so no content ever sits on a half-arrived tint. */
.section--surface {
	background-image: linear-gradient(
		to bottom,
		var(--bg) 0,
		var(--tone-soft) var(--band-fade),
		var(--tone-soft) calc(100% - var(--band-fade)),
		var(--bg) 100%
	);
}

.grid {
	display: grid;
	gap: var(--space-6);
}

.grid--2 {
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
}

.grid--3 {
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.grid--4 {
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 13.5rem), 1fr));
}

.split {
	display: grid;
	gap: clamp(2rem, 5vw, 4rem);
	align-items: center;
}

@media (min-width: 64em) {
	.split {
		grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
	}

	.split--start {
		align-items: start;
	}
}

.stack {
	display: flex;
	flex-direction: column;
	gap: var(--stack-gap, var(--space-4));
}

.stack--sm {
	--stack-gap: var(--space-2);
}

.stack--lg {
	--stack-gap: var(--space-8);
}

.cluster {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3);
	list-style: none;
}

.measure {
	max-width: var(--measure);
}

.measure--narrow {
	max-width: var(--measure-narrow);
}

/* Spacing utilities, used sparingly for one-off gaps between siblings. */
.mt-4 {
	margin-top: var(--space-4);
}

.mt-6 {
	margin-top: var(--space-6);
}

.mt-8 {
	margin-top: var(--space-8);
}

/* -------------------------------------------------------------------------
   Shared text & helpers
   ------------------------------------------------------------------------- */
.lede {
	font-size: var(--fs-450);
	line-height: var(--lh-relaxed);
	color: var(--text-muted);
}

.muted {
	color: var(--text-muted);
}

.subtle {
	color: var(--text-subtle);
}

.small {
	font-size: var(--fs-300);
}

.eyebrow {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font-family: var(--font-label);
	font-size: var(--fs-100);
	font-weight: 500;
	letter-spacing: var(--tracking-label);
	text-transform: uppercase;
	color: var(--tone);
}

.eyebrow::before {
	content: '';
	width: 1.75rem;
	height: 3px;
	border-radius: var(--radius-pill);
	background-color: var(--tone-fill);
}

.section-head {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	margin-bottom: var(--space-10);
}

/* Text the school must replace before launch. */
.placeholder {
	border-bottom: 1px dashed var(--border-strong);
	color: var(--text-subtle);
}

.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.skip-link {
	position: fixed;
	top: var(--space-3);
	left: var(--space-3);
	z-index: var(--z-skip);
	transform: translateY(-200%);
	padding: var(--space-3) var(--space-5);
	background-color: var(--white);
	border: 2px solid var(--accent-600);
	border-radius: var(--radius-pill);
	font-weight: 600;
	color: var(--ink-900);
	text-decoration: none;
}

.skip-link:focus {
	transform: none;
}

/* -------------------------------------------------------------------------
   Site header
   -------------------------------------------------------------------------
   A frosted pane, not a bar. The page keeps its colour and its type moving
   underneath the header, and the blur keeps the links legible over whatever
   lands there.

   `--header-glass` is repointed rather than re-declared, so the glass layer
   (`.site-header::before`, see below) holds the only `background-color` and
   the three states are just different values for it:
     · no `backdrop-filter` support  → the opaque paper colour
     · at the top of the page        → the lighter wash
     · once the page has scrolled    → the firmer wash, plus the joy stripe
   ------------------------------------------------------------------------- */
.site-header {
	--header-glass: var(--header-bg-solid);

	position: sticky;
	top: 0;
	z-index: var(--z-header);
	border-bottom: 1px solid transparent;
}

/* The glass is a pseudo-element, and that is a structural decision, not a
   stylistic one. Any element that carries `backdrop-filter` becomes the
   containing block for its absolute and fixed descendants — and this header
   contains the mobile navigation sheet, which is `position: fixed; bottom: 0`.
   With the filter on the header itself the sheet is measured against a
   4.5rem-tall bar instead of the viewport, so it opens off the top of the
   screen and the menu button looks broken. On the pseudo it blurs exactly the
   same paper and type behind the bar, and nothing is trapped. (A test in
   test/site.spec.js fails if a `backdrop-filter` moves back onto `.site-header`
   itself.) */
.site-header::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-color: var(--header-glass);
	/* The six-hue wash, so the bar is the top of the paintbox rather than a
	   tinted grey. It sits on the glass layer and therefore over the blur,
	   never on the header element itself (see the note above). */
	background-image: var(--header-wash);
}

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.site-header {
		--header-glass: var(--header-bg);

		border-bottom-color: var(--header-border);
	}

	.site-header::before {
		backdrop-filter: blur(var(--header-blur)) saturate(var(--header-saturate));
		-webkit-backdrop-filter: blur(var(--header-blur)) saturate(var(--header-saturate));
	}

	.site-header.is-condensed {
		--header-glass: var(--header-bg-strong);
	}
}

/* The blur is the whole point, so it stays until the visitor tells the system
   they would rather have opacity: a translucent bar over busy content is a
   real legibility problem for some people, and a real contrast problem for
   others. Both queries fall back to the solid paper colour. (Only Safari
   supports `prefers-reduced-transparency` today; the others ignore it, which
   is correct — they simply keep the frosted header.) */
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
	.site-header,
	.site-header.is-condensed {
		--header-glass: var(--header-bg-solid);
		/* The wash is the first thing to go: someone who has asked for more
		   contrast wants the paper, not six faint hues over it. */
		--header-wash: none;
	}

	.site-header::before {
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
	}
}

/* The header's colour signature: the six-hue stripe. It is on from the first
   pixel — the top of a page is the paintbox lid, not a grey bar — and it firms
   up as the page scrolls past the first screen. */
.site-header::after {
	content: '';
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	height: 3px;
	pointer-events: none;
	background-image: var(--joy-stripe);
	opacity: 0.5;
}

.site-header.is-condensed::after {
	opacity: 1;
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	min-height: var(--header-h);
}

/* Zero-height marker at the very top of the document. Watching it with an
   IntersectionObserver tells us when the header should condense — no scroll
   listener required. */
.header-sentinel {
	position: absolute;
	top: 0;
	left: 0;
	width: 1px;
	height: 1px;
}

.brand {
	display: inline-flex;
	align-items: center;
	gap: var(--space-3);
	min-height: var(--tap-target);
	color: var(--text);
	text-decoration: none;
}

/* Flat blue underneath, a two-stop plum → berry gradient on top (see the
   header block in theme.css); the flat colour is only ever the fallback. */
.brand__mark {
	display: grid;
	place-items: center;
	width: 2.5rem;
	height: 2.5rem;
	border-radius: var(--radius-md);
	background-color: var(--accent-600);
	color: var(--white);
}

.brand__mark svg {
	width: 1.15rem;
	height: 1.15rem;
}

.brand__text {
	display: flex;
	flex-direction: column;
}

/* The wordmark is set in the rounded face — the childlike end of the type
   system — at 600, which is a real weight of DynaPuff rather than a
   synthesised one. (Story Script, the slanted script, still sets h1 and h2.) */
.brand__name {
	font-family: var(--font-heading);
	font-size: clamp(1.25rem, 1.05rem + 0.5vw, 1.4rem);
	font-weight: 600;
	line-height: 1.05;
	letter-spacing: 0;
	/* The rounded face is wider than the script it replaced: the lockup is one
	   line or it is not a lockup. */
	white-space: nowrap;
}

.brand__tag {
	font-family: var(--font-label);
	font-size: 0.625rem;
	letter-spacing: var(--tracking-label);
	text-transform: uppercase;
	color: var(--text-subtle);
}

/* -------------------------------------------------------------------------
   Navigation
   ------------------------------------------------------------------------- */
.nav__list {
	position: relative;
	display: flex;
	list-style: none;
}

/* The bouncy pill.
   `.nav__rail` is an empty list item that exists only to carry the geometry
   the pill animates: JS writes --pill-x (how far along the rail it sits) and
   --pill-w (how wide it should be) on the rail, and the pill inherits both.
   It is display:none below the navigation breakpoint, where the nav becomes a
   bottom sheet and a sliding indicator would be meaningless. */
.nav__rail {
	display: none;
}

/* The pill lives in the rail the desktop bar lays out: once the bar is in place
   from 62em, the rail has geometry to measure against. */
@media (min-width: 62em) {
	.nav__rail {
		position: absolute;
		inset-block: 0;
		left: 0;
		display: block;
		width: 0;
		list-style: none;
	}
}

.nav__pill {
	position: absolute;
	inset-block: 0.15rem;
	left: 0;
	z-index: 0;
	width: var(--pill-w, 5.5rem);
	border-radius: var(--radius-pill);
	background-color: var(--tone-soft);
	box-shadow: inset 0 0 0 1px var(--tone-edge);
	transform: translateX(var(--pill-x, 0px));
	opacity: 0;
	pointer-events: none;
}

/* JS sets data-pill="ready" once it has measured the first link, so the pill
   never flashes in the wrong place. */
.nav__rail[data-pill='ready'] .nav__pill {
	opacity: 1;
}

.nav__link {
	position: relative;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	min-height: var(--tap-target);
	/* The marker face, and the hue the rotation in theme.css dealt this
	   destination — the same hue the pill takes when it parks under it.
	   The face ships one weight, so nothing here asks for a bold. */
	font-family: var(--font-label);
	font-weight: 400;
	letter-spacing: 0.005em;
	/* "Parent Hub" is one destination, not two words on two lines. */
	white-space: nowrap;
	color: var(--tone);
	text-decoration: none;
}

.nav__meta {
	display: none;
}

/* The lid's handle. A berry chip rather than a grey circle, so the one control
   that opens the menu reads as part of the paintbox: the hues come from the
   header block in theme.css, the hover state from animations.css. */
.nav-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--tap-target);
	height: var(--tap-target);
	padding: 0;
	background-color: var(--tone-soft);
	border: 1px solid var(--tone-edge);
	border-radius: var(--radius-md);
	color: var(--tone);
	cursor: pointer;
}

.nav-toggle svg {
	width: 1.25rem;
	height: 1.25rem;
}

.nav-toggle__close,
.nav-toggle[aria-expanded='true'] .nav-toggle__open {
	display: none;
}

.nav-toggle[aria-expanded='true'] .nav-toggle__close {
	display: block;
}

.nav-backdrop {
	position: fixed;
	inset: 0;
	z-index: var(--z-backdrop);
	background-color: var(--scrim);
	opacity: 0;
}

.nav-backdrop.is-open {
	opacity: 1;
}

body.is-nav-open {
	overflow: hidden;
}

/* -------------------------------------------------------------------------
   Header tools — the menu button, plus whatever small actions sit beside it.
   There is no light/dark switch: this site ships exactly one theme.
   ------------------------------------------------------------------------- */
.site-header__tools {
	display: flex;
	flex: none;
	align-items: center;
	gap: var(--space-2);
}

/* The primary action the bar keeps on a phone, so the thing parents actually do
   next is one tap away without opening the menu. On the desktop bar both
   actions line up inside the navigation itself (`.nav__actions`, below); below
   the breakpoint the sheet carries the remaining one, so this cluster only ever
   exists while the bar is a phone's bar. */
.site-header__actions {
	display: flex;
	flex: none;
	align-items: center;
	gap: var(--space-2);
}

/* --- Mobile & tablet: bottom sheet -------------------------------------
   The navigation breakpoint. The bar is the shape with room for eight
   destinations, the lockup and the two actions on one line — but only once the
   viewport can hold them, so below 62em the sheet takes over: the same markup,
   in the shape that has the room. The bar's type and padding step down with the
   viewport (see the clamp() values in the desktop block below), which is what
   lets it arrive at 62em — 992px, so a 1024px screen with its scrollbar still
   gets the bar — instead of waiting for 1280px.
   Held in step with `MOBILE`/`DESKTOP` in main.js and the two sheet queries in
   animations.css — change all five together. */
@media (max-width: 61.99em) {
	.nav {
		position: fixed;
		inset-inline: 0;
		bottom: 0;
		z-index: var(--z-nav);
		display: flex;
		flex-direction: column;
		gap: var(--space-4);
		max-height: 86vh;
		max-height: 86dvh;
		overflow-y: auto;
		overscroll-behavior: contain;
		padding: var(--space-6) var(--gutter) calc(var(--space-6) + env(safe-area-inset-bottom));
		background-color: var(--surface);
		/* The sheet is a paintbox: a six-hue edge along the top, then the pastel
		   wash of the joy set behind the list. Background layers paint in order
		   and do not scroll with the content, so the stripe stays welded to the
		   top edge while a long list scrolls underneath it. */
		background-image: var(--joy-stripe),
			linear-gradient(to bottom, var(--joy-rose-soft) 0%, var(--surface) 34%, var(--surface) 62%, var(--joy-sky-soft) 100%);
		background-repeat: no-repeat;
		background-size: 100% 4px, 100% 100%;
		background-position: top left;
		border-radius: var(--radius-xl) var(--radius-xl) 0 0;
		transform: translateY(101%);
		opacity: 0;
		visibility: hidden;
	}

	.nav.is-open {
		transform: none;
		opacity: 1;
		visibility: visible;
	}

	.nav__list {
		flex-direction: column;
		gap: var(--space-1);
	}

	/* Each destination takes the hue its list item was dealt by the rotation in
	   theme.css, so the sheet opens as six colours rather than as one list
	   repeated eight times: a pastel tile, a hairline, a dot and a label in the
	   hue, set in the marker face at the size it wants. */
	.nav__link {
		width: 100%;
		gap: var(--space-3);
		padding: var(--space-3) var(--space-4);
		border: 1px solid var(--tone-edge);
		border-radius: var(--radius-lg);
		background-color: var(--tone-soft);
		color: var(--tone);
		font-size: var(--fs-500);
		font-weight: 400;
	}

	.nav__link::before {
		content: '';
		flex: none;
		width: 0.65rem;
		height: 0.65rem;
		border-radius: 50%;
		background-color: var(--tone-fill);
	}

	/* The page you are already on keeps the AA-safe tint and gains a doubled
	   ring, so it reads as pressed rather than as another tile. The hue's
	   saturated step would be too light to carry this label in the ink. */
	.nav__link[aria-current='page'] {
		border-color: var(--tone-fill);
		background-color: var(--tone-soft);
		box-shadow: inset 0 0 0 2px var(--tone-fill);
		color: var(--tone);
		font-weight: 400;
	}

	.nav__meta {
		display: block;
		padding-top: var(--space-4);
		border-top: 1px solid var(--border);
		font-size: var(--fs-300);
		color: var(--text-muted);
	}

	.nav__actions {
		display: grid;
		gap: var(--space-3);
	}

	/* The bar above already offers the primary action — one tap for the thing
	   parents actually do next — so the sheet holds the remaining one rather
	   than repeating it. */
	.nav__actions .btn--primary {
		display: none;
	}
}

/* --- Phones: room in the bar for one action -----------------------------
   Measured, like the masthead below: at 360px the bar has 320px once the
   gutters are taken, and the lockup (238px), the primary action (101px), the
   menu button (44px) and the two gaps (24px) want 407px together. Something
   has to give, and the wordmark is the only one of the three that can: with the
   name writable onto two lines, the bar's floor is the longest word in it —
   about 314px — rather than the whole lockup, so a 360px phone keeps the action
   at full size and the type steps exactly where the rest of the site puts them,
   instead of a 12px wordmark bought with the last few pixels.
   The floor is the point: flexbox shrinks the lockup only as far as its longest
   word, so no name is ever broken mid-word, and from 30em up the name never
   wraps at all (there is always room for it). */
@media (max-width: 29.99em) {
	.brand__name {
		white-space: normal;
	}
}

/* Below 22em — a 320px phone, or the outer screen of a folding one — even the
   stacked lockup and the menu button leave no room for the action, so it goes
   back into the sheet and the bar is the two things it was before: the lockup
   and the menu button. (Those screens overflow today, with nothing else in the
   bar; this is what gives them their width back.) */
@media (max-width: 21.99em) {
	.site-header__actions {
		display: none;
	}

	.nav__actions .btn--primary {
		display: inline-flex;
	}
}

/* --- Desktop ------------------------------------------------------------ */
@media (min-width: 62em) {
	/* The tools cluster is a phone's cluster — both of the things inside it are
	   hidden here — and the whole cluster goes with them: an empty flex item
	   still takes a line of its own in the masthead below, which would add a
	   16px gap and a third line to the bar for nothing. */
	.nav-toggle,
	.nav-backdrop,
	.site-header__actions,
	.site-header__tools {
		display: none;
	}

	.nav {
		display: flex;
		align-items: center;
		gap: var(--space-4);
	}

	.nav__list {
		align-items: center;
		gap: var(--space-1);
	}

	.nav__link {
		/* Tighter than it looks, because the row shares its width with the
	   lockup and two actions; this is the size and the padding the bar
	   carries from 80em up. Below 70em the bar takes a second line rather
	   than smaller labels — see the masthead block below — and between
	   the two breakpoints the row closes up a step instead: see the
	   one-row block after it. */
		padding: var(--space-2);
		border-radius: var(--radius-pill);
		font-size: var(--fs-300);
		font-weight: 400;
	}

	/* The current page is marked twice on desktop: the pill parks under it in
	   its own hue (main.js carries the hue across as it measures) and a short
	   rule sits under the word, the way a marker would underline it. The face
	   has a single weight, so the label cannot lean on a bold to stand out. */
	.nav__link[aria-current='page']::after {
		content: '';
		position: absolute;
		inset-inline: var(--space-2);
		bottom: 0.5rem;
		height: 3px;
		border-radius: var(--radius-pill);
		background-color: var(--tone-fill);
	}

	.nav__actions {
		display: flex;
		align-items: center;
		gap: var(--space-2);
	}
}

/* --- Mid desktop: two lines, not one (62em – 69.99em) -------------------
   Measured rather than guessed: eight destinations in the marker face, the
   lockup and the two actions want 1140px on one line, and the container only
   holds what the viewport leaves once its two gutters are taken — 912px at a
   992px viewport, 1040px at 1120px. Squeezing one row into that would take
   the labels under 13px, smaller than a parent should have to read to find a
   page, so between 62em and 70em the bar becomes a masthead instead: the
   lockup on the first line, and the navigation with both of its actions on a
   second line of its own. The lines share the container, so it still reads as
   one bar, and nothing hides behind a menu. From 70em the row has room for
   all of it again and the two lines fold back into one — see the one-row
   block below — and the bar's own height changes with the fold, so
   `--header-h` is repointed below. */
@media (min-width: 62em) and (max-width: 69.99em) {
	:root {
		/* Two lines, so every piece of maths that measures the bar has to know:
		   `[id]` anchors and the bar's own min-height both read this. 117px is
		   what the two lines come to on a real render (44px of lockup row, the
		   16px gap, and a 57px navigation row), and pinning it exactly is what
		   keeps the bar the same height across 62em–70em: a token even a pixel
		   taller would make flexbox stretch the two lines and land the type on
		   half pixels. */
		--header-h: 7.3125rem;
	}

	.site-header__inner {
		flex-wrap: wrap;
	}

	.nav {
		/* A line of its own, spanning the container, with the destinations on
		   the left and the two actions on the right. */
		flex: 1 0 100%;
		justify-content: space-between;
		padding-top: var(--space-3);
		border-top: 1px solid var(--border);
	}
}

/* --- One row (70em and up) ----------------------------------------------
   The shape the bar is for: the lockup, the eight destinations and both
   actions on one line. What it costs is what the numbers above say — the row
   wants 1140px and the container only has 1040px at 70em — so between here
   and 80em it closes up instead of overrunning: every value below is its
   compact size at 70em and its full size at 80em, which is where the row
   lands on exactly the numbers the desktop block above sets. The compact end
   is the floor: 13px labels, a 20px wordmark, 4px of air around a label.
   Under that the row stops being something a parent can scan, which is why
   the masthead above covers 62em–70em rather than this block starting at the
   navigation breakpoint. Each clamp() is written so its minimum lands at 70em
   and its maximum at 80em; move the breakpoint and the constants move with
   it. */
@media (min-width: 70em) {
	/* A floor of 8px between the lockup and the navigation, 16px at 80em,
	   for the case where the row has nothing left to give. */
	.site-header__inner {
		gap: clamp(0.5rem, 5vw - 3rem, 1rem);
	}

	.nav {
		/* The air around a label, and the inset the current-page rule
		   beneath it uses, so the two always agree. */
		--nav-link-pad: clamp(0.25rem, 2.5vw - 1.5rem, 0.5rem);

		/* 12px between the destinations and the two actions at 70em, 16px at
		   80em. At the compact end this is the pair the eye reads as one run
		   when it is left at the phone bar's 8px. */
		gap: clamp(0.75rem, 5vw - 3rem, 1rem);
	}

	.nav__link {
		padding: var(--nav-link-pad);
		font-size: clamp(0.8125rem, 0.625vw + 0.375rem, 0.875rem);
	}

	.nav__link[aria-current='page']::after {
		inset-inline: var(--nav-link-pad);
	}

	/* The wordmark gives up the most of any single piece: 20px at 70em, its
	   full 22.4px at 80em. */
	.brand__name {
		font-size: clamp(1.25rem, 1.5vw + 0.2rem, 1.4rem);
	}

	/* The two actions keep their 13px labels and lose some cushioning: 12px
	   of side padding at 70em, the full 16px at 80em. */
	.nav__actions .btn--sm {
		padding-inline: clamp(0.75rem, 2.5vw - 1rem, 1rem);
	}
}

/* -------------------------------------------------------------------------
   Footer
   ------------------------------------------------------------------------- */
/* The deep end of the page, and it does not cut on. The paper deepens into it
   over --edge-ramp, so the last section and the footer meet on a fade rather
   than on a line. (The padding is far deeper than the ramp, so no footer
   content ever sits on a half-arrived surface.) */
.site-footer {
	position: relative;
	background-image: linear-gradient(to bottom, var(--bg) 0, var(--surface-inverse) var(--edge-ramp));
	color: var(--text-muted);
	padding-block: var(--space-16) var(--space-8);
	font-size: var(--fs-300);
}

/* The mirror of the header stripe: the page is bookended in the same six hues
   it used in between. It rides the ramp and dissolves at both edges, so it
   glows out of the paper and into the deeper sheet instead of drawing two more
   hard lines 6px apart. */
.site-footer::before {
	content: '';
	position: absolute;
	inset-inline: 0;
	top: 0;
	height: var(--edge-ramp);
	pointer-events: none;
	background-image: var(--joy-stripe);
	-webkit-mask-image: var(--stripe-dissolve);
	mask-image: var(--stripe-dissolve);
}

.footer-grid {
	display: grid;
	gap: var(--space-10);
	grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 40em) {
	.footer-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 64em) {
	.footer-grid {
		grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
		gap: var(--space-8);
	}
}

.footer-grid > * {
	min-width: 0;
}

.site-footer .brand__name {
	color: var(--text);
}

.site-footer .brand__tag {
	color: var(--text-subtle);
}

/* The one place Yellowtail is used. A script face at 12px uppercase would be
   unreadable, so it steps up to 1.55rem and keeps its natural case. */
.footer__heading {
	margin-bottom: var(--space-4);
	font-family: var(--font-script);
	font-size: 1.55rem;
	font-weight: 400;
	line-height: 1.1;
	letter-spacing: 0.01em;
	color: var(--tone);
}

.footer__heading::after {
	content: '';
	display: block;
	width: 2.25rem;
	height: 3px;
	margin-top: var(--space-2);
	border-radius: var(--radius-pill);
	background-color: var(--tone-fill);
}

.footer__list {
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
	list-style: none;
}

/* Each column carries the hue the rotation in theme.css gave it, so the
   footer closes the page in four colours instead of four columns of grey. */
.footer__link {
	display: inline-flex;
	align-items: center;
	min-height: 2rem;
	color: var(--tone);
	text-decoration: none;
}

.footer__bottom {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	margin-top: var(--space-12);
	padding-top: var(--space-6);
	border-top: 1px solid var(--border-inverse);
}

.footer__legal {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-4);
	list-style: none;
}

/* -------------------------------------------------------------------------
   Buttons
   ------------------------------------------------------------------------- */
.btn {
	position: relative;
	isolation: isolate;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	min-height: var(--tap-target);
	padding: 0.75rem 1.35rem;
	border: 1px solid transparent;
	border-radius: var(--radius-pill);
	font-family: var(--font-heading);
	font-size: var(--fs-300);
	font-weight: 600;
	line-height: 1;
	text-align: center;
	text-decoration: none;
	white-space: nowrap;
	cursor: pointer;
}

/* No shadow layer, no lift. The button takes the section's hue, so the primary
   action on a sky-blue band is sky-blue, and on a rose band it is rose. */
.btn--primary {
	background-color: var(--tone-fill);
	border-color: var(--tone-fill);
	color: var(--white);
}

.btn--secondary {
	background-color: var(--surface);
	border-color: var(--tone-edge);
	color: var(--tone);
}

/* The closing band is a sheet of the site's own paper, so the quiet action on
   it is the quiet button it is everywhere else: a white surface, the section's
   hue on the border, and the hue in the label. */
.btn--inverse {
	background-color: var(--surface);
	border-color: var(--tone-edge);
	color: var(--tone);
}

.btn--sm {
	min-height: 2.5rem;
	padding: 0.5rem 1rem;
	font-size: var(--fs-200);
}

.btn--lg {
	min-height: 3.25rem;
	padding: 1rem 1.85rem;
	font-size: var(--fs-400);
}

.btn[disabled],
.btn[aria-disabled='true'] {
	opacity: 0.6;
	cursor: not-allowed;
}

.btn-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3);
}

.link-arrow {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font-weight: 600;
	text-decoration: none;
}

.link-arrow__icon {
	width: 1rem;
	height: 1rem;
	flex: none;
}

/* -------------------------------------------------------------------------
   Forms
   ------------------------------------------------------------------------- */
.form {
	display: grid;
	gap: var(--space-5);
}

.form-grid {
	display: grid;
	gap: var(--space-5);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

.field {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	min-width: 0;
}

.field--full {
	grid-column: 1 / -1;
}

.field__label {
	font-family: var(--font-label);
	font-size: var(--fs-300);
	font-weight: 400;
	letter-spacing: 0.01em;
	color: var(--text);
}

.field__optional {
	font-family: var(--font-sans);
	color: var(--text-subtle);
}

.field__hint {
	font-size: var(--fs-200);
	color: var(--text-subtle);
}

/* A plain bordered box. The old layered hairline ring is gone; the border is
   the border. */
.input,
.select,
.textarea {
	width: 100%;
	min-height: var(--tap-target);
	padding: 0.7rem 0.9rem;
	color: var(--text);
	font-size: var(--fs-400);
	border: 1px solid var(--border-strong);
	border-radius: var(--radius-sm);
	background-color: var(--surface);
}

.input:hover,
.textarea:hover,
.select:hover {
	border-color: var(--tone);
}

.input:focus,
.select:focus,
.textarea:focus {
	border-color: var(--tone);
	box-shadow: 0 0 0 4px var(--tone-soft);
}

.input::placeholder,
.textarea::placeholder {
	color: var(--ink-400);
}

.textarea {
	min-height: 8rem;
	resize: vertical;
	line-height: var(--lh-normal);
}

/* A picker should look like a picker. It keeps the section's hue in its tint,
   its border, its label and its chevron, so it reads as the one control on a
   form that opens something instead of as a grey default box. */
.select {
	appearance: none;
	padding-right: 2.75rem;
	border-color: var(--tone-edge);
	background-color: var(--tone-soft);
	color: var(--tone);
	font-weight: 600;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236a5b46' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.95rem center;
	background-size: 1.1rem 1.1rem;
}

/* The field opens up on focus, so the focus ring reads against the paper
   rather than against the tint the field starts on. */
.select:focus {
	background-color: var(--surface);
}

/* The prompt row ("Choose a time") is not a value: it stays in muted ink until
   a real choice is made, and the field takes the hue once it is. */
.select:has(option[value='']:checked) {
	color: var(--text-muted);
	font-weight: 400;
}

/* The opened list. Chrome, Edge and Firefox paint these styles; Safari keeps
   its own wheel on a phone, which is the right control there. */
.select option {
	background-color: var(--surface);
	color: var(--text);
	font-family: var(--font-sans);
	font-size: var(--fs-400);
}

.select option:checked {
	background-color: var(--tone-soft);
	color: var(--tone);
	font-weight: 600;
}

.select optgroup {
	background-color: var(--surface);
	color: var(--tone);
	font-weight: 600;
}

/* Chrome 135+ and Edge can render the popup themselves. Where they can, it
   gets the same card treatment as every other surface on the site: a hairline
   in the section's hue, the corner the field has, and a checkmark in the hue.
   A browser without `base-select` simply keeps the native list styled above. */
@supports (appearance: base-select) {
	.select,
	.select::picker(select) {
		appearance: base-select;
	}

	.select {
		/* The control draws its own chevron from here on. */
		background-image: none;
	}

	.select::picker(select) {
		margin-block: var(--space-2);
		padding: var(--space-2);
		border: 1px solid var(--tone-edge);
		border-radius: var(--radius-md);
		background-color: var(--surface);
	}

	.select::picker-icon {
		margin-inline-start: var(--space-2);
		color: var(--tone);
	}

	.select option {
		padding: 0.55rem 0.7rem;
		border-radius: var(--radius-xs);
	}

	.select option:hover,
	.select option:focus-visible {
		background-color: var(--tone-soft);
		color: var(--tone);
	}

	.select option::checkmark {
		margin-inline-start: auto;
		color: var(--tone);
	}
}

[aria-invalid='true'] {
	border-color: var(--danger);
	background-color: var(--danger-bg);
}

.checkbox {
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	font-size: var(--fs-300);
	line-height: var(--lh-normal);
	color: var(--text-muted);
}

.checkbox input {
	width: 1.15rem;
	height: 1.15rem;
	margin-top: 0.15rem;
	flex: none;
	accent-color: var(--accent-600);
}

.checkbox--consent {
	grid-column: 1 / -1;
}

/* Bot trap: kept out of the accessibility tree and out of the tab order. */
.hp {
	position: absolute;
	left: -10000px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* Form feedback, styled as a notification strip: a tone bar on the left and a
   slide-in (see animations.css). No shadow. */
.form-status {
	position: relative;
	overflow: hidden;
	font-size: var(--fs-300);
	border-radius: var(--radius-md);
}

.form-status:empty {
	display: none;
}

.form-status[data-tone='success'],
.form-status[data-tone='error'] {
	padding: var(--space-4) var(--space-5) var(--space-4) var(--space-6);
}

.form-status[data-tone='success'] {
	background-color: var(--success-bg);
	color: var(--success);
}

.form-status[data-tone='error'] {
	background-color: var(--danger-bg);
	color: var(--danger);
}

.form-status[data-tone='success']::before,
.form-status[data-tone='error']::before {
	content: '';
	position: absolute;
	inset-block: 0;
	left: 0;
	width: 4px;
	background-color: currentColor;
	opacity: 0.7;
}
