/* ==========================================================================
   RSA School of Motoring: design system v2

   Loaded INSTEAD of unified.css, never alongside it. A page is either fully on
   the old stylesheet or fully on this one, which is why there are no overrides
   here and no selector fights. The switch is the per-page `design: "v2"` flag,
   read in layouts/base.njk.

   Namespace is `sm-`, after `ck-` for Cork and `au-` for Automatic. Those names
   are ours to move. The legacy names in section 20 are NOT: they are JavaScript
   contracts, re-provided here under their original names because a script or a
   Function depends on them. See data/rsa-school/makeover/interactive-hooks.md,
   which lists 513 of them.

   PHASE 1 SCOPE. This file currently carries the chrome and the primitives:
   reset, layout, type, buttons, eyebrow, header, footer, mobile menu, sticky
   call bar and breadcrumb. The page modules (hero, proof bar, cards, tickets,
   review wall, FAQ, road stops, call band) are added centrally as the Phase 2
   pilots need them, one per page family. Nothing is styled in a page template.
   ========================================================================== */

/* ---------- 0. Local rhythm ---------- */

/* The sticky mobile CTA bar's height. Named because the cookie banner has to
   sit exactly above it, and a magic number there drifts the moment the bar's
   padding changes. */
:root { --sm-sticky-cta-h: 73px; }

/* Two values the pack's section rhythm uses that the shared scale does not
   carry. Declared here rather than in tokens.css, which is copied verbatim
   across Leinster, Cork, Automatic and this site: a value only this sheet
   needs does not belong in the shared file. */
:root {
  --sm-space-64: calc(var(--space-28) + var(--space-4));  /* 64px */
  --sm-space-72: calc(var(--space-28) + var(--space-8));  /* 72px */

  /* The proof bar numeral. The shared scale's --fs-stat tops out at 68px, which
     is a headline figure; a proof cell wants the pack's smaller step. */
  --sm-fs-proof: clamp(40px, 4.2vw, 54px);   /* pack: 54px. Below this P1 stops reading as a module. */
}

/* ---------- 1. Reset and base ---------- */

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

html {
  color-scheme: dark;
  /* Anchored sections clear the sticky header. */
  scroll-padding-top: var(--scroll-offset);
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text-body);
  font: var(--fw-regular) var(--fs-body-lg)/var(--lh-relaxed) var(--font-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

a { color: inherit; }

/* Sizing belongs to the named component, so no global heading clamp beyond
   these defaults. Never set a font-size on a heading in a page template. */
h1, h2, h3, h4 {
  margin: 0 0 var(--space-10);
  font-family: var(--font-display);
  font-weight: var(--fw-black);
  color: var(--color-text);
  text-transform: uppercase;
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-display);
}

h1 {
  font-size: var(--fs-hero-a);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-tight);
}
h1, h2, h3, h4 {
  /* A long unbreakable token in a heading causes real horizontal scroll, not
     just a clipped word. Irish place names joined by a slash do it: "Dun
     Laoghaire/Deansgrange" took six town pages to 450px at a 390px viewport. */
  overflow-wrap: break-word;
}

h2 {
  font-size: var(--fs-h2);
  /* Not --lh-heading. The pack sets a section h2 at .98 and -.035em, and at 1.2
     a 50px heading reads as a caption. Fix number one in the pack's own list of
     what made v1 flat. */
  line-height: 0.98;
  letter-spacing: -0.035em;
}
h3 { font-size: var(--fs-h3); letter-spacing: -0.02em; }
/* h4 is the one heading that is not uppercase: at 18px the caps read as a
   label rather than a heading. */
h4 { font-size: var(--fs-h4); letter-spacing: -0.01em; text-transform: none; }

p { margin: 0 0 var(--space-10); }

/* The accent clause of a heading.
   The content data already marks the accent clause with <em>: seven of the
   eight headings on a town page carry one, written by whoever wrote the copy.
   So the accent comes from the content rather than from a template guess, and
   styling em here gives the green emphasis the other v2 sites have.
   Not italic. The clause is Poppins 800 uppercase like the rest of the
   heading; only its colour changes. */
.sm-accent,
h1 em, h2 em, h3 em, h4 em {
  color: var(--color-accent);
  font-style: normal;
}

/* Prose keeps its real emphasis: an em inside running copy is italic, as the
   author meant, and is not an accent clause. */
.sm-prose em, .sm-lede em, blockquote em { color: inherit; font-style: italic; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- 2. Layout primitives ---------- */

.sm-wrap {
  width: min(100% - (var(--gutter) * 2), var(--container));
  margin-inline: auto;
}

.sm-section { padding: var(--space-section) 0; }
.sm-section--tight { padding: var(--sm-space-64) 0; }
.sm-section--surface { background: var(--color-panel); }
.sm-section--deep { background: var(--color-bg-deep); }

/* Two plain sections in a row need a line between them. A section that brings
   its own ground does not, so it suppresses the rule on itself and on the one
   that follows. */
.sm-section + .sm-section { border-top: 1px solid var(--rsa-line-08); }
.sm-section--surface, .sm-section--deep,
.sm-section--surface + .sm-section, .sm-section--deep + .sm-section { border-top: 0; }

.sm-lede {
  max-width: 54ch;
  font-size: var(--fs-lede);
  line-height: var(--lh-lede);
  color: var(--color-text-lede);
}

.sm-prose { max-width: 72ch; }
.sm-prose a { color: var(--color-accent); text-underline-offset: 3px; }

/* ---------- 3. Eyebrow ---------- */

.sm-eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  margin-bottom: var(--space-8);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--color-eyebrow);
}

.sm-eyebrow__marker {
  display: inline-block;
  padding: 3px var(--space-5) 2px;
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: var(--fw-black);
  color: var(--color-on-accent);
  background: var(--color-accent);
  /* Slanted right edge: the pack's chip shape. */
  clip-path: polygon(0 0, 100% 0, calc(100% - 6px) 100%, 0 100%);
}

/* ---------- 4. Buttons ---------- */

.sm-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
  /* 48px floor is the touch target, not decoration. */
  min-height: 48px;
  padding: var(--pad-btn);
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  background: var(--color-action);
  color: var(--color-on-action);
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  text-decoration: none;
  /* Not the body's 1.7: a button is one line, and inheriting the reading
     line-height makes a wrapped label a 76px tall control. */
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur-fast);
}

/* Darkens on hover. The pack's lighter #F03A32 is 3.92:1 with white. */
.sm-btn:hover { background: var(--color-action-hover); }

.sm-btn--ghost {
  background: transparent;
  border-color: var(--rsa-line-25);
  color: var(--color-text);
}
.sm-btn--ghost:hover { background: var(--rsa-line-08); border-color: var(--color-text); }

.sm-btn--sm { min-height: 40px; padding: var(--space-5) var(--space-9); }
.sm-btn--block { width: 100%; }

.sm-btn-row { display: flex; flex-wrap: wrap; gap: var(--space-6); }

/* Rule 5: anything offering Book or Pay also offers the phone. */
.sm-or-call {
  margin-top: var(--space-6);
  font-size: var(--fs-body-sm);
  color: var(--color-text-muted);
}
.sm-or-call a { color: var(--color-text); font-weight: var(--fw-semi); }
/* On a red band that chalk measures 4.385:1, just under AA, where the white of
   --color-on-red-muted measures 4.82:1. The band already sets white as its
   inherited default, so everything that does NOT state its own colour is
   already right; this link is wrong precisely because it states one. Hence a
   band-scoped override rather than a role sweep.

   FIX F4 was already applied this way in centre.css, home.css, guide.css and
   theory.css, and never here, so every family without a bespoke override still
   carried it: 86 routes. Fixing a shared component where the fault was noticed
   instead of where it lives is how one defect becomes four near-copies and a
   gap. */
.sm-band .sm-or-call a { color: var(--color-on-red-muted); }

/* ---------- 5. Header ---------- */

.sm-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--color-bg-deep);
  border-bottom: var(--rule-header);
  box-shadow: var(--shadow-header);
}

.sm-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-14);
  min-height: 84px;
}

.sm-header__brand { display: flex; align-items: center; text-decoration: none; }
.sm-header__logo { width: 124px; height: auto; }

.sm-nav {
  display: flex;
  align-items: center;
  gap: var(--space-14);
  margin-left: auto;
}

.sm-nav a {
  font-size: var(--fs-label);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-nav);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--color-text-body);
  transition: color var(--dur-fast);
}
.sm-nav a:hover { color: var(--color-text); }

/* Written at matched specificity with the bare class on purpose. On Cork the
   plain .ck-nav__phone lost to `.ck-nav a` and the header phone rendered at
   2.78:1. There is no `.sm-nav a` ancestor here, but the chip is also used
   outside the nav, so both selectors are stated and kept together. */
.sm-nav__phone,
.sm-header .sm-nav__phone {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: var(--space-5) var(--space-11) var(--space-5) var(--space-9);
  background: var(--color-action);
  color: var(--color-on-action);
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-button);
  text-decoration: none;
  /* Slanted trailing edge, the same move as the eyebrow marker. */
  clip-path: polygon(0 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
  transition: background var(--dur-fast);
}
.sm-nav__phone:hover,
.sm-header .sm-nav__phone:hover { background: var(--color-action-hover); }

/* ---------- 6. Mobile menu ---------- */

.sm-menu-btn {
  display: none;
  width: 46px;
  height: 46px;
  padding: 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-12);
  border-radius: var(--radius-xs);
  cursor: pointer;
}

.sm-menu-btn__bar {
  display: block;
  width: 20px;
  height: 2px;
  background: var(--color-text);
}

.sm-mobile-menu { display: none; }

.sm-mobile-menu a {
  display: block;
  padding: var(--space-10) 0;
  border-bottom: 1px solid var(--rsa-line-08);
  font-size: var(--fs-small);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-nav);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--color-text);
}

.sm-mobile-menu__phone { color: var(--color-accent); border-bottom: 0; }

/* ---------- 7. Footer ---------- */

.sm-footer {
  background: var(--color-bg-deep);
  border-top: 1px solid var(--rsa-line-12);
  padding-top: var(--sm-space-72);
  padding-bottom: var(--space-16);
}

