/*
Theme Name: SCHSBM
Theme URI: https://uwischsbm.edu.bb/
Author: SCHSBM build
Description: Custom block theme for the Sagicor Cave Hill School of Business and Management, a school of The University of the West Indies, Cave Hill Campus. Implements the brand system in the supplied v1.0 developer briefing mockup: UWI co-branded masthead, Blue Core / Orange Core / Orange Deep palette with Academic Yellow restricted to degree surfaces, Mont + Acumin type stack with the mockup's fallback chain, and the public, application and portal layouts.
Version: 1.0.0
Requires at least: 6.7
Tested up to: 6.9
Requires PHP: 8.1
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: schsbm
Tags: block-templates, full-site-editing, education, accessibility-ready
*/

/* ==========================================================================
   BRAND RULES ENCODED IN CSS
   --------------------------------------------------------------------------
   Spec §7 fixes three things that are easy to break by hand and are therefore
   made structural here rather than left to an editor's judgement:

   1. Orange Core (#f58308) never carries white text — it fails AA. There is no
      rule in this stylesheet that pairs them. Filled accent buttons use Orange
      Deep (#c66400), which passes at 4.5:1 against white.
   2. Academic Yellow (#ffde17) is reachable only from selectors scoped to
      `body.is-degree-surface` (or the .badge--degree component, which itself
      only renders on degree content). The class is applied server-side from the
      post's business line — see Schsbm\Core\Content\Degree_Surface.
   3. Blue Core carries white text everywhere it appears as a field. That is the
      spec's "primary reversed-out combination".

   The mockup's reviewer toolbar is deliberately absent: it is a review artefact,
   not part of the site.
   ========================================================================== */

/* ==========================================================================
   ACCESSIBLE DERIVATIONS OF TWO BRAND TOKENS
   --------------------------------------------------------------------------
   Two of the specification's own colour rules do not reach WCAG 2.1 AA, which
   the same specification also requires (Stage 7). Measured with axe-core against
   the rendered pages:

     Orange Deep #C66400 with white text  →  4.02:1   (AA needs 4.5:1)
     Orange Deep #C66400 as text on white →  4.02:1   (AA needs 4.5:1)
     Ink 500     #8B8FA3 as text on white →  3.20:1   (AA needs 4.5:1)

   Neither is a large-text case: the mockup's accent buttons are 14px semibold
   and the meta text is 13px, and AA's 3:1 allowance only applies from 18.66px
   bold or 24px regular.

   Rather than silently failing one requirement or the other, the palette tokens
   below keep the specification's values exactly — Orange Deep is still #C66400
   and is still what the focus ring uses, where 3:1 is the applicable threshold
   and it passes — and two derived tokens carry text:

     --schsbm-accent-fill  #B85D00  white text on it measures 4.56:1
     --schsbm-accent-text  #A45300  as text, 4.56:1 on the darkest light surface
     --schsbm-text-muted   #616579  as text, 4.77:1 on the darkest light surface

   These are the smallest darkenings that clear the threshold: 7% and 17% off
   Orange Deep respectively. Reverting is a one-line change here.

   THIS IS FLAGGED FOR THE SCHOOL, not decided unilaterally. The alternative is
   to keep #C66400 on buttons and raise the button type to 18.66px bold, which
   qualifies as large text — a bigger visual change than a slightly darker
   orange, which is why it was not the default. See docs/accessibility-report.md.
   ========================================================================== */

:root {
	--schsbm-accent-fill: #b85d00;
	--schsbm-accent-text: #a45300;
	--schsbm-text-muted: #616579;

	/* The horizontal grid. Three values decide every left and right edge on the
	   public site; nothing else may set one.

	     --schsbm-max      the shared box, centred in the viewport
	     --schsbm-gutter   the inset from that box to the content edge
	     --schsbm-measure* caps how WIDE a block may be, never where it STARTS

	   The distinction in that last line is the whole system. A narrow column is
	   a short line length on the shared left edge, not a box nudged inwards. */
	--schsbm-gutter: clamp(24px, 5vw, 64px);
	--schsbm-max: 1240px;
	--schsbm-max-wide: 1400px;

	--schsbm-measure: 920px;         /* reading measure — prose, article body   */
	--schsbm-measure-panel: 640px;   /* standalone panels — confirmations, etc. */
	--schsbm-measure-form: 560px;    /* auth and single-column form cards       */
	--schsbm-measure-application: 46rem; /* the application column: wider than a login box, narrower than a monitor */

	/* Retained as an alias: some older rules referred to the narrow box by this
	   name, and it now means the reading measure. */
	--schsbm-max-narrow: var(--schsbm-measure);
	--schsbm-radius: 6px;
	--schsbm-radius-sm: 3px;
	--schsbm-shadow-sm: 0 1px 3px rgba(11, 13, 26, .06), 0 2px 6px rgba(11, 13, 26, .04);
	--schsbm-shadow-md: 0 4px 12px rgba(11, 13, 26, .07), 0 12px 32px rgba(11, 13, 26, .06);
	--schsbm-control-min: 44px;

	--schsbm-success: #15803d;
	--schsbm-success-tint: #dcfce7;
	--schsbm-warning: #b45309;
	--schsbm-warning-tint: #fef3c7;
	--schsbm-error: #b91c1c;
	--schsbm-error-tint: #fee2e2;
}

/* ==========================================================================
   1. BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
	margin: 0;
	background: var(--wp--preset--color--white);
	color: var(--wp--preset--color--ink-900);
	font-family: var(--wp--preset--font-family--body);
	font-size: 1rem;
	line-height: 1.55;
	-webkit-font-smoothing: antialiased;
	overflow-wrap: break-word;
}

img, svg, video { max-width: 100%; height: auto; }

/* One visible focus treatment everywhere. Never removed — A11Y-04.
   :focus-visible, not :focus, so a mouse click never draws it — the ring is
   for people navigating by keyboard, and for focus the browser restores (going
   Back re-focuses the link you left from, which is the usual way to meet this
   ring without having touched the keyboard).

   2px is the minimum perimeter SC 2.4.11 accepts and Orange Deep is 4.02:1 on
   white, so this is as quiet as the rule permits. It does not go below 2px and
   it is not removed. */
:focus-visible {
	outline: 2px solid var(--wp--preset--color--orange-deep);
	outline-offset: 2px;
	border-radius: var(--schsbm-radius-sm);
}

.screen-reader-text {
	position: absolute !important;
	width: 1px; height: 1px;
	padding: 0; margin: -1px;
	overflow: hidden; clip-path: inset(50%);
	white-space: nowrap; border: 0;
}

.skip-link:focus {
	position: fixed;
	top: 8px; left: 8px;
	z-index: 1000;
	width: auto; height: auto;
	clip-path: none;
	padding: 12px 18px;
	background: var(--wp--preset--color--blue-core);
	color: var(--wp--preset--color--white);
	font-weight: 600;
	text-decoration: none;
	border-radius: var(--schsbm-radius-sm);
}

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: .001ms !important;
		transition-duration: .001ms !important;
		scroll-behavior: auto !important;
	}
}

/* ==========================================================================
   2. LAYOUT
   ========================================================================== */

/*
 * Every horizontal band on the site resolves through this one rule: the
 * masthead's three rows, the audience strip, page content, and the footer. They
 * previously each restated the same max-width and inset, which is how they
 * drifted apart.
 */
.schsbm-container,
.schsbm-utility__inner,
.schsbm-lockup__inner,
.schsbm-primary__inner,
.schsbm-audience__inner,
.schsbm-footer__inner {
	max-width: var(--schsbm-max);
	margin-inline: auto;
	padding-inline: var(--schsbm-gutter);
}

/*
 * A narrow container keeps the shared left edge and shortens the line, rather
 * than shrinking its own box — a shrunken box would centre itself and pull the
 * content inwards, away from the heading above it.
 */
.schsbm-container--narrow > * { max-width: var(--schsbm-measure); }

/* Sign in, register, forgotten password, reset, verify.
   These sit on the prose template, which caps its children at the 920px
   reading measure and leaves them ranged left — right for Terms, wrong for a
   single-task screen, which ended up as a ~560px card ranged left in a 1112px
   box with 192px of empty page beside it. An auth screen is one task, so it
   takes a form measure and centres. .schsbm-card--form is used by those five
   views and nowhere else, so :has() cannot reach an unrelated page.
   (0,2,0) via :has(), which outranks the (0,1,0) rule above. */
.schsbm-container--narrow:has(.schsbm-card--form) > * {
	max-width: 560px;
	margin-inline: auto;
}
/* The rule under the title has to move with the column. Its own
   `margin: 20px auto 26px 0` is declared later in this file, so the centring
   above does not reach it and it strands against the far left of the page.
   Three classes outranks it wherever it sits in the file. The dash stays 56px
   and sits on the column's left edge, under the title, rather than being
   centred under it. max() so the offset cannot go negative once the viewport
   is narrower than the column. */
.schsbm-container--narrow:has(.schsbm-card--form) > .schsbm-rule {
	margin-inline-start: max(0px, calc((100% - 560px) / 2));
	margin-inline-end: 0;
}
.schsbm-container--wide { max-width: var(--schsbm-max-wide); }

/*
 * WordPress's constrained layout centres its children inside whatever box it
 * finds itself in. Nested inside our container that produced a second, and
 * sometimes a third, centring inset — which is why a page heading, its
 * introduction and its body text each began at a different left edge.
 *
 * The measure it applies is useful and is kept; only the centring is undone, so
 * a constrained child starts on the container's content edge and simply stops
 * sooner. Wide, full and float alignments are left alone — those exist
 * precisely to escape the measure.
 */
.schsbm-container.is-layout-constrained > :where(:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright)),
.schsbm-container .is-layout-constrained > :where(:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright)) {
	margin-inline: 0 auto;
}

/* A section's padding is its spacing. WordPress's flow rule was adding 28px on
   top of that, so the home page ran 115px of padding plus 28px of margin — 148px
   — between the audience band and the section heading under it. */
.schsbm-section { padding-block: clamp(48px, 9vw, 120px); }
.schsbm-section--sm { padding-block: clamp(36px, 6vw, 80px); }
.schsbm-section--xs { padding-block: clamp(24px, 4vw, 48px); }
.schsbm-section,
.schsbm-section--sm,
.schsbm-section--xs { margin-block-start: 0; }

/* ── The footer stays at the bottom ────────────────────────────────────────
   A short page — Terms, a 404, an empty search — left the footer floating in
   the middle of the window with white space beneath it, which reads as a
   half-loaded page rather than a short one.

   The block theme wraps everything in .wp-site-blocks, so that is the column:
   header, main, footer, with main taking whatever height is left over. On a
   long page nothing changes, because main is already taller than the space.

   100dvh with a 100vh fallback: on mobile browsers 100vh is the viewport with
   the address bar hidden, so a sticky footer measured against it sits just
   below the fold and the page scrolls a little for no reason. dvh measures what
   is actually visible.

   The flow margin is cleared here for the same reason as everywhere else — in
   a column it was adding 28px above main and again above the footer. */
