/* ==========================================================================
   AMD Tyres, Brakes & Service
   One stylesheet. No framework, no build step, no preprocessor.
   Budget: this file plus nothing else. See docs/06-wordpress-build-spec.md.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Fonts
   Nata Sans, self-hosted, three weights. The demo shipped four weights of Nata
   and three of Poppins; Poppins was used by nothing and xbold by nothing the
   token scale can reach, so seven files became three. 49 KB, no Google Fonts
   request, no third-party DNS lookup.

   The fallback face exists to stop the swap moving anything. Nata Sans has a
   105% ascent and a 25% descent against Arial's 90.5% and 21.2%, so without
   the override every line box changes height the moment the real font lands.
   size-adjust is 101%: Nata's average lowercase advance is 0.537em against
   roughly 0.529em for the system faces this is standing in for.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Nata Sans";
  src: url("../fonts/nata-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Nata Sans";
  src: url("../fonts/nata-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Nata Sans";
  src: url("../fonts/nata-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Nata Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Roboto"), local("Segoe UI");
  ascent-override: 105%;
  descent-override: 25%;
  line-gap-override: 0%;
  size-adjust: 101%;
}

/* --------------------------------------------------------------------------
   1. Tokens
   Measured from the demo on 20/08/2026 and frozen. See docs/21-design-system.md.
   The type scale is FIXED, never fluid: clamp() on vw units is what produced
   16.96px and 30.08px on the Sportsafe site.
   -------------------------------------------------------------------------- */
:root {
  /* Colour. Contrast verified with tools/contrast.py, ratios noted. */
  --c-ink:        #1d2530;   /* on white 15.45:1 AAA */
  --c-body:       #627084;   /* on white  5.04:1 AA  */
  --c-action:     #ffa500;   /* BACKGROUND ONLY. White on it is 1.97:1 and fails.
                                Ink on it is 7.82:1 AAA. Never use as text. */
  --c-on-action:  #1d2530;   /* the only foreground allowed on --c-action */
  --c-accent-text:#a85800;   /* the brand orange, dark enough to BE text on a light background.
                                on white 5.17:1, on --c-hero 4.94:1, on --c-wash 4.74:1, all AA
                                for normal text. #ffa500 was used here and is 1.97:1 on white:
                                axe-core failed every one of the twenty pages on it, twice per
                                page, on the wordmark and on the accented word in the h1. The
                                rule above it said background only. The rule was right and
                                nothing enforced it. tools/verify-a11y.py enforces it now. */
  --c-white:      #ffffff;
  --c-wash:       #f5f5f5;
  --c-edge:       #e0e0e0;
  --c-focus:      #1d2530;

  /* The open and closed states. Two pairs, because the pill appears on white and on ink and
     one colour cannot clear AA on both. Ratios checked with tools/contrast.py, not eyeballed.
     These were hardcoded in eight places until 21/08/2026, which is exactly the drift a token
     system exists to stop. */
  --c-open:       #146c2e;   /* on white 6.53:1 AA  */
  --c-closed:     #b3261e;   /* on white 6.54:1 AA  */
  --c-open-dark:  #7ddc95;   /* on ink   9.23:1 AAA */
  --c-closed-dark:#ff8a80;   /* on ink   6.77:1 AA  */

  /* The pale fills behind the large PASS and FAIL on an MOT test, added 24/08/2026 with the
     history rewrite. Ink on a pale fill rather than white on a strong one: white on --c-open is
     under 3:1, and this is the largest and most load-bearing word on the entry. Measured, not
     eyeballed: --c-open on this is 5.69:1 and --c-closed on its own is 5.46:1, both AA for
     normal text and well past the 3:1 large text needs. */
  --c-open-wash:  #e4f3e8;
  --c-closed-wash:#fbe6e5;

  /* Type: eight fixed steps, whole numbers only. */
  --text-4xl: 48px;   /* the page opening at 900px and up, nothing else */
  --text-3xl: 40px;
  --text-2xl: 32px;
  --text-xl:  20px;
  --text-lg:  18px;
  --text-base:16px;
  --text-sm:  14px;
  --text-xs:  12px;

  --lh-tight:  1.2;
  --lh-normal: 1.5;
  --lh-loose:  1.75;

  --font: "Nata Sans", "Nata Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  --fw-normal: 400;
  --fw-medium: 500;
  --fw-bold:   700;

  /* Space: a 4px base, so nothing lands on a fractional pixel. */
  --s-1:  4px;
  --s-2:  8px;
  --s-3:  12px;
  --s-4:  16px;
  --s-5:  24px;
  --s-6:  32px;
  --s-7:  48px;
  --s-8:  64px;
  --s-9:  96px;

  --header-h: 64px;
  --topbar-h: 36px;

  /* The hero wash. The demo composited rgba(76,175,80,0.05) over white, measured on
     20/08/2026; this is the flat result, which saves the compositing. */
  --c-hero:      #f6fbf6;
  --c-on-ink:    #f5f5f5;   /* body copy on the dark bands, 13.6:1 on --c-ink */
  /* Borders inside the dark bands. #33404f was 1.46:1 on the ink and effectively invisible,
     which is a divider that divides nothing. This is the body-text colour, 3.07:1, which is
     the 3:1 that WCAG asks of a non-text component that carries meaning. */
  --c-edge-dark: #627084;
  --container-max: 1280px;
  --container-pad: var(--s-4);      /* 16px at 390px leaves 358px of content */
  --radius: 6px;
  --sticky-call-h: 60px;
}

@media (min-width: 768px) {
  :root { --container-pad: var(--s-6); }
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

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

body {
  font-family: var(--font);
  font-size: var(--text-base);
  font-weight: var(--fw-normal);
  line-height: var(--lh-normal);
  color: var(--c-body);
  background: var(--c-white);
  -webkit-font-smoothing: antialiased;
  /* Room for the sticky call bar so it never covers the last element. */
  padding-bottom: var(--sticky-call-h);
}

@media (min-width: 768px) {
  body { padding-bottom: 0; }
}

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

input, button, textarea, select { font: inherit; color: inherit; }

/* --------------------------------------------------------------------------
   3. Type
   -------------------------------------------------------------------------- */
h1, h2, h3, h4, h5, h6 {
  color: var(--c-ink);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  text-wrap: balance;
}

h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-lg); }
h5, h6 { font-size: var(--text-base); }

/* The accented word: "Tyres" in the wordmark, and one word in each h1.
   
   Two colours, because the same orange cannot do both jobs. On a light background it has to be
   --c-accent-text at 5.17:1. On --c-ink it can be the bright --c-action at 7.82:1, which is
   where the brand orange actually belongs.
   
   The default is the light one, because most of the places .accent appears (the header wordmark,
   every h1) are on white. The dark contexts opt back in below. It used to be the other way
   round: one rule set the bright orange everywhere and four selectors set it again, which did
   nothing at all, and the header failed on all twenty pages. */
.accent { color: var(--c-accent-text); }

.topbar .accent,
.trust-band .accent,
.cta-band .accent,
.site-footer .accent { color: var(--c-action); }

p { text-wrap: pretty; }
p + p { margin-top: var(--s-4); }

small, .text-sm { font-size: var(--text-sm); }
.text-xs { font-size: var(--text-xs); }
.lede { font-size: var(--text-lg); color: var(--c-body); }