.sm-footer__grid {
  display: grid;
  grid-template-columns: 1.4fr repeat(3, 1fr);
  gap: var(--space-16);
}

.sm-footer__logo { width: 124px; margin-bottom: var(--space-10); }

.sm-footer__tagline {
  max-width: 34ch;
  font-size: var(--fs-body-sm);
  color: var(--color-text-muted);
}

.sm-footer__phone {
  display: inline-block;
  margin-bottom: var(--space-10);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-black);
  color: var(--color-text);
  text-decoration: none;
}
.sm-footer__phone:hover { color: var(--color-accent); }

.sm-footer__address { font-size: var(--fs-body-sm); color: var(--color-text-muted); }
.sm-footer__address summary {
  cursor: pointer;
  font-weight: var(--fw-semi);
  color: var(--color-text-body);
}
.sm-footer__address p { margin: var(--space-6) 0 0; }

/* Green, uppercase, 12px: the pack's column heading. Scoped to the footer so a
   page heading cannot inherit it. */
.sm-footer .sm-footer__heading {
  margin-bottom: var(--space-10);
  font-family: var(--font-body);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-accent);
}

.sm-footer__links { margin: 0; padding: 0; list-style: none; }
.sm-footer__links li { margin-bottom: var(--space-5); }
.sm-footer__links a {
  font-size: var(--fs-body-sm);
  color: var(--color-text-muted);
  text-decoration: none;
  transition: color var(--dur-fast);
}
.sm-footer__links a:hover { color: var(--color-text); }
.sm-footer__all { color: var(--color-accent) !important; font-weight: var(--fw-semi); }

.sm-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-8);
  margin-top: var(--sm-space-64);
  padding-top: var(--space-12);
  border-top: 1px solid var(--rsa-line-08);
  font-size: var(--fs-caption);
  color: var(--color-text-faint);
}

.sm-footer__legal a { color: var(--color-text-faint); text-decoration: none; }
.sm-footer__legal a:hover { color: var(--color-text-body); }
.sm-footer__sep { margin: 0 var(--space-4); color: var(--rsa-slate); }

/* ---------- 8. Breadcrumb ---------- */

.sm-crumb {
  padding: var(--space-10) 0 0;
  font-size: var(--fs-caption);
  color: var(--color-text-muted);
}
.sm-crumb a { color: var(--color-text-muted); text-decoration: none; }
.sm-crumb a:hover { color: var(--color-text); }

/* ---------- 9. Sticky mobile call bar ---------- */

/* Styled under its ORIGINAL class names, not renamed into sm-. The bar is
   shared markup in layouts/base.njk and it carries real copy ("Call Us",
   "Book a Lesson"), so rebuilding it would be a content change for no gain.
   On Cork this bar was simply forgotten and had no v2 rule at all.
   Rule 3: visible by default on mobile and correct with no JavaScript. The
   display is driven by the media query below, never by a script. */
.sticky-mobile-cta { display: none; }

/* ---------- 10. Hero ---------- */

/* Three skins over one data model: signpost (pack A), route (pack B) and night
   (pack C). Every module appears in all three and only the hero treatment and
   the sidebar differ, so no page is excluded by its data. The assignment is
   data/rsa-school/makeover/town-skin-assignment.json, 44/44/44, deterministic. */

.sm-hero {
  /* How much of the viewport this skin gives the copy column, used to size the
     place name. Skin B puts a photo frame beside the copy, so it gets less. */
  --hero-measure: 64vw;
  position: relative;
  isolation: isolate;
  padding: var(--space-28) 0 var(--space-section);
  overflow: hidden;
  background: var(--color-bg-deep);
}

/* A wrapper now, not the image itself, because the markup is a <picture> with
   its own mobile and desktop sources. */
.sm-hero__photo {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.sm-hero__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 40%;
  /* The pack's hero lift. An earlier note here refused it, on the grounds that
     at 1.45 the right of the frame washes out and the left stops being
     near-black. That was a judgement made by eye and it does not survive
     measurement: with the four-stop ramp in place the viewport gate's
     composited heading-contrast check passes on all three skins, and the
     photographs plainly read better lifted. Kept, with the evidence. */
  filter: brightness(1.45);
}

/* A left-to-right black ramp, so the heading sits on near-black while the
   photograph stays legible on the right. */
.sm-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--overlay-hero-a);
}

/* The hero copy column becomes a container, so the place name can size itself
   against the width it actually has rather than a per-skin guess. */
.sm-hero__grid > * { container-type: inline-size; }

.sm-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 0.5fr);
  gap: var(--space-16);
  align-items: center;
  min-height: var(--hero-min-h);
}

.sm-hero h1 { max-width: 15ch; }
.sm-hero__lede { max-width: 48ch; }

/* The county road sign. The pack's construction, not an approximation: a sign
   green plate at 7px padding and an 8px radius, tilted, with an inner 3px white
   stroke at a 5px radius and 22px by 40px of inner padding, 190px minimum
   width. The Irish name sits in italic above the English in the sign ink
   colour. Reserved for the place, never used as decoration.
   The first attempt used the spacing scale here and came out cramped: the
   letters sat hard against the white stroke. These are the sign's own
   measurements and they are deliberately not tokens. */
.sm-sign {
  display: inline-block;
  margin-bottom: var(--space-12);
  padding: 7px;
  background: var(--rsa-sign-green);
  border-radius: var(--radius-sign);
  box-shadow: var(--shadow-sign);
  transform: var(--tilt-sign-l);
}

.sm-sign__inner {
  display: block;
  min-width: 190px;
  padding: 22px 40px;
  border: 3px solid var(--rsa-white);
  border-radius: var(--radius-sm);
  text-align: center;
}

.sm-sign__irish {
  display: block;
  font-family: var(--font-body);
  font-style: italic;
  font-size: 21px;
  line-height: 1.1;
  color: var(--color-sign-text);
}

.sm-sign__english {
  display: block;
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: 36px;
  line-height: 1.1;
  color: var(--rsa-white);
}

/* The hero aside: a narrow pricing card, so the first price is visible without
   scrolling. Rule 1 lives in the button row below the heading, not here. */
.sm-hero__aside {
  width: 100%;
  max-width: 320px;
  margin-left: auto;
  padding: var(--pad-card);
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-12);
  border-top: var(--rule-green-top);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-card);
}

.sm-hero__aside dl { margin: 0; }
.sm-hero__aside dt {
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.sm-hero__aside dd {
  margin: 0 0 var(--space-8);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-black);
  color: var(--color-text);
}

/* ---------- 11. Proof bar (P1) ---------- */

/* The numeral or the physical object IS the module. Rendering this as small
   bold text loses it, which is the most repeated note in the Tallaght record.
   Equal-width cells on hairline dividers, no boxes. */
.sm-proof {
  display: flex;
  flex-wrap: wrap;
  border-top: 1px solid var(--rsa-line-16);
}

.sm-proof__cell {
  flex: 1 1 0;
  min-width: 180px;
  display: flex;
  align-items: flex-start;
  gap: var(--space-6);
  padding: var(--space-14) var(--space-16);
  border-left: 1px solid var(--rsa-line-16);
}
.sm-proof__cell:first-child { border-left: 0; padding-left: 0; }

.sm-proof__num {
  font-family: var(--font-display);
  font-size: var(--sm-fs-proof);
  font-weight: var(--fw-black);
  line-height: 1;
  letter-spacing: var(--ls-tighter);
  color: var(--color-star);
}
.sm-proof__num--green { color: var(--color-accent); }
.sm-proof__num--chalk { color: var(--color-text); }

.sm-proof__label {
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  color: var(--color-text);
}
.sm-proof__sub {
  font-size: var(--fs-small);
  color: var(--color-text-muted);
}

/* The learner plate, used as a proof cell object. */
.sm-plate {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  border-radius: var(--radius-lg);
  background: var(--rsa-chalk);
  box-shadow: var(--inset-red);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-plate);
  color: var(--rsa-plate-red);
}

/* The approval seal. */
.sm-seal {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  border-radius: var(--radius-round);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-black);
}

.sm-stars { color: var(--color-star); letter-spacing: var(--ls-stars); }

/* ---------- 12. Cards (C1) ---------- */

.sm-cards {
  display: grid;
  /* auto-fit, so the same grid handles a town with two pathway cards and one
     with four. The card count varies by town data. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--space-12);
}

.sm-card {
  display: flex;
  flex-direction: column;
  padding: var(--pad-card);
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-08);
  border-radius: var(--radius-xs);
}
.sm-card--lead { border-left: var(--rule-green); }

/* Not uppercase at this size: caps on a 19px card title read as a label. */
.sm-card h3 {
  font-size: var(--fs-h4);
  text-transform: none;
  letter-spacing: -0.01em;
}
.sm-card p { font-size: var(--fs-body-sm); color: var(--color-text-muted); }
.sm-card .sm-btn { margin-top: auto; align-self: flex-start; }

/* ---------- 13. Split story (S1) ---------- */

.sm-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-24);
  align-items: center;
}
.sm-split--reverse > :first-child { order: 2; }

/* A copy-and-photograph split is not a 50/50. The prose carries more, so it
   takes more, and the ratio inverts when the row is reversed because `order`
   moves the children but not the columns. Three identical even splits down a
   page is the metronome that makes it read as a template. */
.sm-split--media { grid-template-columns: 1.12fr 0.88fr; align-items: stretch; }
.sm-split--media.sm-split--reverse { grid-template-columns: 0.88fr 1.12fr; }

/* The photograph holds the row rather than floating in the middle of it. */
.sm-split--media .sm-split__photo,
.sm-split--media .sm-split__photo picture { height: 100%; }
.sm-split--media .sm-split__photo img { height: 100%; min-height: 380px; }

/* The green block offsets away from the copy, so it mirrors with the row.
   Left as it was, a flipped row pushes the block under the text. */
.sm-split--reverse .sm-offset::after {
  inset: var(--space-10) var(--space-10) calc(var(--space-9) * -1) calc(var(--space-9) * -1);
}