.wp-site-blocks {
	display: flex;
	flex-direction: column;
	min-height: 100vh;
	min-height: 100dvh;
}
.wp-site-blocks > * { margin-block-start: 0; }
.wp-site-blocks > main { flex: 1 0 auto; }

/* ── WordPress flow margins, cleared once ─────────────────────────────────
   Every one of these is authored as a wp-block-group in a template or pattern,
   so each carries WordPress's is-layout-flow class and its
   `> * + * { margin-block-start: 24px }` rule. That is correct for a group that
   really does stack, and wrong for every row here, all of which lay themselves
   out with grid or flex and own the spacing between their children through gap.

   Left alone it offsets the second and subsequent children by 28px. It put dead
   space above the primary navigation and dropped the account button below the
   links; it pushed the footer's link columns below the school name and the
   copyright onto its own line; it stepped the three "Three ways in" cards down
   like a staircase, and did the same to both hero columns.

   Those were found and fixed one row at a time, which is the wrong shape of fix
   for a single cause with one remedy. This is the remedy: our layout owns
   spacing between its own children, so the inherited margin goes — everywhere,
   once, rather than again on the next row somebody adds. */
/* Attribute matches, not exact class names: the split modifiers are standalone
   classes rather than base-plus-modifier — .schsbm-split--hero appears without
   .schsbm-split — so `.schsbm-split > *` missed both of them and left the hero
   columns stepped. Matching the prefix catches the modifier somebody adds next
   year too. */
/* The header's own bands, not just the rows inside them. .schsbm-header is a
   wp-block-group too, so the utility strip, the lockup and the primary bar each
   inherited a 28px top margin: the first collapsed out of the header and became
   a white strip above the whole page, and the other two became gaps above the
   lockup and above the navigation. Three separate-looking complaints, one
   margin. */
/* Our own section wrappers inside <main>. Each already owns its vertical
   rhythm through .schsbm-section's padding — 120px on a business-line page —
   and was carrying the inherited 28px on top of it, so the space above body
   content was 148px where the design says 120.

   Deliberately a direct-child selector and not a descendant one: flow spacing
   between the blocks an editor writes *inside* post content is WordPress
   behaving correctly and is left alone. Only the template's own top-level
   sections are cleared, and they position themselves.

   Matched on the class prefix rather than written as `main > *`, which loses:
   WordPress's generated `.wp-container-… > * + *` carries a class and so
   outranks a selector made only of element and universal parts. */
main > [class*="schsbm-"],
/* A section's padding is its rhythm, so the first thing inside it must not add
   a margin on top of that — WordPress gives post-content 28px, which on the
   checkout sat between our 48px and WooCommerce's own 24px and made a 100px
   gap out of three separate opinions about the same space. Same for the last
   child against the bottom padding. */
[class*="schsbm-section"] > :first-child { margin-block-start: 0; }
[class*="schsbm-section"] > :last-child { margin-block-end: 0; }

.schsbm-header > *,
[class*="schsbm-grid"] > *,
[class*="schsbm-split"] > *,
.schsbm-tiles > *,
.schsbm-steps > *,
.schsbm-finder > *,
.schsbm-utility__inner > *,
.schsbm-lockup__inner > *,
.schsbm-primary__inner > *,
.schsbm-audience__inner > *,
.schsbm-hero__actions > *,
.schsbm-footer__cols > *,
.schsbm-footer__base > * { margin-block-start: 0; }
.schsbm-footer__base > * { margin-block-end: 0; }

.schsbm-grid { display: grid; gap: clamp(24px, 3vw, 36px); }
.schsbm-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.schsbm-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.schsbm-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* A query loop puts its own <li> between the grid and the card. The li is the
   grid item and stretches to the row height, but the card inside it stops at
   its content, so a shorter post left a visibly shorter card. Pass the stretch
   through. Cards placed directly in the grid — the hand-built patterns — are
   already grid items and need none of this. */
.schsbm-grid > .wp-block-post { display: flex; flex-direction: column; }
.schsbm-grid > .wp-block-post > * { flex: 1 1 auto; min-width: 0; }

/* Each modifier is standalone: block markup applies one class, not two, so a
   modifier that only overrode grid-template-columns would silently stay display:block. */
.schsbm-split,
.schsbm-split--even,
.schsbm-split--hero,
.schsbm-split--checkout { display: grid; gap: clamp(36px, 5vw, 72px); }
.schsbm-split { grid-template-columns: 1.4fr 1fr; align-items: start; }
.schsbm-split--even { grid-template-columns: 1fr 1fr; align-items: center; }
.schsbm-split--hero { grid-template-columns: 1.1fr .9fr; gap: clamp(40px, 6vw, 80px); align-items: center; }
.schsbm-split--checkout { grid-template-columns: 1.5fr 1fr; gap: clamp(36px, 4vw, 64px); align-items: start; }

/* Grid children default to min-width:auto, which lets a wide child — the
   programme-facts and statement tables carry a min-width so their columns stay
   readable — push its whole column past the viewport rather than scrolling
   inside its own overflow wrapper. Found at 360px by bin/a11y.sh. */
.schsbm-grid > *,
.schsbm-split > *,
.schsbm-split--even > *,
.schsbm-split--hero > *,
.schsbm-split--checkout > *,
.schsbm-portal__grid > *,
.schsbm-tiles > *,
.schsbm-finder > * { min-width: 0; }

@media (max-width: 980px) {
	.schsbm-split, .schsbm-split--even, .schsbm-split--hero, .schsbm-split--checkout { grid-template-columns: 1fr; }
	.schsbm-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
	.schsbm-grid--2, .schsbm-grid--3 { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
	.schsbm-grid--4 { grid-template-columns: 1fr; }
}

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

.schsbm-eyebrow {
	display: inline-block;
	font-size: .71875rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .14em;
	color: var(--schsbm-accent-text);
	margin-bottom: 14px;
}
/* Both classes, deliberately.
   The element always carries both, and a single-class modifier (0,1,0) loses
   to any surrounding element rule such as `.schsbm-hero p` (0,1,1) — which is
   exactly what happened: the home hero's eyebrow is a paragraph, so it came
   out white, while programme pages were fine only because post-terms renders
   a div. Naming both classes scores (0,2,0) and outranks class+type anywhere
   the eyebrow is reversed out, not just on the hero. */
.schsbm-eyebrow.schsbm-eyebrow--on-dark { color: var(--wp--preset--color--orange-core); }

/* An eyebrow is a label for the heading under it, not a paragraph before it.
   WordPress's flow rule added 28px on top of the eyebrow's own 14px, so the
   two sat 42px apart and read as separate things. */
.schsbm-eyebrow + h1,
.schsbm-eyebrow + h2,
.schsbm-eyebrow + h3,
.schsbm-eyebrow + .wp-block-post-title { margin-block-start: 0; }

.schsbm-lede { font-size: 1.125rem; color: var(--wp--preset--color--ink-700); max-width: 66ch; }
.schsbm-meta { font-size: .8125rem; color: var(--schsbm-text-muted); }

/* The orange rule under a heading — Orange Core as an accent, never a field. */
/*
 * The orange accent rule. Written against .wp-block-separator as well, because
 * core's own separator style sets margin-left/right to auto and loads after the
 * theme — which centred the rule while the heading above it sat on the left
 * edge.
 */
.schsbm-rule,
.schsbm-rule.wp-block-separator {
	width: 56px;
	height: 3px;
	background: var(--wp--preset--color--orange-core);
	border: 0;
	margin: 20px auto 26px 0;
}

/* ==========================================================================
   4. BUTTONS
   ========================================================================== */

.schsbm-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: var(--schsbm-control-min);
	padding: 11px 18px;
	border: 1px solid transparent;
	border-radius: var(--schsbm-radius-sm);
	font-family: inherit;
	font-size: .875rem;
	font-weight: 600;
	line-height: 1.2;
	text-decoration: none;
	cursor: pointer;
	transition: background-color .15s, border-color .15s, color .15s;
}

.schsbm-btn--primary { background: var(--wp--preset--color--blue-core); color: var(--wp--preset--color--white); border-color: var(--wp--preset--color--blue-core); }
.schsbm-btn--primary:hover { background: var(--wp--preset--color--blue-deep); color: var(--wp--preset--color--white); }

/* Orange Deep, not Orange Core: Orange Core with white text fails AA (§7). */
.schsbm-btn--accent { background: var(--schsbm-accent-fill); color: var(--wp--preset--color--white); border-color: var(--schsbm-accent-fill); }
.schsbm-btn--accent:hover { background: var(--wp--preset--color--blue-core); border-color: var(--wp--preset--color--blue-core); color: var(--wp--preset--color--white); }

.schsbm-btn--outline { background: transparent; color: var(--wp--preset--color--blue-core); border-color: var(--wp--preset--color--blue-core); }
.schsbm-btn--outline:hover { background: var(--wp--preset--color--blue-core); color: var(--wp--preset--color--white); }

.schsbm-btn--outline-light { background: transparent; color: var(--wp--preset--color--white); border-color: rgba(255, 255, 255, .55); }
.schsbm-btn--outline-light:hover { background: var(--wp--preset--color--white); color: var(--wp--preset--color--blue-core); border-color: var(--wp--preset--color--white); }

/* A ghost button is a text link, so its label has to sit on the same left edge
   as the heading and body text above it. Both classes named deliberately: it is
   always used with a size modifier, and .schsbm-btn--sm sets padding later in
   the file, which was pushing the label 16px in from the card's text column. */
.schsbm-btn.schsbm-btn--ghost {
	background: transparent;
	color: var(--wp--preset--color--blue-core);
	padding-inline: 0;
	min-height: auto;
	justify-content: flex-start;
}
.schsbm-btn--ghost:hover { text-decoration: underline; text-underline-offset: 4px; }

.schsbm-btn--sm { min-height: 36px; padding: 8px 13px; font-size: .8125rem; }

/* "Touch targets 44 px minimum. Full journey works at 360 px." The compact
   variants keep their smaller footprint on pointer-precise screens and take the
   full target on touch-sized ones. */
@media (max-width: 900px) {
	.schsbm-btn--sm,
	.schsbm-btn--ghost { min-height: var(--schsbm-control-min); }
}
@media (pointer: coarse) {
	.schsbm-btn--sm,
	.schsbm-btn--ghost { min-height: var(--schsbm-control-min); }
}
.schsbm-btn--lg { padding: 15px 26px; font-size: .9375rem; }
.schsbm-btn--block { width: 100%; }

.schsbm-btn[disabled], .schsbm-btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }

/* ==========================================================================
   5. MASTHEAD — UWI utility band, co-branded lock-up, primary navigation
   ========================================================================== */

.schsbm-utility {
	background: var(--wp--preset--color--uwi-band);
	border-bottom: 1px solid #e4e5ec;
	font-size: .75rem;
	color: var(--wp--preset--color--ink-700);
}
/* The band is four short links that wrapped to a second row on a phone and
   stranded the last one. It scrolls instead — same component as the chips. The
   fade has to be told the band colour; it is not the page background. */
