/* Golden Key Training Centre · v4.1, 2026-09-05
   Design lock is in PROJECT.md. Do not add a colour or a face without adding it there.

   COLOUR-BLOCKED LIGHT. Fourth palette on this build. v3 (2026-09-04) was a white page
   with hairlines and grey text, measured 28% under the reference library's section
   floor, and it read as a designer's portfolio rather than a training centre. Anirudh
   called it plain, empty and static on 2026-09-05 and chose this direction over a
   photographic dark hero and over going back to dark. v4.1 the same evening: the first
   cut was reviewed at 1:1 and the spacing, alignment and pill clutter were redone as
   one system, which is what most of the comments below describe.

   The reference is aardvark-book-club in the vault library, the only measured entry
   for a colour-blocked light page: one saturated ground per section, colour rationed
   against white by painted area, two display sizes and a hard drop, and page-level
   slabs rounded on a scale the components do not share. Structure only. No illustration.

   GOLD IS A SURFACE, STILL NEVER TEXT. #F4B62B on white is 1.81:1. It paints the hero
   slab, the CTA slab, one bento cell, the nav underline and the callout edge. Ink sits
   on it at 10.25:1 and --paper-2 at 4.54:1, so a gold slab can carry a paragraph. There
   is deliberately no dark-gold text variant: taste-skill 4.2 names #b08947, #9c6e2a and
   #bc7c3a, and a gold that passes as text is one of those every time.

   THE NEUTRALS RUN COOL for the same reason as v3: the obvious light palette for a gold
   brand is warm off-white on warm near-black, which is #f7f5f1 on #1a1714, both on the
   same banned list. Cool grey slabs leave the gold as the only warm thing on the page.

   PRIMARY BUTTONS ARE INK, on every ground. One accent used one way (taste-skill 14,
   Color Consistency Lock): gold is the slab, ink is the control that sits on it.

   ONE SPACING SYSTEM. --gap-section is the white space between any two visible blocks,
   bands carry half of it each side, so a heading after a card row, a slab after a
   heading and a slab after a slab are all the same distance apart. Slabs inset their
   content by --pad-slab, tiles by --pad-tile, and everything in a grid sits --gap apart.
   Every tile is grey on white and white on grey, by one rule near the end of the
   tiles section, so nothing has to remember which ground it is on.

   SHAPE: slabs round at --r-slab (22 to 44px, page-level), tiles and images at 22px,
   inputs at 12px, buttons pill. Four values, four jobs, nothing else rounds.

   TYPE: one grotesque, Archivo, self-hosted and unchanged. Display went from 500 to
   700 and roughly doubled: h1 caps at 128px, h2 at 72px, then a hard drop to 22px.
   aardvark runs 160/700 and 104/700 with nothing between 104 and 32. The arrow on a
   card link is drawn in CSS: Archivo's latin subset does not carry U+2192, so the
   glyph fell back to the system face and sat on its own baseline. */

/* ---------- Archivo, self-hosted ---------- */
/* One VARIABLE file per subset covering the whole 400 to 700 axis, ranges copied from
   Google's own css2 response. Nothing reaches fonts.googleapis.com; see the privacy
   notice, which says so. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url('/assets/fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url('/assets/fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url('/assets/fonts/archivo-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

:root {
  /* GROUNDS. --ink is the ground and --paper the text, names inherited from the dark
     build and kept because renaming them touches every rule to change nothing. */
  --ink:      #FFFFFF;
  --slab:     #EEF0F4;                   /* the grey slab, 1.14:1 on white: a shape, not a colour */
  --slab-2:   #E4E7ED;                   /* tile hover on white, media backing */

  /* TEXT. Measured on white / slab / gold. */
  --paper:   #111316;                    /* 18.61 / 16.31 / 10.25 */
  --paper-2: #4A4F57;                    /*  8.24 /  7.23 /  4.54, the only grey allowed on gold */
  --paper-3: #5B6068;                    /*  6.33 /  5.55, never on gold */
  --on-ink:  #FFFFFF;                    /* text on an ink button */

  /* GOLD. One gold, painted only. */
  --gold:      #F4B62B;
  --gold-deep: #E0A21A;                  /* hover, downward in lightness; ink on it 8.29 */
  --gold-wash: #FFF3D1;                  /* current item in the courses menu, ink on it 16.83 */

  /* LINES. --line is decorative; --line-strong is 3.35:1 on the slab and may enclose
     an input. Neither passes on gold (2.11), so controls on gold use ink edges. */
  --line:        #E3E6EB;
  --line-strong: #7D838B;

  --danger: #B3261E;                     /* 6.54 white / 5.89 slab */
  --ok:     #186B33;

  /* SHAPE LOCK. Four values, four jobs. */
  --r-slab:  clamp(22px, 3.2vw, 44px);
  --r-tile:  22px;
  --r-input: 12px;
  --r-pill:  999px;

  /* SPACING. One system, see the header comment. */
  --gap-section: clamp(3.5rem, 7vw, 7rem);   /* white space between any two visible blocks */
  --gap:      clamp(.75rem, 1.4vw, 1.25rem); /* between tiles in a grid */
  --pad-tile: clamp(1.25rem, 2.2vw, 1.75rem);
  --pad-slab-x: clamp(1rem, 4vw, 3.5rem);
  --pad-slab-y: clamp(1.5rem, 4vw, 3.5rem);
  --head-gap: clamp(1.5rem, 3vw, 2.5rem);    /* heading group to the block under it */

  --measure: 60ch;
  --inset: 1.25rem;
  --shell: 1360px;

  --sans: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
}

@media (min-width: 900px) { :root { --inset: 2.5rem; } }

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 5.5rem; color-scheme: light; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 1.125rem;
  line-height: 1.55;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}

/* TWO display sizes and a hard drop. h1 128px at 1920 against aardvark's 160 clears
   the library's 20% fail line; h2 is locked to one size the way aardvark locks five of
   eight h2s to 104. Long course titles reuse the h2 size (.h1--long) so the hero
   headline stays inside two lines at 1440. */
h1, h2, h3, h4 { font-family: var(--sans); margin: 0; text-wrap: balance; }
h1 { font-size: clamp(2.9rem, 7.4vw, 8rem); font-weight: 700; line-height: .92; letter-spacing: -0.04em; }
h2 { font-size: clamp(2.1rem, 4.8vw, 4.5rem); font-weight: 700; line-height: .98; letter-spacing: -0.035em; max-width: 18ch; }
.h1--long { font-size: clamp(2.1rem, 4.8vw, 4.5rem); line-height: .98; letter-spacing: -0.035em; }
h3 { font-size: 1.375rem; font-weight: 600; line-height: 1.2; letter-spacing: -0.02em; }
h4 { font-size: 1.125rem; font-weight: 600; line-height: 1.3; letter-spacing: -0.01em; }
p { margin: 0 0 1em; max-width: var(--measure); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }
a { color: inherit; }
img { max-width: 100%; height: auto; }
/* Every photograph is wrapped in <picture> since 2026-09-11 so an AVIF source can sit
   beside the JPEG. display: contents makes the wrapper generate no box, so .media is
   laid out exactly as it was when the <img> stood alone. */