/* A photograph with a green block offset behind it. The frame owns its ground
   so a transparent cutout does not show the section through it. */
.sm-offset {
  position: relative;
  isolation: isolate;
}
.sm-offset::after {
  content: "";
  position: absolute;
  inset: var(--space-10) calc(var(--space-9) * -1) calc(var(--space-9) * -1) var(--space-10);
  z-index: -1;
  background: var(--color-accent);
}
.sm-offset img {
  width: 100%;
  height: 440px;
  object-fit: cover;
  box-shadow: var(--shadow-card-hover);
}
/* The frame owns its ground: .sm-offset isolates, so the green block paints
   above the element's own background, and the licence photographs are
   transparent cutouts. */
.sm-split__photo picture { display: block; background: var(--color-panel); }
.sm-split__photo img { height: 380px; }

/* ---------- 14. Road stops (S4) ---------- */

.sm-stops {
  position: relative;
  margin: 0;
  padding: 0 0 0 var(--space-24);
  list-style: none;
}
/* The road line itself. A gradient cannot be a border value: written as
   `border-left: var(--road-dash-v)` the declaration is invalid and dropped, so
   this painted nothing at all on every page that carries it, pilots included.
   It has to be a painted box with the gradient as its background. */
.sm-stops::before {
  content: "";
  position: absolute;
  top: var(--space-5);
  bottom: var(--space-5);
  left: 15px;
  width: 4px;
  background-image: var(--road-dash-v);
}

.sm-stop { position: relative; margin-bottom: var(--space-14); }

.sm-stop__num {
  position: absolute;
  left: calc(var(--space-24) * -1);
  top: 0;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-round);
  /* Punched out of the line by a border in the page ground. */
  border: 5px solid var(--color-bg);
  box-sizing: content-box;
  margin-left: -5px;
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-display);
  font-size: var(--fs-caption);
  font-weight: var(--fw-black);
}

.sm-stop h3 { font-size: var(--fs-h4); text-transform: none; }
.sm-stop p { font-size: var(--fs-body-sm); color: var(--color-text-muted); margin: 0; }

/* ---------- 15. Price tickets (F3) ---------- */

/* The system inverts here: a ticket is a pale paper object on a dark page. */
.sm-tickets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--space-12);
}

.sm-ticket {
  display: flex;
  flex-direction: column;
  padding: var(--pad-card);
  background: var(--rsa-chalk);
  color: var(--rsa-road-black);
  border-top: 10px solid var(--color-action);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.sm-ticket--best {
  background: var(--color-accent);
  border-top-color: var(--rsa-road-black);
}

.sm-ticket__name {
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}
.sm-ticket__price {
  font-family: var(--font-display);
  font-size: var(--fs-num-xs);
  font-weight: var(--fw-black);
  line-height: 1;
  letter-spacing: var(--ls-tighter);
}
.sm-ticket__per { font-size: var(--fs-small); opacity: 0.75; }
/* The perforation. */
.sm-ticket__rule {
  margin: var(--space-10) 0;
  border: 0;
  border-top: 2px dashed rgba(11, 11, 12, 0.25);
}
.sm-ticket ul { margin: 0 0 var(--space-12); padding-left: var(--space-10); font-size: var(--fs-body-sm); }
.sm-ticket .sm-btn { margin-top: auto; }

/* A ghost button inside a pale ticket must not be chalk on chalk. */
.sm-ticket .sm-btn--ghost {
  border-color: rgba(11, 11, 12, 0.32);
  color: var(--rsa-road-black);
}
.sm-ticket .sm-btn--ghost:hover { background: rgba(11, 11, 12, 0.08); border-color: var(--rsa-road-black); }

/* Rule 6: a review signal beside the prices, not only in the hero. */
.sm-price-proof {
  margin-top: var(--space-12);
  font-size: var(--fs-body-sm);
  color: var(--color-text-muted);
}

/* ---------- 16. Review wall (C2) ---------- */

.sm-reviews {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--space-12);
}

.sm-review {
  display: flex;
  flex-direction: column;
}
.sm-review__body {
  position: relative;
  /* Holds the row, so the attribution lines rest on one line across four cards
     of unequal copy instead of stepping down the page. */
  flex: 1 1 auto;
  padding: var(--space-14);
  background: var(--color-panel-raised);
  border-radius: var(--radius-xs);
}

/* An embossed notch rather than a character: the glyph is clipped to a short
   box so it reads as a mark, not as text. */
.sm-review__quote {
  display: block;
  /* The pack's own numbers: 96px at line-height .6 clipped to a 44px box. The
     clip is what makes it sit INTO the card rather than above it. */
  height: 44px;
  overflow: hidden;
  font-family: var(--font-display);
  font-size: 96px;
  font-weight: var(--fw-plate);
  line-height: 0.6;
  color: var(--color-bg-deep);
}

.sm-review__body blockquote {
  margin: 0;
  font-size: var(--fs-body-sm);
  color: var(--color-text-body);
}

.sm-review--feature .sm-review__body {
  background: var(--color-action);
  color: var(--color-on-action);
}
.sm-review--feature blockquote { color: var(--color-on-action); }
.sm-review--feature .sm-review__quote { color: var(--rsa-red-hover); }

.sm-review__who {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  margin-top: var(--space-10);
}

.sm-review__avatar {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-round);
  background: var(--color-avatar-blue);
  color: var(--rsa-white);
  font-family: var(--font-display);
  font-weight: var(--fw-black);
}

.sm-review__name { font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--color-text); }
.sm-review__meta { font-size: var(--fs-caption); color: var(--color-text-muted); }

.sm-rating { display: flex; align-items: baseline; gap: var(--space-6); }
.sm-rating__value {
  font-family: var(--font-display);
  /* Display scale on purpose. The pack calls numerals at this size its single
     largest source of contrast on a black page, and it costs nothing. */
  font-size: clamp(72px, 9vw, 132px);
  font-weight: var(--fw-plate);
  line-height: 0.78;
  letter-spacing: var(--ls-tighter);
  color: var(--color-star);
}

/* ---------- 17. FAQ (X2) ---------- */

/* Native <details>, so it works with no JavaScript and satisfies Rule 16 by
   construction. The summary holds the question text and NOTHING else: an icon
   span inside it adds a text node and the content-parity gate reads the
   question as changed. The toggle is drawn by CSS. */
.sm-faq { border-top: 1px solid var(--rsa-line-12); }

.sm-faq__item { border-bottom: 1px solid var(--rsa-line-12); }

.sm-faq__q {
  display: grid;
  grid-template-columns: 1fr 32px;
  align-items: center;
  gap: var(--space-10);
  padding: var(--space-12) 0;
  font-size: var(--fs-body-lg);
  font-weight: var(--fw-bold);
  color: var(--color-text);
  cursor: pointer;
  list-style: none;
}
.sm-faq__q::-webkit-details-marker { display: none; }

.sm-faq__q::after {
  content: "+";
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-round);
  background: var(--color-panel);
  color: var(--color-accent);
  font-family: var(--font-display);
  font-weight: var(--fw-black);
}
.sm-faq__item[open] .sm-faq__q::after {
  content: "\2212";
  background: var(--color-accent);
  color: var(--color-on-accent);
}

.sm-faq__a {
  padding: 0 var(--space-24) var(--space-14) 0;
  font-size: var(--fs-body-sm);
  color: var(--color-text-muted);
}
.sm-faq__a p { margin: 0 0 var(--space-6); }

/* ---------- 18. Place links and pills ---------- */

.sm-places {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: var(--space-5);
}

.sm-place {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding: var(--space-6) var(--space-10);
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-16);
  border-radius: var(--radius-md);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  color: var(--color-text);
  text-decoration: none;
  transition: background var(--dur-fast), border-color var(--dur-fast);
}
.sm-place::after { content: "\2192"; color: var(--color-accent); }
.sm-place:hover { background: var(--color-panel-raised); border-color: var(--color-accent); }

/* For an area that has no page of its own. 71 of the 132 location pages name at
   least one, so this is not an edge case: linking them would ship 404s. */
.sm-pills { display: flex; flex-wrap: wrap; gap: var(--space-5); }
.sm-pill {
  padding: var(--space-4) var(--space-7);
  border: 1px solid var(--rsa-line-12);
  border-radius: 100px;
  font-size: var(--fs-small);
  color: var(--color-text-muted);
}

/* ---------- 19. Call band (X3) and map ---------- */

.sm-band {
  padding: var(--space-28) 0;
  background: var(--color-action);
  color: var(--color-on-action);
  text-align: center;
}
.sm-band :is(h2, h3) {
  max-width: 20ch;
  margin-inline: auto;
  color: var(--color-on-action);
}
/* An h3 would otherwise keep its own much smaller size on a full-width band. */
.sm-band h3 { font-size: var(--fs-h2); line-height: 0.98; letter-spacing: -0.035em; }
.sm-band p { color: var(--color-on-action); }
/* The primary action inverts on red: a red button on red is not an action. */
.sm-band .sm-btn { background: var(--color-bg-deep); color: var(--color-text); }
.sm-band .sm-btn:hover { background: var(--rsa-graphite); }
/* And the secondary stays outlined. The rule above is two classes deep, so it
   beat .sm-btn--ghost and every band showed two identical black buttons
   instead of the pack's black-plus-outlined pair. */
.sm-band .sm-btn--ghost {
  background: transparent;
  border-color: var(--rsa-white);
  color: var(--color-on-action);
}
.sm-band .sm-btn--ghost:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: var(--rsa-white);
}
.sm-band .sm-btn-row { justify-content: center; }

.sm-map {
  height: 420px;
  border: 1px solid var(--rsa-line-12);
  border-radius: var(--radius-xs);
  overflow: hidden;
}

/* ---------- 20. Legacy names, re-provided ---------- */

/* These class names are JavaScript contracts. hero-search.js writes 42 of them
   into the page and they appear in no template, so nothing styles them unless
   this file does. The full inventory is
   data/rsa-school/makeover/interactive-hooks.md.
   Phase 1 provides only the container that the shared base.njk markup needs on
   every page. The chat, lead form and checkout vocabularies are added with the
   pilots that first render them, under their original names and never renamed
   into the sm- namespace. */