.schsbm-utility__inner { padding-block: 7px; display: flex; gap: 22px; flex-wrap: nowrap; --schsbm-scroller-fade: var(--wp--preset--color--uwi-band); }
.schsbm-utility a { color: inherit; text-decoration: none; padding-block: 4px; }
.schsbm-utility a:hover { color: var(--wp--preset--color--blue-core); text-decoration: underline; }

/* Pipes between the university links, as in the footer base row. The separator
   is generated, so it is never read out as content and never lands after the
   last item. The track's gap goes to zero — the pipe's own padding is the
   spacing now. */
.schsbm-scroller .schsbm-utility__nav { gap: 0; }
.schsbm-utility__nav .wp-block-navigation-item + .wp-block-navigation-item::before {
	content: "|";
	align-self: center;
	padding-inline: 11px;
	color: var(--wp--preset--color--ink-300);
}

/* Contact directory sits apart, hard right, the way the mockup's utility band
   reads. It keeps no pipe — it is not part of the run of campus links any more.
   Below 900px the band scrolls as one line instead, so the push is dropped. */
@media (min-width: 901px) {
	.schsbm-utility__nav .wp-block-navigation-item:last-child { margin-inline-start: auto; }
	.schsbm-utility__nav .wp-block-navigation-item:last-child::before { content: none; }
}
.schsbm-utility__right { margin-left: auto; display: flex; gap: 22px; }

.schsbm-lockup { background: var(--wp--preset--color--white); border-bottom: 1px solid var(--wp--preset--color--ink-200); }
.schsbm-lockup__inner { padding-block: 18px; display: flex; align-items: center; gap: 18px; flex-wrap: wrap; row-gap: 10px; }
.schsbm-lockup__crest {
	width: 42px; height: 42px; border-radius: 50%;
	background: linear-gradient(155deg, var(--wp--preset--color--blue-core), var(--wp--preset--color--blue-deep));
	color: var(--wp--preset--color--white);
	font-size: .6875rem; font-weight: 700;
	display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.schsbm-lockup__uwi { font-family: var(--wp--preset--font-family--display); font-size: .875rem; color: var(--wp--preset--color--blue-core); font-weight: 600; line-height: 1.1; }
.schsbm-lockup__uwi small { display: block; font-family: var(--wp--preset--font-family--body); font-weight: 500; font-size: .6875rem; color: var(--schsbm-text-muted); margin-top: 2px; }
.schsbm-lockup__divider { width: 1px; height: 36px; background: var(--wp--preset--color--ink-200); }
.schsbm-lockup__school { font-family: var(--wp--preset--font-family--display); font-size: 1.1875rem; font-weight: 700; color: var(--wp--preset--color--blue-core); line-height: 1.1; text-decoration: none; }
.schsbm-lockup__school small { display: block; font-family: var(--wp--preset--font-family--body); font-size: .65625rem; color: var(--schsbm-text-muted); font-weight: 500; margin-top: 3px; }
.schsbm-lockup__image { max-height: 56px; width: auto; }

/* The supplied co-branded artwork.
   The two halves arrive at different aspect ratios (507x113 and 648x216) and
   the school mark is two stacked lines against the university's one, so they
   are sized to match optically rather than to the same box height. The school
   JPEG was supplied on a 648x216 canvas with the mark occupying 58% of it; it
   is trimmed to its content so these heights mean what they say.

   Both classes are named on the height rules deliberately. woocommerce-layout
   ships `.woocommerce img, .woocommerce-page img { height: auto }` at (0,1,1),
   which outranks a lone modifier (0,1,0) — so the masthead and footer marks
   rendered at full natural size on cart, checkout and every account screen,
   and nowhere else. (0,2,0) settles it without !important. */
.schsbm-lockup__inner--art { gap: 18px; padding-block: 14px; }
.schsbm-lockup__art { width: auto; display: block; }
.schsbm-lockup__art.schsbm-lockup__art--uwi { height: 48px; }
.schsbm-lockup__art.schsbm-lockup__art--school { height: 50px; }
.schsbm-lockup__art-link { display: inline-flex; align-items: center; text-decoration: none; }

@media (max-width: 640px) {
	/* Fill the gutter rather than sitting small against it.
	   Fixed heights are dropped and each mark instead grows in proportion to
	   its own aspect ratio — 507/113 for the university, 380/116 for the school
	   — from a zero basis. Widths in proportion to aspect means both land on the
	   same height without either being told what that height is, so the pair
	   fills whatever the container gives it and stays inside the gutter. */
	.schsbm-lockup__inner--art { gap: 12px; }
	.schsbm-lockup__inner--art .schsbm-lockup__divider { align-self: stretch; height: auto; }

	.schsbm-lockup__art.schsbm-lockup__art--uwi {
		flex: 4.4867 1 0;
		width: 100%;
		height: auto;
		min-width: 0;
	}
	.schsbm-lockup__inner--art .schsbm-lockup__art-link {
		flex: 3.2759 1 0;
		min-width: 0;
	}
	.schsbm-lockup__art.schsbm-lockup__art--school {
		width: 100%;
		height: auto;
	}
}

.schsbm-primary { background: var(--wp--preset--color--white); border-bottom: 1px solid var(--wp--preset--color--ink-200); position: relative; }
.schsbm-primary__inner { display: flex; align-items: center; min-height: 58px; gap: 4px; }
/* The links carry 16px of horizontal padding so each one is a comfortable
   target. That padding also pushed the first link's TEXT 16px right of the
   content edge, so "Programmes" sat indented from the school name directly
   above it and from the page content below. Pulling the row left by exactly one
   link's padding puts the text on the edge and leaves every hit area intact.
   Optical alignment: what a reader lines up is the letterform, not the box. */
/* The navigation block puts its className on BOTH the <nav> wrapper and the
   <ul> inside it, so an unqualified .schsbm-primary__menu rule lands on two
   nested elements. The negative margin is meant to shift the row once — applied
   twice it would double — so it is scoped to the outer element. Everything
   below is safe on either and stays unqualified. */
.schsbm-primary__nav > .schsbm-primary__menu { margin-left: -16px; }
.schsbm-primary__menu { display: flex; list-style: none; margin: 0; padding: 0; gap: 2px; flex-wrap: wrap; }
/* Two classes deep so it outranks core's own inline navigation-block rule,
   `.wp-block-navigation .wp-block-navigation-item__content { display: block }`.
   That rule was winning, so the anchor was a 58px block with its text sitting
   at the top — 2px of space above the words and 40px below, which read as a
   strange gap under every link. align-items does nothing to a block. */
.schsbm-primary__nav .schsbm-primary__menu a,
.schsbm-primary__menu a {
	display: flex; align-items: center;
	padding: 0 16px; min-height: 58px;
	color: var(--wp--preset--color--ink-700);
	text-decoration: none;
	font-size: .875rem; font-weight: 500;
	border-bottom: 2px solid transparent;
	margin-bottom: -1px;
}
.schsbm-primary__menu a:hover { color: var(--wp--preset--color--blue-core); }
.schsbm-primary__menu .current-menu-item > a,
.schsbm-primary__menu .current-menu-ancestor > a,
.schsbm-primary__menu a[aria-current="page"] {
	color: var(--wp--preset--color--blue-core);
	border-bottom-color: var(--wp--preset--color--orange-core);
	font-weight: 600;
}
.schsbm-primary__actions { margin-left: auto; display: flex; gap: 10px; align-items: center; padding-block: 9px; }
.schsbm-primary__menu li { margin: 0; padding: 0; }

.schsbm-navtoggle {
	display: none;
	align-items: center; gap: 8px;
	margin-left: auto;
	min-height: var(--schsbm-control-min);
	padding: 8px 14px;
	background: transparent;
	border: 1px solid var(--wp--preset--color--ink-300);
	border-radius: var(--schsbm-radius-sm);
	font: inherit; font-size: .875rem; font-weight: 600;
	color: var(--wp--preset--color--blue-core);
	cursor: pointer;
}
.schsbm-navtoggle__bars { display: inline-block; width: 18px; height: 2px; background: currentColor; position: relative; }
.schsbm-navtoggle__bars::before, .schsbm-navtoggle__bars::after { content: ""; position: absolute; left: 0; width: 18px; height: 2px; background: currentColor; }
.schsbm-navtoggle__bars::before { top: -6px; }
.schsbm-navtoggle__bars::after { top: 6px; }

@media (max-width: 900px) {
	/* The toggle and the account actions share one row: on a 360px screen the
	   masthead already takes three bands before any content, and giving each
	   control its own row pushed the hero below the fold. */
	.schsbm-primary__inner { flex-wrap: wrap; align-items: center; gap: 10px; min-height: 56px; padding-block: 10px; }
	.schsbm-navtoggle { display: inline-flex; margin-left: 0; order: 1; }
	.schsbm-primary__actions { margin-left: auto; order: 2; padding-block: 0; }
	/* Same two classes as the rule that sets it. The desktop optical offset is
	   `.schsbm-primary__nav > .schsbm-primary__menu` at (0,2,0); a one-class
	   reset here lost to it, so the open panel's items and their dividing rules
	   started at 8px while the Menu button and the lock-up above them sat on the
	   24px gutter. */
	.schsbm-primary__nav > .schsbm-primary__menu { margin-left: 0; }
	.schsbm-primary__nav {
		order: 3;
		flex-basis: 100%;
		display: none;
		border-top: 1px solid var(--wp--preset--color--ink-200);
		padding-block: 8px;
	}
	.schsbm-primary__nav.is-open { display: block; }
	/* Two classes deep to outrank core's own .wp-block-navigation rules, which
	   set align-items: center and their own item padding. Centred items also
	   shrank to their text, so the dividing rule under each one spanned only the
	   words rather than the row — the alignment and the separators were the same
	   bug. Stretched, every item starts at the site gutter, in line with the
	   Menu button above it, and its rule runs the full width. */
	.schsbm-primary__nav .schsbm-primary__menu { flex-direction: column; gap: 0; align-items: stretch; }
	.schsbm-primary__nav .schsbm-primary__menu li { width: 100%; }
	.schsbm-primary__nav .schsbm-primary__menu a {
		/* The li is itself a flex row, so the anchor has to be told to fill it —
		   without this the anchor shrinks to its text and each dividing rule
		   stops at the end of the word rather than crossing the panel. */
		width: 100%;
		min-height: var(--schsbm-control-min);
		padding-inline: 0;
		justify-content: flex-start;
		border-bottom: 1px solid var(--wp--preset--color--ink-200);
	}
	/* No rule under the last item: it would sit directly on the panel's own
	   bottom edge and read as a double line. */
	.schsbm-primary__nav .schsbm-primary__menu li:last-child a { border-bottom: 0; }
	.schsbm-primary__nav .schsbm-primary__menu .current-menu-item > a,
	.schsbm-primary__nav .schsbm-primary__menu .current-menu-ancestor > a,
	.schsbm-primary__nav .schsbm-primary__menu a[aria-current="page"] {
		border-left: 3px solid var(--wp--preset--color--orange-core);
		padding-left: 10px;
	}
	.schsbm-utility__inner { column-gap: 16px; padding-block: 5px; font-size: .6875rem; }
	.schsbm-utility__right { margin-left: 0; column-gap: 16px; }
	.schsbm-utility a { padding-block: 2px; }
}

/* ==========================================================================
   6. HERO
   ========================================================================== */

.schsbm-hero { background: linear-gradient(160deg, var(--wp--preset--color--blue-core) 0%, var(--wp--preset--color--blue-deep) 78%); color: var(--wp--preset--color--white); }
.schsbm-hero h1, .schsbm-hero h2, .schsbm-hero h3 { color: var(--wp--preset--color--white); }
.schsbm-hero p { color: rgba(255, 255, 255, .82); }

/* Links on the hero.
   theme.json gives every link Blue Core, which is invisible on a Blue Core
   field — the business-line eyebrow on a programme page is a link, and it was
   rendering as dark blue on dark blue. Anything reversed out has to say so. */
.schsbm-hero a { color: var(--wp--preset--color--white); text-decoration-color: rgba(255, 255, 255, .5); }
.schsbm-hero a:hover { color: var(--wp--preset--color--orange-core); }
.schsbm-eyebrow.schsbm-eyebrow--on-dark a { color: inherit; text-decoration: none; }
.schsbm-eyebrow.schsbm-eyebrow--on-dark a:hover { color: var(--wp--preset--color--white); text-decoration: underline; text-underline-offset: 3px; }
.schsbm-hero .schsbm-lede { color: rgba(255, 255, 255, .86); }
.schsbm-hero__actions { display: flex; gap: 12px; margin-top: 26px; flex-wrap: wrap; }

/* Hero text shares one measure. Core's constrained layout centres post-excerpt
   inside the container, which indented the lede relative to the h1 above it. */
.schsbm-hero .wp-block-post-excerpt,
.schsbm-hero .wp-block-post-excerpt__excerpt { max-width: 66ch; margin-inline: 0; }

.schsbm-hero__media {
	border-radius: var(--schsbm-radius);
	min-height: 280px;
	background:
		linear-gradient(140deg, rgba(0, 0, 102, .25), rgba(0, 0, 77, .55)),
		linear-gradient(120deg, #1a2f6e 0%, #27407e 45%, #b96e1f 130%);
	display: flex; align-items: flex-end; padding: 16px;
	background-size: cover; background-position: center;
}
.schsbm-hero__media em { font-size: .75rem; color: rgba(255, 255, 255, .85); }

/* ==========================================================================
   7. AUDIENCE HELPER
   ========================================================================== */

/* A full-bleed band butts against whatever precedes it. Without this it picks
   up the 28px is-layout-flow margin and leaves a white stripe between the hero
   and the band. */
.schsbm-audience { background: var(--wp--preset--color--blue-paper); border-block: 1px solid var(--wp--preset--color--ink-200); margin-block-start: 0; }
.schsbm-audience__inner { padding-block: 18px; display: flex; gap: 14px; align-items: center; flex-wrap: nowrap; }
.schsbm-audience .schsbm-scroller { flex: 1 1 auto; min-width: 0; }
.schsbm-audience__label { flex: 0 0 auto; }
/* No margin-right: the row is a flex container and its gap owns the spacing
   between the label and the chips. The declaration was being overridden by
   core's `.is-layout-flex > :is(*, div) { margin: 0 }` and had been doing
   nothing — removed rather than escalated, because the 12px gap is already the
   spacing the design wants. */
.schsbm-audience__label { font-weight: 600; font-size: .875rem; }

.schsbm-chip {
	display: inline-flex; align-items: center;
	min-height: var(--schsbm-control-min);
	padding: 8px 16px;
	border: 1px solid var(--wp--preset--color--ink-300);
	border-radius: 999px;
	background: var(--wp--preset--color--white);
	font-size: .84375rem; font-weight: 500;
	color: var(--wp--preset--color--ink-700);
	text-decoration: none;
}
.schsbm-chip:hover { border-color: var(--schsbm-accent-text); color: var(--schsbm-accent-text); }
.schsbm-chip[aria-pressed="true"], .schsbm-chip.is-active { background: var(--wp--preset--color--blue-core); border-color: var(--wp--preset--color--blue-core); color: var(--wp--preset--color--white); }

/* ==========================================================================
   7b. SCROLLER — one row that scrolls instead of wrapping
   ========================================================================== */

/* A row of items that would otherwise wrap into a ragged block on narrow
   screens. The track scrolls; scroller.js adds the arrows, and only when the
   track actually overflows, so on a wide screen this is an ordinary flex row.

   Keyboard access comes from the items themselves — they are links and buttons,
   so tabbing to one scrolls it into view. The arrows are an additional pointer
   affordance, not the only way through, which is why the track needs no
   tabindex of its own. */

.schsbm-scroller { position: relative; display: flex; align-items: center; min-width: 0; }

.schsbm-scroller__track,
.schsbm-scroller .wp-block-navigation__container {
	display: flex;
	flex-wrap: nowrap;
	gap: 10px;
	overflow-x: auto;
	scroll-behavior: smooth;
	scroll-snap-type: x proximity;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	margin: 0;
	padding-block: 4px;
	min-width: 0;
}
.schsbm-scroller__track::-webkit-scrollbar,
.schsbm-scroller .wp-block-navigation__container::-webkit-scrollbar { display: none; }
.schsbm-scroller__track > *,
.schsbm-scroller .wp-block-navigation__container > * { flex: 0 0 auto; scroll-snap-align: start; }
.schsbm-scroller > nav { min-width: 0; flex: 1 1 auto; }

@media (prefers-reduced-motion: reduce) {
	.schsbm-scroller__track,
	.schsbm-scroller .wp-block-navigation__container { scroll-behavior: auto; }
}

/* Arrows sit over the track's edges. `is-scrollable` is set by the script only
   when there is somewhere to scroll to, so nothing appears on a wide screen. */
.schsbm-scroller__arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 2;
	display: none;
	place-items: center;
	width: 32px;
	height: 32px;
	padding: 0;
	border: 1px solid var(--wp--preset--color--ink-300);
	border-radius: 50%;
	background: var(--wp--preset--color--white);
	color: var(--wp--preset--color--blue-core);
	cursor: pointer;
	box-shadow: 0 1px 4px rgba(11, 13, 26, .12);
}
.schsbm-scroller.is-scrollable .schsbm-scroller__arrow { display: grid; }
.schsbm-scroller__arrow--prev { left: 0; }
.schsbm-scroller__arrow--next { right: 0; }
.schsbm-scroller__arrow svg { width: 16px; height: 16px; display: block; }
.schsbm-scroller__arrow:hover:not(:disabled) { border-color: var(--wp--preset--color--blue-core); }
.schsbm-scroller__arrow:disabled { opacity: 0; pointer-events: none; }
.schsbm-scroller__arrow:focus-visible {
	outline: 2px solid var(--wp--preset--color--orange-core);
	outline-offset: 2px;
}

/* Fades that say "there is more this way". Never intercept a click. */
.schsbm-scroller::before,
.schsbm-scroller::after {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	width: 48px;
	z-index: 1;
	pointer-events: none;
	opacity: 0;
	transition: opacity .15s ease;
}
.schsbm-scroller::before { left: 0; background: linear-gradient(to right, var(--schsbm-scroller-fade, var(--wp--preset--color--blue-paper)), transparent); }
.schsbm-scroller::after { right: 0; background: linear-gradient(to left, var(--schsbm-scroller-fade, var(--wp--preset--color--blue-paper)), transparent); }
.schsbm-scroller.can-scroll-prev::before,
.schsbm-scroller.can-scroll-next::after { opacity: 1; }


/* ==========================================================================
   8. CARDS
   ========================================================================== */

.schsbm-card {
	background: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--ink-200);
	border-radius: var(--schsbm-radius);
	padding: clamp(20px, 2.5vw, 34px);
	box-shadow: var(--schsbm-shadow-sm);
}
.schsbm-card > :first-child { margin-top: 0; }
.schsbm-card > :last-child { margin-bottom: 0; }
.schsbm-card--topline { border-top: 3px solid var(--wp--preset--color--orange-core); }
.schsbm-card--flush { padding: 0; overflow: hidden; }

