/*
Theme Name: Long Row
Theme URI: https://www.longrow.derbyshire.sch.uk/
Author: Lead IT Services
Description: Block theme for Long Row Primary & Nursery School, Belper. Implements the Long Row Primary Design System: navy identity, red action, three poppy accents, Fredoka/Nunito, everything rounded, motion short and physical. Colours, typography and spacing are driven by theme.json; the editor palette is locked so the site cannot drift off-brand.
Requires at least: 6.5
Tested up to: 7.0
Requires PHP: 8.2
Version: 0.11.3
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: longrow
Tags: block-theme, education, accessibility-ready, full-site-editing
*/

/* -------------------------------------------------------------------------
   Everything expressible in theme.json lives in theme.json. This file holds
   what it cannot describe: focus behaviour, motion, shape utilities, and the
   handful of layout rules the block editor does not generate.

   Token names mirror the design system so the two can be read side by side:
   --sp-*, --r-*, --dur-*, --ease-* are aliases onto the theme.json presets.
   ------------------------------------------------------------------------- */

:root {
	/* Spacing aliases — DS 4px scale. */
	--sp-1: 4px;
	--sp-2: 8px;
	--sp-3: 12px;
	--sp-4: 16px;
	--sp-5: 20px;
	--sp-6: 24px;
	--sp-8: 32px;
	--sp-10: 40px;
	--sp-12: 48px;
	--sp-16: 64px;
	--sp-20: 80px;
	--sp-24: 96px;

	--gutter: var(--wp--custom--gutter);
	--section-y: var(--wp--custom--section--y);
	--section-y-sm: var(--wp--custom--section--y-sm);

	/* Shape */
	--r-xs: var(--wp--custom--radius--xs);
	--r-sm: var(--wp--custom--radius--sm);
	--r-md: var(--wp--custom--radius--md);
	--r-lg: var(--wp--custom--radius--lg);
	--r-xl: var(--wp--custom--radius--xl);
	--r-pill: var(--wp--custom--radius--pill);
	--bw: var(--wp--custom--border--width);

	/* Motion */
	--dur-fast: var(--wp--custom--motion--fast);
	--dur: var(--wp--custom--motion--base);
	--dur-slow: var(--wp--custom--motion--slow);
	--dur-scene: var(--wp--custom--motion--scene);
	--ease-out: var(--wp--custom--motion--ease-out);
	--ease-pop: var(--wp--custom--motion--ease-pop);
	--hover-lift: var(--wp--custom--motion--hover-lift);
	--press-scale: var(--wp--custom--motion--press-scale);

	/* Shadows, navy-tinted — never grey or black. */
	--shadow-sm: var(--wp--preset--shadow--sm);
	--shadow-card: var(--wp--preset--shadow--card);
	--shadow-lift: var(--wp--preset--shadow--lift);
	--shadow-pop: var(--wp--preset--shadow--pop);
}

@media (max-width: 48rem) {
	:root {
		--section-y: var(--wp--custom--section--y-sm);
	}
}

/* Every duration collapses under reduced motion — DS rule, applied at the
   token level so no individual animation has to remember. */
@media (prefers-reduced-motion: reduce) {
	:root {
		--dur-fast: 1ms;
		--dur: 1ms;
		--dur-slow: 1ms;
		--dur-scene: 1ms;
		--hover-lift: none;
	}
}


/* === Skip link =========================================================== */

.longrow-skip-link {
	position: absolute;
	left: -9999px;
	z-index: 999999;
	padding: 12px 20px;
	border-radius: var(--r-pill);
	background: var(--wp--preset--color--navy);
	color: var(--wp--preset--color--paper);
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	text-decoration: none;
}

.longrow-skip-link:focus {
	left: 1rem;
	top: 1rem;
}


/* === Focus ===============================================================
   The design system specifies a sun-yellow ring with a white inner gap.
   Measured, that fails: sun-500 against white is 1.75:1, under the 3:1 that
   WCAG 2.2 SC 1.4.11 requires of a focus indicator.

   Fixed by making the OUTER ring navy on light grounds (15.7:1) and sun on
   dark grounds (9.0:1), keeping the DS's white inner gap in both cases. The
   look is preserved; the failure is not.
   ========================================================================= */

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: none;
	box-shadow:
		0 0 0 3px var(--wp--preset--color--paper),
		0 0 0 6px var(--wp--preset--color--navy);
	border-radius: var(--r-xs);
}

:where(
	.has-navy-background-color,
	.has-navy-deep-background-color,
	.has-navy-mid-background-color,
	.has-red-background-color,
	.has-sky-background-color,
	.has-leaf-background-color,
	.longrow-band--navy,
	.longrow-band--navy-deep,
	.longrow-band--red,
	.longrow-hero
) :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	box-shadow:
		0 0 0 3px rgba(8, 21, 58, 0.9),
		0 0 0 6px var(--wp--preset--color--sun);
}


/* === Section rhythm ======================================================
   theme.json's global blockGap would otherwise put a margin between the
   top-level header / main / footer blocks, which shows as a seam between
   two coloured surfaces.
   ========================================================================= */