a { color: var(--c-ink); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { text-decoration: none; }

/* Focus is visible and it is not negotiable. */
:focus-visible {
  outline: 3px solid var(--c-focus);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   4. Layout primitives
   -------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.section { padding-block: var(--s-7); }

@media (min-width: 768px) {
  .section { padding-block: var(--s-8); }
}

/* Two sections in a row were paying the padding twice, which put 192px of nothing
   between them on desktop. Measured in the screenshot, not guessed at.

   Only between two SECTIONS. A band paints its own background, so its padding is the
   band's own inset rather than rhythm between neighbours, and collapsing it put the
   heading flush against the top edge of the dark block with nothing above it. */
.section + .section { padding-top: 0; }

/* --------------------------------------------------------------------------
   A band: full bleed, its own background, its own inset.
   Never collapses against a neighbour, because the space inside it is part of
   the block rather than a gap between blocks.
   -------------------------------------------------------------------------- */
.band {
  padding-block: var(--s-7);
}

@media (min-width: 768px) {
  .band { padding-block: var(--s-8); }
}

/* A band sitting directly on another band shares one edge instead of two. */
.band + .band { padding-top: 0; }

.stack > * + * { margin-top: var(--s-4); }
.stack-lg > * + * { margin-top: var(--s-6); }

.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  left: var(--s-4);
  top: var(--s-4);
  z-index: 100;
  padding: var(--s-3) var(--s-4);
  background: var(--c-ink);
  color: var(--c-white);
}

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

/* --------------------------------------------------------------------------
   5. Actions
   The tap target is 48px minimum, because the customer is on a phone in a
   hurry. Orange is a background only.
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  /* The 48px comes from 12px padding, a 24px line box and 12px padding, measured. This
     min-height changes nothing today and is the floor for the day one of those three moves,
     or for a button with an icon and no text. Verified by setting it to 10px: the rendered
     height stayed at 48. */
  min-height: 48px;
  padding: var(--s-3) var(--s-5);
  border: 0;
  border-radius: var(--radius);
  font-size: var(--text-base);
  font-weight: var(--fw-bold);
  text-decoration: none;
  cursor: pointer;
}

.btn--call {
  background: var(--c-action);
  color: var(--c-on-action);   /* 7.82:1 */
}

.btn--ghost {
  background: transparent;
  color: var(--c-ink);
  border: 1px solid var(--c-edge);
}

/* --------------------------------------------------------------------------
   6. Sticky call bar
   84% of home service calls come from mobile click-to-call, so this is the
   single most important element on the site. It must never cover content:
   body carries matching bottom padding above.
   -------------------------------------------------------------------------- */
.sticky-call {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  height: var(--sticky-call-h);
  padding-inline: var(--s-4);
  background: var(--c-ink);
  border-top: 1px solid var(--c-ink);
}

.sticky-call .btn { flex: 1; }

@media (min-width: 768px) {
  .sticky-call { display: none; }
}

/* --------------------------------------------------------------------------
   7. Top bar
   The open-now state and the address. Both are silent when unknown, so this
   whole bar can be absent from the markup and nothing below it shifts.
   -------------------------------------------------------------------------- */
.topbar {
  background: var(--c-ink);
  color: var(--c-on-ink);
  font-size: var(--text-xs);
}

.topbar a { color: inherit; text-decoration: none; }
.topbar a:hover, .topbar a:focus-visible { text-decoration: underline; }
.topbar :focus-visible { outline-color: var(--c-white); }

.topbar__phone {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  font-weight: var(--fw-bold);
}

.topbar__phone .icon { width: 14px; height: 14px; }

.topbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: var(--topbar-h);
}

.topbar__address {
  display: none;
  align-items: center;
  gap: var(--s-1);
}

.topbar__address .icon { width: 14px; height: 14px; }

/* The address is the only other occupant and it is desktop-only, so while the hours are
   unknown this bar is an empty 32px band across the top of the most valuable screen on the
   site. Caught in the 390px screenshot, not in the markup. */
@media (max-width: 767px) {
  .topbar:not(:has(.open-now)) { display: none; }
}

@media (min-width: 768px) {
  .topbar__address { display: inline-flex; }
}

/* --------------------------------------------------------------------------
   8. Open now
   -------------------------------------------------------------------------- */
.open-now {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  font-size: var(--text-xs);
}

.open-now__label { font-weight: var(--fw-bold); }
.open-now__detail { color: var(--c-body); }

.open-now__label::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: var(--s-1);
  border-radius: 50%;
  background: currentColor;
}

.open-now__detail { color: inherit; opacity: 0.75; }

/* On the dark bar. Measured with tools/contrast.py rather than eyeballed: the open colour is
   9.23:1 on the ink and the closed one 6.77:1, so both clear AA comfortably at this size. */
.topbar .open-now[data-state="open"] .open-now__label { color: var(--c-open-dark); }
.topbar .open-now[data-state="closed"] .open-now__label { color: var(--c-closed-dark); }

/* On a light background, in the call to action band's light variant and anywhere else. */
.open-now[data-state="open"] .open-now__label { color: var(--c-open); }
.open-now[data-state="closed"] .open-now__label { color: var(--c-closed); }
.cta-band .open-now[data-state="open"] .open-now__label { color: var(--c-open-dark); }
.cta-band .open-now[data-state="closed"] .open-now__label { color: var(--c-closed-dark); }

/* --------------------------------------------------------------------------
   9. Header
   At 390px: the brand and the menu button. The phone number is deliberately
   not repeated here, because the sticky call bar already holds it on every
   screen and two competing call buttons in one viewport is a worse path than
   one. Above 768px the sticky bar is gone and the header call takes over.
   -------------------------------------------------------------------------- */
.site-header {
  background: var(--c-white);
  border-bottom: 1px solid var(--c-edge);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  min-height: var(--header-h);
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.brand {
  display: block;
  text-decoration: none;
  line-height: var(--lh-tight);
}

.brand__name {
  display: block;
  color: var(--c-ink);
  font-size: var(--text-lg);
  font-weight: var(--fw-bold);
}

.brand__place {
  display: block;
  color: var(--c-body);
  font-size: var(--text-xs);
  font-weight: var(--fw-medium);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* At 390px the header is the wordmark and the menu button, and nothing else. The phone number
   is already in the bar above and in the sticky bar at the bottom, so a third copy of it here
   buys nothing and costs the menu button its tap target. */
.site-header__cta { display: none; }

.site-header__actions { flex: 0 0 auto; }

@media (min-width: 560px) {
  .site-header__cta { display: inline-flex; }
}

@media (min-width: 768px) {
  .brand__name { font-size: var(--text-xl); }
}

/* --------------------------------------------------------------------------
   10. Navigation
   One list, rendered once. A bar on desktop, a panel on mobile.

   The panel is collapsed only when html carries data-js, which the inline
   snippet in the head sets before first paint. Without JavaScript the button
   never unhides and the list stays visible, so the site is still navigable.
   The footer carries the same links for the same reason.
   -------------------------------------------------------------------------- */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  /* flex-shrink is not optional here. Adding a second button to the header squeezed this one
     to 30px wide at 390px, which is a tap target failure the eye does not catch. */
  flex: 0 0 48px;
  width: 48px;
  height: 48px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
  color: var(--c-ink);
  cursor: pointer;
}

.nav-toggle__close { display: none; }
.site-header[data-nav-open="true"] .nav-toggle__open { display: none; }
.site-header[data-nav-open="true"] .nav-toggle__close { display: block; }

.site-nav__list,
.site-footer__list,
.site-footer__legal-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav__list a {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding-block: var(--s-2);
  color: var(--c-ink);
  font-size: var(--text-base);
  font-weight: var(--fw-medium);
  text-decoration: none;
}

.site-nav__list a:hover,
.site-nav__list a:focus-visible { text-decoration: underline; }

.site-nav__list .current-menu-item > a,
.site-nav__list .current-menu-parent > a,
.site-nav__list .current-menu-ancestor > a { font-weight: var(--fw-bold); }

/* Second level. Always visible: eleven top level items plus a handful of
   children is a short list, and a hover-only submenu is unreachable on the
   phone that most of this traffic arrives on. */
.site-nav__list .sub-menu {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--s-4);
}