/* ── "Your account at a glance" ────────────────────────────────────────────
   Used once, in the account teaser, so it carries its own class rather than
   leaning on .schsbm-card's proportions — that card is shared with the
   programme tiles and the 404 routes and must not shift.

   Four rows rather than a 2x2: the card sits in a 1fr 1fr split, so a grid
   would give each supporting sentence about 250px and break it over three
   lines. Rows keep one sentence to one or two lines at every width.

   A <dl> because these are term-and-description pairs, not a list of prose.
   No icons — the project has two one-off masked SVGs and no icon system, and
   this is not worth introducing one for. */
.schsbm-glance {
	padding: clamp(18px, 2vw, 24px);
}
.schsbm-glance__title {
	margin: 0 0 12px;
	font-family: var(--wp--preset--font-family--body);
	font-size: .75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .1em;
	color: var(--schsbm-text-muted);
}
/* 2x2. Four stacked rows made this taller than the bullet list it replaced —
   a label above a sentence costs two lines where a bullet cost one — which is
   the opposite of the point. Two columns halves the row count and the section
   comes out shorter than the original while saying more. One divider under the
   title carries the hierarchy; per-row rules would fight the grid. */
.schsbm-glance__list {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 16px 24px;
	margin: 0;
	padding-top: 14px;
	border-top: 1px solid var(--wp--preset--color--ink-200);
}
.schsbm-glance__item { min-width: 0; }

@media (max-width: 900px) {
	/* The split has already stacked, so the card has the full measure and the
	   sentences read better on one line each. In one column the pairs sit
	   directly above one another, so each gets a rule to separate it from the
	   one before — in the 2x2 the columns do that work and a rule would fight
	   the grid. */
	.schsbm-glance__list { grid-template-columns: 1fr; gap: 0; }
	.schsbm-glance__item { padding-block: 13px; border-top: 1px solid var(--wp--preset--color--ink-200); }
	.schsbm-glance__item:first-child { padding-top: 0; border-top: 0; }
	.schsbm-glance__item:last-child { padding-bottom: 0; }
}
.schsbm-glance__label {
	margin: 0 0 2px;
	font-weight: 600;
	font-size: .9375rem;
	color: var(--wp--preset--color--blue-core);
}
.schsbm-glance__detail {
	margin: 0;
	font-size: .8125rem;
	line-height: 1.5;
	color: var(--schsbm-text-muted);
}
.schsbm-card__body { padding: clamp(20px, 2.5vw, 28px); }
.schsbm-card__media { aspect-ratio: 16 / 9; background: var(--wp--preset--color--blue-tint); object-fit: cover; width: 100%; }
.schsbm-card h3 { margin-top: 0; }

/* ==========================================================================
   9. TABLES
   ========================================================================== */