.wp-site-blocks > header,
.wp-site-blocks > main,
.wp-site-blocks > footer {
	margin-block: 0;
}

.wp-block-post-content > .longrow-band,
.wp-block-post-content > .longrow-hero,
.longrow-band + .longrow-band,
.longrow-hero + .longrow-band {
	margin-block: 0;
}


/* === Shape utilities =====================================================
   Registered as block styles in functions.php, so editors pick them from the
   Styles panel rather than typing class names.
   ========================================================================= */

/* Photographs carry NO resting shadow — they sit flat against the page and
   only lift on hover, so a photo grid reads as pictures rather than as cards.
   DS: Cards. */
.wp-block-image img,
.wp-block-gallery img {
	border-radius: var(--r-lg);
}

.is-style-longrow-pill img,
img.is-style-longrow-pill {
	border-radius: var(--r-pill);
	aspect-ratio: 3 / 2;
	object-fit: cover;
	inline-size: 100%;
}

.is-style-longrow-circle img,
img.is-style-longrow-circle {
	border-radius: 50%;
	aspect-ratio: 1;
	object-fit: cover;
	inline-size: 100%;
}

/* Photo hover: the picture zooms inside a fixed frame — the frame never
   moves, so nothing around it reflows. */
.wp-block-image,
.wp-block-gallery .wp-block-image {
	overflow: hidden;
	border-radius: var(--r-lg);
}

@media (prefers-reduced-motion: no-preference) {
	.wp-block-image img,
	.wp-block-gallery .wp-block-image img {
		transition: transform var(--dur-slow) var(--ease-out);
	}

	.wp-block-image:hover img,
	.wp-block-gallery .wp-block-image:hover img {
		transform: scale(1.06);
	}
}

/* Separators are hairlines, not rules. */
.wp-block-separator.is-style-longrow-hairline.is-style-longrow-hairline {
	border: 0;
	border-top: var(--bw) solid var(--wp--preset--color--line);
	opacity: 1;
	height: 0;
	width: 100%;
}


/* === Motion ==============================================================
   CSS only, no JavaScript animation library. Entrances use the DS keyframes;
   interface transitions are 200ms ease-out. Only transform and opacity are
   animated, so nothing here can cause layout shift.
   ========================================================================= */

@keyframes lr-pop-in {
	from { opacity: 0; transform: translateY(14px) scale(0.96); }
	to   { opacity: 1; transform: none; }
}

@keyframes lr-fade-up {
	from { opacity: 0; transform: translateY(18px); }
	to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: no-preference) {

	/* Cards and buttons lift and deepen their shadow. */
	.longrow-lift {
		transition:
			transform var(--dur) var(--ease-out),
			box-shadow var(--dur) var(--ease-out);
	}

	.longrow-lift:hover,
	.longrow-lift:focus-within {
		transform: var(--hover-lift);
		box-shadow: var(--shadow-lift);
	}

	/* Press: scale, never colour alone. */
	.longrow-lift:active,
	.wp-element-button:active {
		transform: var(--press-scale);
	}

	/* Scroll entrances via native scroll-driven animation — no observer, no JS. */
	@supports (animation-timeline: view()) {
		.longrow-reveal {
			animation: lr-fade-up linear both;
			animation-timeline: view();
			animation-range: entry 8% cover 26%;
		}

		.longrow-stagger > * {
			animation: lr-pop-in var(--dur-slow) var(--ease-pop) both;
			animation-timeline: view();
			animation-range: entry 5% cover 22%;
		}

		.longrow-stagger > :nth-child(2) { animation-delay: 60ms; }
		.longrow-stagger > :nth-child(3) { animation-delay: 120ms; }
		.longrow-stagger > :nth-child(4) { animation-delay: 180ms; }
		.longrow-stagger > :nth-child(5) { animation-delay: 240ms; }
		.longrow-stagger > :nth-child(n+6) { animation-delay: 300ms; }
	}
}

.wp-element-button {
	transition:
		background-color var(--dur) var(--ease-out),
		transform var(--dur-fast) var(--ease-out),
		box-shadow var(--dur) var(--ease-out);
}


/* === Editor-proofing =====================================================
   Long unbroken strings (pasted URLs, long policy filenames) must not push a
   coloured surface wider than the viewport.
   ========================================================================= */

.wp-block-heading,
.wp-block-paragraph,
.wp-block-list li {
	overflow-wrap: break-word;
}

.wp-block-table {
	overflow-x: auto;
}

.wp-block-table table {
	border-collapse: collapse;
}

.wp-block-table td,
.wp-block-table th {
	border: 1px solid var(--wp--preset--color--line);
	padding: var(--sp-3);
}

.wp-block-table th {
	background: var(--wp--preset--color--surface-alt);
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	text-align: left;
}

/* A row header is nearly always a name or a date — the thing the row is
   about. Left to wrap it broke "Mrs L. White" over two lines while the cell
   beside it held a single short word, which reads as though the name is the
   long value. The container scrolls and is keyboard-reachable, so holding
   these on one line costs nothing. */
.wp-block-table th[scope="row"] {
	white-space: nowrap;
	vertical-align: top;
}

/* Header cells sit above the first line of a tall cell rather than floating
   in the middle of it. */