.site-nav__list .sub-menu a {
  min-height: 44px;
  color: var(--c-body);
  font-size: var(--text-sm);
  font-weight: var(--fw-normal);
}

@media (max-width: 767px) {
  .site-nav {
    padding-inline: var(--container-pad);
    border-top: 1px solid var(--c-edge);
  }

  .site-nav__list > li + li { border-top: 1px solid var(--c-edge); }

  [data-js] .nav-toggle { display: inline-flex; }
  [data-js] .site-nav { display: none; }
  [data-js] .site-header[data-nav-open="true"] .site-nav { display: block; }
}

@media (min-width: 768px) {
  .site-nav {
    border-top: 1px solid var(--c-edge);
  }

  .site-nav__list {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--s-5);
    max-width: var(--container-max);
    margin-inline: auto;
    padding-inline: var(--container-pad);
  }

  .site-nav__list a {
    min-height: 44px;
    font-size: var(--text-sm);
  }

  .site-nav__list .sub-menu {
    display: none;   /* desktop children live on the pillar page, not in the bar */
  }
}

/* --------------------------------------------------------------------------
   11. Footer
   Carries the full link list on purpose: it is the no-JavaScript path to
   every page, and the second chance at the phone number.
   -------------------------------------------------------------------------- */
.site-footer {
  padding-block: var(--s-7);
  background: var(--c-ink);
  color: var(--c-on-ink);
  font-size: var(--text-sm);
}

.site-footer a { color: var(--c-on-ink); }
.site-footer :focus-visible { outline-color: var(--c-white); }

.site-footer__grid {
  display: grid;
  gap: var(--s-6);
}

/* Two columns from 560px, so a phone held sideways and a small tablet do not get one very
   long list. Four across only when there is genuinely room for four. */
@media (min-width: 560px) {
  .site-footer__grid { grid-template-columns: repeat(2, 1fr); gap: var(--s-6) var(--s-5); }
}

@media (min-width: 1000px) {
  .site-footer__grid { grid-template-columns: 1.4fr 1fr 1fr; gap: var(--s-7); }
}

.site-footer__brand {
  color: var(--c-white);
  font-size: var(--text-xl);
  font-weight: var(--fw-bold);
}

.site-footer__heading {
  color: var(--c-white);
  font-size: var(--text-base);
  margin-bottom: var(--s-3);
}

.site-footer__address {
  margin-block: var(--s-3);
  font-style: normal;
}

.site-footer__blurb { max-width: 40ch; }

.site-footer__contact a { font-weight: var(--fw-medium); }

.hours {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-1) var(--s-4);
  margin: 0;
}

.hours dt { color: var(--c-white); font-weight: var(--fw-medium); }
.hours dd { margin: 0; text-align: right; }

/* 40px rows so a thumb can hit them. Eleven underlined links stacked in a column reads as
   noise rather than as a list, so the underline is kept for hover and focus only, where it
   is doing work. */
.site-footer__list a,
.site-footer__legal-list a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  text-decoration: none;
}

.site-footer__list a:hover,
.site-footer__list a:focus-visible,
.site-footer__legal-list a:hover,
.site-footer__legal-list a:focus-visible {
  color: var(--c-white);
  text-decoration: underline;
}

/* On a mouse the 40px row is only whitespace: it made the services column 440px of thin
   list. Tap targets are a phone concern, so the rule is too. */
@media (min-width: 768px) {
  .site-footer__list a {
    min-height: 0;
    padding-block: var(--s-1);
  }
}

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid var(--c-edge-dark);
}

.site-footer__legal-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--s-4);
}

.notice-bar {
  padding-block: var(--s-2);
  background: var(--c-ink);
  color: var(--c-white);
  font-size: var(--text-sm);
  text-align: center;
}

/* --------------------------------------------------------------------------
   12. The page spine
   The H1, the opening line, the price, the call, then the answer. In that
   order because it is the order a stressed person on a phone needs it.

   It shares the hero's treatment on purpose: the same tint, the same type, the
   same actions row. A page without a photograph should still open the way the
   homepage opens, or the site reads as two sites.
   -------------------------------------------------------------------------- */
.spine {
  background: var(--c-hero);
  padding-block: var(--s-6) var(--s-7);
  max-width: none;
}

@media (min-width: 900px) {
  .spine { padding-block: var(--s-8); }
}

.spine__inner {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.spine__title {
  font-size: var(--text-3xl);
  text-wrap: balance;
}

.spine__lede {
  margin-top: var(--s-3);
  max-width: 52ch;
  font-size: var(--text-lg);
}

.spine__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-5);
}

.price-from {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
}