/* contact-form.njk wraps itself in `.container`, a unified.css class v2 does
   not define. Without this, the injected lead form has no gutter on any v2
   page, which is exactly how it shipped on Cork. Same measure as .sm-wrap. */
.container {
  width: min(100% - (var(--gutter) * 2), var(--container));
  margin-inline: auto;
}

/* Legacy variable shim.
   locations.11tydata.js:95-107 rewrites phrases inside generated prose and
   injects `style="color: var(--accent-green)"` and `var(--accent-red-bright)`
   directly into the content. Those are v1 token names, defined in unified.css,
   which a v2 page does not load. Without this shim the declarations drop and
   the emphasised phrases render in the body colour.
   The code cannot simply be deleted: the same rewrites add the internal links
   to /edt-driving-lessons, /automatic-driving-lessons, /driving-test-lessons
   and the test centre pages, which are content. So the names are mapped onto
   their v2 equivalents here until that injection is rewritten centrally.
   Cork needed the same shim, for ten names rather than two. */
:root {
  --accent-green: var(--color-accent);
  --accent-red-bright: var(--color-text-red);
}

/* The AI hero search bar and its thread.
   hero-search.js binds #hero-search-form and #hero-search-input and writes 42
   class names into the page at runtime. None of them appears in any template,
   so nothing styles them unless this file does, and the script is shipped
   minified and is not edited by this project. The names are kept exactly as the
   script writes them. Inventory: interactive-hooks.md section 1. */

.hero-search-bar {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  max-width: 620px;
  margin: var(--space-12) 0;
  padding: var(--space-4) var(--space-4) var(--space-4) var(--space-9);
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-16);
  border-radius: var(--radius-xs);
}
.hero-search-bar:focus-within { border-color: var(--color-accent); }

.hero-search-icon { display: flex; color: var(--color-text-muted); }

.hero-search-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--space-5) 0;
  background: transparent;
  border: 0;
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
}
.hero-search-input::placeholder { color: var(--color-placeholder); }
.hero-search-input:focus { outline: none; }

.hero-search-btn {
  flex: 0 0 auto;
  min-height: 40px;
  padding: var(--space-5) var(--space-11);
  background: var(--color-action);
  border: 0;
  border-radius: var(--radius-xs);
  color: var(--color-on-action);
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast);
}
.hero-search-btn:hover { background: var(--color-action-hover); }

.hero-search-results { max-width: 620px; }

/* What the script writes once someone asks something. */
.chat-thread { display: flex; flex-direction: column; gap: var(--space-5); margin-top: var(--space-8); }

.chat-bubble, .chat-msg {
  padding: var(--space-6) var(--space-10);
  border-radius: var(--radius-md);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body);
}
.chat-user { align-self: flex-end; background: var(--color-action); color: var(--color-on-action); }
.chat-assistant, .chat-msg { background: var(--color-panel-raised); color: var(--color-text-body); }
.chat-system { background: transparent; color: var(--color-text-muted); font-size: var(--fs-caption); }
.chat-system-error { color: var(--color-text-red); }
.chat-system-success { color: var(--color-accent); }

.chat-msg-text { color: var(--color-text-body); }
.chat-msg-links, .chat-bubble-links { display: flex; flex-wrap: wrap; gap: var(--space-5); margin-top: var(--space-6); }
.chat-link {
  padding: var(--space-4) var(--space-7);
  border: 1px solid var(--rsa-line-16);
  border-radius: 100px;
  font-size: var(--fs-small);
  color: var(--color-accent);
  text-decoration: none;
}
.chat-link:hover { border-color: var(--color-accent); }
.chat-link-callback { color: var(--color-text); }

.chat-loading-bubble, .chat-msg-loading { color: var(--color-text-muted); }
.loading-dots, .loading-dot { color: var(--color-text-muted); }

.chat-cta-card {
  margin-top: var(--space-6);
  padding: var(--space-10);
  background: var(--color-panel);
  border-left: var(--rule-green);
  border-radius: var(--radius-xs);
}
.chat-cta-title { font-weight: var(--fw-bold); color: var(--color-text); }
.chat-cta-desc { font-size: var(--fs-body-sm); color: var(--color-text-muted); }
.chat-cta-btn {
  display: inline-flex;
  min-height: 40px;
  align-items: center;
  margin-top: var(--space-6);
  padding: var(--space-5) var(--space-11);
  background: var(--color-action);
  border-radius: var(--radius-xs);
  color: var(--color-on-action);
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  text-transform: uppercase;
  text-decoration: none;
}

.chat-call-office, .chat-call-office-link { color: var(--color-accent); }
.chat-meta { margin-top: var(--space-5); font-size: var(--fs-caption); color: var(--color-text-faint); }

.chat-input-bar, .chat-callback-bar {
  display: flex;
  gap: var(--space-5);
  margin-top: var(--space-8);
}
.chat-input-field, .chat-input-inline, .callback-input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: var(--space-5) var(--space-9);
  background: var(--color-panel);
  border: 1px solid var(--color-field-border);
  border-radius: var(--radius-xs);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
}
.chat-input-send, .callback-submit {
  min-height: 44px;
  padding: var(--space-5) var(--space-11);
  background: var(--color-action);
  border: 0;
  border-radius: var(--radius-xs);
  color: var(--color-on-action);
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  text-transform: uppercase;
  cursor: pointer;
}
.callback-header { font-weight: var(--fw-bold); color: var(--color-text); }
.callback-fields { display: flex; flex-wrap: wrap; gap: var(--space-5); margin-top: var(--space-6); }
.callback-success { color: var(--color-accent); }
.callback-error, .status-error { color: var(--color-text-red); }

.hero-inline-form, .hero-callback-form {
  margin-top: var(--space-10);
  padding: var(--pad-card);
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-12);
  border-radius: var(--radius-xs);
}
.hero-inline-form-header { font-weight: var(--fw-bold); color: var(--color-text); margin-bottom: var(--space-6); }

/* The EDT tile numerals, and the two section helpers the town body uses. */
.sm-tile-nums { display: flex; gap: var(--space-4); margin-bottom: var(--space-6); }
.sm-tile-num {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-round);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: var(--fw-black);
}

.sm-figures { display: flex; flex-wrap: wrap; gap: var(--space-24); margin: var(--space-16) 0; }
.sm-figure__value {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-stat);
  font-weight: var(--fw-black);
  line-height: 1;
  letter-spacing: var(--ls-tighter);
  color: var(--color-accent);
}
.sm-figure__label { display: block; margin-top: var(--space-5); font-size: var(--fs-body-sm); color: var(--color-text-muted); }

.sm-pickup-heading { margin-top: var(--space-20); font-size: var(--fs-h4); text-transform: none; }
.sm-pretest { margin-top: var(--space-16); }
.sm-hero__headline { margin-bottom: var(--space-12); }

/* The two-stage lead form.
   Styled under its ORIGINAL class names, because base.njk clones it from a
   <template> and binds it by id, and hero-search.js clones it again into the
   hero and finds its fields by suffix selectors. The markup is NOT changed.
   This is the site's main conversion path, and until this existed it rendered
   as raw browser selects and radio buttons on a v2 page.

   Two structural constraints that are not obvious from the markup:
     - the steps block must stay a direct sibling of the form inside
       .lead-form-card, because the loader finds the pills from the form's
       parent element;
     - stage visibility rides an inline style.display that the loader sets, and
       an inline style wins over this file, so the grid goes on
       .lead-form-grid rather than on .lead-form-stage. */

.lead-form-section { padding: var(--space-section) 0; }

.lead-form-card {
  max-width: 760px;
  margin-inline: auto;
  padding: var(--pad-panel);
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-12);
  border-top: var(--rule-green-top);
  border-radius: var(--radius-xs);
  /* No shadow. Shadows belong to objects, signs, plates, tickets and logbooks.
     A panel gets a hairline and a coloured top rule, which this already has.
     Putting shadows on panels flattens the objects and muddies the panels at
     the same time. */
}

/* The card heading must not inherit the page's display scale: at --fs-hero-a a
   form title is larger than the page h1. But --fs-h3 computes to 22px, under
   the pack's 30px floor for a heading, and this is the one place on the page
   where the learner is asked to do something. 30px clears the floor and stays
   well under the 50px section scale. */
.lead-form-header h2 {
  font-size: 30px;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-4);
}
.lead-form-header p {
  font-size: var(--fs-body-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-12);
}

/* Numbered step pills, joined by a rule. */
.lead-form-steps {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-bottom: var(--space-14);
}
.lead-form-step {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}
.lead-form-step-num {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-round);
  background: var(--color-panel-raised);
  border: 1px solid var(--rsa-line-16);
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: var(--fw-black);
  color: var(--color-text-faint);
}
.lead-form-step.active { color: var(--color-text); }
.lead-form-step.active .lead-form-step-num {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}
.lead-form-step-line {
  flex: 1 1 auto;
  height: 1px;
  background: var(--rsa-line-16);
}