.wp-block-table thead th {
	vertical-align: bottom;
}

/* Identity: the column-header row wears the school navy rather than the
   generic pale blue, so every table on the site opens with the brand.
   Row headers (th[scope="row"]) keep the quiet surface-alt above — only the
   top row speaks. Paper on navy (#fff on #0d1f52) is 15.7:1, well past AA. */
.wp-block-table thead th {
	background: var(--wp--preset--color--navy);
	color: var(--wp--preset--color--paper);
	border-color: var(--wp--preset--color--navy);
}


/* === Lead paragraph ======================================================
   The cheapest de-walling there is. An interior page that opens with eight
   identical paragraphs gives the eye nowhere to start; setting the first one
   larger creates an entry point and costs no markup and no editing.

   Scoped to the first paragraph of a page's content, and only when it is
   genuinely the opening — :first-child, so a paragraph following a heading or
   an image is left alone.
   ========================================================================= */

.wp-block-post-content > p:first-child {
	font-size: var(--wp--preset--font-size--large);
	line-height: var(--wp--custom--line-height--snug);
	color: var(--wp--preset--color--navy);
	max-inline-size: 46ch;

	/* Identity: a red rule down the opening paragraph's left edge, echoing
	   the passform error's shape without its meaning. Decorative — the text
	   stays navy on white (15.7:1). */
	border-inline-start: var(--wp--custom--border--thick) solid var(--wp--preset--color--red);
	padding-inline-start: var(--sp-4);
}


/* === School-identity accents =============================================
   The school asked that the red and blue be "used a little more to make
   certain sections stand out". A little more, not a rebrand: a few quiet,
   repeated marks — the red rule under section headings, the red edge on the
   lead paragraph above, red-spined quotes, and navy table-header rows —
   plus the red top edge on the header dropdowns. All decorative rules or
   AA-passing pairings.
   ========================================================================= */

/* A short red rule beneath content section headings. Scoped to headings in
   the page's own ink (:not(.has-text-color)) so the white headings on navy
   bands — where a red mark this dark would muddy — are left alone. */
.wp-block-post-content h2.wp-block-heading:not(.has-text-color)::after {
	content: "";
	display: block;
	inline-size: var(--sp-12);
	block-size: var(--sp-1);
	margin-block-start: var(--sp-2);
	border-radius: var(--r-pill);
	background: var(--wp--preset--color--red);
}

/* Red bullet markers on content lists — the smallest possible repeat of the
   brand mark, and it gives a policy page's long lists a scannable left rail.
   Unordered lists only: an ordered list's numbers are content, and stay ink.
   The marker sits on paper (#d21f28 on #ffffff is 5.30:1, past AA even
   though a decorative glyph carries no requirement); list text unchanged. */
.wp-block-post-content ul.wp-block-list li::marker {
	color: var(--wp--preset--color--red);
}

/* Quotes carry the brand pairing: red spine, navy voice. Both sit on white —
   navy text at 15.7:1, the muted citation unchanged. */
.wp-block-quote {
	margin-inline: 0;
	padding: var(--sp-2) var(--sp-6);
	border-inline-start: var(--wp--custom--border--thick) solid var(--wp--preset--color--red);
	color: var(--wp--preset--color--navy);
}

.wp-block-quote cite {
	color: var(--wp--preset--color--ink-muted);
	font-style: normal;
}


/* === Password form =======================================================
   Shown in place of a protected page's content. Built to look like a part of
   the site rather than a system error, because for most of the people who
   meet it nothing has gone wrong.
   ========================================================================= */

.longrow-passform {
	max-inline-size: 32rem;
	margin-block: var(--sp-8);
	padding: var(--sp-8);
	border: var(--bw) solid var(--wp--preset--color--line);
	border-radius: var(--r-lg);
	background: var(--wp--preset--color--surface-alt);
}

.longrow-passform__heading {
	margin: 0 0 var(--sp-3);
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-large);
	color: var(--wp--preset--color--navy);
}

.longrow-passform__intro {
	margin: 0 0 var(--sp-5);
	color: var(--wp--preset--color--ink-muted);
}

/* Red on the pale blue surface is 6.4:1. The message is not colour alone —
   it is a sentence, and it carries role="alert" so it is announced. */
.longrow-passform__error {
	margin: 0 0 var(--sp-5);
	padding: var(--sp-3) var(--sp-4);
	border-inline-start: var(--wp--custom--border--thick) solid var(--wp--preset--color--red);
	border-radius: var(--r-xs);
	background: var(--wp--preset--color--red-tint);
	color: var(--wp--preset--color--red-deep);
	font-weight: 600;
}

.longrow-passform__label {
	display: block;
	margin-block-end: var(--sp-2);
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	color: var(--wp--preset--color--navy);
}

.longrow-passform__row {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-3);
}

.longrow-passform__input {
	flex: 1 1 12rem;
	min-block-size: 48px;
	padding: var(--sp-2) var(--sp-3);
	border: var(--bw) solid var(--wp--preset--color--line);
	border-radius: var(--r-sm);
	background: var(--wp--preset--color--paper);
	color: var(--wp--preset--color--ink);
	font-family: inherit;
	font-size: var(--wp--preset--font-size--medium);
}