.price-from__label {
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.price-from__value {
  color: var(--c-ink);
  font-size: var(--text-2xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
}

/* The passage a search engine or an assistant quotes. Set slightly wider than
   body copy and capped at a comfortable measure. */
.answer {
  max-width: 62ch;
  margin-top: var(--s-5);
  font-size: var(--text-lg);
}

/* The trail back up, on a child page only. Sits above the opening block and is deliberately
   quiet: it is a way back, not a heading. */
.breadcrumbs {
  padding-block: var(--s-3);
  border-bottom: 1px solid var(--c-edge);
  font-size: var(--text-sm);
}

.breadcrumbs__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  list-style: none;
  margin: 0;
  padding: 0;
  color: var(--c-body);
}

.breadcrumbs__list li + li::before {
  content: "/";
  margin-right: var(--s-2);
  color: var(--c-edge-strong, var(--c-body));
}

/* 24px is WCAG 2.5.8, and these are standalone controls rather than links inside a sentence,
   so the inline exception does not apply to them. At 14px the line box is 19px, which the
   wider gate walk caught the moment it started covering the two child pages. */
.breadcrumbs__list a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  color: var(--c-body);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.breadcrumbs__list a:hover,
.breadcrumbs__list a:focus-visible { color: var(--c-ink); }

.breadcrumbs__list [aria-current="page"] { color: var(--c-ink); }

/* The list on the 404. A grid of tap targets rather than a column of links: the person is
   already lost and the next thing they do should be one thumb movement away. */
.not-found__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--s-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.not-found__list a {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
  color: var(--c-ink);
  font-weight: var(--fw-medium);
  text-decoration: none;
}

.not-found__list a:hover,
.not-found__list a:focus-visible {
  border-color: var(--c-ink);
  background: var(--c-wash);
}

/* --------------------------------------------------------------------------
   The registration box
   Opens the free car check page in place of a photograph. Same band as the
   hero, same type scale, so the site does not read as two sites.
   -------------------------------------------------------------------------- */
.reg-check {
  padding-block: var(--s-7);
  background: var(--c-hero);
}

.reg-check__inner {
  display: grid;
  gap: var(--s-6);
}

.reg-check__title {
  font-size: var(--text-3xl);
  text-wrap: balance;
}

.reg-check__lede {
  margin-top: var(--s-3);
  max-width: 46ch;
  font-size: var(--text-lg);
}

.reg-check__form {
  padding: var(--s-5);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
  background: var(--c-white);
}

.reg-check__heading {
  margin-bottom: var(--s-4);
  font-size: var(--text-lg);
}

.reg-check__actions { margin-top: var(--s-5); }

/* Stacked on a phone, side by side once there is room. Measured at 390px: with the button
   beside it the plate had 9 characters of space for 8 characters of registration and the
   placeholder was clipped mid word, which reads as a broken box rather than a narrow one. */
.reg-check__row {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

@media (min-width: 480px) {
  .reg-check__row { flex-direction: row; flex-wrap: wrap; }
  .reg-check__row .plate { flex: 1 1 15ch; }
  .reg-check__row .reg-check__go { flex: 1 1 12ch; }
}

/* The plate itself. A yellow rectangle with black characters is the most recognisable
   "put your registration here" signal there is in this country, and people who would
   scroll past a text input will fill one in. */
.plate {
  display: flex;
  /* No grow while the row is a column: flex-grow applies to the main axis, so "1 1 15ch"
     stretched the plate down the page to 120px tall instead of across it. It grows only once
     the media query below turns the row back into a row. */
  flex: 0 0 auto;
  align-items: stretch;
  overflow: hidden;
  min-height: 56px;
  border: 2px solid var(--c-ink);
  border-radius: var(--radius);
  background: #ffd400;
}

.plate__flash {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding-inline: var(--s-2);
  background: #063298;
  color: #fff;
}

.plate__stars { display: block; line-height: 0; }

.plate__country {
  font-size: var(--text-xs);
  font-weight: var(--fw-bold);
  line-height: 1;
  letter-spacing: 0.06em;
}

.plate__input {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  padding: var(--s-2) var(--s-3);
  border: 0;
  background: transparent;
  color: #111;
  font-family: inherit;
  /* On the scale at both ends, and clamped between them: a full registration is 8 characters
     including the space, and at 32px they do not fit a 390px screen, so a fixed size either
     clips the plate on a phone or makes it look like a text box on a desktop. The tokens are
     what the check enforces everywhere else and there is no reason this is special. */
  font-size: clamp(var(--text-xl), 5.4vw, var(--text-2xl));
  font-weight: var(--fw-bold);
  letter-spacing: 0.06em;
  text-align: center;
  text-transform: uppercase;
}

.plate__input::placeholder { color: #8a7400; }
.plate__input:focus { outline: none; }
.plate:focus-within { outline: 3px solid var(--c-focus); outline-offset: 2px; }

.reg-check__go {
  flex: 0 0 auto;
  justify-content: center;
  min-height: 56px;
}

.reg-check__note {
  margin-top: var(--s-3);
  color: var(--c-body);
  font-size: var(--text-sm);
}

@media (min-width: 900px) {
  .reg-check { padding-block: var(--s-8); }

  .reg-check__inner {
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: var(--s-8);
  }

  .reg-check__title { font-size: var(--text-4xl); }
}

/* How long the job takes, under the hero's tick list. A line rather than a fact tile: the
   real strings run to "Depends on the repair. We will tell you once we have seen it", which
   is a sentence and not a number. */
.hero__duration {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-4);
  color: var(--c-ink-soft);
  font-size: var(--text-sm);
}

.hero__duration .icon {
  flex: 0 0 auto;
  width: 1.1em;
  height: 1.1em;
  color: var(--c-brand);
}

/* Under a hero the answer is its own block, and the block already carries the section
   padding. The spine's own top margin on top of that would pay for the gap twice. */
.answer-block .answer { margin-top: 0; }

/* The price sits with the actions on the opening block, not above them. */
.spine .price-from { margin-top: var(--s-4); }

.answer p + p,
.prose p + p { margin-top: var(--s-4); }

/* The cap goes on the children, never on the container. Put on .container itself it
   fights margin-inline:auto and centres the narrow block in the viewport, so the
   questions started 380px in while everything above them started at 80px. */
.prose > * { max-width: 68ch; }

.prose h2 { margin-top: var(--s-6); margin-bottom: var(--s-3); }
.prose h3 { margin-top: var(--s-5); margin-bottom: var(--s-2); }
.prose ul, .prose ol { margin: var(--s-4) 0; padding-left: var(--s-5); }
.prose li + li { margin-top: var(--s-2); }

.section-head { max-width: 62ch; }
.section-head + * { margin-top: var(--s-6); }

/* Shown to signed-in editors only. Never rendered for a visitor. */
.editor-warning {
  padding: var(--s-3) var(--s-4);
  border-left: 4px solid var(--c-closed);
  background: var(--c-wash);
  font-size: var(--text-sm);
}

/* --------------------------------------------------------------------------
   13. Section layouts
   -------------------------------------------------------------------------- */

/* Price tables. A real table, because it is tabular data. Wide content scrolls
   inside its own container so the page body never scrolls sideways. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.prices {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}

.prices th,
.prices td {
  padding: var(--s-3) var(--s-2);
  border-bottom: 1px solid var(--c-edge);
  vertical-align: baseline;
}

.prices thead th {
  color: var(--c-body);
  font-size: var(--text-xs);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.prices tbody th {
  color: var(--c-ink);
  font-weight: var(--fw-medium);
}

.prices__price { text-align: right; white-space: nowrap; }
.prices__price strong { color: var(--c-ink); font-size: var(--text-lg); }

.prices__note,
.prices__unit {
  display: block;
  color: var(--c-body);
  font-size: var(--text-xs);
  font-weight: var(--fw-normal);
}

.prices__updated { color: var(--c-body); }

/* Cards */
.cards {
  display: grid;
  gap: var(--s-4);
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (min-width: 560px) {
  .cards { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 900px) {
  .cards { grid-template-columns: repeat(3, 1fr); }
}

.card {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-4);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
  background: var(--c-white);
}

.card__title { font-size: var(--text-lg); }

/* The whole card is the target, not the four words in the heading. */
.card__title a {
  text-decoration: none;
}

.card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.card { position: relative; }

.card__title a:hover,
.card__title a:focus-visible { text-decoration: underline; }

.card__text { color: var(--c-body); font-size: var(--text-sm); }

.card__icon {
  width: 28px;
  height: 28px;
  color: var(--c-ink);
}

.card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-top: auto;
  padding-top: var(--s-2);
}

.card__price { color: var(--c-ink); font-weight: var(--fw-bold); }

.card__arrow {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  padding: 7px;
  margin-left: auto;
  background: var(--c-action);
  color: var(--c-on-action);
  border-radius: var(--radius);
}

.card:hover { border-color: var(--c-ink); }

/* Text and image. The text comes first on a phone whatever side is chosen:
   the side is a desktop decision, the order is a reading decision. */
.text-media {
  display: grid;
  gap: var(--s-5);
  align-items: center;
}

@media (min-width: 768px) {
  .text-media { grid-template-columns: 1fr 1fr; gap: var(--s-7); }
  .text-media--left .text-media__text { order: 2; }
}

.text-media__img,
.gallery__img {
  width: 100%;
  border-radius: var(--radius);
}

/* Steps. An ordered list, so the count is the browser's and a screen reader
   announces it. Numbers drawn with CSS counters on unordered items say
   nothing to anyone who cannot see them. */
.steps {
  display: grid;
  gap: var(--s-4);
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: amd-step;
}

@media (min-width: 768px) {
  .steps { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
}

.steps__item {
  counter-increment: amd-step;
  padding-top: var(--s-4);
  border-top: 3px solid var(--c-action);
}

.steps__item::before {
  content: counter(amd-step);
  display: block;
  margin-bottom: var(--s-2);
  color: var(--c-body);
  font-size: var(--text-sm);
  font-weight: var(--fw-bold);
}

.steps__title { font-size: var(--text-lg); margin-bottom: var(--s-2); }

/* Reviews */
.reviews {
  display: grid;
  gap: var(--s-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 768px) {
  .reviews { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
}

.review {
  padding: var(--s-4);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
}

.review__text { margin: 0 0 var(--s-3); }
.review__by { color: var(--c-body); }

/* --------------------------------------------------------------------------
   The scroller
   A row that runs off the edge of a phone and is dragged sideways.

   Zero JavaScript. The demo did this with Swiper 8.4.4, which is 39 KB of
   script plus 2 KB of CSS for a strip of logos, against a whole-site budget of
   15 KB. overflow-x with scroll-snap is native: it has momentum on iOS, it
   works with the plugin off, with JavaScript off and with a keyboard, and it
   costs nothing.

   scroll-padding matches the container inset so a snapped item lines up with
   everything else on the page rather than with the raw viewport edge.
   -------------------------------------------------------------------------- */
.scroller {
  display: flex;
  gap: var(--s-4);
  margin: 0;
  padding: 0 var(--container-pad) var(--s-2);
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-padding-inline: var(--container-pad);
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}

.scroller > * {
  flex: 0 0 auto;
  scroll-snap-align: start;
}

/* The strip runs to the screen edge on a phone, which is the affordance that says there is
   more to the right. It is pulled out of the container to do that, and put back at the width
   where the whole row fits without scrolling. */
.scroller-bleed {
  margin-inline: calc(var(--container-pad) * -1);
}

/* A thin, unobtrusive scrollbar rather than none at all: hiding it removes the only cue a
   mouse user gets that the row scrolls. */
.scroller { scrollbar-width: thin; }
.scroller::-webkit-scrollbar { height: 6px; }
.scroller::-webkit-scrollbar-thumb {
  background: var(--c-edge);
  border-radius: 3px;
}

/* Brand logos */
.logos {
  align-items: center;
}

.logos__item {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 128px;
  height: 64px;
  padding: var(--s-3);
  background: var(--c-white);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
}

.logos__img {
  max-width: 100%;
  max-height: 40px;
  width: auto;
  object-fit: contain;
}

@media (min-width: 900px) {
  /* Everything fits, so it stops being a scroller and becomes a row. */
  .logos {
    flex-wrap: wrap;
    justify-content: center;
    overflow-x: visible;
    padding-inline: 0;
  }
  .logos.scroller-bleed { margin-inline: 0; }
}

/* Galleries stay a grid: they are looked at, not skimmed. */
.gallery {
  display: grid;
  gap: var(--s-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.gallery { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }

@media (min-width: 768px) {
  .gallery { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
}

/* The call to action band */
.cta-band {
  background: var(--c-ink);
  color: var(--c-on-ink);
}

/* Centred, and capped at a readable measure.
   
   Heading left with the button pushed to the far right was tried first and measured: with a
   short heading it left about 700px of empty band between the two on a 1440px screen, which
   reads as a broken layout rather than a spacious one. Centring is what the demo did, and a
   full bleed band is the one place on this site where it earns its keep: the band is a stop,
   not something to scan down.

   The open-now state sits directly under the button it qualifies. It used to float below
   everything as a fourth item in a loose stack, which said nothing about what it referred to. */
.cta-band__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-5);
  text-align: center;
}

.cta-band__words { max-width: 46ch; }

.cta-band__heading {
  color: var(--c-white);
  font-size: var(--text-2xl);
  text-wrap: balance;
}

.cta-band__text { margin-top: var(--s-3); }
.cta-band__text p + p { margin-top: var(--s-3); }

/* Centred ragged text needs balancing rather than prettifying: pretty only protects the last
   line, balance evens all of them, which is what a centred block wants. */
.cta-band__heading,
.cta-band__text p { text-wrap: balance; }

.cta-band__side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
}

.cta-band__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-3);
}

/* The call button carries the weight here, so on a narrow phone it takes the full width
   rather than shrinking to fit its own text in the middle of a wide dark band. */
.cta-band__actions .btn { flex: 1 1 auto; }

@media (min-width: 480px) {
  .cta-band__actions .btn { flex: 0 0 auto; }
}

.btn--on-dark {
  background: transparent;
  color: var(--c-white);
  border: 1px solid rgba(255, 255, 255, 0.4);
}

/* Focus has to stay visible on the dark band, where an ink outline disappears. */
.cta-band :focus-visible { outline-color: var(--c-white); }

/* --------------------------------------------------------------------------
   14. Service facts, related work, definition lists
   -------------------------------------------------------------------------- */
.service-facts__duration {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--c-ink);
  font-weight: var(--fw-medium);
}

.ticks {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ticks li {
  position: relative;
  padding-left: var(--s-5);
}

.ticks li + li { margin-top: var(--s-2); }

.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 10px;
  height: 6px;
  border-left: 2px solid var(--c-ink);
  border-bottom: 2px solid var(--c-ink);
  transform: rotate(-45deg);
}

.facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-2) var(--s-5);
  margin: 0;
}