picture { display: contents; }
address { font-style: normal; }

.shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: var(--inset); }

/* ---------- header ---------- */
/* Three states, driven by two attributes site.js sets from IntersectionObservers.
   At the top of the page the bar is solid gold, so the nav and the hero are one gold
   block. Once the page has scrolled and the hero is still under the bar, the bar is
   gold glass: the headline blurs through it and stays readable. Once the hero has
   gone past, it is white glass with a hairline. A white plate over gold was tried
   first (v4) and read as a cream band; a gold plate over gold is invisible, which is
   the point. Solid fallbacks for the reduced-transparency setting and for browsers
   without backdrop-filter. */
.site-head {
  position: sticky; top: 0; z-index: 40;
  background: var(--gold);
  border-bottom: 1px solid transparent;
  transition: background-color 320ms ease, border-color 320ms ease;
}
.site-head[data-scrolled="true"] {
  background: rgba(244, 182, 43, .84);
  -webkit-backdrop-filter: saturate(160%) blur(18px);
  backdrop-filter: saturate(160%) blur(18px);
}
.site-head[data-past-hero="true"] {
  background: rgba(255, 255, 255, .84);
  border-bottom-color: var(--line);
}
@media (prefers-reduced-transparency: reduce) {
  .site-head[data-scrolled="true"] { background: var(--gold); backdrop-filter: none; -webkit-backdrop-filter: none; }
  .site-head[data-past-hero="true"] { background: var(--ink); }
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-head[data-scrolled="true"] { background: var(--gold); }
  .site-head[data-past-hero="true"] { background: var(--ink); }
}
/* The gold underline has no contrast on the gold bar, so it is ink until the hero has
   gone past, and the drawer's own top edge is what separates it from the bar. */
.site-head:not([data-past-hero="true"]) .nav > a::after,
.site-head:not([data-past-hero="true"]) .nav__group > a::after { background: var(--paper); }
.site-head:not([data-past-hero="true"]) .wordmark span { color: var(--paper-2); }
.site-head__inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: 72px; }

.wordmark { display: flex; align-items: baseline; gap: .55rem; text-decoration: none; line-height: 1; padding-block: .7rem; }
.wordmark strong { font-size: 1.2rem; font-weight: 700; letter-spacing: -0.035em; }
.wordmark span { font-size: .95rem; font-weight: 400; color: var(--paper-3); letter-spacing: -0.01em; }

.nav { display: none; gap: 1.5rem; align-items: center; }
/* Scoped to direct children of the nav and the group: the dropdown holds links too and
   the sliding underline is a top-level affordance. */
.nav > a, .nav__group > a {
  position: relative; text-decoration: none; font-size: 1rem; font-weight: 500;
  padding-block: .5rem; white-space: nowrap;
}
.nav > a::after, .nav__group > a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: .05rem; height: 3px;
  border-radius: var(--r-pill);
  background: var(--gold); transform: scaleX(0); transform-origin: left;
  transition: transform 200ms var(--ease-out);
}
.nav > a[aria-current="page"]::after { transform: scaleX(1); }
.nav > a[aria-current="page"] { font-weight: 600; }
.nav__group[data-active="true"] > a::after { transform: scaleX(1); }
/* The phone number is the site's real call to action and the client's traffic is
   phones, so it is a button rather than a grey link. */