.schsbm-tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table.schsbm-table { width: 100%; border-collapse: collapse; font-size: .875rem; min-width: 520px; }
table.schsbm-table th {
	text-align: left;
	font-size: .71875rem; text-transform: uppercase; letter-spacing: .08em;
	color: var(--schsbm-text-muted);
	padding: 8px 10px;
	border-bottom: 2px solid var(--wp--preset--color--ink-200);
	font-weight: 700;
	white-space: nowrap;
}
table.schsbm-table td { padding: 8px 10px; border-bottom: 1px solid var(--wp--preset--color--ink-200); vertical-align: middle; line-height: 1.45; }

/* A statement row was 64px because the date wrapped: "2 Aug" over "2026" in a
   column wide enough for neither. Dates and amounts are short and fixed, so
   they take exactly the room they need and stop deciding the height of the
   row; the description and reference keep whatever is left and wrap there,
   which is where wrapping actually belongs. */
table.schsbm-table td:first-child,
table.schsbm-table th:first-child { white-space: nowrap; width: 1%; }
table.schsbm-table td.is-num, table.schsbm-table th.is-num { white-space: nowrap; width: 1%; }
table.schsbm-table tr:last-child td { border-bottom: 0; }
table.schsbm-table td.is-num, table.schsbm-table th.is-num { text-align: right; font-variant-numeric: tabular-nums; }
table.schsbm-table caption { text-align: left; font-size: .8125rem; color: var(--schsbm-text-muted); padding-bottom: 8px; }

/* ==========================================================================
   10. BADGES
   ========================================================================== */

.schsbm-badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: .71875rem; font-weight: 600; }
.schsbm-badge--paid { background: var(--schsbm-success-tint); color: var(--schsbm-success); }
.schsbm-badge--due { background: var(--schsbm-warning-tint); color: var(--schsbm-warning); }
.schsbm-badge--draft { background: var(--wp--preset--color--blue-tint); color: var(--wp--preset--color--blue-core); }
.schsbm-badge--soon { background: var(--wp--preset--color--ink-100); color: var(--schsbm-text-muted); }
.schsbm-badge--error { background: var(--schsbm-error-tint); color: var(--schsbm-error); }

/*
 * Academic Yellow. The only place the token is used, and it always pairs with
 * ink-900. The component is rendered exclusively on degree-programme surfaces —
 * see Degree_Surface. Anything outside a degree surface falls back to Blue Tint.
 */
.schsbm-badge--degree { background: var(--wp--preset--color--blue-tint); color: var(--wp--preset--color--blue-core); }
body.is-degree-surface .schsbm-badge--degree,
/* The account dashboard's academic tile is itself a degree-bearing element, and
   the mockup marks it as the one permitted yellow cue on a cross-line surface:
   "The ONLY yellow on this view is the small degree cue on the Academic tile
   (degree-bearing surface), always with dark text." */
.schsbm-tile .schsbm-badge--degree {
	background: var(--wp--preset--color--academic-yellow);
	color: var(--wp--preset--color--ink-900);
}
body.is-degree-surface .schsbm-degree-rule { background: var(--wp--preset--color--academic-yellow); }
body.is-degree-surface .schsbm-card--degree { border-top: 3px solid var(--wp--preset--color--academic-yellow); }

/* ==========================================================================
   11. FORMS
   ========================================================================== */

.schsbm-field { margin-bottom: 18px; }
.schsbm-field > label,
.schsbm-field .schsbm-field__label {
	display: block;
	font-size: .84375rem;
	font-weight: 600;
	margin-bottom: 6px;
	color: var(--wp--preset--color--ink-900);
}
.schsbm-field .schsbm-required { color: var(--schsbm-accent-text); font-weight: 700; }


.schsbm-field .schsbm-optional { color: var(--schsbm-text-muted); font-weight: 500; }

.schsbm-field input[type="text"],
.schsbm-field input[type="email"],
.schsbm-field input[type="tel"],
.schsbm-field input[type="url"],
.schsbm-field input[type="number"],
.schsbm-field input[type="password"],
.schsbm-field input[type="date"],
.schsbm-field input[type="search"],
.schsbm-field select,
.schsbm-field textarea {
	width: 100%;
	min-height: var(--schsbm-control-min);
	padding: 11px 12px;
	border: 1px solid var(--wp--preset--color--ink-300);
	border-radius: 4px;
	font-size: .90625rem;
	font-family: inherit;
	background: var(--wp--preset--color--white);
	color: var(--wp--preset--color--ink-900);
}
.schsbm-field textarea { min-height: 120px; resize: vertical; }

/* ── Selects ───────────────────────────────────────────────────────────────
   Native <select>, wearing the control react-select draws: a calm bordered
   field, a hairline separator, and a chevron that reacts to hover — rather
   than whatever the operating system supplies, which is a different shape on
   every machine and the one control that gave the forms away as unstyled.

   Native on purpose. react-select is a React dependency plus its own
   accessibility surface, for a control the browser already gets right: keyboard
   behaviour, type-ahead, and the native picker wheel on a phone, which no
   scripted listbox matches. This is the appearance, not the machinery.

   The indicator is drawn as two background layers — the chevron and a 1px
   separator — because a <select> cannot carry pseudo-elements reliably across
   browsers. Both sit in the padding reserved on the trailing edge, and both are
   mirrored for right-to-left. */
.schsbm-field select,
.schsbm-application select,
.schsbm-portal select,
.schsbm-commerce select,
.schsbm-finder select,
.woocommerce form .form-row select,
select.schsbm-select {
	appearance: none;
	-webkit-appearance: none;
	padding-inline-end: 44px;
	background-color: var(--wp--preset--color--white);
	background-image:
		url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2020%2020'%20fill='none'%20stroke='%23646a7e'%20stroke-width='1.6'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M5%207.5%2010%2012.5%2015%207.5'/%3E%3C/svg%3E"),
		linear-gradient(var(--wp--preset--color--ink-200), var(--wp--preset--color--ink-200));
	background-repeat: no-repeat, no-repeat;
	background-position: right 12px center, right 38px center;
	background-size: 18px 18px, 1px 22px;
	cursor: pointer;
	transition: border-color .12s ease;
}
[dir="rtl"] .schsbm-field select,
[dir="rtl"] .schsbm-application select {
	background-position: left 12px center, left 38px center;
}