.facts dt { color: var(--c-ink); font-weight: var(--fw-medium); }
.facts dd { margin: 0; }

.related__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.related__list a {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
  text-decoration: none;
}

.related__list a:hover,
.related__list a:focus-visible { border-color: var(--c-ink); }

.related__price { color: var(--c-body); font-size: var(--text-sm); }

.post-meta { color: var(--c-body); }

/* --------------------------------------------------------------------------
   15. Questions
   details and summary, so it is a real disclosure with no JavaScript:
   keyboard operable, and announced as expanded or collapsed.
   -------------------------------------------------------------------------- */
.faq > h2,
.faq__list {
  max-width: 74ch;
}

.faq__list { border-top: 1px solid var(--c-edge); }

.faq__item { border-bottom: 1px solid var(--c-edge); }

.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 56px;
  padding-block: var(--s-3);
  color: var(--c-ink);
  font-weight: var(--fw-medium);
  cursor: pointer;
  list-style: none;
}

.faq__q::-webkit-details-marker { display: none; }

.faq__q::after {
  content: "";
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-3px) rotate(45deg);
}

.faq__item[open] .faq__q::after { transform: translateY(2px) rotate(-135deg); }

.faq__a { padding-bottom: var(--s-4); }
.faq__a > :first-child { margin-top: 0; }

/* --------------------------------------------------------------------------
   16. Reduced motion
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   17. The hero
   Composition from the Bricks demo, which the owner liked. Two columns on
   desktop, words then photograph on a phone.

   The DOM order is mobile order: heading, opening line, call button, ticks,
   facts, photograph. The demo put its buttons under the tick list, which on a
   390px screen put the phone number below the fold. Desktop restores the
   original reading order with a grid, which costs nothing and keeps the markup
   in the order a phone should read it.
   -------------------------------------------------------------------------- */