.lead-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-10);
}
.lead-form-field { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.lead-form-field.full { grid-column: 1 / -1; }

.lead-form-label {
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* One rule for every control, so a select cannot fall back to the browser's. */
.lead-form-input,
.lead-form-select,
.lead-form-textarea,
.captcha-input {
  width: 100%;
  min-height: 46px;
  padding: var(--space-5) var(--space-9);
  background: var(--color-bg);
  border: 1px solid var(--color-field-border);
  border-radius: var(--radius-xs);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  line-height: 1.4;
}
.lead-form-textarea { min-height: 96px; padding-top: var(--space-6); resize: vertical; }

.lead-form-input::placeholder,
.lead-form-textarea::placeholder,
.captcha-input::placeholder { color: var(--color-placeholder); }

.lead-form-input:focus,
.lead-form-select:focus,
.lead-form-textarea:focus,
.captcha-input:focus { border-color: var(--color-accent); outline: none; }

/* A native select keeps its own arrow unless it is reset, and that arrow is the
   one part that still reads as an unstyled browser control. */
.lead-form-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--space-24);
  background-image: linear-gradient(45deg, transparent 50%, var(--color-text-muted) 50%),
                    linear-gradient(135deg, var(--color-text-muted) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 14px) 50%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
.lead-form-select option { background: var(--color-panel); color: var(--color-text); }

/* Radios become chips, so a transmission choice is a target rather than a dot. */
.lead-form-radio-group { display: flex; flex-wrap: wrap; gap: var(--space-5); }
.lead-form-radio {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 42px;
  padding: var(--space-5) var(--space-10);
  background: var(--color-bg);
  border: 1px solid var(--color-field-border);
  border-radius: 100px;
  font-size: var(--fs-body-sm);
  color: var(--color-text-body);
  cursor: pointer;
}
.lead-form-radio input { accent-color: var(--color-accent); margin: 0; }
.lead-form-radio:hover { border-color: var(--color-accent); }
.lead-form-radio:has(input:checked) {
  border-color: var(--color-accent);
  background: rgba(142, 219, 79, 0.08);
  color: var(--color-text);
}

.lead-form-captcha { display: flex; flex-direction: column; gap: var(--space-4); }
.captcha-row { display: flex; align-items: center; gap: var(--space-6); }
.captcha-row .captcha-input { max-width: 120px; }
.captcha-error { font-size: var(--fs-caption); color: var(--color-text-red); }

.lead-form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
  margin-top: var(--space-12);
}

.lead-form-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: var(--pad-btn);
  background: var(--color-action);
  border: 0;
  border-radius: var(--radius-xs);
  color: var(--color-on-action);
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur-fast);
}
.lead-form-submit:hover { background: var(--color-action-hover); }

.lead-form-back {
  background: none;
  border: 0;
  padding: var(--space-5) 0;
  color: var(--color-text-muted);
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.lead-form-back:hover { color: var(--color-text); }

.lead-form-fineprint { margin-top: var(--space-6); font-size: var(--fs-caption); color: var(--color-text-faint); }

.lead-form-status { margin-top: var(--space-8); font-size: var(--fs-body-sm); color: var(--color-text-body); }
.lead-form-status.status-error, .status-error { color: var(--color-text-red); }

/* ---------- 22. Gaps closed after the first review ---------- */

/* Everything in this section was found by listing every class the built page
   uses and subtracting the ones these stylesheets define. Each one was
   rendering with no styling at all on a v2 page. That check is now a gate:
   check-unstyled-classes.mjs. */

/* The floating chat nudge and its overlay, both from base.njk, so they appear
   on EVERY page. Original class names: the inline script in base.njk binds
   them and hero-search.js shares the thread vocabulary. */
.chat-nudge {
  position: fixed;
  right: var(--space-16);
  bottom: var(--space-16);
  z-index: var(--z-sticky-cta);
  display: none;
}
.chat-nudge-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-5);
  min-height: 48px;
  padding: var(--space-5) var(--space-12);
  background: var(--color-action);
  border: 0;
  border-radius: 100px;
  box-shadow: var(--shadow-red-glow);
  color: var(--color-on-action);
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  line-height: 1;
  cursor: pointer;
}
.chat-nudge-btn:hover { background: var(--color-action-hover); }
.chat-nudge-label { white-space: nowrap; }

.chat-overlay {
  position: fixed;
  right: var(--space-16);
  bottom: var(--space-16);
  z-index: var(--z-overlay);
  display: none;
  flex-direction: column;
  width: min(380px, calc(100vw - var(--space-24)));
  max-height: min(560px, calc(100vh - var(--space-24)));
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-16);
  border-top: var(--rule-green-top);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-float);
}
.chat-overlay-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-10) var(--space-12);
  border-bottom: 1px solid var(--rsa-line-12);
}
.chat-overlay-title {
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text);
}
.chat-overlay-close {
  width: 32px;
  height: 32px;
  background: transparent;
  border: 1px solid var(--rsa-line-16);
  border-radius: var(--radius-round);
  color: var(--color-text-muted);
  font-size: var(--fs-body);
  line-height: 1;
  cursor: pointer;
}
.chat-overlay-close:hover { color: var(--color-text); border-color: var(--color-text); }

.chat-overlay-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-12);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.chat-overlay-bubble { font-size: var(--fs-body-sm); }
.chat-overlay-user { align-self: flex-end; padding: var(--space-5) var(--space-10); background: var(--color-action); color: var(--color-on-action); border-radius: var(--radius-md); }
.chat-overlay-assistant { padding: var(--space-5) var(--space-10); background: var(--color-panel-raised); color: var(--color-text-body); border-radius: var(--radius-md); }
.chat-overlay-links { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-top: var(--space-5); }
.chat-overlay-link { font-size: var(--fs-small); color: var(--color-accent); text-decoration: none; }

.chat-overlay-input {
  display: flex;
  gap: var(--space-5);
  padding: var(--space-10) var(--space-12);
  border-top: 1px solid var(--rsa-line-12);
}
.chat-overlay-field {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 42px;
  padding: var(--space-5) var(--space-9);
  background: var(--color-bg);
  border: 1px solid var(--color-field-border);
  border-radius: var(--radius-xs);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
}
.chat-overlay-field::placeholder { color: var(--color-placeholder); }
.chat-overlay-field:focus { border-color: var(--color-accent); outline: none; }
.chat-overlay-send {
  min-height: 42px;
  padding: var(--space-5) var(--space-10);
  background: var(--color-action);
  border: 0;
  border-radius: var(--radius-xs);
  color: var(--color-on-action);
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  text-transform: uppercase;
  line-height: 1;
  cursor: pointer;
}

/* The form element itself, inside .lead-form-card. */
.lead-form { display: block; }

/* Hero skins.
   Signpost is the base rule set with no modifier at all: a full-bleed backdrop
   under the black ramp, copy left. Route and night are the other two thirds of
   the 44/44/44 assignment, and until these existed every page rendered as
   signpost whatever its skin said. */
.sm-hero--signpost .sm-hero__photo img { object-position: 62% 40%; }

/* Where the subject actually is in each photograph.
 *
 * A focal point belongs to the picture, not to the page or the skin, so these
 * are keyed to the file: any page using that photograph crops correctly and
 * cannot drift. The three hero images do not agree with one another:
 *
 *   hero-01-hero-1      two faces, the near one about 62% across
 *   hero-02-licence-1   subject right of centre, black to the left
 *   hero-03-licence-3   subject LEFT of centre, black to the right
 *
 * The two licence photographs are near mirror images, so no single value can
 * serve both. The shared 62% is right for hero-01 and lands in hero-03's empty
 * black. On a desktop hero that is a wide crop and the subject still shows; on
 * a phone the crop is a narrow vertical slice, so the wrong value puts the
 * subject off-frame entirely. That is what it did on 49 pages using hero-03.
 *
 * Specificity is deliberate: .sm-hero raises these to (0,3,1) so they beat the
 * per-family rules in guide.css and legal.css, which set 78% and 76% for pages
 * that also use hero-03. */
.sm-hero .sm-hero__photo img[src*="hero-01-hero-1"] { object-position: 62% 38%; }
.sm-hero .sm-hero__photo img[src*="hero-02-licence-1"] { object-position: 66% 32%; }
.sm-hero .sm-hero__photo img[src*="hero-03-licence-3"] { object-position: 32% 30%; }

/* Route: no backdrop at all, so the ground is pure black. The photograph moves
   into the aside, framed in an 8px chalk keyline on the green offset block. */
.sm-hero--route { background: var(--color-bg-deep); }
.sm-hero--route .sm-hero__grid {
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr);
  align-items: center;
}
.sm-hero__frame {
  border: 8px solid var(--rsa-chalk);
  box-shadow: var(--shadow-card-hover);
}
/* The ground goes on the picture, not the frame: .sm-offset isolates, so its
   green block paints above the element's own background, and the licence
   photographs are transparent cutouts. */
.sm-hero__frame picture { display: block; background: var(--color-panel); }
.sm-hero__frame img { width: 100%; height: 300px; object-fit: cover; object-position: 55% 35%; }

/* Night: a slanted photo panel on the right with an 18px green slanted bar as
   its leading edge. The counter-skew on the image undoes the parent skew so
   faces are not sheared, and the scale hides the corners the skew exposes. */
.sm-hero--night .sm-hero__photo {
  left: 56%;
  width: 58%;
  transform: skew(-10deg);
  overflow: hidden;
}
.sm-hero--night .sm-hero__photo img {
  transform: skew(10deg) scale(1.12);
  object-position: 32% center;
}
.sm-hero--night::before { background: var(--overlay-hero); }
.sm-hero--route { --hero-measure: 48vw; }
/* The skewed green leading edge, on the PHOTO wrapper, not on .sm-hero. It
   used to be .sm-hero--night::after, which the hero's own closing rule
   (.sm-hero::after, declared later) silently replaced on all 44 night towns.
   One element has one ::after, and a modifier does not get its own. */
.sm-hero--night .sm-hero__photo::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 56%;
  width: 18px;
  background: var(--color-accent);
  transform: skew(-10deg);
  z-index: 1;
}

/* Proof bar body, the label and sub stacked beside the numeral or object. */
.sm-proof__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* Review card wrapper. */
.sm-review { display: flex; flex-direction: column; }
.sm-review__stars {
  display: block;
  margin-bottom: var(--space-5);
  color: var(--color-star);
  letter-spacing: var(--ls-stars);
  font-size: var(--fs-small);
}
.sm-review--feature .sm-review__stars { color: var(--rsa-white); }

/* Footer columns. */
.sm-footer__col { min-width: 0; }
.sm-footer__col--brand { max-width: 36ch; }
.sm-footer__copyright { color: var(--color-text-faint); }

/* Next junctions. The three cards keep distinct top rules, which is what tells
   them apart at a glance. */
.sm-junctions { margin-top: var(--space-16); }
.sm-junction { border-top: 3px solid var(--rsa-line-16); }
.sm-junction--district { border-top-color: var(--color-action); }
.sm-junction--nearby { border-top-color: var(--color-accent); }
.sm-junction--centres { border-top-color: var(--color-star); }