.nav .tel {
  display: inline-flex; align-items: center; min-height: 44px; padding: .55rem 1.1rem; margin-left: .25rem;
  background: var(--paper); color: var(--on-ink); border-radius: var(--r-pill);
  font-size: .95rem; font-weight: 600; text-decoration: none; font-variant-numeric: tabular-nums;
  transition: transform 160ms var(--ease-out), background-color 180ms ease;
}
.nav .tel::after { content: none; }
@media (hover: hover) and (pointer: fine) {
  .nav > a:hover::after, .nav__group:hover > a::after { transform: scaleX(1); }
  .nav .tel:hover { background: #2A2E35; }
}
.nav .tel:active { transform: scale(.97); }
/* 1240 is where the desktop row appears. Below it the Menu pill and the drawer take
   over, which includes an iPad in landscape.

   RE-MEASURED 2026-09-09, because the previous note here was wrong. It claimed "70px
   in hand" with Jobs added; the real figure with "Trainings" was 195.5px. That matters
   because the label then changed: "Trainings" became "Quality Policies and Procedures",
   which is 163px wider, and on the old figure it would have been impossible.

   Measured at a real 1240 viewport, not estimated: the wordmark, seven items, the
   chevron and the phone pill now leave **27 to 32px** of slack inside the shell. The
   row does not wrap and the header height is unchanged at 72.7px.

   THERE IS NO ROOM LEFT. An eighth nav item, or a longer label on any existing one,
   overflows the row. Measure before adding one, and if it does not fit, the fix is to
   raise this breakpoint rather than to let the header wrap. */
@media (min-width: 1240px) { .nav { display: flex; } .nav-toggle { display: none; } }

.nav-toggle {
  appearance: none; background: var(--paper); color: var(--on-ink); border: 0; border-radius: var(--r-pill);
  padding: .65rem 1.1rem; min-height: 44px; font: inherit; font-size: .95rem; font-weight: 600; cursor: pointer;
  transition: transform 150ms var(--ease-out);
}
.nav-toggle:active { transform: scale(.96); }

/* Transition, not keyframes, so a fast double-tap retargets instead of restarting.
   visibility as well as opacity, or the closed drawer's links stay in the tab order. */
.nav-drawer {
  overflow: hidden; background: var(--ink); border-top: 1px solid rgba(17, 19, 22, .12);
  display: grid; grid-template-rows: 0fr; opacity: 0; visibility: hidden;
  transition: grid-template-rows 260ms var(--ease-out), opacity 180ms ease, visibility 0s linear 260ms;
}
.nav-drawer > div { min-height: 0; }
.nav-drawer[data-open="true"] { grid-template-rows: 1fr; opacity: 1; visibility: visible; transition-delay: 0s; }
.nav-drawer a { display: block; padding: 1rem 0; text-decoration: none; font-weight: 500; font-size: 1.05rem; border-bottom: 1px solid var(--line); }
.nav-drawer a:last-child { border-bottom: 0; }
.nav-drawer .tel { font-weight: 600; }
/* After .nav-drawer in source: same specificity, and display:grid above would win. */
@media (min-width: 1240px) { .nav-drawer { display: none; } }

/* ---------- buttons ---------- */
/* Ink primary on every ground. Ghost is an ink 2px edge, the only border that clears
   3:1 on gold. Below 480px a row of buttons becomes a stack of full-width ones, because
   two pills of different widths stacked left-aligned read as a ragged edge. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  min-height: 54px; padding: .85rem 1.7rem; white-space: nowrap;
  font: inherit; font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; text-decoration: none;
  border: 2px solid var(--paper); border-radius: var(--r-pill); cursor: pointer;
  background: var(--paper); color: var(--on-ink);
  transition: transform 160ms var(--ease-out), background-color 180ms ease, border-color 180ms ease, color 180ms ease;
}
.btn:active { transform: scale(.975); }
.btn--ghost { background: transparent; color: var(--paper); }
@media (hover: hover) and (pointer: fine) {
  .btn:hover { background: #2A2E35; border-color: #2A2E35; }
  .btn--ghost:hover { background: var(--paper); color: var(--on-ink); }
}
.btn[disabled] { opacity: .5; cursor: default; transform: none; }
.actions { display: flex; flex-wrap: wrap; gap: .75rem; }
@media (max-width: 479px) { .actions .btn, .enquire__form .btn { width: 100%; } }

/* Ink ring, 3px, offset, following the element's own corners. On an ink button the
   offset gap is what makes it visible. */
:where(a, button, input, textarea, select):focus-visible {
  outline: 3px solid var(--paper); outline-offset: 3px;
}

/* ---------- scroll reveal ---------- */
/* The hidden state only exists under .reveal-on, which JS adds, so content is never
   gated on a script. Opacity plus 12px, nothing more. */
.reveal-on [data-reveal] {
  opacity: 0; transform: translateY(12px);
  transition: opacity 480ms var(--ease-out), transform 480ms var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
.reveal-on [data-reveal][data-shown] { opacity: 1; transform: none; }

/* ---------- media ---------- */
.media { display: block; width: 100%; height: auto; object-fit: cover; background: var(--slab-2); border-radius: var(--r-tile); }
/* A portrait photograph cropped to landscape keeps its subject's head. */
.media--top { object-position: center 22%; }

/* ---------- rhythm: bands, slabs, heads ---------- */
.band { padding-block: calc(var(--gap-section) / 2); }
.band__head { margin-bottom: var(--head-gap); }
.band__head p { margin-top: .85rem; color: var(--paper-2); font-size: 1.125rem; max-width: 52ch; }

/* Page-level shapes. The hero and the footer bleed to the viewport edge and round the
   corners that meet white; everything else is a slab inside the shell, which is
   aardvark's inset(24px round 160px) at this site's scale. */
.slab {
  background: var(--slab); border-radius: var(--r-slab);
  padding: var(--pad-slab-y) var(--pad-slab-x);
}
.slab--gold { background: var(--gold); }

/* ---------- hero ---------- */
.hero {
  background: var(--gold);
  border-radius: 0 0 var(--r-slab) var(--r-slab);
  padding: clamp(2.5rem, 6vw, 5rem) 0 clamp(2rem, 4.5vw, 3.5rem);
  margin-bottom: calc(var(--gap-section) / 2);
}
.hero h1 { max-width: 14ch; }
.hero .h1--long { max-width: 30ch; }
.hero__kicker { margin: 0 0 1rem; font-size: 1rem; font-weight: 600; color: var(--paper-2); }
.hero .lede { font-size: clamp(1.15rem, 1.8vw, 1.4rem); line-height: 1.4; color: var(--paper-2); max-width: 34ch; margin-top: 1.5rem; }
.hero .actions { margin-top: 1.75rem; }
/* Home: three full-width blocks on one left edge. The headline, then the lede with
   the buttons on the same baseline, then the photograph across the shell. v4 put the
   lede and buttons beside the photograph and they floated next to it whichever way
   they were aligned; a stack has nothing to misalign. */
.hero__row { display: grid; gap: 1.25rem; margin-top: 1.5rem; }
.hero__row .lede { margin-top: 0; }
.hero__row .actions { margin-top: 0; }
.hero__media { margin-top: clamp(1.75rem, 3.5vw, 3rem); }
.hero__media .media { aspect-ratio: 4 / 3; }
@media (min-width: 900px) {
  .hero__row { grid-template-columns: 1fr auto; align-items: end; gap: 3rem; margin-top: 2rem; }
  .hero__media .media { aspect-ratio: 21 / 9; }
}
/* Status notice inside a gold hero: a white tile. */
.hero .callout--status { background: var(--ink); margin-top: 1.5rem; }
/* "Last updated" on /privacy sits in the hero. --paper-3 is 3.49:1 on gold and fails;
   --paper-2 is the only grey that passes there. */
.hero .form-note { color: var(--paper-2); font-size: 1rem; margin-top: 1.2rem; }

/* ---------- course cards ---------- */
/* Two, side by side. Not three: taste-skill 9.C bans the three-equal-cards row, and
   there are two qualifications. The whole card is the link. A flex column, not a grid:
   grid stretched the kicker into a full-width bar, and margin-top:auto on the link
   only pins it to the bottom in flex, which is what lines the two links up. */
.cards { display: grid; gap: var(--gap); }
@media (min-width: 760px) { .cards { grid-template-columns: 1fr 1fr; } }
.card {
  display: flex; flex-direction: column; align-items: flex-start; gap: .85rem;
  padding: var(--pad-tile); border-radius: var(--r-tile);
  text-decoration: none; color: inherit;
  transition: background-color 200ms ease, transform 220ms var(--ease-out);
}
.card__kicker { font-size: .95rem; font-weight: 600; color: var(--paper-2); }
.card h3 { font-size: clamp(1.5rem, 2.4vw, 2rem); line-height: 1.1; letter-spacing: -0.03em; text-wrap: pretty; max-width: 16ch; }
.card p { color: var(--paper-2); }
.card__meta { font-size: .95rem; color: var(--paper-3); font-variant-numeric: tabular-nums; }
.card__go { margin-top: auto; padding-top: .25rem; font-weight: 600; display: inline-flex; align-items: center; gap: .45rem; min-height: 44px; }
/* Drawn, not typed: see the header comment on U+2192. 1em box, currentColor. */
.arrow {
  display: inline-block; width: 1em; height: 1em; flex: none;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 8h11M9 3.5 13.5 8 9 12.5'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 8h11M9 3.5 13.5 8 9 12.5'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform 200ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .card:hover { transform: translateY(-3px); }
  .card:hover .arrow { transform: translateX(4px); }
}

/* ---------- bento ---------- */
/* Six facts, six cells, two of them photographs. Below 700px the cells stack and size
   to their content; between 700 and 999px they run two across; from 1000px the tall
   cell takes two rows on the left. The count is exact at every width, so no empty cell
   sits in the middle or at the end. */
.bento { display: grid; gap: var(--gap); }
.cell {
  position: relative; display: grid; align-content: end; gap: .5rem;
  padding: var(--pad-tile); border-radius: var(--r-tile); overflow: hidden;
}
.cell h3 { font-size: clamp(1.5rem, 2.4vw, 2rem); line-height: 1.05; letter-spacing: -0.03em; max-width: 14ch; }
.cell h4 { font-size: 1.125rem; }
.cell p { color: var(--paper-2); font-size: 1rem; max-width: 34ch; }
/* Locked to the h2 size, so the page still carries two display sizes and not three. */
.cell .figure { font-size: clamp(3rem, 4.8vw, 4.5rem); font-weight: 700; line-height: .9; letter-spacing: -0.045em; font-variant-numeric: tabular-nums; margin-bottom: .35rem; }
.cell--gold { background: var(--gold); }
.cell--photo { padding: 0; aspect-ratio: 4 / 3; background: var(--slab-2); }
.cell--photo .media { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 0; }
@media (min-width: 700px) {
  .bento { grid-template-columns: 1fr 1fr; }
  .cell { min-height: 200px; }
  .cell--tall, .cell--wide { grid-column: span 2; }
  .cell--photo { aspect-ratio: auto; min-height: 320px; }
}
@media (min-width: 1000px) {
  .bento { grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(clamp(200px, 14vw, 270px), auto); }
  .cell { min-height: 0; }
  .cell--tall { grid-column: span 2; grid-row: span 2; }
  .cell--photo { min-height: 0; }
}

/* ---------- split ---------- */
/* Text beside a photograph, centred on it. On the white page the photograph is 4:3,
   which is about the height of the text beside it; inside a slab it stretches to the
   text's height, so the picture fills its column instead of floating in grey. */
.split { display: grid; gap: 1.75rem; align-items: center; }
.split > * { min-width: 0; }
.split__media .media { aspect-ratio: 4 / 3; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; gap: clamp(2.5rem, 5vw, 5rem); }
  .split--media-first .split__media { order: -1; }
  .split__media .media { aspect-ratio: 4 / 3; }
  .slab .split__media { align-self: stretch; }
  .slab .split__media .media { aspect-ratio: auto; height: 100%; min-height: 380px; }
}

/* ---------- tiles ---------- */
/* Every card-like block on the site. Grey on the white page, white inside a grey slab,
   from the one rule at the end of this section. */
.figures { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); }
.figures > div { display: grid; gap: .35rem; align-content: start; padding: var(--pad-tile); border-radius: var(--r-tile); }
.figures dt { font-size: .95rem; font-weight: 500; color: var(--paper-3); }
.figures dd { margin: 0; font-size: clamp(2.4rem, 4.8vw, 4.5rem); font-weight: 700; line-height: .92; letter-spacing: -0.045em; font-variant-numeric: tabular-nums; }
.figures dd small { display: block; font-size: .95rem; font-weight: 500; letter-spacing: -0.005em; color: var(--paper-3); margin-top: .5rem; }
@media (max-width: 599px) {
  .figures { grid-template-columns: 1fr 1fr; }
  .figures > div:last-child { grid-column: 1 / -1; }
}