.hero {
  background: var(--c-hero);
  padding-block: var(--s-6) var(--s-7);
}

.hero__inner {
  display: grid;
  gap: var(--s-5);
}

.hero__title {
  font-size: var(--text-3xl);
  text-wrap: balance;
}

.hero__lede {
  margin-top: var(--s-3);
  max-width: 46ch;
  font-size: var(--text-lg);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-5);
}

.btn--dark {
  background: var(--c-ink);
  color: var(--c-white);
}

.hero__ticks {
  margin-top: var(--s-5);
  padding: 0;
  list-style: none;
}

.hero__ticks li {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  font-weight: var(--fw-medium);
}

.hero__ticks li + li { margin-top: var(--s-2); }

.hero__tick-icon {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  /* A tick that says "this is included" is a meaningful graphic, so WCAG 1.4.11 wants 3:1
     against the hero behind it. The bright orange was 1.89:1 and effectively invisible to
     anyone who needs the contrast. */
  color: var(--c-accent-text);
}

/* The facts row. Three across even at 390px: the values are short, and stacking
   them turns a glance into a scroll. */
.hero__facts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-3);
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid var(--c-edge);
}

.hero__fact-value {
  color: var(--c-ink);
  font-size: var(--text-xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
}

.hero__fact-label {
  margin: var(--s-1) 0 0;
  font-size: var(--text-xs);
  line-height: 1.35;
}

.hero__media { position: relative; }

.hero__img {
  width: 100%;
  border-radius: var(--radius);
}

.hero__badge {
  position: absolute;
  left: 0;
  bottom: var(--s-4);
  margin: 0;
  padding: var(--s-2) var(--s-4);
  background: var(--c-action);
  color: var(--c-on-action);
  font-size: var(--text-sm);
  font-weight: var(--fw-bold);
  border-radius: 0 var(--radius) var(--radius) 0;
}

@media (min-width: 900px) {
  .hero { padding-block: var(--s-8); }

  .hero__inner {
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: var(--s-8);
  }

  /* A hero with no photograph is one column, not one column and an empty half. The recovery
     page has no usable photograph of its own truck yet, and an editor can leave the image
     blank on any of them. */
  .hero__inner--no-media {
    grid-template-columns: minmax(0, 62ch);
  }

  .hero__title,
  .spine__title { font-size: var(--text-4xl); }
  .hero__facts { margin-top: var(--s-7); }
}

/* --------------------------------------------------------------------------
   18. The trust band
   -------------------------------------------------------------------------- */
.trust-band {
  background: var(--c-ink);
  color: var(--c-on-ink);
}

.trust-band .section-head h2 { color: var(--c-white); }

.trust-band__list {
  display: grid;
  gap: var(--s-6);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 768px) {
  .trust-band__list { grid-template-columns: repeat(3, 1fr); gap: var(--s-7); }
  .trust { text-align: center; }
  .trust__icon { margin-inline: auto; }
}

.trust__icon {
  display: block;
  width: 32px;
  height: 32px;
  margin-bottom: var(--s-3);
  color: var(--c-action);
}

.trust__title {
  color: var(--c-white);
  font-size: var(--text-xl);
  margin-bottom: var(--s-2);
}

.trust__text { max-width: 40ch; margin-inline: auto; }

/* --------------------------------------------------------------------------
   What we do for free

   A light band rather than a dark one, deliberately. It sits directly under the dark trust
   band on the homepage and two dark bands in a row read as one long slab, which buries both.

   The count is not fixed at three the way the trust band is, because it comes out of the price
   list and the owner can add to it. auto-fit rather than a column count, so five items lay out
   as three and two instead of overflowing.
   -------------------------------------------------------------------------- */

.free-band {
  background: var(--c-wash);
  color: var(--c-ink);
}

.free-band__list {
  display: grid;
  gap: var(--s-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Two then three, capped. auto-fit was tried first and gave four columns at 1280px, which laid
   the five items out as four and an orphan. Three across reads as three and two. */
@media (min-width: 640px) {
  .free-band__list { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .free-band__list { grid-template-columns: repeat(3, 1fr); }
}

.free {
  background: var(--c-white);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
  padding: var(--s-5);
}

/* Ink on the brand orange, never white: white on this orange is 1.97:1 and fails outright. */
.free__flag {
  display: inline-block;
  margin: 0 0 var(--s-3);
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--c-action);
  color: var(--c-on-action);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
}

.free__title {
  font-size: var(--text-lg);
  margin: 0 0 var(--s-2);
  color: var(--c-ink);
}

.free__text {
  margin: 0;
  color: var(--c-body);
  font-size: var(--text-sm);
}

/* --------------------------------------------------------------------------
   The Google review badge

   A half star is the outline with a filled copy clipped over it at 50%, so 4.8 renders as four
   and a bit. Rounding to five would be a nicer looking badge and an overstatement.

   The star colour is the brand orange, which is background-only against white for text but is
   fine as an icon: the accessible content here is the sentence beside it, not the stars, which
   are aria-hidden.
   -------------------------------------------------------------------------- */

.review-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-4);
  padding: var(--s-2) var(--s-4) var(--s-2) var(--s-3);
  border: 1px solid var(--c-edge);
  border-radius: 999px;
  background: var(--c-white);
  color: var(--c-ink);
  text-decoration: none;
}

.review-badge:hover { border-color: var(--c-ink); }

.review-badge__stars { display: inline-flex; gap: 1px; }

.review-badge__star {
  position: relative;
  display: block;
  width: 18px;
  height: 18px;
  color: var(--c-edge);
}

.review-badge__star .icon { display: block; width: 18px; height: 18px; }

.review-badge__fill {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

/* The icon file is a stroked outline with fill="none" on the element. A presentation attribute
   loses to a stylesheet, so this is what makes the filled copy solid.

   Bright fill, dark edge. A star has to look like a gold star or it is not doing its job, and
   #ffa500 on white is 1.97:1, so the shape on its own would be invisible to the people the rule
   is for. The outline carries the contrast at 5.17:1 and the fill carries the recognition. */
.review-badge__fill .icon { fill: var(--c-action); stroke: var(--c-accent-text); }

.review-badge__text {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  font-size: var(--text-sm);
  white-space: nowrap;
}

.review-badge__score { font-size: var(--text-lg); }
.review-badge__count { color: var(--c-body); }

@media (max-width: 400px) {
  .review-badge__text { white-space: normal; }
}

/* --------------------------------------------------------------------------
   19. Fixture banner
   Signed-in editors only, and never rendered for a visitor. Development
   copy exists so the templates can be measured; it must never be mistaken
   for the client's words.
   -------------------------------------------------------------------------- */
.fixture-banner {
  padding: var(--s-2) var(--s-4);
  background: var(--c-closed);
  color: var(--c-white);
  font-size: var(--text-sm);
  font-weight: var(--fw-bold);
  text-align: center;
}

/* The editors' banner and the owner's read-only one. Same strip, different reader:
   one says the copy has not been approved, the other says the prices are not his. */
.signoff-banner,
.review-banner {
  padding: var(--s-2) var(--s-4);
  background: var(--c-ink);
  color: var(--c-white);
  font-size: var(--text-sm);
  text-align: center;
}

/* --------------------------------------------------------------------------
   20. The price strip
   The one thing on the homepage a competitor cannot copy by editing their own,
   because almost nobody in Colchester publishes a tyre price at all.
   -------------------------------------------------------------------------- */
.price-chip {
  min-width: 148px;
}

.price-chip__link {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  height: 100%;
  min-height: 88px;
  padding: var(--s-3) var(--s-4);
  background: var(--c-white);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
  text-decoration: none;
}

.price-chip__link:hover,
.price-chip__link:focus-visible { border-color: var(--c-ink); }

.price-chip__label {
  color: var(--c-ink);
  font-weight: var(--fw-bold);
}

.price-chip__value {
  color: var(--c-ink);
  font-size: var(--text-lg);
  font-weight: var(--fw-bold);
}

.price-chip__note {
  color: var(--c-body);
  font-size: var(--text-xs);
}

/* --------------------------------------------------------------------------
   21. The marquee
   A scroller that drifts on its own. Built on top of .scroller, so with the
   animation off, or stopped, it is still a row you can drag and tab to.

   No JavaScript: the demo used Swiper 8.4.4, 39 KB, for exactly this.
   -------------------------------------------------------------------------- */
.marquee {
  position: relative;
  overflow: hidden;
}

/* The row is duplicated in the markup, so translating half its width lands the copy exactly
   where the original started and the loop has no seam. */
@keyframes amd-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.marquee--auto .marquee__track {
  width: max-content;
  animation: amd-marquee var(--marquee-duration, 40s) linear infinite;

  /* Dragging it by hand while it animates fights the transform, so the manual scroll is
     turned off while it drives itself. Hovering or tabbing to it stops the animation and
     hands scrolling back. */
  overflow-x: hidden;
}

/* WCAG 2.2.2: moving content that starts on its own and runs for more than five seconds needs
   a way to stop it. Hover covers a pointer, focus-within covers a keyboard. */
.marquee--auto:hover .marquee__track,
.marquee--auto:focus-within .marquee__track {
  animation-play-state: paused;
  overflow-x: auto;
}

/* Faded edges, so logos arrive and leave rather than being chopped off at the container. */
@supports (mask-image: linear-gradient(90deg, #000, #000)) {
  .marquee--auto {
    mask-image: linear-gradient(90deg, transparent, #000 var(--s-6), #000 calc(100% - var(--s-6)), transparent);
  }
}

/* Movement nobody asked for is the exact thing this setting exists to switch off. The row
   stays, and stays draggable. */
@media (prefers-reduced-motion: reduce) {
  .marquee--auto .marquee__track {
    animation: none;
    width: auto;
    overflow-x: auto;
  }
  .marquee--auto { mask-image: none; }
}

.brand-logos .marquee { padding-block: var(--s-2); }

/* --------------------------------------------------------------------------
   22. Where to find us
   The map is a picture that is a link, not an embed. An embed is about a
   megabyte of third party script and it sets cookies, which would make the
   cookie policy on this site untrue.
   -------------------------------------------------------------------------- */
.map-section__inner {
  display: grid;
  gap: var(--s-5);
}

@media (min-width: 768px) {
  .map-section__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    align-items: start;
    gap: var(--s-7);
  }
}

.map-section__address {
  font-style: normal;
  line-height: var(--lh-loose);
}

.map-section__address strong { color: var(--c-ink); }

.map-section__map { margin: 0; }

/* The direct child only. This was `.map-section__map a`, which also matched the OpenStreetMap
   attribution link inside the figcaption below the picture and turned a link in a sentence into
   a 358px wide block. Three things went wrong at once: the whole line became clickable when only
   two words should be, the pointer changed anywhere along it, and a target that WCAG 2.5.8
   exempts because it is inline in text stopped being inline and became an 18px tall block that
   the rule then applies to. Found by tools/verify-a11y.py. */
.map-section__map > a {
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
}

.map-section__map > a:hover,
.map-section__map > a:focus-visible { outline: 3px solid var(--c-focus); outline-offset: 2px; }

.map-section__img {
  width: 100%;
  /* The picture is 2:1. Cropping it shorter on a phone keeps the pin and the station in frame
     without the map taking half the screen. */
  aspect-ratio: 3 / 2;
  object-fit: cover;
  object-position: center;
}

@media (min-width: 768px) {
  .map-section__img { aspect-ratio: 2 / 1; }
}

.map-section__credit {
  margin-top: var(--s-2);
  color: var(--c-body);
}

/* Emphasis inside a fielded paragraph. --c-action is a background colour on white, so a
   coloured phrase in body copy uses the ink and its weight instead: at 4.5:1 the orange would
   fail outright. Checked with tools/contrast.py. */
.accent-text {
  color: var(--c-ink);
  font-weight: var(--fw-bold);
}

/* --------------------------------------------------------------------------
   23. The enquiry form
   Second choice, always. Phone converts at 28% against 2 to 3% for a form in
   this trade, so every page offers the number first and this is for the people
   who will not ring.
   -------------------------------------------------------------------------- */
.enquiry { max-width: 46rem; }

.enquiry__form { margin: 0; }

.field { margin: 0 0 var(--s-4); }

.field label {
  display: block;
  margin-bottom: var(--s-1);
  color: var(--c-ink);
  font-weight: var(--fw-medium);
}

.field__optional {
  color: var(--c-body);
  font-size: var(--text-sm);
  font-weight: var(--fw-normal);
}

.field__help {
  display: block;
  margin-bottom: var(--s-2);
  color: var(--c-body);
  font-size: var(--text-sm);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  /* 48px, so a thumb can hit it, and 16px text so iOS does not zoom the page in
     when the field takes focus. Anything smaller costs the visitor their place
     on the form. */
  min-height: 48px;
  padding: var(--s-3);
  font-size: var(--text-base);
  background: var(--c-white);
  border: 1px solid var(--c-body);
  border-radius: var(--radius);
}

.field textarea { min-height: 96px; resize: vertical; }

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible { outline: 3px solid var(--c-focus); outline-offset: 1px; }

/* The invalid state is a colour AND a border weight AND the word marked in the
   message above. Colour alone is not a signal for everyone. */
.field--invalid input,
.field--invalid select,
.field--invalid textarea {
  border-width: 2px;
  border-color: var(--c-closed);
}

.field--invalid label { color: var(--c-closed); }

/* Off screen rather than display:none: a hidden field some bots skip, an off
   screen one they fill in. Never announced, never focusable. */
.enquiry__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.enquiry__submit { margin: var(--s-5) 0 0; }
.enquiry__submit .btn { width: 100%; }

@media (min-width: 480px) {
  .enquiry__submit .btn { width: auto; min-width: 12rem; }
}

.enquiry__note { margin-top: var(--s-3); color: var(--c-body); }

.notice-success,
.notice-error,
.notice-info {
  padding: var(--s-3) var(--s-4);
  border-left: 4px solid;
  border-radius: var(--radius);
}

.notice-success { border-color: var(--c-open); background: var(--c-wash); }
.notice-error { border-color: var(--c-closed); background: var(--c-wash); }
.notice-info { border-color: var(--c-accent-text); background: var(--c-wash); }

.site-footer__legal-detail {
  display: block;
  margin-top: var(--s-1);
  color: var(--c-on-ink);
  opacity: 0.75;
}

/* --------------------------------------------------------------------------
   24. Tap targets
   WCAG 2.5.8 asks for 24 by 24 CSS pixels on a pointer target. The exception
   is a link sitting inline inside a sentence, whose size is constrained by the
   line height of the text around it.

   These four are NOT that. They are standalone controls that happened to be
   sized by their text, and every one of them is a way to ring the workshop.
   Measured at 390px: the top bar number was 18px tall, the footer number and
   the social links 19px, and "Ring for a price" in the price tables 21px.
   -------------------------------------------------------------------------- */
.topbar__phone {
  min-height: 24px;
  padding-block: var(--s-1);
}

.site-footer__contact a,
.site-footer__social a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
}

.site-footer__social {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--s-4);
  margin: var(--s-2) 0 0;
  padding: 0;
  list-style: none;
}

/* The only action in that cell, and it is a phone call. It gets a real target. */
.prices__price a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding-inline: var(--s-2);
}