.sm-junction__kind {
  display: block;
  margin-bottom: var(--space-5);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.sm-junction__lead {
  display: block;
  margin-bottom: var(--space-6);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--color-text-red);
  text-decoration: none;
}
.sm-junction__lead:hover { color: var(--color-text); }

.sm-junction__list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--space-5); }
.sm-junction__list a {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  font-size: var(--fs-body-sm);
  color: var(--color-text-body);
  text-decoration: none;
}
.sm-junction__list a::before { content: ""; width: 6px; height: 6px; border-radius: var(--radius-round); background: var(--color-accent); flex: 0 0 auto; }
.sm-junction--centres .sm-junction__list a::before { background: var(--color-star); }
.sm-junction__list a:hover { color: var(--color-text); }

.sm-crumb--foot {
  margin-top: var(--space-16);
  padding-top: var(--space-12);
  border-top: 1px solid var(--rsa-line-08);
}
.sm-crumb ol { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.sm-crumb li { display: flex; align-items: center; gap: var(--space-2); }
.sm-crumb__sep { color: var(--rsa-slate); }
.sm-crumb__current { color: var(--color-text); font-weight: var(--fw-semi); }

/* ---------- 24. The moves that make it a design ---------- */

/* Taken from the pack's own skin A, section 1 of pack-town-page-spec.md. Each
   one is presentation applied to words and images already on the page. */

/* The hero title, stacked. The pack sets the service line at 44px chalk and the
   place name at up to 100px green, and says plainly that the size jump between
   the lines IS the effect. One line at a single size is the version that reads
   as plain. */
.sm-hero h1 { max-width: 15ch; margin-bottom: var(--space-10); }

.sm-hero__service {
  display: block;
  font-size: clamp(26px, 3.2vw, 44px);
  line-height: 1;
  letter-spacing: var(--ls-display);
  color: var(--color-text);
}

.sm-hero__place {
  display: block;
  /* Sized from the name's own length, so it never has to break. An uppercase
     display word is about 0.74 x font-size per character; 0.76 leaves a
     margin. --place-ch comes from the template, --hero-measure is how much of
     the viewport this skin gives the copy. Without this, overflow-wrap broke
     BLANCHARDSTOWN across two lines mid-word. */
  font-size: min(clamp(40px, 7vw, 100px),
                 calc(var(--hero-measure, 64vw) / (var(--place-ch, 8) * 0.76)),
                 calc(100cqw / (var(--place-ch, 8) * 0.76)));
  margin: 4px 0 6px;
  /* The padding stops the green clipping its own right edge at the largest
     size, which the pack also guards against. */
  padding-right: 0.06em;
  line-height: 0.95;
  letter-spacing: var(--ls-tighter);
  color: var(--color-accent);
  text-wrap: balance;
}

/* A 6px green rule welds the hero to the body. Without it the photograph just
   stops and the first section begins. */
.sm-hero::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 6px;
  background: var(--color-accent);
}

/* The county sign straightens on hover. It is a road sign, so it should behave
   like an object rather than a label. */
.sm-sign { transition: transform var(--dur-base) var(--ease-reveal); }
.sm-sign:hover { transform: rotate(0deg); }

/* A giant watermark numeral behind the EDT section. The pack puts one at 260px
   in white at 4 per cent, bled off the bottom right corner. It is the cheapest
   depth on the page and it reads as a logbook stamp. */
.sm-section--watermark { position: relative; overflow: hidden; }
.sm-section--watermark > .sm-wrap { position: relative; z-index: 1; }
.sm-section--watermark::after {
  content: attr(data-watermark);
  position: absolute;
  right: -40px;
  bottom: -70px;
  z-index: 0;
  font-family: var(--font-display);
  font-size: clamp(200px, 26vw, 400px);
  font-weight: var(--fw-black);
  line-height: 0.8;
  letter-spacing: -0.08em;
  color: rgba(255, 255, 255, 0.045);
  pointer-events: none;
  user-select: none;
}

/* Outlined, where the browser can outline it. A solid fill at 4 per cent is
   what the pack's older skin A used and it barely registers; the stroke is the
   form the pack assigns to the EDT tracker. The fill above stays as the
   fallback, so nothing disappears if the stroke is unsupported. */
@supports (-webkit-text-stroke: 1px white) {
  .sm-section--watermark::after {
    color: transparent;
    -webkit-text-stroke: 1.5px rgba(255, 255, 255, 0.075);
  }
}

/* Corner tags welded to a photo frame, red at one corner and green at the
   other. They stop a framed photograph reading as a stock image in a box.
   Drawn on the inner <picture>, NOT on .sm-split__photo: that element is also
   .sm-offset, whose green offset block is ::after, and one element cannot carry
   two. The tag won that fight silently and the offset block never painted. */
.sm-split__photo picture { position: relative; }
.sm-split__photo picture::before,
.sm-split__photo picture::after {
  content: "";
  position: absolute;
  z-index: 2;
  width: 56px;
  height: 10px;
}
.sm-split__photo picture::before { left: 0; bottom: 0; background: var(--color-action); }
.sm-split__photo picture::after { right: 0; top: 0; background: var(--color-accent); }

/* Repeated cards take a green top edge. The pack is explicit that a card with
   no green dash top reads as generic, and this page has three grids of them. */
.sm-cards > .sm-card { border-top: 3px solid var(--rsa-line-16); }
.sm-cards > .sm-card:not([class*="sm-junction--"]):nth-child(3n + 1) { border-top-color: var(--color-accent); }

/* Hover as motion. A lift, not just a colour change: these are objects. */
.sm-card,
.sm-place,
.sm-ticket {
  transition: transform var(--dur-fast) var(--ease-reveal),
              border-color var(--dur-fast),
              background var(--dur-fast);
}
.sm-cards > .sm-card:hover,
.sm-place:hover { transform: var(--lift-sm); }
.sm-ticket:hover { transform: var(--lift-sm); }

/* The closing band is closed by a green rule, the mirror of the one that opens
   the page under the hero. */
.sm-band { border-bottom: 6px solid var(--color-accent); }

/* The Book band's two objects, from the pack's section 13. Both were missing
   everywhere, so 27 bands across five families and all three town pilots were a
   flat red box with a green rule under it. Centrally, not in five family sheets:
   the test centre, guide and home sheets had each drawn their own copy. */
.sm-band { position: relative; overflow: hidden; }

/* The furniture below belongs to the RED Book band. A family using .sm-band as
   an amber caution or green fact band opts out with this class, rather than
   redeclaring both pseudo-elements to suppress them. */
.sm-band--plain::before,
.sm-band--plain::after { content: none; }
.sm-band > .sm-wrap { position: relative; z-index: 1; }

/* The white centre line that closes the band. A road marking, not a divider,
   so it is a dashed gradient rather than a border. It sits inside the padding
   box, just above the green rule. */
.sm-band::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 8px;
  background: repeating-linear-gradient(90deg, var(--rsa-chalk) 0 60px, transparent 60px 96px);
  opacity: 0.9;
}

/* The L plate as a physical object. The double inset shadow is what makes it a
   plate rather than a white square: red border, white gap, then the card. It is
   never a real border, because a border sits outside the radius.
   Desktop only: 220px would own a phone screen, and the band's copy comes
   first there. */
@media (min-width: 1100px) {
  .sm-band::after {
    content: "L";
    position: absolute;
    right: 6%;
    top: 50%;
    z-index: 0;
    margin-top: -110px;
    width: 220px;
    height: 220px;
    display: grid;
    place-items: center;
    background: var(--rsa-white);
    border-radius: 10px;
    box-shadow:
      inset 0 0 0 10px var(--color-action),
      inset 0 0 0 14px var(--rsa-white),
      0 30px 60px rgba(0, 0, 0, 0.25);
    transform: rotate(9deg);
    font-family: var(--font-display);
    font-size: 170px;
    font-weight: var(--fw-black);
    line-height: 1;
    color: var(--color-action);
    opacity: 0.97;
    pointer-events: none;
    user-select: none;
  }
}

/* On the red band the accent clause is BLACK, not green. The pack says so, and
   green on RSA Red is the weakest pairing in the palette. */
.sm-band h2 em,
.sm-band .sm-accent { color: var(--rsa-road-black); }

/* The lead form's inner stage becomes a paper surface: a light inset on dark,
   held in by a dashed keyline. The pack uses exactly this for its booking
   panel, and it is the one place on the page where the learner is asked to do
   something, so it earns being the one light surface. */
.lead-form-card { position: relative; }
.lead-form-stage {
  padding: var(--pad-card);
  background: var(--rsa-chalk);
  border: 2px dashed var(--rsa-graphite);
  border-radius: var(--radius-md);
}
.lead-form-stage .lead-form-label { color: var(--rsa-slate); }
.lead-form-stage .lead-form-input,
.lead-form-stage .lead-form-select,
.lead-form-stage .lead-form-textarea,
.lead-form-stage .captcha-input {
  /* background-color, not background. The shorthand resets background-size to
     auto, and the select's 6px arrow gradients then tile over the whole box. */
  background-color: var(--rsa-white);
  border-color: var(--rsa-graphite);
  color: var(--rsa-road-black);
  /* html sets color-scheme: dark, which is right for the rest of the site but
     wrong here: Chrome then draws the date picker's calendar glyph LIGHT, and
     these fields are white, so the control vanishes. It is still there and
     still clickable, which is why it reads as a styling fault rather than a
     missing feature. Declared beside the background it belongs to, so a change
     to one is hard to make without seeing the other. */
  color-scheme: light;
}
.lead-form-stage .lead-form-input::placeholder,
.lead-form-stage .lead-form-textarea::placeholder,
.lead-form-stage .captcha-input::placeholder { color: var(--rsa-slate); }
.lead-form-stage .lead-form-select {
  background-image: linear-gradient(45deg, transparent 50%, var(--rsa-slate) 50%),
                    linear-gradient(135deg, var(--rsa-slate) 50%, transparent 50%);
}
.lead-form-stage .lead-form-select option { background: var(--rsa-white); color: var(--rsa-road-black); }
.lead-form-stage .lead-form-radio {
  background-color: var(--rsa-white);
  border-color: var(--rsa-graphite);
  color: var(--rsa-road-black);
}
.lead-form-stage .lead-form-radio:has(input:checked) {
  border-color: var(--rsa-sign-green);
  background-color: rgba(0, 112, 60, 0.08);
  color: var(--rsa-road-black);
}
.lead-form-stage .lead-form-back { color: var(--rsa-slate); }
.lead-form-stage .captcha-error { color: var(--rsa-red); }