.details { display: grid; gap: var(--gap); margin: 0; }
.figures + .details { margin-top: var(--gap); }
.details > div { display: grid; gap: .3rem; align-content: start; padding: var(--pad-tile); border-radius: var(--r-tile); }
.details dt { font-size: .95rem; font-weight: 500; color: var(--paper-3); }
.details dd { margin: 0; color: var(--paper); font-weight: 500; max-width: 40ch; }
@media (min-width: 700px) {
  .details { grid-template-columns: 1fr 1fr; }
  /* Five items in two columns leaves a hole; the odd last one takes the full row. */
  .details > div:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* Numbered tiles: the four things a qualification covers, and the four steps a
   student goes through. The number is the content's own order, drawn large. */
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gap); counter-reset: step; }
@media (min-width: 700px) { .steps { grid-template-columns: 1fr 1fr; } }
.steps li {
  counter-increment: step; display: grid; gap: .5rem; align-content: start;
  padding: var(--pad-tile); border-radius: var(--r-tile);
}
.steps li::before {
  content: counter(step); font-size: clamp(2.4rem, 4.8vw, 4.5rem); font-weight: 700; line-height: .9;
  letter-spacing: -0.045em; font-variant-numeric: tabular-nums; color: var(--paper); margin-bottom: .5rem;
}
.steps h3 { font-size: 1.375rem; }
.steps p { color: var(--paper-2); font-size: 1rem; }

.values { display: grid; gap: var(--gap); margin: 0; }
@media (min-width: 620px) { .values { grid-template-columns: 1fr 1fr; } }
@media (min-width: 960px) { .values { grid-template-columns: repeat(3, 1fr); } }
.values > div { display: grid; gap: .4rem; align-content: start; padding: var(--pad-tile); border-radius: var(--r-tile); }
.values dt { font-size: 1.25rem; font-weight: 600; letter-spacing: -0.02em; }
.values dd { margin: 0; color: var(--paper-2); font-size: 1rem; }
.values__label { margin: var(--head-gap) 0 var(--head-gap); }

/* ---------- flow: a staged procedure ---------- */
/* Both quality policies are three-stage processes, and the appeals document supplies
   its stages as an ASCII box diagram. This is that diagram in the site's own language:
   one tile per stage, the condition carried on the connector between them, timescales
   as tile metadata. Vertical at every width, because a process is read top to bottom
   and a four-node row has nowhere to go at 390px.

   The connector is a gold rule plus an ink chevron. Gold stays a surface here too: it
   draws the 2px line, which is decorative, and never sets a word. The condition text
   is --paper-2, 8.24:1 on white and 7.23:1 on the slab. The chevron is masked rather
   than a glyph for the same reason .arrow is: Archivo's latin subset has no U+2193, so
   a character would fall back to the system face and sit off the baseline.

   An <ol> because the order is the content, not the styling. The connector lives
   INSIDE the li it precedes, so the marker count stays right and a screen reader hears
   "if unresolved" before the stage it gates. The empty first connector is
   aria-hidden: it is a line with nothing to say. */