.longrow-passform__submit {
	min-block-size: 48px;
	padding-inline: var(--sp-6);
	border: 0;
	border-radius: var(--r-pill);
	background: var(--wp--preset--color--red);
	color: var(--wp--preset--color--paper);
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	cursor: pointer;
	transition:
		background-color var(--dur) var(--ease-out),
		transform var(--dur) var(--ease-out);
}

.longrow-passform__submit:hover,
.longrow-passform__submit:focus-visible {
	background: var(--wp--preset--color--red-deep);
	transform: var(--hover-lift);
}

.longrow-passform__submit:active {
	transform: var(--press-scale);
}


/* === Translate ===========================================================
   The control is ours; the language list inside it is Google's, injected once
   somebody opens the panel. Nothing here loads until then.
   ========================================================================= */

.longrow-translate {
	position: relative;
}

.longrow-translate__button {
	display: flex;
	align-items: center;
	gap: var(--sp-2);
	min-block-size: 44px;
	padding: var(--sp-2) var(--sp-3);
	border: var(--bw) solid rgba(255, 255, 255, 0.3);
	border-radius: var(--r-pill);
	background: none;
	color: var(--wp--preset--color--paper);
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	cursor: pointer;
	transition:
		background-color var(--dur) var(--ease-out),
		border-color var(--dur) var(--ease-out);
}

.longrow-translate__button:hover,
.longrow-translate__button[aria-expanded="true"] {
	background: rgba(255, 255, 255, 0.12);
	border-color: var(--wp--preset--color--paper);
}

.longrow-translate__panel {
	position: absolute;
	inset-inline-end: 0;
	top: calc(100% + var(--sp-3));
	z-index: 40;
	inline-size: min(22rem, calc(100vw - 2 * var(--gutter)));
	padding: var(--sp-4);
	border-radius: var(--r-md);
	background: var(--wp--preset--color--paper);
	color: var(--wp--preset--color--ink);
	box-shadow: 0 18px 40px rgba(8, 21, 58, 0.28);
}

.longrow-translate__note {
	margin: 0 0 var(--sp-3);
	font-size: var(--wp--preset--font-size--caption);
	color: var(--wp--preset--color--ink-muted);
	line-height: var(--wp--custom--line-height--snug);
}

/* Google's own <select>, dressed to match the site. */
#longrow-translate-widget select {
	inline-size: 100%;
	min-block-size: 44px;
	padding: var(--sp-2) var(--sp-3);
	border: var(--bw) solid var(--wp--preset--color--line);
	border-radius: var(--r-sm);
	background: var(--wp--preset--color--paper);
	color: var(--wp--preset--color--navy);
	font-family: inherit;
	font-size: var(--wp--preset--font-size--small);
}

.longrow-translate__reset {
	display: none;
	margin-block-start: var(--sp-3);
	min-block-size: 44px;
	padding: var(--sp-2) var(--sp-3);
	border: var(--bw) solid var(--wp--preset--color--line);
	border-radius: var(--r-sm);
	background: none;
	color: var(--wp--preset--color--navy);
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	cursor: pointer;
}

/* Only offered once there is something to undo. */
.longrow-translate.is-translated .longrow-translate__reset {
	display: block;
}

/* Google injects a banner iframe that pushes <body> down by ~40px, which on
   this site shunts the whole header off the top of the window. Removing it is
   the reason our own control exists: the visitor already has a language
   chooser and a way back to English. */
.skiptranslate iframe,
iframe.skiptranslate,
.goog-te-banner-frame {
	display: none !important;
}

body {
	top: 0 !important;
}

/* Google highlights every translated phrase on hover in yellow-on-black,
   which is neither the school's palette nor legible over photographs. */
.goog-text-highlight {
	background: none !important;
	box-shadow: none !important;
}

@media (max-width: 60rem) {
	.longrow-translate__button span {
		position: absolute;
		inline-size: 1px;
		block-size: 1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}
}


/* === News cards ==========================================================
   The archive listing. Built from the core query loop rather than one of our
   own blocks, because an archive needs real pagination over 141 posts and the
   school needs to be able to change what a card shows without touching PHP.

   Eighteen of those posts have no featured image — they are text notices and
   newsletters rather than picture stories. The media panel is therefore a
   coloured box that the photograph fills when there is one, instead of the
   image itself: a post without a picture keeps the same card shape and the
   grid does not go ragged. It is the same decision as the section tiles.
   ========================================================================= */

.longrow-cards {
	list-style: none;
	padding: 0;
	gap: var(--sp-8) var(--sp-6);
}

.longrow-card {
	display: flex;
	flex-direction: column;
	gap: var(--sp-4);
	block-size: 100%;
}

.longrow-card__media {
	aspect-ratio: 4 / 3;
	border-radius: var(--r-lg);
	overflow: hidden;
	background: var(--wp--preset--color--navy-tint);

	/* The raked texture marks "no photograph" without resorting to an icon,
	   and matches the colour-only section tiles. */
	background-image: repeating-linear-gradient(
		135deg,
		rgba(13, 31, 82, 0.06) 0 14px,
		rgba(13, 31, 82, 0) 14px 28px
	);
}