.schsbm-field select:hover,
.schsbm-application select:hover,
.schsbm-portal select:hover,
.schsbm-commerce select:hover,
.woocommerce form .form-row select:hover {
	border-color: var(--wp--preset--color--ink-400, #9497a3);
}

/* A disabled control should not look pressable. */
.schsbm-field select:disabled,
.schsbm-application select:disabled {
	background-color: var(--wp--preset--color--ink-100, #f0f0f3);
	color: var(--schsbm-text-muted);
	cursor: not-allowed;
	opacity: 1;
}

/* A multi-select renders as a list box, not a dropdown: no chevron, no
   separator, and no room reserved for either. */
.schsbm-field select[multiple],
.schsbm-field select[size]:not([size="1"]),
.schsbm-application select[multiple] {
	appearance: auto;
	-webkit-appearance: auto;
	background-image: none;
	padding-inline-end: 12px;
	min-height: 0;
	cursor: default;
}

/* Firefox draws its own highlight behind the selected option inside a focused
   select; this keeps the closed control reading as one field. */
.schsbm-field select:-moz-focusring,
.schsbm-application select:-moz-focusring { color: transparent; text-shadow: 0 0 0 var(--wp--preset--color--ink-900); }

/*
 * Focus indication on form controls.
 *
 * Declared with longhands and backed by a box-shadow ring rather than as a
 * single `outline` shorthand: a third-party stylesheet resetting `outline` on
 * one input type is enough to remove the indicator entirely, and the keyboard
 * pass caught exactly that on input[type=date] — it was reporting
 * outline-style: none while focused. Longhands plus a second, independent ring
 * mean no single reset elsewhere can take the indicator away.
 */
.schsbm-field input:focus,
.schsbm-field select:focus,
.schsbm-field textarea:focus,
.schsbm-application input:focus,
.schsbm-application select:focus,
.schsbm-application textarea:focus,
.woocommerce form .form-row input:focus,
.woocommerce form .form-row select:focus,
.woocommerce form .form-row textarea:focus {
	outline-style: solid;
	outline-width: 3px;
	outline-color: var(--wp--preset--color--orange-deep);
	outline-offset: 2px;
	box-shadow: 0 0 0 3px rgba(198, 100, 0, .25);
	border-color: var(--wp--preset--color--blue-core);
}
.schsbm-field .schsbm-hint { font-size: .78125rem; color: var(--wp--preset--color--ink-700); margin-top: 5px; }

.schsbm-field--choice label { display: flex; align-items: flex-start; gap: 10px; font-weight: 400; font-size: .9375rem; min-height: var(--schsbm-control-min); padding-block: 8px; }
.schsbm-field--choice input { width: 20px; height: 20px; margin-top: 2px; flex-shrink: 0; }

/* Inline, per-field errors. The spec forbids a single error summary at submit. */
.schsbm-field.has-error input, .schsbm-field.has-error select, .schsbm-field.has-error textarea { border-color: var(--schsbm-error); border-width: 2px; }
/* The error element is always in the DOM so a message can be announced into it
   without moving focus. It must therefore take no space and show no icon until
   it actually says something — an unexplained warning triangle under an
   untouched field tells the applicant they have done something wrong before
   they have done anything at all. */
.schsbm-field__error { display: none; margin-top: 6px; font-size: .8125rem; font-weight: 600; color: var(--schsbm-error); }
.schsbm-field__error:not(:empty) { display: block; }
.schsbm-field__error:not(:empty)::before { content: "⚠"; margin-right: 6px; }

.schsbm-form__actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 24px; max-width: var(--schsbm-measure-application, 46rem); }
.schsbm-form__note { font-size: .8125rem; color: var(--wp--preset--color--ink-700); }
/* Sits under the actions it describes, at the same width, reading as a note
   rather than an alert — nothing is wrong, this is what happens next. */
.schsbm-form__note--fee { max-width: var(--schsbm-measure-application, 46rem); margin: 12px 0 0; }

@media (max-width: 560px) {
	.schsbm-form__actions { flex-direction: column; align-items: stretch; }
	.schsbm-form__actions .schsbm-btn { width: 100%; text-align: center; }
}

/* The application form is single column at every width — a spec rule, not a
   responsive convenience. */
.schsbm-form--single-column { max-width: var(--schsbm-measure-panel); }

/*
 * Card measures. These replace per-view inline max-widths, which had drifted to
 * 480, 560 and 640 across otherwise identical screens.
 *
 * Declared after .schsbm-form--single-column deliberately: a card that is both
 * carries its measure from the card class, while that class keeps describing
 * the form's internal single-column layout.
 */
.schsbm-card--form { max-width: var(--schsbm-measure-form); }
.schsbm-card--panel { max-width: var(--schsbm-measure-panel); }
.schsbm-form--single-column .schsbm-field { max-width: 100%; }

/* ==========================================================================
   12. PROGRESS
   ========================================================================== */

/* ── The application ───────────────────────────────────────────────────────
   On the shared container, so the eyebrow, title, progress, form and fee note
   all sit on the same left edge as the navigation, the body of every other
   page and the footer. It ran edge to edge before this. */
.schsbm-application__inner { padding-block: clamp(28px, 4vw, 52px) clamp(48px, 7vw, 88px); }
.schsbm-application__title {
	font-size: clamp(1.6rem, 3vw, 2.375rem);
	/* The same width as the column beneath it, so the title and the form read
	   as one block. A tighter measure broke "Commercial Pilot Licence — ground
	   school application" across three lines for no reason. */
	max-width: var(--schsbm-measure-application, 46rem);
	margin: 4px 0 8px;
	text-wrap: balance;
}
/* A readable single column rather than a form stretched across a wide monitor.
   Reuses the measure the rest of the site uses for reading, not a width picked
   to suit one screenshot. */
.schsbm-application__card { max-width: var(--schsbm-measure-application, 46rem); padding: clamp(20px, 3vw, 32px); box-shadow: none; }
.schsbm-application__section { margin-bottom: 22px; }
.schsbm-application__section h2 { font-size: 1.2rem; margin: 0; }
.schsbm-application__section p { margin: 6px 0 0; color: var(--wp--preset--color--ink-700); font-size: .9375rem; }

.schsbm-progress { max-width: var(--schsbm-measure-application, 46rem); margin: 20px 0 24px; }
.schsbm-progress__label { font-size: .875rem; color: var(--wp--preset--color--ink-700); margin: 0 0 8px; }
.schsbm-progress__page::before { content: "·"; margin: 0 6px; }
.schsbm-progress__pct::before { content: "·"; margin: 0 6px; }

/* Three states, told three ways — height, fill and position — so the current
   page is identifiable without relying on colour. */
.schsbm-progress__steps { display: flex; gap: 6px; margin: 0 0 8px; padding: 0; list-style: none; }
.schsbm-progress__steps li { flex: 1; height: 6px; border-radius: 3px; background: var(--wp--preset--color--ink-200); }
.schsbm-progress__steps li.is-done { background: var(--wp--preset--color--orange-core); }
.schsbm-progress__steps li.is-current { background: var(--wp--preset--color--blue-core); height: 10px; margin-block: -2px; }

.schsbm-progress__saved { font-size: .8125rem; color: var(--wp--preset--color--ink-700); margin: 0; }
.schsbm-progress__saved::before { content: "✓"; color: var(--schsbm-success); font-weight: 700; margin-right: 6px; }
.schsbm-progress__resume { color: var(--schsbm-text-muted); }
.schsbm-progress__saved[data-state="saving"] { color: var(--schsbm-text-muted); }
.schsbm-progress__saved[data-state="saving"]::before { content: "…"; color: inherit; }
.schsbm-progress__saved[data-state="error"] { color: var(--schsbm-error); font-weight: 600; }
.schsbm-progress__saved[data-state="error"]::before { content: "⚠"; color: inherit; }

@media (max-width: 600px) {
	.schsbm-progress__page::before, .schsbm-progress__pct::before { content: none; }
	.schsbm-progress__page, .schsbm-progress__pct, .schsbm-progress__resume { display: block; }
}

/* ==========================================================================
   13. UPLOADS
   ========================================================================== */

.schsbm-upload__drop {
	border: 2px dashed var(--wp--preset--color--ink-300);
	border-radius: var(--schsbm-radius);
	padding: 26px;
	text-align: center;
	color: var(--wp--preset--color--ink-700);
	font-size: .875rem;
	background: var(--wp--preset--color--ink-100);
}
.schsbm-upload__drop.is-dragover { border-color: var(--wp--preset--color--orange-deep); background: var(--wp--preset--color--orange-tint); }

.schsbm-upload__item {
	display: flex; align-items: center; gap: 12px;
	padding: 11px 14px;
	border: 1px solid var(--wp--preset--color--ink-200);
	border-radius: 5px;
	margin-top: 10px;
	font-size: .875rem;
	background: var(--wp--preset--color--white);
}
.schsbm-upload__item--pending { border-style: dashed; color: var(--schsbm-text-muted); }
.schsbm-upload__name { font-weight: 600; }
.schsbm-upload__meta { color: var(--schsbm-text-muted); font-size: .78125rem; }
.schsbm-upload__ok { margin-left: auto; color: var(--schsbm-success); font-weight: 700; font-size: .78125rem; }
.schsbm-upload__remove { margin-left: auto; }

.schsbm-docicon {
	width: 34px; height: 40px;
	border-radius: 3px;
	background: var(--wp--preset--color--blue-tint);
	color: var(--wp--preset--color--blue-core);
	display: flex; align-items: center; justify-content: center;
	font-size: .625rem; font-weight: 700; flex-shrink: 0;
}
.schsbm-docicon--muted { background: var(--wp--preset--color--ink-100); color: var(--schsbm-text-muted); }

/* ==========================================================================
   14. PORTAL
   ========================================================================== */

.schsbm-portal { background: var(--wp--preset--color--paper); border-top: 1px solid var(--wp--preset--color--ink-200); min-height: 60vh; }

/* The home page reuses the portal's surface for its account teaser, purely for
   the background and the rule above it. The 60vh floor is there so a sparse
   dashboard still fills the window on a real portal screen; on a marketing band
   it just left ~90px of empty paper above the footer. */
.schsbm-portal--band { min-height: 0; }
.schsbm-portal__grid {
	display: grid;
	grid-template-columns: 230px minmax(0, 1fr);
	gap: clamp(28px, 4vw, 64px);
	padding-block: clamp(32px, 5vw, 72px) clamp(56px, 8vw, 104px);
}
@media (max-width: 900px) {
	.schsbm-portal__grid { grid-template-columns: 1fr; }

	.schsbm-portal__nav ul {
		display: flex;
		gap: 6px;
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		padding-bottom: 6px;
		margin-bottom: 8px;
		border-bottom: 1px solid var(--wp--preset--color--ink-200);
		scrollbar-width: thin;
	}
	.schsbm-portal__nav li { flex: 0 0 auto; }
	.schsbm-portal__nav a { white-space: nowrap; padding-inline: 14px; }
}

/* One spacing rule for the whole account navigation, not a margin per link.
   Every row is the same height, the same width and on the same left edge; the
   active and hover states differ only in colour, never in box size, so nothing
   shifts as you move down the list.

   Rows were already uniform before this — what was loose was their height. On a
   pointer they tighten to a comfortable 38px; on touch they stay at the 44px
   minimum, because WCAG 2.5.8 is not something to trade for density. */
.schsbm-portal__nav ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
/* Three selectors deep on purpose. WooCommerce's woocommerce-blocktheme.css
   sets `.woocommerce-account .woocommerce-MyAccount-navigation li { padding:
   1em 0 }`, which is more specific than a bare `.schsbm-portal__nav li` and was
   adding 32px to every row — the rows measured 38px while the pitch between
   them was 72. Resetting margin alone was not enough, because the space was
   padding on the li rather than margin. */
.schsbm-portal .schsbm-portal__nav ul li { margin: 0; padding: 0; }
.schsbm-portal__nav a {
	display: flex;
	align-items: center;
	min-height: 38px;
	padding: 6px 12px;
	font-size: .875rem;
	line-height: 1.4;
	color: var(--wp--preset--color--ink-700);
	text-decoration: none;
	border-radius: 4px;
}
@media (pointer: coarse) {
	.schsbm-portal__nav a { min-height: var(--schsbm-control-min); }
}
.schsbm-portal__nav a:hover { background: var(--wp--preset--color--blue-tint); color: var(--wp--preset--color--blue-core); }
/* Two selectors for one state: our own template marks the current page with
   aria-current, WooCommerce's marks it with a class on the li. Both must land
   on the same box, or the account pages disagree about what "active" looks
   like depending on which shell rendered them. */
.schsbm-portal__nav a[aria-current="page"],
.schsbm-portal__nav li.is-active > a { background: var(--wp--preset--color--blue-core); color: var(--wp--preset--color--white); font-weight: 600; }

/* Signing out is not one of the places you can go, so it is set apart —
   deliberately, once, rather than by hanging a margin on that one link. */
.schsbm-portal__nav li:last-child { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--wp--preset--color--ink-200); }

.schsbm-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; margin-bottom: 34px; }
@media (max-width: 900px) { .schsbm-tiles { grid-template-columns: 1fr; } }

.schsbm-tile {
	background: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--ink-200);
	border-left: 4px solid var(--wp--preset--color--orange-core);
	border-radius: var(--schsbm-radius);
	padding: 20px 22px;
	box-shadow: var(--schsbm-shadow-sm);
}
.schsbm-tile h3 { font-size: 1.0625rem; margin: 0 0 6px; }
.schsbm-tile p { font-size: .875rem; margin: 6px 0 10px; }
.schsbm-tile--muted { border-left-color: var(--wp--preset--color--ink-300); background: var(--wp--preset--color--ink-100); }
.schsbm-tile__cta { font-size: .84375rem; font-weight: 600; color: var(--wp--preset--color--blue-core); text-decoration: none; }
.schsbm-tile__cta:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ==========================================================================
   15. STATEMENT
   ========================================================================== */

.schsbm-statement__head {
	display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
	background: var(--wp--preset--color--blue-core);
	color: var(--wp--preset--color--white);
	border-radius: var(--schsbm-radius) var(--schsbm-radius) 0 0;
	padding: 18px 22px;
}
.schsbm-statement__label { font-size: .71875rem; text-transform: uppercase; letter-spacing: .1em; opacity: .78; }
.schsbm-statement__balance { font-family: var(--wp--preset--font-family--display); font-size: 1.5rem; font-weight: 600; }
.schsbm-statement__body {
	border: 1px solid var(--wp--preset--color--ink-200);
	border-top: 0;
	border-radius: 0 0 var(--schsbm-radius) var(--schsbm-radius);
	background: var(--wp--preset--color--white);
	padding: 6px 10px 14px;
}
.schsbm-statement__filters { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 18px; }
.schsbm-statement__filters .schsbm-field { margin-bottom: 0; }
.schsbm-ledger-reversal td { color: var(--wp--preset--color--ink-500); font-style: italic; }

/* ==========================================================================
   16. CHECKOUT
   ========================================================================== */

.schsbm-summary { background: var(--wp--preset--color--blue-paper); border: 1px solid var(--wp--preset--color--ink-200); border-radius: var(--schsbm-radius); padding: 24px; }
.schsbm-summary__row { display: flex; justify-content: space-between; gap: 16px; font-size: .90625rem; padding: 7px 0; }
.schsbm-summary__row--total { border-top: 2px solid var(--wp--preset--color--ink-300); margin-top: 8px; padding-top: 14px; font-weight: 700; font-size: 1rem; }