/* The hero's foot fade. The pack runs a 220px black ramp up from the bottom
   edge so the photograph does not simply stop where the hero ends. The green
   rule then closes it. Without the fade the join is a hard horizontal line
   across the middle of a photograph. */
.sm-hero__photo::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 220px;
  background: linear-gradient(0deg, var(--rsa-night), rgba(0, 0, 0, 0));
}

/* The giant footer wordmark, bled off the bottom edge. The pack calls this the
   last piece of scale on the page and says dropping it removes a device
   nothing else replaces. Two-tone, aria-hidden in the markup. */
.sm-footer__word {
  display: flex;
  justify-content: center;
  gap: 0.22em;
  margin: var(--space-16) 0 -0.14em;
  font-family: var(--font-display);
  font-size: min(11vw, 150px);
  font-weight: var(--fw-plate);
  line-height: 0.9;
  letter-spacing: -0.05em;
  text-transform: lowercase;
  white-space: nowrap;
  user-select: none;
}
.sm-footer__word span:first-child { color: rgba(255, 255, 255, 0.12); }
.sm-footer__word span:last-child { color: var(--color-accent); }

/* The footer column heading gains the pack's two-gradient underline: a 40px
   green dash sitting on a full-width hairline. It is what turns four lists
   into four columns. */
.sm-footer .sm-footer__heading {
  padding-bottom: var(--space-6);
  background:
    linear-gradient(var(--color-accent), var(--color-accent)) 0 100% / 40px 2px no-repeat,
    linear-gradient(var(--rsa-line-16), var(--rsa-line-16)) 0 100% / 100% 1px no-repeat;
}

/* The FAQ becomes two columns with the heading held beside the questions. */
.sm-faq-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 2fr);
  gap: var(--space-16) var(--space-20);
  align-items: start;
}
.sm-faq-grid__head { position: sticky; top: 24px; }
.sm-faq-grid__head h2 { margin-bottom: 0; }

/* A deliberate heavy rule, not a hairline. The pack is explicit about this
   one: the FAQ opens on 2px of chalk. */
.sm-faq { border-top: 2px solid var(--rsa-chalk); }

/* Shadows belong to objects, and a lifting card is an object while it lifts.
   The transform alone reads as a twitch. */
.sm-cards > .sm-card:hover,
.sm-place:hover { box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5); }
.sm-ticket:hover { box-shadow: 0 30px 60px rgba(0, 0, 0, 0.45); }

/* The pack's scroll offset, so an in-page anchor does not land under the
   sticky header. Every jump link on this page needs it. */
[id] { scroll-margin-top: 110px; }

/* The pack's ghosted section numeral, 72px in the EDT tracker's empty-cell
   colour. On black it is texture rather than a figure, which is the point: the
   system says contrast comes from scale and light, not colour. It marks the two
   sections that carry commercial weight, so they announce themselves
   differently from the six that only need the lane-marker chip. The numeral is
   drawn from data-n, so it never enters the markup as text. */
.sm-section--numbered { position: relative; overflow: hidden; }
.sm-section--numbered > .sm-wrap { position: relative; z-index: 1; }
.sm-section--numbered::before {
  content: attr(data-n);
  position: absolute;
  top: var(--space-10);
  right: var(--space-16);
  z-index: 0;
  font-family: var(--font-display);
  font-size: clamp(88px, 9vw, 132px);
  font-weight: var(--fw-black);
  line-height: 0.8;
  letter-spacing: -0.06em;
  color: var(--rsa-track);
  pointer-events: none;
  user-select: none;
}

/* auto-fit put six cards into four columns and left half of the second row
   empty, which reads as a layout fault rather than a grid. Six belongs in
   three columns: two full rows, and a 420px card carries that much prose
   better than a 302px one. The card counts on a town page are 3, 2 and 6. */
