/* OldsLegend Auto Services — one stylesheet, no framework
   ===================================================================
   v3. LIGHT-LED. This replaces the v2 dark-dominant system outright.

   The world, in one paragraph: warm-white (#ffffff) and light-grey
   (#f4f4f4) grounds carry the site; near-black (#0d0c0f / #080a09)
   bands punctuate it. Display type is Barlow Condensed 600, uppercase,
   tight, and large. Body is DM Sans at 18px. Corners are square —
   radius is 0 everywhere and that is load-bearing, not an oversight.
   Hairlines draw; shadows barely exist. Photography runs full-bleed and
   fully saturated, never washed or greyed.

   Accent rules, binding — amber measures ~1.5:1 on white:
     - amber #ffc107 and orange #fe610d are FILLS carrying near-black
       text. They are never text, never a hairline, never an icon
       stroke on a light ground.
     - red #ea0a0b is reserved for exactly ONE highest-intent button per
       page: the primary button in the site-wide `.cta` band.
     - error text uses --danger #b3080e, which is a semantic state
       colour, not the reserved CTA red.

   Ground-swapping still works by re-declaring the surface tokens inside
   a band class rather than writing a parallel stylesheet. In v2 the
   root was dark and `.band-bone` re-declared light. That mechanism is
   kept and inverted: the ROOT is now light, `.band-dark` re-declares
   dark, and `.band-bone` re-declares light so that a light band nested
   inside a dark context still resets cleanly.

   Legacy names are kept defined where the markup or JS binds to them,
   with their meaning corrected rather than their name changed. Places
   where that happens are commented `legacy:`.
   =================================================================== */

/* ---- the two typefaces, served from this origin -------------------------
   SELF-HOSTED ON PURPOSE. These used to come from fonts.googleapis.com and
   fonts.gstatic.com, which made every page load — including pages with no
   checkout — send every visitor's IP address to Google before first paint.
   The project rule is that Stripe's stripe.js is the only external runtime
   request there is, and sending visitor IPs to a US ad company without consent
   is the exact pattern EU regulators have ruled against.

   These declarations are Google's own, character for character, with the
   gstatic URLs swapped for local ones: same families, same weights, same
   unicode-ranges, same font-display, same variable DM Sans file pinned at
   three weights. Nothing about the rendered type changes.

   The vietnamese subset of Barlow Condensed is the one thing not carried over.
   Nothing on this site is in Vietnamese, and it was only ever downloaded by a
   browser that met one of those codepoints.
   ------------------------------------------------------------------------- */

/* latin-ext */
@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(../fonts/barlow-condensed-600-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(../fonts/barlow-condensed-600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(../fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(../fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(../fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(../fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(../fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(../fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* ---- ground: light dominant, dark punctuates ---------------------- */
  --white: #ffffff;         /* primary ground */
  --bone: #f4f4f4;          /* legacy name. Now the light-grey alternate band. */
  --bone-warm: #ffffff;     /* legacy name. Second value in a light run. */
  --bone-2: #ebebec;        /* sunk/secondary surface on light */
  --bone-line: #dcdcde;     /* hairline on light */

  --ink: #0d0c0f;           /* near-black punctuation band */
  --ink-2: #080a09;         /* deepest band: footer, CTA */
  --ink-3: #16151a;         /* raised surface on the dark ground */
  --ink-4: #1f1e25;         /* second raised surface on the dark ground */

  /* ---- surface + line (re-declared by the band classes) ------------- */
  --surface: #ffffff;
  --surface-2: #f4f4f4;
  --surface-sunk: #ffffff;
  --line: #dcdcde;          /* decorative hairline */
  --line-strong: #8e8e94;   /* control boundary — 3.26:1 on white */
  --line-soft: rgba(0, 0, 0, .05);
  --hair: none;

  /* ---- type colour --------------------------------------------------
     legacy: --paper / --paper-2 are FOREGROUND tokens, not grounds.
     They were near-white on the v2 dark ground; they are near-black
     here. Every `color: var(--paper)` in this sheet still means
     "the strongest text colour for whatever ground I am on". */
  --paper: #0d0c0f;         /* 19.51:1 on #ffffff, 18.67:1 on #f4f4f4 */
  --paper-2: #26252b;       /* 15.27:1 on #ffffff, 14.54:1 on #f4f4f4 */
  --muted: #56555c;         /*  7.35:1 on #ffffff,  7.05:1 on #f4f4f4 */
  --muted-2: #6b6a72;       /*  5.34:1 on #ffffff,  5.11:1 on #f4f4f4 */
  --graphite: #56555c;      /* legacy alias of --muted, kept for old rules */

  /* ---- accent -------------------------------------------------------
     Fills only. See the header note. */
  --amber: #ffc107;         /* primary action fill; ink text = 11.97:1 */
  --amber-tint: #fff6da;    /* selected-row wash. Meaning is carried by the
                               ink border beside it, never by the wash. */
  --orange: #fe610d;        /* secondary / hover fill; ink text = 6.44:1 */
  --orange-2: #f44d00;      /* active fill; ink text = 5.49:1 */
  --red: #ea0a0b;           /* ONE button per page; white text = 4.62:1 */
  /* Rule-like accents (left rules, top edges of panels). Amber reads at
     11.97:1 against near-black but only 1.48:1 against white, so on a light
     ground the same device is drawn in ink. */
  --accent-rule: #0d0c0f;
  --danger: #b3080e;        /* error text on light — 7.10:1 on #ffffff */
  --danger-lite: #ff9d9d;   /* error text on ink — 9.81:1 */
  --ok: #0f7a37;            /* success fill; white text = 5.44:1 */
  --ok-tint: #e6f4ea;
  --ok-ink: #0f5f2b;        /* success text on --ok-tint = 6.87:1 */
  --focus: #0d0c0f;

  /* legacy gold tokens. Nothing in this sheet paints with them any more;
     they stay defined because main.js reads var(--gold) inline for the
     before/after focus ring, which sits over photography (not on a light
     ground), so amber is legitimate there. */
  --gold: #ffc107;
  --gold-hi: #ffd54a;
  --gold-dim: #b98a00;
  --gold-tint: rgba(255, 193, 7, .12);
  --gold-tint-2: rgba(255, 193, 7, .22);

  /* ---- geometry ----------------------------------------------------- */
  --wrap: 1320px;
  --pad: clamp(1.25rem, 5vw, 3.5rem);
  --section-y: clamp(4.5rem, 9vw, 8rem);
  --section-y-lg: clamp(6rem, 11vw, 10rem);
  --header-h: 108px;
  --header-h-min: 74px;
  /* logo.webp is 516×282 (aspect 1.830), trimmed to the artwork. 516px is 3x
     --brand-w below, so it is still native at DPR 3 on a phone; the 1414×771
     PNG master it came from is kept beside it as logo-1414-master.png.bak. The
     client asked for the emblem centred and large; on white it can finally
     carry that size without the badge disappearing. */
  --brand-w: 172px;
  --brand-h: 94px;
  --brand-w-min: 118px;
  --brand-h-min: 64px;
  --topbar-h: 34px;
  --control-h: 3.25rem;

  /* Square corners are the world. These stay defined because rules and
     older markup reference them; every one of them resolves to 0. */
  --r-xs: 0;
  --r-sm: 0;
  --r-md: 0;
  --r-lg: 0;
  --r-pill: 0;

  /* ---- elevation — almost none; the hairline does the work ---------- */
  --shadow-1: none;
  --shadow-2: 0 1px 0 rgba(0, 0, 0, .05);
  --shadow-3: 0 18px 44px -30px rgba(0, 0, 0, .45);
  --glow-gold: none;

  /* ---- motion -------------------------------------------------------- */
  --t-fast: 130ms;
  --t: 180ms;
  --t-slow: 250ms;
  --ease: cubic-bezier(.2, .65, .3, 1);

  /* ---- type ---------------------------------------------------------- */
  --display: "Barlow Condensed", "Arial Narrow", Arial, sans-serif;
  --body: "DM Sans", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, "Cascadia Mono", monospace;

  /* ---- drawn marks --------------------------------------------------
     The one directional arrow on the site, drawn once at a single stroke
     weight and square-capped like everything else in this world. It is a
     mask, not an image, so it takes `currentColor` and works on both
     grounds. A Unicode → renders in whatever fallback face the platform
     picks and never matches the stroke weight around it. */
  --arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='square' stroke-linejoin='miter'%3E%3Cpath d='M2.5 8h11M9 3.5 13.5 8 9 12.5'/%3E%3C/svg%3E");
  /* Same reasoning as the arrow: a Unicode ✓ renders in whatever fallback face
     the platform picks and never matches the stroke weight around it. Drawn at
     the arrow's 1.75 weight and square caps, masked so it takes currentColor. */
  --check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='square' stroke-linejoin='miter'%3E%3Cpath d='m3.5 8.4 3.1 3.1 5.9-6.9'/%3E%3C/svg%3E");
}

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

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background-color: var(--white);
  color: var(--paper);
  font-family: var(--body);
  font-size: 1.125rem;      /* 18px */
  line-height: 1.6;
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

::selection { background: var(--amber); color: var(--ink); }

/* ---------- type scale ----------
   Barlow Condensed carries real weights, so no font-synthesis guard is
   needed. Display line-height is 1.0 and the tracking is negative — the
   condensed face wants to be set tight and large. */

h1, h2, h3, h4, .display {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1;
  text-transform: uppercase;
  text-wrap: balance;
  margin: 0;
}

h1 { font-size: clamp(2.5rem, 5.4vw, 3.5625rem); letter-spacing: -.011em; }   /* 40 → 57px */
h2 { font-size: clamp(2rem, 4vw, 2.9375rem); letter-spacing: -.011em; }       /* 32 → 47px */
h3 { font-size: clamp(1.375rem, 2.1vw, 1.75rem); line-height: 1.05; letter-spacing: -.011em; } /* 22 → 28px */
h4 { font-size: 1.0625rem; letter-spacing: .04em; line-height: 1.15; }        /* 17px */

p { margin: 0 0 1rem; max-width: 68ch; }
p:last-child { margin-bottom: 0; }

/* Headings carry no bottom margin of their own, so state the gap once. */
h1 + p, h1 + .lede, h2 + p, h2 + .lede, h1 + ul, h2 + ul { margin-top: 1.5rem; }
h3 + p, h4 + p { margin-top: .6rem; }
h2 + .grid, h2 + .rail, h1 + .grid, h1 + .rail { margin-top: clamp(2rem, 4vw, 3rem); }

/* width:100% matters: as a grid item, `margin-inline:auto` cancels stretch and
   the box would otherwise shrink to fit its text. */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--pad); }
.narrow { max-width: 68ch; }
.center { text-align: center; margin-inline: auto; }

section { padding-block: var(--section-y); }

/* legacy: kickers above headings are banned site-wide and no markup uses
   `.eyebrow` any more. Kept defined so a stray one degrades gracefully —
   as plain micro-caps in ink, never in accent colour. */
.eyebrow {
  font-family: var(--body);
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: .75rem;
  font-weight: 500;
  color: var(--muted);
  margin-bottom: 1.25rem;
  display: flex;
  align-items: center;
  gap: .65rem;
  line-height: 1.4;
}
.eyebrow::before {
  content: "";
  flex: none;
  width: 1.5rem;
  height: 2px;
  background: currentColor;
}
.center .eyebrow,
.cta .eyebrow { justify-content: center; }

.lede {
  font-size: clamp(1.1875rem, 1.5vw, 1.375rem);
  line-height: 1.55;
  color: var(--paper-2);
  max-width: 62ch;
}
.center p, .cta p, .success p, .statement p, td p, li p { margin-inline: auto; }
.muted { color: var(--muted); }
.small { font-size: .9375rem; line-height: 1.55; }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- focus + skip link ---------- */

:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--amber); color: var(--ink); padding: .9rem 1.5rem;
  font-family: var(--body); font-weight: 500;
  text-transform: uppercase; letter-spacing: .108em; font-size: .8125rem;
  text-decoration: none; border-radius: 0;
}
.skip:focus { left: 0; }

/* ---------- buttons ----------
   13px DM Sans 500, uppercase, +1.4px tracking. Amber fill with near-black
   text is the primary; the hover walks amber → orange → orange-2, all of
   which carry the same near-black text and all of which pass AA. */

.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  font-family: var(--body); text-transform: uppercase;
  letter-spacing: .108em; font-size: .8125rem; font-weight: 500; line-height: 1;
  padding: 1.0625rem 3.125rem;   /* 17px 50px */
  border: 2px solid var(--amber);
  border-radius: 0;
  background: var(--amber);
  color: var(--ink);             /* 11.97:1 on amber */
  text-decoration: none; cursor: pointer;
  text-align: center;
  transition: background var(--t) var(--ease), border-color var(--t) var(--ease),
              color var(--t) var(--ease), opacity var(--t) var(--ease);
}
.btn:hover { background: var(--orange); border-color: var(--orange); color: var(--ink); }
/* .is-sending is the pressed state held past the pointer release — see the
   feedback-motion block at the foot of this file. Same colours, one source. */
.btn:active,
.btn.is-sending { background: var(--orange-2); border-color: var(--orange-2); color: var(--ink); }
.btn:disabled,
.btn[disabled] {
  background: transparent; border-color: var(--line-strong); color: var(--muted-2);
  cursor: not-allowed;
}

.btn-ghost {
  background: transparent;
  color: var(--paper);
  border-color: var(--paper);
}
.btn-ghost:hover { background: var(--paper); color: var(--white); border-color: var(--paper); }
.btn-ghost:active { background: var(--ink-2); color: var(--white); border-color: var(--ink-2); }

.btn-sm { padding: .75rem 2.5rem; }   /* 12px 40px, same 13px type */

.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 2.25rem; align-items: center; }

/* ---------- topbar ----------
   The one dark sliver above a white header. Micro caps, edges of the measure. */

.topbar {
  background: var(--ink-2);
  color: #a2a2a9;
  font-family: var(--body);
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  position: relative; z-index: 101;
}
.topbar .wrap {
  display: flex; flex-wrap: wrap; gap: .2rem 2rem;
  justify-content: space-between; align-items: center;
  padding-block: .5rem;
  min-height: var(--topbar-h);
}
.topbar span { margin-right: auto; }
.topbar a {
  color: #c9c9cf; text-decoration: none; white-space: nowrap;   /* 11.5:1 on #080a09 */
  transition: color var(--t-fast) var(--ease);
}
.topbar a:hover { color: #fff; }

/* ---------- header: MENU · emblem · Book Now ----------
   Structure is unchanged from v2: three-column grid, mark centred,
   scroll-shrink. The ground flips to white, which is the ground the chrome
   emblem was actually drawn for. */

.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255, 255, 255, .93);
  backdrop-filter: blur(16px) saturate(120%);
  -webkit-backdrop-filter: blur(16px) saturate(120%);
  border-bottom: 1px solid var(--bone-line);
  color: var(--paper);
}
.site-header .wrap {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 1rem; min-height: var(--header-h);
  position: relative; z-index: 2;
  padding-block: .55rem;
}
.site-header .brand { justify-self: center; }
.header-end { justify-self: end; display: inline-flex; align-items: center; gap: .75rem; }
.header-cta { white-space: nowrap; }

/* Cart. Same rhythm as MENU on the other side of the emblem: DM Sans 13px
   uppercase at the same tracking, a 22px currentColor glyph where the burger
   has its 22px bars, and the same padding-bought 44px hit area.

   The count is the one amber thing in the header, and it is a FILL carrying
   ink text (11.97:1). Amber as text on this white ground measures 1.48:1 and
   would be a defect — see docs/DESIGN-DIRECTION.md. Square, like everything. */