.schsbm-paypanel { border: 1px solid var(--wp--preset--color--ink-200); border-radius: var(--schsbm-radius); padding: 22px; margin-top: 16px; }
.schsbm-paypanel__gateway { display: flex; align-items: center; gap: 10px; font-weight: 600; font-size: .9375rem; }
.schsbm-gatewaymark { padding: 4px 10px; border-radius: 4px; background: var(--wp--preset--color--blue-core); color: var(--wp--preset--color--white); font-size: .75rem; font-weight: 700; letter-spacing: .04em; }
.schsbm-secure-note { font-size: .78125rem; color: var(--wp--preset--color--ink-700); margin-top: 10px; }

.schsbm-steps { display: flex; gap: 0; margin: 26px 0 30px; font-size: .8125rem; }
.schsbm-steps > * { flex: 1; text-align: center; padding: 9px 4px; border-bottom: 3px solid var(--wp--preset--color--ink-200); color: var(--schsbm-text-muted); }
.schsbm-steps > .is-done { border-color: var(--wp--preset--color--orange-core); color: var(--wp--preset--color--ink-900); font-weight: 600; }
@media (max-width: 560px) { .schsbm-steps { font-size: .6875rem; } }

/* ==========================================================================
   17. NOTICES
   ========================================================================== */