.longrow-card__media .wp-block-post-featured-image {
	margin: 0;
	block-size: 100%;
}

.longrow-card__media img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	display: block;
	border-radius: 0;
	transition: transform var(--dur-slow) var(--ease-out);
}

.longrow-card:hover .longrow-card__media img {
	transform: scale(1.06);
}

.longrow-card__body {
	display: flex;
	flex-direction: column;
	gap: var(--sp-2);
}

/* Date above title, and quieter than it: the headline is what people scan. */
.longrow-card__body .wp-block-post-date {
	color: var(--wp--preset--color--ink-muted);
	font-variant-numeric: tabular-nums;
	margin: 0;
}

.longrow-card__body .wp-block-post-title {
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	line-height: var(--wp--custom--line-height--snug);
	text-wrap: balance;
}

.longrow-card__body .wp-block-post-title a {
	color: var(--wp--preset--color--navy);
	text-decoration: none;
}

.longrow-card__body .wp-block-post-title a:hover,
.longrow-card__body .wp-block-post-title a:focus-visible {
	color: var(--wp--preset--color--red);
	text-decoration: underline;
}

.longrow-card__body .wp-block-post-excerpt {
	margin: 0;
}

.longrow-card__body .wp-block-post-excerpt__excerpt {
	color: var(--wp--preset--color--ink-muted);
}

/* The excerpt's "Read more" is redundant next to a linked headline, and it
   gives a screen reader a second link to the same place with a vaguer name. */
.longrow-card__body .wp-block-post-excerpt__more-text {
	display: none;
}

.longrow-pagination {
	margin-block-start: var(--sp-16);
	gap: var(--sp-2);
}

.longrow-pagination .page-numbers {
	display: inline-grid;
	place-items: center;
	min-inline-size: 44px;
	min-block-size: 44px;
	padding-inline: var(--sp-2);
	border-radius: var(--r-sm);
	color: var(--wp--preset--color--navy);
	text-decoration: none;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
}

/* Identity: red carries the interactive states of this one small control.
   Hover/focus turn the number itself red — #d21f28 on the pale surface-alt
   ground is 4.81:1, past AA — and the current page wears a red pill, paper
   on red at 5.30:1. A 44px pill is an accent-scale control, not a section
   ground, and it gives the row an unmissable "you are here". */
.longrow-pagination .page-numbers:hover,
.longrow-pagination a.page-numbers:focus-visible {
	background: var(--wp--preset--color--surface-alt);
	color: var(--wp--preset--color--red);
}

.longrow-pagination .page-numbers.current {
	background: var(--wp--preset--color--red);
	color: var(--wp--preset--color--paper);
	border-radius: var(--r-pill);
}


/* === Brand mark ==========================================================
   The crest is a ring of children round a globe, and its detail closes up
   below about 48px. 56 is the smallest size at which the figures still read
   as figures rather than as a texture.

   It carries the school's name as artwork and the name is set beside it as
   real text, so the image is decorative and its alt is empty. Both are inside
   one link, which is what a visitor expects of a masthead.
   ========================================================================= */

/* 68px drawn, 56px of layout.
   The crest is a ring of twenty small figures round a globe, and below about
   64px they stop being figures and become texture. It needs to be bigger.

   Growing it would normally grow the header with it, because the header's
   height is whatever its tallest item needs. The negative block margin gives
   back exactly what the extra size took, so the logo occupies 56px of layout
   while drawing at 68 and the header stays 88px. The overflow eats 6px of the
   header's 16px padding at each end, leaving 10px of breathing room.

   68 rather than the 72 first tried: the masthead is horizontally tight, not
   vertically. With the school's name on one line, brand, navigation, translate
   and search together needed 1,199px of the 1,200px container.

   The name is now deliberately stacked onto two lines (see the render_block
   filter in functions.php), which returns about 160px of that budget, so the
   nav and brand gaps are back at their designed 24px and 14px. */
.longrow-brand .wp-block-site-logo img {
	display: block;
	inline-size: 68px;
	block-size: 68px;
	margin-block: -6px;
}

.longrow-brand .wp-block-site-title {
	margin: 0;
}

/* Hard drop shadow on the wordmark: 3px at 60°, solid black, no blur.
   CSS takes offsets rather than an angle, so 3px at 60° is
   x = 3·cos60 = 1.5px, y = 3·sin60 = 2.6px — down and to the right.

   The old PrimarySite theme did the same thing to this wordmark, at
   `text-shadow: 3px 0 0 rgba(0,0,0,1)`, so it is continuity rather than
   invention. Set on the link as well: the anchor carries its own colour and
   would otherwise not inherit it. */
.longrow-brand .wp-block-site-title,
.longrow-brand .wp-block-site-title a {
	text-shadow: 1.5px 2.6px 0 #000;
}

/* Below the point where the header has room for both, the wordmark carries
   it alone: two lines of school name next to a crest leaves nothing for the
   menu button. */
@media (max-width: 30rem) {
	.longrow-brand .wp-block-site-logo {
		display: none;
	}
}