@media (min-width: 768px) {
  .site-footer__social { gap: 0 var(--s-5); }
}

/* --------------------------------------------------------------------------
   The MOT result
   Under the registration box on the free car check page. Reads top to bottom
   on a phone: the car, when its MOT runs out, what it was advised, the price.
   Tokens only. No new colours: the pass and fail states reuse the open and
   closed pair the header pill already uses, which are the two contrast-checked
   greens and reds this site has.
   -------------------------------------------------------------------------- */
.mot {
  padding-block: var(--s-7);
  scroll-margin-top: calc(var(--header-h) + var(--s-4));
}

.mot__head { margin-bottom: var(--s-5); }

/* The registration, set as a plate would read it. Not a plate graphic: one of those on the
   page is the input, and a second competes with it for the eye. */
.mot__reg {
  display: inline-block;
  padding: var(--s-1) var(--s-3);
  border: 2px solid var(--c-ink);
  border-radius: var(--radius);
  background: #ffd400;
  color: #111;
  font-size: var(--text-lg);
  font-weight: var(--fw-bold);
  letter-spacing: 0.06em;
}

.mot__title { margin-top: var(--s-3); font-size: var(--text-2xl); }
.mot__spec { color: var(--c-body); }
.mot__sub { margin-top: var(--s-6); font-size: var(--text-xl); }
.mot__lede { margin-top: var(--s-2); max-width: 60ch; }