.schsbm-notice { border: 1px solid; border-radius: var(--schsbm-radius); padding: 14px 18px; font-size: .875rem; margin-block: 16px; }
.schsbm-notice > :first-child { margin-top: 0; }
.schsbm-notice > :last-child { margin-bottom: 0; }
.schsbm-notice--action { background: var(--wp--preset--color--orange-tint); border-color: var(--wp--preset--color--orange-core); color: var(--wp--preset--color--ink-900); }
.schsbm-notice--info { background: var(--wp--preset--color--blue-tint); border-color: var(--wp--preset--color--blue-core); color: var(--wp--preset--color--ink-900); }
.schsbm-notice--success { background: var(--schsbm-success-tint); border-color: var(--schsbm-success); color: #14532d; }
.schsbm-notice--error { background: var(--schsbm-error-tint); border-color: var(--schsbm-error); color: #7f1d1d; }

/* Development/sample content marker. Present so nobody mistakes placeholder
   copy for school-approved copy — the spec is explicit that all content is
   supplied by the school. */
.schsbm-sample-flag {
	display: inline-block; margin-bottom: 10px;
	padding: 2px 9px; border-radius: 3px;
	background: var(--wp--preset--color--ink-100); border: 1px dashed var(--wp--preset--color--ink-300);
	font-size: .6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: .06em;
	color: var(--wp--preset--color--ink-700);
}

/* ==========================================================================
   18. FOOTER
   ========================================================================== */

.schsbm-footer { background: var(--wp--preset--color--blue-core); color: rgba(255, 255, 255, .8); }
.schsbm-footer__inner { padding-block: clamp(40px, 6vw, 80px) 28px; }
.schsbm-footer__cols { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 32px; font-size: .84375rem; align-items: start; }
@media (max-width: 900px) { .schsbm-footer__cols { grid-template-columns: 1fr 1fr; } }
/* One column below 640px, with a rule between each group.
   The four groups are siblings in the grid, so the separator is a border on
   `* + *` rather than four <hr> elements the editor would have to carry. */
@media (max-width: 640px) {
	.schsbm-footer__cols { grid-template-columns: 1fr; gap: 0; }
	.schsbm-footer__cols > * + * {
		margin-top: 22px;
		padding-top: 22px;
		border-top: 1px solid rgba(255, 255, 255, .16);
	}
}
/* line-height because the school's own name is the one heading here that wraps,
   and uppercase at .1em tracking set solid reads as a block rather than two
   lines. The single-line headings beside it are unaffected. */
.schsbm-footer h2, .schsbm-footer h3, .schsbm-footer h4 { color: var(--wp--preset--color--white); font-size: .75rem; line-height: 1.55; text-transform: uppercase; letter-spacing: .1em; margin: 0 0 12px; font-family: var(--wp--preset--font-family--body); font-weight: 700; }
.schsbm-footer__cols ul { list-style: none; margin: 0; padding: 0; }
.schsbm-footer__cols a { display: block; color: rgba(255, 255, 255, .78); text-decoration: none; padding: 5px 0; }
.schsbm-footer__cols a:hover { color: var(--wp--preset--color--orange-core); text-decoration: underline; }
.schsbm-footer__about { font-size: .8125rem; line-height: 1.6; }
.schsbm-footer__base {
	border-top: 1px solid rgba(255, 255, 255, .16);
	margin-top: 38px; padding-top: 20px;
	display: flex; justify-content: space-between; align-items: baseline; gap: 8px 18px; flex-wrap: wrap;
	font-size: .75rem; color: rgba(255, 255, 255, .65);
}
.schsbm-footer__base a { color: inherit; }

/* The legal run was set tight against its middots. word-spacing opens the gaps
   either side of each separator without touching the copyright line, which is
   ordinary prose and would look loose if it were widened too. */
.schsbm-footer__legal { word-spacing: .32em; }
.schsbm-footer__legal a { padding-inline: 2px; }

/* The school's own name leads the footer, so it gets a rule the other three
   column headings do not. Orange Core and 3px, the same rule .schsbm-rule
   draws under section headings — kept shorter at 44px because the footer
   heading is a much smaller type size than the headings that rule sits under. */
.schsbm-footer__cols > *:first-child h2::after {
	content: "";
	display: block;
	width: 44px;
	height: 3px;
	margin-top: 10px;
	background: var(--wp--preset--color--orange-core);
}

/* The footer lock-up.
   The mockup's base row is three items — mark, legal links, copyright. The
   artwork the school supplied is dark-on-light and one half is a JPEG with an
   opaque white background, so it cannot sit on Blue Core; plating it on white
   read as a sticker. Text holds the slot until reversed artwork arrives, at
   which point render_footer_lockup() swaps to the image with no code change. */
.schsbm-footer__lockup { flex: 0 0 auto; min-width: 0; }
.schsbm-footer__lockup--placeholder a {
	color: rgba(255, 255, 255, .55);
	text-decoration: none;
	font-size: .6875rem;
	font-style: italic;
}
.schsbm-footer__lockup--placeholder a:hover { color: rgba(255, 255, 255, .8); }
/* Both classes: woocommerce-layout ships `.woocommerce img { height: auto }`
   at (0,1,1), which beats a lone modifier and would render this at full size on
   cart, checkout and the account screens. */
.schsbm-footer__lockup.schsbm-footer__lockup--reversed img { height: 40px; width: auto; display: block; }
.schsbm-footer a { color: rgba(255, 255, 255, .78); }
.schsbm-footer a:hover { color: var(--wp--preset--color--orange-core); }

/* ==========================================================================
   19. PROGRAMME FINDER
   ========================================================================== */

.schsbm-finder { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: clamp(28px, 4vw, 56px); align-items: start; }
@media (max-width: 900px) { .schsbm-finder { grid-template-columns: 1fr; } }
.schsbm-finder__filters { background: var(--wp--preset--color--white); border: 1px solid var(--wp--preset--color--ink-200); border-radius: var(--schsbm-radius); padding: 20px; position: sticky; top: 16px; }
@media (max-width: 900px) { .schsbm-finder__filters { position: static; } }
.schsbm-finder__group { border: 0; padding: 0; margin: 0 0 20px; }
.schsbm-finder__group legend { font-size: .78125rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--schsbm-text-muted); padding: 0; margin-bottom: 8px; }
.schsbm-finder__count { font-size: .875rem; color: var(--wp--preset--color--ink-700); margin-bottom: 18px; }
.schsbm-finder__results { display: grid; gap: 20px; }

/* ==========================================================================
   20. BLOCK EDITOR INTEROP
   ========================================================================== */

.wp-block-group.has-blue-core-background-color,
.wp-block-group.has-blue-deep-background-color { color: var(--wp--preset--color--white); }
.wp-block-group.has-blue-core-background-color :where(h1, h2, h3, h4, p) { color: inherit; }

/*
 * If an editor ever sets an Orange Core background, force dark text rather than
 * inheriting white from a parent. Orange Core with white text is a brand and
 * accessibility failure (§7) and must not be reachable by accident.
 */
.has-orange-core-background-color,
.has-academic-yellow-background-color { color: var(--wp--preset--color--ink-900) !important; }

.schsbm-prose > * + * { margin-top: 1.1em; }
.schsbm-prose h2 { margin-top: 1.8em; }
.schsbm-prose h3 { margin-top: 1.6em; }
.schsbm-prose ul, .schsbm-prose ol { padding-left: 1.4em; }
.schsbm-prose li + li { margin-top: .4em; }

/* Print — statements and receipts also render server-side to PDF, but a browser
   print of the on-screen statement should still be clean. */
@media print {
	.schsbm-utility, .schsbm-primary, .schsbm-footer, .schsbm-portal__nav, .schsbm-btn { display: none !important; }
	body { background: #fff; }
	.schsbm-portal__grid { grid-template-columns: 1fr; padding: 0; }
	.schsbm-statement__head { background: none; color: #000; border-bottom: 2px solid #000; }
}

/* ── Fees on a programme page ──────────────────────────────────────────────
   A fee is a line, not a product card: what it is, what it costs, and the
   button that pays it. No thumbnail, because a fee has nothing to show and a
   placeholder image beside "Examination fee" is the store assuming it is
   selling an object. */
.schsbm-fees { list-style: none; margin: 0; padding: 0; }
.schsbm-fee {
	display: grid;
	grid-template-columns: 1fr auto;
	align-items: baseline;
	gap: 2px 12px;
	padding: 12px 0;
	border-top: 1px solid var(--schsbm-rule, #e3e5ec);
}
.schsbm-fee:first-child { border-top: 0; padding-top: 0; }
.schsbm-fee__name { font-weight: 600; }
.schsbm-fee__price { font-variant-numeric: tabular-nums; white-space: nowrap; }
.schsbm-fee form { grid-column: 1 / -1; margin-top: 6px; }
.schsbm-btn--sm { padding: 8px 16px; font-size: 0.9rem; }

/* ── Three tiers of page header ────────────────────────────────────────────
   The distinction is already in the markup, so it does not need inventing:

     Feature      .schsbm-hero > .schsbm-container.schsbm-section  (homepage)
     Editorial    .schsbm-hero > .schsbm-container.schsbm-section--sm
     Transaction  .schsbm-transaction

   Only the middle tier changes here. The homepage keeps its full 120px band —
   that hero is doing the work of a front door — and the transactional header
   below stays the most compact.

   An editorial hero was 302px to carry a 58px title and a 56px line of
   supporting text: 160px of padding and a 28px gap between the two, none of
   which was a decision anybody made. The padding comes down and the gap becomes
   a deliberate 12px, which lands it at roughly 220px — about a quarter shorter,
   still unmistakably a branded band, and clearly taller than the transactional
   header it now sits above in the hierarchy. */
.schsbm-hero > .schsbm-container.schsbm-section--sm {
	padding-block: clamp(28px, 4vw, 48px);
}

/* The supporting line is secondary, not distant. WordPress's flow rule gave it
   28px from the title, which reads as two separate things rather than a heading
   and its subtitle. */
.schsbm-hero .schsbm-lede {
	margin-block-start: 12px;
	line-height: 1.55;
}

@media (max-width: 600px) {
	/* Less navy on a phone, where it is competing with the content for a much
	   smaller viewport. */
	.schsbm-hero > .schsbm-container.schsbm-section--sm { padding-block: 24px; }
	.schsbm-hero .schsbm-lede { margin-block-start: 10px; }
}

/* ── The transactional page header ─────────────────────────────────────────
   Two header patterns, deliberately, rather than one shrunk to fit both.

   An editorial page — Aviation, Programmes, a faculty profile — opens with the
   full navy hero, because arriving there the reader is deciding whether this is
   for them and the banner is doing work. Cart, checkout and the order
   confirmation are not that: somebody is part-way through paying, they know
   where they are, and 218px of navy above a 58px title is 160px of decoration
   between them and the form.

   So this is the same design language at a different weight: the shared
   container, the site's heading type, the orange rule the rest of the site uses
   to mark a heading, and a hairline to close the band. No new visual language,
   and no change to the editorial hero.

   Shared as a template part, so cart, checkout and order-received all draw the
   same header and none of them carries its own copy of the markup. */
.schsbm-transaction {
	border-bottom: 1px solid var(--wp--preset--color--ink-200);
	background: var(--wp--preset--color--white);
}
.schsbm-transaction__inner {
	padding-block: clamp(20px, 3vw, 34px) clamp(16px, 2.4vw, 26px);
}
.schsbm-transaction__title {
	margin: 0;
	font-size: clamp(1.5rem, 2.4vw, 2rem);
	line-height: 1.15;
	color: var(--wp--preset--color--blue-core);
}
/* The same accent that marks a heading elsewhere on the site, at the width it
   uses there — the band is recognisably SCHSBM without repeating the hero. */
.schsbm-transaction__title::after {
	content: "";
	display: block;
	width: 56px;
	height: 3px;
	margin-top: 12px;
	background: var(--wp--preset--color--orange-core);
}
.schsbm-transaction__eyebrow {
	margin: 0 0 6px;
	font-size: .75rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--wp--preset--color--orange-deep);
}
.schsbm-transaction__lede {
	margin: 12px 0 0;
	max-width: var(--schsbm-measure, 920px);
	color: var(--wp--preset--color--ink-700);
}

@media (max-width: 600px) {
	/* Tighter still on a phone, where the form matters most and the header is
	   pure overhead. */
	.schsbm-transaction__inner { padding-block: 16px 14px; }
	.schsbm-transaction__title::after { margin-top: 9px; }
}

/* WooCommerce pads the top of its own checkout and cart blocks, which is right
   when the block is dropped into an unstyled page and wrong here: the section
   around it already owns that space. Removed rather than absorbed, so there is
   one answer to how far the form sits below the heading. */
.schsbm-commerce > .entry-content > .wp-block-woocommerce-checkout,
.schsbm-commerce > .entry-content > .wp-block-woocommerce-cart { padding-top: 0; }

/* The section's own top-level blocks — the store notices and the page content —
   take their spacing from its padding and their own margins, not from
   WordPress's flow rule. Written for every direct child rather than :first-child
   because the notices block sits ahead of the content, so the content is second
   and the :first-child form missed it entirely. Editor spacing inside the
   content is untouched. */
.schsbm-commerce > * { margin-block-start: 0; }

/* ── Commerce surfaces ─────────────────────────────────────────────────────
   WooCommerce runs the cart, the checkout and the order. What it must not do
   is make a course seat look like a catalogue item.

   No product imagery. Nothing this school sells is a thing you can photograph:
   a cohort seat and an examination fee have no picture, so WooCommerce renders
   a placeholder — or, with no placeholder set, a broken-image icon beside the
   line. The column goes rather than the picture being faked. */
.schsbm-commerce .wc-block-cart-item__image,
.schsbm-commerce .wc-block-components-order-summary-item__image,
.schsbm-commerce .wc-block-cart .wc-block-cart-item__image { display: none; }
.schsbm-commerce .wc-block-cart-item__wrap { padding-left: 0; }
/* With the image column gone, WooCommerce's proportions leave the product name
   in a 143px sliver beside a 429px total. The name takes the room; the total
   takes what it needs. */
.schsbm-commerce .wc-block-cart-item__product { width: 100%; }
.schsbm-commerce .wc-block-cart-item__total { width: 1%; white-space: nowrap; text-align: right; vertical-align: top; }

/* Table density and type, matched to the rest of the site rather than left at
   WooCommerce's defaults. */
.schsbm-commerce table th,
.schsbm-commerce .wc-block-cart-items__header { font-size: .8125rem; letter-spacing: .04em; text-transform: uppercase; color: var(--wp--preset--color--ink-700); }
.schsbm-commerce .wc-block-components-product-name { font-weight: 600; text-decoration: none; }
.schsbm-commerce .wc-block-components-totals-item__label { font-weight: 500; }
.schsbm-commerce .wc-block-components-totals-footer-item .wc-block-components-totals-item__value { font-weight: 700; }

/* Buttons: the site has one button system and the checkout is not exempt. */
.schsbm-commerce .wc-block-components-button:not(.is-link) {
	background: var(--wp--preset--color--blue-core);
	border-radius: 2px;
	font-weight: 600;
	min-height: 48px;
}
.schsbm-commerce .wc-block-components-button:not(.is-link):hover { background: var(--wp--preset--color--blue-700, #000052); }
.schsbm-commerce .wc-block-components-button:not(.is-link):focus-visible {
	outline: 3px solid var(--schsbm-accent-fill, #b85d00);
	outline-offset: 2px;
}

/* Empty cart: a dead end on a fee payment is worse than on a shop, because the
   visitor was sent here to pay something specific. */
/* Capped to the measure so the line of copy does not run the full 1112px, but
   it has to be centred in the container as well — with the default margin of 0
   it was ranged left and sat 96px off the page's true centre, while everything
   inside it was centred within the box. Centred box, centred contents. */
.schsbm-commerce .wp-block-woocommerce-empty-cart-block {
	max-width: var(--schsbm-measure, 920px);
	margin-inline: auto;
}

/* WooCommerce's own notice banner, brought into the site's notice language.
   Its defaults are a bright green field, a filled green disc with a tick in it
   and a 4px radius — a shop's confirmation, and the one element on the cart
   that announces which plugin is underneath.

   The SCHSBM notice system already exists for this: a quiet tint, a hairline
   border, a rule down the leading edge and the site's own radius. Only the
   presentation changes — the role, the tabindex and the live-region behaviour
   WooCommerce relies on to announce the message are untouched. */
.schsbm-commerce .wc-block-components-notice-banner {
	align-items: center;
	gap: 10px;
	padding: 12px 16px;
	border: 1px solid var(--wp--preset--color--ink-200);
	border-left: 3px solid var(--wp--preset--color--ink-400, #9497a3);
	border-radius: var(--schsbm-radius-sm, 3px);
	background: var(--wp--preset--color--paper, #f7f7fa);
	font-size: .875rem;
	color: var(--wp--preset--color--ink-900);
}
.schsbm-commerce .wc-block-components-notice-banner.is-success {
	border-color: var(--schsbm-success-tint);
	border-left-color: var(--schsbm-success);
	background: var(--schsbm-success-tint);
	color: #14532d;
}
.schsbm-commerce .wc-block-components-notice-banner.is-error {
	border-color: var(--schsbm-error-tint);
	border-left-color: var(--schsbm-error);
	background: var(--schsbm-error-tint);
}

/* The icon loses its filled disc and becomes a mark in the notice's own colour,
   at the size of the text it sits beside rather than half again as tall. */
.schsbm-commerce .wc-block-components-notice-banner > svg {
	flex: 0 0 auto;
	width: 18px;
	height: 18px;
	margin: 0;
	padding: 0;
	border-radius: 0;
	background: none;
	fill: currentcolor;
}
.schsbm-commerce .wc-block-components-notice-banner .wc-block-components-notice-banner__content {
	margin: 0;
	padding: 0;
}
/* "View cart", on the cart. A quiet link rather than an underlined call to
   action competing with Proceed to Checkout below it. */
.schsbm-commerce .wc-block-components-notice-banner a {
	color: inherit;
	text-underline-offset: 2px;
	text-decoration-thickness: 1px;
}

/* An empty cart does not need a banner announcing which page it is. The card in
   the middle of the screen already says "Your cart is currently empty!", and a
   full-width navy band above it introduces a page with nothing on it.

   Keyed off the block WooCommerce actually renders: when the cart is empty the
   filled block is absent from the DOM entirely, so :has() tracks the real state
   and keeps up when the last item is removed without a page load. Where :has()
   is unsupported the banner simply stays, which is where it was.

   The heading is hidden visually rather than removed. It is the page's only h1,
   and a document that announces itself as "Cart" to a screen reader while
   showing nothing to a sighted reader is the right way round — dropping it
   would leave the page with no h1 at all. */
main:has(.wp-block-woocommerce-empty-cart-block) .schsbm-hero {
	background: none;
	padding-block: 0;
}
main:has(.wp-block-woocommerce-empty-cart-block) .schsbm-hero .schsbm-container {
	padding-block: 0;
}
main:has(.wp-block-woocommerce-empty-cart-block) .schsbm-hero h1 {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* WooCommerce draws a crying face above the empty cart. It is a shop's voice,
   and it is aimed at somebody who has come to pay a university fee — a mild
   commiseration for a situation that calls for directions instead.

   The shape is swapped rather than the mechanism: WooCommerce masks a data URI
   and paints it with currentcolor, so this keeps its sizing and its colour and
   supplies a plain bag outline. Left percent-encoded rather than base64 so the
   next person can read what it draws and change it. */
.schsbm-commerce .wc-block-cart__empty-cart__title.with-empty-cart-icon::before {
	mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='1.4'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M3.5%207.5h17l-1.5%2011a2%202%200%200%201-2%201.7H7a2%202%200%200%201-2-1.7l-1.5-11Z'/%3E%3Cpath%20d='M8.75%207.5V5.75a3.25%203.25%200%200%201%206.5%200V7.5'/%3E%3C/svg%3E");
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='1.4'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M3.5%207.5h17l-1.5%2011a2%202%200%200%201-2%201.7H7a2%202%200%200%201-2-1.7l-1.5-11Z'/%3E%3Cpath%20d='M8.75%207.5V5.75a3.25%203.25%200%200%201%206.5%200V7.5'/%3E%3C/svg%3E");
	opacity: .55;
}

/* The statement's show-more control. The count sits beside the button rather
   than under it: it is confirmation of what just happened, not a heading for
   what comes next. */
.schsbm-ledger__more { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 14px 0 0; }
.schsbm-ledger__more .schsbm-meta { margin: 0; }
/* A revealed row is focused programmatically so a keyboard reader continues
   from where the statement grew; the ring makes that visible. */
.schsbm-table td:focus-visible { outline: 3px solid var(--schsbm-accent-fill, #b85d00); outline-offset: -3px; }