.cart-link {
  display: inline-flex; align-items: center; gap: .5rem;
  text-decoration: none; color: var(--paper);
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .8125rem; line-height: 1;
  padding: .875rem .5rem; margin-inline: -.5rem;
  transition: color var(--t-fast) var(--ease);
}
/* style.css sets display on .cart-link, which beats the UA rule for [hidden].
   Same trap setHidden() exists for in main.js — see that helper. */
.cart-link[hidden] { display: none; }
.cart-link:hover { color: var(--muted); }
.cart-link:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }
.cart-glyph {
  width: 22px; height: 22px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: square; stroke-linejoin: miter;
}
.cart-count {
  display: inline-grid; place-items: center;
  min-width: 22px; height: 22px; padding-inline: .3rem;
  background: var(--amber); color: var(--ink);
  border-radius: 0;
  font-size: .75rem; font-weight: 700; letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

/* Progressive enhancement: the bar solidifies and the lockup drops to ~68% as
   the page moves. Scroll-driven CSS animation — no JS, no scroll listener.
   Where it isn't supported the header simply stays at its full height. */
@supports (animation-timeline: scroll()) {
  .site-header, .site-header .wrap, .brand img {
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: scroll();
    animation-range: 0 120px;
  }
  .site-header { animation-name: header-solid; }
  .site-header .wrap { animation-name: header-squeeze; }
  .brand img { animation-name: brand-shrink; }
}
@keyframes header-solid {
  to { background: #ffffff; box-shadow: 0 1px 0 rgba(0, 0, 0, .12); }
}
@keyframes header-squeeze {
  to { min-height: var(--header-h-min); }
}
@keyframes brand-shrink {
  to { width: var(--brand-w-min); height: var(--brand-h-min); }
}

.brand {
  display: inline-flex; align-items: center; gap: .55rem;
  text-decoration: none; color: inherit;
  transition: opacity var(--t) var(--ease);
}
.brand:hover { opacity: .78; }
.brand img {
  width: var(--brand-w); height: var(--brand-h);
  object-fit: contain;
}
/* legacy type lockup — kept defined for the footer, not used in the header */
.brand span {
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  letter-spacing: .1em; font-size: 1.5rem; line-height: 1;
  color: currentColor; white-space: nowrap;
}
.brand small {
  font-family: var(--body); font-size: .6875rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--muted-2); white-space: nowrap;
}

/* MENU control. The label is the visible control; the checkbox behind it is
   visually hidden but focusable, so it stays keyboard + SR operable. */
.nav-toggle {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.nav-toggle:focus-visible ~ .site-header .burger { outline: 2px solid var(--focus); outline-offset: 4px; }

.burger {
  display: inline-flex; align-items: center; gap: .7rem; cursor: pointer;
  justify-self: start;
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .8125rem; line-height: 1;
  color: var(--paper);
  /* 44×44 minimum hit area (WCAG 2.1 AA target size) bought with padding, not
     with a bigger glyph — the bars stay 22×16. The negative inline margin
     keeps the first bar optically on the container edge. */
  padding: .875rem .6875rem;
  margin-inline: -.6875rem;
  transition: color var(--t-fast) var(--ease);
}
.burger:hover { color: var(--muted); }
.burger i {
  display: block; width: 22px; height: 2px; background: currentColor;
  transition: transform var(--t) var(--ease), opacity var(--t-fast) var(--ease);
}
.burger .bars { display: grid; gap: 5px; width: 22px; flex: none; }
.burger .lbl-close { display: none; }
.nav-toggle:checked ~ .site-header .burger .lbl-open { display: none; }
.nav-toggle:checked ~ .site-header .burger .lbl-close { display: inline; }
.nav-toggle:checked ~ .site-header .burger i:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle:checked ~ .site-header .burger i:nth-child(2) { opacity: 0; }
.nav-toggle:checked ~ .site-header .burger i:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---------- full-screen nav overlay ----------
   The overlay is the site's largest dark punctuation: white Barlow Condensed
   at scale on near-black. */

.nav-overlay {
  position: fixed; inset: 0; z-index: 99;
  background: var(--ink);
  color: #fff;
  overflow-y: auto;
  visibility: hidden; opacity: 0;
  transition: opacity var(--t) var(--ease), visibility 0s linear var(--t);
  display: grid; align-content: space-between;
  padding: calc(var(--header-h) + clamp(2rem, 6vh, 4rem)) 0 clamp(2rem, 6vh, 4rem);
}
.nav-toggle:checked ~ .nav-overlay {
  visibility: visible; opacity: 1;
  transition: opacity var(--t-slow) var(--ease), visibility 0s;
}
/* Click-anywhere-to-close backdrop; also gives the overlay a second, larger
   hit target for closing on touch. */
.nav-scrim { position: absolute; inset: 0; z-index: 0; cursor: default; }
.nav-overlay > .wrap { position: relative; z-index: 1; }

.nav ul { list-style: none; margin: 0; padding: 0; }
.nav a {
  position: relative;
  display: flex; align-items: baseline; gap: 1.25rem;
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  font-size: clamp(1.5rem, 3.6vw, 2.35rem); letter-spacing: -.012em; line-height: 1;
  color: #fff; text-decoration: none;
  padding-block: clamp(.4rem, 1.1vh, .7rem);
  border-bottom: 1px solid #2b2a31;
  transition: color var(--t-fast) var(--ease), padding-left var(--t) var(--ease);
}
.nav a:hover { color: var(--amber); padding-left: .5rem; }   /* amber on ink = 11.97:1 */
.nav a[aria-current="page"] { color: var(--amber); }
.nav a[aria-current="page"]::after {
  content: ""; position: absolute; right: 0; top: 50%; width: 2rem; height: 3px;
  background: var(--amber);
}
.nav .btn {
  display: inline-flex; width: auto; margin-top: 2rem; border-bottom: 0;
  color: var(--ink); padding: 1.0625rem 3.125rem;
}
.nav .btn::before, .nav .btn::after { content: none; }
.nav .btn:hover { color: var(--ink); padding-left: 3.125rem; }

.nav-meta {
  display: flex; flex-wrap: wrap; gap: .6rem 3rem;
  margin-top: clamp(2rem, 6vh, 4rem);
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .12em; font-size: .8125rem; color: #a2a2a9;
}
.nav-meta a { color: #c9c9cf; text-decoration: none; }
.nav-meta a:hover { color: #fff; }

/* Tap-to-call on a phone.
   This used to be a fixed pill floating over the bottom of the viewport, and
   at rest it sat on top of the intake strip's YEAR field — a control the
   first-viewport promise says has to be actionable. A floating overlay can
   never guarantee that, so the number goes back into the topbar and the
   topbar becomes the first sticky layer instead. Sticky occupies layout,
   so it is structurally incapable of covering the form, and it needs no
   shadow to lift off content it never overlaps.

   The topbar also carries the PHONE here and drops the email: calling is the
   highest-intent action for this shop, and the email is still in the nav
   overlay, the footer and on /contact. */
@media (max-width: 760px) {
  /* the topbar row IS the call target, so it is sized to the 44px minimum and
     --topbar-h is restated to match: the header docks against it exactly. */
  :root { --topbar-h: 44px; }

  /* the lede spans the full width on a phone, so it crosses the bright side
     of the photograph — deepen the scrim rather than shrink the type */
  .topbar {
    font-size: .6875rem;
    position: sticky; top: 0;
  }
  .topbar span { display: none; }
  .topbar a[href^="mailto:"] { display: none; }
  .topbar .wrap {
    justify-content: center; gap: 1rem;
    padding-block: 0; min-height: var(--topbar-h);
  }
  .topbar a[href^="tel:"] {
    display: inline-flex; align-items: center;
    min-height: var(--topbar-h);
    padding-inline: 1rem;
    font-weight: 700; letter-spacing: .14em;
    color: #fff;
  }
  /* the topbar is now the first sticky layer, so the header docks below it */
  .site-header { top: var(--topbar-h); }
}

@media (max-width: 900px) {
  :root {
    --header-h: 90px; --header-h-min: 64px;
    --brand-w: 142px; --brand-h: 77px;
    --brand-w-min: 102px; --brand-h-min: 56px;
  }
}
@media (max-width: 560px) {
  :root {
    --header-h: 72px; --header-h-min: 58px;
    --brand-w: 108px; --brand-h: 59px;
    --brand-w-min: 88px; --brand-h-min: 48px;
  }
  .site-header .wrap { gap: .5rem; }
  .brand span { font-size: 1.1rem; }
  .brand small { display: none; }
  .burger .lbl-open, .burger .lbl-close { display: none; }
  .nav-toggle:checked ~ .site-header .burger .lbl-close { display: none; }
  .header-cta { padding: .7rem 1.1rem; letter-spacing: .09em; }
  /* The word goes where MENU's does; the glyph and the count carry it, and the
     accessible name says "Cart, 3 items" either way.

     The count moves onto the glyph rather than sitting beside it. Four controls
     and a centred emblem do not fit across 320px in a row, and the emblem is
     the one that was losing — it collapsed to 38px wide. Overlaying the badge
     gives it back ~30px. Still square, still an amber fill carrying ink. */
  .cart-word { display: none; }
  .header-end { gap: .35rem; }
  /* 44x50, and the 22px it grew by is real layout, not padding bought back with
     a negative margin. The negative-margin trick was what held it at 35 wide:
     the hit area overlapped Book Now by a pixel on one side and the emblem on
     the other, so growing it symmetrically bought nothing — the neighbours just
     took the pixels back. The space comes from Book Now, which is 29px narrower
     now that its own compact rule is no longer overridden further down this
     file. The emblem is not asked for anything; at 360 it gets 2px BACK. */
  .cart-link { position: relative; gap: 0; padding-inline: .6875rem; margin-inline: 0; }
  .cart-count {
    /* Inset, not flush: Book Now is also an amber fill, and a badge touching
       its edge reads as one smeared amber shape rather than two controls. */
    position: absolute; top: .4rem; right: .6875rem;
    min-width: 16px; height: 16px; padding-inline: .15rem;
    font-size: .625rem; line-height: 1;
  }
  /* The emblem is the brand. It gives way to nothing below this. */
  .brand img { min-width: 76px; }
}

/* ---------- hero ----------
   Full-bleed photograph, display type over it, intake strip inside it. */

.hero {
  position: relative;
  min-height: clamp(560px, 84vh, 860px);
  display: grid; align-items: end;
  padding-block: clamp(4rem, 8vw, 6.5rem);
  overflow: hidden;
  text-align: left;
  color: #fff;
  background: var(--ink);
}
/* The cars are the product — keep their colour. Legibility comes from the
   scrim below, not from desaturating the client's own work. */
.hero-bg {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
  filter: contrast(1.04) saturate(1.06);
}
/* Scrim, not desaturation. Measured against the real pixels: 18px white body
   copy needs 4.5:1 and the lede runs to about half the viewport width, so the
   lower band has to hold ~.6 combined alpha there. The top third stays light
   so the photograph still reads as a photograph. */
.hero::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(8, 10, 9, .55) 0%, rgba(8, 10, 9, .15) 26%, rgba(8, 10, 9, .5) 56%, rgba(8, 10, 9, .8) 88%, rgba(8, 10, 9, .9) 100%),
    linear-gradient(90deg, rgba(8, 10, 9, .62) 0%, rgba(8, 10, 9, .3) 52%, rgba(8, 10, 9, .05) 88%);
}
.hero .wrap { position: relative; z-index: 1; }
.hero h1 { max-width: 15ch; color: #fff; }
.hero h1 em { font-style: normal; color: inherit; }
.hero p { max-width: 56ch; margin-top: 1.4rem; }
.hero .lede { color: #ededf0; }        /* 15.1:1 over the scrimmed photograph */
.hero .btn-row { margin-top: 2.25rem; }
.hero .badge-row { margin-top: 1.75rem; }

.hero-sub {
  min-height: clamp(340px, 48vh, 520px);
  text-align: left;
}
.hero-sub h1 { font-size: clamp(2.25rem, 4.6vw, 3.125rem); }

/* legacy: `.hero-center` no longer centres — the mark moved into the header
   and all hero copy is left-aligned. Kept defined; older markup carries it. */
.hero-center { text-align: left; }
.hero-center .wrap > * { margin-inline: 0; }
.hero-center h1 { max-width: 15ch; }
.hero-center p { max-width: 56ch; }
.hero-center .btn-row { justify-content: flex-start; margin-top: 2.25rem; }
.hero-center .eyebrow { display: flex; justify-content: flex-start; }

/* The logo lives in the header. Never stacked in the hero — at hero scale the
   raster lockup is the loudest and least legible thing on the page. */
.hero-logo { display: none; }

/* ---------- intake strip ----------
   The fold IS the intake, so the strip lives inside the hero. It is a solid
   white plate rather than a translucent one: it sits over photography and the
   controls have to read like real product UI, not like a scrim. One horizontal
   row from 900px up — service, year, make, model, submit — with the lead line
   spanning the full width above them. Year is pinned to ~96px because a
   four-digit field has no business being 1fr. */

.strip-form {
  /* re-declare the light tokens: the plate is its own light ground even
     though the hero around it is dark */
  --surface: #ffffff;
  --surface-2: #f4f4f4;
  --surface-sunk: #ffffff;
  --line: #dcdcde;
  --paper: #0d0c0f;
  --paper-2: #26252b;
  --muted: #56555c;
  --muted-2: #6b6a72;
  --focus: #0d0c0f;
  --accent-rule: #0d0c0f;

  display: grid;
  gap: .85rem;
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
  padding: clamp(1.15rem, 2vw, 1.5rem);
  background: #ffffff;
  color: var(--paper);
  border: 0;
  border-top: 6px solid var(--amber);   /* decorative brand edge, carries no meaning */
  border-radius: 0;
  box-shadow: 0 26px 60px -34px rgba(0, 0, 0, .7);
  max-width: 1120px;
}

.strip-lead {
  grid-column: 1 / -1;
  margin: 0; max-width: 62ch;
  color: var(--paper);
  font-size: 1rem; line-height: 1.5; font-weight: 500;
}
.strip-form .field { min-width: 0; gap: .35rem; }
.strip-form .field label { color: var(--muted); }
/* One shared control height so the row sits on a single baseline. */
.strip-form .field input,
.strip-form .field select,
.strip-form .btn { height: var(--control-h); }
.strip-form .btn { padding-inline: 1.9rem; white-space: nowrap; }
.strip-form #q-year { max-width: 96px; }

@media (min-width: 560px) {
  .strip-form { grid-template-columns: 96px minmax(0, 1fr) minmax(0, 1fr); align-items: end; }
  .strip-form > .field:first-of-type,
  .strip-form > .btn { grid-column: 1 / -1; }
}
@media (min-width: 900px) {
  .strip-form {
    grid-template-columns: minmax(190px, .9fr) 96px minmax(0, 1fr) minmax(0, 1fr) auto;
    align-items: end;
  }
  .strip-form > .field:first-of-type,
  .strip-form > .btn { grid-column: auto; }
}

/* ---------- stats bar ----------
   legacy: no markup uses `.stats` since the homepage band was cut. Kept
   defined and re-tuned so it degrades onto the light ground if it returns. */

.stats {
  background: var(--surface-2);
  border-block: 1px solid var(--line);
  padding-block: clamp(2.5rem, 4vw, 3.5rem);
}
.stats .wrap {
  display: grid; gap: 2rem 0;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  text-align: left;
}
.stats .wrap > div { padding-inline: 0; }
.stats dt {
  font-family: var(--display); font-weight: 600;
  font-size: clamp(2.25rem, 3.2vw, 3rem);
  letter-spacing: -.02em;
  color: var(--paper); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stats dd {
  margin: .75rem 0 0; font-size: .8125rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); line-height: 1.5;
  max-width: 24ch; font-weight: 500;
}
@media (min-width: 760px) {
  .stats .wrap > div { padding-left: 1.75rem; }
  .stats .wrap > div:first-child { padding-left: 0; }
  .stats .wrap > div + div { border-left: 1px solid var(--line); }
}

/* ---------- cards ---------- */

/* `min(Npx, 100%)` rather than a bare Npx: a bare minimum is a floor the track
   keeps even when the container is smaller than it, so at 320 the wrap is 280
   wide, the grid-3 track stayed 300, and every product card and service card
   ran from the 20px left gutter to the bare edge of the screen. The page never
   scrolled — the overflow landed exactly on the viewport edge — so it read as
   "the right margin is missing on this one band" rather than as a bug. */
.grid { display: grid; gap: clamp(1.25rem, 2.2vw, 2rem); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }

.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 0;
  overflow: hidden;
  display: flex; flex-direction: column;
  text-decoration: none;
  transition: border-color var(--t) var(--ease), background var(--t) var(--ease);
}
a.card:hover { border-color: var(--paper); }
.card-media { aspect-ratio: 4 / 3; overflow: hidden; background: var(--surface-2); }
.card-media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 400ms var(--ease);
}
a.card:hover .card-media img { transform: scale(1.035); }
.card-body { padding: 1.6rem 1.6rem 1.75rem; flex: 1; display: flex; flex-direction: column; gap: .7rem; }
.card-body p { color: var(--muted); font-size: 1rem; line-height: 1.6; }
/* Provenance line. Sits directly under the copy it qualifies, so a stock or
   illustrative photograph is never left implying it is the shop's own. */
.card-note {
  margin-top: -.15rem;
  font-size: .8125rem; line-height: 1.5; color: var(--muted-2);
}
/* Same job as .card-note, but sitting on the photograph it qualifies rather
   than under it. A photo band's scrim is tuned for display type, not for
   13px, so this carries its own so the provenance is actually readable.
   The second selector only outranks `.statement p`, which sets display type
   on every p in the band. */
.photo-note,
.statement .photo-note {
  display: inline-block;
  margin-top: 1.4rem;
  padding: .45rem .75rem;
  max-width: 66ch;
  background: rgba(8, 10, 9, .8);
  color: #ededf0;
  font-family: var(--body); font-weight: 400; text-transform: none;
  letter-spacing: 0;
  font-size: .8125rem; line-height: 1.5;
}
.card .more {
  margin-top: auto; padding-top: 1.1rem;
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .8125rem; color: var(--paper);
  display: inline-flex; align-items: center; gap: .45rem;
  transition: gap var(--t) var(--ease);
}
a.card:hover .more { gap: .85rem; }
/* Drawn arrow, masked so it inherits the link colour on either ground. */
.more::after {
  content: ""; flex: none;
  width: .95em; height: .95em;
  background: currentColor;
  -webkit-mask: var(--arrow) center / contain no-repeat;
          mask: var(--arrow) center / contain no-repeat;
}

/* ---------- service list ----------
   Flex rather than fixed grid columns: `.svc` is dropped both full-width and
   inside 3-up grids, and a hard three-column rule shreds the copy in the
   narrow case. Here the body text simply wraps to its own line. */

.svc {
  border-top: 2px solid var(--paper);
  padding-block: 2.25rem;
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: .5rem 2.5rem;
  transition: border-color var(--t) var(--ease);
}
.svc > * { flex: 1 1 15rem; min-width: 0; }
.svc .svc-num { flex: 0 0 3.5rem; }
.svc-num {
  font-family: var(--display); font-weight: 600;
  font-size: 2.5rem; line-height: 1; letter-spacing: -.02em;
  color: var(--muted-2);
  font-variant-numeric: tabular-nums;
  transition: color var(--t) var(--ease);
}
.svc:hover .svc-num { color: var(--paper); }
.svc p { color: var(--muted); }

/* ---------- feature ticks ---------- */

.ticks { list-style: none; margin: 1.75rem 0 0; padding: 0; display: grid; gap: .85rem; }
.ticks li {
  display: flex; gap: .9rem; align-items: baseline; color: var(--paper-2);
  font-size: 1.0625rem; line-height: 1.55;
}
.ticks li::before {
  content: ""; flex: none; width: 16px; height: 2px; background: currentColor;
  opacity: .5; transform: translateY(-.35em);
}

/* EXPLICIT COLUMN COUNTS, not auto-fit.
 *
 * This was `repeat(auto-fit, minmax(180px, 1fr))`, which on a wide screen
 * resolved to six columns against a ten-item list: six on the first row, four
 * on the second, and a two-cell hole in the corner. The 180px floor also let
 * columns get narrow enough to break phrases mid-name — "BRAKES & DISC /
 * CONVERSIONS" — so some rows were one line tall and others two.
 *
 * Fixed counts mean the layout is known rather than derived: 4 / 3 / 2 / 1 by
 * width, and the list is kept at a multiple of twelve so every one of them
 * divides evenly. */
.pill-grid {
  display: grid; gap: 0;
  grid-template-columns: 1fr;
  list-style: none; margin: 2rem 0 0; padding: 0;
  border-top: 1px solid var(--line);
}
@media (min-width: 30rem)  { .pill-grid { grid-template-columns: repeat(2, 1fr) } }
@media (min-width: 48rem)  { .pill-grid { grid-template-columns: repeat(3, 1fr) } }
@media (min-width: 75rem)  { .pill-grid { grid-template-columns: repeat(4, 1fr) } }
.pill-grid li {
  border-bottom: 1px solid var(--line);
  padding: .95rem 1.25rem .95rem .25rem;   /* right gutter so columns never touch */
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  letter-spacing: .01em;
  /* Down from 1.25rem: at four columns the longest item — "Body panels, front
   * ends & beds" — now sets on two lines instead of three, which is what makes
   * the row heights read as even. */
  font-size: 1.12rem; line-height: 1.2;
  color: var(--paper);
  /* Baseline-aligned, not centred: a two-line item beside a one-line item
   * looked mis-set when both were vertically centred against a shared rule. */
  display: flex; align-items: baseline; gap: .7rem;
}
.pill-grid li::before {
  content: ""; width: 8px; height: 8px; background: var(--accent-rule); flex: none;
  /* Nudged down to sit on the cap height rather than the text baseline. */
  transform: translateY(-.15em);
}

/* ---------- split ---------- */

.split { display: grid; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
@media (min-width: 900px) { .split { grid-template-columns: 1fr 1fr; } }
.split img {
  width: 100%; height: 100%; max-height: 640px; object-fit: cover;
  border-radius: 0;
}

/* ---------- full-bleed image band (section break) ----------
   legacy: `.imgband` is a drop-in section separator kept defined for older
   markup. Text is optional — with an empty .wrap it is a full-width photo. */

.imgband {
  position: relative;
  min-height: clamp(300px, 48vh, 600px);
  display: grid; align-items: end;
  padding-block: clamp(2.5rem, 6vw, 5rem);
  overflow: hidden;
  color: #fff;
}
.imgband > img,
.imgband .hero-bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  background-size: cover; background-position: center;
}
.imgband::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(8, 10, 9, .3) 0%, rgba(8, 10, 9, .05) 40%, rgba(8, 10, 9, .82) 100%);
}
.imgband .wrap { position: relative; z-index: 2; }
.imgband h2 { max-width: 20ch; color: #fff; }
.imgband .btn-row { margin-top: 1.75rem; }

/* ---------- proof head ----------
   A section heading and one piece of outside proof sharing a row. Both were
   half-empty lines at full band height on their own; together they fill one
   band's head and delete a band's worth of padding. */

.proof-head {
  display: grid; gap: clamp(1.5rem, 4vw, 3.5rem); align-items: start;
  margin-bottom: clamp(2rem, 4vw, 3rem);
}
@media (min-width: 900px) {
  .proof-head { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
}

/* ---------- gallery ---------- */

.gallery {
  display: grid; gap: 2px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
.gallery img {
  width: 100%; aspect-ratio: 4 / 5; object-fit: cover;
  border-radius: 0;
}
/* No hover dim. The tiles are not links, and dulling the client's own
   photography to promise an interaction that does not exist is the one thing
   this page is not allowed to do. */
.gallery figure { margin: 0; }
.gallery figcaption {
  margin-top: .9rem;
  font-size: .9375rem; line-height: 1.5;
  color: var(--muted);
  max-width: 34ch;
}
/* Staggered baseline instead of a uniform contact sheet. Offsetting alternate
   tiles can't leave a hole the way a spanning tile can. */
@media (min-width: 900px) {
  .gallery { gap: 1.5rem; align-items: start; }
  .gallery > :nth-child(even) { margin-top: 3rem; }
}

/* ---------- tabs / filters ----------
   `.tabs` and `.filters` are the same control. The selected state is an ink
   underline, never an accent hairline. */

.filters, .tabs {
  display: flex; flex-wrap: wrap; gap: 0 2rem;
  margin-bottom: 2.5rem;
  border-bottom: 1px solid var(--line);
}
.filters button, .tabs button {
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .8125rem; padding: .95rem 0; cursor: pointer;
  background: none; color: var(--muted);
  border: 0; border-bottom: 3px solid transparent;
  margin-bottom: -1px;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.filters button:hover, .tabs button:hover { color: var(--paper); }
.filters button[aria-pressed="true"], .tabs button[aria-pressed="true"],
.filters button[aria-selected="true"], .tabs button[aria-selected="true"] {
  color: var(--paper); border-bottom-color: var(--paper);
}

/* ---------- product card ----------
   Parts photography is cut out on white, so the product card carries its own
   white ground wherever it is dropped, including inside a dark band. */

.product {
  --surface: #ffffff;
  --surface-2: #f4f4f4;
  --surface-sunk: #ffffff;
  --line: #dcdcde;
  --line-strong: #8e8e94;
  --paper: #0d0c0f;
  --paper-2: #26252b;
  --muted: #56555c;
  --muted-2: #6b6a72;
  --focus: #0d0c0f;
  background: #ffffff;
  color: var(--paper);
  border-color: var(--line);
}
a.product.card:hover { border-color: var(--paper); }
.product .card-media {
  aspect-ratio: 1;
  background: #ffffff;
  border-bottom: 1px solid var(--line);
}
/* Cut-outs on white — multiply drops the white box onto the plinth.
   The four homepage rasters are now square with their padding baked in at a
   single ratio, so the plate only adds the small amount the tile itself needs;
   1.6rem on top of the baked margin left the part floating in the middle of
   its own tile. */
.product .card-media img,
a.product.card:hover .card-media img {
  object-fit: contain; padding: .8rem;
  mix-blend-mode: multiply; filter: none; transform: none;
}
a.product.card:hover .card-media img { transform: scale(1.03); }
.product .card-body { gap: .55rem; }
.product h3 { font-size: 1.5rem; color: var(--paper); }
.product p { color: var(--muted); font-size: .9375rem; }
/* Name first, price second. The price is the firmest number on the card —
   ink, never accent — it just no longer outranks the product name. */
.price {
  font-family: var(--display); font-weight: 600;
  font-size: 1.875rem; letter-spacing: -.015em;
  color: var(--paper); font-variant-numeric: tabular-nums; line-height: 1;
}
/* A grid of seven amber buttons is accent inflation, so the card action is an
   ink outline. Amber stays with the page's primary action. */
.product .btn {
  margin-top: 1rem; width: 100%;
  padding-inline: 1rem;
  background: transparent; border-color: var(--paper); color: var(--paper);
}
.product .btn:hover { background: var(--paper); border-color: var(--paper); color: #fff; }
.product .btn:active { background: var(--ink-2); border-color: var(--ink-2); color: #fff; }

/* ---------- pricing tiers ---------- */

.tier {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 0;
  padding: 2.5rem 2.25rem;
  display: flex; flex-direction: column;
  transition: border-color var(--t) var(--ease);
}
.tier:hover { border-color: var(--paper); }
.tier.featured { border-color: var(--paper); border-width: 2px; }
.tier.featured::before {
  content: ""; position: absolute; inset: -2px -2px auto; height: 6px;
  background: var(--accent-rule);
}
.tier .amount {
  font-family: var(--display); font-weight: 600;
  font-size: 3.5rem; letter-spacing: -.025em;
  color: var(--paper); line-height: 1; margin: 1rem 0 .25rem;
  font-variant-numeric: tabular-nums;
}
.tier .per {
  font-size: .8125rem; letter-spacing: .108em; text-transform: uppercase;
  color: var(--muted); font-weight: 500;
}
.tier .btn { margin-top: auto; justify-content: center; padding-inline: 1rem; }
.tier .ticks { margin-bottom: 2.5rem; }

/* ---------- quote ----------
   Trust proof. The emphasis is typographic and nothing else: set slightly
   above body size with the opening quotation mark hung into the margin so the
   first line still aligns optically. A coloured bar down the side is a widget
   pretending to be typesetting, and above a hairline it reads as a stripe.

   Deliberately NOT the display face. It shares a row with the band's h2, and
   in uppercase condensed at 28px it read as a second headline competing with
   the 47px one beside it. One focal point per band: the heading is the
   headline, the quote is the evidence under it. */

.quote { position: relative; }
.quote blockquote {
  margin: 0; font-family: var(--body); font-weight: 400;
  text-transform: none;
  font-size: clamp(1.0625rem, 1.35vw, 1.1875rem); line-height: 1.55;
  letter-spacing: 0;
  color: var(--paper);
  text-wrap: pretty; max-width: 58ch;
  text-indent: -.34em;      /* hangs the “ — first line only */
}
.quote cite {
  display: block; margin-top: 1.5rem; font-style: normal; color: var(--muted);
  letter-spacing: .108em; text-transform: uppercase; font-size: .8125rem;
  font-weight: 500;
}
/* The attribution dash is the second typographic signal that this is speech. */
.quote cite::before { content: "\2014\00a0"; }

/* ---------- credential ----------
   For a claim rare enough that it should not be bullet two of four. Emphasis
   is weight and full-strength ink against the --paper-2 copy around it, not a
   rule down the side. */

.credential {
  margin-top: 1.75rem;
  color: var(--paper);
  font-weight: 500;
  font-size: 1.0625rem; line-height: 1.6;
  max-width: 54ch;
}

.rule {
  width: 100%; max-width: 96px; height: 4px; border: 0;
  background: var(--paper);
  margin: 2.5rem 0 0;
}

/* ---------- forms ----------
   Control borders are --line-strong (3.26:1 on white), not the decorative
   hairline: on a form the border IS the control boundary. */

.form { display: grid; gap: 1.2rem; }
.field { display: grid; gap: .5rem; min-width: 0; }
.field input, .field select, .field textarea { width: 100%; min-width: 0; }
.field label {
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .75rem; color: var(--muted);
  line-height: 1.35;
}
.field label span[aria-hidden] { color: var(--paper); }
.opt {
  text-transform: none; letter-spacing: 0; font-weight: 400;
  color: var(--muted-2); font-size: .8125rem;
}

.field input, .field select, .field textarea, .ledger select {
  background: var(--surface-sunk);
  border: 1px solid var(--line-strong);
  color: var(--paper);
  padding: .85rem 1rem;
  font: inherit; font-size: 1rem;
  border-radius: 0;
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease);
}
.field input::placeholder, .field textarea::placeholder { color: var(--muted-2); }
.field input:hover, .field select:hover, .field textarea:hover, .ledger select:hover { border-color: var(--paper); }
.field input:focus, .field select:focus, .field textarea:focus, .ledger select:focus {
  border-color: var(--paper);
  box-shadow: inset 0 0 0 1px var(--paper);
  outline: none;
}
.field input:focus-visible, .field select:focus-visible,
.field textarea:focus-visible, .ledger select:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 1px;
}
.field textarea { min-height: 150px; resize: vertical; line-height: 1.6; }

.field select, .ledger select {
  appearance: none; -webkit-appearance: none;
  padding-right: 2.6rem; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.75 6 6.25 11 1.75' fill='none' stroke='%230d0c0f' stroke-width='1.8' stroke-linecap='square'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
}
.field select option, .ledger select option { background: #fff; color: var(--ink); }

.field input[type="file"] {
  padding: .5rem .55rem; cursor: pointer; color: var(--muted); font-size: .9375rem;
}
.field input[type="file"]::file-selector-button {
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .75rem; color: var(--paper); cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--line-strong);
  border-radius: 0; padding: .6rem 1.1rem; margin-right: .9rem;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.field input[type="file"]::file-selector-button:hover { border-color: var(--paper); background: var(--surface-2); }

.two-up { display: grid; gap: 1.2rem; }
@media (min-width: 620px) { .two-up { grid-template-columns: 1fr 1fr; } }

.three-up { display: grid; gap: 1.2rem; margin-top: 1.2rem; }
@media (min-width: 620px) { .three-up { grid-template-columns: 1.4fr .7fr .9fr; } }

/* Inline checkbox row — "billing same as shipping" and friends. */
.check {
  display: flex; align-items: flex-start; gap: .75rem;
  margin-top: 1.4rem; cursor: pointer;
  font-size: 1rem; line-height: 1.5; color: var(--paper-2);
}
.check input {
  flex: none; width: 20px; height: 20px; margin: .15rem 0 0;
  accent-color: var(--ink); cursor: pointer;
}

.billing {
  margin-top: 1.6rem; padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  display: grid; gap: 1.2rem;
}
.billing h4 { color: var(--paper); }
.billing .three-up { margin-top: 0; }

/* Inline validation messages emitted by the booking and contact forms. */
.err {
  display: block;
  margin-top: .45rem;
  color: var(--danger);
  font-size: .9375rem;
  line-height: 1.45;
  font-weight: 500;
}
.field:has(.err) input,
.field:has(.err) select,
.field:has(.err) textarea,
input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] { border-color: var(--danger); box-shadow: inset 0 0 0 1px var(--danger); }

/* ---------- FAQ ---------- */

.faq details {
  border-bottom: 1px solid var(--line);
  transition: background var(--t) var(--ease);
}
.faq summary {
  cursor: pointer; list-style: none; padding: 1.5rem 3.5rem 1.5rem 0;
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  letter-spacing: -.011em;
  font-size: clamp(1.25rem, 1.8vw, 1.5rem); position: relative;
  color: var(--paper-2); line-height: 1.15;
  transition: color var(--t-fast) var(--ease);
}
.faq summary:hover { color: var(--paper); }
.faq details[open] summary { color: var(--paper); }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+"; position: absolute; right: 0; top: 50%;
  width: 2rem; height: 2rem; margin-top: -1rem;
  display: grid; place-items: center;
  border: 1px solid var(--line-strong); border-radius: 0;
  color: var(--paper); font-family: var(--body); font-size: 1.15rem; line-height: 1;
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease),
              background var(--t) var(--ease);
}
.faq summary:hover::after { border-color: var(--paper); }
.faq details[open] summary::after {
  content: "–"; color: var(--white); background: var(--paper); border-color: var(--paper);
}
.faq details p { padding-bottom: 1.75rem; color: var(--muted); max-width: 68ch; }

/* ---------- site-wide CTA ----------
   The deepest band on the page and the one place red is allowed: the primary
   button here is THE highest-intent action, one per page. */

.cta {
  position: relative;
  background: var(--ink-2);
  color: #fff;
  text-align: center;
  overflow: hidden;
  padding-block: var(--section-y);
}
.cta h2 { max-width: 22ch; margin-inline: auto; color: #fff; }
.cta .eyebrow { color: #c9c9cf !important; }
.cta .btn {
  background: var(--red); border-color: var(--red); color: #fff;   /* 4.62:1 */
}
.cta .btn:hover { background: #ff2b2c; border-color: #ff2b2c; color: var(--ink); }
.cta .btn:active { background: #c50809; border-color: #c50809; color: #fff; }
.cta .btn-ghost {
  background: transparent; color: #fff; border-color: #fff;
}
.cta .btn-ghost:hover { background: #fff; color: var(--ink); border-color: #fff; }
.cta .btn-row { justify-content: center; margin-top: 2.5rem; }

/* proof elements dropped next to the CTA read as part of it, not pasted on */
.cta .quote { text-align: left; }
.cta .quote blockquote,
.cta .stats dt,
.cta .kpi b { color: #fff; }
.cta .quote cite,
.cta .stats dd,
.cta .kpi span { color: #c9c9cf; }
.cta .stats,
.cta .kpi {
  background: rgba(255, 255, 255, .05); border-color: rgba(255, 255, 255, .2);
}
.cta .stats .wrap > div + div { border-left-color: rgba(255, 255, 255, .2); }

/* ---------- colophon + footer ----------
   White colophon between the near-black CTA and the near-black footer: the
   rhythm never lands two identical grounds against each other. */

.colophon,
.colophon.band-bone {
  background: var(--white);
  border-block: 1px solid var(--bone-line);
  padding-block: clamp(3.5rem, 6vw, 5.5rem);
}
.colophon p { color: var(--muted); font-size: 1.0625rem; }
.colophon .est {
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .8125rem; color: var(--muted); margin-top: 1.5rem;
}

.site-footer {
  background: var(--ink);
  color: #d8d8dc;
  padding-block: clamp(3.5rem, 6vw, 5rem) 2rem; font-size: 1rem;
}
.site-footer .cols { display: grid; gap: 2.75rem; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.site-footer h3 {
  font-family: var(--body); font-weight: 500; text-transform: none;
  font-size: .75rem; letter-spacing: .14em; text-transform: uppercase;
  color: #a2a2a9; margin-bottom: 1.4rem;
}
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }
.site-footer li { color: #a2a2a9; }
.site-footer a {
  color: #c9c9cf; text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}
.site-footer a:hover { color: #fff; }
.site-footer .brand { align-items: baseline; color: #fff; }
.site-footer .brand span { font-size: 1.4rem; color: #fff; }
.site-footer .muted { color: #a2a2a9; }
.site-footer .btn-ghost { color: #fff; border-color: #fff; }
.site-footer .btn-ghost:hover { background: #fff; color: var(--ink); }
/* ---- footer bottom bar: a payment strip, then the legal line ----

   The marks were tried once INSIDE the legal sentence and failed twice over:
   18px tiles wedged mid-sentence were illegible, and the sentence lost the
   nouns it needed to parse. The fix is not a smaller logo. It is a different
   component. The bar is now a payment strip in its own right — a short label,
   the marks on their own baseline with room around them, and the copyright
   dropped to a second line underneath. Nothing is inline with prose any more.

   26px is the floor, measured not guessed: below ~13px of artwork the Klarna
   and Affirm wordmarks stop resolving as words, and an unreadable mark is
   strictly worse than the name it replaced. So the bar grew to fit the marks
   rather than the marks shrinking to fit the bar. */
.footer-pay {
  margin-top: 3.5rem; padding-top: 1.75rem; border-top: 1px solid #2b2a31;
  display: flex; flex-wrap: wrap; align-items: center; gap: .7rem 1rem;
}

/* Two fragments of one label, bracketing the marks. Afterpay and Cash App gate
   their wordmarks behind a merchant login, so they are named in words at the
   end of the same label rather than drawn, traced or substituted — see
   assets/img/brands/README.md. Same treatment as the lead-in, so the tail
   reads as the label continuing, not as two text plates among seven logo
   plates. */
.footer-pay-label {
  color: #c9c9cf; font-size: .75rem; letter-spacing: .1em;
  text-transform: uppercase; font-weight: 500; line-height: 1.4;
}

/* Reuse the shop badge row's plate wholesale — white ground, hairline, uniform
   height, brand artwork unaltered — one step down. */
.footer-pay .badge-row { margin-top: 0; gap: .4rem; }
.footer-pay .badge-row .logo:has(img) { height: 26px; padding: 0 .45rem; }
.footer-pay .badge-row .logo img { height: 13px; }
.footer-pay .badge-row .lg-visa img,
.footer-pay .badge-row .lg-mastercard img,
.footer-pay .badge-row .lg-amex img,
.footer-pay .badge-row .lg-discover img { height: 19px; }

.legal {
  margin-top: 1.75rem; padding-top: 1.5rem; border-top: 1px solid #2b2a31;
  display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; justify-content: space-between;
  color: #a2a2a9; font-size: .75rem; letter-spacing: .1em;
  text-transform: uppercase; font-weight: 500;
}
.legal-links { display: flex; flex-wrap: wrap; gap: .6rem 1.25rem; }
.legal-links a { color: #a2a2a9; }
.legal-links a:hover, .legal-links a:focus-visible { color: #fff; }

/* ---------- legal documents ----------
   Privacy, Terms, Refunds. These are the only pages on the site whose body is
   WordPress post content rather than an approved static body, so they get one
   rhythm block instead of the per-band composition every other page uses.
   Everything here is the existing type scale and the existing tokens — the
   point is that a policy page reads as this site, not as a WordPress default. */

.legal-doc { max-width: 72ch; }
.legal-doc > * + * { margin-top: 1.15rem; }
.legal-doc h2 {
  font-size: clamp(1.4rem, 2.6vw, 1.75rem);
  margin-top: 3.25rem; padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}
.legal-doc h3 { font-size: 1.125rem; margin-top: 2rem; }
.legal-doc p, .legal-doc li { color: var(--paper-2); }
/* The 1.1rem margin was on top of the UA's own 40px padding-left, so every
   list here was indented 57.6px — 18% of a 320px screen — and the marker sat
   nowhere near the 40px it had been given. One indent, sized for the marker.
   Longest ordered list on any of the three documents is three items, so 24px
   clears the widest counter these pages can produce. */
.legal-doc ul, .legal-doc ol { margin-left: 0; padding-left: 1.5rem; display: grid; gap: .6rem; }
.legal-doc li::marker { color: var(--muted-2); }
.legal-doc a { color: var(--paper); text-underline-offset: .2em; }
.legal-doc dt { font-weight: 700; color: var(--paper); margin-top: 1rem; }
.legal-doc dd { margin: .25rem 0 0; color: var(--paper-2); }
.legal-doc table { width: 100%; border-collapse: collapse; margin-top: 1.25rem; }
.legal-doc th, .legal-doc td {
  text-align: left; padding: .7rem .9rem;
  border: 1px solid var(--line); vertical-align: top;
  overflow-wrap: break-word;
}
/* `wp_woocommerce_session_` is one unbreakable 23-character token, and a table
   column can never be narrower than its longest word. That one cookie name was
   holding the Cookie column at 289px of a 694px table — so at 768, where the
   column the reader actually wants ("What it is for") had 149px, every row ran
   three times taller than it needed to and the table still overflowed its box
   by 3px. Letting the token break costs nothing (it is a machine name, already
   printed with an ellipsis) and the table drops to 691 wide and half the
   height at 768. `anywhere` rather than `break-word` because only `anywhere`
   is counted in min-content, which is the measurement doing the damage.

   Gated at 800 because min-content is a floor, not a target: with the token
   breakable at ANY width the auto layout stopped reserving room for it at all
   and split `ol_ref_view` across two lines at 1440, where the table has 200px
   going spare. 800 is just above the 772 where the unbroken table stops
   fitting its box, so the break appears exactly when it is the lesser evil.

   Below ~600 the table still scrolls inside its box, and that is deliberate:
   forcing all four columns into 350px was measured too, and it is worse —
   54px columns, prose broken mid-word, and a table 45% TALLER than the one
   that scrolls. Stacking the rows into cards is the real phone answer, and it
   needs `data-label` on every cell plus explicit ARIA roles to keep the header
   associations `display:block` destroys. That is a markup change to a legal
   document, not a stylesheet change. */
@media (max-width: 800px) {
  .legal-doc td code, .legal-doc th code { overflow-wrap: anywhere; }
}
.legal-doc th { background: var(--surface-2); color: var(--paper); font-weight: 700; }
/* A policy table is the one place on this site where content can be wider than
   the viewport. It scrolls inside its own box; the page never does. The box
   carries tabindex="0" so a keyboard can reach the scroll, which means it can
   also take focus and therefore has to show it. */
.legal-doc .table-scroll { overflow-x: auto; }
.legal-doc .table-scroll:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

/* Terms only Aaron can set. Deliberately hideous: this is a publication
   blocker, not a design element, and the whole job of the treatment is that
   nobody can look at the page and fail to notice one. Red on this site is
   otherwise reserved for a single highest-intent button per page; that rule is
   about the design system, and this box is not part of it — it is a note
   telling you the page is not finished. #b3080e carries white at 7.10:1. */
/* `.legal-doc p` is (0,2,0) and would otherwise repaint this box's text in
   body ink on the red ground — a 1.7:1 contrast failure on the one element
   whose entire job is to be read. Both selectors, so the box is self-contained
   wherever it is dropped. */
.ol-todo,
.legal-doc .ol-todo {
  margin-top: 1.5rem; padding: 1rem 1.15rem;
  background: var(--danger); color: #fff;   /* 7.10:1 */
  border-left: 6px solid var(--ink);
  font-weight: 500;
}
.ol-todo strong {
  display: block; color: #fff;
  font-family: var(--display); font-size: 1.05rem;
  letter-spacing: .04em; text-transform: uppercase; margin-bottom: .35rem;
}
/* No opacity dimming: this line is the instruction and it stays at full
   contrast. Italic and smaller is enough of a demotion. */
.ol-todo em { display: block; margin-top: .5rem; font-size: .875rem; color: #fff; }
.ol-todo span { color: #fff; }
.ol-todo a { color: #fff; }

/* A term the client considered and chose to leave unstated. Same box shape as
   .ol-todo so it reads as the same kind of annotation, deliberately not the
   same colour and deliberately not the same class: red means "this page is not
   finished" and the self-check counts every .ol-todo on the page. This one is
   finished — the decision was to say nothing — so it is quiet. Grey rule, no
   alarm. --paper-2 on --surface-2 is 14.54:1. */
.ol-noted,
.legal-doc .ol-noted {
  margin-top: 1.5rem; padding: 1rem 1.15rem;
  background: var(--surface-2); color: var(--paper-2);
  border-left: 6px solid var(--line-strong);
}
.ol-noted strong {
  display: block; color: var(--paper);
  font-family: var(--display); font-size: 1.05rem;
  letter-spacing: .04em; text-transform: uppercase; margin-bottom: .35rem;
}
/* `.legal-doc p` is (0,2,0) and would beat a bare `.ol-noted span`, same trap
   the red box has above. */
.ol-noted span,
.legal-doc .ol-noted span { color: var(--paper-2); }

/* The policy line on a form that collects something — the checkout modal, the
   booking wizard, the contact form. Fine print, but not hidden: it has to be
   legible and its links have to be reachable, so it takes --muted (7.35:1 on
   white, 5.4:1 on the dark modal) rather than a lighter grey. */
.form-legal { margin-top: 1rem; font-size: .82rem; line-height: 1.55; color: var(--muted); }
.form-legal a { color: var(--paper-2); text-underline-offset: .18em; }
.form-legal a:hover, .form-legal a:focus-visible { color: var(--paper); }

/* Admin-only banner at the top of a legal page. Amber edge, the same status
   treatment every other "this is a fact about the shop" notice on the site
   uses — see .pay-off. Never rendered for a logged-out visitor. */
.ol-draft-notice {
  padding: 1.1rem 1.25rem; margin-bottom: 2.5rem;
  border: 1px solid var(--line-strong); border-left: 3px solid var(--amber);
  background: var(--surface-2);
}
.ol-draft-notice b { display: block; color: var(--paper); margin-bottom: .35rem; }
.ol-draft-notice p { margin: 0; color: var(--paper-2); }

/* ---------- utilities ---------- */

.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;
}
.hidden { display: none !important; }
/* Spam honeypot. Positioned off-screen rather than display:none, because the
   crawlers worth catching skip anything the CSS has switched off. Paired with
   aria-hidden and tabindex="-1" on the markup, so it is unreachable by mouse,
   keyboard and screen reader alike — only a bot ever fills it. */
.hp {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}
/* `.card` is display:flex and `.form` is display:grid, both of which beat the
   UA's [hidden] rule. Without this, el.hidden = true silently does nothing. */
[hidden] { display: none !important; }

/* thin square scrollbars on any panel that scrolls */
.daybar, .log, .table-scroll, .modal-inner, .modal, .rail {
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.daybar::-webkit-scrollbar, .log::-webkit-scrollbar,
.table-scroll::-webkit-scrollbar, .modal-inner::-webkit-scrollbar,
.rail::-webkit-scrollbar { width: 8px; height: 8px; }
.daybar::-webkit-scrollbar-track, .log::-webkit-scrollbar-track,
.table-scroll::-webkit-scrollbar-track, .modal-inner::-webkit-scrollbar-track,
.rail::-webkit-scrollbar-track { background: transparent; }
.daybar::-webkit-scrollbar-thumb, .log::-webkit-scrollbar-thumb,
.table-scroll::-webkit-scrollbar-thumb, .modal-inner::-webkit-scrollbar-thumb,
.rail::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 0; }
.daybar::-webkit-scrollbar-thumb:hover, .log::-webkit-scrollbar-thumb:hover,
.table-scroll::-webkit-scrollbar-thumb:hover, .modal-inner::-webkit-scrollbar-thumb:hover,
.rail::-webkit-scrollbar-thumb:hover { background: var(--paper); }

/* ---------- statement band ----------
   Full-bleed photograph, one short line of display type, no body copy. */

.statement {
  position: relative;
  display: grid; place-items: center;
  min-height: clamp(340px, 58vh, 620px);
  padding-block: var(--section-y-lg);
  overflow: hidden;
  text-align: center;
  background-size: cover;
  background-position: center;
  color: #fff;
}
.statement > img,
.statement .hero-bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  background-size: cover; background-position: center;
}
/* Even scrim only — the client's candy paint and gold wire are the product.
   Nothing here desaturates the photograph. */
.statement::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(8, 10, 9, .5), rgba(8, 10, 9, .44));
}
.statement .wrap { position: relative; z-index: 2; }
.statement h2,
.statement p,
.statement .display {
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  font-size: clamp(2rem, 4vw, 2.9375rem);
  line-height: 1.03; letter-spacing: -.011em;
  color: #fff; margin: 0 auto; max-width: 20ch;
}
.statement .eyebrow { justify-content: center; color: #ededf0; margin-bottom: 1.5rem; }
.statement .btn-row { justify-content: center; }

/* No-photo variant: a dark textured band that carries type on its own, for
   slots where we have no honest photograph to use. */
.band-texture,
.statement.no-photo {
  background-color: var(--ink);
  background-image: repeating-linear-gradient(
    135deg, rgba(255, 255, 255, .03) 0 1px, transparent 1px 10px);
}
.band-texture::after, .statement.no-photo::after { content: none; }

/* ---------- editorial two-column ----------
   The workhorse text layout: narrow heading column, wide body column. */

.editorial {
  display: grid; gap: clamp(1.25rem, 3vw, 3.5rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 860px) {
  .editorial { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
}
.editorial-head { position: sticky; top: calc(var(--header-h) + 2rem); }
.editorial-head h2 { max-width: 16ch; }
.editorial-body > * + * { margin-top: 1.2rem; }
.editorial-body p { max-width: 68ch; }
.editorial-body .btn-row { margin-top: 2rem; }
@media (max-width: 859px) {
  .editorial-head { position: static; }
}

/* ---------- card rail ---------- */

.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(280px, 1fr);
  gap: clamp(1rem, 1.6vw, 1.5rem);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--pad);
  padding-bottom: 1.25rem;
  /* bleed the rail to the viewport edge so cards run off the measure */
  margin-inline: calc(var(--pad) * -1);
  padding-inline: var(--pad);
}
.rail > * { scroll-snap-align: start; }
@media (min-width: 1100px) {
  .rail { grid-auto-columns: minmax(320px, 1fr); }
}

/* ---------- section head ---------- */

.section-head { margin-bottom: clamp(2rem, 4vw, 3.25rem); max-width: 60ch; }
.section-head h2 { max-width: 26ch; }

/* The one band on a page allowed to lead: more air above and below. */
.band-lead { padding-block: var(--section-y-lg); }
.band-lead .section-head { max-width: 62ch; }
.band-lead .section-head h2 { max-width: 28ch; }

/* ---------- parts merchandising grid ----------
   Deliberately not `.rail`. Four across at desktop, two at tablet, one on a
   phone — no clipped fourth card, no sideways scroll. */

.parts-grid {
  display: grid;
  gap: clamp(1.25rem, 2.2vw, 2rem);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 560px) { .parts-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .parts-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.parts-foot {
  display: flex; flex-wrap: wrap; gap: 1.5rem 3rem;
  align-items: center; justify-content: space-between;
  margin-top: clamp(2rem, 3.5vw, 3rem);
}
.parts-foot .btn-row { margin-top: 0; }
.parts-foot .badge-row { margin-top: 0; }
.pay-note { margin-top: .8rem; font-size: .9375rem; color: var(--muted); max-width: 46ch; }

/* ---------- before / after wipe ----------
   Geometry is set inline by the widget (it is layout the widget needs to
   function); this is only its chrome. Without JS the frame is two stacked
   captioned photographs, which is a perfectly good fallback. */

.compare { margin: clamp(2rem, 4vw, 3rem) 0 0; }
.compare-frame {
  border: 1px solid var(--line);
  background: var(--surface-2);
}
.compare-label {
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .75rem; line-height: 1;
  padding: .6rem .9rem; margin: .85rem;
  color: var(--ink);
  background: var(--amber);   /* fill carrying near-black text — 11.97:1 */
  border: 0;
}
.compare-label.is-before { background: var(--amber); }
.compare-label.is-after { background: #ffffff; }
/* The handle, its range input and the grip are positioned inline by the
   widget — those inline styles win, so these rules exist only to name the
   parts and to give the fallback (no-JS) state something sane. */
.compare-handle { background: #ffffff; }
.compare-range { appearance: none; -webkit-appearance: none; background: transparent; }
.compare.is-focused .compare-frame { outline: 2px solid var(--paper); outline-offset: 2px; }
.compare-grip {
  width: 34px; height: 34px; border-radius: 0;
  border: 2px solid #fff;
  background: rgba(8, 10, 9, .55);
}
.compare figcaption {
  margin-top: 1.2rem;
  color: var(--muted);
  font-size: 1rem; line-height: 1.6;
  max-width: 72ch;
}
.compare.is-live figcaption::after {
  content: " Drag the handle to wipe between the two.";
  color: var(--muted-2);
}
.compare-before, .compare-after { width: 100%; height: auto; }

/* build.html's pair of finished cars, below the wipe */
.finished-row {
  display: grid; gap: clamp(1.25rem, 2.5vw, 2rem);
  margin-top: clamp(1.5rem, 3vw, 2.5rem);
}
@media (min-width: 860px) { .finished-row { grid-template-columns: 1fr 1fr; } }
.finished { margin: 0; }
.finished img { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
.finished figcaption {
  margin-top: .9rem; font-size: .9375rem; line-height: 1.55; color: var(--muted);
  max-width: 46ch;
}
.finished figcaption b {
  display: block; color: var(--paper);
  font-family: var(--body); font-weight: 500;
  text-transform: uppercase; letter-spacing: .108em; font-size: .75rem;
  margin-bottom: .35rem;
}

/* ===================================================================
   Demo workflows — booking wizard, checkout, referrals, bulk import
   These get clicked live in a pitch, so they are held to product-UI
   standards, not brochure standards.
   =================================================================== */

/* ---------- step indicator ---------- */

.steps {
  display: flex; flex-wrap: wrap; gap: 2px;
  margin: 0 0 2.5rem; list-style: none; padding: 0;
}
.steps li {
  flex: 1 1 150px; padding: .95rem 1.1rem;
  border: 1px solid var(--line-strong);
  border-radius: 0;
  background: var(--surface);
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .08em; font-size: .75rem; color: var(--muted);
  display: flex; gap: .7rem; align-items: center; line-height: 1.2;
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease),
              background var(--t) var(--ease);
}
.steps li b {
  flex: none;
  width: 1.6rem; height: 1.6rem; border-radius: 0;
  display: grid; place-items: center;
  font-size: .75rem; font-weight: 700; letter-spacing: 0;
  color: var(--muted); font-family: var(--body);
  border: 1px solid var(--line-strong); background: var(--surface);
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease),
              background var(--t) var(--ease);
}
.steps li[aria-current="step"] {
  border-color: var(--paper);
  color: var(--paper);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--paper);
}
.steps li[aria-current="step"] b {
  border-color: var(--amber);
  background: var(--amber);
  color: var(--ink);          /* 11.97:1 */
}
.steps li.done { color: var(--paper-2); border-color: var(--paper); }
.steps li.done b {
  font-size: 0;
  border-color: var(--paper);
  background: var(--paper);
  color: #fff;
}
.steps li.done b::before {
  content: ""; display: block;
  width: .95rem; height: .95rem;
  background: currentColor;
  -webkit-mask: var(--check) center / contain no-repeat;
          mask: var(--check) center / contain no-repeat;
}

/* ---------- wizard panels ---------- */

.panel { display: none; }
.panel.active { display: block; animation: fade var(--t-slow) var(--ease); }
@keyframes fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

.panel-box {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 0;
  padding: clamp(1.5rem, 4vw, 3rem);
  overflow: hidden;
}
.panel-box::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 6px;
  background: var(--accent-rule);
}
.panel-box > .panel { padding-top: .5rem; }
.panel-title {
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  font-size: clamp(1.375rem, 2.1vw, 1.75rem); letter-spacing: -.011em;
  line-height: 1.05; color: var(--paper); margin: 0 0 .75rem;
  width: fit-content; max-width: 100%;
}

.wizard-nav {
  display: flex; gap: .8rem; justify-content: space-between; align-items: center;
  margin-top: 2.5rem; padding-top: 1.75rem;
  border-top: 1px solid var(--line);
  flex-wrap: wrap;
}
.wizard-nav .btn { padding-inline: clamp(1.25rem, 3vw, 2.5rem); }

/* ---------- choice tiles (radio cards) ---------- */

.choices { display: grid; gap: .75rem; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.choice { position: relative; }
.choice input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
/* Direct child only. The delivery step's label now nests spans inside <strong>
   (the rate name and .choice-price), and a descendant selector painted each of
   them as its own bordered card — three boxes per option. */
.choice > span {
  position: relative;
  display: block; padding: 1.4rem 3.2rem 1.4rem 1.4rem; height: 100%;
  border: 1px solid var(--line-strong);
  border-radius: 0;
  background: var(--surface);
  cursor: pointer;
  transition: border-color var(--t) var(--ease), background var(--t) var(--ease),
              box-shadow var(--t) var(--ease);
}
.choice > span::after {
  content: ""; position: absolute; top: 1.4rem; right: 1.3rem;
  width: 1.15rem; height: 1.15rem; border-radius: 0;
  border: 1px solid var(--line-strong); background: var(--surface);
  transition: border-color var(--t) var(--ease), background var(--t) var(--ease),
              box-shadow var(--t) var(--ease);
}
.choice:hover > span { border-color: var(--paper); }
.choice strong {
  display: flex; flex-wrap: wrap; gap: .25rem .75rem; align-items: baseline;
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  letter-spacing: -.005em; font-size: 1.375rem; margin-bottom: .4rem;
  color: var(--paper);
}
.choice small { color: var(--muted); font-size: .9375rem; line-height: 1.5; display: block; }
.choice-price {
  margin-left: auto;
  font-family: var(--display); font-weight: 600; font-size: 1.375rem;
  font-variant-numeric: tabular-nums; color: var(--paper);
}
.choice input:checked + span {
  border-color: var(--paper);
  box-shadow: inset 0 0 0 1px var(--paper);
  background: var(--amber-tint);
}
.choice input:checked + span::after {
  border-color: var(--ink);
  background: var(--amber);
  box-shadow: inset 0 0 0 3px var(--ink);
}
.choice input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 3px; }

/* ---------- slots + daybar ---------- */

.slots { display: grid; gap: 2px; grid-template-columns: repeat(auto-fit, minmax(124px, 1fr)); }
.slot {
  padding: .95rem .6rem; text-align: center; cursor: pointer;
  border: 1px solid var(--line-strong);
  border-radius: 0;
  background: var(--surface);
  color: var(--paper);
  font-family: var(--body); font-weight: 500; letter-spacing: .02em;
  font-size: 1rem; line-height: 1.2;
  font-variant-numeric: tabular-nums;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.slot:hover:not(:disabled) { border-color: var(--paper); box-shadow: inset 0 0 0 1px var(--paper); }
.slot[aria-pressed="true"] {
  background: var(--orange);
  color: var(--ink);           /* 6.44:1 */
  border-color: var(--ink);
  font-weight: 700;
}
.slot:disabled {
  opacity: 1; color: var(--muted-2); background: var(--surface-2);
  border-color: var(--line);
  cursor: not-allowed; text-decoration: line-through;
}

.daybar {
  display: flex; gap: 2px; overflow-x: auto;
  padding-bottom: .85rem; margin-bottom: 1.6rem;
  scroll-snap-type: x proximity;
}
.daybar button {
  flex: none; min-width: 104px; padding: .85rem .5rem; cursor: pointer;
  scroll-snap-align: start;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 0;
  color: var(--muted);
  font-family: var(--body); font-weight: 500; font-size: .75rem; letter-spacing: .1em;
  text-transform: uppercase; line-height: 1.4;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.daybar button b {
  display: block; font-family: var(--display); font-weight: 600;
  font-size: 1.75rem; letter-spacing: -.02em; line-height: 1.15;
  color: var(--paper); font-variant-numeric: tabular-nums;
  transition: color var(--t-fast) var(--ease);
}
.daybar button:hover { border-color: var(--paper); color: var(--paper); }
.daybar button[aria-pressed="true"] {
  background: var(--orange);
  border-color: var(--ink); color: var(--ink);
}
.daybar button[aria-pressed="true"] b { color: var(--ink); }

/* ---------- summary / receipt list ---------- */

.summary { list-style: none; margin: 0; padding: 0; font-variant-numeric: tabular-nums; }
.summary li {
  display: flex; justify-content: space-between; gap: 1.5rem;
  padding: .85rem 0; border-bottom: 1px solid var(--line);
  font-size: 1rem; line-height: 1.5;
}
.summary li span:first-child { color: var(--muted); flex: none; max-width: 55%; }
/* `balance` earns its keep on the contact page at 320, where this same
   component carries sentences rather than money: right-aligned values were
   dropping a single word onto the last line ("...central / Indiana",
   "...appointment / preferred"), which is the worst place for an orphan
   because the ragged edge is on the left where the eye returns. No effect on
   the cart, the order summary or the receipt — a one-line value cannot be
   balanced, and every value in those is one line. */
.summary li span:last-child {
  text-align: right; font-weight: 500; color: var(--paper);
  text-wrap: balance;
}
.summary li a { color: var(--paper); text-decoration: underline; text-underline-offset: 3px; }
.summary li.total {
  border-bottom: 0; border-top: 2px solid var(--paper);
  margin-top: .5rem; padding-top: 1.25rem; align-items: baseline;
}
.summary li.total span {
  font-family: var(--display); font-weight: 600;
  font-size: 1.875rem; letter-spacing: -.015em; color: var(--paper);
}
.summary li.total span:first-child { color: var(--paper); }

/* ---------- success block ---------- */

.success {
  position: relative;
  text-align: center; padding: clamp(2rem, 5vw, 3.5rem);
  border: 2px solid var(--ok);
  border-radius: 0;
  background: var(--ok-tint);
  color: var(--paper);
}
.success .mark {
  width: 68px; height: 68px; border-radius: 0; margin: 0 auto 1.5rem;
  background: var(--ok);
  color: #fff;                 /* 5.44:1 */
  display: grid; place-items: center; font-size: 2rem; line-height: 1;
  animation: mark-in var(--t-slow) var(--ease);
}
@keyframes mark-in {
  from { opacity: 0; transform: scale(.85); }
  to { opacity: 1; transform: none; }
}
.success .summary { text-align: left; }

/* ---------- payment ---------- */

/* Stripe's Payment Element mounts into .pay-element and draws its own
   controls, its own method tabs and its own official brand marks. There used
   to be a hand-built radio list, a card form, a BNPL schedule and a wallet
   interstitial here; all four were simulations of things Stripe does properly,
   and all four are gone. What is left is the box they sit in.

   min-height reserves the space the element will take so the panel does not
   jump when Stripe finishes loading. */
.pay-element { margin-top: 1.4rem; min-height: 290px; }

/* Said out loud, not styled as an error: "not configured yet" is a fact about
   the shop, not a mistake the customer made. Amber edge, same as every other
   status on the site. */
.pay-off {
  margin-top: 1.4rem; padding: 1.1rem 1.2rem;
  border: 1px solid var(--line-strong); border-left: 3px solid var(--amber);
  background: var(--bone);
}
.pay-off b { display: block; color: var(--paper); margin-bottom: .4rem; }
.pay-off p { margin: 0; }

/* Nothing is clickable while WooCommerce is being asked a question. */
.modal[aria-busy="true"] .checkout-main { opacity: .55; pointer-events: none; }

/* payment badges — square chip, ink type, hairline edge tinted by brand.
   The brand pastels measure ~1.2:1 on white, so the hue goes to the border
   and the type stays ink. */
.logo,
.badge-row .logo {
  font-family: var(--body); font-weight: 500;
  font-size: .75rem; letter-spacing: .06em; text-transform: uppercase;
  padding: .42rem .75rem; border-radius: 0;
  min-width: 88px; text-align: center; flex: none;
  line-height: 1.25; white-space: nowrap;
  color: var(--paper);
  background: rgba(0, 0, 0, .035);
  border: 1px solid rgba(0, 0, 0, .22);
}
.badge-row .logo { font-size: .6875rem; padding: .38rem .7rem; min-width: 0; }

/* The brand's own mark where the name used to be. Each file ships in its own
   colours and its own container — a deliberate exception to the amber-only
   accent rule and to radius: 0, because a payment mark is only useful if it is
   the one the customer already recognises. Our chip stays around it: the
   hairline and its brand tint are ours, the mark inside is theirs, unaltered.
   `min-width` is untouched, so the checkout's chips still line up in a column.
   Sized in `em` so one rule covers the tall chip and the small badge-row one. */
.logo img { display: block; height: 1.9em; width: auto; }
.logo:has(img) { padding: .3rem .55rem; }

/* ---- the badge row: seven official marks on one shared white plate ----
   The row is a trust signal, not a complete list of methods. It carries each
   brand's own wordmark lockup, never recoloured, cropped, stretched or traced;
   see assets/img/brands/README.md for where every file came from.

   Every mark gets the SAME white plate. Klarna's and Affirm's wordmarks are
   black on transparent and are invisible on the dark photographic hero, and a
   payment strip only reads as one object if all of it shares a ground. The
   plate is ours, the mark inside is theirs. Uniform height, free width: each
   mark sizes on its own aspect ratio rather than being stretched to fit.

   Specificity note: this has to out-rank `.hero .logo` / `.band-dark .logo`
   further down the sheet, which is what `:has(img)` buys — the text chips in
   the checkout selector are untouched by it. */
.badge-row .logo:has(img) {
  display: inline-flex; align-items: center; justify-content: center;
  height: 30px; padding: 0 .5rem; min-width: 0;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, .16);
}
.badge-row .logo img { height: 14px; }
/* The four card networks ship as full-bleed rounded card tiles rather than bare
   wordmarks, so they take the taller tile height the plate allows; at wordmark
   height the type inside them would be unreadable. */
.badge-row .lg-visa img,
.badge-row .lg-mastercard img,
.badge-row .lg-amex img,
.badge-row .lg-discover img { height: 21px; }

.lg-klarna   { border-color: #d1748f; background: rgba(255, 179, 199, .3); }
.lg-afterpay { border-color: #4fbf9c; background: rgba(178, 252, 228, .4); }
.lg-affirm   { border-color: #7a7aff; background: rgba(122, 122, 255, .16); }
.lg-paypal   { border-color: #2f7cc4; background: rgba(143, 196, 245, .26); }
.lg-cashapp  { border-color: #22a94a; background: rgba(85, 224, 122, .22); }
.lg-card     { border-color: #6b6a72; background: rgba(0, 0, 0, .04); }

.badge-row { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; margin-top: 1.5rem; }

/* Tighten the plate on narrow screens, never the marks — legibility is the
   whole point of the row. Without this the five widest plates overflow a 390
   line by ~2px; with it 390 wraps a clean 5 + 2 and never scrolls sideways. */
@media (max-width: 480px) {
  .badge-row { gap: .35rem; }
  .badge-row .logo:has(img) { padding: 0 .375rem; }
}

.bnpl-note {
  margin-top: .8rem; font-size: .9375rem; color: var(--muted);
  font-weight: 500;
}

/* ---------- modal ---------- */

.modal[hidden] { display: none; }
.modal {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(8, 10, 9, .74);
  backdrop-filter: blur(6px) saturate(110%);
  -webkit-backdrop-filter: blur(6px) saturate(110%);
  display: grid; place-items: start center; padding: 1rem;
  overflow-y: auto;
  animation: fade var(--t) var(--ease);
}
.modal-inner {
  /* named because the sticky pay bar has to bleed back out to exactly this
     inset to reach the modal's own edges — see .pay-nav */
  --modal-pad: clamp(1.5rem, 4vw, 2.5rem);
  background: var(--white);
  color: var(--paper);
  border: 0;
  border-top: 6px solid var(--amber);
  border-radius: 0;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, .7);
  max-width: 560px; width: 100%; max-height: 92vh; overflow-y: auto;
  padding: var(--modal-pad); position: relative;
  margin-block: auto;
  animation: modal-in var(--t-slow) var(--ease);
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
.modal-inner h3 { color: var(--paper); }
.modal-close {
  position: absolute; top: .9rem; right: .9rem; z-index: 2;
  width: 40px; height: 40px; cursor: pointer; font-size: 1.35rem; line-height: 1;
  display: grid; place-items: center;
  background: var(--white);
  border: 1px solid var(--line-strong); border-radius: 0;
  color: var(--paper);
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease);
}
.modal-close:hover { border-color: var(--paper); background: var(--paper); color: #fff; }

/* ---------- checkout: five screens ----------
   Wide modal: the form column plus a pinned order summary. The summary is a
   <details> that JS forces open above 900px. */

.checkout { max-width: 1060px; }
.checkout-head { margin-bottom: 2rem; padding-right: 3rem; }
.checkout-head h2 { margin: .5rem 0 1.75rem; }
.checkout-head .steps { margin-bottom: 0; }

.checkout-grid { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); align-items: start; }
@media (min-width: 900px) {
  .checkout-grid:has(> .order-summary:not([hidden])) {
    grid-template-columns: minmax(0, 1fr) 320px;
  }
}
.checkout-main { min-width: 0; display: block; }
.checkout-main .field + .field,
.checkout-main .two-up,
.checkout-main .choices { margin-top: 1.2rem; }
.checkout-main .panel > .muted:first-of-type { margin-bottom: 1.4rem; }

.order-summary {
  border: 1px solid var(--line-strong);
  background: var(--surface-2);
  padding: 1.25rem;
}
@media (min-width: 900px) {
  .order-summary { position: sticky; top: 1rem; }
}
.summary-collapse > summary {
  cursor: pointer; list-style: none;
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .75rem; color: var(--muted);
}
.summary-collapse > summary::-webkit-details-marker { display: none; }
.summary-collapse > summary b {
  font-family: var(--display); font-weight: 600; font-size: 1.5rem;
  letter-spacing: -.015em; color: var(--paper); font-variant-numeric: tabular-nums;
}
.summary-collapse[open] > summary { margin-bottom: 1rem; }
.summary-collapse-body .summary { margin-top: .5rem; }
.summary-collapse-body .muted { margin-top: 1rem; }

.mini-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }
.mini-lines li { display: flex; align-items: center; gap: .75rem; }
.mini-lines img {
  width: 46px; height: 46px; object-fit: contain; flex: none;
  background: #fff; border: 1px solid var(--line); padding: 3px;
  mix-blend-mode: multiply;
}
.mini-name { display: grid; gap: .1rem; min-width: 0; flex: 1; font-size: .9375rem; line-height: 1.3; }
.mini-name small { color: var(--muted); font-size: .8125rem; }
.mini-amount {
  font-variant-numeric: tabular-nums; font-weight: 500; white-space: nowrap;
  color: var(--paper);
}

/* ---------- cart lines ---------- */

.cart-lines { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.cart-line {
  display: grid;
  grid-template-columns: 68px minmax(0, 1fr) auto;
  grid-template-areas:
    "thumb info amount"
    "thumb qty   remove";
  gap: .6rem 1rem; align-items: center;
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--line);
}
.cart-thumb {
  grid-area: thumb;
  width: 68px; height: 68px; object-fit: contain;
  background: #fff; border: 1px solid var(--line); padding: 4px;
  mix-blend-mode: multiply;
}
.cart-line-info { grid-area: info; display: grid; gap: .2rem; min-width: 0; }
.cart-line-info b { font-weight: 700; font-size: 1rem; line-height: 1.3; color: var(--paper); }
.cart-line-amount {
  grid-area: amount; text-align: right; white-space: nowrap;
  font-family: var(--display); font-weight: 600; font-size: 1.375rem;
  letter-spacing: -.015em; color: var(--paper); font-variant-numeric: tabular-nums;
}
.cart-line .qty { grid-area: qty; justify-self: start; }
.cart-line .link-btn { grid-area: remove; justify-self: end; }

.qty { display: inline-flex; align-items: stretch; border: 1px solid var(--line-strong); }
.qty-btn {
  width: 38px; min-height: 38px; cursor: pointer;
  background: var(--surface); border: 0; color: var(--paper);
  font-family: var(--body); font-size: 1.1rem; line-height: 1;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.qty-btn:hover:not(:disabled) { background: var(--paper); color: #fff; }
.qty-btn:disabled { color: var(--muted-2); cursor: not-allowed; }
.qty-input {
  width: 46px; text-align: center; border: 0;
  border-inline: 1px solid var(--line-strong);
  background: var(--surface); color: var(--paper);
  font: inherit; font-size: 1rem; font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}
.qty-input::-webkit-outer-spin-button,
.qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-input:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

.link-btn {
  background: none; border: 0; padding: .25rem 0; cursor: pointer;
  color: var(--muted); font: inherit; font-size: .875rem;
  text-decoration: underline; text-underline-offset: 3px;
  transition: color var(--t-fast) var(--ease);
}
.link-btn:hover { color: var(--danger); }

.cart-empty {
  padding: 2rem 0; color: var(--muted);
  border-block: 1px solid var(--line); margin-bottom: 1.5rem;
}

.pay-nav { align-items: flex-end; }

/* The Pay button is the only control on this site whose position is decided by
   a third party. Stripe's Payment Element renders 652px tall once Link, Cash
   App and Amazon Pay are switched on, and the modal it sits in is capped at
   92vh: on a 734px-tall window the button landed at y=960, a full 255px below
   the modal's bottom edge, so the button that takes the money could only be
   found by scrolling. Nothing here can make the element shorter — it is an
   iframe Stripe owns — so the bar stops scrolling instead.
   Bleeds out to --modal-pad on three sides so it covers the full width of the
   modal and rests flush on its bottom edge; the shadow is what says the form
   continues underneath. `bottom` is negative by the same amount because the
   scrollport pins sticky children at its content edge, one padding above the
   edge we actually want.
   Height-gated: below 480px of viewport the bar would be a third of the modal,
   and a checkout that has already lost is not improved by losing more room. */
@media (min-height: 480px) {
  /* A sticky bar hides whatever the browser scrolls to the bottom of the
     scrollport, and on this screen that is the three legal links directly above
     it: tabbing off the payment element put focus on "Terms & Conditions" at
     y=696 with the bar starting at 622, so the focus ring was behind the bar.
     scroll-padding keeps the scroll-into-view above it. Scoped with :has() so
     the four steps that have no sticky bar keep scrolling exactly as they did;
     12rem clears the tallest the bar gets (189px, stacked, at 390). */
  .modal-inner:has(.panel[data-step="4"].active) { scroll-padding-bottom: 12rem; }
  .pay-nav {
    position: sticky; bottom: calc(-1 * var(--modal-pad)); z-index: 1;
    background: var(--white);
    margin-inline: calc(-1 * var(--modal-pad));
    margin-bottom: calc(-1 * var(--modal-pad));
    padding: 1.25rem var(--modal-pad) var(--modal-pad);
    box-shadow: 0 -16px 24px -20px rgba(0, 0, 0, .6);
  }
}
.pay-cta { display: grid; gap: .55rem; justify-items: end; }
.secure-note {
  font-size: .8125rem; color: var(--muted); display: inline-flex; gap: .35rem;
  align-items: center;
}

/* ---------- receipt ---------- */

.receipt { display: block; }
.receipt-head {
  display: flex; flex-wrap: wrap; gap: .5rem 1.5rem;
  align-items: baseline; justify-content: space-between;
  padding-bottom: 1rem; margin-bottom: 1.25rem;
  border-bottom: 2px solid var(--paper);
}
.receipt-head .panel-title { margin: 0; }
.order-no {
  margin: 0; font-family: var(--mono); font-size: .9375rem; color: var(--muted);
}
.order-no b { color: var(--paper); font-weight: 700; }
.receipt h4 { margin-top: 2.25rem; margin-bottom: .85rem; color: var(--paper); }
.receipt-table { margin-top: 0; }
.next-steps {
  margin: 0; padding-left: 1.35rem; display: grid; gap: .7rem;
  color: var(--muted); font-size: 1rem; line-height: 1.55;
}
.next-steps li::marker { color: var(--muted-2); }
.next-steps b { color: var(--paper); font-weight: 700; }

/* ---------- booking: appointment card ---------- */

.appt-card { display: block; }
.appt-head {
  display: flex; flex-wrap: wrap; gap: .5rem 1.5rem;
  align-items: baseline; justify-content: space-between;
  padding-bottom: 1rem; margin-bottom: 1.25rem;
  border-bottom: 2px solid var(--paper);
}
.appt-ref { margin: 0; font-family: var(--mono); font-size: .9375rem; color: var(--muted); }
.appt-ref b { color: var(--paper); font-weight: 700; }
.appt-body { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); margin-top: 1.75rem; }
@media (min-width: 760px) { .appt-body { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.appt-where h3, .appt-bring h3 { font-size: 1.375rem; margin-bottom: .75rem; }
.appt-address {
  font-size: 1.0625rem; line-height: 1.6;
  color: var(--paper); font-weight: 500;
}
.appt-address a { color: var(--paper); }
.appt-bring { margin-top: 2rem; }
.appt-bring .ticks { margin-top: 1rem; }
.appt-actions {
  display: flex; flex-wrap: wrap; gap: .75rem; align-items: center;
  margin: 2.25rem 0 1.25rem; padding-top: 1.75rem;
  border-top: 1px solid var(--line);
}
.appt-actions .btn { padding-inline: clamp(1.25rem, 3vw, 2.5rem); }

/* ---------- referral ---------- */

.link-box { display: flex; gap: 2px; flex-wrap: wrap; margin-top: 1.4rem; }
.link-box input {
  flex: 1 1 280px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 0;
  color: var(--paper); padding: .95rem 1rem; font: inherit; font-size: 1rem;
  font-family: var(--mono); letter-spacing: -.01em;
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.link-box input:focus {
  outline: none; border-color: var(--paper);
  box-shadow: inset 0 0 0 1px var(--paper);
}
.link-box .btn { padding-inline: 2rem; }

.share-sheet {
  margin-top: 2.5rem; padding: 1.5rem;
  border: 1px solid var(--line-strong);
  background: var(--surface-2);
}
.share-sheet h4 { color: var(--paper); margin-bottom: .4rem; }
.share-row {
  list-style: none; margin: 1.25rem 0 0; padding: 0;
  display: grid; gap: 2px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.share-btn {
  display: flex; align-items: center; gap: .7rem;
  padding: .9rem 1.1rem;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--paper); text-decoration: none;
  font-family: var(--body); font-weight: 500; font-size: .875rem;
  text-transform: uppercase; letter-spacing: .08em; line-height: 1.2;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.share-btn:hover { background: var(--paper); color: #fff; border-color: var(--paper); }
.share-icon { font-size: 1.05rem; line-height: 1; }

.statement-block {
  margin-top: 2.5rem; padding: clamp(1.25rem, 3vw, 2rem);
  border: 1px solid var(--line-strong);
  background: var(--surface);
}
.statement-head {
  display: flex; flex-wrap: wrap; gap: 1rem 2rem;
  align-items: flex-start; justify-content: space-between;
  padding-bottom: 1.25rem; border-bottom: 2px solid var(--paper);
}
.statement-head h3 { margin-bottom: .35rem; }
.statement-balance { text-align: right; display: grid; gap: .2rem; }
.statement-balance b {
  font-family: var(--display); font-weight: 600; font-size: 2.5rem;
  letter-spacing: -.02em; line-height: 1; color: var(--paper);
  font-variant-numeric: tabular-nums;
}

.ledger {
  width: 100%; border-collapse: collapse; margin-top: 1.5rem;
  font-size: .875rem; font-variant-numeric: tabular-nums;
}
.ledger th, .ledger td {
  text-align: left; padding: .75rem .55rem; border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
.ledger th:first-child, .ledger td:first-child { padding-left: 0; }
.ledger th:last-child, .ledger td:last-child { padding-right: 0; }
.ledger th {
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .08em; font-size: .6875rem; color: var(--muted);
  white-space: nowrap;
}
/* Only the column headers carry the heavy rule — a row header (`th scope=row`)
   is a cell and takes the hairline like any other. */
.ledger thead th { border-bottom: 2px solid var(--paper); }
.ledger tbody th { border-bottom: 1px solid var(--line); }
.ledger tbody td:first-child { white-space: nowrap; }
.ledger td { color: var(--paper-2); }
.ledger tbody tr:hover { background: var(--line-soft); }
.ledger tbody tr:last-child td,
.ledger tbody tr:last-child th { border-bottom: 0; }
.ledger tfoot th, .ledger tfoot td {
  border-bottom: 0; border-top: 1px solid var(--line);
  color: var(--paper); font-weight: 500;
}
.ledger tfoot tr.total th, .ledger tfoot tr.total td {
  border-top: 2px solid var(--paper); font-weight: 700;
}
.ledger select { padding: .55rem 2.4rem .55rem .7rem; font-size: .9375rem; width: 100%; min-width: 160px; }
.ledger code {
  font-family: var(--mono); font-size: .875rem;
  background: var(--surface-2); padding: .1rem .35rem;
  border: 1px solid var(--line);
}
.preview-table td, .preview-table th { white-space: nowrap; }
.preview-table td:nth-child(3) { white-space: normal; min-width: 18rem; }
.issues td:nth-child(4), .issues td:nth-child(5) { white-space: normal; min-width: 14rem; }

/* A table column can never be narrower than its widest unbreakable thing, and
   `white-space: nowrap` makes every header exactly that. On the catalog import
   mapping screen the four narrow headers were holding 414px at a 768 viewport,
   which left the notes column — the only one carrying sentences — 214px, and
   rows ran 227px tall to fit three warnings into it. Letting headers wrap where
   the room has run out gives that column 294px and takes 15% off the table's
   height; at 390 it takes 117px off the horizontal scroll.
   Top rather than middle for the same rows: a 562px row with a two-word cell
   centred in it is a column of white space with a word floating in the middle
   of it. Single-line rows, which is every row in the referrals statement and
   the receipt, look identical either way. */
@media (max-width: 900px) {
  .ledger th { white-space: normal; }
  .ledger th, .ledger td { vertical-align: top; }
}

.tag {
  display: inline-block; padding: .28rem .55rem; font-size: .625rem;
  font-family: var(--body); font-weight: 500; letter-spacing: .06em;
  text-transform: uppercase;
  border-radius: 0; border: 1px solid transparent;
  white-space: nowrap; line-height: 1.4;
}
.tag-paid { background: var(--ok-tint); color: var(--ok-ink); border-color: var(--ok); }
.tag-pending { background: var(--amber); color: var(--ink); border-color: var(--ink); }
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ---------- bulk import widget ---------- */

.dropzone {
  border: 2px dashed var(--line-strong);
  border-radius: 0;
  background: var(--surface-2);
  padding: clamp(2rem, 6vw, 3.5rem); text-align: center; cursor: pointer;
  transition: border-color var(--t) var(--ease), background var(--t) var(--ease);
}
.dropzone:hover, .dropzone.over {
  border-color: var(--paper);
  background: var(--amber-tint);
}
.dropzone p { color: var(--paper-2); margin-inline: auto; }
.dropzone .icon {
  width: 58px; height: 58px; margin: 0 auto; border-radius: 0;
  display: grid; place-items: center;
  font-size: 1.4rem; color: var(--paper); line-height: 1;
  background: var(--white);
  border: 1px solid var(--line-strong);
  transition: background var(--t) var(--ease), border-color var(--t) var(--ease);
}
.dropzone:hover .icon, .dropzone.over .icon { border-color: var(--paper); }

.progress {
  height: 10px; border-radius: 0;
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  margin-top: 1.5rem; overflow: hidden;
}
/* Ink, not amber: a progress fill is a status indicator and needs 3:1 against
   its track. Amber on #f4f4f4 measures 1.48:1 and would fail. */
.progress i {
  display: block; height: 100%; width: 0;
  border-radius: 0;
  background: var(--paper);
  transition: width .2s linear;
}

.log {
  margin-top: 1rem; max-height: 200px; overflow-y: auto;
  font-family: var(--mono);
  font-size: .8125rem; line-height: 1.8; color: #c9c9cf;
  background: var(--ink-2);
  border: 1px solid var(--line-strong);
  border-radius: 0;
  padding: 1rem 1.1rem;
}
.log b { color: #fff; font-weight: 500; }
.log .ok { color: #5fd68c; }     /* 5.53:1 on #080a09 */
.log .warn { color: #f0c04a; }   /* 9.94:1 — imported, but something was dropped */
.log .bad { color: #ff8f8f; }    /* 7.31:1 — the row did not import at all */

/* ---------- misc demo ---------- */

.demo-flag {
  display: inline-flex; align-items: center; gap: .5rem;
  background: var(--amber);
  border: 0;
  border-radius: 0;
  color: var(--ink); padding: .5rem 1rem; margin-bottom: 1.5rem;
  font-family: var(--body); font-weight: 500; text-transform: uppercase;
  letter-spacing: .108em; font-size: .75rem; line-height: 1.3;
  max-width: 100%;
}
.demo-flag::before {
  content: ""; width: 8px; height: 8px; border-radius: 0; flex: none;
  background: var(--ink);
}
p.demo-flag { text-transform: none; letter-spacing: 0; font-size: .9375rem; }

.kpis { display: grid; gap: 2px; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.kpi {
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 0;
  padding: 1.4rem;
  display: flex; flex-direction: column; gap: .4rem;
  transition: border-color var(--t) var(--ease);
}
.kpi:hover { border-color: var(--paper); }
.kpi b {
  display: block; font-family: var(--display); font-weight: 600;
  font-size: 2.5rem; letter-spacing: -.025em; color: var(--paper); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.kpi span {
  font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); line-height: 1.5; font-weight: 500;
}

/* ===================================================================
   The ground system
   Light dominates; dark punctuates. Never more than two consecutive
   bands on the same ground. The band classes re-declare surface tokens
   so every component below inherits its ground for free.
   =================================================================== */

/* legacy alias — `.band` predates the ground system and still resolves to
   the dark ground. */
.band,
.band-dark,
.cta,
.hero,
.imgband,
.statement,
.site-footer,
.nav-overlay {
  --surface: #16151a;
  --surface-2: #1f1e25;
  --surface-sunk: #08070a;
  --line: #2b2a31;
  --line-strong: #56555c;
  --line-soft: rgba(255, 255, 255, .06);
  --hair: none;
  --ink-3: #16151a;
  --paper: #ffffff;        /* 19.51:1 on #0d0c0f */
  --paper-2: #d8d8dc;      /* 14.4:1  on #0d0c0f */
  --muted: #a2a2a9;        /*  7.69:1 on #0d0c0f */
  --muted-2: #8e8e96;      /*  6.00:1 on #0d0c0f */
  --graphite: #a2a2a9;
  --focus: #ffffff;
  --danger: var(--danger-lite);
  --ok: #5fd68c;
  --ok-tint: rgba(95, 214, 140, .12);
  --ok-ink: #5fd68c;
  --amber-tint: rgba(255, 193, 7, .14);
  --accent-rule: #ffc107;
  --shadow-2: none;
  --shadow-3: none;
}

.band,
.band-dark {
  background: var(--ink);
  color: var(--paper);
}
/* second dark in a row: raise it a step so the seam is visible */
.band-dark + .band-dark,
.band-dark + .band { background: var(--ink-4); }
/* third drops back to base, so a dark run alternates and no two touching
   bands ever sit on the same ground. */
.band-dark + .band-dark + .band-dark { background: var(--ink); }

/* ---------- the light ground ----------
   The root is already light, so this block exists for two reasons: it names
   the alternate light band, and it RESETS the tokens when a light band is
   nested inside — or follows — a dark context. Any component dropped inside
   inherits the light treatment for free. */

.band-bone {
  --surface: #ffffff;
  --surface-2: #f4f4f4;
  --surface-sunk: #ffffff;
  --line: #dcdcde;
  --line-strong: #8e8e94;
  --line-soft: rgba(0, 0, 0, .05);
  --hair: none;
  --ink-3: #ebebec;
  --paper: #0d0c0f;        /* 18.67:1 on #f4f4f4 */
  --paper-2: #26252b;      /* 14.54:1 on #f4f4f4 */
  --muted: #56555c;        /*  7.05:1 on #f4f4f4 */
  --muted-2: #6b6a72;      /*  5.11:1 on #f4f4f4 */
  --graphite: #56555c;
  --focus: #0d0c0f;
  --danger: #b3080e;       /*  6.46:1 on #f4f4f4 */
  --ok: #0f7a37;
  --ok-tint: #e6f4ea;
  --ok-ink: #0f5f2b;
  --amber-tint: #fff6da;
  --accent-rule: #0d0c0f;

  background: var(--bone);
  color: var(--paper);
}
/* Two light bands in a row take the two different whites, so consecutive
   light sections never read as one repeated component. */
.band-bone + .band-bone,
.band-bone.alt { background: var(--bone-warm); }

/* Ground stated in the markup, not inferred from a sibling selector.
   The homepage runs light bands three deep now, so the sequence has to be
   readable off the HTML; the adjacency rule above only knows how to alternate
   once. These come after it and therefore win. Names follow the direction
   contract: --paper #ffffff, --paper-2 #f4f4f4. */
.band-bone.on-paper   { background: var(--bone-warm); }   /* #ffffff */
.band-bone.on-paper-2 { background: var(--bone); }        /* #f4f4f4 */

/* Seam between two light bands. Two full section paddings stack into ~256px
   of undifferentiated white that reads as an unfinished band rather than as
   air, so both sides of the seam give some back. A light band that meets a
   DARK one keeps its full padding — there the colour change draws the seam
   and the air is doing real work. */
.band-bone:has(+ .band-bone) { padding-bottom: clamp(3rem, 5vw, 5.5rem); }
.band-bone + .band-bone      { padding-top:    clamp(2.5rem, 4vw, 4.5rem); }

/* Components that need a nudge back on the light ground. Everything else
   inherits from the token block above. */
.band-bone .eyebrow { color: var(--muted); }
.band-bone .card { background: var(--surface); }
.band-bone .field input,
.band-bone .field select,
.band-bone .field textarea { background: #ffffff; }
.band-bone .field select option, .band-bone .ledger select option { background: #fff; color: var(--ink); }
.band-bone .log { color: #c9c9cf; }
.band-bone ::selection { background: var(--amber); color: var(--ink); }

/* Components that need a nudge on the dark ground. */
.band-dark .field select,
.band .field select,
.band-dark .ledger select,
.band .ledger select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.75 6 6.25 11 1.75' fill='none' stroke='%23ffffff' stroke-width='1.8' stroke-linecap='square'/%3E%3C/svg%3E");
}
.band-dark .field select option,
.band .field select option,
.band-dark .ledger select option { background: #16151a; color: #fff; }
.band-dark .field input,
.band-dark .field select,
.band-dark .field textarea,
.band .field input,
.band .field select,
.band .field textarea { background: var(--surface-sunk); }
.band-dark .pay input,
.band .pay input,
.band-dark .check input { accent-color: #fff; }
.band-dark .steps li[aria-current="step"],
.band .steps li[aria-current="step"] { box-shadow: inset 0 0 0 1px #fff; }
.band-dark .steps li.done b,
.band .steps li.done b { background: #fff; color: var(--ink); }
.band-dark .choice input:checked + span::after,
.band .choice input:checked + span::after { box-shadow: inset 0 0 0 3px var(--ink); }
.band-dark .qty-btn:hover:not(:disabled),
.band .qty-btn:hover:not(:disabled) { background: #fff; color: var(--ink); }
.band-dark .share-btn:hover,
.band .share-btn:hover { background: #fff; color: var(--ink); }
.band-dark .modal-inner,
.band .modal-inner { background: #fff; }
.band-dark .progress i,
.band .progress i { background: #fff; }
.band-dark .dropzone .icon,
.band .dropzone .icon { background: var(--surface-2); }
.band-dark .tag-paid,
.band .tag-paid { background: rgba(95, 214, 140, .14); color: #5fd68c; border-color: #5fd68c; }
.band-dark .faq details[open] summary::after,
.band .faq details[open] summary::after { background: #fff; color: var(--ink); border-color: #fff; }
.band-dark .filters button[aria-pressed="true"],
.band-dark .tabs button[aria-pressed="true"],
.band .filters button[aria-pressed="true"] { color: #fff; border-bottom-color: #fff; }
/* payment chips on the dark ground — hue on the edge, type stays white */
.band-dark .logo,
.band .logo,
.hero .logo {
  color: #fff;
  background: rgba(255, 255, 255, .07);
  border-color: rgba(255, 255, 255, .3);
}
.band-dark .lg-klarna,   .hero .lg-klarna   { border-color: #ffb3c7; }
.band-dark .lg-afterpay, .hero .lg-afterpay { border-color: #b2fce4; }
.band-dark .lg-affirm,   .hero .lg-affirm   { border-color: #a8a8ff; }
.band-dark .lg-paypal,   .hero .lg-paypal   { border-color: #8fc4f5; }
.band-dark .lg-cashapp,  .hero .lg-cashapp  { border-color: #55e07a; }
.band-dark .lg-card,     .hero .lg-card     { border-color: #ddd9d1; }

/* The modal is a light object wherever it is opened from, so it re-declares
   the light tokens the same way `.band-bone` does. */
.modal-inner {
  --surface: #ffffff;
  --surface-2: #f4f4f4;
  --surface-sunk: #ffffff;
  --line: #dcdcde;
  --line-strong: #8e8e94;
  --line-soft: rgba(0, 0, 0, .05);
  --paper: #0d0c0f;
  --paper-2: #26252b;
  --muted: #56555c;
  --muted-2: #6b6a72;
  --graphite: #56555c;
  --focus: #0d0c0f;
  --danger: #b3080e;
  --ok: #0f7a37;
  --ok-tint: #e6f4ea;
  --ok-ink: #0f5f2b;
  --amber-tint: #fff6da;
  --accent-rule: #0d0c0f;
}
.modal-inner .logo {
  color: var(--paper);
  background: rgba(0, 0, 0, .035);
  border-color: rgba(0, 0, 0, .22);
}
.modal-inner .lg-klarna   { border-color: #d1748f; background: rgba(255, 179, 199, .3); }
.modal-inner .lg-afterpay { border-color: #4fbf9c; background: rgba(178, 252, 228, .4); }
.modal-inner .lg-affirm   { border-color: #7a7aff; background: rgba(122, 122, 255, .16); }
.modal-inner .lg-paypal   { border-color: #2f7cc4; background: rgba(143, 196, 245, .26); }
.modal-inner .lg-cashapp  { border-color: #22a94a; background: rgba(85, 224, 122, .22); }
.modal-inner .lg-card     { border-color: #6b6a72; background: rgba(0, 0, 0, .04); }

/* ---------- narrow-screen adjustments ---------- */

/* Must sit after the hero block: same specificity, so source order decides. */
@media (max-width: 900px) {
  /* Below about 900px the copy spans the full width and most of the hero's height,
     so there is no cool side of the frame to hide in. Measured worst case
     against a pure-white region of the photograph: 4.8:1 where the lede
     starts. Luminance drops; hue and saturation are untouched. */
  .hero::after {
    background:
      linear-gradient(180deg, rgba(8, 10, 9, .5) 0%, rgba(8, 10, 9, .58) 12%, rgba(8, 10, 9, .72) 28%, rgba(8, 10, 9, .8) 58%, rgba(8, 10, 9, .9) 100%);
  }
}

/* The home hero is the only one whose height is set by its CONTENT: the intake
   strip lives inside it, and on a phone that strip stacks to five rows. The box
   goes to 390x1092 — taller than it is wide by a factor of three — and
   `cover` on a landscape photograph then shows a 27%-wide vertical slice of it.
   On the car this shipped with that slice is bare trees and a windscreen.

   So below 560 the photograph stops being the hero's whole background and
   becomes a band at the top of it, at the same height every OTHER hero's
   photograph already gets (`.hero-sub`'s min-height, deliberately the same
   expression). That holds the crop at ~72% of the frame on any phone, which is
   what the sub-page heroes have always shown, and it is a ratio not a focal
   point — it survives Aaron swapping the photograph for one of any shape.
   The rest of the hero falls through to `.hero`'s own --ink ground.

   The mask is what makes the band a band rather than a cut edge: the scrim
   above happens to be ~.75 opaque where this photograph ends, so the seam is
   invisible today, but a swapped-in photograph with sky at that line would
   show a hard rule. Fading the last fifth costs three lines and removes the
   whole class of problem.

   560 was the first candidate — it is where the header collapses — but 561 and
   620 still showed a roof, a windscreen and one wheel (52-58% of the frame),
   and 640 was the first width where cover put both wheels and the whole body
   in shot. 620 is the existing breakpoint on the near side of that. */
@media (max-width: 620px) {
  .hero-center .hero-bg {
    bottom: auto;
    height: clamp(340px, 48vh, 520px);
    -webkit-mask-image: linear-gradient(180deg, #000 78%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 78%, transparent 100%);
  }
}

@media (max-width: 620px) {
  /* five full-width step rows pushed 300px of chrome above the content */
  .steps li { flex: 1 1 auto; padding: .6rem .6rem; font-size: .6875rem; gap: .5rem; }
  .steps li b { width: 1.35rem; height: 1.35rem; font-size: .6875rem; }
  .cart-line {
    grid-template-columns: 56px minmax(0, 1fr);
    grid-template-areas:
      "thumb info"
      "thumb amount"
      "qty   remove";
    row-gap: .7rem;
  }
  .cart-thumb { width: 56px; height: 56px; }
  .cart-line-amount { text-align: left; grid-area: amount; }
  .cart-line .qty { justify-self: start; }
  .wizard-nav { gap: .75rem; }
  .wizard-nav .btn { flex: 1 1 100%; }
  .pay-cta { justify-items: stretch; width: 100%; }
  .pay-cta .btn { width: 100%; }
  .secure-note { justify-content: center; }
  .appt-actions .btn, .btn-row .btn { flex: 1 1 100%; }
  .statement-balance { text-align: left; }
  .checkout-head { padding-right: 2.75rem; }
  /* `:not()` because this rule is 160 lines LATER than the one at max-width 560
     that gives the header's Book Now its compact padding, and at equal
     specificity source order decides — so for every phone width this quietly
     put 32px back on the one button that has to share a row with a centred
     emblem, a burger and a cart. The header rule had never once applied. */
  .btn:not(.header-cta) { padding-inline: 2rem; }
  /* At full size the two wipe chips collide across a 350px frame. */
  .compare-label {
    font-size: .625rem; letter-spacing: .06em;
    padding: .45rem .6rem; margin: .5rem;
  }
}

/* ===================================================================
   Feedback motion
   Three moments, all of them confirming an action the user just took.
   Deliberately no scroll-reveals, no parallax, no hover lifts and no
   staggered bands: nine sections fading up on scroll is the single most
   recognisable tell of a template, and two review rounds went into
   removing exactly that character. The one authored moment on the site
   is the before/after wipe, and it lives in main.js.
   =================================================================== */

/* Intake hand-off. The strip form navigates to book.html, and it used to do
   it before the button had visibly acknowledged anything. main.js holds the
   navigation for 130ms and this is what it holds: the button's own :active
   fill (set with it up at .btn:active) plus a pixel of travel. */
.btn.is-sending { transform: translateY(1px); }

/* Shop filters. Same grid narrowed, not a different grid. */
#products { transition: opacity 90ms linear; }
#products.is-swapping { opacity: 0; }

/* Slot selection. The attribute flip is the trigger, so this needs no JS at
   all: the animation starts the moment the selector begins matching. */
.slot[aria-pressed="true"] { animation: slot-ack 150ms var(--ease); }
@keyframes slot-ack {
  from { transform: scale(.965); }
  to   { transform: none; }
}

/* ---------- reduced motion ----------
   Restated here because the ground system adds transitions after the
   original block. */
@media (prefers-reduced-motion: reduce) {
  /* the global .01ms duration override would otherwise pin these
     scroll-driven animations to their end state */
  .site-header, .site-header .wrap, .brand img { animation: none !important; }
  .nav a:hover { padding-left: 0; }
  .rail { scroll-snap-type: none; }
  html { scroll-behavior: auto; }
  .slot[aria-pressed="true"] { animation: none; }
  #products.is-swapping { opacity: 1; }
}

/* Social row: four platform marks inline, on light and dark grounds alike.
   Monochrome and `currentColor` on purpose — they take the colour of the text
   they replaced instead of reading as a row of coloured stickers. Each link
   carries its platform name in `.sr-only`, so an icon-only link is still
   announced. The hover wash works on both grounds; `--line-soft` flips with
   the ground the same way the type colour does. */
.social { display: flex; flex-wrap: wrap; gap: .25rem .35rem; align-items: center; }
.social a {
  position: relative;
  display: inline-flex;
  text-decoration: none;
  padding: .3rem;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.social a:hover { background: var(--line-soft, rgba(0, 0, 0, .05)); }
.social svg { display: block; width: 1.15em; height: 1.15em; fill: currentColor; }
.summary .social { justify-content: flex-end; text-align: right; }

/* Before/after band: the frame is full width, but the caption (72ch) and the
   note (54ch) stacked beneath it left a dead gutter of ~500px on the right.
   Set them side by side under the image so the band fills the measure it has.
   `display: contents` lifts the figure's children into the section grid; the
   figcaption stays inside <figure> so the caption/image association survives. */
@media (min-width: 900px) {
  .band-compare .wrap {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    column-gap: clamp(2rem, 4vw, 4rem);
    align-items: start;
  }
  .band-compare .wrap > :not(.compare):not(.credential) { grid-column: 1 / -1; }
  .band-compare .compare { display: contents; }
  .band-compare .compare-frame { grid-column: 1 / -1; }
  .band-compare .compare figcaption { grid-column: 1; max-width: none; margin-top: 1.6rem; }
  .band-compare .credential { grid-column: 2; margin-top: 1.6rem; max-width: none; }
}

/* ===================================================================
   SOCIAL BANDS — Instagram, TikTok, YouTube
   ===================================================================
   One heading system, three compositions. The whole idea of the section
   is that each platform's native aspect ratio drives its own band:
   Instagram 1:1, TikTok 9:16, YouTube 16:9. Forcing three feeds into one
   shared grid is what makes a page read as a widget dump; respecting the
   three shapes is what makes it read as art direction.

   No plugin CSS is loaded anywhere on the site (see the dequeue in
   mu-plugins/oldslegend-social.php), so nothing below is an override —
   these are the only rules that exist for this markup.

   Amber appears on the play affordance and nowhere else in this block.
   Not on borders, not on the source line, not on the Follow button.
   =================================================================== */

/* Named `.social-band`, not `.social`: `.social` is already the footer's
   inline link list further up this sheet. */
.social-band .social-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem 2rem;
  margin-bottom: clamp(2rem, 4vw, 3.25rem);
}
.social-band .social-head h2 { max-width: 22ch; }
/* .btn-row's standing top margin is for a button under prose; here the row
   is a sibling of the heading block, already spaced by the flex gap. */
.social-band .social-head .btn-row { margin-top: 0; }

/* The platform is a footnote, not a heading. Same treatment as a byline:
   small, tracked, muted, sitting under the line it credits. */
.social-band .social-src {
  margin-top: .7rem;
  font-size: .8125rem;              /* 13px, the button scale */
  font-weight: 500;
  letter-spacing: .108em;
  text-transform: uppercase;
  color: var(--muted);
}
.social-band h2 + .social-src { margin-top: .7rem; }

.social-grid {
  list-style: none;
  margin: 0;
  padding: 0;
}
.social-grid-square {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(.5rem, 1.1vw, 1rem);
}

/* ---- the tile: one shape, three aspect ratios ---- */
.social-tile {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  border-radius: 0;                 /* load-bearing: square corners */
  background: var(--surface-2);
  overflow: hidden;
  text-decoration: none;
  cursor: pointer;
}
.social-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
  transition: opacity var(--t) var(--ease);
}
.social-tile:hover img { opacity: .82; }
.social-tile:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

.social-grid-square .social-tile { aspect-ratio: 1 / 1; }

/* ---- TikTok: 9:16 portrait cards on a snap rail ----
   .rail is the site's existing snap-scroll container; only the card width
   changes, because a 9:16 card wants to be narrower than the 280px the
   rail defaults to. Tabbing between cards scrolls the rail natively, so
   there is no JavaScript and no extra tab stop here. */
.social-rail {
  grid-auto-columns: minmax(210px, 18vw);
  align-items: start;
}
.social-rail li { min-width: 0; }
.social-rail .social-tile { aspect-ratio: 9 / 16; }

/* ---- YouTube: one large 16:9 hero, two smaller beside it ----
   Long-form earns a hero; a grid of equal thumbnails wastes it.

   The two small tiles carry the 16:9 and the hero stretches to the height
   they add up to. Doing it the other way round — aspect-ratio on the
   spanning hero — leaves the row track sized by the small images' own
   intrinsic ratio instead, and the two columns end at different heights.
   At 2fr/1fr the hero lands within ~3% of 16:9, which object-fit absorbs. */
.social-yt {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: clamp(.75rem, 1.4vw, 1.25rem);
}
.social-yt-hero {
  grid-column: 1;
  grid-row: 1 / 3;
  min-height: 0;
}
.social-yt-rest {
  grid-column: 2;
  aspect-ratio: 16 / 9;
  min-height: 0;
}
.social-yt .social-tile { display: flex; }

/* Title sits over the still. The scrim is the only reason it passes AA on
   an arbitrary video frame, so it is drawn on the tile, not the text. */
.social-yt .social-cap {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  padding: 2.75rem .95rem .85rem;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1rem, 1.35vw, 1.375rem);
  line-height: 1.05;
  letter-spacing: -.011em;
  text-transform: uppercase;
  text-align: left;
  color: #fff;
  background: linear-gradient(to top, rgba(8, 7, 10, .92) 0%, rgba(8, 7, 10, .72) 45%, rgba(8, 7, 10, 0) 100%);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.social-yt-rest .social-cap {
  padding: 2rem .7rem .6rem;
  font-size: clamp(.875rem, 1vw, 1rem);
}

/* ---- the play affordance: the one amber in this section ----
   A square amber plate with an ink triangle. Ink on amber is 11.97:1.
   Drawn with a clip-path rather than a glyph so the triangle matches the
   square-capped geometry everything else in this world is drawn with. */
.social-play {
  position: absolute;
  z-index: 2;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: clamp(2.75rem, 4vw, 3.75rem);
  aspect-ratio: 1;
  background: var(--amber);
  border-radius: 0;
  transition: background var(--t) var(--ease), scale var(--t) var(--ease);
}
.social-play::after {
  content: "";
  position: absolute;
  left: 54%;
  top: 50%;
  translate: -50% -50%;
  width: 32%;
  aspect-ratio: 1 / 1.15;
  background: var(--ink);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
.social-tile:hover .social-play { background: var(--orange); scale: 1.06; }
.social-yt .social-play { top: 44%; }

/* On the rail every card is a video, so a centred plate on each one stops
   reading as an affordance and starts reading as a repeated pattern over
   the photography. Corner-anchored and smaller: still the one amber in the
   band, still says "video", no longer competes with the cars. */
.social-rail .social-play {
  left: 0;
  top: auto;
  bottom: 0;
  translate: none;
  width: clamp(1.75rem, 2.2vw, 2.25rem);
}

/* The facade replaces itself with the player on click, so the iframe becomes
   a direct child of the cell and inherits the box the thumbnail had. */
.social-yt > * > iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 0;
  background: var(--ink);
}

/* ---- narrow ---- */
@media (max-width: 900px) {
  .social-yt {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
  }
  .social-yt-hero { grid-column: 1; grid-row: auto; }
  .social-yt-rest { grid-column: 1; aspect-ratio: 16 / 9; }
}
@media (max-width: 640px) {
  .social-band .social-head { align-items: flex-start; }
  /* Three squares across a 390px viewport are 110px each — too small to
     read a car in. Two is the honest answer. */
  .social-grid-square { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .social-rail { grid-auto-columns: minmax(150px, 44vw); }
}

/* Nothing here autoplays and nothing animates on load; this covers the
   hover scale and the rail's smooth scrolling for anyone who asked not to
   see movement. The global reduce block already zeroes transitions. */
@media (prefers-reduced-motion: reduce) {
  .social-tile:hover .social-play { scale: 1; }
  .social-rail { scroll-behavior: auto; }
}
