/* ══════════════════════════════════════════════════════════════
   DIAMOND WRAPS — automotive appearance, protection & enhancement
   "Night shop": a near-black room, graphite surfaces, one warm
   brass accent used sparingly. The work is photographed under
   hard light; the interface stays out of its way.
   Type: Archivo — one family, width and weight carrying the
   hierarchy. Wide for display, normal for text.
   ══════════════════════════════════════════════════════════════ */

/* ── 1. TOKENS ─────────────────────────────────────────────── */
:root{
  /* The surface IS the colour: a drenched near-black with a cool
     lean, so photography and brass are the only warm things on screen. */
  --void:   oklch(0.152 0.009 258);   /* page — the deepest tone */
  --pitch:  oklch(0.198 0.010 258);   /* raised panel */
  --slab:   oklch(0.242 0.011 258);   /* card */
  --rise:   oklch(0.268 0.012 258);   /* alternating band — visibly lighter */
  --rise-2: oklch(0.318 0.013 258);   /* card on that band */
  --steel:  oklch(0.330 0.012 258);   /* hairline / divider */
  --steel-2:oklch(0.420 0.014 258);

  /* the one light room, used deliberately and rarely */
  --day:    oklch(0.968 0.003 258);
  --day-2:  oklch(0.925 0.004 258);

  /* ink on dark */
  --bright: oklch(0.985 0.002 258);
  --ink:    oklch(0.905 0.004 258);
  --muted:  oklch(0.700 0.008 258);
  --faint:  oklch(0.560 0.009 258);
  /* ink on light */
  --dark:   oklch(0.200 0.010 258);
  --dark-2: oklch(0.420 0.010 258);

  /* Accent. Deliberately almost colourless: this shop sells colour, so the
     interface should not compete with the paint. Light is the call to action;
     a cool steel tint marks everything else. */
  --signal: oklch(0.965 0.003 258);   /* CTA fill — light on dark */
  /* Marks and rules are near-neutral: the paint is the only colour that
     should read as colour. Gold is kept for the handful of moments that are
     an offer — free, included, recommended — where warmth earns its place. */
  /* Diamond Wraps brand blue, sampled from the logo wordmark (#A3DBFF).
     Kept to a low chroma so it reads as the house mark, not as paint. */
  --dw:     oklch(0.740 0.130 240);
  --dw-d:   oklch(0.650 0.140 243);
  /* Derived, not restated: a theme sets --dw and --dw-d and these follow, so
     swapping the accent is two lines rather than four. */
  --dw-t:   oklch(from var(--dw) l c h / .16);
  --dw-r:   oklch(from var(--dw) l c h / .34);

  --brass:  oklch(0.800 0.016 258);
  --brass-d:oklch(0.720 0.016 258);
  --brass-t:oklch(0.800 0.016 258 / .12);
  --brass-r:oklch(0.800 0.016 258 / .26);
  /* Green carries every positive in the interface: a tick, a price that is
     included, a warranty that costs nothing. It is the one hue allowed to
     mean something, which is why it is not spent on decoration. */
  --go:     oklch(0.760 0.170 152);
  --go-d:   oklch(0.660 0.160 152);
  --go-t:   oklch(0.760 0.170 152 / .13);
  --go-r:   oklch(0.760 0.170 152 / .34);
  --no:     oklch(0.640 0.140 25);
  --gold:   oklch(0.800 0.115 82);
  --gold-d: oklch(0.700 0.110 78);
  --gold-t: oklch(0.800 0.115 82 / .12);

  --good:   oklch(0.720 0.130 155);
  --bad:    oklch(0.640 0.170 27);

  /* type — one family, width does the work */
  /* Chivo. A grotesque drawn for headlines rather than for interfaces, so
     it has some grit in it — the flat-sided a, the tailed g — instead of
     the smooth neutral face that every generated page arrives wearing. */
  --disp: "Chivo", ui-sans-serif, system-ui, sans-serif;
  /* The hero is a sign above a workshop, not a headline in a deck. Bebas is
     the face that gets painted on those: condensed caps, no tracking games,
     and it cannot be set in the wide tight sentence case that made the old
     headline read like every other generated landing page. */
  --sign: "Bebas Neue", "Chivo", "Arial Narrow", sans-serif;
  --text: "Chivo", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --wide: 100;     /* Chivo has no width axis; the old 118 was Archivo's */
  --norm: 100;

  /* rhythm */
  --s1:.25rem; --s2:.5rem; --s3:.75rem; --s4:1rem;
  --s5:1.5rem; --s6:2rem;  --s7:3rem;   --s8:5rem; --s9:7rem;
  --pad: clamp(1.15rem, 4.5vw, 4rem);
  --maxw: 1280px;
  /* The hero is the one full-bleed showcase on the page, so it is allowed to
     grow past the reading measure the other sections share. Never narrower
     than --maxw, and it stops widening at 1600 so the two columns cannot drift
     so far apart that they stop reading as one composition. */
  /* Wide enough that a big monitor is not left with a dead band down one
     side, but not so wide that the headline ends up hard against the left
     edge and the car hard against the right. The max() this used to carry
     forced the container to at least 1280 even on a 1280 screen, which left
     the whole composition running edge to edge with no gutter at all. */
  --herow: min(92vw, 1560px);
  --nav-h: 68px;

  /* Corners stay tight. Big radii on everything is the tell of a
     template; a premium automotive surface is mostly square. */
  /* Radius. The old scale stopped at 6px, so every tile and swatch on the
     site reached past it for a one-off 9/11/13/14px — the value the design
     actually wanted was simply missing. These are those values, promoted to
     tokens, on a ~1.45 ratio so nesting reads correctly. */
  --r-xs: 5px;    /* ticks, hairline chips */
  --r-sm: 9px;    /* inputs, small controls, swatch ticks */
  --r:    13px;   /* cards, tiles, media */
  --r-lg: 20px;   /* large panels, the form */
  --r-xl: 30px;   /* hero-scale surfaces */

  /* elevation — on near-black, depth comes from a lighter edge and a
     deep shadow, not from a grey box */
  --edge-1: inset 0 1px 0 oklch(1 0 0 / .045);
  --lift-1: 0 1px 2px oklch(0 0 0 / .5);
  --lift-2: 0 10px 30px -12px oklch(0 0 0 / .7);
  --lift-3: 0 30px 70px -28px oklch(0 0 0 / .85);
  --lift-4: 0 50px 110px -40px oklch(0 0 0 / .9);

  /* motion — ease-out only, no bounce */
  --e:      cubic-bezier(.16,1,.3,1);
  --e-io:   cubic-bezier(.65,0,.35,1);
  --spring: cubic-bezier(.16,1,.3,1);
  --t-1: 180ms; --t-2: 320ms; --t-3: 620ms;

  /* z */
  --z-dock:200; --z-nav:300; --z-drawer:400; --z-top:500;

  /* legacy aliases — the component layer still speaks the old names.
     Mapping them here repoints the whole site without touching every rule. */
  --paper:var(--void); --mist:var(--rise); --cloud:var(--rise-2);
  --edge:var(--steel); --edge-2:var(--steel-2);
  --night:var(--void); --night-2:var(--pitch); --night-t:var(--muted);
  --slate:var(--muted); --mute:var(--faint);
  --blue:var(--dw); --blue-d:var(--dw-d);
  --blue-t:var(--dw-t); --blue-2:var(--dw-r); --blue-r:var(--dw-r);
  --sun:var(--brass); --sun-d:var(--brass-d);
  --sun-t:var(--brass-t); --sun-i:var(--brass);
}

/* ── 2. RESET ──────────────────────────────────────────────── */
*,*::before,*::after{ box-sizing:border-box; }
/* Components here set their own display, which outranks the user agent's
   [hidden] rule — so anything hidden from script stayed on screen. Fixed
   once, rather than per component. */
[hidden]{ display:none !important; }
*{ margin:0; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; scroll-padding-top:calc(var(--nav-h) + 12px); }
@media (prefers-reduced-motion: reduce){ html{ scroll-behavior:auto; } }
body{
  background:var(--paper);
  color:var(--ink);
  font-family:var(--text);
  font-size:clamp(1rem,.97rem + .16vw,1.075rem);
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  /* `clip`, not `hidden`. Both stop sideways scroll, but `hidden` makes the
     body a scroll container, and a scroll container is the scrollport every
     `position:sticky` inside it measures against. Since the body never
     scrolls itself, sticky never engaged — which is why the pinned business
     hero slid away under the wipe instead of holding still while it played. */
  overflow-x:clip;
}
body.is-locked{ overflow:hidden; }
img,svg,canvas,video{ display:block; max-width:100%; }
img{ height:auto; }
button,input,select,textarea{ font:inherit; color:inherit; }
button{ background:none; border:0; cursor:pointer; }
a{ color:inherit; text-decoration:none; }
ul,ol{ list-style:none; padding:0; }
table{ border-collapse:collapse; width:100%; }
:focus-visible{ outline:3px solid var(--blue); outline-offset:2px; border-radius:var(--r-sm); }
::selection{ background:var(--sun); color:var(--ink); }
.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; }
.skip{
  position:fixed; top:-100px; left:var(--pad); z-index:var(--z-top);
  background:var(--signal); color:var(--dark); padding:.8rem 1.3rem;
  border-radius:0 0 var(--r-sm) var(--r-sm); font-weight:700;
  transition:top var(--t-2) var(--e);
}
.skip:focus{ top:0; }

/* ── 3. TYPE ───────────────────────────────────────────────── */
/* One family. Display is the same face pulled wide — the width axis
   carries the hierarchy, which is how automotive brands set type. */
h1,h2,h3{
  font-family:var(--disp); font-weight:700; line-height:1.02;
  font-stretch:118%; letter-spacing:-.022em; text-wrap:balance;
  color:var(--bright);
}
h1{ font-weight:800; letter-spacing:-.028em; }
h2{ font-size:clamp(1.9rem,1.2rem + 2.7vw,3.2rem); }
h3{ font-size:clamp(1.12rem,1.02rem + .45vw,1.4rem); font-stretch:108%; letter-spacing:-.012em; }
h4{ font-family:var(--text); font-size:.8rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase; font-stretch:100%; }
p{ text-wrap:pretty; }

.deck{ color:var(--muted); font-size:clamp(1rem,.96rem + .3vw,1.14rem); line-height:1.62; max-width:60ch; }
.wrap{ width:100%; max-width:var(--maxw); margin-inline:auto; padding-inline:var(--pad); }
.lede{ font-size:clamp(1.04rem,1rem + .38vw,1.2rem); color:var(--muted); }

/* A single label style, used where a section genuinely needs naming —
   not stacked above every heading on the page. */
.kicker{
  display:inline-flex; align-items:center; gap:.55rem;
  font-size:.72rem; font-weight:700; letter-spacing:.16em; text-transform:uppercase;
  color:var(--brass); margin-bottom:.9rem; font-stretch:100%;
}
/* currentColor, not --brass. On .prose the kicker's own text colour is set by
   `.prose p`, which out-specifies `.kicker` — so the rule kept the pale brass
   while the words next to it went dark, and on a light ground the dash all but
   disappeared. Following the text keeps the pair together on any background. */
.kicker::before{ content:""; height:2px; width:20px; flex:none; background:currentColor; opacity:.85; border-radius:1px; }
.sec-head--center .kicker{ justify-content:center; }
/* Centred, the leading dash is a rule pointing at nothing — it reads as an
   indent marker, which is a left-aligned idea. The label stands on its own. */
.kicker--bare::before{ content:none; }

.sec-head{ margin-bottom:clamp(2rem,4.5vw,3.5rem); }
.sec-head h2 + .deck{ margin-top:var(--s4); }
.sec-head--center{ text-align:center; }
.sec-head--center .deck{ margin-inline:auto; }

.only-lg{ display:none; }
.only-sm{ display:inline; }
@media (min-width:800px){ .only-lg{ display:inline; } .only-sm{ display:none; } }

/* ── 4. BUTTONS ────────────────────────────────────────────── */
.btn{
  --bg:transparent; --fg:var(--ink); --bd:var(--edge-2);
  position:relative; overflow:hidden;
  display:inline-flex; align-items:center; justify-content:center; gap:.5em;
  min-height:46px; padding:.7em 1.35em;
  background:var(--bg); color:var(--fg);
  border:1.5px solid var(--bd); border-radius:999px;
  font-weight:700; font-size:.97rem; letter-spacing:-.006em;
  cursor:pointer; white-space:nowrap;
  transition:background var(--t-1) var(--e), border-color var(--t-1) var(--e),
             color var(--t-1) var(--e), transform var(--t-1) var(--e), box-shadow var(--t-2) var(--e);
}
.btn svg{ width:1.05em; height:1.05em; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; transition:transform var(--t-2) var(--e); }
.btn--primary{ --bg:var(--signal); --fg:var(--dark); --bd:var(--signal); box-shadow:0 6px 18px -8px var(--blue-r); }
.btn--primary:hover{ --bg:#fff; --bd:#fff; box-shadow:0 12px 34px -14px oklch(1 0 0 / .28); }
.btn--primary:hover svg{ transform:translateX(3px); }
.btn--quiet{ --bg:transparent; --fg:var(--ink); --bd:var(--steel-2); }
.btn--quiet:hover{ --bg:oklch(1 0 0 / .06); --bd:var(--muted); --fg:var(--bright); }
.btn--quiet:hover svg{ transform:translateX(3px); }
.btn--sun{ --bg:var(--signal); --fg:var(--dark); --bd:var(--signal); }
.btn--sun:hover{ --bg:#fff; --bd:#fff; }
.btn--ghost{ --bg:transparent; --fg:#fff; --bd:oklch(1 0 0 / .35); }
.btn--ghost:hover{ --bg:oklch(1 0 0 / .12); --bd:#fff; transform:translateY(-2px); }
.btn:active{ transform:translateY(0) scale(.985); }
.btn:disabled{ opacity:.42; pointer-events:none; }
.btn--sm{ min-height:42px; padding:.5em 1.1em; font-size:.9rem; }
.btn--lg{ min-height:56px; padding:.9em 1.7em; font-size:1.05rem; }
.btn--block{ display:flex; width:100%; }

/* ── 5. NAV ────────────────────────────────────────────────── */
.progress{ position:fixed; inset:0 0 auto 0; height:3px; z-index:calc(var(--z-nav) + 1); pointer-events:none; }
.progress i{
  display:block; height:100%; width:100%;
  background:linear-gradient(90deg,var(--blue),var(--sun));
  transform:scaleX(0); transform-origin:0 50%;
}

.nav{
  position:fixed; inset:0 0 auto 0; z-index:var(--z-nav);
  border-bottom:1px solid transparent;
  transition:background var(--t-2) var(--e), border-color var(--t-2) var(--e),
             box-shadow var(--t-2) var(--e), transform var(--t-2) var(--e);
}
.nav.is-stuck{
  background:oklch(0.145 0.008 258 / .78);
  backdrop-filter:saturate(140%) blur(18px);
  -webkit-backdrop-filter:saturate(180%) blur(16px);
  border-bottom-color:var(--edge);
}
.nav.is-hidden{ transform:translateY(-102%); }
.nav__in{ max-width:var(--maxw); margin-inline:auto; padding:.6rem var(--pad); display:flex; align-items:center; gap:var(--s5); }
.brand{ display:inline-flex; align-items:center; gap:.55rem; flex:none; padding-block:.45rem; }
.brand__mark{
  width:34px; height:34px; border-radius:var(--r); background:var(--signal); color:var(--dark);
  display:grid; place-items:center; flex:none;
  box-shadow:0 4px 12px -4px var(--blue-r);
  transition:transform var(--t-2) var(--spring);
}
.brand:hover .brand__mark{ transform:rotate(-8deg) scale(1.06); }

/* Diamond Wraps logo mark: art is light-on-dark, so it needs no tile behind it. */
.brand__mark--logo{
  width:38px; height:38px; background:none; box-shadow:none; border-radius:0;
  overflow:visible;
}
.brand__mark--logo picture{ display:block; width:100%; height:100%; }
.brand__mark--logo img{ width:100%; height:100%; object-fit:contain; display:block; }
/* The phone gets the mark on its own — cut out of its tile, so it sits in
   the nav pill rather than putting a black square inside one. It runs a
   touch larger because it is the whole brand at that width. */
/* The phone lockup. The mark is a diamond — a triangle with its mass at the
   top and a point at the bottom — so centring its box leaves it sitting high
   against the cap height of the word beside it. It is nudged down by a pixel
   and the pair is aligned on the type, not on the boxes. */
@media (max-width:719px){
  .brand{ gap:.5rem; padding-block:.3rem; }
  .brand__mark--logo{ width:36px; height:36px; }
  .brand__mark--logo img{
    transform:translateY(1px);
    /* A glow of its own, so the mark reads as cut glass on the pill rather
       than as a flat sticker. Cheap: one drop-shadow, no filter chain. */
    filter:drop-shadow(0 1px 6px oklch(from var(--dw) l c h / .38));
  }
  .brand__word{ font-size:1.2rem; letter-spacing:-.028em; }
}
.foot .brand__mark.brand__mark--logo{ background:none; box-shadow:none; }
.brand__mark svg{ width:22px; height:22px; }
.brand__word{ font-family:var(--disp); font-weight:700; font-size:1.06rem; letter-spacing:-.02em; }

.nav__links{ display:none; align-items:center; gap:var(--s4); margin-inline:auto; }
.nav__links > a,.nav__moreBtn{
  position:relative; display:inline-flex; align-items:center; justify-content:center; gap:.3rem;
  min-width:44px; flex:none; padding:.72rem .35rem;
  font-size:.94rem; font-weight:600; color:var(--slate);
  transition:color var(--t-1) var(--e);
}
.nav__links > a::after{
  content:""; position:absolute; left:.35rem; right:100%; bottom:.42rem; height:2px;
  border-radius:2px; background:var(--blue); transition:right var(--t-2) var(--e);
}
.nav__links > a:hover,.nav__moreBtn:hover{ color:var(--ink); }
.nav__links > a:hover::after,.nav__links > a[aria-current="page"]::after{ right:.35rem; }
.nav__links > a[aria-current="page"]{ color:var(--ink); }
.nav__moreBtn svg{ width:14px; height:14px; fill:none; stroke:currentColor; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; transition:transform var(--t-2) var(--e); }
.nav__moreBtn[aria-expanded="true"] svg{ transform:rotate(180deg); }
.nav__more{ position:relative; }
.nav__menu{
  position:absolute; top:calc(100% + 10px); left:50%; transform:translateX(-50%);
  width:min(330px,80vw); padding:.5rem;
  background:var(--paper); border:1px solid var(--edge); border-radius:var(--r);
  box-shadow:var(--lift-3); display:grid; gap:1px;
  animation:pop var(--t-2) var(--e);
}
.nav__menu[hidden]{ display:none; }
@keyframes pop{ from{ opacity:0; transform:translateX(-50%) translateY(-6px); } }
.nav__menu a{ display:grid; gap:.1rem; padding:.62rem .8rem; border-radius:var(--r-sm); transition:background var(--t-1); }
.nav__menu a:hover{ background:var(--mist); }
.nav__menu b{ font-size:.95rem; }
.nav__menu small{ font-size:.82rem; color:var(--mute); line-height:1.4; }

.nav__act{ display:flex; align-items:center; gap:var(--s3); margin-left:auto; }
.nav__tel{ display:none; align-items:center; gap:.45rem; font-size:.92rem; font-weight:700; color:var(--ink); padding-block:.6rem; }
.nav__tel svg{ width:16px; height:16px; fill:var(--blue); }
.nav__tel:hover{ color:var(--blue); }
.nav__cta{ display:none; }
.burger{
  width:46px; height:46px; display:grid; place-content:center; gap:5px;
  border:1px solid var(--steel-2); border-radius:var(--r); background:var(--pitch);
}
.burger span{ display:block; width:18px; height:2px; border-radius:2px; background:var(--bright); transition:transform var(--t-2) var(--e), opacity var(--t-1) var(--e); }
.burger[aria-expanded="true"] span:first-child{ transform:translateY(7px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2){ opacity:0; }
.burger[aria-expanded="true"] span:last-child{ transform:translateY(-7px) rotate(-45deg); }
/* Below the desktop nav, the bar was a full-width slab with a square-cornered
   box of three identical lines in the corner — the default every framework
   ships with. It floats now: an island with the brand at one end and a round
   button at the other, clear of the edges so the page shows past it, and the
   three lines are ragged right (16 / 11 / 16) so the mark reads as drawn
   rather than generated. The two long lines still cross into an X on open;
   the short one still fades. */
@media (max-width:999px){
  .nav{ padding-top:.45rem; }
  .nav.is-stuck{
    background:none; border-bottom-color:transparent;
    backdrop-filter:none; -webkit-backdrop-filter:none;
  }
  .nav__in{
    margin-inline:.7rem; padding:.45rem;
    /* Brand centred in the pill. A spacer column the width of the burger on
       the left, so it sits on the true centre rather than off by 42px. */
    display:grid; grid-template-columns:42px minmax(0,1fr) 42px; align-items:center;
    border-radius:999px; gap:var(--s3);
    background:oklch(0.145 0.008 258 / .82);
    backdrop-filter:saturate(150%) blur(16px);
    -webkit-backdrop-filter:saturate(150%) blur(16px);
    box-shadow:0 14px 34px -20px oklch(0 0 0 / .95), inset 0 0 0 1px oklch(1 0 0 / .10);
  }
  .brand{ grid-column:2; justify-self:center; }
  .burger{
    width:42px; height:42px; border:0; border-radius:50%;
    background:oklch(1 0 0 / .06);
    box-shadow:inset 0 0 0 1px oklch(1 0 0 / .13);
    justify-items:end; padding-right:13px;
    transition:background var(--t-1) var(--e), box-shadow var(--t-1) var(--e);
  }
  .burger:active{ background:var(--blue-t); box-shadow:inset 0 0 0 1px var(--blue-r); }
  .burger span{ width:16px; }
  .burger span:nth-child(2){ width:11px; }
  .burger[aria-expanded="true"] span:first-child,
  .burger[aria-expanded="true"] span:last-child{ width:16px; }
}

@media (min-width:1000px){
  .nav__links{ display:flex; }
  .nav__tel{ display:inline-flex; }
  .nav__cta{ display:inline-flex; }
  .burger{ display:none; }
}

.mobnav{
  position:fixed; inset:var(--nav-h) 0 0 0; z-index:var(--z-drawer);
  background:var(--paper);
  display:flex; flex-direction:column;
  opacity:0; transform:translateY(-8px);
  transition:opacity var(--t-2) var(--e), transform var(--t-2) var(--e);
}
.mobnav.is-open{ opacity:1; transform:none; }
.mobnav[hidden]{ display:none; }
.mobnav__scroll{ flex:1; overflow-y:auto; padding:var(--s4) var(--pad) var(--s5); -webkit-overflow-scrolling:touch; }
.mobnav nav{ display:flex; flex-direction:column; }
.mobnav nav a{
  display:grid; gap:.1rem; padding:.85rem 0; border-bottom:1px solid var(--edge);
}
.mobnav nav a span{ font-family:var(--disp); font-weight:700; font-size:1.28rem; letter-spacing:-.026em; }
.mobnav nav a em{ font-style:normal; font-size:.86rem; color:var(--mute); }
.mobnav nav a:first-child span{ color:var(--blue); }
.mobnav__foot{
  padding:var(--s4) var(--pad) calc(var(--s5) + env(safe-area-inset-bottom));
  border-top:1px solid var(--edge); background:var(--mist); display:grid; gap:.7rem;
}
.mobnav__tel{ text-align:center; color:var(--slate); font-weight:700; padding:.4rem; font-size:.92rem; }

/* ── 6. HERO ───────────────────────────────────────────────── */
/* Overridden at both breakpoints below; the number that matters is the
   travel (height - 100vh), because the wipe takes the first 42% of it and
   the three remaining promises split what is left. Give each of them about
   a third of a screen, or one flick of a phone deals out two at once. */
.hero{ height:280vh; padding:0; position:relative; }
.hero__sticky{
  position:sticky; top:0; min-height:100svh;
  display:grid; place-items:center; overflow:hidden;
  padding:calc(var(--nav-h) + var(--s4)) 0 var(--s5);
}
/* The hero sits on a photograph, not on a gradient. It is graded dark in
   tools/hero.py and scrimmed here so type keeps its contrast at every size. */
/* The backdrop is three panels of the shop's own work, built by
   tools/hero.py, then scrimmed here so type always has ground under it. */
.hero__sticky::before{
  content:""; position:absolute; inset:0; z-index:-2;
  background-image:image-set(url("../img/hero-work-2200.webp") type("image/webp"),
                             url("../img/hero-work-2200.jpg") type("image/jpeg"));
  background-size:cover; background-position:0% 50%;
  opacity:.95;
  /* Ends before the car does, and ends in a curve. A straight-edged mask puts
     a vertical seam down the hero that reads as a cropped rectangle; an
     ellipse anchored off the left edge just runs out of light. */
  -webkit-mask-image:radial-gradient(104% 124% at 2% 50%,
      #000 0%, #000 22%, oklch(0 0 0 / .42) 42%, transparent 66%);
          mask-image:radial-gradient(104% 124% at 2% 50%,
      #000 0%, #000 22%, oklch(0 0 0 / .42) 42%, transparent 66%);
}
.hero__sticky::after{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:
    /* The light the car stands in. Kept tight and to the right: spread wide it
       stops being a pool of light and becomes a grey wash over the whole
       hero, which flattens the photograph and the headline with it. */
    /* Wide enough to hold the whole plate, not just the space behind it. The
       photograph's own wall is light, so unless the page is light where they
       meet, the picture edge shows as a bright rectangle on a dark ground. */
    radial-gradient(60% 88% at 74% 50%,
      oklch(0.955 0.002 258 / .93) 0%, oklch(0.91 0.004 258 / .72) 40%,
      oklch(0.78 0.008 258 / .32) 64%, oklch(0.50 0.010 258 / .07) 84%, transparent 95%),
    /* Dark enough under the words, thin enough over the photograph that you
       can still see whose hands those are. */
    /* Dark under the words only. Past them the photograph is allowed to be a
       photograph — it is the one thing on this page that shows the work. */
    /* A soft pool of dark under the headline only. The backdrop's middle panel
       is its brightest, and it was landing straight behind the outlined city
       name, where there is least ink to read it by. */
    radial-gradient(54% 66% at 20% 46%, oklch(0.135 0.010 258 / .50), transparent 80%),
    linear-gradient(96deg, oklch(0.152 0.009 258 / .72) 0%, oklch(0.152 0.009 258 / .56) 26%, oklch(0.152 0.009 258 / .20) 42%, transparent 56%),
    linear-gradient(0deg, var(--void) 0%, transparent 26%),
    linear-gradient(180deg, oklch(0.152 0.009 258 / .74) 0%, transparent 22%);
}
@media (max-width:959px){
  /* Stacked, there is no left and right to play with: the photograph sits
     behind everything and the light field goes under the car only. */
  .hero__sticky::before{
    background-position:50% 46%; opacity:.42;
    -webkit-mask-image:none; mask-image:none;
  }
  .hero__sticky::after{
    background:
      radial-gradient(100% 42% at 50% 72%, oklch(0.90 0.004 258 / .55) 0%, transparent 70%),
      linear-gradient(180deg, oklch(0.152 0.009 258 / .88) 0%, oklch(0.152 0.009 258 / .70) 46%, var(--void) 90%);
  }
}
.hero__in{
  width:100%; max-width:var(--maxw); margin-inline:auto; padding-inline:var(--pad);
  display:grid; gap:var(--s6); align-content:center;
}
.hero__eyebrow{
  display:inline-flex; align-items:center; gap:.55rem;
  font-size:.83rem; font-weight:700; color:var(--blue);
  background:var(--paper); border:1.5px solid var(--edge);
  padding:.4rem .95rem; border-radius:999px; box-shadow:var(--lift-1);
  margin-bottom:var(--s4);
}
.hero__pip{ width:7px; height:7px; border-radius:50%; background:var(--good); box-shadow:0 0 0 3px oklch(0.6 0.15 155 / .2); animation:pulse 2.6s var(--e) infinite; }
@keyframes pulse{ 0%,100%{ box-shadow:0 0 0 3px oklch(0.6 0.15 155 / .2); } 50%{ box-shadow:0 0 0 6px oklch(0.6 0.15 155 / 0); } }
/* The lower end is set by "without a new car." fitting on one line at 360px —
   any bigger and "car." orphans onto a third line. */
/* Capped at 4rem, not 5. The copy column is half the hero now, and at 80px a
   fifteen-character line does not fit it — the headline broke mid-phrase,
   which is worse than being a little smaller. */
.hero__h1{
  font-family:var(--sign); font-weight:400;
  letter-spacing:.012em; line-height:.86; text-transform:uppercase;
  text-shadow:0 2px 26px oklch(0.12 0.01 258 / .6);
}
/* The service is a sign, and a sign does not break in the middle. It holds one
   line at every width; the size is what gives, not the wrap. */
.hero__h1a{
  display:block; white-space:nowrap;
  font-size:clamp(2.6rem, 1.1rem + 4.6vw, 5.4rem);
}
/* The place name is the quieter half: same face, outlined, so the service
   reads first and the city sits under it like a sign painter's second line. */
.hero__h1b{
  display:block; color:transparent;
  font-size:clamp(2.6rem, 1.1rem + 4.6vw, 5.4rem);
  -webkit-text-stroke:1.5px oklch(0.98 0 0 / .72);
}
@supports not ((-webkit-text-stroke:1px #000)){
  .hero__h1b{ color:oklch(0.98 0 0 / .78); }
}
.hero__deck{
  margin-top:clamp(1rem, 1.6vw, 1.6rem); color:var(--paper-2, oklch(0.88 0.004 258));
  font-size:clamp(1.04rem,1rem + .38vw,1.2rem); max-width:46ch;
  text-shadow:0 1px 14px oklch(0.12 0.01 258 / .6);
}
/* The wipe used to float on the backdrop with nothing under it, which read as
   a picture pasted onto a photograph. It now sits in a viewer: a raised panel
   with its own ground, a hairline edge, and a wash of the current film colour
   spilling out behind it so the two layers belong to each other. */
.hero__fig{
  position:relative; --glow:#1B5BFF;
  padding:clamp(.35rem,.8vw,.6rem);
  border-radius:var(--r-xl);
  /* Nothing at all. No tray, no wash, no shadow. The photograph is lit to
     match the pool behind it and dissolves its own edges, so every extra
     layer here was only redrawing the box that work removed. */
  background:none; box-shadow:none;
  /* and it sits into the right-hand gutter, where the light already is */
  justify-self:end; width:100%;
}
/* the colour wash, thrown out past the panel */
/* A cast of whatever colour is on the car, thrown out past the picture. It
   has to stay a hint: at full strength a pale film turns it into a white
   rectangle sitting behind the car, which is the effect this was added to
   avoid in the first place. */
.hero__fig::before{
  content:none;
  position:absolute; inset:-30% -26% -34% -26%; z-index:-1;
  /* A circle, and only a circle. The old version drew a rounded box and then
     ran a 74px blur over it, and a blur is clipped to its element — which put
     a soft-cornered rectangle behind the car, the exact edge this is supposed
     to dissolve. A gradient is already smooth; it does not need the filter. */
  background:radial-gradient(circle closest-side,
    var(--glow) 0%, oklch(from var(--glow) l c h / .45) 40%, transparent 72%);
  opacity:.22;
  transition:background 420ms var(--e), opacity 420ms var(--e);
}
@supports not (color:oklch(from red l c h)){
  .hero__fig::before{
    background:radial-gradient(circle closest-side, var(--glow), transparent 70%);
  }
}
/* and a hard hairline so the panel has a real edge, not a soft shadow */
.hero__fig::after{ content:none; }
/* One soft ellipse instead of a top-and-bottom band. The corners were the
   giveaway: fading two edges on a rectangle still leaves a rectangle. The
   ring and the opaque backing have to go with it — a mask feathers the
   picture, but a border and a dark fill underneath just redraw the box. */
/* An ellipse can only dissolve the corners of a 16:10 frame — to reach the
   middle of the top and bottom edges as well it has to pull in far enough to
   start eating the car. So the plate stops pretending it has no edge and
   becomes a deliberate one: generously rounded, feathered, and lifted off the
   page on a soft shadow so it reads as floating rather than pasted. */
.hero__fig .reveal{
  background:transparent;
  border-radius:clamp(18px, 2.4vw, 36px);
  box-shadow:
    0 2px 2px -1px oklch(1 0 0 / .30) inset,
    0 26px 60px -34px oklch(0.10 0.01 258 / .55),
    0 60px 120px -60px oklch(0.10 0.01 258 / .40);
}
/* The fade belongs to the photograph, not to the control on top of it. Masking
   the whole component washed out the wipe handle and both labels along with
   the picture. The frame now carries roughly a quarter of its width as empty
   lit wall, so the fade finishes inside that margin and never touches the car. */
.hero__fig .reveal__img,
.hero__fig .reveal__clip{
  /* Gentle now. The photograph carries its own dissolve, baked in by
     tools/herolight.py, so stacking a hard mask on top double-darkens the
     corners and puts a ring back where there was none. */
  /* Gentle now — the rounded corner does the shaping, this only softens the
     picture into it so the sweep does not stop dead against the radius. */
  -webkit-mask-image:radial-gradient(86% 94% at 50% 48%,
      #000 0%, #000 72%, oklch(0 0 0 / .78) 90%, oklch(0 0 0 / .55) 100%);
          mask-image:radial-gradient(86% 94% at 50% 48%,
      #000 0%, #000 72%, oklch(0 0 0 / .78) 90%, oklch(0 0 0 / .55) 100%);
}
/* The clip is the moving half of the wipe, so its mask has to be pinned to the
   frame rather than to the sliver that is currently showing. */
.hero__fig .reveal__clip{
  -webkit-mask-size:100% 100%; mask-size:100% 100%;
  -webkit-mask-position:center; mask-position:center;
}
.hero__fig .reveal__clip .reveal__img{
  -webkit-mask-image:none; mask-image:none;
}
.hero__fig figcaption{
  margin-top:.7rem; padding:0 .2rem;
  font-size:.78rem; color:var(--faint); text-align:center;
}
.hero__cta{
  display:flex; flex-wrap:wrap; gap:clamp(.6rem, 1vw, 1rem);
  margin-top:clamp(1.4rem, 2.4vw, 2.4rem);
}
.hero__cta .btn{ flex:1 1 auto; min-width:168px; }
/* Three numbers under the deck. The hero was asking people to take a
   colour change on trust with nothing but a slogan; the price, the time off
   the road and the warranty are the three things anyone actually wants before
   they scroll, and they are cheap to read. Wide screens only — the phone hero
   has to fit one sticky screen and this is the first thing that would push
   the call to action off it. */
/* the studio's own kicker, set larger — it is the label on the most
   important thing on the page, not a footnote above it */
.kicker--lg{ font-size:clamp(1rem,.9rem + .4vw,1.18rem); letter-spacing:.13em; margin-bottom:1.05rem; }
/* the leading rule is sized off the base kicker, so it needs to grow with it */
.kicker--lg::before{ width:30px; height:2.5px; }
/* the studio's label, set as a label rather than a footnote — it names the
   thing the whole page is built around */
.kicker--xl{
  font-size:clamp(1rem,.86rem + .6vw,1.35rem); letter-spacing:.16em;
  color:var(--blue); font-weight:800;
}
/* One line. It was broken across two, which on a centred heading gave a short
   line over a long one and read as a stack rather than a sentence. And it no
   longer says "your car": the photograph is a Model 3, and most people looking
   at it do not own one. */
.studio__h2{ text-wrap:balance; }
.deck--wide{ max-width:62ch; margin-inline:auto; text-wrap:balance; }

.hero__facts{
  display:none;                      /* not `.only-lg`: that sets `inline` */
  flex-wrap:wrap; gap:var(--s5); margin-top:var(--s4);
  padding-top:var(--s4); border-top:1.5px solid var(--edge);
  list-style:none;
}
/* Desktop only. Below this the hero copy is a flex column with explicit
   `order` on every child and a height budget that has to leave the call to
   action on screen — this row would both sort to the top and push the button
   off the fold. */
@media (min-width:960px){ .hero__facts{ display:flex; } }
.hero__facts li{ display:grid; gap:.1rem; }
.hero__facts b{
  font-family:var(--disp); font-weight:800; letter-spacing:-.03em;
  font-size:clamp(1.15rem,1rem + .5vw,1.45rem); color:var(--ink); line-height:1;
}
/* These sit at the point where the dark scrim has already given out and the
   light pool has not yet arrived, so a mid grey lands on whichever is behind
   it and loses either way. */
.hero__facts span{
  font-size:.82rem; color:oklch(0.82 0.004 258);
  text-shadow:0 1px 10px oklch(0.12 0.01 258 / .75);
}
.hero__facts b{ text-shadow:0 1px 12px oklch(0.12 0.01 258 / .7); }

/* ── the hero promises ─────────────────────────────────────────
   Four things every job includes, under the photograph. Default: a plain,
   fully visible list. `.is-seq` (added by JS only when motion is welcome)
   dims them and lets the wipe bring them in one per scroll — stacked, so they
   accumulate rather than swapping in place, and the hero finishes with all
   four standing.

   They keep their space whether they are showing or not. Growing the block as
   each one arrives would push the buttons down the page under the reader's
   cursor, which is the one thing worse than a static list. */
/* Four things worth reading, so they are staged like four things rather than
   listed like four things. Each one steps further right than the last, which
   gives the eye a path to follow down instead of a block to skim past, and
   each lands on its own as the film goes on. */
.promise{ margin-top:var(--s5); }
/* Centred under the photograph. The staircase is the point, but hung off the
   left edge of the list box it read as the whole group being pushed left of
   the picture it belongs to. Straddling the centre — step 0 sits as far left
   as step 3 sits right — keeps the stagger and puts the set under the car.

   The stagger used to be a left margin, which meant each pill was a
   different width: the margin ate into the grid column and the shortest
   promise ended up in the smallest bubble, so the one with a page behind it
   read as a lesser thing than the three around it. It is a transform now.
   The column sizes to the longest promise, every pill fills it, and the
   staircase is offset from the centre (-1.5 to +1.5 steps) rather than
   pushed off the left edge. */
.promise__list{ display:grid; gap:.5rem; justify-items:stretch; width:fit-content; max-width:100%; margin-inline:auto; }
.promise__item{
  --step: 0;
  --shift: calc((var(--step) - 1.5) * clamp(10px, 1.6vw, 26px));
  transform:translateX(var(--shift));
  padding:.5rem 1.05rem .5rem 3rem; position:relative; min-height:40px;
  display:flex; align-items:center; align-self:start;
  max-width:100%; border-radius:999px;
  background:oklch(0.14 0.009 258 / .82);
  box-shadow:inset 0 0 0 1px oklch(1 0 0 / .09);
  backdrop-filter:blur(7px) saturate(120%); -webkit-backdrop-filter:blur(7px) saturate(120%);
}
.promise__item:nth-child(2){ --step:1; }
.promise__item:nth-child(3){ --step:2; }
.promise__item:nth-child(4){ --step:3; }

/* The tick takes the colour of the film currently on the car, so the hero
   agrees with itself. --tick is set alongside --glow and falls back to the
   house blue whenever the film is clear and would vanish against the disc. */
.promise__item::before{
  content:""; position:absolute; left:.5rem; top:calc(50% - 12px);
  width:24px; height:24px; border-radius:50%;
  background:var(--tick, #1B5BFF);
  box-shadow:0 2px 10px oklch(0.14 0.009 258 / .55);
}
.promise__item::after{
  content:""; position:absolute; left:1.2rem; top:calc(50% - 6px);
  width:6px; height:11px;
  border:2.2px solid oklch(1 0 0 / .96); border-top:0; border-left:0;
  transform:rotate(42deg);
}
.promise__item b{
  display:block; font-family:var(--disp); font-weight:700;
  font-size:clamp(.98rem,.9rem + .32vw,1.16rem);
  letter-spacing:-.015em; line-height:1.25; color:oklch(0.97 0.002 258);
}
/* All four read the same. Singling the rental out in another colour made it
   look like a different kind of thing — an advert inside the list rather than
   one more thing that comes with the job. */
/* One of the four has a page behind it. It keeps the same face and weight as
   its neighbours — colouring it would make it an advert sitting in the list.
   It carried a small arrow to say it was a link, which is what made it the
   odd one out; the underline on hover says the same thing and only when it
   is being asked. The link stretches over the whole pill so the bubble is
   the hit area rather than the words inside it. */
.promise__item--go a{
  display:flex; align-items:center; color:inherit;
}
.promise__item--go a::after{ content:""; position:absolute; inset:0; border-radius:inherit; }
.promise__item--go a:hover b,
.promise__item--go a:focus-visible b{ text-decoration:underline; text-underline-offset:3px; }


.promise.is-seq .promise__item{
  opacity:0; transform:translate(calc(var(--shift) - 14px), 10px);
  transition:opacity 420ms var(--e), transform 560ms var(--spring);
}
.promise.is-seq .promise__item.is-on{ opacity:1; transform:translateX(var(--shift)); }
.promise.is-seq .promise__item::before{
  transform:scale(.35); transition:transform 420ms var(--spring) 80ms;
}
.promise.is-seq .promise__item.is-on::before{ transform:scale(1); }
@media (max-width:640px){
  .promise__item{ --shift:0 !important; }
  /* All four are on screen at once here, so they are a list to read rather
     than four arrivals to watch. Tighter type and padding takes about 70px
     off the block, which is 70px the hero no longer has to be. */
  .promise__list{ gap:.35rem; }
  .promise__item{ padding:.35rem .85rem .35rem 2.5rem; min-height:34px; }
  .promise__item b{ font-size:.92rem; line-height:1.2; }
  .promise__item::before{ left:.4rem; top:calc(50% - 10px); width:20px; height:20px; }
  .promise__item::after{ left:1.02rem; top:calc(50% - 5px); width:5px; height:9px; border-width:2px; }
}
.promise.is-seq .promise__item::after{
  opacity:0; transition:opacity 220ms var(--e) 300ms;
}
.promise.is-seq .promise__item.is-on::after{ opacity:1; }

/* Stacked, they still arrive with the scroll — but they arrive from a ghost,
   not from nothing. The list reserves the height of all four from the start,
   so the block cannot grow and shove the buttons down the page; the earlier
   version answered that by simply showing all four at once, which threw away
   the one piece of motion the phone hero had. Holding the un-landed ones at
   low opacity keeps the block looking like a list the whole way through and
   still lets each line land as the film goes on. */
@media (max-width:959px){
  .promise.is-seq .promise__item{
    opacity:.22; transform:translate(var(--shift), 6px);
    transition:opacity 380ms var(--e), transform 460ms var(--spring);
  }
  .promise.is-seq .promise__item.is-on{ opacity:1; transform:translateX(var(--shift)); }
  .promise.is-seq .promise__item::before{ transform:scale(.72); }
  .promise.is-seq .promise__item.is-on::before{ transform:scale(1); }
  .promise.is-seq .promise__item::after{ opacity:0; }
  .promise.is-seq .promise__item.is-on::after{ opacity:1; }
}
@media (prefers-reduced-motion: reduce){
  .promise.is-seq .promise__item{ opacity:1; transform:translateX(var(--shift)); }
  .promise.is-seq .promise__item::before{ transform:scale(1); }
  .promise.is-seq .promise__item::after{ opacity:1; }
}

.promise__ticks{ display:none; }

.hero__in > *,.hero__copy > *,.studio__shell > *{ min-width:0; }

.hero__cue{ display:none; }
@media (min-width:960px){
  .hero{ height:280vh; }
  /* even columns: the headline now carries the argument rather than a slogan,
     and at .9fr it broke across four lines mid-phrase */
  /* The copy needs enough width to hold its own sign line, and the picture
     needs to sit out in the light rather than crowding the middle. Squeezing
     the left column to buy the right one room did neither: the headline broke
     in half and the two columns met in a jam down the centre. */
  /* The copy keeps a reading measure whatever the screen does; the picture
     takes everything left over and sits at the far end of it. Sizing both
     columns as fractions instead meant the text line grew with the monitor
     while the photograph stopped short, which is what left the dead band down
     the right of a wide screen. */
  .hero__in{
    max-width:var(--herow);
    /* Its own gutter. Cutting this to almost nothing did close the gap on the
       right, but it also pushed the headline out to the screen edge, which is
       the opposite complaint. Kept close to the page measure so both sides
       breathe by the same amount. */
    padding-inline:clamp(1.2rem, 2.4vw, 2.75rem);
    /* Both columns are capped and the pair is centred. Letting the picture
       column take all the leftover width and pinning the car to the far end
       of it meant the gap between the words and the car grew with the
       monitor — 280px of nothing down the middle of a 1920 screen. Cap it,
       and the gap is just the grid gap at every width. */
    grid-template-columns:minmax(0, 33rem) minmax(0, 50rem);
    justify-content:center;
    align-items:center; gap:clamp(2rem, 4vw, 5rem);
  }
  /* Pushed into the light on the right. Clamped rather than a flat offset so a
     wide screen can lean it into the gutter without pushing the page sideways. */
  /* Pinned to the right end of its column and capped, so it moves outward
     with the screen without the car itself growing to billboard size. A
     transform used to do this job; real layout does it without the risk of
     shunting the page sideways. */
  .hero__fig{ justify-self:end; width:100%; transform:none; }
  .hero__cue{
    position:absolute; left:50%; bottom:1.4rem; transform:translateX(-50%);
    display:flex; align-items:center; gap:.55rem;
    font-size:.76rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
    color:var(--mute); pointer-events:none;
    animation:cue 2.6s var(--e) infinite;
  }
  .hero__cue svg{ width:15px; height:15px; fill:none; stroke:var(--blue); stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
}
@keyframes cue{ 0%,100%{ transform:translate(-50%,0); opacity:.85; } 50%{ transform:translate(-50%,5px); opacity:1; } }
@media (prefers-reduced-motion: reduce){ .hero__cue{ animation:none; } }
/* The phone hero has to fit inside one sticky screen or the wipe scrolls
   the call to action out of reach. Everything below is a size budget. */
@media (max-width:959px){
  /* 245vh bought a long wipe, and a long wipe is worth paying for while the
     promises are arriving one per screen. They are not: below 960px all four
     are on from the start (see the .is-seq override above), so the extra
     screen and a half was scrolling that changed nothing. 165vh leaves the
     wipe about half a screen to run in, which is still a deliberate drag
     rather than a jump, and hands the reader the rest of the page sooner. */
  .hero{ height:165vh; }
  /* `safe` so that if the content ever outgrows the screen it pins to the
     top and scrolls, rather than centring and clipping both ends */
  .hero__sticky{ padding-top:calc(var(--nav-h) + var(--s2)); align-content:safe center; }
  .hero__in{ gap:0; align-content:safe center; }
  .hero__copy{ display:contents; }
  .hero__eyebrow{ order:1; margin-bottom:var(--s3); font-size:.76rem; padding:.32rem .8rem; }
  .hero__h1{ order:2; }
  .hero__deck{ order:3; margin-top:var(--s3); font-size:1rem; }
  /* Stacked, the copy has the whole width to itself and nothing to align
     against on the right, so left-alignment reads as a column that lost its
     other half. Centred, the sign sits over the sentence the way it would on
     a shopfront — and the sign gets the room a phone can actually give it. */
  .hero__copy > *{ text-align:center; }
  .hero__eyebrow{ justify-self:center; }
  .hero__h1a, .hero__h1b{ font-size:clamp(3.4rem, 1rem + 6.6vw, 5.4rem); }
  .hero__deck{ margin-inline:auto; max-width:34ch; }
  .hero__cta{ justify-content:center; }
  .hero__cta .btn{ flex:1 1 46%; }
  .hero__fig{ order:4; margin-top:var(--s4); }
  /* The film name and "factory white" are captions for a picture that is
     three times this size on a desktop. At phone width they sit on the car
     itself, and the car is the argument — the switcher underneath already
     says which film is on. */
  .hero__fig .reveal__tag{ display:none; }
  .hero__cta{ order:5; margin-top:var(--s4); gap:.5rem; }
  .hero__cta .btn{ flex:1 1 46%; min-width:0; padding-inline:1rem; }
  /* without JS the four stack, so keep them compact */
}
@media (max-width:400px){
  .hero__deck{ font-size:.95rem; }
}

/* Tablet-width single column. The photograph is still full-bleed here, which
   at 700-950px makes it tall enough to push the promises off the fold — and
   the promises are the reason the hero scrolls at all. Give the figure a
   height budget taken from the viewport instead of the column. */
/* Stacked, the plate is much wider than it is tall, so the desktop ellipse
   leaves the left and right edges half-opaque and they read as cuts. Pull the
   radii in until every side reaches transparent. This has to cover the whole
   stacked range — first time round it went into the 601-959 query and never
   matched a phone at all. */
@media (max-width:959px){
  .hero__fig .reveal__img,
  .hero__fig .reveal__clip{
    -webkit-mask-image:radial-gradient(88% 96% at 50% 48%,
        #000 0%, #000 74%, oklch(0 0 0 / .8) 92%, oklch(0 0 0 / .6) 100%);
            mask-image:radial-gradient(88% 96% at 50% 48%,
        #000 0%, #000 74%, oklch(0 0 0 / .8) 92%, oklch(0 0 0 / .6) 100%);
  }
  .hero__fig .reveal{ border-radius:clamp(14px, 4vw, 26px); }
}

@media (min-width:601px) and (max-width:959px){
  .hero__fig .reveal{ max-width:calc(min(34svh, 340px) * 1.6); margin-inline:auto; }
  .hero__in{ max-width:640px; }
}
@media (prefers-reduced-motion: reduce){
  .hero{ height:auto; }
  .hero__sticky{ position:static; min-height:0; padding-block:calc(var(--nav-h) + var(--s6)) var(--s7); }
}

/* ── 7. THE REVEAL ─────────────────────────────────────────── */
.reveal{
  position:relative; border-radius:var(--r-lg); overflow:hidden;
  box-shadow:inset 0 0 0 1px oklch(0 0 0 / .35);
  background:var(--void);
  --edge-ring:inset 0 0 0 1px oklch(0 0 0 / .35);
  touch-action:pan-y; cursor:ew-resize; isolation:isolate;
  /* 2:1, matching the reframed photograph. At 16/10 a third of the card was
     empty road under the car, so the car came out smaller than the type next
     to it. */
  aspect-ratio:2/1; --x:52%;
}
.reveal__img,.reveal__clip canvas{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:50% 50%;
}
.reveal__clip{ position:absolute; inset:0; clip-path:inset(0 calc(100% - var(--x)) 0 0); }
/* The line where the film meets the paint. One plain divider everywhere: the
   hero used to dress this as a roll of film — a cylinder with a lit crown and
   end caps — but the same wipe on the business-wraps page is a line, and the
   line is the one that reads. */
.reveal__bar{
  position:absolute; top:0; bottom:0; left:var(--x); width:2px; margin-left:-1px;
  background:#fff; box-shadow:0 0 0 1px oklch(0 0 0 /.14), 0 0 14px oklch(1 0 0 /.5);
  pointer-events:none;
}
.reveal__grip{
  position:absolute; top:50%; left:var(--x); transform:translate(-50%,-50%);
  width:56px; height:56px; border-radius:50%;
  background:var(--day); color:var(--dark); display:grid; place-items:center;
  box-shadow:0 6px 20px oklch(0 0 0 /.28); cursor:grab;
  transition:transform var(--t-1) var(--e), box-shadow var(--t-1) var(--e);
}
.reveal__grip svg{ width:22px; height:22px; fill:none; stroke:currentColor; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
.reveal__grip:hover{ transform:translate(-50%,-50%) scale(1.06); }
.reveal.is-dragging .reveal__grip{ transform:translate(-50%,-50%) scale(1.1); }
.reveal__tag{
  position:absolute; bottom:.9rem; font-size:.78rem; font-weight:700; letter-spacing:.02em;
  padding:.4rem .8rem; border-radius:999px; backdrop-filter:blur(8px);
  background:oklch(0.145 0.008 258 / .82); color:var(--bright); box-shadow:var(--lift-1);
  pointer-events:none; z-index:4;
}
.reveal__tag--l{ left:.9rem; background:var(--signal); color:var(--dark); }
.reveal__tag--r{ right:.9rem; }
.reveal__edge{
  position:absolute; top:-2%; bottom:-2%; left:var(--x);
  width:150px; margin-left:-75px; z-index:3; pointer-events:none;
  background:linear-gradient(90deg, transparent 0%,
    oklch(1 0 0 / .08) 34%, oklch(0.96 0.03 250 / .34) 47%,
    oklch(1 0 0 / .52) 50%, oklch(0.96 0.03 250 / .24) 53%,
    oklch(1 0 0 / .05) 66%, transparent 100%);
  filter:blur(7px); mix-blend-mode:screen; opacity:.85;
}
.reveal.is-dragging .reveal__edge{ opacity:1; }
@media (prefers-reduced-motion: reduce){ .reveal__edge{ display:none; } }
@media (max-width:719px){ .reveal__edge{ width:96px; margin-left:-48px; } .reveal__grip{ width:48px; height:48px; } }

/* ── 8. BEFORE / AFTER ─────────────────────────────────────── */
.cmp{
  position:relative; border-radius:var(--r-lg); overflow:hidden; isolation:isolate;
  box-shadow:0 30px 70px -34px oklch(0 0 0 / .7);
  touch-action:pan-y; cursor:ew-resize; --x:50%;
  max-width:820px; margin-inline:auto;
  /* the aspect is fixed here rather than by the image, because the two
     breakpoints load differently-cropped photographs */
  aspect-ratio:1400/1407;
  background:var(--night-2);
}
.cmp picture{ display:contents; }
.cmp__img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
/* A short linear catch-up on the seam. Scroll arrives in discrete wheel
   steps, and driving clip-path straight off them makes the film look like it
   is snapping on in chunks; 140ms is enough to read as one continuous sweep
   and short enough that it never feels detached from the wheel. Off while
   dragging — there the handle has to sit under your finger. */
.cmp__clip{
  position:absolute; inset:0; clip-path:inset(0 calc(100% - var(--x)) 0 0);
  transition:clip-path 140ms linear;
}

.cmp__clip img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
/* The phone used to get its own tight crop in a tall frame. It filled the
   screen, but it filled it with one wheel arch: you could no longer see
   that the two halves are the same car, which is the whole comparison.
   Same framing as the desktop now, at the same aspect. */
.cmp__bar{
  position:absolute; top:0; bottom:0; left:var(--x); width:2px; margin-left:-1px;
  background:#fff; box-shadow:0 0 0 1px oklch(0 0 0 /.2), 0 0 16px oklch(0 0 0 /.35);
  pointer-events:none; z-index:3;
}
.cmp__grip{
  position:absolute; top:50%; left:var(--x); transform:translate(-50%,-50%); z-index:4;
  width:54px; height:54px; border-radius:50%;
  background:var(--day); color:var(--dark); display:grid; place-items:center;
  box-shadow:0 6px 20px oklch(0 0 0 /.35); cursor:grab;
  transition:transform var(--t-1) var(--e);
}
.cmp__grip svg{ width:22px; height:22px; fill:none; stroke:currentColor; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
.cmp__grip:hover{ transform:translate(-50%,-50%) scale(1.07); }
.cmp.is-dragging .cmp__grip{ transform:translate(-50%,-50%) scale(1.12); }
.cmp__tag{
  position:absolute; bottom:.9rem; z-index:3; font-size:.76rem; font-weight:700;
  padding:.4rem .8rem; border-radius:999px; pointer-events:none;
  background:oklch(0.16 0.02 258 / .72); color:#fff;
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
}
.cmp__tag--l{ left:.9rem; background:var(--good); }
.cmp__tag--r{ right:.9rem; background:var(--bad); }
/* stated after .cmp__bar / .cmp__grip so it is not overwritten by them */
.cmp__bar{ transition:left 140ms linear; }
.cmp__grip{ transition:left 140ms linear, transform var(--t-1) var(--e); }
.cmp.is-dragging .cmp__clip{ transition:none; }
.cmp.is-dragging .cmp__bar{ transition:none; }
.cmp.is-dragging .cmp__grip{ transition:transform var(--t-1) var(--e); }
@media (prefers-reduced-motion: reduce){
  .cmp__clip,.cmp__bar{ transition:none; }
}
@media (max-width:520px){ .cmp__tag{ font-size:.7rem; padding:.3rem .6rem; bottom:.6rem; } .cmp__tag--l{ left:.6rem; } .cmp__tag--r{ right:.6rem; } }

/* ── 9. TICKER ─────────────────────────────────────────────── */
.ticker{
  border-block:1px solid var(--edge); background:var(--mist);
  overflow:hidden; padding:.85rem 0;
  mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
}
.ticker__row{ display:flex; gap:var(--s6); width:max-content; animation:slide 52s linear infinite; }
.ticker__row span{ display:inline-flex; align-items:center; gap:var(--s6); font-size:.88rem; font-weight:700; color:var(--slate); white-space:nowrap; }
.ticker__row span::after{ content:""; width:6px; height:6px; border-radius:50%; background:var(--sun); }
@keyframes slide{ to{ transform:translateX(-50%); } }
@media (prefers-reduced-motion: reduce){ .ticker__row{ animation:none; } }

/* ── 10. SERVICE CARDS ─────────────────────────────────────── */
.lineup{ padding:var(--s8) 0 var(--s7); background:var(--mist); }
.lineup .sec-head{ margin-bottom:var(--s5); }

/* A manufacturer shows its range as a row of cars you walk along, not as a
   grid of equal boxes. The panels share the row until one is asked for, and
   then it takes the space it needs from the others. */
.lu{
  display:flex; gap:2px;
  padding-inline:var(--pad); margin-inline:auto; max-width:calc(var(--maxw) + 2rem);
  scroll-snap-type:x mandatory; overflow-x:auto; overscroll-behavior-x:contain;
  scrollbar-width:none; -webkit-overflow-scrolling:touch;
}
.lu::-webkit-scrollbar{ display:none; }

.lu__item{
  position:relative; isolation:isolate; overflow:hidden;
  flex:0 0 82%; scroll-snap-align:center;
  min-height:clamp(400px, 68vw, 520px);
  display:flex; align-items:flex-end;
  color:var(--paper-i, oklch(0.98 0 0)); text-decoration:none;
  border-radius:var(--r-lg);
  background:oklch(0.16 0.008 258);
}
.lu__item img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  transition:transform 900ms var(--e), filter 600ms var(--e);
  transform:scale(1.04);
}
/* Type sits on a photograph, so the photograph has to give way at the bottom.
   Two stops, not three: a single long ramp reads as a gradient effect. */
.lu__shade{
  position:absolute; inset:0;
  background:linear-gradient(0deg, oklch(0.11 0.01 258 / .94) 6%,
                                   oklch(0.11 0.01 258 / .55) 44%,
                                   oklch(0.11 0.01 258 / .08) 88%);
}
.lu__body{
  position:relative; z-index:1; width:100%;
  padding:var(--s5) clamp(1.1rem, 2.4vw, 1.7rem) clamp(1.1rem, 2.2vw, 1.5rem);
  display:flex; flex-direction:column; gap:.55rem;
}
.lu__k{
  font-size:.68rem; font-weight:700; letter-spacing:.16em; text-transform:uppercase;
  color:oklch(0.98 0 0 / .62);
}
.lu__h{
  font-family:var(--disp); font-variation-settings:"wdth" var(--wide);
  font-size:clamp(1.5rem, 1.2rem + 1.1vw, 2.05rem); font-weight:700;
  letter-spacing:-.025em; line-height:1.02;
  /* A closed panel is narrow. The name wraps rather than clips: two short
     lines still read, half a word does not. */
  text-wrap:balance; hyphens:none;
}
.lu__line{ display:block; font-size:.94rem; color:oklch(0.98 0 0 / .82); max-width:34ch; }
.lu__pts{ display:grid; gap:.3rem; margin-top:.7rem; }
.lu__pts li{
  position:relative; padding-left:1.05rem; font-size:.86rem;
  color:oklch(0.98 0 0 / .68);
}
.lu__pts li::before{
  content:""; position:absolute; left:0; top:.6em;
  width:6px; height:1.5px; background:oklch(0.98 0 0 / .5);
}
.lu__foot{
  display:flex; align-items:flex-end; justify-content:space-between; gap:1rem;
  margin-top:.9rem; padding-top:.85rem;
  border-top:1px solid oklch(0.98 0 0 / .16);
}
.lu__price{ display:flex; align-items:baseline; gap:.4rem; flex-wrap:wrap; }
.lu__price i{ font-style:normal; font-size:.76rem; color:oklch(0.98 0 0 / .6); }
.lu__price b{
  font-family:var(--disp); font-variation-settings:"wdth" var(--wide);
  font-size:1.5rem; letter-spacing:-.035em;
}
.lu__price em{ font-style:normal; font-size:.76rem; color:oklch(0.98 0 0 / .6); }
.lu__go{
  flex:none; display:grid; place-items:center; width:2.1rem; height:2.1rem;
  border-radius:50%; border:1px solid oklch(0.98 0 0 / .3);
  transition:background var(--t-2) var(--e), border-color var(--t-2) var(--e),
             transform var(--t-2) var(--e);
}
.lu__go svg{
  width:1rem; height:1rem; fill:none; stroke:currentColor;
  stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round;
}
.lu__item:hover .lu__go, .lu__item:focus-visible .lu__go{
  background:oklch(0.98 0 0); border-color:oklch(0.98 0 0);
  color:oklch(0.16 0.008 258); transform:translateX(3px);
}
.lu__item:focus-visible{ outline:2px solid var(--signal); outline-offset:3px; }

@media (min-width:900px){
  .lu{ overflow:visible; gap:4px; }
  .lu__item{
    flex:1 1 0; min-width:0; min-height:clamp(440px, 46vw, 580px);
    transition:flex-grow 620ms var(--e);
  }
  /* Ask for one and it opens. The rest give up the room rather than shrinking
     out of legibility, which is why the closed state keeps its name and price. */
  .lu:hover .lu__item{ flex-grow:.78; }
  .lu__item:hover, .lu__item:focus-within{ flex-grow:2.1; }
  .lu__item:hover img, .lu__item:focus-within img{ transform:scale(1); }
  .lu__item:not(:hover):not(:focus-within) img{ filter:saturate(.72) brightness(.9); }

  /* The supporting copy is the reward for opening a panel. It collapses to
     nothing rather than fading in place, so the name never jumps. */
  .lu__more{
    display:grid; grid-template-rows:0fr;
    opacity:0; transition:grid-template-rows 620ms var(--e), opacity 340ms var(--e);
  }
  .lu__more > *{ overflow:hidden; min-height:0; }
  .lu__item:hover .lu__more, .lu__item:focus-within .lu__more{
    grid-template-rows:1fr; opacity:1; transition-delay:0ms, 150ms;
  }
  .lu__h{ font-size:clamp(1.35rem, .9rem + 1.1vw, 2.1rem); }
}

@media (prefers-reduced-motion:reduce){
  .lu__item, .lu__item img, .lu__more, .lu__go{ transition:none; }
  .lu__more{ grid-template-rows:1fr; opacity:1; }
  .lu:hover .lu__item{ flex-grow:1; }
}

/* ── 11. THE STUDIO ────────────────────────────────────────── */
.studio{ padding:var(--s8) 0; background:
  linear-gradient(180deg, var(--paper), var(--blue-t) 42%, var(--paper)); }
.studio .sec-head{ margin-bottom:var(--s5); }
.studio__shell{
  max-width:calc(var(--maxw) + 2rem); margin-inline:auto; padding-inline:var(--pad);
  display:grid; gap:var(--s5);
}
.studio__viz{ display:grid; gap:var(--s3); align-content:start; }
.studio__stage{
  position:relative; border-radius:var(--r-lg); overflow:hidden;
  box-shadow:var(--lift-3); background:var(--cloud);
  aspect-ratio:16/10;
}
.studio__stage img,.studio__stage canvas{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:50% 50%;
}
.studio__cv{ animation:fadein var(--t-2) var(--e); }
.studio__cv.is-swap{ animation:swap 420ms var(--e); }
@keyframes fadein{ from{ opacity:0; } }
@keyframes swap{ from{ opacity:0; transform:scale(1.015); } }
.studio__spin{
  position:absolute; inset:0; z-index:3; display:none; place-items:center;
  background:oklch(0.145 0.008 258 / .62); backdrop-filter:blur(2px);
}
.studio__stage.is-loading .studio__spin{ display:grid; }
.studio__spin::after{
  content:""; width:34px; height:34px; border-radius:50%;
  border:3px solid var(--blue-2); border-top-color:var(--blue);
  animation:spin .8s linear infinite;
}
@keyframes spin{ to{ transform:rotate(360deg); } }
.studio__chip,.studio__quote{
  position:absolute; z-index:2; border-radius:999px; padding:.55rem .95rem;
  background:oklch(0.185 0.009 258 / .88); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  box-shadow:var(--lift-1); display:flex; align-items:center; gap:.6rem; line-height:1.25;
}
.studio__chip{ left:.9rem; top:.9rem; }
.studio__dot{ width:22px; height:22px; border-radius:50%; flex:none; box-shadow:inset 0 0 0 1.5px oklch(0 0 0 /.12); transition:background var(--t-2) var(--e); }
.studio__dot.is-clear{ background:repeating-linear-gradient(45deg,var(--day),var(--day) 3px,var(--slab) 3px,var(--slab) 6px) !important; }
.studio__chipT{ display:grid; }
.studio__chip b{ font-size:.92rem; }
.studio__chip small{ font-size:.78rem; color:var(--mute); }
.studio__quote{ right:.9rem; top:.9rem; flex-direction:column; align-items:flex-end; gap:0; border-radius:var(--r-sm); }
.studio__quote span{ font-size:.7rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase; color:var(--mute); }
.studio__quote b{ font-family:var(--disp); font-size:1.35rem; letter-spacing:-.02em; }
.studio__quote small{ font-size:.76rem; color:var(--mute); }

.studio__cars{ display:grid; grid-template-columns:repeat(4,1fr); gap:.5rem; }
.carpick{
  position:relative; display:grid; gap:.05rem; text-align:left; padding:.6rem .7rem;
  background:var(--paper); border:1.5px solid var(--edge); border-radius:var(--r-sm);
  transition:border-color var(--t-1) var(--e), transform var(--t-1) var(--e), box-shadow var(--t-2) var(--e);
  min-height:56px;
}
.carpick:hover{ transform:translateY(-2px); box-shadow:var(--lift-1); }
.carpick[aria-checked="true"]{ border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-t); }
.carpick b{ font-size:.9rem; }
.carpick small{ font-size:.74rem; color:var(--mute); }
.carpick__ours{
  position:absolute; top:-7px; right:-4px; font-size:.6rem; font-weight:800;
  letter-spacing:.05em; text-transform:uppercase; background:var(--sun); color:var(--ink);
  padding:.1rem .38rem; border-radius:999px;
}


/* ── the finish picker, under the car ──────────────────────────
   It used to be step two. A finish is not really a question — it is a way of
   looking at the colour you already chose — and behind a Next button it meant
   anyone wanting matte or satin had to guess we offered them. On screen the
   whole time, it also stops the studio reading as a form. */
.finpick{
  margin-top:var(--s3); padding:var(--s4);
  background:var(--paper); border:1px solid var(--edge); border-radius:var(--r);
  box-shadow:var(--lift-1);
}
.finpick__k{
  font-size:.7rem; font-weight:800; letter-spacing:.11em; text-transform:uppercase;
  color:var(--mute); margin-bottom:.55rem;
}
.finpick__row{ display:grid; grid-template-columns:repeat(auto-fit,minmax(84px,1fr)); gap:.4rem; }
.finpick__b{
  display:grid; justify-items:center; gap:.35rem;
  padding:.55rem .35rem; border-radius:var(--r-sm);
  border:1.5px solid var(--edge); background:var(--paper);
  font-size:.78rem; font-weight:700; color:var(--slate);
  transition:border-color var(--t-1) var(--e), color var(--t-1) var(--e), transform var(--t-1) var(--e);
}
.finpick__b:hover{ transform:translateY(-2px); border-color:var(--blue-2); }
.finpick__b[aria-checked="true"]{ border-color:var(--good); color:var(--ink); }
/* each chip previews the finish in the colour that is actually on the car */
.finpick__b i{
  width:100%; height:20px; border-radius:var(--r-sm); --c:var(--blue);
  background:linear-gradient(150deg, oklch(1 0 0 /.55), transparent 42%, oklch(0 0 0 /.25)), var(--c);
  /* a hairline, so Clear and the pale greys are still a swatch and not a hole */
  box-shadow:inset 0 0 0 1px oklch(0 0 0 / .14);
}
.finpick__b i[data-fin="satin"]{ background:linear-gradient(150deg, oklch(1 0 0 /.26), transparent 55%, oklch(0 0 0 /.16)), var(--c); }
.finpick__b i[data-fin="matte"]{ background:linear-gradient(150deg, oklch(1 0 0 /.08), transparent 60%, oklch(0 0 0 /.10)), var(--c); }
.finpick__b i[data-fin="metallic"]{
  background:
    repeating-linear-gradient(118deg, oklch(1 0 0 /.20) 0 1px, transparent 1px 3px),
    linear-gradient(150deg, oklch(1 0 0 /.5), transparent 46%, oklch(0 0 0 /.22)), var(--c);
}
.finpick__b i[data-fin="shift"]{
  background:linear-gradient(110deg, oklch(0.62 0.20 300), oklch(0.66 0.19 200) 48%, oklch(0.72 0.18 150));
}
.finpick__note{ margin-top:.55rem; font-size:.82rem; color:var(--mute); min-height:2.2em; }
@media (max-width:600px){
  /* five fixed columns rather than auto-fit: auto-fit dropped Colour-shift
     onto a second row on its own, which reads as an afterthought instead of
     one of five things you can have */
  .finpick__row{ grid-template-columns:repeat(5,1fr); gap:.3rem; }
  .finpick__b{ font-size:.62rem; padding:.5rem .18rem; letter-spacing:-.01em; }
  .finpick__b i{ height:15px; }
  .finpick__note{ min-height:0; }
}

.studio__panel{
  display:grid; gap:var(--s4); align-content:start;
  background:var(--paper); border:1px solid var(--edge); border-radius:var(--r-lg);
  padding:var(--s5); box-shadow:var(--lift-2);
}
.rail{ display:flex; gap:.4rem; align-items:center; }
.rail__dot{
  flex:1; height:34px; min-width:34px; border-radius:999px;
  /* --mute on --cloud measures 2.75:1. These are step buttons, not decoration. */
  background:var(--cloud); color:var(--slate);
  font-size:.8rem; font-weight:800; display:grid; place-items:center;
  transition:background var(--t-2) var(--e), color var(--t-2) var(--e), flex var(--t-2) var(--e);
}
.rail__dot.is-done{ background:var(--blue-2); color:var(--blue-d); }
.rail__dot[aria-current="step"]{ background:var(--signal); color:var(--dark); flex:1.6; }

/* Steps slide in the direction you are travelling — forward when you answer,
   backward when you go back. */
.qstep{ opacity:0; transform:translateX(22px); transition:opacity 260ms var(--e), transform 380ms var(--spring); }
.qstep--back{ transform:translateX(-22px); }
.qstep.is-in{ opacity:1; transform:none; }
.qstep .qopt,.qstep .sw{ opacity:0; animation:qin 380ms var(--e) forwards; }
.qstep .qopt:nth-child(1),.qstep .sw:nth-child(-n+4){ animation-delay:60ms; }
.qstep .qopt:nth-child(2),.qstep .sw:nth-child(n+5):nth-child(-n+8){ animation-delay:100ms; }
.qstep .qopt:nth-child(3),.qstep .sw:nth-child(n+9):nth-child(-n+12){ animation-delay:140ms; }
.qstep .qopt:nth-child(4),.qstep .sw:nth-child(n+13):nth-child(-n+16){ animation-delay:180ms; }
.qstep .qopt:nth-child(n+5),.qstep .sw:nth-child(n+17){ animation-delay:220ms; }
@keyframes qin{ from{ opacity:0; transform:translateY(7px); } to{ opacity:1; transform:none; } }
.qstep__n{ font-size:.74rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase; color:var(--mute); }
.qstep__q{ font-size:clamp(1.3rem,1.1rem + .8vw,1.72rem); margin-top:.2rem; }
.qstep__help{ font-size:.88rem; color:var(--mute); margin-top:.4rem; }
.qopts{ margin-top:var(--s4); display:grid; gap:.5rem; }
/* Six across, not four. Twenty-four chips at four wide is six rows, and the
   panel ran most of a screen past the photograph it is meant to sit beside.
   The chips lose nothing at this size — the name still comes up on the one you
   are on, and the picture is the real preview. */
.qopts--swatches{ grid-template-columns:repeat(auto-fill,minmax(clamp(46px,7vw,58px),1fr)); gap:.4rem; }
.qopts--swatches .sw{ min-height:0; border-radius:var(--r); }
/* The phone-only "show all 24" toggle. Off everywhere else: on a wide panel
   the whole shelf fits and a button to reveal it is a step for nothing. */
.swmore{ display:none; }

.qopt{
  display:flex; align-items:center; gap:.75rem; text-align:left;
  padding:.75rem .9rem; min-height:56px;
  background:var(--paper); border:1.5px solid var(--edge); border-radius:var(--r-sm);
  transition:border-color var(--t-1) var(--e), background var(--t-1) var(--e), transform var(--t-1) var(--e);
}
.qopt:hover{ border-color:var(--edge-2); transform:translateY(-1px); }
.qopt[aria-checked="true"]{ border-color:var(--blue); background:var(--blue-t); }
.qopt[aria-pressed="true"]{ border-color:var(--blue); background:var(--blue-t); }
.qopt__t{ display:grid; gap:.08rem; flex:1; }
.qopt__t b{ font-size:.98rem; }
.qopt__t small{ font-size:.82rem; color:var(--mute); line-height:1.4; }
.qopt__sw{ width:30px; height:30px; border-radius:var(--r-sm); flex:none; box-shadow:inset 0 0 0 1.5px oklch(0 0 0 /.12); }
.qopt__k{ font-size:.86rem; font-weight:700; color:var(--blue); flex:none; }
.qopt__flag{
  flex:none; font-size:.68rem; font-weight:800; letter-spacing:.05em; text-transform:uppercase;
  background:var(--sun); color:var(--ink); padding:.22rem .55rem; border-radius:999px;
}
.qopt__tick{
  width:22px; height:22px; border-radius:var(--r-sm); flex:none;
  border:1.5px solid var(--edge-2); position:relative; transition:background var(--t-1), border-color var(--t-1);
}
.qopt[aria-pressed="true"] .qopt__tick{ background:var(--blue); border-color:var(--blue); }
.qopt[aria-pressed="true"] .qopt__tick::after{
  content:""; position:absolute; left:6px; top:5px; width:6px; height:10px;
  border:2px solid var(--dark); border-top:0; border-left:0; transform:rotate(42deg);
}

.sw{
  position:relative; aspect-ratio:1; min-height:56px; border-radius:var(--r); cursor:pointer;
  /* the gloss is a background layer, not ::after — the pseudos are both spent
     on the tick, and a swatch with no tick has nothing to say */
  background:linear-gradient(155deg, oklch(1 0 0 /.32), transparent 46%, oklch(0 0 0 /.22)), var(--c);
  border:1px solid oklch(0 0 0 /.10); overflow:hidden;
  transition:transform var(--t-1) var(--e), box-shadow var(--t-1) var(--e);
}
.sw:hover{ transform:translateY(-3px); box-shadow:var(--lift-2); }
/* Selected reads as a tick, not a white ring. A thick pale ring around a
   colour chip looks like the chip turned white — which is exactly what it was
   being read as on the light colours. */
/* Selected reads as a green tick, not a ring. Any pale ring round a colour
   chip reads as the chip turning white — which is exactly what a paper halo
   was being seen as on the light colours. The ring is green now, so it says
   "chosen" rather than "changed", and the tick carries the message. */
.sw[aria-checked="true"]{
  box-shadow:0 0 0 2.5px var(--good), 0 6px 18px oklch(0.60 0.15 155 / .34);
  transform:translateY(-3px);
}
.sw::before{
  content:""; position:absolute; z-index:3; top:5px; right:5px;
  width:22px; height:22px; border-radius:50%;
  background:var(--good); box-shadow:0 2px 7px oklch(0 0 0 / .38);
  transform:scale(0); transition:transform 260ms var(--spring);
}
.sw::after{
  content:""; position:absolute; z-index:4; top:11px; right:10.5px;
  width:10px; height:5px; border:2.2px solid var(--dark); border-top:0; border-right:0;
  transform:rotate(-45deg) scale(0); transform-origin:50% 50%;
  transition:transform 260ms var(--spring) 60ms;
}
.sw[aria-checked="true"]::before{ transform:scale(1); }
.sw[aria-checked="true"]::after{ transform:rotate(-45deg) scale(1); }
.sw__n{
  position:absolute; left:0; right:0; bottom:0; z-index:2;
  font-size:.6rem; font-weight:700; line-height:1.15; padding:.5rem .25rem .3rem;
  color:#fff; text-align:center; opacity:0;
  background:linear-gradient(180deg,transparent,oklch(0 0 0 /.6));
  transition:opacity var(--t-1) var(--e);
}
.sw:hover .sw__n,.sw[aria-checked="true"] .sw__n,.sw:focus-visible .sw__n{ opacity:1; }
.sw--sm{ min-height:34px; width:34px; border-radius:var(--r-sm); }
.sw--sm .sw__n{ display:none; }
.sw--sm::before{ width:15px; height:15px; top:3px; right:3px; }
.sw--sm::after{ width:7px; height:3.5px; top:7.5px; right:6.5px; border-width:1.8px; }

/* Clear — the "keep the paint I have" chip.
   It cannot be a colour, so it is drawn as one: pale ground, dashed edge to
   say nothing is being added, and the word in the middle. Same size and same
   tick as the rest, so it reads as one of the options rather than as a control
   that wandered into the grid. */
.sw--clear{
  background:var(--day);
  border:1.5px dashed var(--edge-2);
  display:grid; place-items:center;
}
.sw__clear{
  font-size:.7rem; font-weight:800; letter-spacing:.07em; text-transform:uppercase;
  color:var(--slate); line-height:1; padding-inline:.15rem; text-align:center;
}
.sw--clear[aria-checked="true"]{ border-color:var(--good); border-style:solid; }
.sw--clear[aria-checked="true"] .sw__clear{ color:var(--ink); }
.sw--clear .sw__n{ display:none; }

/* the three ways out of the shelf: custom, ask us, or business */
/* auto-fit, not three fixed columns: the panel is a sidebar on a wide screen
   and 90px a tile is not a tile, it is a word per line */
.qmore{
  display:grid; gap:.4rem; margin-top:var(--s4);
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
}
.qmore__t{
  display:grid; gap:.12rem; align-content:start;
  padding:.7rem .8rem; border:1px solid var(--edge); border-radius:var(--r-sm);
  background:var(--paper); position:relative;
  transition:border-color var(--t-1) var(--e), transform var(--t-1) var(--e), box-shadow var(--t-1) var(--e);
}
.qmore__t:hover{ border-color:var(--blue-2); transform:translateY(-2px); box-shadow:var(--lift-1); }
.qmore__t b{ font-size:.9rem; }
.qmore__t span{ font-size:.76rem; color:var(--mute); line-height:1.3; }
.qmore__t svg{
  position:absolute; top:.75rem; right:.7rem; width:14px; height:14px;
  fill:none; stroke:var(--blue); stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round;
  transition:transform var(--t-1) var(--e);
}
.qmore__t:hover svg{ transform:translateX(3px); }

.qnav{ display:grid; grid-template-columns:auto 1fr; gap:.6rem; }
.qnav .btn{ justify-content:center; }
.qnav .btn--quiet svg{ transition:transform var(--t-2) var(--e); }
.qnav .btn--quiet:hover svg{ transform:translateX(-3px); }

.qdone{
  border-top:1px solid var(--edge); padding-top:var(--s4); display:grid; gap:.55rem;
  opacity:0; transform:translateY(8px); transition:opacity var(--t-3) var(--e), transform var(--t-3) var(--e);
}
.qdone.is-in{ opacity:1; transform:none; }
.qdone__k{ font-size:.74rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase; color:var(--mute); }
.qdone__price{ font-family:var(--disp); font-weight:800; font-size:clamp(2.4rem,2rem + 2vw,3.2rem); letter-spacing:-.04em; line-height:1; }
.qdone__sum{ font-size:.88rem; color:var(--slate); }
.qdone__inc{ display:grid; gap:.35rem; margin:.4rem 0 .2rem; }
.qdone__inc li{ position:relative; padding-left:1.5rem; font-size:.88rem; color:var(--slate); }
.qdone__inc li::before{ content:""; position:absolute; left:0; top:.42em; width:15px; height:15px; border-radius:50%; background:var(--good); }
.qdone__inc li::after{ content:""; position:absolute; left:4.5px; top:.66em; width:6px; height:3px; border:1.8px solid var(--dark); border-top:0; border-right:0; transform:rotate(-45deg); }
.qdone__fine{ font-size:.78rem; color:var(--mute); line-height:1.5; }

@media (min-width:1000px){
  .studio__shell{
    grid-template-columns:minmax(0,1.12fr) minmax(370px,.7fr);
    grid-template-areas:"viz panel";
    gap:var(--s5) var(--s5); align-items:start;
  }
  .studio__viz{ grid-area:viz; position:sticky; top:calc(var(--nav-h) + 20px); }
  .studio__panel{ grid-area:panel; }
}

/* On a phone the photograph is the whole point, so it stays on screen while
   you answer — otherwise every tap means scrolling back up to see what it did. */
@media (max-width:999px){
  .studio__viz{
    position:sticky; top:calc(var(--nav-h) + 6px); z-index:3;
    margin-inline:calc(var(--pad) * -1); padding:.5rem var(--pad);
    background:oklch(0.145 0.008 258 / .86);
    backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  }
  .studio.is-done .studio__viz{
    position:static; background:none; backdrop-filter:none; -webkit-backdrop-filter:none;
  }
  .studio__cars{ display:flex; gap:.4rem; overflow-x:auto; min-width:0; max-width:100%; scrollbar-width:none; -ms-overflow-style:none; }
  .studio__cars::-webkit-scrollbar{ display:none; }
  .carpick{ flex:0 0 auto; min-height:44px; padding:.4rem .8rem; align-content:center; }
  .carpick small{ display:none; }
  .carpick__ours{ top:-6px; right:-2px; font-size:.55rem; }
}
@media (max-width:719px){
  /* On a phone the stage is about 340px wide and the car fills it corner to
     corner, so a floating chip and a floating price sit ON the paintwork —
     which is the one thing the panel exists to show. Give the card a strip of
     its own at the bottom instead: the photograph takes the height above it,
     the two labels sit in the strip, and nothing overlaps the car. */
  .studio__stage{ aspect-ratio:16/11.3; }
  .studio__stage img, .studio__stage canvas{ height:calc(100% - 44px); }
  .studio__chip, .studio__quote{
    top:auto; bottom:0; height:44px; padding:0 .7rem; border-radius:0;
    background:none; box-shadow:none;
    backdrop-filter:none; -webkit-backdrop-filter:none;
  }
  .studio__chip{ left:0; gap:.45rem; }
  .studio__chip b{ font-size:.86rem; }
  .studio__chip small{ font-size:.72rem; }
  .studio__dot{ width:18px; height:18px; }
  .studio__quote{ right:0; flex-direction:row; align-items:baseline; gap:.4rem; }
  .studio__quote span{ display:none; }          /* the word ESTIMATE, not the number */
  .studio__quote s{ font-size:.78rem; }
  .studio__quote b{ font-size:1.05rem; }
  .studio__quote small{ display:none; }         /* "whole car · sedan" is in the panel below */
  .studio__panel{ padding:var(--s4); }

  /* Get to the colours sooner: the question and its help line were taking a
     third of the screen before the first swatch. */
  .studio__panel .qstep__head{ margin-bottom:var(--s3); }
  .studio__panel .qstep__q{ font-size:1.15rem; }
  .studio__panel .qstep__help{ font-size:.86rem; margin-top:.2rem; }

  /* The sticky block was the photograph AND the finish picker, so a third of
     the screen stayed put while the questions crawled underneath — and the
     finish row, pinned above the fold, kept sliding against the panel as you
     scrolled, which is the movement that looks broken. Only the photograph
     pins now. `display:contents` drops the wrapper so the stage and the
     finish row become items of the section grid in their own right, and the
     finish sits directly under the colour swatches: pick a colour, then pick
     how it reads, in the order you would say them. */
  .studio__viz{ display:contents; }
  .studio__stage{
    position:sticky; top:calc(var(--nav-h) + 6px); z-index:3; order:1;
    box-shadow:var(--lift-3), 0 0 0 1px oklch(0 0 0 / .25);
  }
  .studio.is-done .studio__stage{ position:static; }
  .studio__panel{ order:2; }
  .finpick{ order:3; }

  /* Five across, two rows, then the rest on request. Twenty-four swatches
     unrolled pushed everything under them off the bottom of the phone. */
  .qopts--swatches{ grid-template-columns:repeat(5,1fr); gap:.45rem; }
  .qopts--swatches.is-clipped > .sw:nth-child(n+11){ display:none; }
  .swmore{
    display:block; width:100%; margin-top:.6rem; min-height:44px;
    background:var(--paper); border:1.5px solid var(--edge); border-radius:var(--r-sm);
    font-weight:700; font-size:.9rem; color:var(--ink);
  }
  .swmore:hover{ border-color:var(--blue); }
}

/* ── 13. PREP ──────────────────────────────────────────────── */
.prep{ padding:var(--s8) 0; }
.prep__in{ display:grid; gap:var(--s6); align-items:center; }
.prep__steps{ counter-reset:p; display:grid; gap:.2rem; margin:var(--s5) 0; }
.prep__steps li{
  counter-increment:p; position:relative; padding:.85rem 0 .85rem 3.1rem;
  border-bottom:1px solid var(--edge); display:grid; gap:.1rem;
}
.prep__steps li::before{
  content:counter(p,decimal-leading-zero);
  position:absolute; left:0; top:.9rem;
  font-family:var(--disp); font-weight:700; font-size:.9rem; color:var(--blue);
  background:var(--blue-t); border-radius:var(--r-sm); width:30px; height:26px;
  display:grid; place-items:center;
}
.prep__steps b{ font-size:1rem; }
.prep__steps span{ font-size:.9rem; color:var(--slate); }
.prep__say{ font-size:.95rem; color:var(--ink); font-weight:600; margin-bottom:var(--s4); }
.prep__fig{ border-radius:var(--r-lg); overflow:hidden; box-shadow:var(--lift-3); background:var(--cloud); }
.prep__fig img{ width:100%; height:auto; }
.prep__fig figcaption{ padding:.85rem var(--s4); font-size:.84rem; color:var(--mute); background:var(--mist); }
@media (min-width:940px){ .prep__in{ grid-template-columns:1.02fr .98fr; gap:var(--s7); } }

/* ── 14. PROTECTION (the dark room) ────────────────────────── */
.protect{ background:var(--pitch); color:var(--ink); padding:var(--s8) 0; border-block:1px solid var(--steel); }

/* The comparison holds the screen while you scroll through it: the section is
   tall, the stage inside is sticky, and the wipe is driven by how far through
   the section you are. You arrive, it stops, the film goes on, you carry on. */
.protect--pin{ height:290vh; padding:0; }
.protect__pin{
  position:sticky; top:0; min-height:100svh;
  display:grid; align-content:center; gap:var(--s5);
  padding-block:calc(var(--nav-h) + var(--s5)) var(--s6); overflow:hidden;
}
.protect--pin .sec-head{ margin-bottom:0; }
.protect--after{ padding-top:var(--s7); }
/* Set down and back from the two states it separates, so the eye reads the
   pair and not the conjunction. Scoped: a bare `.vs` is already the class on
   the comparison table further down the page. */
/* Neither half breaks in the middle. Once the line is allowed to run on a
   wide screen there is room for all of it, and "With / PPF" splitting across
   two lines would be worse than the stack it replaced. */
.protect__side{ white-space:nowrap; }
.protect__vs{
  font-size:.52em; font-weight:700; letter-spacing:.10em;
  text-transform:uppercase; color:var(--night-t);
  vertical-align:.22em; margin-inline:.08em;
}
/* Phone: one line, and only the line. The <br class="only-sm"> in the markup
   stacked the two halves; hiding it puts them back on one row, and the size
   comes down enough to hold "With PPF vs Without PPF" at 320px. The deck goes
   with it — "same road, same stones, same miles" is the picture's caption,
   and the picture is directly underneath. */
@media (max-width:799px){
  .protect--pin h2 br.only-sm{ display:none; }
  .protect--pin h2{ font-size:clamp(1.5rem, .72rem + 3.5vw, 2.4rem); }
  /* font-size:0 silences the caption text without touching the cue, which
     sets its own size back — and needs nowrap, or the zeroed line box breaks
     "Keep scrolling" in half. */
  /* The caption is silenced with font-size:0, which leaves its whitespace
     behind as an inline box — that stray half-space is what pushed the cue
     off centre. A flex row centres what is left of the line whatever the
     zeroed text node does. */
  .protect--pin .deck{ font-size:0; display:flex; justify-content:center; }
  .protect--pin .deck .protect__cue{ font-size:.95rem; white-space:nowrap; }

  /* The conjunction, in the shop's gold. Set small-but-not-tiny and on the
     baseline rather than raised: at 24px the old .52em superscript read as a
     typo between two words rather than as the word "versus". */
  .protect--pin .protect__vs{
    /* --gold, not --sun: --sun is aliased to the near-neutral brass, which
       put a grey word between two white ones. */
    font-size:.62em; color:var(--gold); vertical-align:baseline;
    letter-spacing:.06em; margin-inline:.22em;
  }
}
.protect__cue{
  display:inline-flex; align-items:center; gap:.5rem;
  font-weight:700; color:var(--sun);
}
.protect__cue::after{
  content:""; width:9px; height:9px; border-bottom:2px solid currentColor;
  border-right:2px solid currentColor; transform:rotate(45deg) translate(-1px,-1px);
  animation:cuedrop 2.2s var(--e) infinite;
}
.protect__cue.is-done{ color:var(--night-t); }
.protect__cue.is-done::after{ animation:none; opacity:.35; }
@keyframes cuedrop{ 0%,100%{ transform:rotate(45deg) translate(-1px,-1px); } 50%{ transform:rotate(45deg) translate(2px,2px); } }
@media (prefers-reduced-motion: reduce){
  .protect--pin{ height:auto; padding-block:var(--s8); }
  .protect__pin{ position:static; min-height:0; padding:0; }
  .protect__cue::after{ animation:none; }
}
@media (max-width:719px){
  .protect--pin{ height:250vh; }
  /* The pin centres its content in a full screen. On a phone the heading and
     the stage together come up about 120px short of that, so centring split
     the difference and dropped a band of empty black between the nav and the
     heading. Anchored to the top instead — the stage still finishes well
     inside the screen, so nothing is lost off the bottom. */
  .protect__pin{ align-content:start; }
}
.protect h2{ color:var(--bright); }
.protect .deck{ color:var(--night-t); }
.protect .kicker{ color:var(--sun); }
.protect__stage{ display:block; max-width:calc(var(--maxw) + 2rem); margin-inline:auto; padding-inline:var(--pad); }
.protect__legend{ margin:var(--s4) auto 0; max-width:62ch; text-align:center; font-size:.8rem; color:oklch(0.66 0.015 250); }
.duel__say{ margin:var(--s4) auto var(--s8); max-width:58ch; text-align:center; color:var(--night-t); font-size:1rem; }
.duel__say b{ color:var(--bright); }

/* The comparison, grouped by product.
   It was one card per question, which made "stops stone chips" look like a
   section of the page in its own right and buried the thing anyone is actually
   choosing between. Four products, four cards, the same eight lines in the
   same order down each — so you read a column to understand one option, or
   read across to settle one question. Cards rather than a table because a
   table this wide has to scroll sideways on a phone. */
/* A comparison is a matrix. Four cards each repeating the same six row
   labels made the reader hold the question in their head while they walked
   across, which is why it read as a wall of words. One row per question, one
   column per option, and the eye can run either way. */
.vs__scroll{
  position:relative;
  overflow-x:auto; overscroll-behavior-x:contain;
  margin-inline:calc(var(--pad) * -1); padding-inline:var(--pad);
  scrollbar-width:thin;
}
/* Below the table's own width the last column falls off the screen, and a
   table that scrolls with no edge to it looks like a table that is simply
   cut off. The fade says there is more, and it goes away once you reach it. */
.vs__scroll::after{
  content:""; position:sticky; right:0; top:0; float:right;
  width:44px; height:1px; pointer-events:none;
}
.vs__wrapx{ position:relative; }
.vs__wrapx::after{
  content:""; position:absolute; top:0; right:0; bottom:0; width:56px;
  pointer-events:none; opacity:1; transition:opacity 260ms var(--e);
  background:linear-gradient(90deg, transparent, var(--void) 88%);
}
.vs__wrapx.is-end::after{ opacity:0; }
.vs__swipe{
  display:none; margin-top:.7rem; font-size:.78rem; color:var(--faint);
  align-items:center; gap:.4rem;
}
.vs__swipe::after{
  content:""; width:22px; height:1px; background:currentColor;
  animation:vsSwipe 1.8s var(--e) infinite;
}
@keyframes vsSwipe{ 0%,100%{ transform:translateX(0); opacity:.5; } 50%{ transform:translateX(7px); opacity:1; } }
@media (max-width:760px){ .vs__swipe{ display:flex; } }
@media (prefers-reduced-motion:reduce){ .vs__swipe::after{ animation:none; } }
.vs__t{
  width:100%; min-width:640px; border-collapse:separate; border-spacing:0;
  font-size:.95rem;
}
.vs__t th, .vs__t td{ text-align:left; vertical-align:middle; }

/* Column heads */
.vs__t thead th{
  padding:var(--s4) 1rem .9rem; vertical-align:bottom;
  border-bottom:1px solid var(--edge);
}
.vs__t thead th b{
  display:block; font-family:var(--disp);
  font-variation-settings:"wdth" var(--wide);
  font-size:1.02rem; font-weight:700; letter-spacing:-.015em; line-height:1.15;
  color:var(--ink);
}
.vs__flag{
  display:inline-block; margin-bottom:.5rem;
  background:var(--go); color:oklch(0.16 0.02 152);
  font-size:.68rem; font-weight:800; letter-spacing:.04em;
  padding:.28rem .6rem; border-radius:999px; white-space:nowrap;
}

/* Row labels: the question being asked. */
.vs__t tbody th{
  padding:.85rem 1.1rem .85rem 0; font-weight:600; color:var(--slate);
  border-bottom:1px solid var(--edge); width:34%;
}
.vs__t tbody td{
  padding:.85rem 1rem; border-bottom:1px solid var(--edge);
  color:var(--mute); white-space:nowrap;
}
.vs__t tbody tr:last-child th, .vs__t tbody tr:last-child td{ border-bottom:0; }
.vs__t tbody tr:hover th{ color:var(--ink); }
.vs__t tbody tr:hover td{ background:oklch(1 0 0 / .03); }

/* The mark carries the answer; the words behind it only qualify it. */
.vs__t td i{
  display:inline-grid; place-items:center; vertical-align:-.28em;
  width:1.2rem; height:1.2rem; border-radius:50%; margin-right:.55rem;
}
.vs__t td i::before{ content:""; display:block; }
.vs__t td[data-v="yes"] i{ background:var(--go-t); }
.vs__t td[data-v="yes"] i::before{
  width:6px; height:11px; margin-top:-3px;
  border:2px solid var(--go); border-top:0; border-left:0;
  transform:rotate(42deg);
}
.vs__t td[data-v="part"] i{ background:oklch(1 0 0 / .08); }
.vs__t td[data-v="part"] i::before{
  width:9px; height:2px; background:var(--mute); border-radius:2px;
}
.vs__t td[data-v="no"] i{ background:oklch(0.64 0.14 25 / .13); }
.vs__t td[data-v="no"] i::before{
  width:10px; height:10px;
  background:
    linear-gradient(45deg, transparent 43%, var(--no) 43% 57%, transparent 57%),
    linear-gradient(-45deg, transparent 43%, var(--no) 43% 57%, transparent 57%);
}
.vs__t td[data-v="yes"] span{ color:var(--ink); font-weight:600; }

/* The recommended column is a column, not a card: a continuous tint down the
   table so the eye can follow it without a border cutting the rows apart. */
.vs__t th.is-hero, .vs__t td.is-hero{
  background:linear-gradient(180deg, var(--go-t), oklch(1 0 0 / .02));
  box-shadow:inset 1px 0 0 var(--go-r), inset -1px 0 0 var(--go-r);
}
.vs__t thead th.is-hero{
  border-radius:var(--r) var(--r) 0 0;
  box-shadow:inset 1px 0 0 var(--go-r), inset -1px 0 0 var(--go-r), inset 0 1px 0 var(--go-r);
}
.vs__t tbody tr:last-child td.is-hero{
  border-radius:0 0 var(--r) var(--r);
  box-shadow:inset 1px 0 0 var(--go-r), inset -1px 0 0 var(--go-r), inset 0 -1px 0 var(--go-r);
}
.vs__t thead th.is-hero b{ color:var(--go); }

.vs__after{ display:flex; flex-wrap:wrap; gap:.7rem; margin-top:var(--s5); }
.vis-hidden{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
}

@media (max-width:640px){
  .vs__t{ font-size:.88rem; }
  .vs__t tbody th{ width:auto; min-width:11rem; }
  .vs__t thead th, .vs__t tbody td{ padding-inline:.7rem; }
}

/* On a phone the matrix cannot be a matrix. 640px of table in a 343px window
   puts the recommended column — the one the section exists to argue for — off
   screen, and every attempt at flattening 28 cells into one column was a wall
   of text: floats first, then a 2x2 grid per question. Both failed the same
   way, by trying to show everything at once.

   So below 620px it becomes one product at a time (built by app.js from this
   same table, so the table is still what ships to desktop, to screen readers
   and to a browser with no JS). The recommendation opens; the four names are a
   segmented control; switching re-deals the seven answers with a stagger, and
   watching the marks change is what makes the comparison legible. */
.vsm{ display:none; }
@media (max-width:620px){
  .vs__wrapx::after, .vs__swipe{ display:none; }
  .vs.is-mobilized .vs__wrapx{ display:none; }
  .vs.is-mobilized .vsm{ display:block; }

  /* Fallback for no JS: the old stacked cards, plain and readable. */
  .vs:not(.is-mobilized) .vs__scroll{ overflow-x:visible; margin-inline:0; padding-inline:0; }
  .vs:not(.is-mobilized) .vs__t{ min-width:0; width:100%; display:block; font-size:.9rem; }
  .vs:not(.is-mobilized) .vs__t thead{ display:none; }
  .vs:not(.is-mobilized) .vs__t tbody, .vs:not(.is-mobilized) .vs__t tr{ display:block; }
  .vs:not(.is-mobilized) .vs__t tr + tr{ margin-top:.7rem; }
  .vs:not(.is-mobilized) .vs__t tbody th{
    display:block; width:auto; min-width:0; border-bottom:0; padding:0 0 .4rem;
    font-family:var(--disp); font-weight:700; font-size:1rem; color:var(--bright);
  }
  .vs:not(.is-mobilized) .vs__t tbody td{
    display:flex; align-items:center; gap:.5rem; width:auto;
    padding:.3rem 0; border-bottom:0; white-space:normal;
  }
  .vs:not(.is-mobilized) .vs__t tbody td::before{
    content:attr(data-p) ": "; font-size:.8rem; color:var(--faint);
    background:none; box-shadow:none;
  }
}

/* The segmented control. Four products, one row, no scrolling: the names are
   short enough to fit if the type is, and a control you have to scroll to see
   all of is a control that hides its options. */
.vsm__tabs{
  display:grid; grid-template-columns:repeat(4, 1fr); gap:.25rem;
  padding:.25rem; border-radius:999px;
  border:1px solid var(--edge); background:oklch(1 0 0 / .03);
}
.vsm__tab{
  /* 44px, not 38. This is the only control in the section and it was under
     the minimum comfortable touch target on the one platform it exists for. */
  min-height:44px; padding:.45rem .25rem; border-radius:999px; cursor:pointer;
  border:0; background:none; color:var(--slate);
  font:inherit; font-size:.8rem; font-weight:700; line-height:1.15;
  transition:background var(--t-1) var(--e), color var(--t-1) var(--e);
}
.vsm__tab[aria-selected="true"]{ background:var(--rise-2); color:var(--bright); box-shadow:var(--lift-1); }
.vsm__tab[data-best][aria-selected="true"]{ background:var(--go-t); color:var(--go); box-shadow:inset 0 0 0 1px var(--go-r); }
/* A badge, not a caption. As a line of small uppercase text above the answers
   it read as a section label and got skipped — which is a shame, since it is
   the one piece of advice the whole comparison exists to give. */
.vsm__flag{
  display:inline-flex; align-items:center; gap:.4rem;
  margin:.7rem 0 .1rem; padding:.32rem .7rem .32rem .55rem;
  border-radius:999px;
  font-size:.72rem; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; color:var(--go);
  background:var(--go-t); box-shadow:inset 0 0 0 1px var(--go-r);
}
.vsm__flag::before{
  content:""; width:15px; height:15px; border-radius:50%;
  background:var(--go); flex:none;
  clip-path:polygon(20% 52%, 42% 74%, 82% 28%, 90% 38%, 43% 90%, 12% 60%);
}
.vsm__rows{ display:grid; gap:0; margin:.5rem 0 0; padding:0; list-style:none; }
/* The two links close the section rather than interrupting it: full width,
   stacked, and given air after the last answer. */
@media (max-width:620px){
  .vs__after{ display:grid; gap:.5rem; margin-top:var(--s5); }
  .vs__after .btn{ width:100%; justify-content:center; }
}
/* Question left, answer right in a column of its own. Letting the answers sit
   wherever the question ended meant seven ticks down a ragged edge; fixing the
   column lines them up so the row of yeses reads as a row. */
.vsm__row{
  display:grid; grid-template-columns:1fr auto; align-items:center;
  gap:var(--s4); min-height:44px;
  padding:.62rem 0; border-bottom:1px solid var(--edge);
}
.vsm__a{ justify-self:end; min-width:7.5rem; }
.vsm__row.is-end{ border-bottom:0; }   /* sorted, so the last row is not :last-child */
.vsm__q{ font-size:.94rem; color:var(--ink); }
.vsm__a{ display:inline-flex; align-items:center; gap:.45rem; flex:none; }
.vsm__a b{ font-size:.92rem; font-weight:700; color:var(--bright); }
.vsm__a[data-v="no"] b{ color:var(--mute); font-weight:400; }
/* The mark, drawn rather than pulled from a font: three states, one element. */
.vsm__a i{
  width:20px; height:20px; border-radius:50%; flex:none; position:relative;
  background:oklch(1 0 0 / .07);
}
.vsm__a[data-v="yes"] i{ background:var(--go-t); box-shadow:inset 0 0 0 1px var(--go-r); }
.vsm__a[data-v="no"] i{ background:oklch(0.64 0.14 25 / .13); }
.vsm__a i::before, .vsm__a i::after{ content:""; position:absolute; background:currentColor; }
.vsm__a[data-v="yes"] i{ color:var(--go); }
.vsm__a[data-v="yes"] i::before{ width:2px; height:6px; left:7px; top:9px; rotate:-45deg; border-radius:1px; }
.vsm__a[data-v="yes"] i::after{ width:2px; height:10px; left:11px; top:5px; rotate:35deg; border-radius:1px; }
.vsm__a[data-v="no"] i{ color:var(--bad); }
.vsm__a[data-v="no"] i::before,
.vsm__a[data-v="no"] i::after{ width:9px; height:2px; left:5.5px; top:9px; border-radius:1px; }
.vsm__a[data-v="no"] i::before{ rotate:45deg; }
.vsm__a[data-v="no"] i::after{ rotate:-45deg; }
.vsm__a[data-v="part"] i{ color:var(--muted); }
.vsm__a[data-v="part"] i::before{ width:9px; height:2px; left:5.5px; top:9px; border-radius:1px; }
.vsm__a[data-v="part"] i::after{ display:none; }

/* Each answer arrives a beat after the one above it, so switching product
   reads as the marks changing rather than as the block blinking. */
@media (prefers-reduced-motion: no-preference){
  .vsm__row{ opacity:.999; }
  .vsm__row .vsm__a{
    opacity:0; transform:translateY(6px);
    transition:opacity 260ms var(--e), transform 320ms var(--e);
  }
  .vsm__row.is-in .vsm__a{
    opacity:1; transform:none;
    transition-delay:calc(var(--i, 0) * 34ms);
  }
}

/* ── the cost of not doing it ──────────────────────────────────
   Two numbers, side by side, so the comparison is read rather than parsed. */
.cost{
  display:grid; gap:var(--s4); align-items:stretch;
  margin-bottom:var(--s8);
}
.cost__side{
  background:var(--slab); border:1px solid var(--steel); border-radius:var(--r);
  padding:var(--s5) var(--s5) var(--s6);
}
.cost__side--good{ border-color:var(--go-r); background:linear-gradient(180deg, var(--go-t), transparent 50%), var(--slab); }
.cost__side--good .cost__n{ color:var(--go); }
.cost__k{ font-size:.78rem; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--faint); }
.cost__n{
  font-family:var(--disp); font-stretch:118%; font-weight:800; letter-spacing:-.045em;
  font-size:clamp(3rem,2.2rem + 4vw,5rem); line-height:.94; margin:.35rem 0 .5rem;
  color:var(--gold); font-variant-numeric:tabular-nums;
}
.cost__n--bad{ color:var(--muted); text-decoration:line-through; text-decoration-thickness:3px; text-decoration-color:var(--bad); }
.cost__note{ color:var(--muted); font-size:.95rem; max-width:34ch; }
.cost__vs{
  align-self:center; justify-self:center;
  font-size:.75rem; font-weight:800; letter-spacing:.14em; text-transform:uppercase;
  color:var(--faint);
}
@media (min-width:820px){
  .cost{ grid-template-columns:1fr auto 1fr; gap:var(--s5); }
}

.matrix__scroll{ overflow-x:auto; margin-inline:calc(var(--pad) * -1); padding-inline:var(--pad); }
.matrix{ min-width:760px; font-size:.92rem; }
.matrix th,.matrix td{ padding:.9rem .85rem; text-align:left; vertical-align:top; }
.matrix thead th{ font-family:var(--text); font-size:.76rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--night-t); border-bottom:1px solid oklch(1 0 0 /.16); }
.matrix thead th.is-hero{ color:var(--paper); background:var(--sun); border-radius:var(--r) var(--r) 0 0; text-align:center; position:relative; padding-top:1.9rem; }
.matrix__flag{
  position:absolute; left:50%; top:.42rem; transform:translateX(-50%);
  font-size:.6rem; letter-spacing:.08em; white-space:nowrap;
  background:var(--paper); color:var(--ink); padding:.14rem .5rem; border-radius:999px;
}
.matrix tbody th{ font-weight:700; color:#fff; width:24%; border-bottom:1px solid oklch(1 0 0 /.1); }
.matrix tbody td{ color:var(--night-t); border-bottom:1px solid oklch(1 0 0 /.1); position:relative; padding-left:2.05rem; }
.matrix tbody td::before{ content:""; position:absolute; left:.8rem; top:1.22rem; width:13px; height:13px; border-radius:50%; }
td[data-v="no"]::before{ background:oklch(1 0 0 /.14); box-shadow:inset 0 0 0 1px oklch(1 0 0 /.22); }
td[data-v="part"]::before{ background:oklch(0.85 0.155 88 / .35); box-shadow:inset 0 0 0 1px var(--sun); }
td[data-v="yes"]::before{ background:var(--good); }
.matrix td.is-hero{ background:oklch(0.855 0.155 88 / .1); color:#fff; font-weight:700; }
.matrix tbody tr:last-child td.is-hero{ border-radius:0 0 var(--r) var(--r); }
.matrix th:last-child,.matrix td:last-child{ padding-right:1.1rem; }
.matrix__note{ margin-top:var(--s5); color:var(--night-t); max-width:66ch; }
.matrix__note b{ color:#fff; }
.matrix__note a{ color:var(--sun); text-decoration:underline; text-underline-offset:3px; }

/* ── 15. THE LOANER CAR ────────────────────────────────────── */
/* The gold room. The site accent is deliberately colourless, but this one
   block keeps brass — it is the only section selling something free, and the
   warm metal against the black is what makes it read as an offer rather than
   another dark panel. Pinned locally so it never follows the site accent. */
.perk{
  --gold:      oklch(0.800 0.115 82);
  --gold-d:    oklch(0.700 0.110 78);
  --gold-t:    oklch(0.800 0.115 82 / .10);
  --gold-line: oklch(0.800 0.115 82 / .30);
  padding:var(--s8) 0;
  background:
    radial-gradient(120% 90% at 82% 18%, var(--gold-t), transparent 62%),
    oklch(0.175 0.012 70);
  border-block:1px solid var(--gold-line);
  color:var(--ink);
}
.perk__in{ display:grid; gap:var(--s5); align-items:center; }

}
/* Phone only: the warm band ends in a curve rather than a ruled edge. With
   the grey gone from the business section under it, the gold band was the
   only thing left drawing a straight line across the page — rounding its foot
   lets it sit on the page instead of butting into what follows.
   `overflow:hidden` so the radius clips the gradient, and the bottom border
   goes with it: a hairline across a curve reads as a mistake. */
.perk .kicker{ color:var(--gold); }
.perk .kicker::before{ background:var(--gold); }
.perk h2,.perk .perk__h{ color:var(--bright); }
.perk__t p{ color:var(--muted) !important; }
.perk__say{ color:var(--ink) !important; }
.perk__list b{ color:var(--bright); }
.perk__list li::before{ background:var(--gold) !important; }
.perk__list li::after{ border-color:oklch(0.20 0.012 70) !important; }
.perk .btn--primary{ --bg:var(--gold); --fg:oklch(0.20 0.012 70); --bd:var(--gold); }
.perk .btn--primary:hover{ --bg:var(--gold-d); --bd:var(--gold-d); box-shadow:0 12px 34px -14px oklch(0.80 0.115 82 / .45); }
/* the loaner photograph needs a warm shadow on this ground, not a cool one */
.perk .loaner img{ filter:drop-shadow(0 26px 30px oklch(0.10 0.02 70 / .75)); }
.perk h2{ font-size:clamp(1.7rem,1.25rem + 2vw,2.6rem); }
.perk__t p{ margin-top:var(--s4); color:var(--slate); max-width:56ch; }
.perk__say{ font-size:1.05rem; font-weight:600; color:var(--ink) !important; }

/* The price other shops charge, struck through, replaced by nothing. The
   swap does the arguing so the copy does not have to. */
/* The headline is the message here — it used to be a big number with a
   hand-drawn car beside it, which read as clip art next to real photography
   everywhere else on the page. */
.perk__h{
  font-size:clamp(2.1rem,1.3rem + 3.4vw,3.9rem);
  letter-spacing:-.035em; line-height:1.02;
}
.perk__say{ margin-top:var(--s4) !important; font-weight:600; color:var(--ink) !important; font-size:1.05rem; }
.perk__t .btn{ margin-top:var(--s5); }

/* The loaner arrives and settles, then a highlight travels across the paint —
   the same thing that happens when a car rolls out of a bay under lights.
   The shine is the car's own alpha used as a mask, so it only ever falls on
   bodywork and never on the panel behind it. */
.loaner{ position:relative; display:grid; place-items:center; isolation:isolate; }
.loaner img{
  width:100%; height:auto;
  filter:drop-shadow(0 24px 22px oklch(0.47 0.115 72 / .30));
  opacity:0; transform:translateX(-42px) scale(.985);
  transition:opacity 520ms var(--e), transform 1000ms var(--spring);
}
.loaner.is-in img{ opacity:1; transform:none; }

.loaner__shine{
  position:absolute; inset:0; z-index:2; pointer-events:none; opacity:0;
  background:linear-gradient(102deg,
    transparent 34%,
    oklch(1 0 0 / .0) 42%,
    oklch(1 0 0 / .72) 50%,
    oklch(1 0 0 / .0) 58%,
    transparent 66%);
  background-size:260% 100%; background-position:120% 0;
  -webkit-mask-image:url("../img/loaner-800.png"); mask-image:url("../img/loaner-800.png");
  -webkit-mask-size:100% 100%; mask-size:100% 100%;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
}
.loaner.is-in .loaner__shine{ animation:loanerShine 1400ms var(--e-io) 620ms forwards; }
@keyframes loanerShine{
  0%{ opacity:0; background-position:120% 0; }
  22%{ opacity:1; }
  85%{ opacity:1; }
  100%{ opacity:0; background-position:-40% 0; }
}

@media (prefers-reduced-motion: reduce){
  .loaner img{ opacity:1; transform:none; transition:none; }
  .loaner__shine{ display:none; }
}

.perk__list{ display:grid; grid-template-columns:1fr 1fr; gap:var(--s3) var(--s4); margin-top:var(--s5); }
.perk__list li{ display:grid; gap:.1rem; padding-left:1.5rem; position:relative; }
.perk__list li::before{ content:""; position:absolute; left:0; top:.4em; width:16px; height:16px; border-radius:50%; background:var(--sun-d); }
.perk__list li::after{ content:""; position:absolute; left:4.5px; top:.64em; width:6px; height:3px; border:2px solid #fff; border-top:0; border-right:0; transform:rotate(-45deg); }
.perk__list b{ font-size:.98rem; }
.perk__list span{ font-size:.86rem; color:var(--mute); }
@media (min-width:900px){ .perk__in{ grid-template-columns:1.02fr .98fr; gap:var(--s7); align-items:center; } }
/* Stacked, the button landed between the list and the photograph of the car
   it is offering you, so you were asked to act before you had seen the thing.
   `display:contents` on the text column lets the four parts take their own
   order in the section grid: heading, line, list, car, then the button. */
@media (max-width:899px){
  .perk__in{ gap:0; align-items:start; }
  .perk__t{ display:contents; }
  .perk__h{ order:1; }
  .perk__say{ order:2; }
  .perk__list{ order:3; }
  .perk__in .loaner{ order:4; margin-top:var(--s5); }
  .perk__t > .btn{ order:5; margin-top:var(--s5); justify-self:start; }
}

/* ── 16. WARRANTY ──────────────────────────────────────────── */
.warranty{ padding:var(--s8) 0; }
/* What the certificate does on a phone is decided down with the certificate
   itself, under `.cert__foot`. */
.warranty__in{ display:grid; gap:var(--s6); align-items:center; }
.warranty__copy h2 + .deck{ margin-top:var(--s4); }
/* The promise is the headline. It used to be a 12px uppercase kicker above a
   sentence about bubbling — the smallest type in the section carrying the
   biggest claim in the business. */
.warranty__shout{
  font-size:clamp(2.6rem,1.5rem + 5.4vw,5.4rem);
  letter-spacing:-.045em; line-height:.92; text-wrap:balance;
}
.warranty__lead{
  margin-top:var(--s4); max-width:46ch;
  font-size:clamp(1rem,.95rem + .3vw,1.14rem); color:var(--slate);
}
.warranty__list{ display:grid; margin:var(--s5) 0; }
.warranty__list li{ display:grid; grid-template-columns:auto 1fr; align-items:baseline; gap:.2rem 1rem; padding:.85rem 0; border-bottom:1px solid var(--edge); }
.warranty__list b{ font-size:1rem; }
.warranty__list span{ font-size:.89rem; color:var(--mute); text-align:right; }
/* The promise, said in two words, arriving one at a time. */
.warranty__free{
  display:flex; align-items:baseline; gap:.35em; margin:var(--s5) 0 .4rem;
  font-family:var(--disp); font-weight:800; letter-spacing:-.045em; line-height:.95;
  font-size:clamp(3rem,11vw,5rem);
  /* The whole section is this one word. Grey was doing it no favours.
     Carried in the house blue so the warranty reads as the brand's promise
     rather than as a generic positive. */
  color:var(--blue);
}
.warranty__free span{
  opacity:0; transform:translateY(.14em);
  transition:opacity 460ms var(--e), transform 560ms var(--spring);
}
/* The separator is punctuation, not a word: it steps back so "Free" and
   "Forever" carry the line. Alpha rather than opacity, which the reveal owns. */
.warranty__free span:nth-child(2){ color:oklch(from var(--dw) l c h / .45); transition-delay:180ms; }
.warranty__free span:nth-child(3){ transition-delay:340ms; }
.warranty__free.is-in span{ opacity:1; transform:none; }
.warranty__sub{ font-size:1rem; color:var(--paper-2, oklch(0.86 0.004 258)); margin-bottom:var(--s5); max-width:46ch; }

/* Phone. The guarantee is three words — free, forever, yours — and it was
   taking a screen and a half to say them. The shout and the FREE · FOREVER
   line stay full size, because they are the point; everything around them
   gets out of their way, and the three covered faults become one row of chips
   instead of three full-width rows with a rule between each. */
/* A phone-only shortcut; see the `.jumpq` block below. */
.jumpq{ display:none; }

/* Prices end on a bright blue card and the guarantee opens on a headline,
   with nothing between them but black. On a phone, where the two arrive one
   after the other rather than side by side, that read as the page losing its
   place rather than as one section ending and another starting.

   A hairline that fades out before it reaches either edge, with the mark from
   the logo sitting above it. It is set half way into the section's bottom
   padding so there is the same air above the rule as below it: pinned to the
   section's own edge it would have had forty pixels above and none beneath,
   because the guarantee no longer carries a top padding on a phone.

   Scoped to the homepage's own prices section by id: the same block appears
   on paint-protection.html, where the section under it changes background and
   draws its own rule, and a second divider on top of that is one too many. */
@media (max-width:719px){
  #pricing{ position:relative; }
  #pricing::after{
    content:""; position:absolute; bottom:calc(var(--s8) * .5);
    left:var(--pad); right:var(--pad); height:1px;
    background:linear-gradient(90deg,
      transparent 0%, oklch(1 0 0 / .13) 24%,
      oklch(1 0 0 / .13) 76%, transparent 100%);
  }
  #pricing::before{
    content:""; position:absolute; left:50%; bottom:calc(var(--s8) * .5 + 7px);
    width:7px; height:7px; margin-left:-3.5px;
    transform:rotate(45deg);
    box-shadow:inset 0 0 0 1px oklch(from var(--dw) l c h / .55);
  }
}

@media (max-width:719px){
  /* Centred, and read as one claim rather than four blocks: the promise, the
     two words that are the promise, what it costs, then what it covers.
     "Free · Forever" comes down from the size it holds on a desktop — beside
     a 5rem headline it is a second headline, and on a phone two headlines in
     a column is a shouting match. */
  /* The panel belongs between the promise and the two words that are the
     promise: read what we do, watch it happen, then get told what it costs.
     Left at the end of the column it landed under the button, which is the
     one place on a page nothing should follow.

     `display:contents` drops the copy wrapper so its paragraphs become items
     of the section grid in their own right and the card can be ordered in
     among them. The gap goes to zero with it — every one of those paragraphs
     already carries its own margin, and they kept that rhythm when they were
     items of the wrapper, so zero here reproduces it exactly rather than
     adding a second spacing system on top. */
  /* Every section on the page opens and closes with `--s8`, so any two of
     them meeting on a phone leave 160px of black between them. Beside a
     headline this size that read as a missing section rather than as air.
     The section above still closes with its own `--s8`, which is the gap. */
  .warranty{ padding-top:0; }
  .warranty__in{ gap:0; justify-items:center; text-align:center; }
  .warranty__copy{ display:contents; }
  .warranty__shout{ order:1; }
  .warranty__lead{ order:2; }
  .cert{ order:3; width:100%; margin:var(--s5) 0 var(--s3); }
  .warranty__free{ order:4; }
  .warranty__sub{ order:5; }
  .warranty__shout{ font-size:clamp(2.1rem, 1.2rem + 4.4vw, 3rem); }
  .warranty__shout br{ display:none; }
  /* Wide enough for the short version to sit on one line: two centred lines
     of five words read as a stack rather than as a sentence. */
  .warranty__lead{ font-size:.95rem; margin-top:var(--s3); max-width:42ch; text-wrap:balance; }
  .warranty__free{
    justify-content:center; margin:var(--s4) 0 .3rem;
    font-size:clamp(2rem, 1.1rem + 4.2vw, 2.8rem);
  }
  .warranty__sub{ font-size:.9rem; margin-bottom:var(--s4); max-width:32ch; }
  /* The three faults go. They were a row of chips saying bubbling, peeling
     and adhesive failure directly under a panel that shows two of them
     happening and a line of copy that names both — the same short list three
     times in one screen. The full list is on warranty.html, which the button
     underneath goes to. */
  .warranty__list{ display:none; }
}
@media (prefers-reduced-motion: reduce){
  .warranty__free span{ opacity:1; transform:none; transition:none; }
}

/* The two things the guarantee is actually about, shown one at a time:
   film lifts, we take it off and do it again.

   The bubble used to be drawn as a tunnel running the full height of the
   panel along the body line, in `overlay` blend. On a bright blue panel that
   reads as a beam of light rather than as trapped air, which is fatal: it is
   the one defect in this card everybody is meant to recognise on sight. It
   is now a discrete lens on an otherwise clean panel, drawn the way the eye
   actually identifies a raised dome — a contact shadow on the panel beneath
   it, a rim that darkens where the film turns away from the light, one hard
   specular up toward the light, and a reflection band crossing it at its own
   angle. A straight reflection that kinks is the cheapest possible proof
   that a surface is not flat.

   The panel used to carry a body line down it, on the theory that a real
   bubble tunnels along one. Nobody read it as a crease: it never moved, so
   across a nine second loop it was simply a line sitting in every frame of
   an animation. The panel is plain gloss now, and the only things on it are
   the two faults and the tool that fixes them.

   The peel keeps its geometry, which was the half that worked, and gains
   what it was missing: the standing corner casts a shadow back onto the
   panel, the bare paint is uncovered progressively as the flap swings rather
   than fading up underneath it, and the squeegee actually reaches the corner
   now. It used to travel `100% + 46px` of its own 34px width — about 80px
   across a 260px panel — so it died a third of the way over and nothing it
   did ever lined up with the thing it was supposed to be fixing. It is now a
   full-width mover carrying a fixed-width blade, so one pass is one pass.

   All CSS: no JS, nothing to go wrong, and it costs one composited layer. */
.cert__demo{
  position:relative; height:168px; margin-bottom:var(--s5);
  border-radius:var(--r-sm); overflow:hidden; isolation:isolate;
}
.cert__panel{
  position:absolute; inset:0;
  background:
    /* the sky the panel is reflecting — gloss is mostly what it reflects */
    radial-gradient(ellipse 58% 42% at 26% 22%, oklch(1 0 0 / .22), oklch(1 0 0 / 0) 70%),
    radial-gradient(ellipse 40% 30% at 74% 78%, oklch(1 0 0 / .12), oklch(1 0 0 / 0) 70%),
    linear-gradient(160deg, var(--blue) 0%, var(--blue-d) 62%, var(--blue) 100%);
}
/* A lens of trapped air. Everything here is a lighting cue: nothing about
   the shape alone would say "raised". */
.cert__bubble{
  position:absolute; left:46%; top:48%;
  width:98px; height:56px; margin:-28px 0 0 -49px;
  border-radius:50%;
  background:
    /* the panel's own reflection, crossing the dome at its own angle */
    linear-gradient(114deg,
      oklch(1 0 0 / 0)   28%,
      oklch(1 0 0 / .26) 43%,
      oklch(1 0 0 / .46) 50%,
      oklch(1 0 0 / .10) 59%,
      oklch(1 0 0 / 0)   72%),
    /* the far side, where the film turns away from the light */
    radial-gradient(ellipse 74% 74% at 68% 76%, oklch(0 0 0 / .38), oklch(0 0 0 / 0) 64%),
    /* the dome itself. Tinted toward the panel rather than white: it is the
       same film, lit differently, not a stone sitting on the paint. */
    radial-gradient(ellipse 98% 98% at 32% 24%,
      oklch(.95 .05 240 / .34)  0%,
      oklch(.95 .05 240 / .12) 38%,
      oklch(0 0 0 / .06) 66%,
      oklch(0 0 0 / .20) 100%);
  box-shadow:
    0 13px 20px -9px oklch(0 0 0 / .62),       /* what it casts on the panel */
    inset 0 7px 12px -9px oklch(1 0 0 / .52),  /* lit crown */
    inset 0 -9px 15px -9px oklch(0 0 0 / .52); /* far edge falling away */
  filter:blur(.4px);                           /* film curves; it has no edge */
  transform:rotate(5deg) scale(0);
  opacity:0;
  animation:bub 9s var(--e) infinite;
}
/* the specular. Small, hard, and off toward the light: the single detail
   that stops it reading as a stain. */
.cert__bubble i{
  position:absolute; left:19%; top:15%; width:31%; height:31%;
  border-radius:50%;
  background:radial-gradient(ellipse at 50% 50%,
    oklch(1 0 0 / .96) 0%, oklch(1 0 0 / .34) 48%, oklch(1 0 0 / 0) 76%);
  transform:rotate(-22deg);
  filter:blur(1px);
}
/* bare paint under the lifted corner */
.cert__bare{
  position:absolute; right:0; bottom:0; width:86px; height:86px;
  clip-path:polygon(100% 0, 100% 100%, 0 100%);
  background:
    radial-gradient(ellipse 62% 62% at 90% 14%, oklch(1 0 0 / .10), oklch(1 0 0 / 0) 70%),
    linear-gradient(225deg, oklch(.46 .028 258), oklch(.30 .022 258));
  box-shadow:inset 5px -5px 14px oklch(0 0 0 / .5);
  opacity:0;
  animation:bare 9s var(--e) infinite;
}
/* what the standing corner throws back onto the panel. Offset down and
   right of the flap, because the panel is lit from the upper left. */
.cert__shade{
  position:absolute; right:0; bottom:0; width:86px; height:86px;
  clip-path:polygon(0 0, 100% 0, 0 100%);
  background:linear-gradient(315deg,
    oklch(0 0 0 / .60)  0%,
    oklch(0 0 0 / .26) 22%,
    oklch(0 0 0 / .07) 44%,
    oklch(0 0 0 / 0)   66%);
  filter:blur(4px);
  opacity:0; transform:translate(2px,2px);
  animation:shade 9s var(--e) infinite;
}
/* The corner of film, folding back along its own diagonal.
   Two elements on purpose. Opacity is a grouping property: animate it on the
   same element that carries `transform-style:preserve-3d` and the browser
   flattens the 3D, which kills backface-visibility and the fold shows its
   gloss side the whole way over. So the outer span fades and the inner one
   folds, and the perspective moves up to the fader so the folding element is
   still a direct child of it. */
.cert__flap{
  position:absolute; right:0; bottom:0; width:86px; height:86px;
  perspective:420px;
  opacity:0;
  animation:peelfade 9s var(--e) infinite;
}
.cert__flap b{
  position:absolute; inset:0;
  transform-style:preserve-3d;
  transform:rotate3d(1,-1,0,0deg);
  animation:peel 9s var(--e) infinite;
}
.cert__flap i{
  position:absolute; inset:0;
  clip-path:polygon(100% 0, 100% 100%, 0 100%);
  backface-visibility:hidden;
}
.cert__flap i:first-child{        /* gloss side, matching the panel */
  background:linear-gradient(225deg, var(--blue) 0%, var(--blue-d) 76%);
}
.cert__flap i:last-child{         /* adhesive side, once it is over */
  background:linear-gradient(225deg, oklch(.93 .008 258), oklch(.79 .012 258));
  transform:rotate3d(1,-1,0,180deg);
}
/* A full-width mover carrying a fixed blade, so `translateX(100%)` is one
   width of the panel and the pass is a pass. Linear, too: a squeegee is
   pushed at a steady rate by a person, and an eased one reads as an effect. */
.cert__squeegee{
  position:absolute; top:-16%; bottom:-16%; left:0; width:100%;
  opacity:0; transform:translateX(-46px) rotate(6deg);
  animation:squee 9s linear infinite;
}
.cert__squeegee::before{        /* the blade, and the pressure under it */
  content:""; position:absolute; top:0; bottom:0; left:0; width:46px;
  background:linear-gradient(90deg,
    oklch(1 0 0 / 0)   0%,
    oklch(1 0 0 / .12) 30%,
    oklch(1 0 0 / .78) 50%,
    oklch(1 0 0 / .16) 62%,
    oklch(1 0 0 / 0)  100%);
  filter:blur(2px);
}
.cert__squeegee::after{         /* the wet trail it leaves behind it */
  content:""; position:absolute; top:0; bottom:0; right:100%; width:120px;
  background:linear-gradient(90deg, oklch(1 0 0 / 0), oklch(1 0 0 / .10));
  filter:blur(6px);
}
/* Two lines, one after the other: the fault, then what the shop does about
   it. The fault is named in the corner while it is on the panel, out of the
   way of the thing you are meant to be looking at. The badge is not a caption
   at all, so it does not stay in the corner — the squeegee has just cleared
   the panel and the badge lands in the middle of it, on the space the fault
   was occupying. This is a guarantee, and the last thing on screen each time
   round should be the fix rather than the fault. */
.cert__stage{ position:absolute; left:.7rem; bottom:.6rem; height:1.8em; }
.cert__fault, .cert__ok{
  position:absolute; white-space:nowrap;
  display:inline-flex; align-items:center;
  font-weight:800; font-style:normal;
  letter-spacing:.1em; text-transform:uppercase;
  border-radius:999px;
  opacity:0;
}
.cert__fault{
  left:0; bottom:0; transform-origin:left bottom;
  font-size:.72rem; padding:.28rem .7rem;
  color:oklch(1 0 0 / .96); text-shadow:0 1px 6px oklch(0 0 0 / .55);
  background:oklch(0 0 0 / .34); box-shadow:inset 0 0 0 1px oklch(1 0 0 / .12);
}
/* The badge is the one thing in the card that is not the panel, so it is
   solid rather than translucent: a white seal with the accent inked into it.
   The ink is derived from the accent rather than restated, so it follows a
   theme change and stays legible on white while it does. */
.cert__ok{
  left:50%; top:50%;
  --ok-ink:oklch(from var(--dw) .40 calc(c * 1.3) h);
  color:var(--ok-ink);
  font-size:.78rem; padding:.36rem .95rem .36rem 2rem;
  background:oklch(1 0 0 / .97);
  box-shadow:
    0 12px 26px -10px oklch(0 0 0 / .6),
    inset 0 0 0 1px oklch(from var(--dw) l c h / .30);
  transform:translate(-50%,-50%) scale(.88);
}
/* drawn, not typed: a tick glyph is at the mercy of whatever font the
   platform has for it, and this one has to sit on the baseline every time */
.cert__ok::before{
  content:""; position:absolute; left:.88rem; top:50%;
  width:.32rem; height:.6rem; margin-top:-.38rem;
  border:0 solid currentColor; border-right-width:2px; border-bottom-width:2px;
  transform:rotate(42deg);
}
.cert__stage i:nth-child(1){ animation:stgA 9s var(--e) infinite; }
.cert__stage i:nth-child(2){ animation:stgB 9s var(--e) infinite; }
.cert__ok--a{ animation:okA 9s var(--e) infinite; }
.cert__ok--b{ animation:okB 9s var(--e) infinite; }

/* One loop, two acts, and each act ends the same way: the fault is named
   while it is on the panel, the squeegee takes it off, and the badge lands on
   the clean panel behind it. Peeling runs first — a lifted corner is the
   defect everybody recognises on sight, so it is what should be on screen
   when the section arrives.

   The bubble swells past its resting size and settles back, then breathes
   once. A shape that fades up at a constant size reads as a light effect;
   one that overshoots, relaxes and moves a little reads as something being
   pushed from underneath, which is what it is. And neither fault fades out
   at the end — the blade reaches the corner at 41% and the bubble at 83%,
   and they go when it gets there. */
@keyframes peel{
  0%, 3%     { transform:rotate3d(1,-1,0,0deg); }
  15%        { transform:rotate3d(1,-1,0,154deg); }
  22%        { transform:rotate3d(1,-1,0,147deg); }
  31%        { transform:rotate3d(1,-1,0,152deg); }
  40.5%      { transform:rotate3d(1,-1,0,150deg); }
  44.5%,100% { transform:rotate3d(1,-1,0,0deg); }
}
/* fades across the first and last few degrees only. The flap's own gradient
   can never line up with the panel's, so one sitting flat at full opacity
   draws a triangle on a panel that is meant to be perfect; fading it while it
   is under ~25deg is invisible, fading it while it is flat is not. */
@keyframes peelfade{
  0%, 3%     { opacity:0; }
  8%, 41%    { opacity:1; }
  44.5%,100% { opacity:0; }
}
@keyframes bare{
  0%, 4%     { opacity:0; clip-path:polygon(100% 100%, 100% 100%, 100% 100%); }
  9%         { opacity:1; clip-path:polygon(100% 58%, 100% 100%, 42% 100%); }
  16%, 39%   { opacity:1; clip-path:polygon(100% 0, 100% 100%, 0 100%); }
  43%        { opacity:.55; clip-path:polygon(100% 74%, 100% 100%, 26% 100%); }
  45%, 100%  { opacity:0; clip-path:polygon(100% 100%, 100% 100%, 100% 100%); }
}
@keyframes shade{
  0%, 5%     { opacity:0; transform:translate(2px,2px); }
  16%        { opacity:1; transform:translate(6px,6px); }
  31%        { opacity:.9; transform:translate(5px,5px); }
  40%        { opacity:.92; transform:translate(6px,6px); }
  44.5%,100% { opacity:0; transform:translate(2px,2px); }
}
@keyframes bub{
  0%, 54%    { transform:rotate(5deg) scale(0);                       opacity:0; }
  57%        { transform:rotate(5deg) scale(.44,.36);                  opacity:1; }
  62%        { transform:rotate(5deg) scale(1.10,1.17);                opacity:1; }
  66%, 70%   { transform:rotate(5deg) scale(1,1);                      opacity:1; }
  74%        { transform:rotate(5deg) scale(1.035,1.06);               opacity:1; }
  77%, 82.5% { transform:rotate(5deg) scale(1,1);                      opacity:1; }
  85%        { transform:rotate(5deg) translateX(10px) scale(.70,.34); opacity:1; }
  87%        { transform:rotate(5deg) translateX(17px) scale(.20,.12); opacity:0; }
  87.5%,100% { transform:rotate(5deg) scale(0);                       opacity:0; }
}
/* crosses twice: once to lay the corner back, once to push the bubble out */
@keyframes squee{
  0%, 25.5%  { transform:translateX(-46px) rotate(6deg); opacity:0; }
  26%        { transform:translateX(-46px) rotate(6deg); opacity:1; }
  42%        { transform:translateX(100%)  rotate(6deg); opacity:1; }
  43%, 75.4% { transform:translateX(100%)  rotate(6deg); opacity:0; }
  75.5%      { transform:translateX(-46px) rotate(6deg); opacity:0; }
  76%        { transform:translateX(-46px) rotate(6deg); opacity:1; }
  92%        { transform:translateX(100%)  rotate(6deg); opacity:1; }
  93%, 100%  { transform:translateX(100%)  rotate(6deg); opacity:0; }
}
/* The fault names itself flatly and leaves. The badge arrives with a little
   more behind it — up from under, a touch past its size and back — because
   it is the only thing in the card that is good news. */
@keyframes stgA{
  0%, 2%     { opacity:0; transform:translateY(4px); }
  6%, 38%    { opacity:1; transform:none; }
  41%, 100%  { opacity:0; transform:translateY(4px); }
}
@keyframes stgB{
  0%, 53%    { opacity:0; transform:translateY(4px); }
  57%, 82%   { opacity:1; transform:none; }
  85%, 100%  { opacity:0; transform:translateY(4px); }
}
@keyframes okA{
  0%, 41.5%  { opacity:0; transform:translate(-50%, calc(-50% + 8px)) scale(.86); }
  44%        { opacity:1; transform:translate(-50%, -50%) scale(1.06); }
  46%, 50%   { opacity:1; transform:translate(-50%, -50%) scale(1); }
  53%, 100%  { opacity:0; transform:translate(-50%, calc(-50% - 5px)) scale(.96); }
}
@keyframes okB{
  0%, 86.5%  { opacity:0; transform:translate(-50%, calc(-50% + 8px)) scale(.86); }
  89%        { opacity:1; transform:translate(-50%, -50%) scale(1.06); }
  91%, 96%   { opacity:1; transform:translate(-50%, -50%) scale(1); }
  99%, 100%  { opacity:0; transform:translate(-50%, calc(-50% - 5px)) scale(.96); }
}

@media (prefers-reduced-motion: reduce){
  /* hold the defect on screen instead of animating it — the point is what
     the guarantee covers, and that reads fine standing still */
  .cert__bubble{ animation:none; opacity:1; transform:rotate(5deg) scale(1); }
  .cert__squeegee{ animation:none; opacity:0; }
  .cert__flap{ animation:none; opacity:1; }
  .cert__flap b{ animation:none; transform:rotate3d(1,-1,0,146deg); }
  .cert__bare{ animation:none; opacity:1; }
  .cert__shade{ animation:none; opacity:.8; transform:translate(8px,8px); }
  /* both defects are on screen at once here, so a label naming one of them
     would be wrong, and there is no fix to announce when nothing moves */
  .cert__stage, .cert__ok{ display:none; }
}
.cert{
  background:var(--sun-t); border:1.5px solid var(--sun); border-radius:var(--r-lg);
  padding:var(--s6) var(--s5); text-align:center; box-shadow:var(--lift-2);
}
/* On the homepage `--sun` is aliased to the near-neutral brass, so this card
   reads as a quiet ruled panel. business-wraps repoints `--sun` to a real
   gold for its own use, and the shared card inherited it — a yellow border
   and a yellow dashed rule that belong to nothing else in the section.
   Repointing the two tokens on the card itself fixes the border, the tint and
   the rule together, and leaves the page's gold alone everywhere else. */
body[data-page="business-wraps"] .cert{
  --sun:   var(--edge-2);
  --sun-t: oklch(1 0 0 / .04);
  /* and the panel keeps the brand blue it has on the homepage. This page
     lightens `--blue` so it can sit on gunmetal, which is right for its own
     type and wrong for a painted panel pretending to be a car. */
  --blue:   var(--dw);
  --blue-d: var(--dw-d);
}
.cert__kind{ font-size:.74rem; font-weight:800; letter-spacing:.12em; text-transform:uppercase; color:var(--sun-i); }
.cert__big{ font-family:var(--disp); font-weight:800; letter-spacing:-.04em; line-height:.98; font-size:clamp(2rem,7vw,2.7rem); margin:.4rem 0 .3rem; text-wrap:balance; }
.cert__big small{ display:block; font-family:var(--text); font-size:.34em; font-weight:700; letter-spacing:.02em; color:var(--slate); margin-top:.35em; }
.cert__big--alt{ font-size:clamp(3rem,11vw,4.4rem); color:var(--blue); }
.cert__big--alt small{ display:inline; font-size:.24em; margin-left:.2em; }
.cert__sub{ font-size:.88rem; color:var(--slate); }
.cert hr{ border:0; border-top:1.5px dashed var(--sun); margin:var(--s5) 0; }
/* Two facts, side by side and weighted, each led by its quantity: once, and
   none. As one flex row of grey sentences they were a footnote, and a
   footnote is the wrong register for the only two things on the card a
   competitor would find hard to copy. */
.cert__foot{
  display:grid; grid-template-columns:1fr 1fr;
  margin-top:var(--s5); font-size:.78rem; color:var(--slate); font-weight:600;
}
.cert__foot span{ display:grid; gap:.15rem; padding:0 var(--s3); align-content:start; }
.cert__foot span + span{ border-left:1px solid var(--edge); }
.cert__foot b{ font-family:var(--disp); font-size:1.15rem; font-weight:800; color:var(--ink); letter-spacing:-.02em; }

/* On a desktop the card is a caption under a picture, not a sheet of
   statistics. It used to carry four blocks under the panel: the promise, the
   list of faults, a ten-year figure and two footnotes. The faults line
   repeated the three rows in the left-hand column word for word, side by
   side with them, and the ten years was the film manufacturer's term rather
   than the shop's — a second, smaller guarantee standing under the first.
   Both are gone, so the panel gets the room they were using and the card says
   one thing: for as long as you own the car. The ten-year term is stated
   where it belongs, on warranty.html. */
@media (min-width:720px){
  .cert{ padding:var(--s5) var(--s5) var(--s6); }
  .cert__demo{ height:clamp(180px, 17vw, 236px); margin-bottom:var(--s5); }
  .cert__big{ margin:.1rem 0 0; }
  .cert hr{ margin:var(--s5) 0 0; }
}

/* On a phone, the panel and nothing else.

   The certificate's figures stay off. The one that mattered — ten years on
   the film — is the manufacturer's warranty rather than the shop's, and sat
   under the shop's own promise it read as a second, smaller guarantee
   competing with the first. It is stated in full on warranty.html, which the
   nav and the footer reach from every page.

   The demo went with it, on the grounds that it was a wide, slow, decorative
   loop nobody scrolling a phone waits through. That was true of what it was
   then. Rebuilt, it is the only thing on the page that shows what the two
   words in the headline actually cover, and it gets there faster than the
   sentence above it does. So it comes back — as the panel alone, with the
   card's border, background and figures left behind, because on a phone it is
   not a certificate any more, it is the illustration.

   These rules live after `.cert`'s own so they win on order rather than on a
   specificity fight nobody would enjoy reading later. */
@media (max-width:719px){
  .cert{
    display:block; padding:0;
    background:none; border:0; box-shadow:none;
  }
  .cert > :not(.cert__demo){ display:none; }
  .cert__demo{ height:clamp(150px, 46vw, 200px); margin-bottom:0; border-radius:var(--r); }
  /* The panel is half again as wide here and no taller, so the faults are
     scaled up with it. Left at their desktop sizes they sit in the middle of
     a wider panel looking incidental, which is the opposite of the job. */
  .cert__bubble{ width:118px; height:66px; margin:-33px 0 0 -59px; }
  .cert__bare, .cert__shade, .cert__flap{ width:102px; height:102px; }
  .cert__stage{ left:.85rem; bottom:.75rem; }
}
@media (min-width:940px){ .warranty__in{ grid-template-columns:1.06fr .94fr; gap:var(--s8); } }

/* ── 17. PROCESS ───────────────────────────────────────────── */
.process{ padding:var(--s8) 0; background:var(--mist); }
/* The process, as a sticky panel that changes as you read past it.
   Seven cards in a grid is seven things to skim and none to look at. Here the
   copy scrolls and one picture holds the frame beside it, so the section has
   something to watch while it explains itself — and the order is unmistakable
   because the numbers count up as you go.
   Below 960px the picture goes back above its own step: a sticky panel on a
   phone eats half the screen for the whole section. */
.proc{ display:grid; gap:var(--s5); }
/* Desktop only. The seven steps are a long read with a picture per step, and
   the picture column is already hidden on a phone — which leaves a tall
   numbered list doing the job the process page does better. The section header
   goes with it, or the heading introduces nothing. */
@media (max-width:719px){
  #process{ display:none; }
}

.proc__media{ display:none; }
.proc__steps{ display:grid; gap:0; }
.proc__step{
  display:grid; grid-template-columns:auto minmax(0,1fr);
  gap:var(--s4); align-items:start;
  padding:0 0 var(--s6); position:relative;
}
.proc__step:not(:last-child)::before{
  content:""; position:absolute; left:23px; top:46px; bottom:0; width:2px;
  background:linear-gradient(180deg, var(--blue-2), var(--edge));
}
.proc__n{
  position:relative; z-index:1;
  width:48px; height:48px; border-radius:50%; display:grid; place-items:center;
  background:var(--blue); color:var(--paper);
  font-family:var(--disp); font-size:1.02rem; font-weight:800; letter-spacing:-.02em;
  box-shadow:0 4px 14px var(--blue-r);
  transition:transform var(--t-2) var(--e), box-shadow var(--t-2) var(--e);
}
.proc__step h3{ font-size:clamp(1.1rem,1.02rem + .35vw,1.32rem); margin-top:.55rem; }
.proc__step p:not(.proc__n){ color:var(--slate); font-size:.95rem; margin-top:.3rem; max-width:56ch; }

/* Phone. Seven steps, each a 48px disc above a heading above a paragraph, came
   to 1,200px of column — you scrolled past the process rather than read it.
   Number and title share a line, the disc comes down to 30px, and the body
   drops to the short line written for this width (see .only-sm in the markup).
   The rail down the left survives, because it is the thing that says these are
   in order. Each step lifts in as it arrives; app.js stages them in fours. */
@media (max-width:719px){
  .proc__steps{ counter-reset:none; }
  .proc__step{
    grid-template-columns:30px minmax(0,1fr);
    gap:.7rem; padding-bottom:var(--s4);
  }
  .proc__step:not(:last-child)::before{ left:14px; top:34px; }
  .proc__n{ width:30px; height:30px; font-size:.8rem; box-shadow:0 2px 8px var(--blue-r); }
  .proc__step h3{ font-size:1.02rem; margin-top:.28rem; }
  .proc__step p{ font-size:.88rem; margin-top:.15rem; line-height:1.45; }
}

.proc__fig{
  border-radius:var(--r-lg); overflow:hidden; background:var(--cloud);
  aspect-ratio:3/2; box-shadow:var(--lift-2);
}
.proc__fig img{ width:100%; height:100%; object-fit:cover; display:block; }
/* Steps we have no honest photograph of get a plate rather than a borrowed
   one. A squeegee picture captioned "wash" is worse than no picture. */
.proc__plate{
  width:100%; height:100%; display:grid; place-content:center; gap:.4rem; text-align:center;
  background:linear-gradient(160deg, var(--blue), var(--blue-d));
  color:#fff; padding:var(--s5);
}
.proc__plate b{ font-family:var(--disp); font-size:clamp(3rem,2rem + 6vw,5.5rem); line-height:.9; letter-spacing:-.05em; }
.proc__plate em{ font-style:normal; font-size:.95rem; color:oklch(1 0 0 / .82); }

@media (min-width:960px){
  .proc{ grid-template-columns:minmax(0,1fr) minmax(0,1.05fr); gap:var(--s7); align-items:start; }
  .proc__media{
    display:block; position:sticky; top:calc(var(--nav-h) + var(--s5));
    order:2;
  }
  .proc__steps{ order:1; }
  .proc__fig{ display:none; }
  .proc__fig.is-on{ display:block; animation:fadein var(--t-2) var(--e); }
  .proc__step{ padding-bottom:var(--s8); }
  .proc__step:not(:last-child)::before{ left:27px; top:56px; }
  .proc__n{ width:56px; height:56px; font-size:1.16rem; }
  /* The step you are reading is the one lit. The dimming is on the copy
     only: fading the whole step took the numeral down with it, and a dark
     figure on its own blue disc fell to 2.4:1 at .42. The numbers are the
     rail you navigate this section by, so they stay readable throughout. */
  .js .proc__step > *:not(.proc__n){ opacity:.42; transition:opacity var(--t-2) var(--e); }
  .js .proc__step.is-on > *:not(.proc__n){ opacity:1; }
  .js .proc__n{ opacity:.78; transition:opacity var(--t-2) var(--e); }
  .js .proc__step.is-on .proc__n{ opacity:1; }
  .js .proc__step.is-on .proc__n{ transform:scale(1.08); box-shadow:0 8px 24px var(--blue-r); }
}
@media (max-width:959px){
  /* No sticky panel on a phone — it would hold half the screen for the whole
     section, and `display:contents` on both columns would deal out seven
     pictures and then seven steps rather than interleaving them. The numbered
     rail already carries the order on its own. */
  .proc__media{ display:none; }
}
@media (prefers-reduced-motion: reduce){
  .js .proc__step > *:not(.proc__n),
  .js .proc__n{ opacity:1; }
  .proc__fig.is-on{ animation:none; }
}

/* ── LIVERY SHOWCASE (business page) ───────────────────────────
   The page asked people to imagine what their van would look like. Six worked
   examples do that better than any amount of copy, and drawing them rather
   than photographing other firms' vehicles means we can show the *treatments*
   — cut vinyl, full colour change, partial, printed panel — instead of six
   pictures that all read as "a van with writing on it". */
.livery{ padding-block:var(--s9); background:var(--mist); }
.livery__grid{ display:grid; gap:var(--s4); margin-top:var(--s6); }
@media (min-width:680px){ .livery__grid{ grid-template-columns:repeat(2,1fr); } }
@media (min-width:1120px){ .livery__grid{ grid-template-columns:repeat(3,1fr); } }
.livery__card{
  background:var(--paper); border:1px solid var(--edge); border-radius:var(--r-lg);
  overflow:hidden; box-shadow:var(--lift-1); display:flex; flex-direction:column;
  transition:transform var(--t-2) var(--e), box-shadow var(--t-2) var(--e);
}
.livery__card:hover{ transform:translateY(-5px); box-shadow:var(--lift-3); }
.livery__stage{
  padding:var(--s5) var(--s4) var(--s3);
  background:
    radial-gradient(120% 90% at 50% 8%, oklch(1 0 0 / .9), oklch(1 0 0 / 0) 70%),
    linear-gradient(180deg, var(--cloud), var(--mist));
  border-bottom:1px solid var(--edge);
}
.van{ width:100%; height:auto; display:block; filter:drop-shadow(0 10px 16px oklch(0 0 0 / .16)); }
.van__name{
  font-family:var(--disp); font-weight:800; font-size:23px; letter-spacing:-.01em;
  fill:var(--ink-on, #fff);
}
.van__sub{ font-family:var(--text); font-weight:700; font-size:10.5px; letter-spacing:.11em; fill:oklch(1 0 0 / .82); }
.van__tel{ font-family:var(--text); font-weight:800; font-size:13px; letter-spacing:.02em; fill:oklch(1 0 0 / .92); }
.livery__card .van__name{ fill:var(--ink); }
.livery__body{ padding:var(--s4) var(--s5) var(--s5); display:grid; gap:.2rem; }
.livery__body b{ font-size:1.06rem; }
.livery__body span{ font-size:.78rem; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:var(--blue); }
.livery__body p{ margin-top:.4rem; color:var(--slate); font-size:.9rem; }
.livery__note{
  margin-top:var(--s6); max-width:70ch; color:var(--slate);
  padding:var(--s4) var(--s5); background:var(--sun-t);
  border:1.5px solid var(--sun); border-radius:var(--r);
}

/* ── 18. PRICING ───────────────────────────────────────────── */
.pricing{ padding:var(--s8) 0; }
.plans{ display:grid; gap:var(--s4); }
/* The swipe hint belongs to the phone carousel below and is off everywhere
   else; declared here so the phone rule that switches it on comes later. */
.plans__swipe{ display:none; }
.plan{
  background:var(--paper); border:1px solid var(--edge); border-radius:var(--r);
  padding:var(--s5); display:flex; flex-direction:column; gap:var(--s3);
  box-shadow:var(--lift-1);
  transition:transform var(--t-2) var(--e), box-shadow var(--t-2) var(--e);
}
.plan:hover{ transform:translateY(-4px); box-shadow:var(--lift-2); }

/* ── the coverage picture ───────────────────────────────────────
   The real studio car, twice: once recoloured in the studio off-white for
   bare paint and once in the site blue for what the package covers, cut
   along the panel edge by tools/coverage.py. A drawing of a car was the
   first attempt and it looked like a drawing of a car — the boundary only
   means anything when it falls on a real shut line. */
.cov{ margin:0 0 var(--s3); }
.cov img{
  display:block; width:100%; height:auto; border-radius:var(--r-sm);
  background:var(--mist);
}
.plan--best{
  border-color:var(--go-r); box-shadow:var(--lift-2), inset 0 0 0 1px var(--go-t);
  background:linear-gradient(180deg, var(--go-t), transparent 34%), var(--slab);
  position:relative; overflow:hidden;
}
/* One pass, when the card first comes into view, and then it is done. A shine
   that loops is a shop sign; a shine that happens once is the light moving
   over a panel as you walk past it. */
.plan__shine{
  position:absolute; inset:0; pointer-events:none; opacity:0;
  background:linear-gradient(105deg,
    transparent 38%, oklch(1 0 0 / .13) 47%, oklch(1 0 0 / .26) 50%,
    oklch(1 0 0 / .13) 53%, transparent 62%);
  transform:translateX(-115%);
}
.plan--best.is-in .plan__shine{
  animation:planShine 1100ms linear 260ms 1 forwards;
}
/* Linear, and lit for the whole travel: the light crosses the panel at one
   speed and leaves. Easing made it drift in, hang in the middle and drift
   out, which reads as an effect rather than as light. */
@keyframes planShine{
  from { opacity:1; transform:translateX(-115%); }
  to   { opacity:1; transform:translateX(115%); }
}
@media (prefers-reduced-motion:reduce){ .plan__shine{ display:none; } }
.plan__flag{
  align-self:flex-start; background:var(--go); color:oklch(0.16 0.02 152);
  font-size:.76rem; font-weight:800; padding:.3rem .8rem; border-radius:999px;
  margin-bottom:.2rem;
}
.plan__for{ color:var(--mute); font-size:.92rem; margin-top:-.35rem; }
.plan__price{ font-family:var(--disp); font-weight:700; letter-spacing:-.035em; font-size:clamp(2.4rem,1.9rem + 1.8vw,3rem); line-height:1; display:flex; align-items:baseline; gap:.45rem; }
.plan__price small{ font-family:var(--text); font-size:max(12px,.3em); font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--mute); }
.plan__list{ display:grid; gap:.6rem; padding-top:var(--s4); border-top:1px solid var(--edge); flex:1; }
.plan__list li{ font-size:.93rem; color:var(--slate); padding-left:1.6rem; position:relative; }
.plan__list li::before{ content:""; position:absolute; left:0; top:.42em; width:16px; height:16px; border-radius:50%; background:var(--go-t); }
.plan__list li::after{ content:""; position:absolute; left:5.5px; top:.6em; width:5px; height:9px; border:2px solid var(--go); border-top:0; border-left:0; transform:rotate(42deg); }
.plan--best .plan__list li{ color:var(--paper-2, oklch(0.86 0.004 258)); }
.plan .btn{ margin-top:var(--s2); }
@media (min-width:880px){
  .plans{ grid-template-columns:repeat(3,1fr); align-items:stretch; }
  .plan--best{ transform:scale(1.03); }
  .plan--best:hover{ transform:scale(1.03) translateY(-4px); }
}
.pricing__more{ text-align:center; margin-top:var(--s5); }
.beat{
  margin-top:var(--s6); background:var(--blue); color:var(--dark); border-radius:var(--r-lg);
  padding:var(--s6) var(--s5); display:grid; gap:var(--s4); align-items:center;
  box-shadow:var(--lift-2); position:relative; overflow:hidden;
}
.beat::before{
  content:""; position:absolute; inset:auto -10% -60% auto; width:340px; height:340px;
  border-radius:50%; background:oklch(0 0 0 / .07); pointer-events:none;
}
.beat h3{ color:var(--dark); }
.beat p{ color:oklch(0.30 0.045 245); font-size:.98rem; max-width:60ch; margin-top:.5rem; }
.beat .btn{ --bg:var(--dark); --fg:var(--bright); --bd:var(--dark); justify-self:start; position:relative; }
.beat .btn:hover{ --bg:var(--bright); --fg:var(--dark); --bd:var(--bright); }
@media (min-width:880px){ .beat{ grid-template-columns:1fr auto; gap:var(--s6); padding:var(--s7) var(--s6); } }

/* Phone. Three plans stacked ran to 1,300px, so the card most people pick was
   two full screens below the heading and nobody compared anything — they read
   one card, then another card, from memory. Swipe instead: one plan per view,
   snapped, with the next one's edge showing so it is obvious there is more.
   Comparing is now a thumb movement rather than a scroll and a memory test. */
@media (max-width:719px){
  .plans{
    display:flex; gap:.7rem; align-items:stretch;
    overflow-x:auto; overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory; scrollbar-width:none;
    margin-inline:calc(var(--pad) * -1); padding:.25rem var(--pad) .5rem;
  }
  .plans::-webkit-scrollbar{ display:none; }
  .plan{
    flex:0 0 86%; scroll-snap-align:center; padding:var(--s4);
    transform:none !important;   /* no hover lift on a surface you swipe */
  }
  .plan h3{ font-size:1.15rem; }
  .plan__for{ font-size:.84rem; }
  .plan__list{ font-size:.88rem; }
  .plan .btn{ margin-top:auto; }
  .plans__swipe{
    display:flex; align-items:center; gap:.4rem; justify-content:center;
    margin-top:.5rem; font-size:.78rem; color:var(--mute);
  }
  .plans__swipe::after{
    content:""; width:22px; height:1px; background:currentColor;
    animation:vsSwipe 1.8s var(--e) infinite;
  }
  .beat{ padding:var(--s5) var(--s4); margin-top:var(--s5); }
  .beat h3{ font-size:1.15rem; }
  .beat p{ font-size:.9rem; }
  .beat .btn{ width:100%; justify-content:center; }
  .pricing__more .btn{ width:100%; justify-content:center; }
}
@media (prefers-reduced-motion:reduce){ .plans__swipe::after{ animation:none; } }

/* ── 18b. FIVE QUESTIONS ───────────────────────────────────────
   Numbered rows rather than two columns: this only makes claims about us,
   so there is no second column to fill. */
/* ── the closing third ────────────────────────────────────────
   Everything from the intake form down had settled into one shape: a centred
   heading, a centred deck, then a stack of rounded boxes with a blue disc in
   the corner of each. Five sections running the same three moves reads as
   generated, however good the words are.

   So this run gets its own manner, and it is a printed one. Headings sit
   hard left under a heavy rule with the section's own number set beside them.
   Counters come out of their discs and become large ghosted numerals in the
   margin, the way a contents page sets them. Answers are underscored small
   caps rather than coloured pills. Nothing here is a card. */
.versus .sec-head--center,
.faq .sec-head,
.process .sec-head,
.pricing .sec-head,
#intake .sec-head{
  text-align:left; margin-inline:0; max-width:none;
  display:grid; grid-template-columns:minmax(0,1fr) auto;
  gap:.45rem var(--s5);
  border-top:2px solid currentColor; padding-top:var(--s4);
}
/* Everything the author wrote stays in the first column; the index label
   gets the second. Absolute positioning let a long heading slide under it. */
.versus .sec-head--center > *,
.faq .sec-head > *,
.process .sec-head > *,
.pricing .sec-head > *,
#intake .sec-head > *{ grid-column:1; }
.versus .sec-head--center::before,
.faq .sec-head::before,
.process .sec-head::before,
.pricing .sec-head::before,
#intake .sec-head::before{
  content:attr(data-ix); grid-column:2; grid-row:1; align-self:start;
  font-family:var(--disp); font-weight:800; font-size:.78rem;
  letter-spacing:.18em; opacity:.5;
}
/* On a phone the heading wraps to two or three lines and the index label,
   pinned to the top right, ends up sitting in the gap between them. In flow
   above the heading it reads as the kicker it always was. */
@media (max-width:719px){
  .versus .sec-head--center,
  .faq .sec-head,
  .process .sec-head,
  .pricing .sec-head,
  #intake .sec-head{ grid-template-columns:minmax(0,1fr); }
  .versus .sec-head--center::before,
  .faq .sec-head::before,
  .process .sec-head::before,
  .pricing .sec-head::before,
  #intake .sec-head::before{
    grid-column:1; grid-row:auto; margin-bottom:.15rem; opacity:.6;
  }
}
.versus .sec-head--center .deck,
.faq .sec-head .deck,
.process .sec-head .deck,
.pricing .sec-head .deck,
#intake .sec-head .deck{ margin-inline:0; max-width:56ch; }
/* the break stays: killing it ran the two sentences together with no space */

/* The flat mid-grey band these two sections sat on read as a placeholder
   slab — a lighter rectangle dropped behind the words with nothing else
   happening in it. They keep the page's own black now and are separated by a
   hairline and a breath of the house blue at the top edge instead. */
.versus, .faq{
  background:var(--void);
  border-top:1px solid var(--edge);
  background-image:linear-gradient(180deg, var(--blue-t), transparent 220px);
}
.versus{ padding:var(--s8) 0; }
.ask{ counter-reset:q; display:grid; gap:0; max-width:78ch; margin:var(--s5) 0 0; }
.ask__row{
  counter-increment:q; position:relative;
  padding:var(--s5) 0 var(--s5) 3.4rem;
  border-bottom:1px solid var(--edge);
}
.ask__row:last-child{ border-bottom:0; }
.ask__row::before{
  content:counter(q,decimal-leading-zero);
  position:absolute; left:0; top:calc(var(--s5) - .35rem);
  font-family:var(--disp); font-weight:800; font-size:1.9rem;
  letter-spacing:-.05em; color:var(--edge-2); line-height:1;
  transition:color var(--t-2) var(--e);
}
.ask__row.is-in::before{ color:var(--ink); }
.ask__q{
  font-family:var(--disp); font-weight:700; letter-spacing:-.02em;
  font-size:clamp(1.12rem,1rem + .7vw,1.5rem); text-wrap:balance;
}
.ask__a{
  margin-top:.6rem; color:var(--slate); font-size:.97rem;
  display:flex; align-items:baseline; gap:.65rem; flex-wrap:wrap;
}
.ask__a span{
  flex:none; font-weight:800; font-size:.72rem; letter-spacing:.13em;
  text-transform:uppercase; color:var(--ink);
  padding:0 0 .1rem; border-bottom:2px solid var(--sun);
}
@media (max-width:520px){
  .ask__row{ padding-left:2.6rem; }
  .ask__row::before{ font-size:1.45rem; }
}

/* ── 19. PROOF ─────────────────────────────────────────────── */
.proof{ padding:var(--s8) 0; }
/* The same four-things treatment as the promises in the hero: a ticked pill
   each, rather than four headings with captions under them. They are the same
   kind of statement, said in the same voice, so they get the same shape — and
   set as headings they read as four separate claims instead of one list. */
.proof__nums{ display:grid; gap:.6rem; }
/* No pill. Four outlined boxes on a dark section read as four cards holding
   four unrelated things; the tick is already the shape doing that work, so the
   line takes the ground the section stands on. */
.proof__nums div{
  position:relative; min-height:44px;
  display:flex; align-items:center; flex-wrap:wrap; gap:0 .45rem;
  padding:.55rem 0 .55rem 3rem;
}
.proof__nums div::before{
  content:""; position:absolute; left:.5rem; top:calc(50% - 12px);
  width:24px; height:24px; border-radius:50%;
  background:var(--tick, #1B5BFF);
  box-shadow:0 2px 10px oklch(0.14 0.009 258 / .55);
}
.proof__nums div::after{
  content:""; position:absolute; left:1.2rem; top:calc(50% - 6px);
  width:6px; height:11px;
  border:2.2px solid oklch(1 0 0 / .96); border-top:0; border-left:0;
  transform:rotate(42deg);
}
.proof__nums b{
  font-family:var(--disp); font-weight:700;
  font-size:clamp(.98rem,.9rem + .32vw,1.12rem);
  letter-spacing:-.015em; line-height:1.3; color:oklch(0.97 0.002 258);
}
.proof__nums span{ font-size:.9rem; color:var(--mute); line-height:1.3; }
@media (min-width:820px){
  .proof__nums{ grid-template-columns:repeat(2,1fr); gap:.7rem var(--s4); }
}
.quotes{ display:grid; gap:var(--s4); }
.quotes blockquote{ background:var(--mist); border-radius:var(--r); padding:var(--s5); border:1px solid var(--edge); }
.quotes p{ font-size:1.01rem; line-height:1.55; }
.quotes cite{ display:block; margin-top:var(--s4); font-style:normal; font-weight:700; font-size:.9rem; }
.quotes cite span{ display:block; font-weight:500; color:var(--mute); font-size:.83rem; margin-top:.1rem; }
/* Four across only once a quarter of the row can hold the longest claim on
   one line; two across below that. */
@media (min-width:760px){ .quotes{ grid-template-columns:repeat(3,1fr); } }
@media (min-width:900px){ .proof__nums{ grid-template-columns:repeat(4,1fr); } }

/* ── 20. FAQ ───────────────────────────────────────────────── */
.faq{ padding:var(--s8) 0; }
.faq__in{ display:grid; gap:var(--s5); }
.faq__list{ display:grid; gap:.7rem; }
/* Eight rounded boxes with a blue plus in each corner is the shape every
   FAQ on the internet has. These are ruled lines instead: the question sits
   on the rule, the mark is a hairline cross in the page's own ink, and the
   row it belongs to lights up rather than lifting off the page. */
.faq__list{ border-top:1px solid var(--edge-2); }
.faq details{
  background:none; border:0; border-bottom:1px solid var(--edge);
  border-radius:0; overflow:hidden;
  transition:background var(--t-2) var(--e);
}
.faq details[open]{ background:oklch(1 0 0 / .035); box-shadow:none; }
.faq summary{
  list-style:none; cursor:pointer; padding:1.15rem 3rem 1.15rem 0; position:relative;
  font-family:var(--disp); font-weight:700; font-size:1.06rem; letter-spacing:-.015em;
  transition:padding-left var(--t-2) var(--e);
}
.faq details[open] summary{ padding-left:.9rem; }
.faq summary::-webkit-details-marker{ display:none; }
.faq summary:hover{ color:var(--blue); }
.faq__ic{ position:absolute; right:.35rem; top:1.55rem; width:15px; height:15px; }
.faq__ic::before,.faq__ic::after{
  content:""; position:absolute; inset:0; margin:auto;
  background:var(--ink); border-radius:0;
  transition:transform var(--t-2) var(--e), background var(--t-2) var(--e);
}
.faq__ic::before{ width:15px; height:1.5px; }
.faq__ic::after{ width:1.5px; height:15px; }
.faq details[open] .faq__ic::after{ transform:scaleY(0); }
.faq details[open] .faq__ic::before{ background:var(--blue); }
.faq details > div{ padding:0 3rem 1.25rem .9rem; }
.faq details p{ color:var(--slate); font-size:.95rem; max-width:70ch; }
.faq details p + p{ margin-top:.7rem; }
.faq details a{ color:var(--blue); font-weight:700; }
.faq__more{ margin-top:var(--s4); }
@media (min-width:940px){
  .faq__in{ grid-template-columns:.66fr 1.34fr; gap:var(--s8); align-items:start; }
  .faq .sec-head{ margin-bottom:0; position:sticky; top:calc(var(--nav-h) + 24px); }
}

/* the searchable FAQ page */
.faqsearch{ position:relative; display:flex; align-items:center; margin-top:var(--s3); max-width:520px; }
.faqsearch svg{ position:absolute; left:1rem; width:19px; height:19px; fill:none; stroke:var(--mute); stroke-width:2.2; stroke-linecap:round; pointer-events:none; }
.faqsearch input{
  width:100%; min-height:54px; padding:.8rem 5.2rem .8rem 2.9rem;
  background:var(--paper); border:1.5px solid var(--edge-2); border-radius:999px;
  font-size:1rem; box-shadow:var(--lift-1);
  transition:border-color var(--t-1) var(--e), box-shadow var(--t-1) var(--e);
}
.faqsearch input::-webkit-search-cancel-button{ display:none; }
.faqsearch input:focus{ outline:none; border-color:var(--blue); box-shadow:0 0 0 4px var(--blue-t); }
.faqsearch__x{ position:absolute; right:.5rem; min-height:40px; padding:0 .95rem; border-radius:999px; font-size:.85rem; font-weight:700; color:var(--slate); }
.faqsearch__x:hover{ background:var(--cloud); color:var(--ink); }
.faq--page{ background:var(--paper); }
.faq--page .faq details{ background:var(--mist); }
.faq--page .faq__list{ margin-bottom:var(--s7); }
.faqgroup{
  font-size:clamp(1.25rem,1.1rem + .6vw,1.6rem); margin-bottom:var(--s4);
  padding-bottom:.6rem; border-bottom:2px solid var(--sun);
  display:inline-block;
}
.faqgroup__note{ max-width:60ch; margin:calc(var(--s4) * -0.5) 0 var(--s4); color:var(--slate); }
.faqempty{ padding:var(--s5); background:var(--mist); border-radius:var(--r); color:var(--slate); }
.faqempty a{ color:var(--blue); font-weight:700; }
[data-faq-group][hidden]{ display:none; }

/* ── 21. END CTA ───────────────────────────────────────────── */
.endcta{ padding:var(--s8) 0; background:var(--blue-t); border-top:1px solid var(--edge); }
.endcta__in{ text-align:center; display:grid; justify-items:center; gap:var(--s4); }
.endcta__in p{ color:var(--slate); max-width:52ch; }
.endcta__row{ display:flex; flex-wrap:wrap; gap:var(--s3); justify-content:center; }

/* ── 22. FOOTER + DOCK ─────────────────────────────────────── */
.foot{ background:var(--night); color:#fff; }
.foot__top{
  display:grid; gap:var(--s6); padding-block:var(--s8);
  border-bottom:1px solid oklch(1 0 0 /.12);
}
.foot__pitch h2{ color:#fff; font-size:clamp(1.8rem,1.3rem + 2vw,2.7rem); }
.foot__pitch p{ color:var(--night-t); margin-top:var(--s4); max-width:52ch; }
.foot__cta{ display:flex; flex-wrap:wrap; gap:var(--s3); margin-top:var(--s5); }
/* The name, explained once. Set on a hairline under the brand so it reads as
   a maker's mark rather than a second paragraph of blurb. */
.foot__why{
  margin-top:var(--s4); padding-top:var(--s4);
  border-top:1px solid var(--edge);
  font-size:.86rem; line-height:1.55; color:var(--mute); max-width:46ch;
}
.foot__why b{
  display:block; font-family:var(--disp); font-weight:700;
  font-size:.72rem; letter-spacing:.16em; text-transform:uppercase;
  color:var(--blue); margin-bottom:.3rem;
}
.foot__promises{ display:grid; gap:var(--s4); align-content:center; }
.foot__promises li{ display:grid; gap:.1rem; padding-left:1.6rem; position:relative; }
.foot__promises li::before{ content:""; position:absolute; left:0; top:.42em; width:16px; height:16px; border-radius:50%; background:var(--sun); }
.foot__promises li::after{ content:""; position:absolute; left:4.5px; top:.66em; width:6px; height:3px; border:2px solid var(--night); border-top:0; border-right:0; transform:rotate(-45deg); }
.foot__promises b{ font-size:.98rem; }
.foot__promises span{ font-size:.86rem; color:var(--night-t); }
@media (min-width:900px){ .foot__top{ grid-template-columns:1.1fr .9fr; gap:var(--s8); } }

.foot__in{ display:grid; gap:var(--s6); padding-block:var(--s7); }
.foot__brand p{ margin-top:var(--s4); color:var(--night-t); font-size:.92rem; line-height:1.6; }
.foot .brand__mark{ background:#fff; color:var(--blue); }
.foot__rate{ display:flex; align-items:center; gap:.5rem; margin-top:var(--s4) !important; }
.foot__rate b{ font-family:var(--disp); font-size:1.3rem; color:#fff; }
.foot__rate span:first-of-type{ color:var(--sun); letter-spacing:.08em; }
.foot__col{ display:grid; gap:.2rem; align-content:start; }
/* Above the phone breakpoint the groups are plain columns: the summary is
   just a heading and the disclosure never closes. */
@media (min-width:720px){
  .foot__col summary{ list-style:none; }
  .foot__col summary::-webkit-details-marker{ display:none; }
  .foot__links{ display:grid; gap:.2rem; }
}
.foot__col :is(h3,h4){ color:var(--night-t); text-transform:uppercase; letter-spacing:.1em; font-size:.74rem; margin-bottom:.5rem; }
.foot__col a,.foot__col p{ font-size:.91rem; color:oklch(0.90 0.01 250); }
.foot__col a{ width:fit-content; min-width:44px; min-height:40px; display:inline-flex; align-items:center; transition:color var(--t-1); }
.foot__col a:hover{ color:var(--sun); }
.foot__addr{ margin:.6rem 0 .2rem; color:var(--night-t) !important; line-height:1.55; }
.foot__bar{ border-top:1px solid oklch(1 0 0 /.12); padding-block:var(--s4) calc(var(--s4) + 76px); display:flex; flex-wrap:wrap; gap:.5rem var(--s5); justify-content:space-between; font-size:.79rem; color:var(--night-t); }
@media (min-width:760px){ .foot__in{ grid-template-columns:1.5fr 1fr 1fr 1.2fr; gap:var(--s6); } }
@media (min-width:1000px){ .foot__bar{ padding-bottom:var(--s4); } }

/* ── the footer on a phone ──────────────────────────────────────
   Stacked, this ran to 2,400px: three sitemap columns became three long
   single-file lists, each link a 40px row, with a paragraph of brand story
   and four promises with subtitles above them. Nobody reads a footer that
   way — they scan it for one link.

   So on a phone the link groups go two-up, each row is a tap target and
   nothing more, the story paragraph steps out (it is on the about page),
   and the promises keep their claim and drop their sub-clause. Same links,
   about a third of the height, and the group headings survive as the thing
   that makes it scannable rather than a wall. */
@media (max-width:719px){
  .foot__top{ padding-block:var(--s6); gap:var(--s5); }
  .foot__pitch p{ font-size:.94rem; }
  .foot__cta{ margin-top:var(--s4); gap:.5rem; }
  .foot__cta .btn{ flex:1 1 auto; justify-content:center; }

  /* The claim is the point; the sub-clause is detail for a wider screen. */
  .foot__promises{ gap:.55rem; }
  .foot__promises span{ display:none; }
  .foot__promises li{ padding-left:1.5rem; }
  .foot__promises b{ font-size:.92rem; font-weight:600; }

  .foot__in{ padding-block:var(--s5); gap:var(--s5); }
  .foot__brand p{ font-size:.88rem; }
  .foot__why{ display:none; }

  /* Each group is a row you can open. Closed, the footer is four lines and a
     brand mark instead of forty links; open, it is the same two-up list it
     was. The chevron is drawn here rather than left to the browser's marker,
     which is a different shape in every one of them. */
  .foot__col{ display:block; padding-top:0; border-top:1px solid oklch(1 0 0 / .12); }
  .foot__col summary{
    display:flex; align-items:center; justify-content:space-between;
    min-height:52px; cursor:pointer; list-style:none;
  }
  .foot__col summary::-webkit-details-marker{ display:none; }
  .foot__col summary::after{
    content:""; width:9px; height:9px; margin-right:.2rem;
    border-right:2px solid var(--night-t); border-bottom:2px solid var(--night-t);
    rotate:45deg; translate:0 -2px;
    transition:rotate var(--t-2) var(--e);
  }
  .foot__col[open] summary::after{ rotate:-135deg; translate:0 2px; }
  .foot__col summary h3{ margin:0; }
  .foot__links{
    display:grid; grid-template-columns:1fr 1fr; gap:0 var(--s4);
    padding-bottom:var(--s3);
  }
  .foot__col a{ min-height:38px; font-size:.88rem; }
  .foot__links .foot__addr{ grid-column:1 / -1; margin:.5rem 0 0; font-size:.85rem; }
  /* An address does not belong in a column half a phone wide: the email broke
     mid-word to fit. Contact details take the full row. */
  .foot__links a[href^="tel:"],
  .foot__links a[href^="mailto:"]{ grid-column:1 / -1; }

  /* The brand block is a mark, a line and nothing else. `justify-items:center`
     shrank every child to its own content width, which set the sentence in a
     20-character column; the block centres its text and centres the mark on
     its own instead. */
  .foot__brand{ display:block; text-align:center; }
  .foot__brand .brand{ justify-content:center; }
  .foot__brand p{ margin-top:var(--s3); font-size:.86rem; }
  .foot__brand p br{ display:none; }

  .foot__bar{ flex-direction:column; gap:.35rem; font-size:.74rem; padding-top:var(--s3); text-align:center; }
}

.dock{
  position:fixed; left:0; right:0; bottom:0; z-index:var(--z-dock);
  display:grid; grid-template-columns:auto 1fr; gap:.6rem;
  padding:.6rem var(--pad) calc(.6rem + env(safe-area-inset-bottom));
  background:oklch(0.175 0.010 258 / .93); backdrop-filter:blur(14px) saturate(130%); -webkit-backdrop-filter:blur(14px) saturate(130%);
  border-top:1px solid var(--edge);
  transform:translateY(115%); transition:transform var(--t-2) var(--e);
}
.dock.is-up{ transform:translateY(0); }
.dock__call{ display:inline-flex; align-items:center; gap:.45rem; min-height:48px; padding:0 1.1rem; border:1.5px solid var(--edge-2); border-radius:999px; font-weight:700; font-size:.94rem; color:var(--ink); }
.dock__call svg{ width:17px; height:17px; fill:none; stroke:var(--blue); stroke-width:1.9; stroke-linejoin:round; }
.dock__cta{ min-height:48px; }
@media (min-width:1000px){ .dock{ display:none; } }

/* ── 23. INNER PAGES ───────────────────────────────────────── */
.pagehead{
  padding:calc(var(--nav-h) + var(--s7)) 0 var(--s7);
  background:
    radial-gradient(50% 60% at 88% 0%, oklch(0.855 0.155 88 / .16), transparent 70%),
    linear-gradient(180deg, var(--blue-t), var(--paper) 92%);
  border-bottom:1px solid var(--edge);
}
.pagehead__in{ display:grid; gap:var(--s4); max-width:72ch; }
.crumb{ display:flex; align-items:center; gap:.5rem; font-size:.85rem; color:var(--mute); font-weight:600; }
.crumb a{ color:var(--blue); display:inline-flex; align-items:center; min-height:40px; }
.crumb a:hover{ text-decoration:underline; }
.pagehead h1{ font-family:var(--disp); font-weight:800; letter-spacing:-.035em; line-height:1.03; font-size:clamp(2.15rem,1.4rem + 3.4vw,3.7rem); }
.pagehead .deck{ font-size:clamp(1.04rem,1rem + .4vw,1.22rem); }
.pagehead__row{ display:flex; flex-wrap:wrap; gap:var(--s3); margin-top:var(--s2); }
.pagehead__facts{ display:flex; flex-wrap:wrap; gap:var(--s3) var(--s5); margin-top:var(--s3); font-size:.9rem; color:var(--slate); }
.pagehead__facts b{ color:var(--ink); }

.slabs{ padding:var(--s8) 0; display:grid; gap:var(--s8); }
.slab{ display:grid; gap:var(--s5); align-items:center; }
.slab figure{ border-radius:var(--r-lg); overflow:hidden; box-shadow:var(--lift-2); background:var(--cloud); }
.slab figure img{ width:100%; height:100%; object-fit:cover; aspect-ratio:3/2; }
.slab__t h2{ font-size:clamp(1.7rem,1.2rem + 2vw,2.6rem); }
.slab__t p{ color:var(--slate); margin-top:var(--s4); }
.slab__t ul{ display:grid; gap:.6rem; margin-top:var(--s4); }
.slab__t li{ padding-left:1.7rem; position:relative; color:var(--slate); font-size:.95rem; }
.slab__t li::before{ content:""; position:absolute; left:0; top:.42em; width:16px; height:16px; border-radius:50%; background:var(--blue-t); }
.slab__t li::after{ content:""; position:absolute; left:4.5px; top:.66em; width:6px; height:3px; border:2px solid var(--blue); border-top:0; border-right:0; transform:rotate(-45deg); }
@media (min-width:900px){ .slab{ grid-template-columns:1fr 1fr; gap:var(--s7); } .slab:nth-child(even) figure{ order:2; } }

.fins{ display:grid; gap:var(--s4); grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); }
.fin{ border:1px solid var(--edge); border-radius:var(--r); padding:var(--s5); background:var(--paper); box-shadow:var(--lift-1); }
.fin__sw{ width:100%; height:74px; border-radius:var(--r); margin-bottom:var(--s4); box-shadow:inset 0 0 0 1px oklch(0 0 0 /.08); }
.fin h3{ font-size:1.1rem; }
.fin p{ color:var(--slate); font-size:.92rem; margin-top:.4rem; }
.fin__k{ display:inline-block; margin-top:var(--s3); font-size:.82rem; font-weight:700; color:var(--blue); }

/* gallery */
.gal{ display:grid; gap:var(--s4); grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); }
.gal__item{ border-radius:var(--r); overflow:hidden; background:var(--cloud); box-shadow:var(--lift-1); position:relative; }
.gal__item img{ width:100%; height:100%; object-fit:cover; aspect-ratio:4/3; transition:transform var(--t-3) var(--e); }
.gal__item:hover img{ transform:scale(1.05); }
.gal__item figcaption{
  position:absolute; left:0; right:0; bottom:0; padding:1.8rem .9rem .8rem;
  background:linear-gradient(180deg, transparent, oklch(0.16 0.02 258 / .85));
  color:#fff; font-size:.88rem; font-weight:600;
}
.gal__item figcaption span{ display:block; font-weight:400; font-size:.8rem; color:oklch(0.88 0.01 250); }
.gal__item--wide{ grid-column:span 2; }
@media (max-width:640px){ .gal__item--wide{ grid-column:span 1; } }

/* filter pills */
.pills{ display:flex; flex-wrap:wrap; gap:.5rem; margin-bottom:var(--s5); }
.pill{
  min-height:42px; padding:.45rem 1rem; border-radius:999px;
  border:1.5px solid var(--edge-2); background:var(--paper); color:var(--slate);
  font-size:.9rem; font-weight:700; transition:all var(--t-1) var(--e);
}
.pill:hover{ border-color:var(--ink); color:var(--ink); }
.pill[aria-pressed="true"]{ background:var(--ink); border-color:var(--ink); color:#fff; }

/* price tables */
.ptable{ width:100%; border:1px solid var(--edge); border-radius:var(--r); overflow:hidden; background:var(--paper); box-shadow:var(--lift-1); }
.ptable caption{ text-align:left; padding:var(--s5) var(--s5) 0; font-family:var(--disp); font-weight:700; font-size:1.25rem; }
.ptable th,.ptable td{ padding:.9rem var(--s5); text-align:left; border-bottom:1px solid var(--edge); }
.ptable thead th{ font-size:.76rem; letter-spacing:.06em; text-transform:uppercase; color:var(--mute); }
.ptable tbody th{ font-weight:700; }
.ptable td:last-child{ text-align:right; font-family:var(--disp); font-weight:700; white-space:nowrap; }
.ptable tbody tr:last-child th,.ptable tbody tr:last-child td{ border-bottom:0; }
.ptable__note{ padding:var(--s4) var(--s5); background:var(--mist); font-size:.86rem; color:var(--slate); }
.ptables{ display:grid; gap:var(--s5); }
@media (min-width:900px){ .ptables{ grid-template-columns:1fr 1fr; align-items:start; } }
@media (max-width:719px){
  .ptable th:nth-child(2),.ptable td:nth-child(2){ display:none; }
  .ptable th,.ptable td{ padding:.85rem var(--s4); }
  .ptable caption{ padding:var(--s4) var(--s4) 0; font-size:1.15rem; }
  .ptable__note{ padding:var(--s4); }
  .pagehead{ padding-top:calc(var(--nav-h) + var(--s5)); }
  .pagehead__row .btn{ flex:1 1 100%; }
  .slabs{ gap:var(--s7); }
}

/* contact */
.contact__grid{ display:grid; gap:var(--s6); align-items:start; }
.card{ border:1px solid var(--edge); border-radius:var(--r); padding:var(--s5); background:var(--paper); box-shadow:var(--lift-1); }
.card :is(h2,h3){ font-size:1.14rem; margin-bottom:var(--s3); }
.card p{ color:var(--slate); font-size:.94rem; }
.card--sun{ background:var(--sun-t); border-color:var(--sun); }
.hours{ display:grid; gap:.45rem; }
.hours div{ display:flex; justify-content:space-between; gap:var(--s4); font-size:.94rem; }
.hours dt{ color:var(--slate); }
.hours dd{ font-weight:700; }
.contact__big{ font-family:var(--disp); font-weight:700; font-size:clamp(1.5rem,1.2rem + 1.4vw,2rem); letter-spacing:-.025em; display:inline-flex; align-items:center; gap:.55rem; min-height:44px; }
.contact__big svg{ width:22px; height:22px; fill:var(--blue); }
/* A phone number is one object. In the intake sidebar the clamp ceiling
   was wide enough to break it across two lines, which reads as two
   numbers for a moment. */
.contact__big{ white-space:nowrap; }
.sum .contact__big, .intake__side .contact__big{ font-size:clamp(1.25rem,1.05rem + .7vw,1.55rem); }
.card a[href^="mailto:"],.card a[href^="tel:"]{ display:inline-flex; align-items:center; min-height:44px; color:var(--blue); font-weight:700; }
@media (min-width:980px){ .contact__grid{ grid-template-columns:1.05fr .95fr; gap:var(--s7); } }

.form{ background:var(--mist); border:1px solid var(--edge); border-radius:var(--r-lg); padding:var(--s5); display:grid; gap:var(--s4); }
.form__row{ display:grid; gap:var(--s4); }
.field{ display:grid; gap:.35rem; }
.field label{ font-size:.88rem; font-weight:700; }
.field__hint{ font-weight:500; color:var(--mute); }
.field input,.field select,.field textarea{
  width:100%; min-height:50px; padding:.75rem .95rem;
  background:var(--paper); border:1.5px solid var(--edge-2); border-radius:var(--r-sm);
  font-size:1rem; transition:border-color var(--t-1) var(--e), box-shadow var(--t-1) var(--e);
}
.field textarea{ min-height:110px; resize:vertical; line-height:1.5; }
.field input::placeholder,.field textarea::placeholder{ color:var(--mute); }
.field input:hover,.field select:hover,.field textarea:hover{ border-color:var(--slate); }
.field input:focus,.field select:focus,.field textarea:focus{ outline:none; border-color:var(--blue); box-shadow:0 0 0 4px var(--blue-t); }
.field input[readonly]{ background:var(--cloud); color:var(--slate); }
.field select{ appearance:none; background-image:linear-gradient(45deg,transparent 50%,var(--slate) 50%),linear-gradient(135deg,var(--slate) 50%,transparent 50%); background-position:calc(100% - 21px) 22px, calc(100% - 15px) 22px; background-size:6px 6px; background-repeat:no-repeat; }
.field__err{ font-size:.84rem; color:var(--bad); font-weight:600; }
.field.is-bad input{ border-color:var(--bad); }
.form__fine{ font-size:.82rem; color:var(--mute); text-align:center; }
.form__done{ background:oklch(0.60 0.15 155 / .12); border:1.5px solid var(--good); color:oklch(0.42 0.13 155); padding:.9rem 1rem; border-radius:var(--r-sm); font-weight:600; font-size:.94rem; }
@media (min-width:620px){ .form__row{ grid-template-columns:1fr 1fr; } }
@media (min-width:980px){ .form{ padding:var(--s6); } }

/* generic prose */
.prose{ padding:var(--s8) 0; }
.prose__in{ max-width:72ch; display:grid; gap:var(--s4); }
.prose h2{ margin-top:var(--s4); }
.prose h3{ margin-top:var(--s3); }
.prose p{ color:var(--slate); }
.prose ul{ display:grid; gap:.5rem; }
.prose ul li{ padding-left:1.5rem; position:relative; color:var(--slate); }
.prose ul li::before{ content:""; position:absolute; left:0; top:.62em; width:6px; height:6px; border-radius:50%; background:var(--blue); }
.prose a{ color:var(--blue); font-weight:600; text-decoration:underline; text-underline-offset:3px; }

/* timeline (process page) */
.tl{ display:grid; gap:0; counter-reset:t; }
.tl__item{
  counter-increment:t; position:relative; padding:0 0 var(--s6) var(--s7);
  border-left:2px solid var(--edge); margin-left:14px;
}
.tl__item:last-child{ border-left-color:transparent; padding-bottom:0; }
.tl__item::before{
  content:counter(t); position:absolute; left:-16px; top:0;
  width:30px; height:30px; border-radius:50%;
  background:var(--blue); color:var(--paper); font-size:.8rem; font-weight:800;
  display:grid; place-items:center; box-shadow:0 0 0 4px var(--paper);
}
.tl__when{ font-size:.75rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase; color:var(--mute); }
.tl__item h3{ margin-top:.15rem; font-size:1.22rem; }
.tl__item p{ color:var(--slate); margin-top:.5rem; font-size:.96rem; max-width:64ch; }
.tl__item ul{ display:grid; gap:.4rem; margin-top:.7rem; }
.tl__item li{ position:relative; padding-left:1.3rem; font-size:.92rem; color:var(--slate); }
.tl__item li::before{ content:""; position:absolute; left:0; top:.6em; width:6px; height:6px; border-radius:50%; background:var(--sun-d); }

/* .tl carries four other pages, so the picture variant is a modifier.
   The text is wrapped in .tl__t because each child of a grid item becomes a
   cell of its own — without the wrapper the kicker, heading and paragraph
   would each claim a column. */
.tl--img .tl__item{ display:grid; gap:var(--s4); align-items:start; }
@media (min-width:900px){
  .tl--img .tl__item{ grid-template-columns:minmax(0,1fr) minmax(0,.82fr); column-gap:var(--s6); }
  /* Alternate the picture side down the list. The spine and the numbered
     badge stay pinned to the far left — they mark the row, not the text —
     so only the two content columns swap, and the wider column follows the
     text rather than staying put. Explicit grid-column on both children:
     without it the source order would put the text back on the left. */
  .tl--img .tl__item:nth-child(even){ grid-template-columns:minmax(0,.82fr) minmax(0,1fr); }
  .tl--img .tl__item:nth-child(even) .tl__t{ grid-column:2; grid-row:1; }
  .tl--img .tl__item:nth-child(even) .tl__pic{ grid-column:1; grid-row:1; }
}
.tl--img .tl__t{ min-width:0; }
/* The word-numbers this list used to carry said what the numbered badge two
   inches to the left already said. The slot earns its place by holding the
   timing instead, set as a tag rather than a line of grey caps — and the
   headings come up a size, because on this page they are the step. */
.tl--img .tl__when{
  display:inline-block; margin-bottom:.55rem; padding:.3rem .6rem;
  border-radius:999px; background:var(--blue-t); color:var(--blue);
  font-size:.7rem; letter-spacing:.08em;
}
.tl--img .tl__item h3{
  margin-top:0; font-size:clamp(1.3rem,1.16rem + .5vw,1.6rem);
  letter-spacing:-.02em; text-wrap:balance;
}
.tl--img .tl__item p{ margin-top:.6rem; max-width:46ch; }
.tl--img .tl__pic{
  margin:0; border-radius:var(--r); overflow:hidden;
  background:var(--cloud); box-shadow:var(--lift-1); border:1px solid var(--edge);
}
.tl--img .tl__pic img{ display:block; width:100%; height:100%; object-fit:cover; aspect-ratio:16/10; }

/* the colour library */
.swatchgrid{ display:grid; gap:var(--s4); grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); }
.swatchcard{ border-radius:var(--r); overflow:hidden; border:1px solid var(--edge); background:var(--paper); box-shadow:var(--lift-1); transition:transform var(--t-2) var(--e), box-shadow var(--t-2) var(--e); }
.swatchcard:hover{ transform:translateY(-4px); box-shadow:var(--lift-2); }
.swatchcard__c{ aspect-ratio:5/4; background:var(--c); position:relative; }
.swatchcard__c::after{ content:""; position:absolute; inset:0; background:linear-gradient(155deg,oklch(1 0 0 /.3),transparent 44%,oklch(0 0 0 /.24)); }
.swatchcard__t{ padding:.7rem .85rem .9rem; display:grid; gap:.1rem; }
.swatchcard__t b{ font-size:.94rem; }
.swatchcard__t small{ font-size:.76rem; color:var(--mute); font-variant-numeric:tabular-nums; letter-spacing:.03em; }
.swatchcard__t span{ font-size:.82rem; color:var(--slate); line-height:1.4; margin-top:.3rem; }
.swatchcard[hidden]{ display:none; }
.prose__note{ margin-top:var(--s5); font-size:.9rem; color:var(--mute); max-width:66ch; }

/* reviews */
.revs{ display:grid; gap:var(--s4); }
.rev{ background:var(--paper); border:1px solid var(--edge); border-radius:var(--r); padding:var(--s5); box-shadow:var(--lift-1); display:grid; gap:.7rem; align-content:start; }
.rev__stars{ color:var(--sun-d); letter-spacing:.1em; font-size:.9rem; }
.rev p{ font-size:.98rem; line-height:1.55; }
.rev cite{ font-style:normal; font-weight:700; font-size:.9rem; }
.rev cite span{ display:block; font-weight:500; color:var(--mute); font-size:.82rem; }
@media (min-width:700px){ .revs{ grid-template-columns:repeat(2,1fr); } }
@media (min-width:1040px){ .revs{ grid-template-columns:repeat(3,1fr); } }

/* people */
.people{ display:grid; gap:var(--s4); }
.people__card{ background:var(--paper); border:1px solid var(--edge); border-radius:var(--r); padding:var(--s5); box-shadow:var(--lift-1); }
.people__card h3{ font-size:1.16rem; }
.people__card p{ color:var(--slate); font-size:.94rem; margin-top:.4rem; }
.people__role{ font-size:.78rem; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:var(--blue); }
@media (min-width:760px){ .people{ grid-template-columns:repeat(3,1fr); } }

/* 404 */
.oops{ min-height:70svh; display:grid; place-items:center; text-align:center; padding:calc(var(--nav-h) + var(--s8)) var(--pad) var(--s8); }
.oops__n{ font-family:var(--disp); font-weight:800; font-size:clamp(5rem,22vw,11rem); line-height:.9; letter-spacing:-.05em; color:var(--blue-2); }
.oops__links{ display:flex; flex-wrap:wrap; justify-content:center; gap:.4rem var(--s4); margin-top:var(--s7); padding-top:var(--s5); border-top:1px solid var(--edge); }
.oops__links a{ font-size:.92rem; font-weight:600; color:var(--slate); min-height:40px; display:inline-flex; align-items:center; }
.oops__links a:hover{ color:var(--blue); }

/* ── 23a. THE SIDE RAIL ────────────────────────────────────────
   Where you are on the page, always, without taking any room. */
.sider{
  /* Follows the same fluid measure as the hero, so on a wide screen the dots
     move out with the content instead of staying pinned to the old 1280 cap
     with a band of nothing beyond them. */
  position:fixed; right:max(.9rem, calc((100vw - var(--herow)) / 2 - 1.5rem));
  top:50%; transform:translateY(-50%);
  z-index:var(--z-dock); display:none; flex-direction:column; gap:.1rem;
  opacity:0; pointer-events:none; transition:opacity var(--t-3) var(--e);
}
.sider.is-on{ opacity:1; pointer-events:auto; }
/* The label used to sit in the link's own flow, to the left of the dot. It
   is invisible until you hover, but an invisible flex child still takes its
   width — so every dot was a 100-200px click target reaching left across
   whatever the page had there, and it ate clicks meant for the content. The
   colour swatches in the studio were the worst of it. The label is lifted
   out of flow and made untouchable: the link is now the dot and nothing
   more, and the label floats to its left when you are on it. */
.sider a{
  position:relative; display:flex; align-items:center; justify-content:flex-end;
  width:22px; height:26px; padding-right:2px; color:var(--mute);
}
.sider i{
  width:8px; height:8px; border-radius:50%; flex:none;
  background:var(--edge-2); transition:all var(--t-2) var(--e);
}
/* White, on a dark pill. The rail is fixed and floats over whatever section
   happens to be behind it, so the label cannot take its colour from the page
   — and the business page now has two near-white bands in it, which plain
   white type would disappear into. The pill is the same glass the hero
   promises use, and it carries the label over any ground. */
.sider span{
  position:absolute; right:calc(100% + .4rem); top:50%; translate:0 -50%;
  pointer-events:none;
  padding:.28rem .6rem; border-radius:999px;
  background:oklch(0.14 0.009 258 / .82);
  box-shadow:inset 0 0 0 1px oklch(1 0 0 / .10);
  backdrop-filter:blur(7px) saturate(120%); -webkit-backdrop-filter:blur(7px) saturate(120%);
  font-size:.84rem; font-weight:700; letter-spacing:.005em; white-space:nowrap;
  color:oklch(0.99 0 0);
  opacity:0; transform:translateX(6px);
  transition:opacity var(--t-2) var(--e), transform var(--t-2) var(--e);
}
.sider a:hover i{ background:var(--slate); transform:scale(1.25); }
.sider a:hover span{ opacity:1; transform:none; }
.sider a[aria-current="true"] i{ background:var(--blue); width:9px; height:9px; box-shadow:0 0 0 4px var(--blue-t); }
/* The dot already says which one you are on — it is bigger, blue and ringed —
   so the label does not have to say it a second time in another colour. */
.sider a[aria-current="true"] span{ opacity:1; transform:none; }
/* That label is on screen the whole way down the page, and until the viewport
   is wider than the content plus room for it, "the whole way down the page"
   means "on top of the page". Below that it waits to be asked for; hovering a
   dot still brings it up, and an overlap you caused yourself is a different
   thing from one that was already there. */
@media (max-width:1420px){
  .sider a[aria-current="true"]:not(:hover) span{ opacity:0; transform:translateX(6px); }
}
@media (min-width:1160px){ .sider{ display:flex; } }
@media (prefers-reduced-motion: reduce){ .sider span{ opacity:1; transform:none; } }

/* the mobile bar picks up your build as soon as there is one */

/* ── 23b. PARALLAX + LIGHTBOX ──────────────────────────────── */
[data-parallax]{ will-change:transform; }
[data-parallax] img,[data-parallax] picture img{ transform:translate3d(0,var(--par,0),0) scale(1.06); transition:transform 60ms linear; }

.lightbox{
  position:fixed; inset:0; z-index:var(--z-top);
  display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:var(--s3);
  padding:clamp(1rem,4vw,3rem);
  background:oklch(0.14 0.02 258 / .93);
  backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  opacity:0; transition:opacity var(--t-2) var(--e);
}
.lightbox.is-open{ opacity:1; }
.lightbox[hidden]{ display:none; }
.lightbox__fig{ display:grid; gap:var(--s3); justify-items:center; max-height:100%; min-width:0; }
.lightbox__fig img{
  max-width:100%; max-height:78svh; width:auto; border-radius:var(--r);
  box-shadow:0 40px 90px oklch(0 0 0 / .55);
  transform:scale(.97); transition:transform var(--t-3) var(--spring);
}
.lightbox.is-open .lightbox__fig img{ transform:none; }
.lightbox__fig figcaption{ color:#fff; font-size:.92rem; text-align:center; max-width:60ch; }
.lightbox__fig figcaption span{ display:block; color:oklch(0.82 0.01 250); font-size:.85rem; margin-top:.15rem; }
.lightbox__x{
  position:absolute; top:clamp(.75rem,3vw,1.5rem); right:clamp(.75rem,3vw,1.5rem);
  width:46px; height:46px; border-radius:50%; display:grid; place-items:center;
  background:oklch(1 0 0 / .12); color:#fff;
}
.lightbox__x:hover{ background:oklch(1 0 0 / .22); }
.lightbox__nav{
  width:48px; height:48px; border-radius:50%; display:grid; place-items:center;
  background:oklch(1 0 0 / .12); color:#fff; flex:none;
}
.lightbox__nav:hover{ background:oklch(1 0 0 / .22); }
.lightbox svg{ width:22px; height:22px; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }
.gal__item{ cursor:zoom-in; }
@media (max-width:600px){
  .lightbox{ grid-template-columns:1fr; }
  .lightbox__nav{ position:absolute; bottom:clamp(.75rem,4vw,2rem); }
  .lightbox__nav--prev{ left:clamp(.75rem,6vw,3rem); }
  .lightbox__nav--next{ right:clamp(.75rem,6vw,3rem); }
  .lightbox__fig figcaption{ padding-bottom:3.5rem; }
}

/* ── 23c. MICRO-INTERACTIONS ───────────────────────────────── */

/* A light sweep across a card on hover. Cheap, and it makes a static grid
   feel like it is made of physical things. Pointer devices only — on touch
   there is no hover state and the animation would never resolve. */
@media (hover:hover) and (pointer:fine){
  .svc__card,.plan,.fin,.swatchcard,.people__card,.rev{ position:relative; }
  .svc__card::after,.fin::after,.swatchcard::after{
    content:""; position:absolute; inset:0; z-index:3; pointer-events:none;
    background:linear-gradient(105deg, transparent 38%, oklch(1 0 0 / .38) 48%, transparent 58%);
    transform:translateX(-130%); opacity:0;
  }
  .svc__card:hover::after,.fin:hover::after,.swatchcard:hover::after{
    opacity:1; transform:translateX(130%);
    transition:transform 780ms var(--e-io), opacity 120ms linear;
  }
  /* the ticker is decorative; let people stop it to read one */
  .ticker:hover .ticker__row{ animation-play-state:paused; }
}

/* The swatch you just picked gives a small, single bounce.
   `forwards`, and every frame states opacity, and both of those are load-
   bearing. The swatches are dealt in by `qin`, which means their base rule is
   `opacity:0` with the animation supplying the 1. Naming a second animation
   here replaced `qin` on the selected chip — so the moment `swpop` finished,
   the chip fell back to the base rule and vanished. A colour you had just
   chosen turned into a white hole in the grid, which is exactly what it looked
   like: the selection going white. */
.sw[aria-checked="true"]{ animation:swpop 340ms var(--spring) forwards; }
@keyframes swpop{
  0%  { opacity:1; transform:translateY(-3px) scale(.94); }
  60% { opacity:1; transform:translateY(-3px) scale(1.06); }
  100%{ opacity:1; transform:translateY(-3px) scale(1); }
}

.rail__dot{ cursor:pointer; }
.rail__dot:hover:not([aria-current="step"]){ background:var(--blue-2); color:var(--blue-d); }

/* photographs uncover rather than fade — the mask travels with the reveal */
.js .rv-mask{ clip-path:inset(0 0 100% 0); transition:clip-path 900ms var(--e-io), opacity 500ms var(--e); }
.js .rv-mask.is-in{ clip-path:inset(0 0 0 0); }

/* a primary button lifts its own shadow toward the pointer */
.btn--primary::after{
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:radial-gradient(120px circle at var(--mx,50%) var(--my,50%), oklch(1 0 0 / .22), transparent 70%);
  opacity:0; transition:opacity var(--t-2) var(--e);
}
.btn--primary:hover::after{ opacity:1; }

/* ── 24. REVEAL ON SCROLL ──────────────────────────────────── */
.js .rv{ opacity:0; transform:translateY(16px); transition:opacity 620ms var(--e), transform 620ms var(--e); }
.js .rv.is-in{ opacity:1; transform:none; }
.js .rv-d1{ transition-delay:60ms; } .js .rv-d2{ transition-delay:120ms; }
.js .rv-d3{ transition-delay:180ms; } .js .rv-d4{ transition-delay:240ms; }

/* ── 25. REDUCED MOTION ────────────────────────────────────── */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; }
  .js .rv{ opacity:1 !important; transform:none !important; }
  .nav.is-hidden{ transform:none; }
}

/* ── 30. APPOINTMENT NOTICE ─────────────────────────────────────
   A standing policy, not a promotion. It borrows the house blue so it
   reads as information from the shop, and stops short of the alarm
   colours, which are reserved for things that have gone wrong. */
/* ── the line above the form ────────────────────────────────────
   Was a panel of service facts, then a phone number at headline size. Both
   were the page asking for a different conversation than the one it wants:
   the shop would rather have a message it can price than a call it has to
   take under a car. One line now — what the rule is, and why it exists. */
.apt{
  margin:0 auto var(--s5); max-width:62ch; text-align:center;
  color:var(--slate); font-size:.95rem; line-height:1.55;
}
.apt b{ color:var(--bright); font-weight:700; }
/* One alternative route, and it is the one the shop wants: a text it can read
   between panels and answer with a price. */
.apt__alt{
  display:inline-block; margin-top:.5rem; font-weight:700; color:var(--blue);
  text-decoration:underline; text-underline-offset:3px;
  text-decoration-color:var(--blue-r);
}
.apt__alt:hover{ text-decoration-color:currentColor; }

/* ── 31. INTAKE ─────────────────────────────────────────────────
   A long form asked in four short passes. Every field is in the DOM at
   once, so the page still works and still submits with no JavaScript;
   the step behaviour is layered on top of that. */
.intake__grid{ display:grid; gap:var(--s5); align-items:start; }
/* One column. The three cards that used to sit beside the form were three
   things to read before answering the first question; the form gets the room
   instead, capped so the fields do not run to newspaper width. */
.intake__grid{ max-width:60rem; }

.steps{ display:grid; grid-auto-flow:column; grid-auto-columns:1fr; margin-bottom:var(--s5); }
.steps li{ display:grid; position:relative; text-align:center; }
/* The whole node is the target, number and label together, so the rail can
   be walked with a thumb as well as a mouse. */
.steps__btn{
  display:grid; justify-items:center; gap:.5rem; width:100%;
  background:none; border:0; margin:0; padding:.3rem .2rem;
  font:inherit; color:inherit; cursor:pointer; border-radius:var(--r-sm);
}
.steps__btn:focus-visible{ outline:2px solid var(--blue); outline-offset:2px; }
.steps li:not(.is-on) .steps__btn:hover .steps__n{ box-shadow:inset 0 0 0 1.5px var(--blue); color:var(--ink); }
.steps li:not(.is-on) .steps__btn:hover .steps__lb{ color:var(--ink); }
/* The rail is drawn back from each node rather than under the whole row,
   so it cannot outrun the last step or peek out past the first. */
.steps li::before{
  content:""; position:absolute; top:16px; right:50%; width:100%; height:2px;
  background:var(--edge-2); z-index:0; transition:background var(--t-2) var(--e);
}
.steps li:first-child::before{ display:none; }
.steps li.is-on::before,.steps li.is-done::before{ background:var(--blue); }
.steps__n{
  position:relative; z-index:1; width:34px; height:34px; border-radius:50%;
  display:grid; place-items:center; font-weight:700; font-size:.92rem;
  background:var(--cloud); color:var(--mute);
  box-shadow:inset 0 0 0 1.5px var(--edge-2);
  transition:background var(--t-2) var(--e), color var(--t-2) var(--e), box-shadow var(--t-2) var(--e);
}
.steps li.is-on .steps__n{ background:var(--blue); color:var(--paper); box-shadow:0 0 0 4px var(--blue-t); }
.steps li.is-done .steps__n{ background:var(--blue); color:var(--paper); box-shadow:none; }
.steps__lb{ font-size:.78rem; font-weight:600; color:var(--mute); }
.steps li.is-on .steps__lb{ color:var(--ink); }
@media (max-width:560px){ .steps__lb{ display:none; } }

.step{ display:grid; gap:var(--s4); }

/* ── the rail as a bar ──────────────────────────────────────────
   Four numbered nodes with four labels is the whole form's table of contents
   shown before the first question, and it reads as work. The same markup —
   intake.js still drives is-on and is-done on these — renders as four
   segments of a bar with only the current step named. Progress without the
   inventory. */
/* The four proofs — clay bar, loan car, lifetime cover, fixed price — are a
   desktop opener for "what you get". On a phone they were tried in two
   places and read as clutter in both: between the booking band and the
   gallery, and again directly above the form. The reason is that the page has
   already made all four claims by then — the hero promises say clay bar, loan
   car, wrapped edges and the warranty inside the first screen, and the band
   above the form says fixed price. A phone gets them once, in the hero.
   Desktop keeps the row, where it opens a section rather than interrupting
   one. */
@media (max-width:799px){
  /* The ticks are the only thing left in this section, and they are hidden
     here — an empty band with a section's padding is 160px of nothing above
     the form. */
  .proof{ display:none; }
}

/* The invitation stands on the light ground above the panel, in the section's
   own ink — inside the panel it was a second title competing with the
   question, and it pushed the first question below the fold. */
.book__k{ display:none; }
@media (max-width:799px){
  /* `.prose p` sets a colour at a weight a lone class cannot beat, which is
     why this line rendered slate grey rather than the near-black it was given.
     Stated back at matching weight — the same trap this stylesheet sets every
     time a new element lands inside .prose. */
  .prose .book__k{ color:var(--bright); }
  .book__k{
    display:block; margin:var(--s3) 0 var(--s3);
    font-family:var(--disp); font-weight:800; font-size:1.8rem;
    letter-spacing:-.03em; text-align:center;
  }
}

/* The rail is a bar and the name of the step, nothing else. It carried a
   title and a subtitle as well, which is three pieces of chrome above the
   first question on the smallest screen the form has. */
.intake__rail{ padding-block:var(--s3); }
/* One line that fills. --p is how far along the form you are, 0 to 1,
   written by intake.js; the track and the fill both read from it. The four
   list items are still the four steps and still click through to them — they
   are just the hit area now, with nothing drawn on them. */
.steps--bar{
  --p:0;
  grid-auto-flow:column; grid-auto-columns:1fr; gap:0;
  position:relative; margin-bottom:var(--s4); padding-top:.55rem;
}
.steps--bar li::before{ display:none; }
.steps--bar .steps__btn{ justify-items:stretch; gap:0; padding:0; }
/* The segments themselves are invisible. They keep their height so the row is
   still a comfortable thing to tap along. */
.steps--bar .steps__n,
.steps--bar li.is-on .steps__n,
.steps--bar li.is-done .steps__n{
  position:relative; z-index:2;
  width:100%; height:12px; border-radius:0; font-size:0;
  background:none; box-shadow:none; color:transparent;
}
.steps--bar li.is-done .steps__n::after{ content:none; }
/* The track: the panel's own hairline tone, so an empty bar is part of the
   card rather than a second colour on it. */
.steps--bar::before{
  content:""; position:absolute; left:0; right:0; bottom:3px; height:6px;
  border-radius:999px; background:var(--edge);
}
.steps--bar::after{
  content:""; position:absolute; left:0; bottom:3px; height:6px;
  width:clamp(6px, calc(var(--p) * 100%), 100%);
  border-radius:999px;
  background:linear-gradient(90deg, var(--blue-d), var(--blue));
  transition:width .6s cubic-bezier(.3,1.02,.4,1);
}
@media (prefers-reduced-motion:reduce){
  .steps--bar::after{ transition:none; }
}
/* No step names. The road says how far along you are and the question right
   under it says what you are answering — naming the step as well is the same
   fact twice. The text stays in the page for the buttons' accessible names,
   which is why this hides it rather than removing it. */
.steps--bar .steps__lb{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}
.intake__rail:has(.steps--bar){ padding-bottom:var(--s3); }

.js .step{ display:none; }
.js .step.is-on{ display:grid; }
.step__h{ display:grid; gap:.25rem; margin-bottom:.2rem; }
.step__t{ font-family:var(--disp); font-weight:700; font-size:clamp(1.3rem,1.1rem + .8vw,1.7rem); letter-spacing:-.02em; }
.step__d{ color:var(--slate); font-size:.94rem; max-width:56ch; }
.fset{ border:0; margin:0; padding:0; display:grid; gap:var(--s4); }
.fset > legend{ font-size:.88rem; font-weight:700; padding:0; margin-bottom:.5rem; }
.grid2{ display:grid; gap:var(--s4); }
@media (min-width:620px){ .grid2{ grid-template-columns:1fr 1fr; } }

/* Selectable cards. The whole card is the target, not a small circle. */
.picks{ display:grid; gap:.6rem; }
@media (min-width:620px){
  .picks--2{ grid-template-columns:1fr 1fr; }
  /* Seven cards in two columns leaves the last one alone on its own row
     looking like a mistake. It is the catch-all, so it runs the full width
     and reads as "or, anything else" instead. */
  .picks--svc .pick--svc:last-child{ grid-column:1 / -1; }
}
.pick{
  position:relative; display:grid; gap:.15rem; cursor:pointer;
  padding:.85rem 2.9rem .85rem 1rem; min-height:56px; align-content:center;
  border:1.5px solid var(--edge); border-radius:var(--r); background:var(--paper);
  transition:border-color var(--t-1) var(--e), background var(--t-1) var(--e);
}
.pick:hover{
  border-color:oklch(from var(--dw) l c h / .55);
  background:oklch(from var(--dw) l c h / .07);
}
.pick input{ position:absolute; opacity:0; width:1px; height:1px; }
.pick__b{ font-weight:700; font-size:.96rem; }
.pick__s{ font-size:.85rem; color:var(--slate); line-height:1.4; }
.pick__tick{
  position:absolute; right:.9rem; top:50%; translate:0 -50%;
  width:24px; height:24px; border-radius:50%; display:grid; place-items:center;
  /* A visible ring at rest. At 1.5px of --edge-2 against the card it was
     nearly invisible, so the one control on the card read as a gap at the end
     of the text rather than as the thing you tick. */
  background:oklch(0 0 0 / .22);
  box-shadow:inset 0 0 0 1.6px oklch(1 0 0 / .26);
  transition:box-shadow var(--t-1) var(--e), background var(--t-1) var(--e);
}
.pick:hover .pick__tick{ box-shadow:inset 0 0 0 1.6px oklch(from var(--dw) l c h / .75); }
.pick--check .pick__tick{ border-radius:var(--r-xs); }
.pick__tick::after{
  content:""; width:10px; height:10px; border-radius:50%; background:var(--blue);
  scale:0; transition:scale var(--t-1) var(--spring);
}
/* A checkbox should confirm with a check, not with a smaller square inside a
   square. Drawn from two borders and rotated, so it costs no markup and can
   spring in from nothing the way the radio dot does. */
.pick--check .pick__tick::after{
  width:11px; height:6px; border-radius:0; background:none;
  border-left:2.4px solid var(--blue); border-bottom:2.4px solid var(--blue);
  rotate:-45deg; translate:0 -1px;
}
.pick.is-picked{ border-color:var(--blue); background:var(--blue-t); }
.pick.is-picked .pick__tick{ box-shadow:inset 0 0 0 1.5px var(--blue); }
.pick.is-picked .pick__tick::after{ scale:1; }
.pick:focus-within{ outline:2px solid var(--blue); outline-offset:2px; }

/* Segmented choice, for the answers short enough to sit on one line. */
.seg{ display:flex; flex-wrap:wrap; gap:.45rem; }
.seg label{ position:relative; cursor:pointer; }
.seg input{ position:absolute; opacity:0; width:1px; height:1px; }
.seg span{
  display:inline-flex; align-items:center; min-height:44px; padding:.4rem .95rem;
  border-radius:999px; border:1.5px solid var(--edge); background:var(--paper);
  font-size:.9rem; font-weight:600; color:var(--slate);
  transition:border-color var(--t-1) var(--e), background var(--t-1) var(--e), color var(--t-1) var(--e);
}
.seg label:hover span{ border-color:var(--slate); }
.seg label.is-picked span{ border-color:var(--blue); background:var(--blue-t); color:var(--ink); }
.seg label:focus-within span{ outline:2px solid var(--blue); outline-offset:2px; }

/* ── the folding question ───────────────────────────────────────
   An answered group collapses to one row: the question on the left, the
   answer on the right, a chevron that says it can be opened again. It is a
   real button, so it keeps its own focus ring and reads to a screen reader as
   the expander it is. */
.fold{
  display:none; width:100%; align-items:center; gap:.6rem;
  padding:.65rem 0; margin:0; border:0; background:none; cursor:pointer;
  font:inherit; color:var(--ink); text-align:left;
}
.fold__q{ font-weight:600; }
.fold__a{ margin-left:auto; font-weight:700; color:var(--blue); }
.fold__i{
  width:18px; height:18px; flex:none; fill:none; stroke:currentColor;
  stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; color:var(--mute);
  transition:rotate var(--t-2) var(--e);
}
.fold[aria-expanded="true"] .fold__i{ rotate:180deg; }
/* The legend is replaced by the fold's own question, so showing both would
   ask it twice. */
.js .fset:has(> .fold) > legend{
  position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%);
}
.js .fset > .fold{ display:flex; }
.fset.is-folded .seg{ display:none; }
.fset.is-folded{ border-bottom:1px solid var(--edge); }

.intake__nav{ display:flex; gap:.6rem; align-items:center; margin-top:var(--s2); flex-wrap:wrap; }
.intake__nav .btn{ flex:1 1 12rem; justify-content:center; }
.intake__nav .btn--quiet{ flex:0 1 auto; }

/* Review pass. Reading it back is the last chance to catch a wrong plate. */
.rev{ display:grid; border:1px solid var(--edge); border-radius:var(--r); background:var(--paper); overflow:hidden; }
.rev__row{ display:grid; grid-template-columns:10.5rem 1fr auto; gap:var(--s3); align-items:baseline; padding:.8rem var(--s4); border-bottom:1px solid var(--edge); }
.rev__row:last-child{ border-bottom:0; }
.rev__k{ font-size:.86rem; color:var(--mute); }
.rev__v{ font-weight:600; font-size:.94rem; }
.rev__v em{ color:var(--mute); font-weight:400; font-style:normal; }
.rev__edit{ font-size:.82rem; font-weight:700; color:var(--blue); background:none; border:0; cursor:pointer; padding:.25rem; }
.rev__edit:hover{ text-decoration:underline; }
@media (max-width:560px){
  .rev__row{ grid-template-columns:1fr auto; }
  .rev__k{ grid-column:1 / -1; }
}

/* The running summary. Sticky on desktop only: on a phone it would be a
   second copy of the form sitting on top of the form. */
.sum{ display:grid; gap:var(--s4); }
@media (min-width:1040px){ .sum{ position:sticky; top:calc(var(--nav-h) + var(--s3)); } }
.sum__list{ display:grid; gap:.5rem; }
.sum__row{ display:grid; grid-template-columns:auto 1fr; gap:var(--s3); align-items:baseline; font-size:.88rem; padding-bottom:.5rem; border-bottom:1px solid var(--edge); }
.sum__row:last-child{ border-bottom:0; padding-bottom:0; }
.sum__k{ color:var(--mute); }
.sum__v{ font-weight:600; text-align:right; }
.sum__empty{ color:var(--mute); font-size:.9rem; }
.next{ display:grid; gap:.7rem; counter-reset:n; }
.next li{ display:grid; grid-template-columns:auto 1fr; gap:.7rem; align-items:start; font-size:.9rem; color:var(--slate); }
.next li::before{
  counter-increment:n; content:counter(n);
  width:22px; height:22px; border-radius:50%; display:grid; place-items:center;
  background:var(--blue-t); color:var(--blue); font-weight:700; font-size:.76rem;
}
.next b{ color:var(--ink); font-weight:700; }

/* A button inside a prose section was losing its colour and picking up an
   underline: `.prose a` outranks `.btn` on both. Buttons are not body links. */
.prose a.btn{ color:var(--fg); text-decoration:none; font-weight:700; }

/* ── 32. INTAKE, SECOND PASS ────────────────────────────────────
   The first version was legible but flat: one grey slab, seven
   identical cards, and the brand colour showing only as a 10px dot.
   This gives the panel structure and lets the blue actually land. */

/* The notice heading is the policy, so it reads before the body. It has to
   out-specify `.apt p`, which was quietly holding it at body size and
   body colour. */
.apt p.apt__t{ font-size:1.22rem; font-weight:800; color:var(--bright); letter-spacing:-.015em; margin-top:0; }

/* A lit panel rather than a flat one: cool at the top where the rail
   sits, settling darker under the fields. */
.form--intake{
  padding:0; gap:0; overflow:hidden;
  border-color:oklch(1 0 0 / .14);
  /* Lit from the top rather than sitting in the dark. The whole panel was
     within a few points of the page behind it, so the one thing on the page
     with something to ask read as a hole rather than as a place to answer. */
  background:
    radial-gradient(96% 58% at 50% -8%, oklch(from var(--dw) l c h / .18), transparent 64%),
    linear-gradient(180deg, oklch(0.330 0.014 250), oklch(0.258 0.012 255));
  box-shadow:0 34px 74px -34px oklch(0 0 0 / .82), inset 0 1px 0 oklch(1 0 0 / .10);
}
.intake__rail{
  padding:var(--s5) var(--s5) var(--s4);
  /* Lighter than the body, not darker. A header that recedes behind the
     content it labels puts the step you are on in the dimmest part of the
     panel. */
  background:oklch(1 0 0 / .045);
  border-bottom:1px solid oklch(1 0 0 / .09);
}
.intake__rail .steps{ margin-bottom:0; }
.intake__body{ padding:var(--s5); display:grid; gap:var(--s4); }
@media (max-width:560px){
  .intake__rail{ padding:var(--s4) var(--s4) var(--s3); }
  .intake__body{ padding:var(--s4); }
}

/* A passed step shows a tick, not the number it used to be. */
.steps li.is-done .steps__n{ font-size:0; }
.steps li.is-done .steps__n::after{
  content:""; width:11px; height:6px; margin-top:-3px;
  border-left:2.2px solid currentColor; border-bottom:2.2px solid currentColor;
  rotate:-45deg;
}

/* ── the service chooser ────────────────────────────────────────
   Seven identical rectangles are a list, not a choice. The icon tile
   gives each one a silhouette you can aim at, and carries the colour
   when it is picked. */
.pick--svc{
  grid-template-columns:auto 1fr; align-items:center; gap:.9rem;
  padding:.95rem 2.9rem .95rem 1rem;
  transition:border-color var(--t-1) var(--e), background var(--t-1) var(--e),
             transform var(--t-1) var(--e), box-shadow var(--t-1) var(--e);
}
.pick__ic{
  width:46px; height:46px; border-radius:14px; flex:none;
  display:grid; place-items:center;
  /* Tinted, and drawn in something brighter than the caption beside it. A
     5%-white tile with a muted-grey mark inside it was the lowest-contrast
     thing on the card, which is the wrong end for the only picture. */
  background:oklch(from var(--dw) l c h / .13);
  color:oklch(0.86 0.045 250);
  box-shadow:inset 0 0 0 1px oklch(from var(--dw) l c h / .28);
  transition:background var(--t-2) var(--e), color var(--t-2) var(--e), box-shadow var(--t-2) var(--e);
}
.pick__ic svg{ width:23px; height:23px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.pick:hover .pick__ic{ background:oklch(from var(--dw) l c h / .20); color:oklch(0.93 0.03 250); }
.pick__txt{ display:grid; gap:.1rem; min-width:0; }
/* The list is only ever trimmed on a phone, so the control that untrims it
   does not exist anywhere else. */
.morepicks{ display:none; }
.pick:hover{ transform:translateY(-1px); }

/* ── the form on a phone ────────────────────────────────────────
   The first mobile pass squeezed the desktop layout: seven icon tiles two
   up, a four-node rail, and a panel dark enough that the fields disappeared
   into it. A form is the one place on this site that is work rather than
   window-shopping, so on a phone it gets lighter ground, one column, and
   nothing decorative competing with the question being asked.

   The icon tiles go. A glyph in a rounded square, repeated seven times, is
   the house style of every generated form on the internet — and at 36px it
   was carrying no information the label did not already carry. The row is
   the target instead: name, one line of plain English, and a tick. */
@media (max-width:719px){
  /* Lighter, and lit from the top. The panel is where the work happens, so
     it steps forward from the page rather than receding into it. */
  .form--intake{
    background:
      radial-gradient(120% 60% at 50% -10%, oklch(from var(--dw) l c h / .16), transparent 70%),
      linear-gradient(180deg, oklch(0.345 0.014 250), oklch(0.288 0.012 254));
    border-color:oklch(1 0 0 / .14);
    box-shadow:0 22px 50px -28px oklch(0 0 0 / .7), inset 0 1px 0 oklch(1 0 0 / .10);
  }
  .intake__rail{ background:oklch(0 0 0 / .12); padding:var(--s4) var(--s4) .9rem; }
  .intake__body{ padding:var(--s4) var(--s4) var(--s5); gap:var(--s4); }

  /* The phone rail used to be turned into a bar here, with its own segment
     colours. `.steps--bar` now does that at every width, and these rules came
     later in the sheet — so the phone was quietly painting over the bar's
     surface with a flat fill. What is left is the part that is still only
     true on a phone: the step name set in the accent, small and in caps. */
  .steps li::before{ display:none; }
  .steps__btn{ gap:0; padding:.55rem 0 0; }
  .steps li.is-done .steps__n::after{ display:none; }
  .steps li{ position:static; }

  /* Fields match the rows: a light film on the panel, not a hole cut in it.
     The placeholder comes up to --muted, which clears 4.5:1 here; --mute did
     not, and a placeholder is often the only label a rushed reader gets. */
  .field input,.field select,.field textarea{
    background:oklch(1 0 0 / .055); border-color:oklch(1 0 0 / .14);
    min-height:52px;
  }
  .field input::placeholder,.field textarea::placeholder{ color:var(--muted); }
  /* Chips on the same film as the fields and the rows, so one screen has one
     surface rather than three shades of nearly black. */
  .seg span{ background:oklch(1 0 0 / .05); border-color:oklch(1 0 0 / .14); min-height:46px; }
  .seg label.is-picked span{ background:oklch(from var(--dw) l c h / .18); }
  /* The read-back is the same film again, not a darker slab inside the panel. */
  .rev{ background:oklch(1 0 0 / .05); border-color:oklch(1 0 0 / .13); }
  .rev__row{ border-bottom-color:oklch(1 0 0 / .10); padding:.85rem var(--s4); }

  /* One column. A row is a whole answer: what it is, what it means, and
     whether it is chosen — nothing to decode. */
  .picks--svc{ grid-template-columns:1fr; gap:.45rem; }
  .pick--svc{
    grid-template-columns:1fr; justify-items:stretch; text-align:left;
    gap:.1rem; padding:.85rem 3rem .85rem .95rem; min-height:60px;
  }
  .pick--svc .pick__ic{ display:none; }
  .pick--svc .pick__txt{ justify-items:stretch; }
  .pick--svc .pick__b{ font-size:.98rem; line-height:1.3; }
  .pick--svc .pick__s{ display:block; font-size:.82rem; line-height:1.4; }
  .pick--svc .pick__tick{ top:50%; right:1rem; translate:0 -50%; }

  /* Held back until asked for. Not display:none — the inputs stay in the form
     and stay reachable, they are just out of the way until the button says
     otherwise. A hidden input that has been checked (a ?job= link lands
     here) forces the list open on load. */
  .picks--svc:not(.is-open) .pick[data-more]{ display:none; }
  /* A link, not a second button. Two full-width buttons in a row make the
     optional one look like the way forward. */
  .morepicks{
    display:inline-flex; align-items:center; justify-self:center;
    min-height:40px; margin-top:.2rem; padding:.35rem 0;
    border:0; background:none; color:var(--blue);
    font:inherit; font-size:.9rem; font-weight:700;
    text-decoration:underline; text-underline-offset:4px;
    text-decoration-color:var(--blue-r);
  }
  .morepicks:active{ text-decoration-color:currentColor; }
  /* The rows lift off the panel rather than sinking into it: a light film
     over the ground instead of a darker card on top of it, which is what
     made the first pass read as a black box with black boxes in it. */
  .pick{
    background:oklch(1 0 0 / .055); border-color:oklch(1 0 0 / .13);
    box-shadow:inset 0 1px 0 oklch(1 0 0 / .06);
  }
  .pick__s{ color:var(--muted); }
  .pick__tick{ width:24px; height:24px; box-shadow:inset 0 0 0 1.6px oklch(1 0 0 / .30); }
  .pick.is-picked .pick__tick{ box-shadow:inset 0 0 0 1.6px var(--blue); }
  .pick.is-picked{ box-shadow:0 10px 24px -16px oklch(from var(--dw) l c h / .9); }
  /* Room under the last control for the phone bar that floats over it. */
  .intake__body{ padding-bottom:calc(var(--s5) + 3.2rem); }
  /* A press has to feel like a press: the row gives a little, then the tick
     springs in. Touch has no hover to fall back on. */
  .pick:active{ scale:.995; }

  .step__t{ font-size:1.3rem; letter-spacing:-.022em; }
  .step__d{ font-size:.88rem; }
  .apt{ gap:.7rem; }
  .apt__t{ font-size:1.05rem !important; }
  .apt p{ font-size:.88rem; }
  .apt__actions{ margin-top:var(--s3); gap:.45rem; }
  .apt__actions .btn{ flex:1 1 0; padding-inline:.6rem; font-size:.88rem; }

  /* The controls are a bar, not two buttons that happen to be near each
     other: forward is full width and unmissable, back is quiet beside it. */
  .intake__nav{ gap:.5rem; }
  .intake__nav .btn{ min-height:52px; flex:1 1 auto; }
  .intake__nav .btn--quiet{ flex:0 0 auto; padding-inline:1rem; }
}

/* ── the step change ────────────────────────────────────────────
   Answering a question should feel like moving, so the next step arrives
   rather than appearing: the panel's fields rise a few pixels and fade up in
   sequence. It hangs off .is-on, which the form already sets, and it is a
   transition on transform and opacity only — nothing that costs layout.

   Everything animated here is visible by default; the keyframes only run
   where the class is applied and JavaScript is present. With JS off the
   steps are plain stacked sections, which is what they should be. */
@media (prefers-reduced-motion: no-preference){
  .js .step.is-on > *{ animation:stepIn 520ms cubic-bezier(0.16,1,0.3,1) both; }
  .js .step.is-on > *:nth-child(2){ animation-delay:40ms; }
  .js .step.is-on > *:nth-child(3){ animation-delay:80ms; }
  .js .step.is-on > *:nth-child(4){ animation-delay:120ms; }
  .js .step.is-on > *:nth-child(n+5){ animation-delay:150ms; }
}
@keyframes stepIn{
  from{ opacity:0; transform:translateY(10px); }
  to{ opacity:1; transform:none; }
}

/* Picked reads as lit, not merely tinted. */
.pick.is-picked{
  border-color:var(--blue);
  background:linear-gradient(180deg, oklch(from var(--dw) l c h / .15), oklch(from var(--dw) l c h / .05));
  box-shadow:0 12px 28px -18px oklch(from var(--dw) l c h / .9);
}
.pick--svc.is-picked .pick__ic{
  background:var(--blue); color:oklch(0.17 0.012 252); box-shadow:none;
}
.pick.is-picked .pick__b{ color:var(--bright); }

/* Chips: a definite ring rather than a wash, so a selected answer is
   findable at a glance without seven solid blue pills shouting. */
.seg label.is-picked span{
  border-color:var(--blue); color:var(--bright);
  background:oklch(from var(--dw) l c h / .16);
  box-shadow:0 0 0 3px oklch(from var(--dw) l c h / .10);
}

/* Legends were the same weight as the hint text beside them. */
.fset > legend{ color:var(--ink); font-size:.9rem; }

/* ── 33. BOOK BAND ──────────────────────────────────────────────
   One pointer to the intake form, placed where somebody has just read
   the service list and is deciding. The form itself is much further
   down, under the prices, which is too long to wait for a first ask. */
.band{
  padding:var(--s6) 0;
  border-block:1px solid oklch(1 0 0 / .08);
  background:
    radial-gradient(64% 130% at 12% 50%, oklch(from var(--dw) l c h / .18), transparent 72%),
    oklch(0.205 0.012 255);
}
.band__in{ display:grid; gap:var(--s5); align-items:center; }
@media (min-width:880px){ .band__in{ grid-template-columns:1fr auto; gap:var(--s6); } }
/* The rule the shop runs on, said once and quietly, so the heading under it
   can be the friendly version of the same fact rather than the stern one. */
.band__k{
  font-size:.85rem; font-weight:800; letter-spacing:.13em; text-transform:uppercase;
  color:var(--blue); margin:0 0 .5rem;
}
.band__t{
  font-family:var(--disp); font-weight:800;
  font-size:clamp(1.55rem,1.2rem + 1.5vw,2.25rem); letter-spacing:-.025em;
}
.band__d{ color:var(--slate); margin-top:.45rem; max-width:50ch; font-size:.98rem; }
.band__d strong{ color:var(--ink); font-weight:700; }
.band__row{ display:flex; flex-wrap:wrap; gap:.6rem; }
@media (max-width:560px){ .band__row .btn{ flex:1 1 auto; justify-content:center; } }

/* On a phone this is not a section, it is a handover. It was tried as a flat
   slab and then as a lifted card, and the card was the worse of the two: a
   panel of its own, floating in a field of black, repeating a claim the page
   has already made twice — with two competing buttons under it.

   What it becomes is a rule across the page with one line of type sitting in
   it and one thing to do. No panel, no second button, and the padding that
   was holding the card off its neighbours goes with it. The text link under
   the button is the quiet alternative rather than a rival to it. */
@media (max-width:719px){
  /* And it moves. Between the prices and the guarantee it interrupted the one
     run of the page that is actually making the argument, with two buttons
     that duplicate the bar already fixed to the bottom of the screen. It now
     sits immediately above the intake form it points at, where it reads as
     the sentence before the form rather than as an advert between sections.

     `main` becomes a grid so one section can be ordered out of its DOM
     position. Everything above the band keeps `order:0` and so keeps its
     sequence; only the three sections that have to stay after it need
     naming. Sticky lives on elements inside these sections rather than on the
     sections themselves, so none of it is affected. */
  /* `minmax(0,1fr)`, not `1fr`: a grid item's automatic minimum size is its
     content, so one section with a wide row inside it stretches the column
     and takes the whole page off the right-hand edge of the phone. */
  #main{ display:grid; grid-template-columns:minmax(0,1fr); }
  #main > .band{ order:1; }
  #main > #book{ order:2; }
  #main > #compare{ order:3; }
  #main > #process{ order:4; }
  #main > #faq{ order:5; }

  /* The four-way comparison is a table of sixteen cells that has to be
     swiped sideways on a phone, dropped in between the guarantee and the
     booking band — a research exercise standing in the path of somebody who
     has already decided. It moves below the form, and the question it
     answers goes into the form itself, directly under the two options it is
     about, where it is asked at the moment somebody is choosing between
     them rather than a screen and a half earlier. */
  .jumpq{
    display:block; margin:.7rem 0 0; text-align:center;
  }
  .jumpq a{
    color:var(--blue); font-weight:700; font-size:.88rem;
    text-decoration:underline; text-underline-offset:3px;
    text-decoration-color:var(--blue-r);
  }

  .band{
    padding:var(--s4) 0; border-block:0; background:none;
    /* A hairline above, not a box around: the band belongs to the flow of the
       page rather than sitting on top of it. */
    border-top:1px solid oklch(1 0 0 / .10);
    margin-inline:var(--pad);
  }
  .band__k{ margin-bottom:.35rem; }
  .band__in{
    width:auto; margin-inline:0; padding:0;
    text-align:center; gap:var(--s4); justify-items:center;
  }
  .band__t{ font-size:1.22rem; letter-spacing:-.024em; }
  /* The line under the heading goes. "A few questions about the car, and a
     fixed price comes back within the hour" is the first thing the form
     itself says, and the form is now directly underneath — so on a phone it
     was a caption for something already on screen. Heading, then the two
     ways to start, and nothing in between. */
  .band__d{ display:none; }
  .band__row{ flex-direction:column; gap:.2rem; width:100%; }
  .band__row .btn{ width:100%; }
  /* `btn--lg` is sized for a section that is the point of the screen. This one
     is a handover between two sections that are. */
  .band__row .btn--primary{ min-height:48px; padding:.7em 1.3em; font-size:.95rem; }
  /* The second action stops being a button. Two full-width pills read as two
     equal choices, and one of them is meant to be the fallback. */
  .band__row .btn--ghost{
    min-height:auto; padding:.5rem 0; border:0; background:none;
    font-size:.9rem; font-weight:700; color:var(--blue);
    text-decoration:underline; text-underline-offset:4px;
    text-decoration-color:var(--blue-r);
  }
}

/* ── 34. BUSINESS WRAPS ─────────────────────────────────────────
   Photographs rather than drawn mock-ups. Both shots are honest about
   what they are: a fleet before any livery goes on, and film being
   trimmed in. Neither passes another firm's branding off as our work.
   The inset overlaps the main frame so the pair reads as one
   composition instead of two stacked pictures. */
.biz{ padding-block:var(--s9); background:var(--mist); }
.biz__in{ display:grid; gap:var(--s6); align-items:center; }
@media (min-width:960px){
  .biz__in{ grid-template-columns:minmax(0,.86fr) minmax(0,1fr); gap:var(--s7); }
}
.biz__copy .deck{ margin-top:var(--s3); max-width:46ch; }

.biz__list{ display:grid; gap:0; margin-top:var(--s5); }
.biz__list li{
  display:grid; gap:.15rem; padding:var(--s3) 0;
  border-bottom:1px solid var(--edge);
}
.biz__list li:first-child{ border-top:1px solid var(--edge); }
.biz__list b{ font-size:.98rem; }
.biz__list span{ color:var(--slate); font-size:.9rem; line-height:1.5; }
.biz__cta{ display:flex; flex-wrap:wrap; gap:.6rem; margin-top:var(--s5); }

/* The two products, priced. Desktop has room for the four services and what
   each one means; a phone does not, and a price is the thing being looked
   for anyway. */
.biz__quick{ display:none; }
@media (max-width:799px){
  /* A white slab on a phone. The grey band read as a box drawn around the
     section; white makes it the one bright thing in a dark page, which is
     what a section selling something to a business should be. Tokens are
     repointed rather than colours overridden, so the tiles, the rules and
     the buttons all follow without a rule each. */
  .biz{
    padding-block:var(--s6);
    background:oklch(1 0 0);
    border-radius:clamp(22px, 7vw, 40px);
    margin-inline:var(--pad);
    --paper:  oklch(1 0 0);
    --mist:   oklch(0.962 0.004 250);
    --cloud:  oklch(0.962 0.004 250);
    --edge:   oklch(0.884 0.006 250);
    --edge-2: oklch(0.780 0.010 250);
    --bright: oklch(0.160 0.012 252);
    --ink:    oklch(0.245 0.012 252);
    --slate:  oklch(0.430 0.012 252);
    --mute:   oklch(0.532 0.012 252);
    --signal: oklch(0.160 0.012 252);
    color:var(--ink);
  }
  /* The primary button inverts with the ground: dark on white, white on it. */
  .biz .btn--primary{ --bg:oklch(0.160 0.012 252); --fg:oklch(1 0 0); --bd:oklch(0.160 0.012 252); }
  .biz .btn--quiet{ --bg:transparent; --fg:var(--bright); --bd:var(--edge-2); }
  .biz__quick li{ background:var(--mist); border-color:var(--edge); }
  /* display:contents made every child a grid item of .biz__in, which carries
     a 2rem gap for the desktop two-column layout — so the sub-line sat two
     rems under its own heading. The stack sets its own rhythm instead. */
  .biz__in{ gap:0; }
  .biz__copy h2{ margin-bottom:.35rem; }
  .biz__copy .deck{ margin-top:0; }
  .biz__quick{ margin-top:var(--s4); }
  .biz__list{ display:none; }
  .biz__quick{
    display:grid; grid-template-columns:1fr 1fr; gap:.5rem;
    margin:var(--s4) 0 0; padding:0; list-style:none;
  }
  .biz__quick li{
    padding:.7rem .8rem; border-radius:var(--r-sm);
    border:1px solid var(--edge); background:oklch(1 0 0 / .03);
  }
  .biz__quick b{
    display:block; font-size:.72rem; font-weight:800; letter-spacing:.08em;
    text-transform:uppercase; color:var(--slate);
  }
  .biz__quick i{
    display:block; margin-top:.15rem; font-style:normal;
    font-family:var(--disp); font-weight:800; font-size:1.5rem;
    letter-spacing:-.03em; line-height:1; color:var(--bright);
  }
  .biz__quick span{ display:block; margin-top:.2rem; font-size:.78rem; color:var(--mute); }
  /* One photograph is the point; the inset and the caption are a gallery. */
  .biz__inset, .biz__fig figcaption{ display:none; }
  /* The van goes between the prices and the buttons: you see what you are
     buying, then you are asked to act. `display:contents` lifts the copy's
     children into the section grid so the photograph can be ordered among
     them without a second copy of the markup. */
  .biz__copy{ display:contents; }
  .biz__media{ order:3; }
  .biz__cta{ order:4; margin-top:var(--s4); justify-content:center; }
  /* Left-aligned over a full-width column with two centred tiles under it,
     the heading read as a column that had lost its other half — the same
     fault the hero had. Centred, the block has an axis. */
  /* `.biz__copy`, not `.biz__in >`: display:contents changes which boxes are
     generated, not which selectors match — the heading is still a child of
     the copy block as far as CSS is concerned. */
  .biz__copy h2, .biz__copy .deck{ text-align:center; }
  .biz__copy .deck{ margin-inline:auto; }
  .biz__quick li{ text-align:center; }
}
@media (max-width:560px){ .biz__cta .btn{ flex:1 1 auto; justify-content:center; } }

.biz__media{ position:relative; }
.biz__fig{ margin:0; }
.biz__frame{ position:relative; }
.biz__fig > .biz__frame > picture{ display:block; border-radius:var(--r-lg); overflow:hidden; box-shadow:var(--lift-3); }
.biz__fig img{ display:block; width:100%; height:auto; }
.biz__fig figcaption{
  margin-top:var(--s3); color:var(--mute); font-size:.84rem; line-height:1.5;
  max-width:46ch;
}
/* The inset sits under the frame on small screens, where overlapping it
   would cover the vans rather than accent them. */
.biz__inset{ margin:var(--s3) 0 0; }
.biz__inset picture{ display:block; border-radius:var(--r); overflow:hidden; }
.biz__inset img{ display:block; width:100%; height:auto; }
@media (min-width:760px){
  /* Anchored to the frame, so it juts out to the left over the photograph
     and never lands on the caption underneath it. */
  .biz__inset{
    position:absolute; left:calc(var(--s6) * -1); bottom:var(--s4);
    width:min(36%, 200px); margin:0;
  }
  .biz__inset picture{ box-shadow:var(--lift-4), 0 0 0 6px var(--mist); }
}

/* ── 35. BUSINESS WRAPS PAGE ────────────────────────────────────
   Three helpers the page needed. Everything else on it reuses the
   components the rest of the site already ships. */
.grid-2{ display:grid; gap:var(--s4); margin-top:var(--s5); }
@media (min-width:760px){ .grid-2{ grid-template-columns:repeat(2,1fr); } }
/* The second slab on the page reads better with the photograph on the
   right, so the pair does not march down the page in one direction. */
@media (min-width:900px){ .slab--flip figure{ order:2; } }
.nowrap{ white-space:nowrap; }

/* ── 36. BUSINESS WRAPS HERO + BENEFITS ─────────────────────────
   The page opens on the thing being sold: a row of plain work vans,
   before any livery. The headline sits on the photograph rather than
   above it, so the first screen carries an image instead of a wall
   of type. */

/* ── 40. BUSINESS WRAPS: THE ROOM ──────────────────────────
   Everything below this page's hero gets its own palette. It works by
   repointing the alias layer on the sections themselves, which is what that
   layer is for — no component below has to know. The hero is deliberately
   excluded: it is a photograph with a dark scrim and light type over it, and
   it already works. `main > :not(.bizpin)` is the whole trick, since every
   section after the hero is a direct child of main and the hero is the one
   wrapped in the pin host.

   Twenty grounds were tried here behind a chooser. Gunmetal won: dark, so it
   belongs to the rest of the site, but a full step off pure black, so eight
   sections of it do not read as a void — and blue enough not to be a dead
   neutral under photographs that are mostly white vans and grey sky. */
body[data-page="business-wraps"] main > :not(.bizpin){
  background:var(--paper); color:var(--ink);
  --paper:  oklch(0.248 0.016 245);
  --mist:   oklch(0.296 0.018 245);
  --cloud:  oklch(0.278 0.017 245);
  --edge:   oklch(0.380 0.020 245);
  --edge-2: oklch(0.476 0.024 245);
  --bright: oklch(0.985 0.004 245);
  --ink:    oklch(0.935 0.008 245);
  --slate:  oklch(0.792 0.014 245);
  --mute:   oklch(0.672 0.014 245);
  --blue:   oklch(0.806 0.116 242);
  --blue-d: oklch(0.862 0.100 242);
  --blue-t: oklch(0.806 0.116 242 / .14);
  --blue-r: oklch(0.806 0.116 242 / .30);
  --blue-2: oklch(0.806 0.116 242 / .30);
  --sun:    oklch(0.806 0.100 70);
  --sun-d:  oklch(0.870 0.092 70);
  --sun-t:  oklch(0.806 0.100 70 / .16);
  --go:     oklch(0.800 0.150 155);
  --go-t:   oklch(0.800 0.150 155 / .14);
  --lift-1: 0 1px 3px oklch(0 0 0 / .5);
  --lift-2: 0 12px 32px -14px oklch(0 0 0 / .72);
  --lift-3: 0 28px 64px -30px oklch(0 0 0 / .82);
}

/* ── the two contrast bands ─────────────────────────────────────
   Eight sections of one colour is a corridor. Three of them break out of
   the gunmetal instead, spaced so the page changes ground roughly every
   other section rather than settling:

     Why it pays   light   the argument for the money, set on paper
     What it costs black   the deepest panel on the page, under the prices
     Send us the van light the closing ask

   Both bands are the same alias repoint as the room itself, so nothing
   inside either one needed a rule of its own. The recommended tier rides
   with the light band: it is a white card standing out of the black price
   section, which is the one place on the page where the contrast is doing
   work rather than rhythm. */
body[data-page="business-wraps"] main > .bizsec--light,
body[data-page="business-wraps"] main .tier--hero{
  --paper:  oklch(0.968 0.006 245);
  --cloud:  oklch(0.968 0.006 245);
  --mist:   oklch(0.930 0.008 245);
  --edge:   oklch(0.874 0.010 245);
  --edge-2: oklch(0.754 0.014 245);
  --bright: oklch(0.140 0.010 250);
  --ink:    oklch(0.222 0.012 250);
  --slate:  oklch(0.418 0.016 246);
  --mute:   oklch(0.522 0.014 246);
  --blue:   oklch(0.452 0.162 248);
  --blue-d: oklch(0.386 0.166 248);
  --blue-t: oklch(0.452 0.162 248 / .10);
  --blue-r: oklch(0.452 0.162 248 / .26);
  --blue-2: oklch(0.452 0.162 248 / .26);
  --sun:    oklch(0.522 0.112 60);
  --sun-d:  oklch(0.456 0.114 58);
  --sun-t:  oklch(0.522 0.112 60 / .14);
  --go:     oklch(0.492 0.144 152);
  --go-t:   oklch(0.492 0.144 152 / .12);
  --lift-1: 0 1px 3px oklch(0.16 0.02 245 / .13);
  --lift-2: 0 12px 32px -14px oklch(0.16 0.02 245 / .30);
  --lift-3: 0 28px 64px -30px oklch(0.16 0.02 245 / .38);
  background:var(--paper); color:var(--ink);
}
body[data-page="business-wraps"] main .tier--hero{ border-color:var(--paper); }

/* Black, once. Deeper than the room so it reads as a panel dropped into it
   rather than as a second background colour. */
body[data-page="business-wraps"] main > .bizsec--dark{
  --paper:  oklch(0.128 0.010 250);
  --mist:   oklch(0.190 0.012 250);
  --cloud:  oklch(0.170 0.011 250);
  --edge:   oklch(0.300 0.014 250);
  --edge-2: oklch(0.410 0.016 250);
  --bright: oklch(0.985 0.003 250);
  --ink:    oklch(0.930 0.006 250);
  --slate:  oklch(0.775 0.012 248);
  --mute:   oklch(0.650 0.012 248);
  --blue:   oklch(0.790 0.120 240);
  --blue-d: oklch(0.850 0.104 240);
  --blue-t: oklch(0.790 0.120 240 / .14);
  --blue-r: oklch(0.790 0.120 240 / .30);
  --blue-2: oklch(0.790 0.120 240 / .30);
  --sun:    oklch(0.800 0.100 70);
  --sun-t:  oklch(0.800 0.100 70 / .16);
  background:var(--paper); color:var(--ink);
}

/* Buttons are token-driven, but --signal is a light fill with dark text,
   which is right on the two dark grounds and invisible on the light band.
   So: light fill everywhere below the hero, flipped back to a dark fill
   inside anything on paper. */
body[data-page="business-wraps"] main > :not(.bizpin) .btn--primary{
  --bg:var(--bright); --fg:oklch(0.150 0.012 248); --bd:var(--bright);
  box-shadow:0 8px 22px -12px oklch(0 0 0 / .6);
}
body[data-page="business-wraps"] main > :not(.bizpin) .btn--primary:hover{
  --bg:#fff; --bd:#fff;
}
body[data-page="business-wraps"] main > :not(.bizpin) .btn--quiet{
  --fg:var(--ink); --bd:var(--edge-2);
}
body[data-page="business-wraps"] main > :not(.bizpin) .btn--quiet:hover{
  --bg:oklch(1 0 0 / .08); --bd:var(--slate); --fg:var(--bright);
}
/* Same specificity as the two rules above, so these have to sit after them
   rather than merely be more specific. */
body[data-page="business-wraps"] main > .bizsec--light .btn--primary,
body[data-page="business-wraps"] main .tier--hero .btn--primary{
  --bg:oklch(0.168 0.016 252); --fg:oklch(0.985 0.004 250); --bd:oklch(0.168 0.016 252);
  box-shadow:0 8px 22px -12px oklch(0.25 0.04 250 / .55);
}
body[data-page="business-wraps"] main > .bizsec--light .btn--primary:hover,
body[data-page="business-wraps"] main .tier--hero .btn--primary:hover{
  --bg:oklch(0.108 0.014 252); --bd:oklch(0.108 0.014 252);
}
body[data-page="business-wraps"] main > .bizsec--light .btn--quiet:hover{
  --bg:oklch(0.25 0.03 250 / .06); --bd:var(--slate); --fg:var(--bright);
}
body[data-page="business-wraps"] main > .faq .faq__ic{ color:var(--slate); }

/* Headings: the same Archivo the footer uses, at the footer's settings.
   The display sizes on this page had drifted up to 51px with -1.13px of
   tracking, which is the squeezed look you get on every generated landing
   page. The footer sits at 43px and -0.95px and reads like a person wrote
   it, so this page is set to match. */
body[data-page="business-wraps"] main h2{
  font-weight:700; font-stretch:118%; letter-spacing:-.022em; line-height:1.02;
}
body[data-page="business-wraps"] main .sec-head h2{
  font-size:clamp(1.85rem,1.45rem + 1.5vw,2.7rem);
}
body[data-page="business-wraps"] main h3{
  font-weight:700; font-stretch:112%; letter-spacing:-.012em;
}


/* ── the price tiers ────────────────────────────────────────────
   Replaces a scrolling comparison table plus a definition list. Two
   numbers is the whole decision, so they are the whole section. */
.tiers{ display:grid; gap:var(--s4); margin-top:var(--s5); grid-template-columns:1fr; }
@media (min-width:760px){ .tiers{ grid-template-columns:repeat(2,1fr); } }
.tier{
  position:relative; display:flex; flex-direction:column;
  padding:var(--s6) var(--s5) var(--s5);
  border:1px solid var(--edge); border-radius:var(--r-lg);
  background:var(--cloud); box-shadow:var(--lift-1);
}
.tier--hero{ border-color:var(--blue); box-shadow:var(--lift-2); }
.tier__flag{
  position:absolute; top:-.7rem; left:var(--s5);
  padding:.3rem .65rem; border-radius:999px;
  background:var(--blue); color:#fff;
  font-size:.68rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
}
.tier__n{ font-size:.76rem; font-weight:800; letter-spacing:.1em; text-transform:uppercase; color:var(--slate); }
.tier__p{
  font-family:var(--disp); font-weight:800; line-height:1; letter-spacing:-.042em;
  font-size:clamp(2.7rem,2rem + 2.8vw,3.7rem); color:var(--bright); margin-top:.2rem;
}
.tier__d{ margin-top:.6rem; color:var(--slate); font-size:.95rem; max-width:36ch; }
/* The old price and the saving ride with the figure rather than above it:
   one line reads as a discount, two stacked lines read as two prices. The
   struck figure is muted and small enough that the number people remember is
   still the one they pay. */
.tier__p{ display:flex; align-items:baseline; flex-wrap:wrap; gap:.5rem; }
.tier__was{
  order:-1; font-size:.4em; font-weight:700; letter-spacing:-.01em;
  color:var(--mute); text-decoration-thickness:.09em;
}
.tier__save{
  font-family:var(--body); font-size:.24em; font-weight:800; line-height:1;
  letter-spacing:.06em; text-transform:uppercase; white-space:nowrap;
  padding:.42em .6em; border-radius:999px;
  color:var(--go); background:oklch(from var(--go) l c h / .14);
}
/* These sit inside .prose, whose `ul li::before` paints a blue dot and
   outranks a single class. Matching its specificity is the whole fix —
   the third component on this page to need it. */
.tier__l{ display:grid; gap:.55rem; margin:var(--s5) 0; padding:0; list-style:none; }
.prose .tier__l li{ position:relative; padding-left:1.6rem; font-size:.93rem; color:var(--ink); }
.prose .tier__l li::before{
  content:""; position:absolute; left:.15rem; top:.3em;
  width:10px; height:5px; border:2.2px solid var(--go);
  border-top:0; border-right:0; border-radius:0; background:none;
  transform:rotate(-45deg);
}
/* the extras list is a priced table, not a bulleted one */
.prose .tier__extra li{ padding-left:0; }
.prose .tier__extra li::before{ content:none; }
/* .prose p sets its own colour, which turned the flag text grey. Using
   --paper rather than #fff keeps it legible in every palette: the pill
   is the accent, so its text wants to be whatever the page ground is —
   dark ink on a light accent, light on a dark one. */
.prose .tier__flag{ color:var(--paper); }
.prose .tier__n{ color:var(--slate); }
.prose .tier__p{ color:var(--bright); }
.tier .btn{ margin-top:auto; }

/* ── what comes with it ─────────────────────────────────────────
   Was a priced table, then a row of bordered cards — both louder than three
   things that cost nothing. No boxes now: a hairline over each column, the
   word in green under a small tick, and the detail in body grey. It reads as
   a footnote to the prices, which is what it is. */
.addons{
  display:grid; gap:var(--s5) var(--s6); margin:var(--s6) 0 0; padding:0;
  list-style:none; grid-template-columns:1fr;
}
@media (min-width:760px){ .addons{ grid-template-columns:repeat(3,1fr); } }
.prose .addon{ padding:var(--s4) 0 0; border-top:1px solid var(--edge); }
.prose .addon::before{ content:none; }
/* `.prose p` beats a lone class here, as it does everywhere else on this
   page, so the green has to be stated with .prose in front of it. */
.prose .addon__v{ color:var(--go); }
.addon__v{
  display:flex; align-items:center; gap:.42rem;
  font-size:.72rem; font-weight:800; letter-spacing:.11em; text-transform:uppercase;
  color:var(--go);
}
.addon__v::before{
  content:""; flex:none; width:13px; height:9px;
  border:2.1px solid currentColor; border-top:0; border-right:0;
  transform:translateY(-2px) rotate(-45deg);
}
.addon b{ display:block; margin-top:.5rem; font-size:1.02rem; letter-spacing:-.012em; color:var(--bright); }
.prose .addon span{ display:block; margin-top:.28rem; font-size:.9rem; line-height:1.5; color:var(--slate); }

/* ── the promo banner ───────────────────────────────────────────
   Sits on the consumer wrap page and points at the business one. One
   link, so the whole strip is the target rather than a button inside it. */
.promo{
  display:grid; gap:var(--s3) var(--s5); align-items:center;
  grid-template-columns:1fr; margin:var(--s7) auto 0; max-width:var(--maxw);
  padding:var(--s5); border-radius:var(--r-lg);
  border:1px solid var(--edge-2);
  /* A white card on the dark page, not another shade of the page. The strip
     is the one ad on the home page, so it stops looking like the section it
     sits in. Tokens are repointed rather than colours overridden, the same
     move the light bands elsewhere make, so the pill, the link and the small
     print all follow without a rule each. */
  --bright: oklch(0.150 0.010 250);
  --slate:  oklch(0.418 0.016 246);
  --blue:   oklch(0.452 0.162 248);
  --paper:  oklch(0.995 0 0);
  color:var(--bright);
  background:linear-gradient(100deg, oklch(0.995 0 0), oklch(0.955 0.006 245) 78%);
  border-color:oklch(0.855 0.012 245);
  box-shadow:var(--lift-1);
  transition:transform var(--t-2) var(--e), box-shadow var(--t-2) var(--e), border-color var(--t-1);
}
@media (min-width:820px){ .promo{ grid-template-columns:auto 1fr auto; padding:var(--s5) var(--s6); } }
.promo:hover{ transform:translateY(-2px); box-shadow:var(--lift-2); border-color:var(--blue); }
/* Solid, not the 10% blue tint it had: the banner's own ground is a pale
   gradient, so a translucent pill sat on it as a smudge rather than a label.
   Filled blue with the page ground as its text, same as the pricing flag. */
.promo__l{
  justify-self:start; padding:.34rem .7rem; border-radius:999px;
  background:var(--blue); color:var(--paper);
  font-size:.68rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  box-shadow:0 1px 6px oklch(from var(--blue) l c h / .35);
}
.promo__t{ display:grid; gap:.15rem; }
.promo__t b{ font-family:var(--disp); font-weight:800; letter-spacing:-.025em; font-size:clamp(1.15rem,1rem + .8vw,1.5rem); }
.promo__t small{ font-size:.9rem; color:var(--slate); }
/* The banner is one line on a phone: a price and where it goes. The
   sub-line is the same promise the business page opens with, and here it
   turned a two-line strip into a four-line block. Hidden at the width the
   rest of the site treats as small, so "mobile" means one thing. */
@media (max-width:799px){ .promo__t small{ display:none; } }
.promo__a{ display:inline-flex; align-items:center; gap:.45rem; font-weight:700; font-size:.95rem; color:var(--blue); white-space:nowrap; }
.promo__a svg{ width:1.05em; height:1.05em; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; transition:transform var(--t-2) var(--e); }
.promo:hover .promo__a svg{ transform:translateX(3px); }

/* ── 39. THE WRAPS HERO AND PICKER ────────────────────────
   Its own hero rather than a restyle of .pagehead, which sixteen other
   pages share. Same idea as .bizhero — photograph behind, copy on the
   dark side of a scrim, price carrying the weight — without dragging
   that page's markup along with it. */
.wraphero{
  position:relative; isolation:isolate; overflow:hidden;
  padding:calc(var(--nav-h) + var(--s7)) 0 0;
  border-bottom:1px solid var(--edge);
}
.wraphero__bg{ position:absolute; inset:0; z-index:-2; display:block; }
.wraphero__bg img{ width:100%; height:100%; object-fit:cover; object-position:64% 56%; }
.wraphero::before{
  content:""; position:absolute; inset:0; z-index:-1;
  background:
    linear-gradient(100deg, var(--paper) 4%, oklch(0.152 0.009 258 / .86) 33%, oklch(0.152 0.009 258 / .38) 60%, oklch(0.152 0.009 258 / .06) 100%),
    linear-gradient(180deg, oklch(0.152 0.009 258 / .62) 0%, transparent 30%, var(--paper) 98%);
}
.wraphero__in{ display:grid; gap:var(--s3); padding-bottom:var(--s7); justify-items:start; }
.wraphero h1{
  max-width:16ch; font-family:var(--disp); font-weight:800;
  letter-spacing:-.038em; line-height:.98;
  font-size:clamp(2.3rem,1.3rem + 4.4vw,4.2rem); text-wrap:balance;
}
.wraphero .deck{ font-size:clamp(1.02rem,1rem + .4vw,1.2rem); max-width:46ch; }
.wraphero__price{
  display:flex; flex-wrap:wrap; gap:var(--s5); margin-top:var(--s3);
  padding-top:var(--s4); list-style:none; border-top:1px solid oklch(1 0 0 / .14);
}
.wraphero__price li{ display:grid; gap:.1rem; }
.wraphero__price b{ font-size:.74rem; font-weight:800; letter-spacing:.1em; text-transform:uppercase; color:var(--slate); }
.wraphero__price i{
  font-style:normal; font-family:var(--disp); font-weight:800;
  font-size:clamp(1.6rem,1.2rem + 1.5vw,2.3rem); letter-spacing:-.035em;
  line-height:1; color:var(--bright);
}
.wraphero__price em{ font-style:normal; font-size:.86rem; color:var(--slate); }
.wraphero__row{ display:flex; flex-wrap:wrap; gap:.6rem; margin-top:var(--s3); }
@media (max-width:560px){ .wraphero__row .btn{ flex:1 1 auto; justify-content:center; } }
.wraphero__alt{ font-size:.92rem; color:var(--slate); }
.wraphero__alt a{ color:var(--blue); font-weight:700; }
.wraphero__facts{
  position:relative; margin:0 auto; padding:var(--s4) var(--pad) var(--s5);
  max-width:var(--maxw); list-style:none;
  display:grid; gap:var(--s4) var(--s5); grid-template-columns:repeat(2,1fr);
  border-top:1px solid oklch(1 0 0 / .12);
}
@media (min-width:820px){ .wraphero__facts{ grid-template-columns:repeat(4,1fr); } }
.wraphero__facts b{ display:block; font-size:1rem; letter-spacing:-.01em; }
.wraphero__facts span{ display:block; margin-top:.15rem; font-size:.9rem; color:var(--slate); }

/* The picker. Photograph tiles for the jobs, swatch chips for the finishes.

   Called .picker, not .pick: the intake form's option label is already .pick,
   and a section rule and a label rule sharing a class is not a collision you
   see until it bites. This one sat later in the file and won, so every label
   in the booking form was getting a section's 80px of vertical padding and no
   horizontal padding at all. The children keep their pick__ prefix — those do
   not clash with anything. */
.picker{ padding:var(--s8) 0; }
/* Six tiles. auto-fill gave four across and stranded two on their own row,
   so the columns are pinned to divisors of six instead. */
.pick__grid{ display:grid; gap:var(--s4); margin-top:var(--s5); grid-template-columns:1fr; }
@media (min-width:620px){ .pick__grid{ grid-template-columns:repeat(2,1fr); } }
@media (min-width:1000px){ .pick__grid{ grid-template-columns:repeat(3,1fr); } }
.pick__t{
  position:relative; display:block; overflow:hidden;
  border:1px solid var(--edge); border-radius:var(--r); background:var(--cloud);
  box-shadow:var(--lift-1); transition:transform var(--t-2) var(--e), box-shadow var(--t-2) var(--e);
}
.pick__t img{ width:100%; height:100%; object-fit:cover; aspect-ratio:4/3; transition:transform var(--t-3) var(--e); }
/* a tall source whose subject sits low in the frame */
.pick__t--low img{ object-position:50% 64%; }
.pick__t:hover{ transform:translateY(-3px); box-shadow:var(--lift-3); }
.pick__t:hover img{ transform:scale(1.05); }
.pick__c{
  position:absolute; left:0; right:0; bottom:0; display:grid; gap:.12rem;
  padding:2.4rem .95rem .9rem;
  background:linear-gradient(180deg, transparent, oklch(0.13 0.01 258 / .92) 62%);
  color:#fff;
}
.pick__c b{ font-size:1.02rem; letter-spacing:-.01em; }
.pick__c small{ font-size:.85rem; color:oklch(0.86 0.006 258); }
.pick__c i{ font-style:normal; margin-top:.2rem; font-size:.82rem; font-weight:800; color:var(--blue); }
.pick__sub{ margin-top:var(--s7); font-size:1.1rem; }
.pick__fins{
  display:grid; gap:var(--s3); margin-top:var(--s4);
  grid-template-columns:repeat(auto-fill,minmax(170px,1fr));
}
.pick__f{
  display:grid; gap:.1rem; padding:var(--s3) var(--s4) var(--s4);
  border:1px solid var(--edge); border-radius:var(--r); background:var(--paper);
  transition:border-color var(--t-1), transform var(--t-2) var(--e);
}
.pick__f:hover{ border-color:var(--edge-2); transform:translateY(-2px); }
.pick__sw{
  display:block; width:100%; height:42px; border-radius:var(--r-sm);
  margin-bottom:var(--s3); box-shadow:inset 0 0 0 1px oklch(0 0 0 /.10);
}
.pick__sw--all{
  background:conic-gradient(from 210deg,#1B5BFF,#7a2fd0,#0f8b84,#c8402f,#e0b23c,#1B5BFF);
}
.pick__f b{ font-size:.95rem; }
.pick__f small{ font-size:.82rem; color:var(--mute); }

/* ── the pinned hero ────────────────────────────────────────────
   The wipe is worth a beat of its own, so the hero holds still while it
   plays and the page carries on afterwards. .bizpin is the scroll host,
   the section is sticky inside it, and .bizpin__sp is the travel the pin
   consumes — sized by the script, and zero until the script says so, so
   nobody without JS scrolls through an empty screen. --pin-top is set in
   JS to min(0, viewport - hero) so a hero taller than the screen pins
   against its own bottom edge instead of hiding its buttons. */
.reveal.is-full .reveal__tag--r{ opacity:0; }
.reveal__tag--r{ transition:opacity 200ms ease; }

.bizpin{ position:relative; }
.bizpin__sp{ height:0; }
.bizpin.is-pinned > .bizhero{ position:sticky; top:var(--pin-top, 0px); }
.bizpin.is-pinned .bizpin__sp{ height:var(--pin-len, 70vh); }

.bizhero{
  position:relative; isolation:isolate; overflow:hidden;
  padding:calc(var(--nav-h) + var(--s7)) 0 0;
  border-bottom:1px solid var(--edge);
}
.bizhero__bg{ position:absolute; inset:0; z-index:-2; display:block; }
.bizhero__bg img{ width:100%; height:100%; object-fit:cover; object-position:50% 42%; }
.bizhero::before{
  content:""; position:absolute; inset:0; z-index:-1;
  background:
    linear-gradient(100deg, var(--paper) 4%, oklch(0.152 0.009 258 / .88) 38%, oklch(0.152 0.009 258 / .55) 62%, oklch(0.152 0.009 258 / .35) 100%),
    linear-gradient(180deg, oklch(0.152 0.009 258 / .72) 0%, transparent 34%, var(--paper) 97%);
}
/* .wrap centres itself, so capping the wrap at 60ch floated the copy into
   the middle of the photograph. Cap the lines instead and let the wrap keep
   its own width, so the text sits on the dark side of the scrim. */
.bizhero__in{ display:grid; gap:var(--s4); padding-bottom:var(--s7); justify-items:start; }
.bizhero h1{ max-width:15ch; }
.bizhero h1{
  font-family:var(--disp); font-weight:800; letter-spacing:-.038em; line-height:.98;
  font-size:clamp(2.3rem,1.3rem + 4.4vw,4.2rem); text-wrap:balance;
}
.bizhero h1{ max-width:18ch; }

/* The h1 carries the search term, so the line people actually respond to
   sits under it rather than inside it. Smaller, but still the second thing
   read — it is set above the deck and given the accent colour so the eye
   lands on it before the paragraph. */
.bizhero__sub{
  margin-top:.45rem; font-family:var(--disp); font-weight:700;
  font-size:clamp(1.15rem,1rem + .9vw,1.6rem); letter-spacing:-.02em;
  color:var(--sun); text-wrap:balance;
}
.bizhero .deck{ font-size:clamp(1.02rem,1rem + .4vw,1.2rem); max-width:48ch; }

/* Price leads, so it gets the weight of a headline rather than a footnote.
   Two tiers only: any more and the reader starts comparing instead of
   calling. */
.bizhero__price{
  display:flex; flex-wrap:wrap; gap:var(--s5); margin-top:var(--s4);
  padding:var(--s4) 0 0; list-style:none; border-top:1px solid oklch(1 0 0 / .14);
}
.bizhero__price li{ display:grid; gap:.1rem; }
.bizhero__price b{
  font-size:.74rem; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--slate);
}
.bizhero__price i{
  font-style:normal; font-family:var(--disp); font-weight:800;
  font-size:clamp(1.9rem,1.4rem + 1.9vw,2.7rem); letter-spacing:-.035em;
  line-height:1; color:var(--bright);
}
.bizhero__price em{ font-style:normal; font-size:.86rem; color:var(--slate); }

/* What the designer works from. Four inputs, set as a list rather than
   four more cards — the page already has a card grid further down. */
/* Four short inputs, so they read as one row of labels rather than a 2x2
   block of paragraphs. Rules between the columns instead of under every
   item — fewer lines on screen for the same structure. */
.brandin{ display:grid; gap:0; margin-top:var(--s5); list-style:none; }
@media (min-width:620px){ .brandin{ grid-template-columns:repeat(2,1fr); column-gap:var(--s6); } }
/* .prose ul li already carries a bullet and an indent, and outranks a bare
   .brandin li. This is a labelled list like .ben, not a bulleted one. */
.prose .brandin li{ padding:var(--s4) 0; border-top:1px solid var(--edge); }
.prose .brandin li::before{ content:none; }
/* AFTER the base rule, not before it. Both state `.prose .brandin li` at the
   same weight, so source order decides — with the media block first the wide
   layout lost its side padding and its dividers, and every caption ran the
   full width of its cell and sat straight on the rule. */
@media (min-width:1000px){
  .brandin{ grid-template-columns:repeat(4,1fr); column-gap:var(--s5); border-top:1px solid var(--edge); }
  .prose .brandin li{ border-top:0; padding:var(--s4) var(--s4) 0; }
  .prose .brandin li + li{ border-left:1px solid var(--edge); }
}
/* A tick each. The four are the things we ask you for, and a ticked list
   reads as four things already handled rather than four things wanted. Same
   disc-and-check as the hero promises, at three-quarter size. */
.brandin li{ text-align:center; }
/* One pseudo-element, not two. Centred, the disc has to sit in the flex row
   next to the words rather than at a fixed offset from the left edge — and a
   check drawn with borders cannot follow it there, so the check is baked into
   the disc as a background instead. */
.brandin b{
  display:flex; align-items:center; justify-content:center; gap:.55rem;
  line-height:1.3; font-size:1.14rem; letter-spacing:-.014em; color:var(--bright);
}
.brandin b::before{
  content:""; flex:none; width:21px; height:21px; border-radius:50%;
  background:var(--go) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23071a10' stroke-width='3.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / 12px no-repeat;
  box-shadow:0 2px 10px oklch(0 0 0 / .35);
}
/* One line each. The captions are labels, not sentences: kept short enough to
   sit on a single line in a quarter-width column, and nowrap so a narrow
   viewport shrinks the row rather than stacking four two-line blocks. */
.brandin span{
  display:block; margin:.35rem 0 0;
  font-size:.86rem; line-height:1.4; color:var(--slate); white-space:nowrap;
}

/* They arrive one after another once the block is on screen: the line lifts
   in, the disc springs after it, the check lands last. Four ticks going in,
   rather than a row of text that was simply already there. The whole thing
   hangs off the parent's own .is-in, so it uses the reveal the page already
   has instead of a second observer — and the reduced-motion rule in section
   25 flattens every one of these transitions for free. */
.js .brandin li{
  opacity:0; transform:translateY(10px);
  transition:opacity 520ms var(--e) var(--d,0ms), transform 520ms var(--e) var(--d,0ms);
}
.js .brandin b::before{ transform:scale(.25); transition:transform 460ms var(--spring) calc(var(--d,0ms) + 130ms); }
.js .sec-head.is-in .brandin li{ opacity:1; transform:none; }
.js .sec-head.is-in .brandin b::before{ transform:scale(1); }
.brandin li:nth-child(2){ --d:95ms; }
.brandin li:nth-child(3){ --d:190ms; }
.brandin li:nth-child(4){ --d:285ms; }
.bizhero__row{ display:flex; flex-wrap:wrap; gap:.6rem; margin-top:var(--s2); }
@media (max-width:560px){ .bizhero__row .btn{ flex:1 1 auto; justify-content:center; } }
/* Underlined words, not a pill. It sits under the sentence it finishes and
   reads as part of it; as a bordered button directly above two other buttons
   it read as a third thing to do. */
.bizhero__more{ margin-top:.85rem; }
.bizhero__more a{
  display:inline-flex; align-items:center; gap:.4rem;
  color:var(--blue); font-weight:700; font-size:.95rem;
  text-decoration:underline; text-underline-offset:4px;
  text-decoration-color:var(--blue-r);
}
.bizhero__more a:hover{ text-decoration-color:currentColor; }
.bizhero__more svg{
  width:1em; height:1em; fill:none; stroke:currentColor;
  stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round;
  transition:transform var(--t-2) var(--e);
}
.bizhero__more a:hover svg{ transform:translateY(2px); }

/* A strip of four along the bottom edge, sitting on the darkest part of
   the scrim so it reads without another panel behind it. */
.bizhero__facts{
  position:relative; margin:0 auto; padding:var(--s4) var(--pad) var(--s5);
  max-width:var(--maxw); list-style:none;
  display:grid; gap:var(--s4) var(--s5); grid-template-columns:repeat(2,1fr);
  border-top:1px solid oklch(1 0 0 / .12);
}
/* Four across only when four will actually fit. At 820px each column was
   about 168px wide, which is twenty characters — every second line wrapped
   and the strip grew a ragged extra row. Two-up holds until there is room
   for the real thing. */
@media (min-width:1080px){ .bizhero__facts{ grid-template-columns:repeat(4,1fr); } }
.bizhero__facts li{ display:flex; align-items:flex-start; gap:.6rem; min-width:0; }
/* The same tick the benefits list and the comparison table use, at the size
   this strip can carry. Green because on this site green means a positive
   that has been confirmed, and these four are the promises the hero has just
   finished making. */
.fact__t{
  flex:none; width:17px; height:17px; margin-top:.12rem;
  fill:none; stroke:var(--go); stroke-width:2.8;
  stroke-linecap:round; stroke-linejoin:round; overflow:visible;
}
.bizhero__facts b{ display:block; font-size:1rem; letter-spacing:-.01em; }
.bizhero__facts span{ display:block; margin-top:.15rem; font-size:.9rem; color:var(--slate); }

/* Benefits. Rules rather than boxes: six cards in a grid is the thing
   every other page in this category does. */
.bens{ display:grid; gap:0; margin-top:var(--s5); }
@media (min-width:720px){ .bens{ grid-template-columns:repeat(2,1fr); column-gap:var(--s7); } }
.ben{ padding:var(--s4) 0; border-top:1px solid var(--edge); }
.ben h3{ font-size:1rem; letter-spacing:-.01em; }
.ben p{ margin-top:.35rem; color:var(--slate); font-size:.9rem; max-width:44ch; }
.slab__note{ margin-top:var(--s4); font-size:.88rem; color:var(--mute); }

/* A hero the exact height of the viewport pushes the page itself out of the
   first frame. On a phone it was 815px against an 812px screen, so the whole
   opening screen was hero and nothing below it was even hinted at. */
@media (max-width:719px){
  .bizhero{ padding-top:calc(var(--nav-h) + var(--s5)); }
  .bizhero__in{ padding-bottom:var(--s5); gap:var(--s3); }
  .bizhero .deck{ font-size:1rem; max-width:34ch; }
  .bizhero__facts{ padding-block:var(--s4) var(--s4); gap:var(--s3) var(--s4); }
  .bizhero__facts b{ font-size:.94rem; }
  .bizhero__facts span{ font-size:.8rem; }
}
/* ── 38. BUSINESS PRICE LIST ────────────────────────────────────
   A business asks the price before it asks the process. These are the
   shop's own published commercial figures, not a "from" with the
   interesting part missing. */
.bizprice{
  margin-top:var(--s6); padding:var(--s5);
  border:1px solid var(--edge); border-radius:var(--r-lg); background:var(--mist);
}
.bizprice h3{ font-size:1.14rem; margin-bottom:var(--s4); }
.bizprice__list{ display:grid; gap:0; margin:0; }
.bizprice__list > div{
  display:flex; flex-wrap:wrap; gap:.4rem var(--s4); align-items:baseline;
  padding:.75rem 0; border-bottom:1px solid var(--edge);
}
.bizprice__list > div:last-of-type{ border-bottom:0; }
.bizprice dt{ font-weight:700; font-size:1rem; flex:1 1 16rem; }
.bizprice dt span{ display:block; font-weight:400; font-size:.9rem; color:var(--slate); margin-top:.1rem; }
.bizprice dd{
  margin:0; margin-left:auto; font-family:var(--disp); font-weight:800;
  font-size:1.3rem; letter-spacing:-.02em; white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
.bizprice .bizprice__note{ margin-top:var(--s4); font-size:.9rem; color:var(--slate); max-width:62ch; }


/* ── FOOTER OVERFLOW ────────────────────────────────────────────
   The email address is one unbreakable token. In the four-column footer
   it measured 200px inside a 150px column and pushed the whole grid
   16px past the viewport, so every page scrolled sideways at tablet
   widths. `anywhere` also lets the column shrink below the word, which
   `break-word` does not. A real domain will be longer than the
   placeholder, so this matters more after launch, not less. */
.foot__in > *{ min-width:0; }
.foot__col a,
.foot__addr{ overflow-wrap:anywhere; }


/* On a 320px screen the four hero facts wrap to two lines each and become
   181px — the single largest block in the hero, pushing it past the
   viewport. The bold labels carry the claim on their own at that width;
   the detail lines are the part that can go. 375px keeps them. */
@media (max-width:360px){
  .bizhero__facts span{ display:none; }
  .bizhero__facts{ gap:var(--s3) var(--s4); padding-block:var(--s3) var(--s4); }
}

/* The page runs to nearly nine thousand pixels with a call to action only at
   the very top and the very bottom. Intent peaks at the price list, so that
   is where the third one goes. */
.bizprice__act{
  display:flex; flex-wrap:wrap; align-items:center; gap:.7rem var(--s4);
  margin-top:var(--s5); padding-top:var(--s4); border-top:1px solid var(--edge);
}
.bizprice__act span{ font-size:.9rem; color:var(--slate); }
@media (max-width:520px){ .bizprice__act .btn{ flex:1 1 auto; justify-content:center; } }



/* The benefits section used to open with a heading, a deck and six equal
   rows. It opens on the argument itself now, set large and quiet, with four
   short proofs under it — one statement rather than a stack of blocks. */
/* .prose p sets the body colour and out-specifies a lone class, so the
   statement has to match it. */
.prose p.claim{
  font-family:var(--disp); font-weight:800; letter-spacing:-.03em; line-height:1.08;
  font-size:clamp(1.6rem,1.1rem + 2.1vw,2.6rem); max-width:24ch; text-wrap:balance;
  color:var(--ink);
}
.bens{ margin-top:var(--s6); }
.ben p{ max-width:38ch; }

/* ── why it pays ──────────────────────────────────────────────
   Was four small text pairs with no header, floating in an empty band —
   the argument the hero opens with ("the wrap that pays for itself"),
   set smaller than everything around it. The figures are arithmetic on
   our own price rather than borrowed outdoor-advertising statistics: a
   number we can stand behind reads better than a large one we cannot. */
.payoff{
  display:grid; gap:0; margin-top:var(--s5);
  border-top:1px solid var(--edge-2);
}
@media (min-width:760px){
  .payoff{ grid-template-columns:repeat(3,1fr); column-gap:var(--s6); }
}
.payoff__f{ padding:var(--s5) 0; border-bottom:1px solid var(--edge); }
/* stacked, the last rule would sit a hairline above .bens's own top rule */
.payoff__f:last-child{ border-bottom:0; }
@media (min-width:760px){
  .payoff__f{ border-bottom:0; padding:var(--s5) var(--s5) var(--s5) 0; }
  .payoff__f + .payoff__f{ border-left:1px solid var(--edge); padding-left:var(--s5); }
}
/* Sized for a phrase rather than a figure. These carried numbers once —
   "$999", "55¢" — which is what the 53px was for; a benefit named in three
   words at that size is four lines of type in a third of a row. */
.payoff__f b{
  display:block; font-family:var(--disp); font-weight:800; line-height:1.06;
  letter-spacing:-.03em; font-size:clamp(1.72rem,1.34rem + 1.4vw,2.3rem);
  color:var(--bright);
}
/* Side by side, a two-line heading next to two one-line ones pushed its own
   column's copy down and left the row ragged. Two lines are reserved for all
   three so the paragraphs and the labels under them line up. Stacked, they
   are not next to anything, so the reservation would just be a hole. */
@media (min-width:760px){ .payoff__f b{ min-height:2.12em; } }
.payoff__f span{
  display:block; margin-top:.7rem; color:var(--slate);
  font-size:.94rem; line-height:1.55; max-width:30ch;
}
.payoff__f i{
  display:block; margin-top:.5rem; font-style:normal;
  font-size:.7rem; font-weight:800; letter-spacing:.1em; text-transform:uppercase;
  color:var(--mute);
}
/* The four short proofs sit under the figures as the softer half of the
   argument, so they get a rule above them and a little more air. */
.bens--under{ margin-top:var(--s7); }

/* ── the six benefits ───────────────────────────────────────────
   Six short statements, two columns under a centred heading. They have been
   through three shapes: cards with a line of explanation each (the
   explanations turned a glanceable list into a paragraph), then one tall
   column beside a sticky heading (half the section empty, the rows blown up
   to 34px to fill the height, and a filled green disc on every one of them —
   a clip-art checklist).

   What is left is the phrase and a drawn check. The check is a stroke, not a
   badge: at this size a filled disc is a blob, and six blobs down a page read
   as a template. Called .bene, not .perk — .perk is already the courtesy-car
   section on three other pages, and its dark-gold panel painted straight
   through these. Second time a section class and a list-item class have
   collided in this stylesheet; check the name before adding a third. */
body[data-page="business-wraps"] main .benefits .sec-head h2{
  font-size:clamp(1.95rem,1.5rem + 1.9vw,2.9rem);
  letter-spacing:-.028em; text-wrap:balance;
}
.prose .benefits .sec-head .deck{ color:var(--slate); }

.benes{
  --row: 64ms;
  display:grid; gap:0 var(--s8); grid-template-columns:1fr;
  margin:var(--s6) 0 0; padding:0; list-style:none;
}
@media (min-width:760px){ .benes{ grid-template-columns:repeat(2,1fr); } }
/* .prose paints a bullet on every li it can reach and out-specifies a lone
   class, so the reset has to match its weight. */
.prose .benes li{ padding-left:0; }
.prose .benes li::before{ content:none; }

.bene{
  position:relative; display:flex; align-items:center; gap:.75rem;
  padding:clamp(.85rem,1.5vw,1.15rem) 0;
  border-top:1px solid var(--edge);
  font-family:var(--disp); font-weight:700; font-stretch:110%;
  font-size:clamp(1.1rem,.98rem + .55vw,1.45rem);
  letter-spacing:-.014em; line-height:1.28; color:var(--bright);
}
/* Two columns, so the last row of each column needs the rule underneath it
   that the next row would otherwise have supplied. */
.bene:last-child, .bene:nth-last-child(2):nth-child(odd){ border-bottom:1px solid var(--edge); }
@media (max-width:759px){ .bene:nth-last-child(2):nth-child(odd){ border-bottom:0; } }

.bene b{ font-weight:inherit; display:block; text-wrap:balance; }
.bene__t{
  flex:none; width:clamp(19px,1.7vw,22px); height:auto;
  fill:none; stroke:var(--go); stroke-width:2.6;
  stroke-linecap:round; stroke-linejoin:round;
  overflow:visible;
}

/* ── the same checklist, carrying its reason ────────────────────
   On Why it pays each line has a sentence under it, so the row is two lines
   deep and centring the tick on the pair floats it away from the words it
   belongs to. It aligns to the first line instead, nudged by the cap height,
   and the row gets more air because it is no longer one line tall. */
.benes--rich .bene{ align-items:flex-start; padding:clamp(1rem,1.8vw,1.35rem) 0; }
.benes--rich .bene__t{ margin-top:.18em; }
/* `.prose ul li` sets colour at a weight a lone `.bene` cannot beat, so the
   headline was rendering slate like body copy. Stated back at matching weight,
   and heavier than the rest of the list because these lines are the argument. */
.prose .benes--rich .bene b{ color:var(--bright); font-weight:800; }
.prose .benes--rich .bene span{
  display:block; margin-top:.3rem;
  font-family:var(--body); font-weight:400; font-stretch:normal;
  font-size:1rem; letter-spacing:0; line-height:1.5; color:var(--slate);
}

/* ── the choreography ───────────────────────────────────────────
   Two beats per row, one row behind the last: the rule draws out from the
   left, then the check draws itself in. A checklist being ticked off, which
   is the one thing this section is about — worth a real sequence where the
   rest of the page is content with a fade.

   Everything hangs off the list's own .is-in, so all six run as one sequence
   from a single observer rather than six that fire as they each cross the
   line, and it sits inside `no-preference` rather than being undone again
   inside `reduce` — an override at equal weight is a cascade fight you can
   lose.

   THE WORDS ARE NEVER HIDDEN. The first version wiped them in from a
   clip-path, and a hidden tab proved why that is wrong: transitions do not
   advance in a background or headless render, so the text sat clipped to
   nothing and the section rendered blank. The words move and nothing else —
   worst case they are three pixels low and perfectly readable. The rule and
   the check keep the full treatment because they are decoration; if they
   never arrive, nothing has been said that isn't still on the page. */
.js .benes.rv{ opacity:1; transform:none; }     /* the list holds still; its rows move */

@media (prefers-reduced-motion: no-preference){
  .js .benes .bene{
    --d: calc(var(--i, 0) * var(--row));
    border-top-color:transparent;
    background:linear-gradient(var(--edge), var(--edge)) 0 0 / 0 1px no-repeat;
    transition:background-size 620ms cubic-bezier(0.16,1,0.3,1) var(--d);
  }
  .js .benes .bene b{
    transform:translateY(.12em);
    transition:transform 620ms cubic-bezier(0.16,1,0.3,1) calc(var(--d) + 70ms);
  }
  .js .benes .bene__chk{
    stroke-dasharray:1; stroke-dashoffset:1;
    transition:stroke-dashoffset 380ms cubic-bezier(0.22,1,0.36,1) calc(var(--d) + 180ms);
  }

  .js .benes.is-in .bene{ background-size:100% 1px; }
  .js .benes.is-in .bene b{ transform:none; }
  .js .benes.is-in .bene__chk{ stroke-dashoffset:0; }
}

/* ── 34b. FREE BRAND VIDEO ─────────────────────────────────────
   The offer that comes with a wrap job, on business-wraps.html.

   Built from the page's own furniture — `prose bizsec`, a centred `sec-head`,
   the standard buttons — plus the one thing that furniture has no equivalent
   for: a row of vertical films. Delete it with the section. */
/* The marques are the proof, so they are picked out of the sentence rather
   than left to blend into it — this is the line people scan before they
   decide whether the offer is worth anything. */
#brandvideo .deck b{ color:var(--bright); font-weight:700; }
/* Bright, not the accent. The blue put a second colour above a headline that
   is already the loudest thing in the section, and the label's job is to be
   read first, not to compete. `.prose p` out-specifies `.kicker`, so it has to
   be said at this weight to land at all. */
#brandvideo .kicker{ color:var(--bright); }
#brandvideo .kicker::before{ background:var(--bright); opacity:.5; }
.filmstage__h{
  font-size:clamp(2rem, 1.1rem + 3.4vw, 3.6rem);
  letter-spacing:-.04em; line-height:1.03; text-wrap:balance;
}
@media (max-width:719px){ .filmstage__h br{ display:none; } }

/* Nine by sixteen, three across, because that is the shape they were cut in
   and the shape a trade business posts in. Muted and looping: sound on a page
   somebody is reading at work is an ambush, and these are showing a style of
   work rather than telling a story. */
.prose ul.filmstage__reels{
  display:grid; grid-template-columns:repeat(3, 1fr);
  gap:clamp(.6rem, .3rem + 1.2vw, 1.25rem);
  margin:var(--s7) auto 0; max-width:56rem; list-style:none;
}
.prose ul.filmstage__reels li{ padding-left:0; margin:0; }
.prose ul.filmstage__reels li::before{ content:none; }
.reel__v{
  display:block; width:100%; aspect-ratio:9 / 16; object-fit:cover;
  border-radius:var(--r); background:oklch(0 0 0 / .4);
  box-shadow:var(--lift-2), inset 0 0 0 1px oklch(1 0 0 / .09);
}

/* The turn: what they have shot, then what they will shoot for you. */
.filmstage__offer{ margin:var(--s7) auto 0; max-width:52rem; text-align:center; }
.filmstage__sub{
  margin:var(--s5) auto 0; max-width:46ch;
  font-size:clamp(.95rem, .92rem + .2vw, 1.05rem);
  line-height:1.62; color:var(--slate); text-wrap:pretty;
}

/* The header sits in a centred column with a third of the band empty either
   side of it, which is why the section reads as quiet. Two things fill it
   without putting objects in the way: a rule on both sides of the label —
   the stylesheet already notes that a single leading dash on a centred
   kicker reads as an indent marker — and a soft screen glow behind the type,
   the light a projector throws rather than a shape drawn on the page.
   `isolation` keeps the glow's negative z-index inside the header instead of
   dropping it behind the band. */
#brandvideo .sec-head{ position:relative; isolation:isolate; }
#brandvideo .sec-head::before{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  left:50%; top:46%; transform:translate(-50%,-50%);
  width:min(130%, 64rem); aspect-ratio:2.1 / 1;
  background:radial-gradient(closest-side,
    oklch(from var(--dw) l c h / .16), oklch(from var(--dw) l c h / 0) 74%);
}
/* And a frame around it. Corner ticks rather than a box: the section is about
   film, the header is the thing being looked at, and four marks in the margins
   claim the width the centred column leaves empty without putting anything in
   the reader's way. Eight gradients, four corners, no extra markup. */
#brandvideo .sec-head::after{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  left:50%; top:50%; transform:translate(-50%,-50%);
  width:min(108%, 46rem); height:calc(100% + 2.6rem);
  --c:oklch(from var(--dw) l c h / .42);
  --n:26px;
  background:
    linear-gradient(var(--c),var(--c)) left top    /var(--n) 1.5px no-repeat,
    linear-gradient(var(--c),var(--c)) left top    /1.5px var(--n) no-repeat,
    linear-gradient(var(--c),var(--c)) right top   /var(--n) 1.5px no-repeat,
    linear-gradient(var(--c),var(--c)) right top   /1.5px var(--n) no-repeat,
    linear-gradient(var(--c),var(--c)) left bottom /var(--n) 1.5px no-repeat,
    linear-gradient(var(--c),var(--c)) left bottom /1.5px var(--n) no-repeat,
    linear-gradient(var(--c),var(--c)) right bottom/var(--n) 1.5px no-repeat,
    linear-gradient(var(--c),var(--c)) right bottom/1.5px var(--n) no-repeat;
}

#brandvideo .kicker::after{
  content:""; height:2px; width:20px; flex:none;
  background:currentColor; opacity:.85; border-radius:1px;
}

/* A sentence, not a label, so it is set as one — and qualified with the id
   because `.prose p` beats a lone class and was pulling it back to the left
   margin. */
#brandvideo .filmstage__for{
  margin:var(--s6) auto 0; max-width:none; text-align:center;
  font-size:clamp(1rem, .94rem + .3vw, 1.14rem); font-weight:400; letter-spacing:normal;
  text-transform:none; color:var(--slate); line-height:1.5;
}

/* The marques, as a row under the work. `.prose ul` and `.prose ul li` carry
   two element selectors and beat a lone class, so every one of these has to be
   qualified — unqualified they come out as a stacked, blue-bulleted list,
   which is exactly what happened when an unrelated cleanup took these rules
   away with the block they were sitting next to. */
.prose ul.filmstage__marques{
  display:flex; flex-wrap:wrap; justify-content:center; align-items:baseline;
  gap:.5rem clamp(.9rem, .5rem + 1.6vw, 2rem);
  margin:var(--s4) auto 0; max-width:52rem; list-style:none;
}
.prose ul.filmstage__marques li{
  padding-left:0; margin:0;
  font-family:var(--disp); font-weight:700; letter-spacing:-.015em;
  font-size:clamp(1rem, .9rem + .5vw, 1.35rem);
  color:var(--ink); white-space:nowrap;
}
.prose ul.filmstage__marques li::before{ content:none; }

.filmstage__cta{
  display:flex; flex-wrap:wrap; gap:.7rem; justify-content:center;
  margin-top:var(--s6);
}
.filmstage__note{
  margin:var(--s4) auto 0; max-width:52ch;
  font-size:.9rem; color:var(--mute); text-wrap:pretty;
}

@media (max-width:719px){
  /* Three nine-by-sixteens across a phone would be a hundred pixels wide
     each. They become a swipe instead, which is how the format is watched
     anyway. */
  .prose ul.filmstage__reels{
    grid-template-columns:none; grid-auto-flow:column; grid-auto-columns:62%;
    overflow-x:auto; overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;
    margin-top:var(--s6); padding-bottom:.4rem;
    scrollbar-width:none;
  }
  .prose ul.filmstage__reels::-webkit-scrollbar{ display:none; }
  .prose ul.filmstage__reels li{ scroll-snap-align:center; }
  .filmstage__offer{ margin-top:var(--s6); }
  .filmstage__cta{ margin-top:var(--s5); }
  .filmstage__cta .btn{ width:100%; justify-content:center; }
}

/* ── joining two sections on the same ground ────────────────────
   The design section and How it works sit on the same gunmetal with 80px of
   padding each, so 160px of nothing opened between them and the page looked
   like it had lost something. Their padding is halved and a hairline across
   the measure takes the join. One pseudo-element, so a piece of decoration
   costs the markup nothing. */
.bizsec.bizsec--tight{ padding-bottom:var(--s6); }
.bizsec.bizsec--join{ padding-top:var(--s6); }
/* Same halved padding, no rule of its own: --snug follows a section that
   already closes on one, and two hairlines 30px apart read as a mistake. */
.bizsec.bizsec--snug{ padding-top:var(--s6); }
.bizsec--join::before{
  content:""; display:block; height:1px;
  max-width:calc(var(--maxw) - var(--pad) * 2); width:calc(100% - var(--pad) * 2);
  margin:0 auto var(--s7);
  background:var(--edge);
}
/* Three under three, so the lower row sits on the same columns as the row
   above it rather than reading as a separate two-column block. */
@media (min-width:760px){
  .bens--three{ grid-template-columns:repeat(3,1fr); column-gap:var(--s6); }
}

/* three-up cards, for the fleet section */
.grid-3{ display:grid; gap:var(--s4); margin-top:var(--s5); }
@media (min-width:720px){ .grid-3{ grid-template-columns:repeat(2,1fr); } }
@media (min-width:1000px){ .grid-3{ grid-template-columns:repeat(3,1fr); } }

/* the closing ask: three short reassurances under the buttons, so the last
   thing on the page answers "what am I actually agreeing to" rather than
   ending on a phone number. */
.endcta__re{
  display:grid; gap:var(--s3) var(--s6); margin-top:var(--s5); padding-top:var(--s5);
  border-top:1px solid var(--edge); width:100%; max-width:52rem;
  grid-template-columns:1fr; list-style:none; text-align:left;
}
@media (min-width:720px){ .endcta__re{ grid-template-columns:repeat(3,1fr); } }
.endcta__re li{ display:grid; gap:.2rem; }
.endcta__re b{ font-size:.95rem; letter-spacing:-.01em; color:var(--bright); }
.endcta__re span{ font-size:.86rem; color:var(--slate); }
.prose .endcta__re li{ padding-left:0; }
.prose .endcta__re li::before{ content:none; }

/* ── the trades strip ─────────────────────────────────────────
   Four reference photographs at four different aspect ratios, so they run in
   a row of equal frames rather than a ragged mosaic. Captions sit under the
   picture on a hairline: these are examples, not our portfolio, and a caption
   floating over the image in a scrim is how you make a stock photo look like
   a case study. */
.trades{ padding:var(--s8) 0; }
/* Two up rather than four. These are whole photographs of whole vehicles now,
   and a quarter-width tile made every one of them a thumbnail of a wheel. */
.trades__grid{
  display:grid; gap:var(--s4); margin-top:var(--s5);
  grid-template-columns:repeat(auto-fit,minmax(330px,1fr));
}
.trades__i{ display:grid; gap:.6rem; align-content:start; min-width:0; }
.trades__i picture{ display:block; overflow:hidden; border-radius:var(--r-sm); background:var(--cloud); }
.trades__i img{
  display:block; width:100%; height:auto; object-fit:cover;
  aspect-ratio:7/4;              /* the frame's own shape, so nothing is cut */
  transition:transform 700ms var(--e);
}
.trades__i:hover img{ transform:scale(1.03); }
.trades__i figcaption{ display:grid; gap:.1rem; padding-top:.55rem; border-top:1px solid var(--edge); }
.trades__i figcaption b{ font-size:.95rem; letter-spacing:-.01em; }
.trades__i figcaption span{ font-size:.82rem; color:var(--mute); }
@media (max-width:520px){ .trades__grid{ grid-template-columns:1fr 1fr; gap:var(--s3); } }


/* ── the business hero, two columns ───────────────────────────
   Copy left, the before/after on the right. The background photograph of the
   plain fleet stays, dimmed further, because the wipe is now the bright thing
   on the page and two competing photographs is one too many. */
.bizhero__copy{ display:grid; gap:var(--s4); justify-items:start; }
.bizhero__reveal{ width:100%; aspect-ratio:1344/768; }
.bizhero__reveal .reveal__img{ object-position:58% 50%; }
@media (min-width:900px){
  .bizhero__in{ grid-template-columns:1.02fr .98fr; gap:var(--s6); align-items:center; }
  .bizhero__bg img{ opacity:.5; }
}
@media (max-width:899px){
  .bizhero__bg img{ opacity:.42; }
  /* Stacked, the van ended up under the prices and the buttons, which is
     below the fold on a phone — so the page led with a price for a thing you
     had not seen yet. `display:contents` lifts the copy's own children into
     the hero grid so the picture can be ordered between the deck and the
     price rule, without a second copy of the markup for small screens. */
  .bizhero__copy{ display:contents; }
  /* The deck repeats what the five steps below say at length, and on a phone
     it pushed the van and the price a screen down. Hidden rather than
     shortened: the sub-head above it already carries the promise. */
  .bizhero .deck{ display:none; }
  .bizhero__reveal{ order:2; margin-top:var(--s2); }
  .bizhero__price{ order:3; margin-top:var(--s2); }
  .bizhero__row{ order:4; }
}

/* ── the hero wrap-style picker ───────────────────────────────
   Bottom right of the wipe, above the grip's travel. Numbered, because on a
   small screen the numbers are what fits and the words are the hint. */
.wrapsw{
  position:absolute; right:.6rem; bottom:.6rem; z-index:5;
  display:flex; gap:4px; padding:4px; border-radius:999px;
  background:oklch(0.13 0.008 258 / .82);
  box-shadow:inset 0 0 0 1px oklch(1 0 0 / .16), 0 10px 26px -14px oklch(0 0 0 / .9);
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
}
.wrapsw button{
  display:inline-flex; align-items:center; gap:.4rem;
  min-height:30px; padding:0 .6rem; border:0; border-radius:999px; cursor:pointer;
  background:oklch(1 0 0 / .07); color:oklch(0.9 0.004 258);
  font-family:var(--text); font-size:.76rem; font-weight:700;
  transition:background 160ms ease, color 160ms ease;
}
.wrapsw button b{ font-size:.72rem; opacity:.7; }
.wrapsw button:hover{ background:oklch(1 0 0 / .15); }
.wrapsw button[aria-checked="true"]{ background:var(--blue); color:oklch(0.16 0.01 252); }
.wrapsw button[aria-checked="true"] b{ opacity:.85; }
@media (max-width:640px){
  .wrapsw{ right:.4rem; bottom:.4rem; }
  .wrapsw button span{ display:none; }
  .wrapsw button{ padding:0 .55rem; }
  .wrapsw button b{ font-size:.8rem; opacity:1; }
}
/* The picker took the bottom-right corner, which is where the "after" tag
   sat. Send that tag to the top of the frame on this hero only. */
.bizhero__reveal .reveal__tag--r{ bottom:auto; top:.6rem; }

/* ── skins for the booking section ──────────────────────────────
   The site is Diamond blue and stays that way. This is a set of grounds for
   the one section that is a form rather than a page — the thing that has to
   feel answerable, and which the rest of the site's near-black does not
   obviously help. The choice is this browser's alone: it writes to
   localStorage and the head partial restores it before the first paint.

   Everything is a token repoint scoped to #book, so the fields, the picks,
   the rules and the summary follow without a rule each. Only the two panels
   that paint their own gradient — the form itself and its rail — need saying
   out loud. */
#book{
  --edge:   oklch(0.884 0.006 250);
  --edge-2: oklch(0.782 0.010 250);
  --bright: oklch(0.160 0.012 252);
  --ink:    oklch(0.245 0.012 252);
  --slate:  oklch(0.432 0.012 252);
  --mute:   oklch(0.536 0.012 252);
  --muted:  oklch(0.432 0.012 252);
  --faint:  oklch(0.536 0.012 252);
  --signal: oklch(0.160 0.012 252);
  --blue:   oklch(0.520 0.150 248);
  --blue-d: oklch(0.448 0.156 248);
  --blue-t: oklch(0.520 0.150 248 / .10);
  --blue-r: oklch(0.520 0.150 248 / .26);
  --blue-2: oklch(0.520 0.150 248 / .26);
  color:var(--ink);
}
/* Whatever sits above the booking section keeps a square foot: two curves
   meeting — one rounding down, one rounding up — leave a pinched waist rather
   than the smooth hand-off of one shape sliding under another. */
@media (max-width:799px){
  main > section:has(+ #book){
    border-end-start-radius:0; border-end-end-radius:0;
  }
}

/* The light ground is a slab set into a dark page, so it opens with a curve
   the way every other band on a phone now does. */
#book{
  border-start-start-radius:clamp(22px, 7vw, 40px);
  border-start-end-radius:clamp(22px, 7vw, 40px);
}
/* Soft grey, warmed. Pure white panels on a cool grey read as a lit screen
   inside a dark page — the glare was the complaint. The ground stays grey and
   the panels come off white toward bone, which drops the contrast between the
   two and takes the sheet-of-paper look off the form. */
#book{
  background:oklch(0.930 0.006 250);
  --paper:oklch(0.972 0.006 88); --cloud:oklch(0.955 0.007 88); --mist:oklch(0.938 0.007 88);
  --edge:oklch(0.868 0.008 250); --edge-2:oklch(0.768 0.012 250);
}
/* The panel and its rail are the two surfaces with their own gradient, so a
   token cannot reach them. On a light ground they go flat white with a real
   border, which is what a form on paper looks like. */
#book .form--intake{
  background:oklch(0.972 0.006 88);
  border-color:var(--edge);
  box-shadow:0 24px 60px -34px oklch(0.16 0.012 252 / .22), inset 0 1px 0 oklch(1 0 0 / .5);
}
/* The rail takes the panel's own colour. A separate tint behind it made a
   header band out of what is really just the top of the card, and the bar
   then sat on a third tone: panel, band, track. One surface, one hairline. */
#book .intake__rail{ background:transparent; border-bottom:0; }

/* The service icons. On the dark ground they are a pale glyph in a tinted
   tile, which on a light one turns into a grey smudge in a grey box. Here the
   tile is the panel colour with a real hairline, the glyph takes the accent,
   and a chosen service fills the tile with the accent and reverses the glyph
   out of it — so the pick reads at a glance rather than by its tick alone. */
#book .pick__ic{
  background:var(--paper); color:var(--blue);
  box-shadow:inset 0 0 0 1px var(--edge);
}
#book .pick__ic svg{ stroke-width:1.9; }
#book .pick--svc.is-picked .pick__ic{
  background:var(--blue); color:oklch(1 0 0); box-shadow:none;
}
#book .pick:hover .pick__ic{ box-shadow:inset 0 0 0 1px var(--blue-r); }

/* The tick. Its rest state is a dark fill with a white-alpha ring, which on a
   light panel is a grey square that reads as a disabled box rather than as
   the control you tap. On these grounds it is a white box with a real border,
   and a chosen one fills with the accent. */
#book .pick__tick{
  background:var(--paper); box-shadow:inset 0 0 0 1.6px var(--edge-2);
}
#book .pick:hover .pick__tick{ box-shadow:inset 0 0 0 1.6px var(--blue); }
#book .pick.is-picked .pick__tick{
  background:var(--blue); box-shadow:inset 0 0 0 1.6px var(--blue);
}
#book .pick--check.is-picked .pick__tick::after{ border-color:oklch(1 0 0); }

/* Each service is a card, so the row you can tap looks like one. Chosen, it
   takes a tinted ground and an accent border — the tick alone was carrying
   the whole state at the far right of a wide row. */
#book .pick{ background:var(--paper); border-color:var(--edge); }
#book .pick.is-picked{
  border-color:var(--blue); background:var(--blue-t); box-shadow:none;
}

/* The option pills. Their phone styling is a white-alpha fill and a
   white-alpha border, which on a light panel is invisible — the unchosen
   options read as loose words rather than as things to tap. Given the same
   card treatment as everything else on these grounds. */
#book .seg span{
  background:var(--paper); border-color:var(--edge-2); color:var(--ink);
}
#book .seg label.is-picked span{
  background:var(--blue-t); border-color:var(--blue); color:var(--bright);
}
#book .seg label:hover span{ border-color:var(--blue); }
#book .btn--primary{
  --bg:oklch(0.160 0.012 252); --fg:oklch(1 0 0); --bd:oklch(0.160 0.012 252);
}
#book .btn--ghost,
#book .btn--quiet{ --fg:var(--bright); --bd:var(--edge-2); }

/* ── banded sections end in a curve on a phone ──────────────────
   Full-bleed colour bands are a desktop device: at 1400px a band reads as a
   room the page walks through. On a phone every band is the full width of the
   screen, so a stack of them is a stack of rectangles with ruled edges, and
   the page looks like a spreadsheet of sections.

   Rounding the foot of each band lets the next section sit under it instead
   of butting into it. Bottom only, deliberately: rounding both ends would put
   a notch between two bands that share a colour. The bottom hairline goes at
   the same time — a straight rule across a curve reads as a mistake — and no
   overflow:hidden, because a border-radius already clips a background and
   trapping overflow would break any sticky child inside.

   .biz is not here: on a phone it has no band at all any more. */
@media (max-width:799px){
  .studio,
  .perk,
  .protect--after,
  .band,
  .process,
  .livery,
  .endcta{
    border-bottom:0;
    border-end-start-radius:clamp(22px, 7vw, 40px);
    border-end-end-radius:clamp(22px, 7vw, 40px);
  }
  /* Centred, like the hero above it and the business block below: stacked,
     a heading with the whole width to itself and nothing to align against on
     the right reads as a column that lost its other half. */
  /* The ticker's edge mask fades the strip to transparent at both ends, and
     what shows through is the page — which on a phone is a black bar down each
     side of a band that is meant to run edge to edge. The strip is short
     enough here that a word being clipped mid-letter is not the problem the
     fade was solving. */
  .ticker{ mask-image:none; -webkit-mask-image:none; }
  .lineup .sec-head{ text-align:center; }
  .lineup .sec-head .deck{ margin-inline:auto; }

  /* The curve at this junction belongs to the hero, not to the grey. The dark
     panel ends in a rounded foot and the grey runs up behind it, so the grey
     appears to wrap around the hero's corners rather than the hero sitting in
     a rounded box. Two parts: the radius goes on the sticky panel, which is
     the element that actually paints the photograph, and the grey is pulled up
     by the same amount so there is something behind those corners to see. */
  /* Bigger than the section curves elsewhere: the hero's foot and the grey
     behind it are both dark, so a 26px corner reads as a rounding error
     rather than as a shape. At 44 it is unmistakably a curve. */
  .hero__sticky{
    border-end-start-radius:clamp(34px, 11vw, 56px);
    border-end-end-radius:clamp(34px, 11vw, 56px);
  }
  .lineup{
    border-top:0; position:relative; z-index:0;
    margin-top:calc(-1 * clamp(34px, 11vw, 56px));
    /* The 5rem this band uses on desktop was on top of the 41px the curve
       already borrows, so the heading sat a third of a screen down. The curve
       is doing the separating here; the padding only has to clear it. */
    padding-top:calc(var(--s4) + clamp(34px, 11vw, 56px));
    border-radius:0;
  }
  /* The warm band is the one section that is a colour rather than a shade, so
     it is curved at both ends: a slab set into the page rather than a stripe
     across it. The section above it already ends in a curve, which is what
     leaves room for this one. */
  .perk{
    border-top:0;
    border-start-start-radius:clamp(22px, 7vw, 40px);
    border-start-end-radius:clamp(22px, 7vw, 40px);
  }
  /* The pinned protection section runs straight into the comparison under it,
     so the pair is curved as one thing: this one at the head, the one below
     it at the foot, and nothing between them. */
  .protect--pin{
    border-top:0;
    border-start-start-radius:clamp(22px, 7vw, 40px);
    border-start-end-radius:clamp(22px, 7vw, 40px);
    border-end-start-radius:0; border-end-end-radius:0;
  }

/* ── PHONE: the booking screen ──────────────────────────────────
   The intake is the one thing on this page with a job to do, and on a phone
   it was arriving after two screens of preamble and then running 795px in an
   844px viewport — so the first question and the Next button could never be
   seen at once.
   What goes: the section heading, which repeats what the band above it
   already said; the appointment card, which repeats "Appointment only. No
   walk-ins." word for word from that same band; and the recent-work gallery,
   1,877px of placeholder photographs between the reader and the form.
   What tightens: the step's helper line — seven labelled cards do not need a
   sentence explaining that you tap one — and the padding around them. */
@media (max-width:719px){
  #book > .wrap > .sec-head{ display:none; }
  .apt{ display:none; }
  /* With the heading and the note gone, the section's 5rem of top padding is
     5rem of nothing above the first thing there is to read. `!important`
     because the padding is written inline on the section in every page, and
     inline is the one thing a stylesheet cannot out-specify. */
  #book{ padding-block:var(--s5) var(--s6) !important; }

  .intake__rail{ padding:var(--s4) var(--s4) var(--s3); }
  .intake__body{ padding:var(--s4); gap:var(--s3); }
  .form--intake .step__d{ display:none; }
  .form--intake .step__h{ margin-bottom:0; }
  .pick{ min-height:52px; padding:.7rem 2.6rem .7rem .85rem; }
  .pick--svc{ padding:.7rem 2.6rem .7rem .85rem; gap:.7rem; }
  .pick__s{ font-size:.8rem; }
  .intake__nav{ margin-top:0; }

  /* Short answers pair up. "Colour it is now" and "Plate" are four characters
     each and were taking a full row apiece — 177px for two fields that fit in
     89px side by side. The 620px breakpoint that pairs them was written for
     the long fields; these are short enough to pair at any width. */
  .grid2{ grid-template-columns:1fr 1fr; gap:var(--s3); }
  .grid2 > .field:only-child{ grid-column:1 / -1; }

  /* Labels sit closer to what they label, and the fieldset stack tightens.
     Inputs keep their 50px height: they are the touch target. */
  .form--intake .field{ gap:.25rem; }
  .form--intake .fset{ gap:var(--s3); }
  .form--intake .step{ gap:var(--s3); }
  .form__fine{ font-size:.76rem; }

  /* Chosen, and not deliberately reopened: the menu folds to the choice. The
     questions that choice raises are what matters now, and they were sitting
     under a seven-item list. `.is-open` still wins, so the button always
     brings the full list back. */
  .picks--svc.is-picked:not(.is-open) .pick--svc:not(:has(input:checked)){ display:none; }
}

/* ── 35. BUSINESS WRAPS ON A PHONE ─────────────────────────────
   One block, because these are one decision: the page reads as a stack of
   full-bleed bands on a desktop and that is right there, but on a phone the
   same bands are a mile of scrolling with a sentence of explanation under
   every heading. On a phone the labels carry it and the sentences go.

   The bands also stop being bands. Rounded and inset, each section reads as a
   card the thumb can see the end of, which is the difference between "this
   page is long" and "this page has six things in it". */
@media (max-width:719px){

  /* ── the bands become panels ─────────────────────────────── */
  body[data-page="business-wraps"] main > .bizsec,
  body[data-page="business-wraps"] main > #warranty,
  body[data-page="business-wraps"] main > .endcta{
    border-radius:var(--r-lg);
    overflow:hidden;
  }

  /* ── A. the design ───────────────────────────────────────── */
  /* Two up, captions gone. Four rows of "label + caption" was most of a
     screen to say we work from your logo. */
  #design .brandin{
    grid-template-columns:1fr 1fr; column-gap:var(--s4); margin-top:var(--s5);
  }
  #design .brandin span{ display:none; }
  .prose #design .brandin li{ padding:var(--s3) 0; }
  #design .brandin li:nth-child(2){ border-top:1px solid var(--edge); }
  #design .brandin b{ font-size:1rem; gap:.45rem; }
  #design .brandin b::before{ width:18px; height:18px; background-size:10px; }
  #design .deck{ margin-bottom:0; }
  /* the band was closing on eighty pixels of nothing under the last two ticks */
  body[data-page="business-wraps"] main > #design{ padding-bottom:var(--s6); }


  /* ── B. why it pays ──────────────────────────────────────── */
  #why .sec-head{ text-align:center; }
  /* The line the section is named for, said like it means it. */
  #why .kicker{
    color:var(--ink); font-weight:800; letter-spacing:.14em;
    font-size:.86rem;
  }
  #why .kicker::before{ background:var(--ink); opacity:.45; }
  /* Six claims with a sentence each is a page of reading for something the
     reader already agrees with. The claims stay; the arguments go. */
  #why .bene span{ display:none; }
  #why .bene:nth-child(6){ display:none; }
  #why .benes{ gap:0; }
  #why .bene{ padding:.7rem 0; }
  #why .bene b{ font-size:1.02rem; }

  /* ── C. how it works ─────────────────────────────────────── */
  /* The step titles are the process. The paragraphs under them repeat it in
     longer words, and on a phone they turn five steps into five screens. */
  #how .tl__t p{ display:none; }
  #how .tl__t h3{ margin:0; }

  /* ── D. what it costs ────────────────────────────────────── */
  #cost .sec-head{ text-align:center; }
  #cost .deck{ margin-inline:auto; }
  /* The tiers lose their paragraph and keep their figure. What each one is
     called, what it costs and what is in it — in ticks, not sentences. */
  .tier{ padding:var(--s5) var(--s4) var(--s4); }
  .tier__d{ display:none; }
  .tier__l{ margin:var(--s4) 0; gap:.45rem; }
  .prose .tier__l li{ font-size:.9rem; }
  .tier__p{ font-size:3rem; }
  /* The add-ons were three labelled paragraphs. On a phone they are three
     ticked lines. */
  .addons{ gap:0; margin-top:var(--s5); }
  .prose .addon{ padding:var(--s3) 0; }
  .addon__v{ display:none; }
  .addon b{ margin-top:0; font-size:.98rem; }
  .prose .addon span{ display:none; }
  .prose .addon b{
    display:flex; align-items:flex-start; gap:.55rem;
  }
  .prose .addon b::before{
    content:""; flex:none; width:13px; height:8px; margin-top:.42em;
    border:2.1px solid var(--go); border-top:0; border-right:0;
    transform:rotate(-45deg);
  }

  /* ── E. trades ───────────────────────────────────────────── */
  .trades .sec-head{ text-align:center; }
  .trades .deck{ margin-inline:auto; }

  /* ── G. the guarantee into the film ──────────────────────── */
  /* Two closed bands with eighty pixels of padding each left a hole between
     the guarantee and the offer under it. */
  #warranty{ padding-bottom:var(--s6); }
  #brandvideo{ padding-top:var(--s6); }

  /* ── H. the film ─────────────────────────────────────────── */
  #brandvideo .deck{ font-size:.95rem; }
  /* no spare margin on a phone, so the frame would be crowding rather than
     framing */
  #brandvideo .sec-head::after{ display:none; }
  #brandvideo .filmstage__for{ margin-top:var(--s5); font-size:.98rem; }
  .prose ul.filmstage__marques{ gap:.4rem .9rem; margin-top:var(--s3); }
  .prose ul.filmstage__marques li{ font-size:.95rem; }
}

/* Everything in those lists arrives rather than being there. One transition
   per family, delayed off the item's own index, hung on the reveal the page
   already runs — no second observer, and section 25's reduced-motion rule
   flattens all of it for free. */
@media (max-width:719px){
  .js #why .bene,
  .js #how .tl__item,
  .js .addons .addon{
    opacity:0; transform:translateY(12px);
    transition:opacity 520ms var(--e) var(--d,0ms), transform 520ms var(--e) var(--d,0ms);
  }
  .js #why .benes.is-in .bene,
  .js #how .tl.is-in .tl__item,
  .js .addons.is-in .addon{ opacity:1; transform:none; }
  .js #why .bene:nth-child(1){ --d:0ms; }
  .js #why .bene:nth-child(2){ --d:70ms; }
  .js #why .bene:nth-child(3){ --d:140ms; }
  .js #why .bene:nth-child(4){ --d:210ms; }
  .js #why .bene:nth-child(5){ --d:280ms; }
  .js .addons .addon:nth-child(2){ --d:90ms; }
  .js .addons .addon:nth-child(3){ --d:180ms; }
}