@media (min-width: 769px) {
  .sm-cards:has(> :nth-child(6):last-child) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* Five tickets went 4 + 1 and stranded the pretest on a row of its own. A price
   list wants one row: five prices read as a scale, four and a straggler read as
   a fault. At 1280px that is 237px a ticket, which still holds a price and a
   button. Between the tablet break and 1200px there is not room for five, so
   they go 3 + 2, which is short by one rather than by three. */
@media (min-width: 769px) {
  .sm-tickets:has(> :nth-child(3):last-child) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (min-width: 1200px) {
  .sm-tickets:has(> :nth-child(5):last-child) {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}
@media (min-width: 769px) and (max-width: 1199px) {
  .sm-tickets:has(> :nth-child(5):last-child) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* ---------- 25. Fare switch and bookable tickets ---------- */

/* The transmission selector. Two slanted chips in one slot, the pack's shape,
   so it reads as part of the road-sign furniture rather than a widget. */
.sm-fare-switch {
  display: inline-flex;
  gap: 2px;
  margin-bottom: var(--space-12);
  padding: 3px;
  background: var(--color-panel);
  border: 1px solid var(--rsa-line-12);
  border-radius: var(--radius-xs);
}

.sm-fare-switch__btn {
  /* 44px is the touch-target floor. At the chip's natural padding these came
     out 40px, which is the same mistake the sticky bar made in Phase 1. */
  min-height: 44px;
  padding: var(--space-5) var(--space-12);
  border: 0;
  border-radius: var(--radius-xxs, 2px);
  background: transparent;
  font-family: var(--font-display);
  font-size: var(--fs-label);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.sm-fare-switch__btn:hover { color: var(--color-text); }
.sm-fare-switch__btn.is-active {
  background: var(--color-accent);
  color: var(--color-on-accent);
}
.sm-fare-switch__btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Nothing is hidden until the script says it is running. Without JavaScript
   both fares stay on the page, which is what the page did before. */
#pricing[data-switch="on"][data-trans-active="manual"] [data-trans="automatic"],
#pricing[data-switch="on"][data-trans-active="automatic"] [data-trans="manual"] {
  display: none;
}

/* A ticket is now a link to that package in the shop. It was a div, so every
   anchor reset the sheet applies to buttons has to be undone here. */
a.sm-ticket {
  text-decoration: none;
  color: var(--rsa-road-black);
  cursor: pointer;
}
a.sm-ticket:hover { border-top-color: var(--rsa-road-black); }
a.sm-ticket.sm-ticket--best:hover { border-top-color: var(--rsa-chalk); }
a.sm-ticket:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: 3px;
}

/* The arrow that says the card goes somewhere. ::after on .sm-ticket was free;
   the perforation is its own <hr> and --best only changes colours. */
a.sm-ticket { position: relative; padding-bottom: var(--space-16); }
a.sm-ticket::after {
  content: "→";
  position: absolute;
  right: var(--pad-card);
  bottom: var(--space-10);
  font-family: var(--font-display);
  font-size: var(--fs-body-lg);
  font-weight: var(--fw-black);
  color: var(--color-action);
  transition: transform var(--dur-fast) var(--ease-reveal);
}
a.sm-ticket.sm-ticket--best::after { color: var(--rsa-road-black); }
a.sm-ticket:hover::after { transform: translateX(4px); }

/* The fare block keeps the price and its unit together, so the two
   transmissions swap as one unit rather than four loose lines. */
.sm-ticket__fare { display: block; }
.sm-ticket__fare .sm-ticket__price { display: block; }
.sm-ticket__fare .sm-ticket__per { display: block; margin-bottom: var(--space-6); }

/* The headline figures swap their numeral and the single word that names the
   transmission, so the figures above the tickets never contradict them. */
.sm-swap { display: inline; }

/* ---------- 21. Responsive ---------- */

@media (max-width: 1100px) {
  .sm-footer__grid { grid-template-columns: 1fr 1fr; }

  /* The pack's own framing threshold. Below it the hero aside sits under the
     heading rather than beside it, and the split story stops being a split. */
  .sm-hero__grid { grid-template-columns: 1fr; gap: var(--space-16); }
  .sm-hero__aside { margin-left: 0; max-width: none; }
  /* The two-class selector .sm-split--media.sm-split--reverse out-specifies a
     single-class collapse at every width, so the lessons row stayed in two
     columns on a phone: 140px of prose beside a 178px photograph. Both
     variants are named here so the collapse actually wins. */
  .sm-split,
  .sm-split--media,
  .sm-split--media.sm-split--reverse { grid-template-columns: 1fr; gap: var(--space-16); }
  .sm-split--reverse > :first-child { order: 0; }
  /* The photograph stops holding the row once the row is a stack. */
  .sm-split--media .sm-split__photo,
  .sm-split--media .sm-split__photo picture { height: auto; }
  .sm-split--media .sm-split__photo img { height: 300px; min-height: 0; }
}

@media (max-width: 768px) {
  /* The hero ramp runs left to right, which is right for a two-column hero and
     wrong for a stacked one: at phone width the copy sits over the whole frame.
     A scrim across the whole frame, darkest where the heading sits. */
  .sm-hero::before { background: var(--overlay-hero-mobile); }
  /* The copy has the full width here, so the place name may use it. */
  .sm-hero, .sm-hero--route { --hero-measure: 90vw; }

  /* A held heading beside the questions needs room. Below the tablet break it
     goes back to a stack and stops sticking. */
  .sm-faq-grid { grid-template-columns: 1fr; gap: var(--space-12); }
  .sm-faq-grid__head { position: static; }
  /* Reassigning the token narrows every .sm-wrap and .container at once. */
  :root { --gutter: var(--gutter-mobile); }

  body { font-size: var(--fs-body); }

  .sm-section { padding: var(--sm-space-64) 0; }
  .sm-section--tight { padding: var(--space-16) 0; }

  /* The 50px floor on --fs-hero-a clipped long place names at 375px. */
  h1 { font-size: clamp(31px, 8.6vw, 52px); }
  /* The stacked hero title keeps its size jump at phone width, just smaller:
     the jump is the effect, so it is not flattened away here. */
  /* Dialled up for a phone: the hero is the whole screen there, so the type
     can afford to be larger. The place name keeps the same min() shape as the
     base rule, so a long name still shrinks to fit rather than breaking, but
     with a bigger vw coefficient: the base rule's 7vw is tuned for a desktop
     column and would land a short name on the 40px floor here, which is
     smaller than it used to be rather than larger. */
  .sm-hero__service { font-size: clamp(23px, 7vw, 34px); }
  .sm-hero__place {
    font-size: min(clamp(46px, 14vw, 72px),
                   calc(var(--hero-measure, 90vw) / (var(--place-ch, 8) * 0.76)),
                   calc(100cqw / (var(--place-ch, 8) * 0.76)));
  }
  .sm-hero__lede, .sm-hero .sm-lede { font-size: clamp(17px, 4.6vw, 20px); line-height: 1.5; }
  .sm-hero .sm-btn { min-height: 52px; font-size: var(--fs-body-sm); }
  .lead-form-stage { padding: var(--space-12); }

  .sm-header__inner { min-height: 72px; gap: var(--space-8); }
  .sm-nav { display: none; }
  /* Touch targets. Buttons take the 44px floor outright. Dense link rows take
     vertical padding instead: a footer of 44px-tall rows is a screen and a
     half of links, so they get a comfortable row height rather than the
     button floor. */
  .sm-btn--sm,
  .hero-search-btn { min-height: 44px; }
  .sm-footer__phone { display: inline-flex; align-items: center; min-height: 44px; }
  .sm-footer__address summary { padding: var(--space-5) 0; }
  .sm-footer__links a { display: block; padding: var(--space-4) 0; }
  /* .sm-crumb, not .sm-breadcrumb. The first attempt at this rule used a class
     that does not exist in the markup, so it silently did nothing and the
     breadcrumb stayed at 21px. Only measuring caught it. */
  .sm-crumb a { display: inline-block; padding: var(--space-4) 0; }
  /* A standalone link under a review, not a link inside a sentence, so the
     inline exemption does not apply to it. */
  .sm-review__meta { display: inline-block; padding: var(--space-4) 0; }
  /* The "or call" alternative is a phone action, so it gets a real target even
     though it sits in a sentence. */
  .sm-or-call a { display: inline-block; padding: var(--space-4) 0; }
  /* A different class from .sm-footer__links, so the rule above never reached
     these three. */
  .sm-footer__legal a { display: inline-block; padding: var(--space-4) 0; }
  /* The cookie banner's OK button is inline-styled in base.njk, which v1
     shares, so it takes !important for the same reason the banner itself does.
     It is the only control on screen for a first-time visitor on a phone. */
  #cb button {
    min-height: 44px !important;
    padding: var(--space-5) var(--space-12) !important;
  }

  /* Rule 2: the phone stays reachable on mobile. The label will not fit, so
     it becomes a 44px icon button that still dials. It must never be
     display:none, which is what this rule used to do on all 224 v2 routes
     while the live site kept the number visible. */
  .sm-nav__phone, .sm-header .sm-nav__phone {
    display: inline-grid;
    place-items: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
  }
  /* Hidden the way the sheet already hides text, not by shrinking it to 0px:
     the number stays in the accessibility tree and stops reporting as 0px
     text to the viewport gate. */
  .sm-nav__phone-text {
    position: absolute; width: 1px; height: 1px; padding: 0;
    margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
    white-space: nowrap; border: 0;
  }
  .sm-nav__phone::after,
  .sm-header .sm-nav__phone::after {
    content: "☎";
    font-size: 19px;
    line-height: 1;
  }
  .sm-menu-btn { display: flex; margin-left: auto; }

  .sm-mobile-menu {
    position: fixed;
    top: 72px;
    left: 0;
    right: 0;
    z-index: var(--z-overlay);
    display: none;
    padding: var(--space-8) var(--gutter-mobile) var(--space-14);
    background: var(--color-bg-deep);
    border-bottom: var(--rule-header);
    max-height: calc(100vh - 72px);
    overflow-y: auto;
  }
  .sm-mobile-menu--open { display: block; }

  .sm-footer__grid { grid-template-columns: 1fr; gap: var(--space-14); }
  /* Clears the sticky bar below. */
  .sm-footer { padding-bottom: 86px; }

  /* The cookie banner carries inline bottom:0 and z-index:9999 from base.njk,
     which v1 shares, so it cannot be overridden without !important and must
     not be changed at source: v1 is the live site. Both it and the sticky bar
     sat at bottom:0, and the banner won, covering 61px of the 73px bar on
     every v2 route until the visitor dismissed it. Stack them rather than
     fight over z-index, because hiding either one is worse than showing both. */
  #cb {
    bottom: calc(var(--sm-sticky-cta-h) + env(safe-area-inset-bottom, 0px)) !important;
    padding-bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px)) !important;
  }

  .sticky-mobile-cta {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: var(--z-sticky-cta);
    display: block;
    padding: var(--space-6) var(--gutter-mobile);
    /* Rule 18: iOS safe area. */
    padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
    background: var(--color-bg-deep);
    border-top: 1px solid var(--rsa-line-16);
  }

  .sticky-mobile-cta-inner {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-6);
  }

  .sticky-cta-call,
  .sticky-cta-book {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-5);
    min-height: 48px;
    padding: var(--pad-btn);
    border: 1px solid transparent;
    border-radius: var(--radius-xs);
    font-family: var(--font-display);
    font-size: var(--fs-label);
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-button);
    text-transform: uppercase;
    text-decoration: none;
    line-height: 1;
    white-space: nowrap;
  }

  /* Call is the ghost, Book is the one red action. */
  .sticky-cta-call {
    background: transparent;
    border-color: var(--rsa-line-25);
    color: var(--color-text);
  }
  .sticky-cta-book {
    background: var(--color-action);
    color: var(--color-on-action);
  }
  .sticky-cta-book:hover { background: var(--color-action-hover); }

  /* The inline SVG inherits the button's colour. */
  .sticky-cta-call svg, .sticky-cta-book svg { flex: 0 0 auto; }

  /* The bar sat over the Stripe card input while someone was typing in it, found
     on Tallaght. Any focused field hides it, and blur brings it back. */
  body:has(input:focus, select:focus, textarea:focus) .sticky-mobile-cta { display: none; }

  .sm-btn-row { width: 100%; }
  .sm-btn-row .sm-btn { flex: 1 1 auto; }

  .sm-hero { padding: var(--space-16) 0 var(--sm-space-64); }
  .sm-hero__grid { min-height: 0; }

  /* A slanted panel and a side-by-side frame both stop working at this width,
     so route and night collapse to the signpost shape. */
  .sm-hero--route .sm-hero__grid { grid-template-columns: 1fr; }
  .sm-hero--night .sm-hero__photo { left: 0; width: 100%; transform: none; }
  .sm-hero--night .sm-hero__photo img { transform: none; object-position: 50% 35%; }
  /* Hides the leading edge on a phone, where the hero is a single stacked
     column. It used to read .sm-hero--night::after, which by then was the
     hero's closing rule, so night towns lost that instead. */
  .sm-hero--night .sm-hero__photo::before { display: none; }
  .sm-hero__frame img { height: 220px; }

  /* One cell per row, and the divider rotates from the left edge to the top. */
  .sm-proof__cell {
    flex: 1 1 100%;
    border-left: 0;
    border-top: 1px solid var(--rsa-line-16);
    padding: var(--space-12) 0;
  }
  .sm-proof__cell:first-child { border-top: 0; }

  .sm-offset img { height: 300px; }
  .sm-offset::after { inset: var(--space-6) calc(var(--space-5) * -1) calc(var(--space-5) * -1) var(--space-6); }

  .sm-band { padding: var(--sm-space-64) 0; }

  .lead-form-section { padding: var(--sm-space-64) 0; }
  .lead-form-card { padding: var(--space-16); }
  .lead-form-grid { grid-template-columns: 1fr; }
  .lead-form-steps { gap: var(--space-4); }
  .lead-form-submit { width: 100%; }

  /* Clear of the sticky call bar, which owns the bottom of a phone screen. */
  .chat-nudge { right: var(--gutter-mobile); bottom: 86px; }
  .chat-overlay { right: var(--gutter-mobile); left: var(--gutter-mobile); bottom: 86px; width: auto; }
  .sm-junctions { margin-top: var(--space-12); }
  .sm-map { height: 300px; }
  .sm-faq__a { padding-right: 0; }
}

@media (max-width: 480px) {
  .sm-btn-row { display: grid; grid-template-columns: 1fr; }
  .sm-footer__bottom { flex-direction: column; }

  /* A tilted sign clips its own corners once the viewport is this narrow. */
  .sm-sign { transform: none; }
  .sm-sign__inner { min-width: 0; padding: 14px 24px; }
  .sm-sign__english { font-size: 28px; }
  .sm-sign__irish { font-size: 17px; }

  .sm-rating__value { font-size: var(--fs-num-xs); }
  .sm-stops { padding-left: var(--space-20); }
  .sm-stop__num { left: calc(var(--space-20) * -1); }
}

/* ---------- 23. Variant content shapes ---------- */

/* v3 towns price by transmission rather than by package, so the ticket grid
   carries two wide tickets instead of five narrow ones. The content differs,
   not just the layout: those pages say "Book Manual" where the others say
   nothing of the kind, and losing that is losing content. */
.sm-tickets--pair { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
/* On a v3 town this per-hour figure IS the headline price, because those pages
   carry no separate figures block. At the ticket's usual size the page was left
   with no display numeral at all. */
.sm-tickets--pair .sm-ticket__price { font-size: var(--fs-num-md); }
.sm-ticket__unit { font-size: 0.45em; font-weight: var(--fw-regular); opacity: 0.7; }
.sm-ticket ul { list-style: none; padding: 0; }
.sm-ticket li { padding: 2px 0; }
.sm-ticket__total { margin-top: var(--space-5); font-weight: var(--fw-bold); }