/* === Header dropdowns ====================================================
   Each section heading in the header opens a panel of the pages beneath it.

   The sections are big — Our Learning has twenty-four children — so a single
   column would be a list longer than the window. The panel is instead the full
   width of the header with the links flowed into columns, which is the only
   shape that holds that many items and stays scannable.

   Anchoring to the header rather than to the menu item is what makes that
   possible: the item is taken out of the positioning flow with position:static
   so the panel's containing block becomes the header, and it can then span the
   viewport. It also removes the usual mega-menu problem of the last item's
   panel hanging off the right-hand edge.

   No JavaScript. :hover opens it for a mouse and :focus-within opens it for a
   keyboard, so tabbing through the header walks into each panel in DOM order.
   Below 60rem none of this applies — core's own overlay menu takes over, and
   the submenus are nested lists inside it.
   ========================================================================= */

@media (min-width: 60rem) {
	.longrow-header {
		position: relative;

		/* How long the panel stays open after the pointer leaves, and how far
		   the menu item's hit area reaches down toward it. See the transition
		   on the submenu container below for why both exist.

		   Deliberately NOT reduced under prefers-reduced-motion. The grace is
		   not animation, it is how long you have to reach the menu, and the
		   people most likely to set that preference are the least likely to
		   want a shorter one. Only the fade itself collapses, via --dur. */
		--nav-grace: 260ms;
		--nav-bridge: 20px;
	}

	/* The menu item is not the only thing in the way. Core positions four
	   nested wrappers — the <nav>, the responsive container, the responsive
	   dialog and the item — and the panel anchors to whichever is nearest, so
	   leaving any of them relative pins it to the 490px menu instead of the
	   2070px header. They are only needed for the mobile overlay, which this
	   media query has already excluded. */
	.longrow-header .wp-block-navigation,
	.longrow-header .wp-block-navigation .wp-block-navigation__responsive-container,
	.longrow-header .wp-block-navigation .wp-block-navigation__responsive-dialog,
	.longrow-header .wp-block-navigation .wp-block-navigation__container,
	.longrow-header .wp-block-navigation .wp-block-navigation__container > .wp-block-navigation-item.has-child {
		position: static;
	}

	.longrow-header .wp-block-navigation .has-child > .wp-block-navigation__submenu-container {
		position: absolute;
		inset-inline: 0;
		top: 100%;
		inline-size: 100%;

		/* Without this the element is content-box, so the 435px side padding
		   is added to the 100% rather than taken out of it and the panel ends
		   up 870px wider than the viewport — a horizontal scrollbar on every
		   page. */
		box-sizing: border-box;
		min-inline-size: 0;
		max-inline-size: none;
		z-index: 30;

		/* Sits below a navy header, so it is the deeper navy — a panel of the
		   same colour would have no edge. !important is not a preference here:
		   the navigation block puts its overlay colour on as the preset class
		   has-navy-background-color, and WordPress emits every preset colour
		   utility with !important, so no amount of specificity beats it. */
		background: var(--wp--preset--color--navy-deep) !important;
		border: 0;

		/* The school's red, as a crisp top edge where the panel meets the
		   header. It marks the seam between the two navies — which otherwise
		   read as one surface — and puts the brand's second colour on the
		   menu itself. Decorative, so no contrast requirement; the text below
		   it is untouched. */
		border-top: 4px solid var(--wp--preset--color--red);
		border-radius: 0 0 var(--r-lg) var(--r-lg);
		box-shadow: 0 18px 40px rgba(8, 21, 58, 0.38);

		/* Content is held to the 1200px container, matching every other band. */
		padding: var(--sp-6) max(var(--wp--custom--gutter), calc((100% - 1200px) / 2)) var(--sp-8);

		/* Grid, not column-count. Core makes this element a flex container, and
		   multi-column layout does not apply to one — the property computes
		   fine and does nothing. Stating display here settles it either way,
		   and a grid gives even columns rather than balanced-height ones. */
		display: grid;

		/* Three columns, not four. The school liked how Castle's menus "come
		   straight down in one strip and appear a bit larger" — one strip is
		   impossible at twenty-four items, but three wider columns give each
		   link more room and the panel a more deliberate, less packed read. */
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: var(--sp-1) var(--sp-8);
		align-content: start;

		opacity: 0;
		visibility: hidden;
		transform: translateY(-10px);

		/* Closing waits --nav-grace. The menu item is 24px tall and the panel
		   opens 32px below it, at the foot of the header's padding, so the
		   pointer has to cross a strip that belongs to neither — and on the
		   way across, :hover is false and the panel was vanishing before it
		   could be reached.

		   The delay is on the close only; opening stays instant. A geometric
		   fix would have to know the header's height, and the header changes
		   height when the site title wraps — which it does as soon as anyone
		   translates the page into a language with longer words. */
		transition:
			opacity var(--dur) var(--ease-out) var(--nav-grace),
			transform var(--dur) var(--ease-out) var(--nav-grace),
			visibility 0s linear calc(var(--dur) + var(--nav-grace));
	}

	/* Belt to the delay's braces: the link's hit area is stretched down over
	   the gap, so most of the journey is not a journey at all. Sized off the
	   header's own padding rather than a magic number, and it hangs off the
	   link — the list item is position:static so the panel can anchor to the
	   header, which rules out putting it there. */
	.longrow-header .wp-block-navigation .has-child > .wp-block-navigation-item__content {
		position: relative;
	}

	.longrow-header .wp-block-navigation .has-child > .wp-block-navigation-item__content::after {
		content: "";
		position: absolute;
		inset-inline: calc(var(--sp-3) * -1);
		top: 100%;
		block-size: var(--nav-bridge);
	}

	.longrow-header .wp-block-navigation .has-child:hover > .wp-block-navigation__submenu-container,
	.longrow-header .wp-block-navigation .has-child:focus-within > .wp-block-navigation__submenu-container {
		opacity: 1;
		visibility: visible;
		transform: translateY(0);
		transition-delay: 0s, 0s, 0s;
	}

	.longrow-header .wp-block-navigation .has-child > .wp-block-navigation__submenu-container > .wp-block-navigation-item {
		break-inside: avoid;
	}

	.longrow-header .wp-block-navigation .has-child > .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
		display: block;

		/* Set a step up from the header's own links and given roomier padding,
		   for the larger, more deliberate read the school asked for. */
		font-size: 1.0625rem;
		padding: var(--sp-3) var(--sp-4);
		border-radius: var(--r-sm);
		color: var(--wp--preset--color--paper);
		text-decoration: none;
		line-height: var(--wp--custom--line-height--snug);
		transition:
			background-color var(--dur-fast) var(--ease-out),
			color var(--dur-fast) var(--ease-out);
	}

	.longrow-header .wp-block-navigation .has-child > .wp-block-navigation__submenu-container .wp-block-navigation-item__content:hover,
	.longrow-header .wp-block-navigation .has-child > .wp-block-navigation__submenu-container .wp-block-navigation-item__content:focus-visible {
		background: rgba(255, 255, 255, 0.1);

		/* Sun-300 on navy-deep is 12.7:1. The palette's sun would be 9.4:1,
		   which also passes, but reads muddier at this size. */
		color: #ffd96b;
	}

	/* The chevron is decorative here: the panel opens on hover and on focus,
	   so nothing depends on pressing it. */
	.longrow-header .wp-block-navigation__submenu-icon {
		pointer-events: none;
	}

	.longrow-header .has-child:hover > .wp-block-navigation-item__content .wp-block-navigation__submenu-icon,
	.longrow-header .has-child:focus-within > .wp-block-navigation-item__content .wp-block-navigation__submenu-icon {
		transform: rotate(180deg);
	}

	.longrow-header .wp-block-navigation__submenu-icon {
		transition: transform var(--dur) var(--ease-out);
	}
}