.flow { list-style: none; margin: 0; padding: 0; max-width: 46rem; }
.flow > li { display: grid; }
.flow__node {
  display: grid; gap: .45rem; align-content: start;
  padding: var(--pad-tile); border-radius: var(--r-tile);
}
.flow__label { margin: 0; font-size: .95rem; font-weight: 600; color: var(--paper-2); }
.flow__node h3 { font-size: 1.375rem; max-width: 32ch; }
.flow__meta { margin: 0; font-size: 1rem; color: var(--paper-2); max-width: 46ch; }

/* max-width:none is load-bearing, not tidying. .flow__cond is a <p>, so the global
   p { max-width: var(--measure) } capped it at 60ch inside a 736px node and centred the
   rule and chevron in that narrower box: the connector sat ~59px left of the tiles it
   joins. Visible only at 1:1, invisible in a full-page thumbnail. */
.flow__cond { display: grid; justify-items: center; gap: .45rem; margin: 0; padding-block: .85rem; max-width: none; }
.flow__cond::before {
  content: ""; display: block; width: 2px; height: 1.5rem;
  background: var(--gold); border-radius: 1px;
}
.flow__cond::after {
  content: ""; display: block; width: 1.15rem; height: 1.15rem; background: var(--paper);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2.5v11M3.5 9 8 13.5 12.5 9'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2.5v11M3.5 9 8 13.5 12.5 9'/%3E%3C/svg%3E") center / contain no-repeat;
}
.flow__cond span { font-size: .95rem; font-weight: 600; color: var(--paper-2); }

.pair { display: grid; gap: var(--gap); }
@media (min-width: 760px) { .pair { grid-template-columns: 1fr 1fr; } }
.pair > div { display: grid; gap: .6rem; align-content: start; padding: var(--pad-tile); border-radius: var(--r-tile); }
.pair h3 { font-size: 1.5rem; letter-spacing: -0.025em; }
.pair p { color: var(--paper-2); }

.contact-card { display: grid; gap: var(--gap); align-content: start; }
.contact-card > div { padding: var(--pad-tile); border-radius: var(--r-tile); }
.contact-card dt { font-size: .95rem; font-weight: 500; color: var(--paper-3); margin-bottom: .25rem; }
.contact-card dd { margin: 0; font-size: 1.05rem; line-height: 1.5; }
.contact-card dd + dt { margin-top: 1.2rem; }
.contact-card dd > a { display: inline-flex; align-items: center; min-height: 44px; font-size: clamp(1.5rem, 2.4vw, 2rem); font-weight: 700; letter-spacing: -0.03em; text-decoration: none; font-variant-numeric: tabular-nums; }
.contact-card dd > a:hover { text-decoration: underline; text-decoration-color: var(--gold); text-decoration-thickness: 3px; text-underline-offset: 6px; }

.enquire { display: grid; gap: var(--gap); }
.enquire > * { min-width: 0; }
@media (min-width: 940px) { .enquire { grid-template-columns: 1.1fr .9fr; gap: clamp(1.25rem, 2.5vw, 2.5rem); align-items: start; } }
.enquire__form { padding: var(--pad-tile); border-radius: var(--r-tile); }

/* The one rule that puts every tile on the right ground. */
.card, .cell, .figures > div, .details > div, .steps li, .values > div, .pair > div,
.contact-card > div, .enquire__form, .callout, .flow__node { background: var(--slab); }
.slab .card, .slab .cell, .slab .figures > div, .slab .details > div, .slab .steps li,
.slab .values > div, .slab .pair > div, .slab .contact-card > div, .slab .enquire__form, .slab .callout,
.slab .flow__node { background: var(--ink); }
.cell--gold, .slab .cell--gold { background: var(--gold); }
/* The entry node of a flow, gold like the hero it sits under. --paper-2 is the only
   grey that clears 4.5:1 on gold (4.54), which is why .flow__label is that and the
   start node carries no .flow__meta. */
.flow__node--start, .slab .flow__node--start { background: var(--gold); }
.cell--photo, .slab .cell--photo { background: var(--slab-2); }
@media (hover: hover) and (pointer: fine) {
  .card:hover { background: var(--slab-2); }
  .slab .card:hover { background: var(--ink); }
}

/* ---------- prose ---------- */
.prose { max-width: var(--measure); }
.prose p { font-size: 1.125rem; }
.prose ul { list-style: none; padding: 0; margin: 0 0 1.1em; }
.prose li { position: relative; padding-left: 1.4rem; margin-bottom: .6em; }
.prose li::before { content: ""; position: absolute; left: 0; top: .55em; width: .6rem; height: .6rem; border-radius: 50%; background: var(--gold); }
.prose a {
  color: var(--paper); text-decoration: underline; text-decoration-color: var(--line-strong);
  text-decoration-thickness: 1px; text-underline-offset: 3px;
}
@media (hover: hover) and (pointer: fine) {
  .prose a:hover { text-decoration-color: var(--gold); text-decoration-thickness: 3px; }
}
.prose--lead p { font-size: clamp(1.2rem, 1.8vw, 1.5rem); line-height: 1.4; letter-spacing: -0.015em; max-width: 40ch; }
.prose-grid { display: grid; gap: 1.5rem; }
@media (min-width: 900px) { .prose-grid { grid-template-columns: 1fr 1fr; gap: clamp(2.5rem, 5vw, 5rem); align-items: start; } }
.about__address { margin-bottom: 1em; font-size: 1.125rem; font-weight: 500; max-width: 34ch; }

/* ---------- callouts ---------- */
/* A plain tile on the page's grid, same as every other tile. It carried a gold wash
   and a 6px left edge until 2026-09-06; the edge is the notice-box pattern every
   generator reaches for, and Anirudh named it on sight. The bold first sentence does
   the work now. */
.callout {
  border-radius: var(--r-tile);
  padding: var(--pad-tile); margin: 1.75rem 0 0; max-width: var(--measure);
}
.callout p { margin-bottom: .65em; }
.callout p:last-child { margin-bottom: 0; }
.callout strong { font-weight: 600; }
/* Approval status. Golden Key holds no awarding organisation approval yet, confirmed
   in writing 2026-08-31. Omitting that is a misleading omission under DMCCA 2024
   s.227, so it is page furniture on /courses, /admissions, both course pages and,
   since 2026-09-09, /quality-policies.

   ON THE HUB IT IS LOAD-BEARING, not just informative. The two policy pages under it
   name Highfield Qualifications as the awarding organisation, which is the one place
   on the site an awarding body is named. This notice, plus the children linking back
   to the hub, is the only thing stopping that name from reading as a claim of
   approval. verify-shared.py check 11b fails the build if it is deleted.

   REMOVE ONLY when the client confirms approval IN WRITING. */