.mot__status {
  margin-block: var(--s-5);
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--c-edge);
  border-left: 4px solid var(--c-body);
  border-radius: var(--radius);
  background: var(--c-wash);
}

.mot__status-line { font-size: var(--text-xl); font-weight: var(--fw-bold); }
.mot__status-note { margin-top: var(--s-1); color: var(--c-body); }

.mot__status--ok      { border-left-color: var(--c-open); }
.mot__status--soon    { border-left-color: var(--c-action); }
.mot__status--expired { border-left-color: var(--c-closed); }
.mot__status--expired .mot__status-line { color: var(--c-closed); }

.mot__danger {
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius);
  background: var(--c-closed);
  color: var(--c-white);
  font-weight: var(--fw-medium);
}

.mot__jobs {
  display: grid;
  gap: var(--s-4);
  margin-top: var(--s-4);
  padding: 0;
  list-style: none;
}

.mot__job {
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--c-edge);
  border-radius: var(--radius);
}

.mot__job--danger { border-color: var(--c-closed); }

.mot__job-title {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  align-items: baseline;
  justify-content: space-between;
  font-size: var(--text-lg);
}

.mot__job-count { color: var(--c-body); font-size: var(--text-sm); font-weight: var(--fw-normal); }

/* A defect list, grouped under a heading that says what to do about it. Used in both places
   defects appear: the outstanding jobs at the top and the history underneath, so a customer
   reading down the page meets one way of listing a defect rather than two. */
.mot__group { margin-top: var(--s-4); }

.mot__group-title {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-2);
  align-items: baseline;
  font-size: var(--text-sm);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* The DVSA's own instruction, kept as a quieter second line rather than a second heading. */
.mot__group-note {
  color: var(--c-body);
  font-size: var(--text-xs);
  font-weight: var(--fw-normal);
  text-transform: none;
  letter-spacing: 0;
}

.mot__group--dangerous .mot__group-title { color: var(--c-closed); }
.mot__group--major .mot__group-title     { color: var(--c-accent-text); }

/* Real bullets. The tester's wording is a list of separate faults and it reads as one paragraph
   without them. Indented so a defect that wraps to three lines still reads as one item. */
.mot__group-list {
  margin-top: var(--s-2);
  padding-left: 1.25em;
  list-style: disc;
}

.mot__group-list li {
  margin-top: var(--s-1);
  color: var(--c-body);
}

.mot__group-list li::marker { color: var(--c-edge-dark); }

.mot__group-none {
  margin-top: var(--s-3);
  color: var(--c-body);
  font-size: var(--text-sm);
}

.mot__job-price { margin-top: var(--s-3); }
.mot__job-price a { margin-left: var(--s-2); }

.mot__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-5);
}

.mot__history { margin-top: var(--s-6); }

.mot__history > summary {
  padding: var(--s-3) 0;
  border-top: 1px solid var(--c-edge);
  border-bottom: 1px solid var(--c-edge);
  cursor: pointer;
  font-weight: var(--fw-medium);
}

.mot__tests { margin-top: var(--s-4); padding: 0; list-style: none; }

/* One test, boxed and well separated. The service people know puts a rule between each one and
   plenty of space around it, and a year of tests run together is unreadable on a phone. */
.mot__test {
  margin-top: var(--s-5);
  padding-top: var(--s-5);
  border-top: 2px solid var(--c-edge);
}

.mot__test:first-child { margin-top: var(--s-4); }

/* The verdict beside the facts on any screen wide enough, stacked below 480px. */
.mot__test-head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3) var(--s-5);
  align-items: flex-start;
}

/* Large, and the one thing on the entry that reads from across the room. */
.mot__verdict {
  flex: 0 0 auto;
  min-width: 5.5ch;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--radius);
  font-size: var(--text-2xl);
  font-weight: var(--fw-bold);
  line-height: 1;
  letter-spacing: 0.04em;
  text-align: center;
}

/* Ink on the pale fills rather than white on the strong ones: white on either of these is under
   3:1 and this is the largest, most load-bearing word on the entry. */
.mot__verdict--pass { background: var(--c-open-wash); color: var(--c-open); }
.mot__verdict--fail { background: var(--c-closed-wash); color: var(--c-closed); }

.mot__test-facts { flex: 1 1 14ch; }
.mot__test-date { font-size: var(--text-lg); font-weight: var(--fw-bold); }

.mot__facts { margin-top: var(--s-2); }

.mot__facts > div {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-2);
  margin-top: var(--s-1);
  font-size: var(--text-sm);
}

.mot__facts dt { color: var(--c-body); }
.mot__facts dt::after { content: ""; }
.mot__facts dd { font-weight: var(--fw-medium); }

.mot__source {
  margin-top: var(--s-5);
  color: var(--c-body);
  font-size: var(--text-sm);
}

@media (min-width: 900px) {
  .mot__title { font-size: var(--text-3xl); }
  .mot__jobs { grid-template-columns: repeat(2, 1fr); }
}

/* The "what this costs" link under each job. Its own line and a 48px target: inline at the end
   of the price sentence it measured 21px tall on a 390px screen, which is under even the WCAG
   2.2 minimum of 24 and not a link anybody hits with a thumb in a workshop. */
.mot__job-more { margin-top: var(--s-2); }

.mot__job-more a {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  font-weight: var(--fw-medium);
}