/* === Footer ==============================================================
   The footer was a flat slab of the darkest colour on the site, which made
   the page end on a dead rectangle. It now carries colour three ways, in
   descending order of how loudly each one speaks.

   Contrast was the constraint, not an afterthought. Body text is #dfe6fa and
   the links are #ffd96b; measured against the lightest point of the finished
   background — the top of the gradient with both washes stacked over it —
   they come out at 6.7:1 and 6.1:1. Both clear SC 1.4.3 AA (4.5:1) with room
   to spare, and most of the footer sits darker than that worst case.
   See DECISIONS.md D-047.
   ========================================================================= */

.longrow-footer {
	position: relative;
	isolation: isolate;

	/* The first attempt washed sky over navy and it read as nothing: the two
	   are the same hue, so more of it only made the navy paler. What reads as
	   colour is a change of hue, so the base gradient travels — indigo at the
	   top left, through mid blue, to a teal-leaning navy at the bottom right,
	   settling into navy-deep at the very bottom so the page still ends on
	   the site's darkest colour.

	   Every stop is dark enough to carry the text on its own: the palest of
	   them holds body copy at 9.5:1. The warmth at the bottom right is a sun
	   glow, which also puts something in the corner the three columns leave
	   empty. Over the palest stop it still leaves 6.9:1 body and 6.3:1 links,
	   and in reality it falls on the teal end, which is darker again. */
	background-image:
		radial-gradient(46rem 30rem at 88% 112%, rgba(255, 183, 3, 0.18), rgba(255, 183, 3, 0) 62%),
		linear-gradient(150deg, #2a2f78 0%, #143a69 38%, #0b3350 68%, #08153a 100%);
}

/* The brand ribbon: four hard-edged bands of the accent palette across the
   top edge. It is the one piece of unapologetic colour, and it is the same
   sun/red/sky/leaf cycle the tiles run through, so it reads as the system
   rather than as decoration.

   Hard stops rather than a blend, and plain rgb/hex rather than color-mix:
   cssnano has previously rewritten a zero-percent color-mix into something
   invalid and taken a whole gradient with it. */
.longrow-footer::before {
	content: "";
	position: absolute;
	inset-block-start: 0;
	inset-inline: 0;
	z-index: 1;
	block-size: 6px;
	background-image: linear-gradient(
		90deg,
		#ffb703 0 25%,
		#d21f28 25% 50%,
		#2ba6c9 50% 75%,
		#3f9e56 75% 100%
	);
}

/* The crest. It carries more colour than anything else available — a ring of
   illustrated children round a globe — and the artwork is already transparent
   at the corners, so it sits on the navy without a white box behind it,
   exactly as it does in the header.

   Sitting it in the flow above the school's name pushed that column's heading
   128px below the headings of the other two, which read as a mistake. Once the
   columns are side by side the crest is lifted out of the flow and the column
   is indented to make room, so all three headings start on the same line and
   the crest reads as a mark set beside the name rather than as a block shoved
   into the first column. */
.longrow-footer__crest {
	margin-block-end: var(--sp-5);
}

/* 782px is where core stacks columns; above it they are side by side and
   there is something to line up with. Below it each column is full width,
   the crest keeps its place above the name, and nothing is out of line.

   Grid rather than padding the column: the column's width comes from an
   inline flex-basis of 40%, and padding is added outside that basis, so
   indenting this way took 128px from the two columns beside it. A grid track
   is inside the column instead, and leaves them at their proper width. */
@media (min-width: 782px) {
	.longrow-footer .wp-block-column:first-child {
		display: grid;
		/* Fluid, not a fixed 104px. Just above the breakpoint this column is
		   only 295px wide, and a fixed crest left 167px for the school's
		   name — three cramped lines. The crest gives way instead, since it
		   is the decoration and the name is the content. */
		grid-template-columns: clamp(64px, 8vw, 104px) 1fr;
		column-gap: var(--sp-6);
		align-content: start;
	}

	.longrow-footer__crest img {
		inline-size: 100%;
	}

	/* span 20 rather than 1 / -1: the rows here are implicit, and -1 resolves
	   to the last *explicit* line, which would leave the crest one row tall
	   and the text wrapping under it. Same trap as the news lead in D-026. */
	.longrow-footer__crest {
		grid-column: 1;
		grid-row: 1 / span 20;
		align-self: start;
		margin-block-end: 0;
	}

	.longrow-footer .wp-block-column:first-child > :not(.longrow-footer__crest) {
		grid-column: 2;
	}

	/* The crest is the column's first child in the DOM, so the heading is no
	   longer :first-child and keeps the top margin core would have stripped —
	   24px, which is exactly how far out of line it sat. */
	.longrow-footer .wp-block-column:first-child > h2 {
		margin-block-start: 0;
	}
}

.longrow-footer__crest img {
	block-size: auto;
	border-radius: 0;
}

/* Decorative: the school's name is the next element, so a screen reader
   should not hear it twice. functions.php empties the alt; this stops the
   image being announced as an image at all. */
.longrow-footer__crest a {
	display: inline-block;
}


/* === Embeds ==============================================================
   An embed pasted from Google Maps carries width="600" height="450" in its
   markup. On a 375px phone that is not 600px of map, it is 600px of page:
   the document scrolls sideways and every line of text on it goes with it.
   /contact-details/ was the only page on the site that did this.

   Constraining the element rather than editing the embed means the next one
   somebody pastes in behaves too. The ratio is kept from the width and height
   the embed states, so nothing letterboxes. WCAG 2.2 SC 1.4.10.
   ========================================================================= */

.wp-block-post-content iframe,
.wp-block-group iframe,
.entry-content iframe {
	max-inline-size: 100%;
}

/* An iframe with both attributes gets its ratio from them; one without keeps
   whatever height it was given. `height: auto` alone would collapse it. */
.wp-block-post-content iframe[width][height],
.wp-block-group iframe[width][height],
.entry-content iframe[width][height] {
	aspect-ratio: attr(width) / attr(height);
	block-size: auto;
}

@supports not (aspect-ratio: attr(width) / attr(height)) {
	/* attr() outside content: is still not everywhere. 4:3 is the ratio the
	   map embed ships with, and a sensible floor for anything else. */
	.wp-block-post-content iframe[width][height],
	.wp-block-group iframe[width][height],
	.entry-content iframe[width][height] {
		aspect-ratio: 4 / 3;
		block-size: auto;
	}
}


/* === Target size =========================================================
   SC 2.5.8 asks 24x24 of anything you have to hit. A link standing on its
   own line missed it by a pixel — the line box was 23px — which is the sort
   of thing only measurement finds. Inline links inside a sentence are exempt
   from the rule and are left alone, so reading text keeps its own rhythm.

   Grown with padding rather than `display: inline-block` and a min-height.
   inline-block was the first attempt and it broke /science/ on a phone: an
   inline-block does not wrap, so a link with long text stopped being a line
   of text and became one atomic 627px box, scrolling the page sideways. The
   fix for a target-size problem should not introduce a reflow one.

   Padding enlarges the hit area, which is what the criterion measures, and
   leaves the link an inline box that still wraps like text. */

.wp-block-post-content > p > a:only-child,
.wp-block-post-content li > a:only-child,
.longrow-band a:only-child {
	padding-block: 3px;
}


/* === Print =============================================================== */

@media print {
	.longrow-skip-link,
	.wp-block-navigation,
	.wp-block-search {
		display: none !important;
	}

	body {
		background: #fff;
		color: #000;
	}
}