.callout--status { max-width: 52ch; }
/* Beside a prose column in .prose-grid, not after it. */
.callout--aside { margin: 0; align-self: start; }

/* ---------- onward links ---------- */
/* A plain underlined link, gold rule under it. No arrow: the glyph problem above, and
   an underline that breaks into two pieces around one. */
.more { margin: var(--head-gap) 0 0; }
.more a {
  display: inline-flex; align-items: center; min-height: 44px;
  font-weight: 600; text-decoration: underline; text-decoration-color: var(--gold);
  text-decoration-thickness: 3px; text-underline-offset: 6px;
  transition: text-decoration-color 180ms ease;
}
@media (hover: hover) and (pointer: fine) { .more a:hover { text-decoration-color: var(--paper); } }

/* ---------- CTA slab ---------- */
.cta { display: grid; gap: 1.5rem; }
.slab.cta { padding-block: clamp(2rem, 5vw, 4.5rem); }
.cta h2 { max-width: 14ch; }
.cta h2 + p { margin-top: 1rem; }
.cta p { color: var(--paper-2); font-size: 1.125rem; max-width: 44ch; }
.cta .btn--ghost { border-color: var(--paper); }
@media (min-width: 900px) {
  .cta { grid-template-columns: 1.2fr .8fr; align-items: end; gap: 3rem; }
  .cta .actions { justify-content: flex-end; }
}

/* ---------- enquiry form ---------- */
.field { display: grid; gap: .4rem; margin-bottom: 1.1rem; min-width: 0; }
.field label { font-size: .95rem; font-weight: 600; }
.field input, .field select, .field textarea {
  font: inherit; font-size: 1rem; color: var(--paper);
  padding: .8rem .95rem; min-height: 52px; width: 100%; min-width: 0; max-width: 100%;
  background: var(--ink); border: 1.5px solid var(--line-strong); border-radius: var(--r-input);
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.field textarea { min-height: 124px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--paper); box-shadow: inset 0 -3px 0 var(--gold);
}
/* Pointer focus keeps the underline; keyboard focus gets the ring on top of it. */
.field input:focus:not(:focus-visible),
.field select:focus:not(:focus-visible),
.field textarea:focus:not(:focus-visible) { outline: none; }
.field input[aria-invalid="true"], .field select[aria-invalid="true"], .field textarea[aria-invalid="true"] { border-color: var(--danger); }

/* .field's own rules would make a checkbox a full-width 52px control. */
.field--check { grid-template-columns: auto 1fr; align-items: center; gap: .75rem; margin-top: 1.5rem; }
.field--check input[type="checkbox"] {
  width: 1.5rem; height: 1.5rem; min-height: 0; min-width: 0;
  padding: 0; margin: 0; accent-color: var(--paper);
}
.field--check label { display: flex; align-items: center; min-height: 44px; font-size: 1rem; font-weight: 400; line-height: 1.45; }
/* Part of the label, so never display:none and never aria-hidden. */
.field__opt { font-size: .8rem; font-weight: 400; color: var(--paper-3); }
/* Help text under a label, above the control it describes. --paper-3 is 6.33:1 on
   white and 5.55 on the slab, and this never sits on gold. */
.field__help { font-size: .9rem; color: var(--paper-3); margin: 0 0 .2rem; max-width: 60ch; }

/* A heading INSIDE a form, numbering the sections of the paper document the form is
   derived from. Deliberately not the site h2 size: an 72px "1. Learner and assessment
   details" inside a form card would outweigh the page's own h1. */
.form-section {
  font-size: 1.25rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.2;
  max-width: none; margin: 2.25rem 0 1.1rem;
  padding-top: 1.5rem; border-top: 1px solid var(--line);
}
.form-section:first-of-type { margin-top: 1.5rem; }

/* A set of checkboxes where at least one must be ticked. A fieldset, because that is
   what groups controls under one question, and site.js validates it as a group:
   `required` on a single box would mean "this one", which is not the rule. */
.field-set { border: 0; padding: 0; margin: 0 0 1.1rem; min-width: 0; }
.field-set legend { padding: 0; font-size: .95rem; font-weight: 600; margin-bottom: .7rem; }
.checks { display: grid; gap: .5rem; }
.check { display: grid; grid-template-columns: auto 1fr; gap: .75rem; align-items: start; }
/* 1.5rem, the same as .field--check. 22px measured under the site's own documented
   24px control floor, and matching the existing checkbox is better than inventing a
   second size for the same control. min-height/min-width zeroed for the same reason
   .field--check does it: .field's rules would otherwise stretch this to 52px. */
.check input[type="checkbox"] {
  width: 1.5rem; height: 1.5rem; min-height: 0; min-width: 0;
  padding: 0; margin: 0; accent-color: var(--paper);
  align-self: start; margin-top: .65rem;
}
/* 44px minimum target, met by padding the label rather than growing the box. */
.check label {
  display: block; min-height: 44px; padding-block: .6rem;
  font-size: 1rem; font-weight: 400; line-height: 1.45; cursor: pointer;
}
.check label strong { font-weight: 600; }
.field-set[aria-describedby] legend { color: var(--danger); }

/* A definition list cell carrying a sentence rather than a phone number, so it must
   not inherit the contact card's display-size link treatment. */
.contact-card__plain { font-size: 1rem; line-height: 1.5; color: var(--paper-2); }
.contact-card__plain a { color: var(--paper); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }

/* This is the notice that makes the form lawful, not fine print. */
.form-consent { margin: 1.7rem 0 1.4rem; font-size: .9rem; color: var(--paper-3); }
.form-consent p { margin-bottom: .6em; max-width: 52ch; }
.form-consent p:last-child { margin-bottom: 0; }
.form-consent a { color: var(--paper); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
@media (hover: hover) and (pointer: fine) { .form-consent a:hover { text-decoration-color: var(--gold); } }

/* Honeypot. NOT display:none and NOT visibility:hidden: bot frameworks check for both. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-note { font-size: .9rem; color: var(--paper-3); margin-top: .9rem; }
.form-legend { font-size: .9rem; color: var(--paper-3); margin: 0 0 1.2rem; }
/* Blur bridges the swap so two strings do not read as two overlapping objects. */
.form-status {
  font-size: .95rem; font-weight: 500; margin-top: .9rem; min-height: 1.5em; max-width: 40ch;
  transition: opacity 200ms ease, filter 200ms ease;
}
.form-status[data-swapping] { opacity: .35; filter: blur(2px); }
.form-status[data-tone="error"] { color: var(--danger); }
.form-status[data-tone="ok"] { color: var(--ok); }

/* ---------- footer ---------- */
/* The grey slab, rising from the white page on the slab radius. No dark band: a single
   dark section inside a light page is what redesign-skill calls a copy-paste accident,
   and the brief chose no dark sections. */
.site-foot {
  background: var(--slab); color: var(--paper-2);
  border-radius: var(--r-slab) var(--r-slab) 0 0;
  margin-top: calc(var(--gap-section) / 2);
  padding: clamp(3rem, 5.5vw, 5rem) 0 1.9rem; font-size: .95rem;
}
.site-foot a { color: var(--paper); text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
@media (hover: hover) and (pointer: fine) { .site-foot a:hover { text-decoration-color: var(--gold); text-decoration-thickness: 2px; } }
.site-foot__grid { display: grid; gap: 2rem; }
@media (min-width: 820px) { .site-foot__grid { grid-template-columns: 1.5fr 1fr 1fr; gap: 3rem; } }
.site-foot h2 { font-size: .95rem; font-weight: 600; letter-spacing: 0; line-height: 1.4; color: var(--paper); margin: 0 0 .7rem; max-width: none; }
.site-foot ul { list-style: none; margin: 0; padding: 0; }
.site-foot li { margin-bottom: .3rem; max-width: 30ch; }
/* A 16px line box is under the 24px target floor (WCAG 2.5.8); the padding lifts each
   footer link to 24px without moving the text. */
.site-foot li a, .site-foot__legal a { display: inline-block; padding-block: .25rem; }
.site-foot p { color: var(--paper-2); }
.site-foot .wordmark strong { color: var(--paper); }
.site-foot .wordmark span { color: var(--paper-2); }
.site-foot__tagline { margin-top: .75rem; max-width: 30ch; }
.site-foot__label { margin: -.2rem 0 .5rem; font-size: .9rem; color: var(--paper-3); }
.site-foot__address { margin-top: .85rem; line-height: 1.5; color: var(--paper-2); max-width: 30ch; }
.site-foot__hours { margin-top: .55rem; color: var(--paper-2); }
.site-foot__directions { margin-top: .35rem; }
.site-foot__directions a { display: inline-flex; align-items: center; min-height: 44px; }
.site-foot__legal { margin-top: 2.5rem; padding-top: 1.4rem; border-top: 1px solid var(--line-strong); font-size: .85rem; }
.site-foot__legal p { color: var(--paper-3); max-width: 76ch; margin-bottom: .5em; }
/* Column 4 exists so .no-js can hide the map and its heading together. Between 820
   and 1000px the map takes its own row. */
@media (min-width: 820px) { .site-foot__location { grid-column: 1 / -1; } }
@media (min-width: 1000px) {
  .site-foot__grid { grid-template-columns: 1.4fr 1fr 1fr 1.2fr; }
  .site-foot__location { grid-column: auto; }
}
.no-js .site-foot__location { display: none; }
@media (min-width: 1000px) { .no-js .site-foot__grid { grid-template-columns: 1.5fr 1fr 1fr; } }

/* ---------- footer registrations ---------- */
/* Added 2026-09-11. White tiles on the grey slab (the tile rule), rectangular at the
   input radius, never pills. .marks .mark outranks .site-foot li a, which would
   otherwise force inline-block and a .25rem padding-block onto the tile. The image
   tile sizes the badge by height so the pink frame that is part of the artwork stays
   proportionate; the text tile matches it by min-height. */
.site-foot__marks { margin-top: 2.5rem; }
.marks { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .75rem; }
.marks li { margin: 0; max-width: none; }
.marks .mark {
  display: inline-flex; align-items: center; min-height: 4.5rem; padding: .6rem .9rem;
  background: var(--ink); border: 1px solid var(--line); border-radius: var(--r-input);
  color: var(--paper); text-decoration: none;
  transition: border-color 160ms ease;
}
.marks .mark:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }
@media (hover: hover) and (pointer: fine) { .marks .mark:hover { border-color: var(--line-strong); } }
.mark--image img { display: block; height: 3.25rem; width: auto; }
.marks .mark--text { flex-direction: column; align-items: flex-start; justify-content: center; gap: .15rem; padding-inline: 1rem; }
.mark__label { font-size: .8rem; line-height: 1.3; color: var(--paper-3); }
.mark__value { font-size: 1.05rem; font-weight: 600; line-height: 1.2; letter-spacing: .01em; color: var(--paper); }

/* ---------- map facade ---------- */
/* Nothing is fetched from Google until the button is pressed. The privacy caption sits
   under the button as a paragraph the button is described by, rather than inside it:
   a two-line pill read as a broken button. */
.map-facade { width: 100%; }
@media (min-width: 820px) { .map-facade { max-width: 320px; } }
/* 4:3 is a floor, not a fixed height: overflow stays visible until the iframe is in,
   because overflow:hidden zeroes the content-based minimum height an aspect-ratio box
   has, and the plate is a stretched grid item rather than height:100%, because a
   percentage resolves against the 4:3 height and leaves the minimum nothing taller to
   measure. Found at 1024 on 2026-09-05, when the caption was cut off. */
.map-facade__frame {
  aspect-ratio: 4 / 3; display: grid;
  background: var(--ink); border-radius: var(--r-tile);
}
.map-facade__frame[data-loaded="true"] { overflow: hidden; }
.map-facade__frame:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }
.map-facade__plate {
  display: grid; place-content: center; justify-items: center;
  gap: 0; padding: 1.25rem; text-align: center;
}
.map-facade__pin { color: var(--gold); }
.map-facade__where { font-size: .9rem; color: var(--paper-2); max-width: 24ch; margin: .6rem 0 1rem; }
.map-facade__btn {
  font: inherit; font-size: .95rem; font-weight: 600; line-height: 1.3;
  min-height: 44px; padding: .6rem 1.2rem; cursor: pointer;
  background: var(--paper); color: var(--on-ink);
  border: 0; border-radius: var(--r-pill);
  transition: transform 160ms var(--ease-out), background-color 180ms ease;
}
.map-facade__btn:active { transform: scale(.975); }
@media (hover: hover) and (pointer: fine) { .map-facade__btn:hover { background: #2A2E35; } }
.map-facade__note { margin: .7rem 0 0; font-size: .78rem; line-height: 1.35; color: var(--paper-3); max-width: 26ch; }
.map-facade__status { margin: .5rem 0 0; min-height: 1.4em; font-size: .85rem; color: var(--paper-3); }
.map-facade__embed { display: block; width: 100%; height: 100%; border: 0; }

/* ---------- courses menu ---------- */
.nav__group { position: relative; display: flex; align-items: center; gap: .25rem; }
.nav__disclosure {
  appearance: none; background: none; border: 0; margin: 0; padding: .5rem .35rem;
  min-height: 44px; min-width: 28px;
  display: flex; align-items: center; justify-content: center; color: var(--paper-3); cursor: pointer;
  transition: color 150ms ease;
}
.nav__disclosure svg {
  width: 10px; height: 6px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  transition: transform 200ms var(--ease-out);
}
.nav__disclosure[aria-expanded="true"] svg { transform: rotate(180deg); }
.nav__disclosure:hover { color: var(--paper); }
/* With JS off the button is a dead control, so .no-js takes it away, settled before
   first paint by the inline head script. Same for the toggle: the drawer stands open
   as a plain stacked list, which is what a nav is before anyone enhances it. */
.no-js .nav__disclosure { display: none; }
.no-js .nav-toggle { display: none; }
.no-js .nav-drawer { grid-template-rows: 1fr; opacity: 1; visibility: visible; transition: none; }

.nav__menu {
  position: absolute; top: calc(100% + .55rem); left: -1rem; z-index: 50;
  min-width: 22rem; padding: 1rem;
  background: var(--ink); border: 1px solid var(--line); border-radius: var(--r-tile);
  box-shadow: 0 14px 34px rgba(17, 19, 22, .10), 0 2px 6px rgba(17, 19, 22, .05);
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity 160ms ease, transform 160ms var(--ease-out), visibility 0s linear 160ms;
}
.nav__menu[data-open="true"] { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }
/* Bridges the gap under the trigger so the pointer can travel down into the panel. */
.nav__menu::before { content: ""; position: absolute; left: 0; right: 0; top: -.7rem; height: .7rem; }
.nav__menu-label { margin: 0; font-size: .72rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--paper-3); }
.nav__menu-sub { margin: .15rem 0 .6rem; font-size: .85rem; font-weight: 600; color: var(--paper-3); }
.nav__menu a {
  display: block; margin-inline: -.6rem; padding: .6rem .6rem; border-radius: var(--r-input);
  text-decoration: none; font-size: .95rem; font-weight: 500; line-height: 1.35;
  transition: background-color 150ms ease;
}
.nav__menu a:hover { background: var(--slab); }
.nav__menu a[aria-current="page"] { font-weight: 700; background: var(--gold-wash); }

/* ---------- mobile drawer, nested items ---------- */
.nav-drawer__label { margin: 0; padding: 1rem 0 .35rem; font-size: .85rem; font-weight: 600; color: var(--paper-3); }
.nav-drawer__label--sub { padding: 0 0 .35rem 1rem; font-size: .78rem; font-weight: 500; color: var(--paper-3); }
.nav-drawer a.nav-drawer__sub { padding-left: 1rem; border-left: 1px solid var(--line); }
.nav-drawer a[aria-current="page"] { font-weight: 700; box-shadow: inset 4px 0 0 var(--gold); padding-left: .75rem; }

/* ---------- utility ---------- */
.skip { position: absolute; left: -9999px; background: var(--paper); color: var(--on-ink); padding: .8rem 1.2rem; z-index: 100; font-weight: 600; }
.skip:focus { left: 0; top: 0; }
/* Reachable by a screen reader, invisible on screen. */
.u-vh {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

/* ---------- reduced motion ---------- */
/* Fewer and gentler, not zero. Opacity survives, movement does not. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal-on [data-reveal] { transform: none; transition: opacity 300ms ease; transition-delay: 0ms; }
  .btn, .nav-toggle, .card, .nav .tel, .map-facade__btn { transition: background-color 180ms ease, border-color 180ms ease; }
  .btn:active, .nav-toggle:active, .nav .tel:active, .map-facade__btn:active { transform: none; }
  .card:hover { transform: none; }
  .arrow { transition: none; }
  .nav > a::after, .nav__group > a::after { transition: none; }
  .nav__menu { transform: none; transition: opacity 150ms ease, visibility 0s linear 150ms; }
  .nav__menu[data-open="true"] { transform: none; }
  .nav__disclosure svg { transition: none; }
  .nav-drawer { transition: opacity 150ms ease; }
}

/* ---------- print ---------- */
/* Browsers drop background colours when printing, so every slab goes white and the
   text has to stand on its own. The privacy notice is the page people print. */
@media print {
  @page { margin: 14mm; }
  :root { --measure: 118mm; }
  html, body { background: #fff !important; color: #111 !important; }
  body { font-size: 10.5pt; line-height: 1.45; }
  * { box-shadow: none !important; text-shadow: none !important; }
  .site-head, .nav-drawer, .nav-toggle, .skip, .map-facade,
  .hero__media, .split__media, .cell--photo, .site-foot__location,
  #enquiry-form, .form-note, .form-consent, .btn, .card__go, .more { display: none !important; }
  .hero, .slab, .slab--gold, .cell, .card, .steps li, .values > div, .pair > div,
  .figures > div, .details > div, .contact-card > div, .site-foot,
  .flow__node, .flow__node--start { background: #fff !important; border-radius: 0 !important; }
  h1, h2, h3, h4, strong, dt { color: #000 !important; }
  p, li, dd, address, .lede, .band__head p { color: #1a1a1a !important; }
  a { color: #000 !important; text-decoration: underline; }
  h1, .h1--long { font-size: 22pt !important; line-height: 1.1; }
  h2 { font-size: 15pt !important; line-height: 1.15; max-width: none !important; }
  h3 { font-size: 11.5pt !important; }
  h1, h2, h3 { break-after: avoid; }
  .band, .card, .callout, .contact-card, .steps li, .flow > li { break-inside: avoid; }
  .reveal-on [data-reveal] { opacity: 1 !important; transform: none !important; }
  .band, .hero { padding-block: 0 !important; margin-bottom: 12pt; }
  .shell { padding-inline: 0 !important; max-width: none !important; }
  .site-foot { border-top: 1pt solid #999 !important; padding-top: 10pt !important; }
  .card, .callout, .contact-card > div, .steps li, .flow__node { border: 1pt solid #bbb !important; padding: 8pt !important; }
  /* Printers drop backgrounds, so the gold connector rule would vanish and leave the
     stages looking unrelated. Print the condition as text on its own line instead. */
  .flow__cond { padding-block: 6pt !important; }
  .flow__cond::before, .flow__cond::after { display: none !important; }
  .flow__cond span::before { content: "\2193  "; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 8.5pt; color: #555; word-break: break-all; }
  a[href^="tel:"]::after { content: ""; }
}
