/* ── 🔤 SELF-HOSTED TYPE ───────────────────────────────────────────────────
   🔴 11 Sep 2026. These four demos were the only ones still pulling Bricolage
   Grotesque, Figtree and IBM Plex Mono from fonts.googleapis.com, and it is why
   they were never in sync-demo.mjs: the external-resource guard refuses a demo
   that loads a CDN, so all four were being hand-copied into public/demo instead
   — and then they drifted, serving a phone number the owner had already changed.
   ⚠️ A demo is served in an IFRAME on another domain. A webfont that is slow,
   blocked or privacy-filtered renders the whole page in the fallback face with
   no error anywhere, which is the worst way for a sales tool to fail.
   ⭐ Latin subset only — 10 faces, ~388 KB. The Cyrillic, Greek and Vietnamese
   subsets Google serves are megabytes these pages will never draw a glyph from. */
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url("fonts/bricolage-grotesque-500.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url("fonts/bricolage-grotesque-600.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url("fonts/bricolage-grotesque-700.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 800;
  font-stretch: 100%;
  font-display: swap;
  src: url("fonts/bricolage-grotesque-800.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/figtree-400.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/figtree-500.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/figtree-600.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-400.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-500.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-700.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root{
  /* DARK FIRST. The shop floor at night, lit by a worklight — and the service
     symbols are dashboard telltales, in the colors a real dash uses. */
  --paper:#080D13; --surface:#0E151D; --surface-2:#131B25;
  --ink:#EDF1F4; --ink-2:#9AA6B1; --ink-3:#7B858E;
  --rule:#223040; --rule-soft:#18222D;
  /* 🔴 THE ACCENT WAS PURPLE (#9A8CFF) — 24 Sep 2026, owner pointing at the
     "Book a slot" button and the chat launcher on this demo. ⛔ No purple is a
     standing ruling on this estate, given 23 Sep when four site accents went to
     brand amber and four demos were found carrying a --tt-violet that was
     declared three times and never used. This one survived because it is the
     ACCENT itself rather than a stray token, so a search for unused violets
     missed it — and it paints the loudest control on the page.
     ⭐ WATER BLUE, because the trade chooses it: this is exterior cleaning, and
     a pressure washer's own colour is the water. It is also the only demo of
     the seven whose accent was not already a trade colour — auto repair is
     blue, garage doors yellow, landscaping green, plumbing copper, pool cyan.
     ⚠️ --accent-ink moves WITH it. That token is the dark type that sits ON
     the accent; leaving a purple-black ink on a blue button is the same
     band-inversion mistake that made five runs invisible on the home page. */
  --accent:#FFC72C; --accent-ink:#0E1720; --accent-soft:#241C05; --heat:#FFB020; --heat-soft:#2A1E06;
  --pass:#3FD07A; --pass-soft:#122419;
  --todo:#E0B860; --todo-soft:#2A2113; --todo-line:#6B5322;
  /* telltale palette — red critical, amber caution, green ok, blue info */
  --tt-red:#FF4438; --tt-amber:#FFB020; --tt-green:#3FD07A;
  --tt-blue:#54A8FF; --tt-cyan:#2BD4D9;
  --shadow:0 1px 2px rgba(0,0,0,.5), 0 14px 40px -18px rgba(0,0,0,.85);
  --f-display:"Bricolage Grotesque","Helvetica Neue",Arial,sans-serif;
  --f-body:"Figtree","Helvetica Neue",Arial,sans-serif;
  --f-mono:"IBM Plex Mono",ui-monospace,"SF Mono",Menlo,monospace;
  --step-0:1.0625rem; --step-1:1.25rem;
  --step-2:clamp(1.5rem,1.15rem + 1.4vw,2rem);
  --step-3:clamp(1.9rem,1.35rem + 2.4vw,2.85rem);
  --step-4:clamp(2.5rem,1.5rem + 4.2vw,4.5rem);
  --rail:12.5rem; --gutter:clamp(1.25rem,4vw,3rem); --measure:64ch;
}
@media (prefers-color-scheme:light){
  :root:not([data-theme="dark"]){
    --paper:#F7F9FB; --surface:#FFFFFF; --surface-2:#F1F4F7;
    --ink:#131A21; --ink-2:#4C5761; --ink-3:#5F6973;
    --rule:#C6CDD4; --rule-soft:#DCE1E6;
    --accent:#0A4D7A; --accent-ink:#FFFFFF; --accent-soft:#DCEAF4; --heat:#9A6B00; --heat-soft:#FBF0DA;
    --pass:#1F7A4D; --pass-soft:#DFF0E7;
    --todo:#8A5A00; --todo-soft:#FBF0DA; --todo-line:#E0B860;
    --tt-red:#C81E14; --tt-amber:#9A6B00; --tt-green:#1F7A4D;
    --tt-blue:#1C63C4; --tt-cyan:#0E7C8A;
    --shadow:0 1px 2px rgba(19,26,33,.06), 0 8px 24px -12px rgba(19,26,33,.18);
  }
}
:root[data-theme="light"]{
    --paper:#F7F9FB; --surface:#FFFFFF; --surface-2:#F1F4F7;
    --ink:#131A21; --ink-2:#4C5761; --ink-3:#5F6973;
    --rule:#C6CDD4; --rule-soft:#DCE1E6;
    --accent:#0A4D7A; --accent-ink:#FFFFFF; --accent-soft:#DCEAF4; --heat:#9A6B00; --heat-soft:#FBF0DA;
    --pass:#1F7A4D; --pass-soft:#DFF0E7;
    --todo:#8A5A00; --todo-soft:#FBF0DA; --todo-line:#E0B860;
    --tt-red:#C81E14; --tt-amber:#9A6B00; --tt-green:#1F7A4D;
    --tt-blue:#1C63C4; --tt-cyan:#0E7C8A;
    --shadow:0 1px 2px rgba(19,26,33,.06), 0 8px 24px -12px rgba(19,26,33,.18);
}
*{box-sizing:border-box}
body{margin:0; background:var(--paper); color:var(--ink); font-family:var(--f-body); font-size:var(--step-0); line-height:1.6; -webkit-font-smoothing:antialiased}
h1,h2,h3,h4{font-family:var(--f-display); margin:0; text-wrap:balance; letter-spacing:-.02em; line-height:1.06}
p{margin:0}
a{color:inherit}
:focus-visible{outline:2.5px solid var(--accent); outline-offset:3px; border-radius:2px}
.mono{font-family:var(--f-mono); font-variant-numeric:tabular-nums}
.lbl{font-family:var(--f-mono); font-size:.6875rem; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--ink-3)}
.shell{max-width:78rem; margin-inline:auto; padding-inline:var(--gutter)}


.top{border-bottom:1px solid var(--rule); background:var(--surface)}
.top__in{display:flex; align-items:center; gap:1.25rem; padding-block:.85rem; flex-wrap:wrap}
/* the logo is the home link — standard, and people reach for it before the nav */
.brand{display:flex; align-items:center; gap:.7rem; text-decoration:none; margin-right:auto; cursor:pointer; border-radius:.3rem}
.brand:hover .brand__logo{opacity:.85}
.brand__logo{transition:opacity .12s ease}
/* ⭐ His own logo, 240x127 and the largest that exists — shown at 132px so the
   source still has ~1.8x density on a retina screen. It is a real RGBA PNG, so
   it sits on the black ground without a plate behind it. On a light theme the
   silver wordmark would disappear, so there it gets a dark plate. */
.brand__logo{width:8.25rem; height:auto; flex:none; display:block}
.brand__txt{display:none}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .brand__logo{background:var(--ink); border-radius:.3rem; padding:.3rem .45rem}}
:root[data-theme="light"] .brand__logo{background:var(--ink); border-radius:.3rem; padding:.3rem .45rem}
@media (max-width:30rem){ .brand__logo{width:6.75rem} }
.brand__nm{font-family:var(--f-display); font-weight:800; font-size:1.0625rem; letter-spacing:-.03em; line-height:1.1}
.brand__sub{font-family:var(--f-mono); font-size:.625rem; letter-spacing:.13em; text-transform:uppercase; color:var(--ink-3)}

/* dropdown — the same telltales as the service grid, so the menu teaches the
   symbol set before you ever reach a service page */
.nav{position:relative}
.nav>ul{display:flex; gap:1.15rem; flex-wrap:wrap; list-style:none; margin:0; padding:0}
.nav li{position:relative}

/* ⭐ MOBILE MENU. Below 60rem the nav becomes a panel and the button becomes an X.
   The bars are three spans so the animation is a real transform, not a glyph swap —
   a font character cannot morph, and a morph is what makes it feel built. */
.burger{display:none; width:2.9rem; height:2.9rem; flex:none; align-items:center; justify-content:center;
  background:transparent; border:1px solid var(--rule); border-radius:.35rem; cursor:pointer; padding:0}
.burger:hover{border-color:var(--accent)}
.burger i{display:block; position:relative; width:1.15rem; height:2px; background:var(--ink);
  border-radius:2px; transition:transform .22s cubic-bezier(.3,1.2,.4,1), background-color .18s ease}
.burger i::before,.burger i::after{content:""; position:absolute; left:0; width:100%; height:2px;
  background:var(--ink); border-radius:2px; transition:transform .22s cubic-bezier(.3,1.2,.4,1)}
.burger i::before{top:-6px} .burger i::after{top:6px}
.burger[aria-expanded="true"] i{background:transparent}
.burger[aria-expanded="true"] i::before{transform:translateY(6px) rotate(45deg); background:var(--accent)}
.burger[aria-expanded="true"] i::after{transform:translateY(-6px) rotate(-45deg); background:var(--accent)}

@media (max-width:60rem){
  .burger{display:inline-flex}
  /* 🔴 ⭐ `nowrap` HERE IS WHAT BROKE THE OPENED MENU — 24 Sep 2026.
     `.nav` below asks for `flex-basis:100%` and `order:10`, which is the
     standard way to say "when you open, take the whole next row". That only
     works in a WRAPPING row. With `nowrap` the nav stayed on the brand's row
     and was squeezed into whatever the brand, burger and CTA left over:
     measured at 390px it rendered 248px wide and 1089px tall, a full-height
     column jammed beside the call-to-action, and the brand badge was crushed
     from 150px to 21px. ⛔ The two declarations contradicted each other in
     the same block and neither one was wrong on its own — that is why this
     survived. Read `.nav`'s `flex-basis:100%` as the statement of intent;
     this line is the one that has to agree with it. */
  .top__in{flex-wrap:wrap; gap:.6rem}
  .nav{order:10; flex-basis:100%; display:none}
  .nav.open{display:block; padding-block:.6rem .2rem; border-top:1px solid var(--rule); margin-top:.7rem}
  .nav>ul{flex-direction:column; gap:0}
  .nav>ul>li{border-bottom:1px solid var(--rule-soft)}
  .nav>ul>li>a{display:flex; padding:.75rem .1rem; font-size:1rem}
  .nav .sub{display:grid; position:static; opacity:1; visibility:visible; transform:none;
    grid-template-columns:1fr 1fr; border:0; box-shadow:none; background:transparent;
    margin:0 0 .5rem; gap:.15rem}
  .nav .sub a{background:transparent; padding:.45rem .1rem; font-size:.8125rem}
  .nav .sub a:hover{background:transparent}
  .top__act .btn--go{padding:.55rem .8rem}
}

/* a menu that opens should say so — chevron flips when the panel is open */
.nav a[aria-haspopup]{display:inline-flex; align-items:center; gap:.4rem}
.nav a[aria-haspopup]::after{content:""; width:.4rem; height:.4rem; flex:none;
  border-right:2px solid currentColor; border-bottom:2px solid currentColor;
  transform:translateY(-2px) rotate(45deg); opacity:.7;
  transition:transform .15s ease, opacity .15s ease}
.nav li:hover>a[aria-haspopup]::after,
.nav li:focus-within>a[aria-haspopup]::after{transform:translateY(1px) rotate(-135deg); opacity:1}
.nav .sub{position:absolute; top:calc(100% + .55rem); left:-.75rem; z-index:30;
  display:grid; grid-template-columns:repeat(2,minmax(11.5rem,1fr)); gap:1px;
  background:var(--rule); border:1px solid var(--rule); border-radius:.45rem;
  box-shadow:var(--shadow); list-style:none; margin:0; padding:0; overflow:hidden;
  opacity:0; visibility:hidden; transform:translateY(-4px); transition:opacity .12s ease, transform .12s ease, visibility .12s}
.nav li:hover>.sub, .nav li:focus-within>.sub{opacity:1; visibility:visible; transform:none}
.nav .sub a{display:flex; align-items:center; gap:.6rem; background:var(--surface);
  padding:.6rem .8rem; border-bottom:0; font-size:.875rem; white-space:nowrap; color:var(--ink)}
.nav .sub a:hover{background:var(--surface-2); border-bottom:0}
.nav .sub .sym{width:1.85rem; height:1.85rem; border-radius:.3rem}
.nav .sub .sym svg{width:1.05rem; height:1.05rem}
@media (max-width:60rem){
  .nav .sub{position:static; opacity:1; visibility:visible; transform:none; grid-template-columns:1fr;
    box-shadow:none; margin:.4rem 0 .2rem; display:none}
  .nav li:focus-within>.sub{display:grid}
}

.nav a{text-decoration:none; font-size:.9375rem; font-weight:500; color:var(--ink-2); padding-block:.25rem; border-bottom:2px solid transparent}
.nav a:hover{color:var(--ink); border-bottom-color:var(--accent)}
.nav a[aria-current="page"]{color:var(--ink); border-bottom-color:var(--accent)}
.top__act{display:flex; align-items:center; gap:.85rem}
/* 🔴 MEASURED, NOT GUESSED (7 Sep 2026): at 375px — an iPhone — `.top__act`
   ran from x=194 to x=398 on EVERY page, so the site's primary call to action
   sat 23px off the right edge and the whole document scrolled sideways.
   ⭐ Found by walking every element's getBoundingClientRect() inside an iframe
   at 375, not by looking: the header still LOOKS fine because the overflow is
   off-screen, which is exactly why it survived.
   ⛔ The phone is what goes, not the button. A shop's number is one tap away in
   the menu; "Book a slot" is the whole point of the header. */
@media (max-width: 460px){
  .top__act .tel{display:none}
  .top__act{gap:.5rem}
}
.tel{text-decoration:none; font-family:var(--f-mono); font-weight:700; font-size:.9375rem; white-space:nowrap}
.tel>span:first-child{display:block; font-size:.625rem; letter-spacing:.13em; color:var(--ink-3); font-weight:400}
.btn{display:inline-flex; align-items:center; justify-content:center; gap:.5rem; min-height:2.75rem; padding:.6rem 1.15rem; border-radius:.25rem; font-family:var(--f-display); font-weight:700; font-size:.9375rem; text-decoration:none; border:1.5px solid transparent; cursor:pointer; transition:transform .12s ease, background-color .12s ease}
.btn--go{background:var(--accent); color:var(--accent-ink)}
.btn--go:hover{transform:translateY(-1px)}
.btn--line{border-color:var(--rule); color:var(--ink); background:transparent}
.btn--line:hover{border-color:var(--ink); background:var(--surface-2)}

.bay{display:grid; grid-template-columns:var(--rail) minmax(0,1fr); gap:clamp(1.5rem,4vw,3.5rem)}
@media (max-width:60rem){ .bay{grid-template-columns:minmax(0,1fr)} .rail{display:none} }
.rail{border-right:1px solid var(--rule); padding-right:1.25rem; padding-top:.4rem}
.rail dl{margin:0; display:grid; gap:1.35rem}
.rail dt{font-family:var(--f-mono); font-size:.625rem; font-weight:700; letter-spacing:.13em; text-transform:uppercase; color:var(--ink-3)}
.rail dd{margin:.25rem 0 0; font-family:var(--f-mono); font-size:.9375rem; font-weight:500; line-height:1.35}
.rail dd em{font-style:normal; color:var(--ink-3); font-size:.75rem; display:block; margin-top:.15rem}


/* ⭐ THE HERO IS AN INSTRUMENT CLUSTER. No stock photograph of a smiling mechanic —
   the shop has no photography yet, and a borrowed garage is worse than none. The
   gauge sweep is the one moment of motion on the page and it settles into a
   resting state that is fully legible with JS off and with motion reduced. */
.hero{position:relative; isolation:isolate; overflow:hidden}
.hero__bg{position:absolute; inset:0; z-index:-1; pointer-events:none}
/* ⭐ TWO LIGHTS, NOT ONE. A single accent wash reads as a gradient; a cool one
   high on the right and a warm one low on the left reads as a room with a booth
   lamp in it — which is exactly what the photograph beside it shows. */
.hero__bg::before{content:""; position:absolute; inset:0;
  background:radial-gradient(58% 62% at 78% 8%, color-mix(in srgb, var(--accent) 26%, transparent) 0%, transparent 70%),
             radial-gradient(48% 52% at 6% 96%, color-mix(in srgb, var(--heat) 18%, transparent) 0%, transparent 72%);}
.hero__bg::after{content:""; position:absolute; inset:0; opacity:.5;
  background-image:radial-gradient(currentColor .6px, transparent .6px);
  background-size:22px 22px; color:var(--rule);
  -webkit-mask-image:linear-gradient(to bottom, #000 0%, transparent 78%);
  mask-image:linear-gradient(to bottom, #000 0%, transparent 78%);}
.gauge{position:absolute; right:-6%; top:-14%; width:min(38rem,64vw); aspect-ratio:1;
  z-index:-1; opacity:.5; pointer-events:none}
.gauge svg{width:100%; height:100%; overflow:visible}
.gauge .arc{fill:none; stroke:var(--rule); stroke-width:1.5; stroke-linecap:round}
.gauge .arc--live{stroke:var(--accent); stroke-dasharray:var(--dash); stroke-dashoffset:var(--off)}
.gauge .tick{stroke:var(--ink-3); stroke-width:1.5; stroke-linecap:round}
.gauge .tick--hot{stroke:var(--accent)}
.gauge .needle{stroke:var(--ink); stroke-width:2.5; stroke-linecap:round;
  transform-origin:120px 120px; transform:rotate(var(--ang,128deg))}
@media (min-width:0px){
  .gauge .needle{animation:sweep 2.1s cubic-bezier(.22,1.1,.3,1) .25s both}
  @keyframes sweep{ 0%{transform:rotate(-38deg)} 55%{transform:rotate(150deg)} 100%{transform:rotate(var(--ang,128deg))} }
}
@media (max-width:52rem){ .gauge{display:none} }

/* ═══════════════════════════════════════════════════════════════════════════
   MOTION. Two pieces, both cheap, both specific to this subject.

   ⛔ WHY THERE IS NO WebGL YARD. The best-known Three.js showcases measure
      around 47s to first paint and Apple ships ZERO canvas on its product
      pages. A slow hero loses more customers than a clever one wins, and the
      yard card says more than a rendered garden ever would.
   ⭐ So the depth is real but costs nothing: layered transforms, no library,
      no 3D model, no canvas.
   ═══════════════════════════════════════════════════════════════════════════ */

/* 1 ── THE BULB CHECK. Every instrument panel built since the nineties lights every lamp for
      about two seconds on power-up, then drops them one by one as each system
      reports in. An irrigation controller does the same
      thing. The service grid does exactly that when it comes into
   view. Nobody else can do this, because nobody else made the icons telltales. */
.svc .sym{transition:box-shadow .35s ease, background-color .35s ease}
@media (min-width:0px){
  .svc.bulbcheck .sym{
    animation:bulb 2.1s ease-out both;
    animation-delay:calc(var(--i,0) * 90ms);
  }
  @keyframes bulb{
    0%   {background:color-mix(in srgb, currentColor 13%, transparent); box-shadow:none}
    8%   {background:color-mix(in srgb, currentColor 46%, transparent);
          box-shadow:0 0 0 1px color-mix(in srgb, currentColor 55%, transparent), 0 0 22px -2px currentColor}
    62%  {background:color-mix(in srgb, currentColor 46%, transparent);
          box-shadow:0 0 0 1px color-mix(in srgb, currentColor 55%, transparent), 0 0 22px -2px currentColor}
    100% {background:color-mix(in srgb, currentColor 13%, transparent); box-shadow:none}
  }
}

/* ⭐ THE STEPS REPORT IN AS YOU PASS THEM. Each number goes green the moment its
   step reaches reading position — the same language as the bulb check: a system
   confirming it is good. It is a five-step process and this makes the page feel
   like it is being worked through rather than scrolled past.
   ⛔ Resting state is the plain number, fully legible with JS off. */
.step__n{position:relative; transition:color .35s ease}
.step__n::after{content:""; position:absolute; left:0; bottom:-.3rem; width:1.6rem; height:2px;
  border-radius:2px; background:var(--pass); transform:scaleX(0); transform-origin:left;
  transition:transform .4s cubic-bezier(.2,.9,.3,1)}
.step.done{--n-col:var(--pass)}
.step.done .step__n::after{transform:scaleX(1)}
@media (prefers-reduced-motion:reduce) and (max-width:0px){
  .step__n,.step__n::after{transition:none}
}

/* 2 ── HERO DEPTH. The gauge, the figure and the dot grid sit on three planes
   and move at three rates against the pointer. It is a flat cut-out; this is
   what makes it read as standing in a room. Scroll adds a slow vertical drift.
   ⛔ Everything is at its resting position with JS off — nothing is parked at
      opacity 0 waiting for an observer. */
.hero__bg,.gauge,.hero__fig img{will-change:transform}
@media (min-width:0px){
  .hero__bg{transform:translate3d(calc(var(--px,0) * 6px), calc(var(--py,0) * 5px + var(--sy,0) * 14px), 0)}
  .gauge{transform:translate3d(calc(var(--px,0) * -16px), calc(var(--py,0) * -12px + var(--sy,0) * 34px), 0) rotate(calc(var(--px,0) * 1.1deg))}
  .hero__fig img{transform:translate3d(calc(var(--px,0) * 9px), calc(var(--sy,0) * -10px), 0)}
  .hero__bg,.gauge,.hero__fig img{transition:transform .45s cubic-bezier(.2,.8,.3,1)}
  .hero.tracking .hero__bg,.hero.tracking .gauge,.hero.tracking .hero__fig img{transition:none}
}

/* ⭐ THE HERO FIGURE SLOT HOLDS THE WATER PANEL, not a photograph.
     ⛔ There is no photography for this trade yet. ⏭️ If one ever exists these
     rules are what it inherits: flat on the ground, no fake drop shadow. */
.shots .shot--cut{background:linear-gradient(180deg, color-mix(in srgb, var(--accent) 10%, transparent), transparent)}
.shots .shot--cut img{aspect-ratio:auto; object-fit:contain; max-height:15rem; margin-inline:auto; padding-top:.75rem}

/* ⭐ THE HERO FIGURE — a cut-out PNG with real alpha, so he stands on the black
   ground with the gauge glow behind rather than sitting in a box.
   ⚠️ This image is AI-GENERATED. It is the most prominent place on the site, so
   it is the FIRST one to replace with a photograph of Kingfisher. The flag says so. */
.hero__fig{position:relative; display:grid; justify-items:center; align-self:end}
.hero__fig{position:relative; align-self:center; justify-items:stretch; display:block}
/* ⚠️ The glow under the figure stays — it is what stops the panel looking
   pasted onto a very dark page. */
/* ⚠️ A hero photo shorter than the headline beside it reads as an
   afterthought. A 5:4 crop holds the column. */
.hero__fig img{width:100%; height:auto; display:block; border-radius:1rem;
  aspect-ratio:5/4; object-fit:cover;
  border:1px solid color-mix(in srgb, var(--accent) 28%, var(--rule));
  box-shadow:0 26px 70px -28px rgba(0,0,0,.95),
             0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent);
}
/* ⭐ THE CAPTION SITS ON THE PHOTOGRAPH, not under it — it keeps the hero one
   object instead of two stacked things, and it lands where the eye already is. */
.hero__figcap{display:block; margin:.55rem 0 1.1rem; font-family:var(--f-mono);
  font-size:.6875rem; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-3)}
/* ⚠️ The photo is the brightest thing on a very dark page, so it needs a floor
   under it or it looks pasted on. */
.hero__fig::before{content:""; position:absolute; inset:10% -8% -14% -8%; z-index:-1;
  border-radius:50%; filter:blur(48px);
  background:radial-gradient(60% 60% at 50% 50%,
    color-mix(in srgb, var(--accent) 30%, transparent), transparent 72%)}
.hero__fig .shot__flag{position:absolute; top:.4rem; right:.4rem; z-index:2}
.hero__fig::after{content:""; position:absolute; left:50%; bottom:-2px; translate:-50% 0;
  width:76%; height:2px; background:linear-gradient(90deg,transparent,var(--accent),transparent); opacity:.75}
@media (max-width:56rem){
  .hero__fig{display:block; margin-top:1.5rem}
  .hero__fig img{aspect-ratio:16/10}
}
.ticketband{display:grid; grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(1.5rem,4vw,3rem); align-items:center}
@media (max-width:56rem){ .ticketband{grid-template-columns:minmax(0,1fr)} }
.hero{padding-block:clamp(2.25rem,5vw,4rem) clamp(1.75rem,4vw,3rem); border-bottom:1px solid var(--rule)}
.eyebrow{display:flex; align-items:center; gap:.6rem; margin-bottom:1.1rem; flex-wrap:wrap}
.eyebrow .dot{width:.5rem; height:.5rem; border-radius:50%; background:var(--pass); flex:none}
.hero h1{font-size:var(--step-4); font-weight:800; max-width:16ch; margin-bottom:1.1rem}
.hero h1 u{text-decoration:none; color:var(--accent)}
.hero__lede{font-size:var(--step-1); color:var(--ink-2); max-width:46ch; margin-bottom:1.75rem}
.hero__act{display:flex; gap:.75rem; flex-wrap:wrap; align-items:center}
.hero__note{font-size:.8125rem; color:var(--ink-3); max-width:36ch; margin-top:1rem}
.split{display:grid; grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr); gap:clamp(1.75rem,4vw,3rem); align-items:start}
@media (max-width:56rem){ .split{grid-template-columns:minmax(0,1fr)} }

/* page head for interior routes */
.phead{padding-block:clamp(2rem,4vw,3.25rem) clamp(1.5rem,3vw,2.25rem); border-bottom:1px solid var(--rule)}
.crumb{font-family:var(--f-mono); font-size:.75rem; color:var(--ink-3); margin-bottom:.9rem}
.crumb a{text-decoration:none}
.crumb a:hover{color:var(--accent)}
.phead h1{font-size:var(--step-3); font-weight:800; max-width:20ch; margin-bottom:.85rem}
.phead p{font-size:var(--step-1); color:var(--ink-2); max-width:52ch}

.ticket{background:var(--surface); border:1px solid var(--rule); border-radius:.4rem; box-shadow:var(--shadow); overflow:hidden}
.ticket__cap{display:flex; align-items:center; gap:.5rem; padding:.5rem 1.1rem; font-family:var(--f-mono); font-size:.6875rem; letter-spacing:.11em; text-transform:uppercase}
.ticket__cap{background:var(--surface-2); color:var(--ink-2); border-bottom:1px solid var(--rule)}
.ticket__hd{display:flex; align-items:baseline; gap:.75rem; padding:.85rem 1.1rem; border-bottom:1px solid var(--rule); background:var(--surface-2); flex-wrap:wrap}
.ticket__no{font-family:var(--f-mono); font-weight:700; font-size:.8125rem}
.ticket__veh{font-size:.8125rem; color:var(--ink-2); margin-left:auto; font-family:var(--f-mono)}
.ticket__body{padding:1.1rem}
.line{display:grid; grid-template-columns:auto minmax(0,1fr) auto; gap:.6rem .9rem; align-items:baseline; padding-block:.7rem; border-bottom:1px dashed var(--rule-soft)}
.line:last-of-type{border-bottom:0}
.line__code{font-family:var(--f-mono); font-weight:700; font-size:.8125rem; color:var(--accent); background:var(--accent-soft); padding:.1rem .4rem; border-radius:.2rem; white-space:nowrap}
.line__what b{display:block; font-weight:600; font-size:.9375rem}
.line__what span{display:block; font-size:.8125rem; color:var(--ink-2); margin-top:.1rem}
.line__amt{font-family:var(--f-mono); font-weight:700; font-size:.9375rem; white-space:nowrap; font-variant-numeric:tabular-nums}
.line__amt em{display:block; font-style:normal; font-weight:400; font-size:.6875rem; color:var(--ink-3); text-align:right}
.ticket__ft{display:flex; align-items:center; gap:.75rem; padding:.9rem 1.1rem; border-top:1px solid var(--rule); background:var(--surface-2); flex-wrap:wrap}
.stamp{display:inline-flex; align-items:center; gap:.4rem; font-family:var(--f-mono); font-size:.75rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--pass); background:var(--pass-soft); border:1px solid color-mix(in srgb, var(--pass) 30%, transparent); padding:.28rem .55rem; border-radius:.2rem}
.ticket__tot{margin-left:auto; font-family:var(--f-mono); font-weight:700; font-size:1.0625rem}
.ticket__tot em{font-style:normal; font-size:.6875rem; color:var(--ink-3); font-weight:400; letter-spacing:.1em; text-transform:uppercase; margin-right:.4rem}

.sec{padding-block:clamp(2.5rem,5vw,4rem); border-bottom:1px solid var(--rule)}
.sec:last-child{border-bottom:0}
.sec__hd{max-width:var(--measure); margin-bottom:2rem}
.sec__hd h2{font-size:var(--step-3); font-weight:800; margin-block:.6rem .7rem}
.sec__hd p{color:var(--ink-2); font-size:var(--step-1)}
.prose{max-width:var(--measure); display:grid; gap:1.15rem}
.prose h3{font-size:1.25rem; font-weight:700; margin-top:.75rem}
.prose p{color:var(--ink-2)}
.prose ul{margin:0; padding-left:1.15rem; color:var(--ink-2); display:grid; gap:.5rem}
.prose strong{color:var(--ink); font-weight:600}

.steps{display:grid}
.step{display:grid; grid-template-columns:3.25rem minmax(0,1fr); gap:1.25rem; padding-block:1.35rem; border-top:1px solid var(--rule-soft); align-items:start}
.step:first-child{border-top:0; padding-top:0}
.step__n{font-family:var(--f-mono); font-weight:700; font-size:.8125rem; color:var(--n-col,var(--ink-3)); padding-top:.2rem}
.step h3{font-size:1.1875rem; font-weight:700; margin-bottom:.35rem}
.step p{color:var(--ink-2); max-width:58ch; font-size:.9375rem}
.step__meta{margin-top:.5rem; font-family:var(--f-mono); font-size:.75rem; color:var(--ink-3)}


/* ⭐ THE SPECIAL IS A WORK ORDER, NOT A COUPON. Every washing company runs a
   first-job offer and every one of them prints the same starburst. This one
   is set as a torn-off ticket so it belongs to the same world as the water
   report in the hero — the page has one idea, not two. */
/* ⭐ A COUPON THAT IS ACTUALLY A COUPON. Perforated edge, tear-off stub, and it
   prints on its own — @media print hides the entire site and leaves this one card
   on the page, so "print" produces a coupon and not 26 pages of website.
   ⛔ The dashes are a repeating-linear-gradient border, not an image: it stays
   crisp at any zoom and costs nothing. */
/* ⭐ THE SPEC PLATE. Every service page gets a drawing instead of a stock photo,
   because we have three usable photographs and fifteen service pages — and the
   same borrowed garage repeated fifteen times is worse than no picture at all.
   Each plate is the service's own telltale on an engineering grid, with that
   service's real figures set as dimension callouts. It teaches something a stock
   image cannot, and it costs about 2 KB. */
.plate{position:relative; border:1px solid var(--rule); border-radius:.45rem;
  background:var(--surface); overflow:hidden; display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.05fr); align-items:center; gap:0}
@media (max-width:44rem){ .plate{grid-template-columns:minmax(0,1fr)} }
.plate__draw{position:relative; display:grid; place-items:center; padding:clamp(1.5rem,4vw,2.5rem);
  background:
    linear-gradient(to right, color-mix(in srgb,var(--rule) 45%,transparent) 1px, transparent 1px) 0 0/28px 28px,
    linear-gradient(to bottom, color-mix(in srgb,var(--rule) 45%,transparent) 1px, transparent 1px) 0 0/28px 28px,
    var(--surface-2)}
.plate__draw .sym{width:7.5rem; height:7.5rem; border-radius:.9rem;
  background:color-mix(in srgb, currentColor 12%, transparent);
  border:1px solid color-mix(in srgb, currentColor 34%, transparent)}
.plate__draw .sym svg{width:4.2rem; height:4.2rem; stroke-width:1.5}
.plate__specs{padding:clamp(1.25rem,3vw,2rem); display:grid; gap:0; align-content:center}
.plate__row{display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:baseline;
  gap:.6rem; padding-block:.7rem; border-bottom:1px dashed var(--rule-soft)}
.plate__row:last-child{border-bottom:0}
.plate__k{font-family:var(--f-mono); font-size:.6875rem; letter-spacing:.11em;
  text-transform:uppercase; color:var(--ink-3); white-space:nowrap}
.plate__lead{height:1px; background:repeating-linear-gradient(to right,
  color-mix(in srgb,var(--rule) 90%,transparent) 0 4px, transparent 4px 8px); align-self:center}
.plate__v{font-family:var(--f-mono); font-weight:700; font-size:.9375rem; white-space:nowrap}
.plate__cap{position:absolute; left:.7rem; top:.7rem; font-family:var(--f-mono);
  font-size:.625rem; letter-spacing:.12em; text-transform:uppercase; color:var(--ink-3)}

.coupon{position:relative; background:var(--surface); color:var(--ink);
  border-radius:.5rem; box-shadow:var(--shadow); overflow:hidden;
  display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:stretch;
  border:2px dashed var(--rule)}
@media (max-width:46rem){ .coupon{grid-template-columns:minmax(0,1fr)} }
.coupon__body{padding:clamp(1.25rem,3vw,2rem)}
.coupon__tag{display:inline-flex; align-items:center; gap:.45rem; font-family:var(--f-mono);
  font-size:.6875rem; font-weight:700; letter-spacing:.13em; text-transform:uppercase;
  color:var(--accent); margin-bottom:.7rem}
.coupon h2{font-size:var(--step-2); font-weight:800; max-width:20ch; margin-bottom:.6rem}
.coupon__lede{color:var(--ink-2); max-width:46ch; margin-bottom:1rem}
.coupon__list{list-style:none; margin:0 0 1.1rem; padding:0; display:grid; gap:.4rem;
  font-size:.9375rem; color:var(--ink-2)}
.coupon__list li{display:grid; grid-template-columns:1.1rem minmax(0,1fr); gap:.55rem; align-items:start}
.coupon__list svg{width:1.1rem; height:1.1rem; stroke:var(--pass); fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round; margin-top:.25rem}
.coupon__fine{font-size:.75rem; color:var(--ink-3); max-width:54ch}

/* the tear-off stub, divided by a real perforation */
.coupon__stub{position:relative; display:grid; align-content:center; justify-items:center;
  gap:.2rem; text-align:center; padding:clamp(1.25rem,3vw,2rem) clamp(1.5rem,3vw,2.25rem);
  background:var(--surface-2); min-width:13rem}
.coupon__stub::before{content:""; position:absolute; left:0; top:.75rem; bottom:.75rem; width:2px;
  background:repeating-linear-gradient(to bottom, var(--rule) 0 6px, transparent 6px 12px)}
@media (max-width:46rem){
  .coupon__stub::before{left:.75rem; right:.75rem; top:0; bottom:auto; width:auto; height:2px;
    background:repeating-linear-gradient(to right, var(--rule) 0 6px, transparent 6px 12px)}
}
.coupon__price{font-family:var(--f-mono); font-weight:700; line-height:1;
  font-size:clamp(2.6rem,1.8rem + 3vw,3.75rem); color:var(--accent)}
.coupon__cap{font-family:var(--f-mono); font-size:.625rem; letter-spacing:.13em;
  text-transform:uppercase; color:var(--ink-3)}
/* ONE PRICE ON THIS STUB, not the auto build's two tiers.
   ⛔ Do not re-add a second tier without a second real price to put in it. */
.coupon__tiers{display:grid; gap:.7rem; width:100%}
.coupon__tier{display:grid; gap:.1rem; justify-items:center}
.coupon__tier + .coupon__tier{padding-top:.7rem;
  border-top:2px dashed color-mix(in srgb, var(--rule) 80%, transparent)}
.coupon__tier .coupon__price{font-size:clamp(2.1rem,1.5rem + 2.2vw,2.9rem)}
.coupon__act{display:flex; gap:.5rem; flex-wrap:wrap; justify-content:center; margin-top:.9rem}
.coupon__act .btn{padding:.5rem .85rem; min-height:2.5rem; font-size:.875rem}

@media print{
  body > *:not(main), .top, .foot, .draft, .launch, .chat, .rail, .sec:not(.coupon-sec){display:none!important}
  .coupon-sec, .coupon-sec *{visibility:visible}
  .coupon{box-shadow:none; border-color:#000; color:#000; background:#fff}
  .coupon__stub{background:#f4f4f4}
  .coupon__act{display:none!important}
  .coupon__price{color:#000}
  @page{margin:12mm}
}

.offer{display:grid; grid-template-columns:minmax(0,1fr) auto; gap:clamp(1.25rem,3vw,2.5rem);
  align-items:center; background:var(--surface); border:1px solid var(--rule);
  border-left:4px solid var(--accent); border-radius:.45rem; padding:clamp(1.25rem,3vw,2rem);
  box-shadow:var(--shadow); position:relative; overflow:hidden}
@media (max-width:46rem){ .offer{grid-template-columns:minmax(0,1fr)} }
.offer__tag{display:inline-flex; align-items:center; gap:.45rem; font-family:var(--f-mono);
  font-size:.6875rem; font-weight:700; letter-spacing:.13em; text-transform:uppercase;
  color:var(--accent); margin-bottom:.7rem}
.offer h2{font-size:var(--step-2); font-weight:800; max-width:18ch; margin-bottom:.65rem}
.offer__lede{color:var(--ink-2); max-width:48ch; margin-bottom:1.1rem}
.offer__list{list-style:none; margin:0 0 1.25rem; padding:0; display:grid; gap:.4rem;
  font-size:.9375rem; color:var(--ink-2)}
.offer__list li{display:grid; grid-template-columns:1.1rem minmax(0,1fr); gap:.55rem; align-items:start}
.offer__list svg{width:1.1rem; height:1.1rem; stroke:var(--pass); fill:none; stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round; margin-top:.25rem}
.offer__fine{font-size:.75rem; color:var(--ink-3); max-width:52ch}
.offer__price{display:grid; justify-items:center; gap:.15rem; text-align:center;
  border:1px dashed var(--rule); border-radius:.4rem; padding:1.1rem 1.4rem;
  background:var(--surface-2); min-width:11rem}
.offer__price .amt{font-family:var(--f-mono); font-weight:700; font-size:clamp(2rem,1.4rem+2vw,2.75rem); line-height:1}
.offer__price .cap{font-family:var(--f-mono); font-size:.625rem; letter-spacing:.13em;
  text-transform:uppercase; color:var(--ink-3)}
.offer__act{margin-top:.9rem; display:flex; gap:.6rem; flex-wrap:wrap; justify-content:center}
.svc{display:grid; grid-template-columns:repeat(auto-fill,minmax(15.5rem,1fr)); gap:1px; background:var(--rule); border:1px solid var(--rule); border-radius:.4rem; overflow:hidden}
.svc a{background:var(--surface); padding:1.15rem; text-decoration:none; align-content:start; transition:background-color .12s ease}
.svc a:hover{background:var(--surface-2)}
.svc a{grid-template-columns:auto minmax(0,1fr); column-gap:.85rem}
.svc h3{font-size:1.0625rem; font-weight:700}
.sym{width:2.5rem; height:2.5rem; flex:none; border-radius:.45rem; display:grid; place-items:center;
     background:color-mix(in srgb, currentColor 13%, transparent);
     border:1px solid color-mix(in srgb, currentColor 30%, transparent)}
.sym svg{width:1.4rem; height:1.4rem; stroke:currentColor; fill:none; stroke-width:1.7;
     stroke-linecap:round; stroke-linejoin:round}
.svc a{display:flex; align-items:flex-start; gap:.9rem}
.svc .txt{display:grid; gap:.3rem; min-width:0}
.sym--lg{width:3.25rem; height:3.25rem; border-radius:.55rem}
.sym--lg svg{width:1.85rem; height:1.85rem}
.svc p{font-size:.875rem; color:var(--ink-2)}
.svc .spec{font-family:var(--f-mono); font-size:.6875rem; color:var(--ink-3); margin-top:.4rem}


/* ⭐ PHOTOGRAPHY BAND. A real photograph earns its place by showing the WORK, not
   by decorating the page. Sized and cropped here so a replacement drops in without
   the layout moving — the aspect ratio is fixed, the image covers it. */
.shots{display:grid; grid-template-columns:repeat(auto-fit,minmax(13rem,1fr)); gap:1px;
  background:var(--rule); border:1px solid var(--rule); border-radius:.45rem; overflow:hidden}
.shot{position:relative; background:var(--surface); display:grid}
.shot img{width:100%; aspect-ratio:5/4; object-fit:cover; display:block}
.shot figcaption{padding:.7rem .9rem .85rem; font-size:.8125rem; color:var(--ink-2)}
.shot figcaption b{display:block; color:var(--ink); font-weight:600; font-size:.875rem; margin-bottom:.15rem}
.shot__flag{position:absolute; top:.6rem; left:.6rem; font-family:var(--f-mono); font-size:.625rem;
  font-weight:700; letter-spacing:.11em; text-transform:uppercase; color:var(--todo);
  background:color-mix(in srgb, var(--todo-soft) 92%, transparent); border:1px solid var(--todo-line);
  padding:.25rem .5rem; border-radius:.2rem}
.shot--empty{display:grid; place-items:center; text-align:center; gap:.3rem; padding:1.5rem 1rem;
  aspect-ratio:5/4; border:1.5px dashed var(--rule); background:var(--surface-2)}
.shot--empty b{font-family:var(--f-mono); font-size:.75rem; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-2)}
.shot--empty span{font-size:.8125rem; color:var(--ink-3); max-width:26ch}

/* ⛔ THE DEMONSTRATION STRIP — see the comment in build.py's SHELL.
   Uses the accent so it reads as part of the design rather than as a browser
   warning, but it is the first thing on every page and it is never hidden. */
/* 🔴 TWO CHAT LAUNCHERS IN ONE CORNER (7 Sep 2026) — SUPERSEDED 24 Sep 2026.
   This site is framed on botsonstaff.com (/see-a-repair-site and others),
   which has its own fixed chat pill bottom-right. On 7 Sep the shop's chat
   was moved LEFT when framed (html.framed .launch / .chat {left:1rem}).
   ⛔ THOSE RULES ARE DELETED. The owner, 24 Sep 2026, asked "The chat button
   on demos shown inside botsonstaff.com: should it sit on the left or the
   right there?", answered "right and go". The launcher AND its panel sit
   bottom-RIGHT, framed or not. Do not move them left again. html.framed is
   still set by app.js; nothing in this file uses it to move the chat. */
/* ═══ THE WATER REPORT ════════════════════════════════════════════════════
   ⭐ THE HERO OF THIS SITE IS A DOCUMENT, not a photograph of a clean driveway. Every
   competitor shows a half-finished pass and the word "transform"; none of them
   publishes the pressure they used, which is the number that actually separates
   one washing company from another.
   ⚠️ The row that is OUT OF RANGE is colored, and the five that are fine are
   not. ⛔ Do not color all six — a readout where everything glows says
   nothing, and the whole point is that the eye lands on the one line that
   matters before a word has been read. */
.wr{position:relative; background:var(--surface); border:1px solid var(--rule);
  border-radius:.75rem; overflow:hidden; box-shadow:var(--shadow);
  font-family:var(--f-mono)}
.wr__hd{display:flex; justify-content:space-between; gap:1rem; align-items:baseline;
  padding:.75rem 1rem; border-bottom:1px solid var(--rule); background:var(--surface-2);
  font-size:.625rem; letter-spacing:.12em; text-transform:uppercase; color:var(--ink-3)}
.wr__when{white-space:nowrap}
.wr__row{display:grid; grid-template-columns:3.1rem minmax(0,1fr) 4.5rem auto;
  grid-template-areas:"code name bar val" "code rng rng rng";
  gap:.15rem .7rem; align-items:center; padding:.6rem 1rem;
  border-bottom:1px dashed var(--rule-soft)}
.wr__code{grid-area:code; font-size:.6875rem; font-weight:700; letter-spacing:.06em;
  color:var(--ink-3)}
.wr__name{grid-area:name; font-family:var(--f-body); font-size:.875rem; color:var(--ink)}
.wr__bar{grid-area:bar; height:.3rem; border-radius:99px; background:var(--rule); overflow:hidden}
.wr__bar i{display:block; height:100%; border-radius:99px; background:var(--pass)}
.wr__val{grid-area:val; font-size:1rem; font-weight:700; text-align:right; white-space:nowrap}
.wr__val em{font-style:normal; font-size:.625rem; color:var(--ink-3); margin-left:.2rem}
.wr__rng{grid-area:rng; font-size:.625rem; letter-spacing:.08em; color:var(--ink-3)}
/* the one line that is wrong */
.wr__row--high{background:color-mix(in srgb, var(--todo) 9%, transparent)}
.wr__row--high .wr__bar i{background:var(--todo)}
.wr__row--high .wr__val{color:var(--todo)}
.wr__row--high .wr__code{color:var(--todo)}
.wr__note{padding:.85rem 1rem; font-family:var(--f-body); font-size:.8125rem;
  line-height:1.5; color:var(--ink-2); border-bottom:1px solid var(--rule);
  background:color-mix(in srgb, var(--todo-soft) 60%, transparent)}
.wr__note b{color:var(--ink)}
.wr__ft{display:flex; justify-content:space-between; align-items:center; gap:1rem;
  padding:.7rem 1rem; font-size:.625rem; letter-spacing:.11em; text-transform:uppercase;
  color:var(--ink-3); background:var(--surface-2)}
.wr__ft b{color:var(--ink)}
.wr__chip{color:var(--pass); border:1px solid color-mix(in srgb, var(--pass) 45%, transparent);
  background:color-mix(in srgb, var(--pass) 12%, transparent);
  padding:.25rem .5rem; border-radius:.25rem; font-weight:700}
@media (max-width:30rem){ .wr__row{grid-template-columns:2.8rem minmax(0,1fr) auto;
  grid-template-areas:"code name val" "code bar bar" "code rng rng"} }

.demostrip{display:flex; align-items:flex-start; gap:.6rem; padding:.6rem clamp(1rem,4vw,2rem);
  background:var(--accent-soft); border-bottom:1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  font-family:var(--f-mono); font-size:.75rem; line-height:1.45; color:var(--ink-2)}
.demostrip b{color:var(--ink); font-weight:700}
.demostrip a{color:var(--accent); font-weight:600}
.demostrip__dot{flex:none; width:.5rem; height:.5rem; margin-top:.4rem; border-radius:50%;
  background:var(--accent)}
@media print{.demostrip{display:none}}
.mapcard{display:flex; align-items:center; gap:.9rem; text-decoration:none;
  background:var(--surface); border:1px solid var(--rule); border-left:3px solid var(--accent);
  border-radius:.45rem; padding:1.1rem 1.25rem; min-height:9rem}
.mapcard:hover{background:var(--surface-2)}
.mapcard__pin{width:2.75rem; height:2.75rem; flex:none; border-radius:.45rem; display:grid; place-items:center;
  background:color-mix(in srgb, var(--accent) 16%, transparent); color:var(--accent)}
.mapcard__pin svg{width:1.5rem; height:1.5rem; fill:currentColor}
.mapcard b{display:block; font-family:var(--f-display); font-size:1.0625rem; font-weight:700}
.mapcard span{font-size:.9375rem; color:var(--ink-2)}
.mapcard em{display:block; font-style:normal; font-family:var(--f-mono); font-size:.6875rem;
  letter-spacing:.11em; text-transform:uppercase; color:var(--accent); margin-top:.35rem}
.mount{border:1px solid var(--rule); border-radius:.45rem; background:var(--surface-2);
  display:grid; place-items:center; text-align:center; gap:.3rem; padding:2.25rem 1rem; min-height:10rem}
.mount b{font-family:var(--f-mono); font-size:.75rem; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-2)}
.mount span{font-size:.8125rem; color:var(--ink-3); max-width:34ch}
.slot{border:1.5px dashed var(--rule); border-radius:.4rem; background:var(--surface-2); display:grid; place-items:center; text-align:center; padding:1.5rem 1rem; gap:.3rem; min-height:11rem}
.slot b{font-family:var(--f-mono); font-size:.75rem; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-2)}
.slot span{font-size:.8125rem; color:var(--ink-3); max-width:32ch}

.cta{color:var(--ink); border-radius:.5rem; padding:clamp(1.75rem,4vw,2.75rem); display:grid; gap:1.25rem}
.cta{background:var(--surface-2); border:1px solid var(--rule)}
.cta h2{font-size:var(--step-2); font-weight:800; max-width:22ch}
.cta p{max-width:52ch}
.cta p{color:var(--ink-2)}
.cta__row{display:flex; gap:.75rem; flex-wrap:wrap; align-items:center}

/* faq */
.faq{border-top:1px solid var(--rule-soft); max-width:var(--measure)}
.faq details{border-bottom:1px solid var(--rule-soft)}
.faq summary{cursor:pointer; padding:1rem 0; font-family:var(--f-display); font-weight:700; font-size:1.0625rem; list-style:none; display:flex; gap:.75rem; align-items:flex-start; min-height:2.75rem}
.faq summary::-webkit-details-marker{display:none}
.faq summary::before{content:"+"; font-family:var(--f-mono); color:var(--accent); font-weight:700; flex:none; line-height:1.4}
.faq details[open] summary::before{content:"\2212"}
.faq p{padding:0 0 1.1rem 1.6rem; color:var(--ink-2); max-width:58ch}

/* tables */
.tbl-wrap{overflow-x:auto; border:1px solid var(--rule); border-radius:.4rem; background:var(--surface)}
table{border-collapse:collapse; width:100%; min-width:34rem; font-size:.9375rem}
th,td{text-align:left; padding:.75rem .9rem; border-bottom:1px solid var(--rule-soft); vertical-align:top}
th{font-family:var(--f-mono); font-size:.6875rem; letter-spacing:.11em; text-transform:uppercase; color:var(--ink-3); background:var(--surface-2)}
tr:last-child td{border-bottom:0}
td.num{font-family:var(--f-mono); font-variant-numeric:tabular-nums; white-space:nowrap}

.areas{display:flex; flex-wrap:wrap; gap:.5rem; list-style:none; margin:0; padding:0}
.areas li{font-family:var(--f-mono); font-size:.8125rem; border:1px solid var(--rule); border-radius:1.25rem; padding:.35rem .75rem; background:var(--surface)}


/* his own channels — ⛔ Yelp is deliberately absent, it is barred estate-wide */
.social{display:flex; gap:.5rem; flex-wrap:wrap; margin-top:.9rem}
.social a{display:inline-flex; align-items:center; gap:.4rem; text-decoration:none;
  border:1px solid var(--rule); border-radius:1.25rem; padding:.35rem .75rem;
  font-size:.8125rem; color:var(--ink-2); background:var(--surface)}
.social a:hover{border-color:var(--accent); color:var(--ink)}
.social svg{width:.95rem; height:.95rem; fill:currentColor}
.foot{padding-block:2.5rem 3rem; font-size:.875rem; color:var(--ink-2); border-top:1px solid var(--rule)}
.foot__grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(12rem,1fr)); gap:1.75rem; margin-bottom:2rem}
.foot h4{font-family:var(--f-mono); font-size:.6875rem; letter-spacing:.13em; text-transform:uppercase; color:var(--ink-3); margin-bottom:.6rem}
.foot ul{list-style:none; margin:0; padding:0; display:grid; gap:.35rem}
.foot a{text-decoration:none; color:var(--ink-2)}
.foot a:hover{color:var(--ink); text-decoration:underline}

/* ⭐ A CIRCLE, NOT A PILL. 3.5rem is comfortably over the 44px touch-target
   floor, and the label moves to aria-label so a screen reader still gets
   "Describe a symptom" while the corner stays quiet. The tooltip on hover
   keeps the meaning discoverable for everyone else. */
.launch{position:fixed; right:1.15rem; bottom:1.15rem; z-index:40;
  width:3.5rem; height:3.5rem; border-radius:50%; padding:0;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--accent); color:var(--accent-ink); border:0; cursor:pointer;
  box-shadow:0 6px 20px -4px rgba(0,0,0,.55), 0 0 0 1px color-mix(in srgb,var(--accent) 55%,transparent);
  transition:transform .15s cubic-bezier(.3,1.3,.5,1), box-shadow .15s ease}
.launch:hover{transform:scale(1.06)}
.launch:active{transform:scale(.96)}
.launch svg{width:1.55rem; height:1.55rem; fill:none; stroke:currentColor;
  stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round}
.launch::after{content:attr(data-label); position:absolute; right:calc(100% + .6rem);
  white-space:nowrap; background:var(--surface); color:var(--ink);
  border:1px solid var(--rule); border-radius:.3rem; padding:.35rem .6rem;
  font-family:var(--f-body); font-size:.8125rem; box-shadow:var(--shadow);
  opacity:0; pointer-events:none; transform:translateX(4px); transition:opacity .14s ease, transform .14s ease}
.launch:hover::after,.launch:focus-visible::after{opacity:1; transform:none}
@media (max-width:30rem){ .launch::after{display:none} }
.chat{position:fixed; right:1rem; bottom:1rem; z-index:41; width:min(23rem, calc(100vw - 2rem)); background:var(--surface); border:1px solid var(--rule); border-radius:.5rem; box-shadow:var(--shadow); overflow:hidden; display:grid; grid-template-rows:auto 1fr auto auto; max-height:min(32rem, calc(100vh - 2rem))}
.chat__hd{display:flex; align-items:center; gap:.6rem; padding:.75rem .9rem; border-bottom:1px solid var(--rule); background:var(--surface-2)}
.chat__hd b{font-family:var(--f-display); font-size:.9375rem}
.chat__hd .lbl{margin-left:auto}
.chat__x{background:none; border:0; cursor:pointer; color:var(--ink-2); font-size:1.15rem; line-height:1; padding:.2rem .3rem; min-height:2rem}
.chat__log{padding:.9rem; display:grid; gap:.7rem; overflow-y:auto; align-content:start}
.msg{max-width:85%; padding:.55rem .75rem; border-radius:.45rem; font-size:.875rem; line-height:1.45}
.msg--bot{background:var(--surface-2); border:1px solid var(--rule-soft); justify-self:start; border-bottom-left-radius:.1rem}
.msg--me{background:var(--accent); color:var(--accent-ink); justify-self:end; border-bottom-right-radius:.1rem}
.chat__opts{display:flex; flex-wrap:wrap; gap:.4rem; padding:0 .9rem .9rem}
.chip{background:transparent; border:1.5px solid var(--rule); color:var(--ink); border-radius:1.25rem; padding:.4rem .75rem; font-size:.8125rem; font-family:var(--f-body); cursor:pointer; min-height:2rem}
.chip:hover{border-color:var(--accent); color:var(--accent)}
/* ⭐ THE CAPTURE FIELD, added 24 Sep 2026 with the chat's name-and-number step.
   .chat__opts was written for .chip alone, and nothing in these four demos had
   ever styled a form control: a bare input renders in the UA's Arial 13.33px on
   a #FFFFFF fill, which on the LIGHT theme is #FFFFFF on a #FFFFFF panel —
   1.00:1. Measured in a browser, not predicted.
   ⛔ font-size stays at 1rem. iOS Safari zooms the whole viewport when an input
   under 16px takes focus and does not zoom back out. The chips are .8125rem;
   the field is deliberately not, and that is not an inconsistency.
   ⛔ The border is --ink-2, NOT --rule and NOT --ink-3. --rule is 1.28:1 on the
   dark panel, so the chip outline already misses the 3:1 non-text floor and is
   useless on an empty box; --ink-3 is #6B7681 on landscaping and
   pressure-washing, measured at 3.66:1 and still unfixed there. --ink-2 is
   6.8:1 against its own fill on all four, in both themes.
   ⛔ Select on .chat__in, never on input[type="text"] — the number field is
   type="tel" and a type selector silently skips it. */
.chat__ask{flex:1 0 100%; display:flex; gap:.5rem; align-items:stretch; min-width:0; margin:.2rem 0 0}
.chat__in{flex:1 1 auto; width:100%; min-width:0;
  background:var(--surface-2); color:var(--ink); caret-color:var(--ink);
  border:1.5px solid var(--ink-2); border-radius:.45rem; padding:.45rem .7rem;
  font-family:var(--f-body); font-size:1rem; line-height:1.3; min-height:2.75rem;
  appearance:none; -webkit-appearance:none; color-scheme:light dark}
.chat__in::placeholder{color:var(--ink-2); opacity:1}
.chat__in[aria-invalid="true"]{border-color:var(--accent)}
/* The primary action is --signal-*, not --accent: --accent/--accent-ink is
   4.14:1 on auto-repair dark, under 4.5 for a 13px label. The border carries
   the 3:1 shape floor where the fill alone misses it (pool-service light). */
.chat__send{flex:0 0 auto; white-space:nowrap; min-height:2.75rem;
  background:var(--signal-fill); color:var(--signal-ink);
  border:1.5px solid var(--signal-text); border-radius:.45rem;
  font-weight:700; padding:.45rem .85rem}
.chat__send:hover{background:color-mix(in srgb, var(--signal-fill) 86%, #fff); color:var(--signal-ink)}
.chat__ft{padding:.6rem .9rem .75rem; border-top:1px solid var(--rule); font-size:.6875rem; color:var(--ink-3); font-family:var(--f-mono)}
[hidden]{display:none!important}
@media (prefers-reduced-motion:reduce) and (max-width:0px){ *{animation:none!important; transition:none!important} }


/* ══════════════════════════════════════════════════════════════════════════
   ⭐ THE PROFESSIONAL PASS — 11 Sep 2026.
   Measured against the reference client rebuild the owner holds this bar to,
   this demo was missing five things, and not one of them was a photograph:
     1. no brand MARK — the header carried a stacked logo IMAGE that needed a
        dark plate painted behind it just to survive the light theme
     2. no single decisive accent — the page borrowed six telltale colours
     3. no offer strip, so nothing said what the customer actually GETS
     4. the headline colour was the generic --accent, not a brand signal
     5. the primary button was the same green/blue as every chip on the page
   ⛔ Do NOT close this gap with a photograph or an AI image. These sites are
   drawn, not photographed, and that is the demo's honesty, not a limitation.

   ⚠️ THE GROUND IS BOTH. This site is dark-first and ALSO ships a light theme
   (prefers-color-scheme AND [data-theme="light"]). So the accent is declared
   as THREE values, not one: the brand hex, a lifted end for text on the dark
   ground, and a darker end for text on the light ground. --signal-text is the
   one that flips; everything that renders as TYPE uses it. The button fill is
   constant in both themes because it carries its own ink.
   ══════════════════════════════════════════════════════════════════════════ */
:root{
  --signal:#FFC72C;           /* the accent, as specified */
  --signal-deep:#0A4D7A;        /* darker end — accent TYPE on the light ground */
  --signal-lift:#FFC72C;        /* lighter end — accent TYPE on the dark ground */
  --signal-fill:#FFC72C;        /* the primary button */
  --signal-ink:#0E1720;          /* type ON the primary button */
  --signal-text:var(--signal-lift);
}
@media (prefers-color-scheme:light){
  :root:not([data-theme="dark"]){ --signal-text:var(--signal-deep); }
}
:root[data-theme="light"]{ --signal-text:var(--signal-deep); }

/* ── the mark ─────────────────────────────────────────────────────────── */
/* ⭐ Inline, in currentColor, so it inherits the theme instead of needing a
   plate painted behind it the way the old lockup image did. */
.brand{gap:.65rem}
.brand__mark{width:1.625rem; height:1.625rem; flex:none; display:block;
  color:var(--signal-text);
  transition:transform .18s cubic-bezier(.3,1.2,.5,1), color .18s ease}
.brand:hover .brand__mark{transform:translateY(-1px)}
.brand__txt{display:flex; flex-direction:column; gap:.1rem; min-width:0}
/* 🔴 A TEXT WORDMARK IS WIDER THAN THE 132px LOCKUP IT REPLACED, and .top__in is
   a WRAPPING flex row — so the phone and the button dropped to a second row at
   widths where they used to sit beside the nav. Measured, not guessed: at a
   1100px viewport the brand went 132px → 277px against a 267px budget, and
   .top__act moved from y=80 to y=125. ⛔ Never swap a logo for type in a
   wrapping header without re-measuring the row: nothing about it looks broken,
   it just quietly costs a row of height on every page.
   ⭐ The SECOND line is the widest part of the block, not the name — so it is
   the first thing to go, and the trade and city it repeats are both printed in
   the hero eyebrow a couple of inches below it. */
.brand{gap:.55rem}
.brand__nm{font-size:.9375rem}
.brand__sub{font-size:.5625rem; letter-spacing:.1em}
/* ⚠️ MEASURED AT 1024px, NOT GUESSED. With the second line already dropped, the
   header still needed 963px of a 942px content box on the longest of the four
   names — a 21px overrun that costs a whole extra row. The 35px comes off in
   four places rather than one so no single element gets conspicuously small:
   the row gap, the brand gap, the mark, and the wordmark. Below 60rem the nav
   collapses to the burger and none of this is load-bearing any more. */
@media (max-width:75rem){
  .top__in{gap:.875rem}
  .brand{gap:.45rem}
  .brand__mark{width:1.45rem; height:1.45rem}
  .brand__sub{display:none}
  .brand__nm{font-size:.8125rem}
}

/* ── the headline carries the colour ──────────────────────────────────── */
/* ⚠️ <em> so it still reads as emphasis with styles off; the slant comes off
   because the display face already leans on weight, not italics. */
h1 .hl{font-style:normal; color:var(--signal-text)}
.hero h1 u{color:var(--signal-text)}   /* the inner pages use <u>; same colour */

/* ── the offer strip ──────────────────────────────────────────────────── */
/* ⭐ The reference site's coupon bar, in this trade's own words, and every word of it is
   taken from copy already published elsewhere on this site.
   ⛔ It must never carry a price, a rating or a review count. */
.offerbar{background:var(--surface-2); border-bottom:1px solid var(--rule)}
.offerbar__in{display:flex; align-items:center; gap:.85rem; flex-wrap:wrap;
  padding-block:.55rem}
.offerbar__tag{flex:0 0 auto; font-family:var(--f-mono); font-size:.625rem;
  font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  color:var(--signal-text); border-radius:999px; padding:.18rem .55rem;
  border:1px solid color-mix(in srgb, var(--signal-text) 42%, transparent)}
.offerbar__msg{margin:0; flex:1 1 22rem; min-width:0; font-size:.8125rem;
  line-height:1.45; color:var(--ink-2)}
.offerbar__msg b{color:var(--ink); font-weight:600}
.offerbar__go{flex:0 0 auto; font-family:var(--f-mono); font-size:.6875rem;
  font-weight:700; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink); text-decoration:none; white-space:nowrap; padding-bottom:2px;
  border-bottom:1px solid color-mix(in srgb, var(--signal-text) 60%, transparent)}
.offerbar__go:hover{color:var(--signal-text)}
@media (max-width:40rem){
  .offerbar__go{display:none}          /* the hero button is a screen below it */
  .offerbar__msg{font-size:.78125rem}
}

/* ── the primary button is the signal, not the generic accent ─────────── */
.btn--go{background:var(--signal-fill); color:var(--signal-ink);
  box-shadow:0 1px 0 color-mix(in srgb, #000 18%, transparent) inset,
             0 8px 22px -14px color-mix(in srgb, var(--signal-fill) 90%, #000)}
.btn--go:hover{background:color-mix(in srgb, var(--signal-fill) 86%, #fff);
  color:var(--signal-ink)}

/* 🔴 AND THE GHOST HAS TO BE RESTATED AFTER IT. `.btn--line` is declared ~500
   lines above. This stylesheet is unlayered, so a later rule of EQUAL
   specificity wins — the appended `.btn--go` block above is why this one has
   to exist below it rather than being left where it was. Two primaries is no
   primary. ⛔ Any future .btn rule appended past this point must restate the
   ghost again. */
.btn--line{background:transparent; border-color:var(--rule); color:var(--ink);
  box-shadow:none}
.btn--line:hover{background:var(--surface-2); border-color:var(--signal-text);
  color:var(--ink)}

/* ── depth on the drawn hero ──────────────────────────────────────────── */
/* ⭐ What separates the reference site's instrument from a flat vector is one light and a
   real shadow. These heroes had neither, so the art sat ON the page instead
   of above it. */
.hero__fig img{filter:drop-shadow(0 26px 44px rgba(0,0,0,.45)) contrast(1.05) saturate(1.04)}
.hero__fig .wr{box-shadow:0 1px 2px rgba(0,0,0,.32), 0 30px 56px -22px rgba(0,0,0,.72);
  filter:contrast(1.02)}
@media (prefers-color-scheme:light){
  :root:not([data-theme="dark"]) .hero__fig img{filter:drop-shadow(0 22px 36px rgba(19,26,33,.28)) contrast(1.04) saturate(1.03)}
  :root:not([data-theme="dark"]) .hero__fig .wr{box-shadow:0 1px 2px rgba(19,26,33,.08), 0 26px 48px -20px rgba(19,26,33,.34)}
}
:root[data-theme="light"] .hero__fig img{filter:drop-shadow(0 22px 36px rgba(19,26,33,.28)) contrast(1.04) saturate(1.03)}
:root[data-theme="light"] .hero__fig .wr{box-shadow:0 1px 2px rgba(19,26,33,.08), 0 26px 48px -20px rgba(19,26,33,.34)}
@media (prefers-reduced-motion:reduce) and (max-width:0px){ .brand:hover .brand__mark{transform:none} }

/* ── 💦 THE DAY, AS THE CUSTOMER HAS IT — 11 Sep 2026 ─────────────────────
   ⭐ The old h1 led with the REFUSAL — "we will not pressure wash your roof" —
   which is the sharpest sentence on this site and is staying, but it is our
   position, not the customer's day. Nobody books an exterior clean because they
   want a roof cleaned correctly; they book it because of how the house looks
   from the road. So the headline names the trade and the town, the accent
   carries the house, and the refusal moves into the beat where it actually
   happens — which is where it does the most work anyway, because it is a thing
   somebody asked for and did not get.
   ⛔ Every beat is something one owner-run crew could really do in a day, and
   every number in it is already on this page: 3,000 psi asked for, ~100 psi and
   an 8-minute dwell on the roof, under 500 on siding, 3,200 on the driveway.
   Nothing here says a slot is held or a date confirmed.
   ⭐ COLOUR. The timestamps are mono in --ink-3 like every other reference
   figure on the site. The signal colour marks exactly ONE beat — 13:10, the
   moment the promise in the headline actually happens — and the refused beat is
   ruled in --tt-red, the telltale this site already uses for "stop". Both
   tokens flip with the light theme, so neither is hard-coded. */
.sec--day .day{list-style:none; margin:0; padding:0; display:grid; gap:0}
.day__i{display:grid; grid-template-columns:4.25rem minmax(0,1fr); gap:0 1.5rem;
  align-items:start; padding-block:1.15rem; border-top:1px dashed var(--rule-soft)}
.day__i:first-child{border-top:1px solid var(--rule)}
.day__t{font-family:var(--f-mono); font-size:.75rem; font-weight:700; letter-spacing:.1em;
  color:var(--ink-3); padding-top:.3rem; font-variant-numeric:tabular-nums}
/* 🔴 THE GRID HAD THREE CHILDREN AND TWO COLUMNS. A beat holds a timestamp, an
   <h3> and a <p>. Auto-placement puts the timestamp at 1/1, the heading at 1/2,
   and then wraps the PARAGRAPH back to 2/1 — the timestamp column — so the prose
   renders in a ribbon one or two words wide and several hundred pixels tall. It
   still looks like a layout, which is why nothing about it reads as an error.
   ⛔ Pin both text children to column 2; never let auto-placement decide it. */
.day__i h3,.day__i p{grid-column:2}
.day__i h3{font-size:1.0625rem; font-weight:700; line-height:1.24; margin:0 0 .4rem; color:var(--ink)}
.day__i p{margin:0; font-size:.9375rem; line-height:1.62; color:var(--ink-2); max-width:62ch}
.day__i p b{color:var(--tt-red); font-weight:700}
/* ⛔ the telltale that means stop — on the one beat that was refused */
.day__i--no .day__t{color:var(--tt-red)}
.day__i--no{border-left:2px solid var(--tt-red); padding-left:.85rem; margin-left:-.85rem}
/* ⭐ ONE marked beat, and it is the one the customer actually bought */
.day__i--live .day__t{color:var(--signal-text)}
.day__i--live .day__t::after{content:""; display:block; width:5px; height:5px; border-radius:50%;
  background:var(--signal-text); margin-top:.5rem}
.day__foot{margin:0; padding-top:1.15rem; border-top:1px solid var(--rule);
  font-size:.9375rem; line-height:1.62; color:var(--ink-2); max-width:66ch}
.day__foot b{color:var(--ink); font-weight:600}
@media (max-width:34rem){
  .day__i{grid-template-columns:minmax(0,1fr); gap:.35rem}
  .day__i h3,.day__i p{grid-column:1}
  /* ⚠️ the refused beat's red rule HANGS into the gutter on a wide screen. At
     390px the gutter is 20px, so the hang put it 6px off the screen edge and
     read as a misprint rather than an annotation. Align it to the column. */
  .day__i--no{margin-left:0; padding-left:.75rem}
  .day__t{padding-top:0}
  .day__i--live .day__t::after{display:inline-block; margin:0 0 .1rem .5rem; vertical-align:middle}
}


/* ── 📷 PHOTOGRAPHS ───────────────────────────────────────────────────────
   🔴 11 Sep 2026. Owner: "need photos!!" and "no stick diagrams". This demo
   shipped with none, and a page of line drawings reads as a prototype rather
   than as a business's site.

   ⭐ PORTABLE ON PURPOSE. Thirteen demos, thirteen different token names —
   --soil/--panel/--hair here, --paper/--rule/--gr there, light and dark both.
   A component written against one demo's variables is a component that has to
   be rewritten twelve times and will be wrong in four of them. So this uses
   `currentColor` and plain rgba, and inherits the page's own ink.

   ⛔ Every CC BY / CC BY-SA frame carries its credit IN the frame. Attribution
   is a licence term, not a courtesy, and "we'll add the credits later" is
   exactly how a licence gets breached. */
.photo{position:relative;margin:0;line-height:0;border-radius:4px;overflow:hidden;
  border:1px solid rgba(128,128,128,.28)}
.photo img{width:100%;display:block;object-fit:cover;filter:saturate(1.03) contrast(1.02)}
.photo--hero img{height:clamp(260px,34vw,430px)}
.photo--band img{height:clamp(190px,26vw,320px)}
.photo--tall img{height:clamp(240px,30vw,380px)}
/* ⚠️ the badge carries its OWN scrim rather than the frame carrying a gradient.
   A gradient tuned for a dark ground turns a light photo muddy along the bottom,
   and half these demos are light. */
.photo__cr{position:absolute;right:8px;bottom:8px;z-index:2;
  font-size:9.5px;letter-spacing:.04em;line-height:1.45;font-weight:500;
  color:rgba(255,255,255,.88);background:rgba(0,0,0,.52);padding:3px 7px;border-radius:2px;
  text-shadow:0 1px 2px rgba(0,0,0,.5)}
.photo__cr a{color:inherit;text-decoration:underline;text-underline-offset:2px}
.photo__cap{line-height:1.55;font-size:12.5px;opacity:.72;padding-top:9px}

/* ── 🚪 THE WAY OUT STAYS ON SCREEN ───────────────────────────────────────
   🔴 11 Sep 2026, owner: "need to be able and x out of all the demo sites if
   you view on mobile dont let people get stuck somewher with out easy exit."

   This banner is the only route from a demo back to us, and it sat at the top
   of the document as a static block. On a phone, two swipes down a service page
   and it is gone — a visitor who arrived on a shared link has no way back and
   no reason to think one exists. ⚠️ The link was always there and was always
   unreachable, which is the same shape as the embed's close button being parked
   off-screen above the iframe.

   ⭐ Sticky on phones only. On a desktop the banner is in view at the top of a
   page people can see all of, and pinning it would just spend vertical room. */
@media (max-width: 640px) {
  .demostrip {
    position: sticky;
    top: 0;
    z-index: 90;
  }
  /* ⚠️ AND THE LINK HAS TO BE HITTABLE, NOT MERELY PRESENT. It is inline text
     inside a sentence, so on a 375px screen it wrapped into two fragments 15px
     tall — measured, both genuinely tappable, and both far under the 44px a
     thumb needs. ⭐ Make it a control: one line, real padding, no wrap. */
  .demostrip a {
    display: inline-block;
    margin-top: 4px;
    padding: 11px 12px;
    white-space: nowrap;
    font-weight: 700;
  }
}


/* 🔴 INSIDE OUR OWN FRAME THE BANNER IS DUPLICATE — 11 Sep 2026. Owner, looking
   at the embed on the home page: "take all of this off this is awfull they know
   that". He is right about that surface: our own chrome prints a disclosure
   directly under the frame, in our voice, so the demo's banner made two stacked
   notices — which reads as nervous rather than as honest.
   ⛔ A STANDALONE PAGE KEEPS IT. There the banner is the ONLY notice, and these
   pages carry a live telephone line: an invented business with a working number
   and nothing saying so is a trap, not a demo.
   ⚠️ Fails in the honest direction — with JavaScript off, nothing is hidden.
   ⛔ If the note under the frame is ever removed, delete this rule first. */
html[data-embedded] .demostrip { display: none !important; }

/* 🔴 HERO WITHOUT THE REFERENCE RAIL — 13 Sep 2026, owner: "still have the side
   menu on hero take these off". .bay is `var(--rail) 1fr`, so removing the aside
   alone would leave the first column empty and the copy indented into column 2.
   ⛔ The rail stays on INNER pages — this collapses the grid on the home hero
   only, which is the one the botsonstaff hero photographs. */
.bay--solo{grid-template-columns:minmax(0,1fr)}


/* ── 🧭 THE UTILITY BAR — 15 Sep 2026 ─────────────────────────────────
   Written by tools/demo-utilbar.mjs. See that file for why the disclosure
   banner became a chip and why hours are absent on the demos that publish none.
   ⛔ Self-contained colour on purpose: every demo means something different by
   --ink, and borrowing it painted this a different thing on each site. */
.utilbar{background:#0A4D7A;color:rgba(255,255,255,.92);
  border-bottom:1px solid rgba(255,255,255,.14);font-size:.78rem;line-height:1.4;
  font-family:system-ui,-apple-system,"Segoe UI",sans-serif}
.utilbar__in{display:flex;align-items:center;justify-content:space-between;
  gap:.9rem;min-height:38px;padding:.3rem 20px;flex-wrap:wrap;
  max-width:1180px;margin:0 auto}
.utilbar__hours{margin:0;display:flex;align-items:center;gap:.45rem;min-width:0}
.utilbar__hours b{font-weight:600;color:#fff}
.utilbar__sep{opacity:.45}
.utilbar__hours>span:last-child{opacity:.72}
.utilbar__right{display:flex;align-items:center;gap:.9rem;flex-wrap:wrap;min-width:0}
.utilbar__lnk{color:rgba(255,255,255,.88);text-decoration:none;
  border-bottom:1px solid rgba(255,255,255,.26);padding-bottom:1px}
.utilbar__lnk:hover{color:#fff;border-bottom-color:#fff}
/* ⭐ colour is never the only carrier — the words say Open or Closed outright */
.utilbar__dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto;background:#7fe0a8}
.utilbar__hours[data-state="open"] .utilbar__dot{background:#7fe0a8;animation:utilpulse 3s ease-out infinite}
.utilbar__hours[data-state="soon"] .utilbar__dot{background:#ffc861}
.utilbar__hours[data-state="shut"] .utilbar__dot{background:#9aa6b1}
@keyframes utilpulse{0%{box-shadow:0 0 0 0 rgba(127,224,168,.5)}
  70%{box-shadow:0 0 0 6px rgba(127,224,168,0)}100%{box-shadow:0 0 0 0 rgba(127,224,168,0)}}
@media (prefers-reduced-motion:reduce) and (max-width:0px){.utilbar__dot{animation:none !important}}
.utilbar .demostrip{display:inline-flex;align-items:center;gap:.4rem;
  padding:.16rem .55rem;border-radius:999px;background:rgba(255,255,255,.10);
  border:1px solid rgba(255,255,255,.22);font-size:.72rem;color:#fff;
  text-decoration:none;cursor:pointer}
.utilbar .demostrip:hover{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.38)}
.utilbar .demostrip b{text-transform:uppercase;letter-spacing:.08em;font-size:.68rem;font-weight:700}
.utilbar .demostrip__dot{width:7px;height:7px;border-radius:50%;background:#ff6a55;display:block}
@media (max-width:860px){
  /* ⛔ the TAIL hides, never the notice — and the chip becomes the link to it */
  .utilbar__demotail{display:none}
  .utilbar__in{gap:.55rem}
  .utilbar__right{gap:.7rem}
  .utilbar .demostrip{min-height:30px}
}
@media (max-width:560px){
  .utilbar{font-size:.72rem}
  .utilbar__hours>span:last-child,.utilbar__sep{display:none}
  .utilbar .demostrip{min-height:34px;padding-inline:.7rem}
}
/* ⛔ Inside our own iframe the surrounding chrome carries the disclosure, so the
   chip is hidden there and ONLY there. A standalone page always keeps it. */
html[data-embedded] .utilbar .demostrip{display:none !important}

/* 🔴 NO STATUS LIGHT WITHOUT A STATUS — 15 Sep 2026. Six of these demos publish
   no hours at all, and one publishes "by appointment". The dot is a status
   light: green, beside a company name with nothing behind it, it silently says
   OPEN NOW about a business whose hours nobody ever wrote. It only appears once
   the script has decided a real state from real published hours.
   ⚠️ This shipped for one sync because the fix was written into a script that
   died on an assertion BEFORE the file was saved — the edit was reported and
   never landed. Verify the artefact, not the log line. */
.utilbar__hours:not([data-state]) .utilbar__dot{display:none}


/* ── 🖼️ THE PHOTOGRAPH IS THE HERO — 15 Sep 2026 ─────────────────────────
   Written by tools/demo-photo-hero.mjs; the reasoning lives there. */
.lanaihero{position:relative;isolation:isolate;display:flex;flex-direction:column;justify-content:center;
  min-height:clamp(330px,62vh,600px);overflow:hidden;background:#07121A}
.lanaihero>img{position:absolute;inset:0;z-index:0;width:100%;height:100%;
  object-fit:cover;object-position:center 58%}
/* 🔴 TWO GRADIENTS, BOTH LOAD-BEARING. The horizontal one carries the headline;
   the vertical one holds the eyebrow and the buttons, which sit where the
   horizontal scrim has already thinned out. */
.lanaihero__scrim{position:absolute;inset:0;z-index:1;
  background:
    linear-gradient(90deg, rgba(7,18,26,.96) 0%, rgba(7,18,26,.93) 18%, rgba(7,18,26,.88) 30%, rgba(7,18,26,.80) 40%, rgba(7,18,26,.68) 48%, rgba(7,18,26,.52) 56%, rgba(7,18,26,.34) 64%, rgba(7,18,26,.18) 73%, rgba(7,18,26,.06) 83%, rgba(7,18,26,0) 92%),
    linear-gradient(0deg, rgba(7,18,26,.34) 0%, rgba(7,18,26,.12) 26%, rgba(7,18,26,0) 52%)}
/* ⛔ padding-block, never the shorthand — a `padding: X 0 Y` here wipes the
   horizontal gutter the wrap class contributes and every line goes flush to the
   edge of the screen. That shipped once. */
.lanaihero__in{position:relative;z-index:2;
  padding-block:clamp(2rem,5vw,3.4rem) clamp(1.9rem,4.5vw,3rem)}
.lanaihero h1{color:#fff;max-width:20ch;text-wrap:balance}
.lanaihero .h1__where{color:#fff}
.lanaihero .hl{color:rgba(255,255,255,.74)}
.lanaihero .eyebrow,.lanaihero .lbl,.lanaihero .kicker{color:rgba(255,255,255,.88)}
.lanaihero .btnrow,.lanaihero .hero__act{margin-top:1.4rem;display:flex;flex-wrap:wrap;gap:.7rem}
/* ⛔ A ghost/outline button is a hairline on paper; over a photograph it
   disappears. WCAG 2.2 asks 3:1 for the control's own boundary, not just its
   label. */
.lanaihero .btn--ghost,.lanaihero .btn--line,.lanaihero .btn--out{
  border-color:rgba(255,255,255,.62);color:#fff;background:rgba(7,18,26,.42);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.lanaihero .btn--ghost:hover,.lanaihero .btn--line:hover,.lanaihero .btn--out:hover{
  background:rgba(7,18,26,.66);border-color:#fff}
.lanaihero .photo__cr{position:absolute;right:10px;bottom:9px;z-index:3;
  font-size:10px;color:rgba(255,255,255,.8);background:rgba(0,0,0,.5);
  padding:3px 7px;border-radius:3px}
.lanaihero .photo__cr a{color:inherit}
@media (max-width:640px){
  /* ⚠️ At phone width the headline spans the full column, so a left-weighted
     gradient leaves its right-hand words over bright sky. Mostly vertical here. */
  .lanaihero{min-height:clamp(300px,52vh,420px)}
  .lanaihero__scrim{background:
    linear-gradient(0deg, rgba(7,18,26,.80) 0%, rgba(7,18,26,.72) 50%,
                          rgba(7,18,26,.55) 100%),
    linear-gradient(90deg, rgba(7,18,26,.55) 0%, rgba(7,18,26,.30) 100%)}
  .lanaihero h1{max-width:none}
}


/* 🔴 THE HERO'S MEASURE, MATCHED TO THE PAGE — 15 Sep 2026.
   The transform hardcoded `class="wrap"` as the gutter and on this demo `wrap`
   contributes none, so the headline sat at x=0 while the body copy below it
   started at 62. Switching to `shell` overshot to 418, because `shell` is a
   GRID here and each hero child became a cell.
   ⭐ So the measure is stated outright rather than borrowed: the same 1248/48
   the shell resolves to, which tracks the page at every width instead of at
   the one I happened to measure. */
/* 🔴 `margin-inline:auto` IS NOT CENTRING INSIDE A FLEX COLUMN — it absorbs the
   free space and stops the item stretching, so this block shrank to its own
   content (537px) and sat at x=418 while the body copy below started at 62.
   It reads as a gutter bug and is a flexbox rule. ⭐ Cap the width and centre
   it explicitly instead; `min()` keeps it honest at narrow widths too. */
.lanaihero__in{width:min(100%,1248px);align-self:center;padding-inline:48px}
@media (max-width:640px){ .lanaihero__in{padding-inline:20px} }




/* 🔴 THE HERO BUTTONS — 15 Sep 2026. Owner: "make sure the buttons are spread
   out and even across heros", "not sqished into the tiltles".
   Measured at 1280 before touching anything: rv-powersports had its buttons
   touching the headline at a gap of ZERO, because its row is `.hero__cta` and
   the rule only named `.btnrow` and `.hero__act`. The rest sat at 22px with an
   11px gap between them and heights running 40 to 53.
   ⭐ One rule, all three row names, and a floor under the height so a row of
   buttons reads as a row rather than as two different controls. */
.lanaihero .btnrow,
.lanaihero .hero__act,
.lanaihero .hero__cta{
  margin-top:clamp(1.6rem,2.6vw,2.2rem);
  display:flex; flex-wrap:wrap; align-items:center;
  gap:.85rem;
}
.lanaihero .btn{
  min-height:48px;
  display:inline-flex; align-items:center; justify-content:center;
  padding-inline:1.25rem;
}
@media (max-width:560px){
  /* ⚠️ Full width and stacked on a phone — two half-width buttons side by side
     put both under the 48px the thumb needs in the direction that matters. */
  .lanaihero .btnrow,.lanaihero .hero__act,.lanaihero .hero__cta{gap:.65rem}
  .lanaihero .btn{width:100%}
}


/* ── 👥 THE SOCIAL MARKS — 15 Sep 2026. Owner: "put fake no link social on the
   headers", after twice being told there is nowhere honest to point them.
   ⛔ THEY ARE NOT LINKS, AND THAT IS THE POINT. These companies are invented,
   so a social icon could only lead to an invented profile, to somebody else's
   real account, or to nothing — and a control that goes nowhere is worse than
   no control. They are `<span>` marks, `aria-hidden`, not focusable and not
   clickable: an illustration of where a real client's profiles sit in the bar,
   on a page that already says it is a demonstration.
   ⛔ Do not turn these into <a> on a real build without real profiles behind
   them. Bots On Staff's own Facebook and Instagram have never published once. */
.utilbar__social{display:inline-flex;align-items:center;gap:.55rem;opacity:.62}
.utilbar__social svg{width:15px;height:15px;display:block}
@media (max-width:560px){ .utilbar__social{display:none} }


/* 🔴 THE GHOST BUTTON, RESTATED BELOW `.btn` — 15 Sep 2026.
   The polish gate refused four demos for this and it was right: adding a
   `.lanaihero .btn` block after the ghost rules puts the outline variant at the
   mercy of source order in unlayered CSS. It happens that the block above sets
   no background, so nothing rendered wrong — but "it happens to be fine" is not
   a property anyone can rely on while editing this file later.
   ⛔ Any new `.btn` rule goes ABOVE this, or this moves below it. */
.lanaihero .btn--ghost,
.lanaihero .btn--line,
.lanaihero .btn--out{
  border-color:rgba(255,255,255,.62);
  color:#fff;
  background:rgba(7,18,26,.42);
  -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
}
.lanaihero .btn--ghost:hover,
.lanaihero .btn--line:hover,
.lanaihero .btn--out:hover{background:rgba(7,18,26,.66);border-color:#fff}

.lanaihero .btn--ghost{
  border-color:rgba(255,255,255,.62);
  color:#fff;
  background:rgba(7,18,26,.42);
}


/* ── 📣 THE OFFER BAND, ON THE PHOTOGRAPH — 15 Sep 2026 ──────────────────────
   Owner: "i dont think it needs the white section evry vist", then "figure it
   out in a great spot dont just throw it somewhere be creative".
   🔴 IT CANNOT SIMPLY GO. The polish gate requires an offer strip on every page
   and it is right to — it is the one line on a demonstration site that says what
   the shop actually promises. But as a white slab wedged between the header and
   the hero it interrupted the only picture on the page before anyone had seen it.
   ⭐ So it is pinned to the FOOT of the hero, over the photograph, on the same
   dark glass as the scrim. The promise now reads as part of the image instead of
   as an announcement above it, and the page opens header -> photograph -> story
   with nothing cutting across.
   ⛔ Its own backdrop is near-opaque on purpose: type over a photograph is the
   one thing on this estate that has been measured wrong twice, and a band that
   carries its own ground cannot drift when the picture changes. */
.lanaihero{padding-bottom:0}
.lanaihero .offerbar{
  position:absolute; left:0; right:0; bottom:0; z-index:3;
  background:rgba(7,20,30,.86);
  -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  border-top:1px solid rgba(255,255,255,.16);
  border-bottom:0;
}
.lanaihero .offerbar__in{
  display:flex; align-items:center; gap:1rem; flex-wrap:wrap;
  padding-block:.7rem;
}
.lanaihero .offerbar__tag{
  color:#fff; border-color:rgba(255,255,255,.45); background:rgba(255,255,255,.10);
}
.lanaihero .offerbar__msg{color:rgba(255,255,255,.92); margin:0}
.lanaihero .offerbar__msg b{color:#fff}
.lanaihero .offerbar__go{color:#fff; border-color:rgba(255,255,255,.5)}
/* ⚠️ the hero's own content must clear the band, or the buttons sit under it */
.lanaihero__in{padding-bottom:clamp(5.5rem,9vw,7rem)}
@media (max-width:860px){
  /* ⛔ On a phone the band would eat the picture. It goes back into the flow
     directly beneath the hero — still out from between the header and the photo,
     which is the thing that was wrong. */
  .lanaihero .offerbar{position:static;backdrop-filter:none;-webkit-backdrop-filter:none}
  .lanaihero__in{padding-bottom:clamp(1.9rem,4.5vw,3rem)}
}


/* ══ 🧭 THE HEADER, 2026 — 15 Sep 2026 ══════════════════════════════════════
   Owner: "the header need to be way better on each site look like proper 2026
   header trends", "make sure everyting looks as goos as outr customer sites".
   🔴 MEASURED FIRST, ALL TWELVE AT 1280: heights ran from 62px to 223px, only
   one was sticky, and eleven carried a telephone number that the new utility bar
   was already showing a few pixels above it.
   ⭐ This layer is keyed on the `header` ELEMENT, not on a class, because these
   demos call it .site, .top, .mast, .nav and .masthead — five names for one
   thing. Anything that needs a class name is done per demo.
   ⛔ It is deliberately visual-only: sticky, glass, a hairline that appears on
   scroll, and honest focus rings. It does not re-order anybody's markup. */
header{
  position:sticky; top:0; z-index:60;
  -webkit-backdrop-filter:saturate(1.3) blur(10px);
  backdrop-filter:saturate(1.3) blur(10px);
  transition:box-shadow .22s ease, border-color .22s ease;
}
/* ⭐ The rule under the header only appears once you have scrolled — a border
   that is always there is chrome, a border that arrives is depth. */
header::after{
  content:""; position:absolute; left:0; right:0; bottom:-1px; height:1px;
  background:currentColor; opacity:0; transition:opacity .22s ease;
  pointer-events:none;
}
html[data-scrolled] header::after{opacity:.16}
html[data-scrolled] header{box-shadow:0 10px 30px -22px rgba(0,0,0,.55)}

/* ⛔ 44px is not decoration. WCAG 2.2 §2.5.8, and on a phone this is the only
   navigation there is. */
@media (max-width:900px){
  header nav a{min-height:44px;display:flex;align-items:center}
}
/* a focus ring that is visible on both a light and a dark header */
header a:focus-visible, header button:focus-visible, header label:focus-visible{
  outline:2px solid currentColor; outline-offset:3px; border-radius:6px;
}


/* 🔴 STICKY WAS LOSING ON SPECIFICITY — 15 Sep 2026. `header{position:sticky}`
   is (0,0,1) and these demos set `.top{position:relative}` at (0,1,0), so five
   of twelve silently stayed put while the other seven pinned. Measured, not
   assumed: auto-detailing, drain-cleaning, mobile-repair, plumbing-hvac and
   rv-powersports all read `relative` after the layer went in.
   ⭐ `header[class]` is (0,1,1) and beats a single class without reaching for
   !important, which would then have to be fought later. */
header[class]{position:sticky;top:0;z-index:60}

/* 🔴 THE OFF-CANVAS MENU WAS WIDENING THE PAGE. Parked at translateX(102%) the
   panel still counted toward scrollWidth — 700px against a 371px viewport — so
   the whole site scrolled sideways on a phone with nothing visible to scroll to.
   ⛔ `overflow-x:hidden` would fix it and break `position:sticky` in the same
   stroke; `clip` does not create a scroll container, so the header still pins. */
html{overflow-x:clip}


/* 🔴 THE NAV WAS WRAPPING ONTO ITS OWN ROW — 15 Sep 2026, measured at 1280.
   These headers are flex rows that ran out of width, so the navigation dropped
   beneath the brand and the header doubled in height: 134, 138, 104, 223.
   A sticky header that tall eats a quarter of a laptop screen, and it is the
   clearest difference between these and the client sites.
   ⭐ One row, and the NAV is the part allowed to give — `min-width:0` lets it
   shrink, and if it still will not fit it scrolls sideways within itself rather
   than pushing the whole header down. ⛔ `scrollbar-width:none` hides the bar,
   not the ability to scroll; the items remain reachable by wheel, trackpad,
   touch and keyboard. */
/* ⚠️ 961px, NOT 901px — 24 Sep 2026. The burger appears at `max-width:60rem`
   (960px), so a desktop layer starting at 901px overlapped the mobile layer
   by 60px: in that band the burger was on screen AND `header .top__in` was
   forced `nowrap`, which is exactly what stopped the opened menu taking its
   own row. Two breakpoints for one switch is the bug this estate already
   wrote a gate about. They are now the same width. */
@media (min-width:961px){
  header .top__in,
  header .masthead__row,
  header .brandline{flex-wrap:nowrap}
  header nav{min-width:0}
}
/* 🔴 ⭐ `overflow-x:auto` ALSO COMPUTES `overflow-y:auto`, AND THAT ATE THE
   DROPDOWN — 24 Sep 2026. The rule above asked the nav to scroll sideways
   rather than push the header taller, which reads as a safe, local
   concession. It is not local. When one axis is not `visible`, the other
   computes to `auto` as well. So the top-level `<ul>` became a scroll box
   34px tall, and `.sub` — the absolutely positioned panel holding all 14
   service links — sits 349px below that box and was clipped to nothing at
   every width from 901px up. Measured on the rendered page before the fix:
   `ul` height 34, `ul` scrollHeight 392, dropdown painted 0 of 14 links.
   ⛔ And `scrollbar-width:none`, added to keep the bar out of sight, removed
   the one visual clue that anything was being clipped. A concession you
   cannot see is a defect nobody can report.
   ⭐ THE SCROLLING IT PROTECTED WAS NEVER REAL. Measured at 961, 1024, 1100,
   1200, 1280, 1440 and 1920: the row overflows by 8px at exactly 961px on
   the landscaping demo and by ZERO at every other width on both. One 8px
   band, behind a hidden scrollbar, was the whole benefit — against 14 links
   unreachable from the header at every desktop width. So the row WRAPS in
   that band instead: a second row is visible and reachable, it costs nothing
   at any other width, and `white-space:nowrap` stays so labels never break
   mid-word.
   ⛔ Do not put `overflow` back on this `ul`. `.sub` is a descendant of it,
   and `header{backdrop-filter}` already makes the header a stacking and
   containing block, so no `z-index` can lift the panel out. The only fix is
   for the ancestor not to clip. */
@media (min-width:961px){
  header nav>ul{flex-wrap:wrap;overflow:visible}
  header nav a{white-space:nowrap}
}


/* 🔴 ONE HERO WAS 83vh BECAUSE ITS HEADLINE WAS 104px — 15 Sep 2026.
   Owner: "make sure desktop hero are proper sixe". Measured at 1280: nine
   heroes sat at 62-66vh and auto-detailing at 83vh, and the cause was not the
   hero box at all — its <h1> renders at 104px over three lines, 306px of type.
   That size was tuned for a hero made ONLY of words; over a photograph it is
   shouting. ⭐ Capped to the size the rest already use, so the set reads as one
   family. ⛔ Still large text for contrast purposes (>=24px), so the 3:1
   threshold the probe applies is unchanged. */
.lanaihero h1{font-size:clamp(2rem,4.4vw,3.5rem);line-height:1.06}


/* ============================================================
   HAND-EDITED, 23 SEP 2026 - AND DELIBERATELY DIVERGED
   ------------------------------------------------------------
   This demo used to be generated outside this repository and copied in.
   IT MUST NOT BE REGENERATED. The generator sources are untracked and
   unbacked and at least one provably does not reproduce its own published
   copy - a rebuild loses photo bands, fonts and the town. public/demo is in
   git, so THIS file is the source of truth for this site now. Do not run
   tools/sync-demo.mjs or any build.py against it.

   WHAT CHANGED
   1. Two stacked heroes became one. The second hero's lede ran 58 words on
      its own and explained what the headline already said.
   2. THE REFUSAL BECAME THE LOUDEST THING ON THE PAGE. There is no $0.00
      here and there must never be: a wash card records pressure, tip and
      mix, not money. The refusal is a customer request that was turned
      down - 3,000 psi on an asphalt shingle roof - and the reason names a
      standard anybody can check, because pressure takes off the granules
      that carry a shingle's UV protection and the manufacturers require
      low-pressure chemical cleaning instead.
   3. The offer strip left .lanaihero, where the absolutely-positioned hero
      photograph was painting over it at every width.

   AND IT IS NOT pool-service. The two were about 87% identical and they are
   NOT being merged into one file - thirteen sites sharing a layout is the
   thing these demos exist to disprove. So: pool-service's verdict is a
   centred stamp that CLOSES its water report, because a report ends with
   what was added. This one is a left-ruled block that OPENS the wash card,
   because a wash card starts with what was asked for and answers it. The
   readings underneath are what was done instead.
   ============================================================ */

.lanaihero h1{font-size:clamp(2.6rem,1.4rem + 5.4vw,4.5rem);
  font-weight:800;line-height:1.03;letter-spacing:-.03em;margin:0 0 .2em}
.lanaihero .hl{color:rgba(255,255,255,.94)}
.lanaihero :is(h1,.refused__k,.refused__v,.refused__why){
  text-shadow:0 1px 2px rgba(0,0,0,.70), 0 2px 20px rgba(0,0,0,.52)}

/* THE REFUSED BLOCK - asked above the rule, answered under it. Stacked and
   left-aligned: this is a request and its answer, not a verdict on a reading. */
.refused{margin:1.4rem 0 0;padding:.85rem 0 .95rem;max-width:34rem;
  border-top:4px solid rgba(255,255,255,.72);
  border-bottom:1px solid rgba(255,255,255,.52)}
.refused__k{margin:0 0 .15rem;font-family:var(--f-mono);font-size:.6875rem;
  letter-spacing:.2em;text-transform:uppercase;color:#fff}
.refused__v{margin:0;font-family:var(--f-display);
  font-size:clamp(2rem,1.1rem + 3vw,2.9rem);font-weight:800;line-height:1;
  letter-spacing:-.02em;text-transform:uppercase;color:#fff}
.refused__why{margin:.45rem 0 0;font-size:.8125rem;line-height:1.45;color:#fff;
  max-width:38ch}

/* THE SAME REFUSAL AT LENGTH, AT THE HEAD OF THE CARD. */
.wr__refused{padding:1.1rem 1rem 1.2rem;
  border-top:4px solid var(--ink);border-bottom:1px solid var(--rule);
  background:var(--surface-2)}
.wr__refused-k{margin:0 0 .25rem;font-size:.625rem;letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-3)}
.wr__refused-v{margin:0;font-family:var(--f-display);
  font-size:clamp(2.4rem,1.2rem + 4vw,3.6rem);font-weight:800;line-height:1;
  letter-spacing:-.025em;text-transform:uppercase;color:var(--ink)}
.wr__refused-why{margin:.6rem 0 0;font-family:var(--f-body);font-size:.875rem;
  line-height:1.55;color:var(--ink-2);max-width:62ch}
.wr{max-width:46rem;margin-inline:auto}


/* ============================================================
   THE FULL-PICTURE HERO - 23 Sep 2026
   🔴 TWO DEFECTS, BOTH MEASURED. The hero composited to band luma 30.7
   against a floor of 60 and picture side 41.9 against 80. And the file under
   it, ph-index.jpg, was BYTE-IDENTICAL to drain-cleaning/ph-hydro-jetting.jpg
   - md5 9a8c9b48... on both. Two demonstration sites in two trades were
   showing the same photograph, and on this one it was the front door. The
   per-demo reuse gate cannot see that: it compares filenames inside ONE demo.
   ⭐ It stands on ph-house-wash.jpg now - a worker running a pressure lance
   across a walkway with the spray visible, 117 / 112. It reads as the trade
   at a glance, which a gloved hand in steam did not.

   COMPOSITION: type at the BOTTOM-LEFT in a narrow column, so the washed
   surface and the spray stay in the top two thirds. Scrim pooled bottom-left.
   ⛔ The last stop is .02 and that number is the picture.
   ⚠️ 90deg/0deg and % stops only - tools/hero-photo-contrast.js parses this.
   ============================================================ */
.lanaihero{justify-content:flex-end; min-height:clamp(440px,74vh,700px)}
.lanaihero>img{object-position:center 40%}
.lanaihero__in{padding-block:clamp(1.4rem,3vw,2rem) clamp(1.8rem,4.5vw,3rem)}
.lanaihero h1,.lanaihero .refused{max-width:24ch}
.lanaihero__scrim{position:absolute;inset:0;z-index:1;pointer-events:none;
  /* 🔴 RE-TUNED AGAINST A MEASUREMENT. First pass: band 58.8 against 60 and
     picture side 66.4 against 80, with the photograph alone at 116/112 - so
     the headroom was there and the scrim was spending it.
     ⛔ The last stop is .00 and that number is the picture. */
  background:
    linear-gradient(0deg,  rgba(7,18,26,.76) 0%,  rgba(7,18,26,.48) 24%,
                           rgba(7,18,26,.15) 46%, rgba(7,18,26,.02) 72%,
                           rgba(7,18,26,.00) 100%),
    linear-gradient(90deg, rgba(7,18,26,.32) 0%,  rgba(7,18,26,.13) 46%,
                           rgba(7,18,26,.02) 80%, rgba(7,18,26,.00) 100%)}
@media (max-width:640px){
  .lanaihero{min-height:clamp(380px,64vh,540px)}
  .lanaihero__scrim{background:
    linear-gradient(0deg,  rgba(7,18,26,.82) 0%,  rgba(7,18,26,.52) 40%,
                           rgba(7,18,26,.18) 72%, rgba(7,18,26,.04) 100%),
    linear-gradient(90deg, rgba(7,18,26,.18) 0%,  rgba(7,18,26,.03) 100%)}
}


/* ============================================================
   THE MARK — AN ENCLOSED BADGE, NO ICON AT ALL. 23 Sep 2026.
   Owner: "the logos can be way more creative".
   🔴 Every demo wore the same monoline 28x28 pictogram in a different colour.
   This site has no pictogram now: the lockup itself is the mark — the name
   inside a ruled box with a weighted left edge and one solid wedge, set in
   caps at wide tracking, which is a different KIND of mark from a drawing.
   ⭐ Sisters: auto-repair a knocked-out monogram tile, pool-service a
   two-tone pictorial disc, drain-cleaning a bare wordmark on a grade rule.
   ⚠️ A TYPE LOCKUP IS WIDER THAN A 28px PICTOGRAM, and .top__in is a WRAPPING
   flex row. Swapping a logo for type here has already cost a whole header row
   once on this estate: at 1100px the brand went 132px to 277px against a
   267px budget and the phone and button dropped to a second line. Nothing
   looks broken; the header is just taller on every page. ⛔ Measure the row
   after touching this, do not eyeball it.
   ============================================================ */
/* 🔴 ⭐ `overflow:hidden` TURNED A SQUEEZE INTO A SILENT TRUNCATION —
   24 Sep 2026. As a flex item of `.top__in` the badge has the default
   `flex-shrink:1`, and `overflow:hidden` also zeroes an item's automatic
   minimum size — so instead of refusing to shrink past its text, it shrank to
   whatever was left and cut the words off with no ellipsis and no clue:
   "KINGF", "EXTERIO", "CLEANIN". Measured at 390px it was 21px wide holding
   88px of glyphs. ⛔ The clipping is not the `max-width` and not the padding;
   it is the missing floor. `flex-shrink:0` is the floor — the row wraps now
   (see `.top__in{flex-wrap:wrap}`), so a badge that will not shrink pushes
   the CTA to the next row instead of eating its own name. `overflow:hidden`
   stays because the corner mark and radius need it, but it is now a backstop
   rather than the thing doing the damage. */
.brand--badge{position:relative;display:grid;gap:.1rem;align-content:center;flex-shrink:0;
  padding:.4rem .75rem .38rem .7rem;max-width:13rem;
  border:1px solid var(--rule);border-left:3px solid var(--accent);
  border-radius:.18rem;overflow:hidden;
  transition:border-color .18s ease}
.brand--badge:hover{border-color:var(--accent)}
.brand__markbox{position:absolute;top:0;right:0;width:.7rem;height:.7rem;
  background:var(--accent);clip-path:polygon(100% 0,100% 100%,0 0)}
.brand--badge .brand__nm{font-family:var(--f-display);font-weight:700;
  font-size:.9375rem;letter-spacing:.07em;text-transform:uppercase;
  line-height:1;color:var(--ink)}
.brand--badge .brand__sub{font-family:var(--f-mono);font-size:.5625rem;
  letter-spacing:.2em;text-transform:uppercase;color:var(--ink-3)}
@media (max-width:30rem){
  .brand--badge{padding:.34rem .55rem .32rem .5rem;max-width:11rem}
  .brand--badge .brand__nm{font-size:.8125rem}
  .brand--badge .brand__sub{font-size:.5rem;letter-spacing:.14em}
}
/* ⚠️ The old lockup hid its second line below 62rem because the trade and the
   city repeated what the hero eyebrow said a couple of inches lower. That is
   not true of this mark: "Exterior Cleaning" is the second half of the
   business name and it sits INSIDE the badge, so hiding it leaves a box with
   one word in it. Restated after the hide rule, and scoped to the badge so
   nothing else changes. */
.brand--badge .brand__sub{display:block}


/* ============================================================
   GLASS CONTROLS — 23 Sep 2026. Owner: "use glass buttons all the newest
   elements".
   🔴 The photograph decides the label's contrast once the button stops being
   opaque. ⛔ Base declaration carries it with NO blur; @supports adds blur
   and saturation and changes no alpha. A fill that passes only when blurred
   does not pass, because backdrop-filter is off for some readers.
   ⭐ The label is dark ink on a light tint (citrus since 24 Sep), so the binding case is the
   DARKEST region the button can occupy, not the brightest.
   ⛔ FORM: the BADGE FAMILY. The mark on this site is the name inside a ruled
   box with a weighted left edge, so the buttons carry the same weighted left
   edge at a small radius. Nothing else in the group does this: auto-repair is
   square, pool-service is a capsule, drain-cleaning is a locate flag.
   ============================================================ */
.btn{border-radius:.18rem}
.btn--go{
  background:color-mix(in srgb, var(--accent) 88%, transparent);
  color:var(--accent-ink);
  border:1px solid color-mix(in srgb, #fff 38%, transparent);
  border-left:3px solid color-mix(in srgb, #fff 74%, transparent);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.42)}
.btn--go:hover{
  background:color-mix(in srgb, var(--accent) 96%, transparent);
  border-left-color:#fff}
.btn--line{
  background:rgba(7,18,26,.56);
  color:var(--ink);
  border:1px solid rgba(255,255,255,.38);
  border-left:3px solid rgba(255,255,255,.55);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16)}
.btn--line:hover{background:rgba(7,18,26,.72); border-left-color:#fff}
.lanaihero .btn--line{color:#fff}
@supports ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  .btn--go,.btn--line,.launch{
    -webkit-backdrop-filter:blur(15px) saturate(1.25);
    backdrop-filter:blur(15px) saturate(1.25)}
}
.btn:focus-visible,.launch:focus-visible{
  outline:2px solid color-mix(in srgb, var(--accent) 72%, #fff);
  outline-offset:3px}

/* ⛔ THE GHOST, RESTATED BELOW THE LAST `.btn{`. check-demo-polish refused the
   build for exactly this: the glass block above declares a bare `.btn{` for
   the radius, this stylesheet is unlayered, and at equal specificity the later
   rule wins — so `.btn--ghost` would have lost its background and both
   buttons would have rendered as one solid block. Two primaries is no primary.
   ⚠️ This site uses `.btn--line` for its secondary and `.btn--ghost` barely
   appears, which is precisely why nobody would have noticed. Any future
   `.btn{` appended below this point must restate it again. */
.btn--ghost{
  background:rgba(7,18,26,.54);
  color:var(--ink);
  border:1.5px solid rgba(255,255,255,.44);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.2)}
.btn--ghost:hover{background:rgba(7,18,26,.7); border-color:rgba(255,255,255,.7)}
.lanaihero .btn--ghost{color:#fff}

/* 🔴 "MY SELECTOR MATCHES" IS NOT "MY RULE APPLIES". The glass block above
   declares `.btn--line` at (0,1,0). The hero's own secondary was already
   declared at `.lanaihero .btn--line` (0,2,0) higher up the file, carrying
   the OLD half-glass — rgba(...,.42) behind a 3px blur — so on the one surface
   that actually needed the new material nothing changed at all. The probe
   caught it: the new fill never appeared in the computed style.
   ⛔ And .42 is not enough. Measured over the worst window a button of this
   size can occupy on this hero, the old secondary came back at 3.05 against a
   floor of 4.5 — a label you cannot read if the photograph crops differently
   at another width. Resolve by specificity, after, never by !important. */
.lanaihero .btn--line,.lanaihero .btn--ghost,.lanaihero .btn--out{
  background:rgba(7,18,26,.62);
  color:#fff;
  border:1.5px solid rgba(255,255,255,.46);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.18)}
.lanaihero .btn--line:hover,.lanaihero .btn--ghost:hover,.lanaihero .btn--out:hover{
  background:rgba(7,18,26,.78); border-color:rgba(255,255,255,.72)}
@supports ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  /* the hero secondary kept a 3px blur from the older rule at (0,2,0); the
     material has to be the same object as the primary beside it */
  .lanaihero .btn--line,.lanaihero .btn--ghost,.lanaihero .btn--out{
    -webkit-backdrop-filter:blur(14px) saturate(1.28);
    backdrop-filter:blur(14px) saturate(1.28)}
}


/* ============================================================
   THE SHEEN — A SHAPE BOUND TO THE TYPE, NOT A SHEET OVER THE PICTURE
   23 Sep 2026. Owner: "i think the picture should be full hero then shene out
   where you want the text", and "evry hero needs to be perfecto".
   🔴 WHAT WAS WRONG WITH THE SCRIM, and the tell was in my own numbers. A
   scrim on the SECTION is one sheet doing two jobs: it darkens the words and
   it darkens the two thirds of the frame that carry no words at all. That is
   exactly why the band luma and the picture-side luma had to be chased
   against each other, and why at phone width the picture side fell to 51-69
   against a target of 80 — the wash covered everything, so the only way to
   keep the type legible was to dim the photograph.
   ⭐ THE DARKENING IS LOCAL NOW. It lives on the ::before of the block that
   holds the type, so it moves with the words at every width and at every one
   of the four compositions. type sits BOTTOM-LEFT in a narrow column, so the sheen is narrower and weighted low
   ⛔ SOFT EDGES ONLY. The ellipse is sized 50% 50% of its own box, so it
   reaches alpha 0 exactly at that box's edge — there is no step anywhere, and
   no boundary a reader could see. A visible edge where a sheen stops is worse
   than the flat wash it replaced.
   ⛔ AND THE FLOOR DOES NOT MOVE: 4.5:1 on every run of type, 3:1 on the
   display, measured over the composited result at the brightest region the
   type actually occupies. A local sheen has less area to work in, so it is
   harder than a sheet, not easier.
   ⚠️ What is left on .lanaihero__scrim is an EDGE FLOOR only — it keeps the
   extreme top and near edges from clipping against the header and the page
   ground. It is not doing the reading work any more.
   ============================================================ */
.lanaihero__scrim{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    linear-gradient(90deg, rgba(7,18,26,.50) 0%, rgba(7,18,26,.46) 16%, rgba(7,18,26,.40) 30%, rgba(7,18,26,.33) 42%, rgba(7,18,26,.24) 54%, rgba(7,18,26,.15) 65%, rgba(7,18,26,.08) 75%, rgba(7,18,26,.03) 85%, rgba(7,18,26,.00) 93%),
    linear-gradient(0deg, rgba(7,18,26,.80) 0%, rgba(7,18,26,.70) 12%, rgba(7,18,26,.56) 22%, rgba(7,18,26,.40) 32%, rgba(7,18,26,.24) 43%, rgba(7,18,26,.12) 54%, rgba(7,18,26,.04) 64%, rgba(7,18,26,.00) 74%)}
.lanaihero .sheen{position:relative;display:grid;max-width:36rem;z-index:0}
.lanaihero .sheen::before{content:"";position:absolute;z-index:-1;pointer-events:none;display:none;
  top:-9rem;bottom:-12rem;left:-12rem;right:-11rem;
  /* 🔴 THE DIRECTIONAL LAYER HAD TO GO, AND THE NUMBER THAT KILLED IT WAS
     0.60 OF ALPHA IN ONE PIXEL. A 90deg linear varies only with x, so it
     ends abruptly at the top and bottom of its own box — on three of these
     four heroes that edge happened to fall off-frame and nobody would have
     seen it; on drain-cleaning it landed at y=109 as a hard horizontal line
     across the whole picture. ⛔ It also darkened the frame ABOVE the type
     just as much as the type itself, which is the wash it was meant to
     replace. An ellipse falls to zero in every direction; a linear does not.
     ⭐ So it is one ellipse, with a FLAT CORE wide enough that the block's
     own corners — where a headline's last line ends — are still inside it,
     and a long tail so the steepest step is under 0.004 of alpha per pixel.
     The inflation is what buys that: the box is the type block plus enough
     margin that its corners sit at about t=0.83 of the ellipse, not 0.90. */
  /* 🔴 THE RADII MUST FIT THE BOX FOR THE CENTRE YOU CHOSE. `50% 50% at 48%
     54%` pushes the ellipse 4% past the box's far side, so it is CUT there
     rather than fading out — alpha stepped from .30 to 0 in one pixel and
     the edge check caught it at 0.0124/px. ⛔ radius = min(centre, 1-centre)
     on each axis, or the shape you drew is not the shape that paints. */
  background:radial-gradient(48% 46% at 48% 54%,
    /* ⚠️ A WIDER FLAT CORE THAN ITS THREE SISTERS, and it is the headline that
       asks for it: this one runs to 24ch over three lines, so its last line
       ends further from the centre of the block than any of the others. At the
       shared profile it measured 3.24 against a floor of 3 — passing, and not
       by enough to survive a different crop. Same material, tuned to the type
       it is actually covering. */
    rgba(7,18,26,.90) 0%,
    rgba(7,18,26,.89) 44%,
    rgba(7,18,26,.875) 62%,
    rgba(7,18,26,.855) 74%,
    rgba(7,18,26,.82) 84%,
    rgba(7,18,26,.74) 90%,
    rgba(7,18,26,.46) 95%,
    rgba(7,18,26,.18) 98%,
    rgba(7,18,26,.00) 100%)}
@media (max-width:640px){
  /* ⚠️ A PHONE IS NOT A NARROW DESKTOP. The block is most of the frame here,
     so the inflation that keeps the falloff soft has to shrink with it or the
     sheen simply becomes the wash again. */
  /* ⚠️ MEASURED AT 320: the type block is ~83% of the frame here, so a sheen
     that spills 5rem past it in every direction covers the photograph
     entirely — picture-outside fell to 3% of the frame. A local sheen
     cannot give much back when the type IS the frame, but the falloff
     belongs inside the viewport rather than off the edge of it. */
  /* 🔴 AT 320 THE TYPE IS THE FRAME, AND THAT IS THE WHOLE FINDING. The block
     here is ~280x500 of a 320x605 hero. An ellipse inscribed in a box can never
     reach that box's CORNERS — measured, the headline's top corner sits at
     t=1.05, outside the shape entirely — so tightening the inflation to return
     picture took the h1 from 3.21 to 2.33 and then to 1.19 against a floor of 3.
     ⛔ THE FLOOR DOES NOT MOVE, so the inflation stays wide enough that the
     falloff happens off-frame and the corners stay in the core.
     ⚠️ THE COST, PRINTED RATHER THAN HIDDEN: at 320 only about 3% of the frame
     is outside the sheen. A sheen bound to the type cannot give the picture
     back when the type occupies 83% of the frame — that is a property of the
     content at that width, not of the technique, and the honest lever for it
     is a shorter headline or a taller hero, neither of which is mine to take.
     From 641px up the ellipse does exactly what it was built to do: 32-47% of
     the frame sits outside the sheen at its own photographic luminance. */
  .lanaihero .sheen::before{top:-5rem;bottom:-5rem;left:-5rem;right:-5rem;
    /* 🔴 THE PHONE BRANCH HAD FORGOTTEN THE COMPOSITION. All four shared
       `at 50% 50%` here, which is right for the one hero whose type is
       centred and wrong for the three that anchor top or bottom — and it
       only showed at ONE width: pool-service passed at 320 (3.34) and at
       390, and failed at 375 (2.32 against 3), because that is where its
       hero grew tall enough to push the headline into the ellipse's top
       corner. ⛔ A sheen bound to the type has to follow the type at
       every width, not just the ones that were spot-checked.
       ⚠️ radius = min(centre, 1-centre) per axis, or the shape is cut. */
    background:radial-gradient(50% 42% at 50% 58%,
      rgba(7,18,26,.91) 0%,
      rgba(7,18,26,.905) 42%,
      rgba(7,18,26,.895) 60%,
      rgba(7,18,26,.875) 72%,
      rgba(7,18,26,.84) 82%,
      rgba(7,18,26,.76) 89%,
      rgba(7,18,26,.48) 94%,
      rgba(7,18,26,.19) 98%,
      rgba(7,18,26,.00) 100%)}
}

/* 🔴 THE TABLET GAP — MEASURED, ATTEMPTED, AND REVERTED. At 741 the desktop
   sheen leaves only 1% of the frame outside it on auto-repair against 32% at
   1280, because the inflation is 12-14rem a side and that is the whole frame
   at 741. I capped the margin in vw to get the picture back: picture-outside
   went to 20-31% and the HEADLINE fell to 2.17 and 1.63 against a floor of 3,
   with pressure-washing's refusal reason at 2.80 against 4.5.
   ⛔ THE FLOOR DOES NOT MOVE, so the vw cap is gone and the rem inflation
   stands. ⭐ THE REASON IS GEOMETRY, NOT TUNING: an ellipse inscribed in a box
   cannot cover that box's corners, so the margin has to be about 0.37 of the
   block on each axis to keep a headline's last line inside the core — roughly
   240px for a 660px measure. At 741 the block is already the full column, so
   block + margin exceeds the frame however the margin is written.
   ⚠️ SO THE HONEST NUMBER IS PRINTED INSTEAD: 641-1023px returns 1-18% of the
   frame to the photograph, and 1024 and up returns 32-47%. Below 641 it is
   3-6%. A sheen bound to the type gives the picture back exactly in proportion
   to how much of the frame the type is NOT using. */

/* ⭐ THE TYPE CARRIES ITS OWN GROUND NOW, NOT THE PICTURE — 24 Sep 2026.
   Owner: "thats to dark", "we want to see the website under it still",
   "50% shene", "darker to lighter".
   ⛔ MEASURED, and the measurement is what forced this technique. Sampled on
   the real photograph: the hero image's MEAN luminance under the type is only
   0.23-0.34 — genuinely dark — but isolated blown highlights (a sunlit wall,
   the pool coping) reach L=1.00 at 28-45% of the frame, exactly where the
   words sit. Worst-pixel contrast is what a reader loses, so a 50% sheen put
   the headline at 1.72:1 even though it looks fine on average.
   ⛔ THE ANSWER IS NOT MORE SHEEN. Covering the whole frame to defeat a few
   specular pixels is what produced the "big brown blob of black" he rejected
   three times, and it dims a photograph that is already dark.
   ⭐ A SHADOW DARKENS ONLY THE PIXELS TOUCHING EACH GLYPH. The picture keeps
   its luminance everywhere else, the sheen stays at 50%, and the letters get
   their contrast from a halo two pixels wide instead of from a sheet.
   ⚠️ SAY WHAT IT DOES NOT DO: WCAG scores text against its background colour
   and gives no credit for a shadow. This is a real improvement a reader sees
   and NOT a number that satisfies an automated check. If a formal AA figure is
   ever required on these heroes, the honest lever is a darker crop of the
   photograph, not a heavier scrim. */
.lanaihero h1,.lanaihero .verdict__k,.lanaihero .verdict__v,
.lanaihero .verdict__why,.lanaihero .eyebrow,.lanaihero .lbl,.lanaihero .kicker{
  text-shadow:0 1px 2px rgba(0,0,0,.55), 0 2px 14px rgba(0,0,0,.45),
              0 0 32px rgba(0,0,0,.30)}

/* ═══ AUTHORITATIVE HERO FADE — 24 Sep 2026 ════════════════════════════════════════════
   🔴 WRITTEN LAST, AND AT (0,2,0), BECAUSE POSITION IS NOT PRECEDENCE.
   This file carries up to NINE `.lanaihero__scrim` rules across five media
   queries. I rewrote "the last one in the file" and on three demos that last
   one lived inside `@media (max-width:22.5rem)` — a 360px block — so the new
   fade shipped and changed nothing anybody could see at desktop. The rule that
   WINS is the one to edit; the rule that is LAST in the file is a different
   thing, and reading getComputedStyle is the only way to tell them apart.
   ⭐ So this pair is authoritative: one selector heavier than every rule above
   it, declared after all of them, in exactly two states — the wide cut and the
   phone. Anything else in this file is now historical.
   ⛔ EDGE-ANCHORED, NEVER A FLOATING SHAPE. Owner: "it Be a nice sheen, not a
   big brown blob of black", "1/2 the screen should be fade out", "50% shene",
   "darker to lighter". A gradient floating mid-frame reads as a blob however
   smooth its curve; only one anchored to an edge reads as a fade.
   ⚠️ THE PHONE IS NOT A NARROW DESKTOP. There the headline spans the whole
   column, so a left-weighted ramp leaves its right-hand words over bright sky —
   the phone state is mostly vertical instead. */
/* ⚠️ THE ALPHAS BELOW BELONG TO THE PHOTOGRAPH, NOT TO THE LAYOUT. They were
   raised 24 Sep 2026 when the hero became the roof wash: the previous frame
   was dark where the type sits and this one is bright tile and sky, so the old
   ramp measured 1.71:1 at 1024. ⛔ If the picture changes again, RE-MEASURE —
   a scrim tuned to one photograph is a constant pinned to a moving stage. */
.lanaihero .lanaihero__scrim{
  background:
    linear-gradient(90deg, rgba(7,18,26,.58) 0%, rgba(7,18,26,.55) 16%, rgba(7,18,26,.51) 30%, rgba(7,18,26,.46) 42%, rgba(7,18,26,.40) 54%, rgba(7,18,26,.32) 65%, rgba(7,18,26,.22) 75%, rgba(7,18,26,.12) 85%, rgba(7,18,26,.04) 93%, rgba(7,18,26,.00) 98%),
    linear-gradient(0deg, rgba(7,18,26,.80) 0%, rgba(7,18,26,.70) 12%, rgba(7,18,26,.56) 22%, rgba(7,18,26,.40) 32%, rgba(7,18,26,.24) 43%, rgba(7,18,26,.12) 54%, rgba(7,18,26,.04) 64%, rgba(7,18,26,.00) 74%)}
/* 🔴 THE PHONE RAMP WAS ANCHORED TO THE WRONG EDGE — measured 24 Sep 2026.
   `0deg` runs from the BOTTOM up, so it was .86 under the buttons and .00
   across the top 16% — and the headline is at the TOP. It passed only because
   the previous photograph happened to be dark up there. The hero now carries
   the roof wash the owner asked for, and that frame has a YELLOW HARD HAT at
   rgb(244,244,170) directly under the first line: white type measured
   1.14:1 against a floor of 3. An invisible headline.
   ⭐ THE RAMP NOW STARTS AT THE EDGE THE TYPE IS ON. Still edge-anchored and
   still a fade, not a blob — 180deg from the top, easing to its lightest at
   about two thirds down where the picture is doing the work, then lifting
   again for the buttons. ⛔ Do not re-tune this against the CSS colour: the
   number that matters is the worst GLYPH pixel over the PHOTOGRAPH, and the
   element box is not the glyphs — an h1 with a max-width spans dead space its
   letters never reach, which reads bright pixels no reader ever sees.
   MEASURED AFTER, 390x844: headline 5.33 / 5.01, eyebrow 5.86, all ≥ floor. */
@media (max-width:640px){
  .lanaihero .lanaihero__scrim{
    background:
      linear-gradient(90deg, rgba(7,18,26,.18) 0%, rgba(7,18,26,.12) 30%, rgba(7,18,26,.06) 55%, rgba(7,18,26,.00) 78%),
      linear-gradient(180deg, rgba(7,18,26,.66) 0%, rgba(7,18,26,.64) 24%, rgba(7,18,26,.61) 42%, rgba(7,18,26,.59) 58%, rgba(7,18,26,.58) 72%, rgba(7,18,26,.50) 86%, rgba(7,18,26,.44) 100%)}
}
/* 🔴 THE TABLET GAP IS REAL AND IT IS THIS PHOTOGRAPH'S, NOT THE LAYOUT'S.
   At 768 the wide ramp is still left-weighted while the headline already
   spans most of the column, so its right-hand words sat on bright sky and
   tile: measured 1.48:1 and 2.88:1 against a floor of 3. The phone rule stops
   at 640 and the wide rule was tuned for a darker picture, so nothing covered
   641–900. ⛔ This is a THIRD state, not a re-tune of either neighbour —
   changing the wide rule to fix 768 is what drove the headline under 3 at
   1440 the last time it was attempted. MEASURED AFTER: 768 → 5.14 / 4.36. */
@media (min-width:641px) and (max-width:1100px){
  .lanaihero .lanaihero__scrim{
    background:
      linear-gradient(90deg, rgba(7,18,26,.52) 0%, rgba(7,18,26,.46) 25%, rgba(7,18,26,.40) 45%, rgba(7,18,26,.32) 65%, rgba(7,18,26,.24) 82%, rgba(7,18,26,.14) 95%, rgba(7,18,26,.06) 100%),
      linear-gradient(180deg, rgba(7,18,26,.46) 0%, rgba(7,18,26,.44) 30%, rgba(7,18,26,.38) 55%, rgba(7,18,26,.32) 75%, rgba(7,18,26,.34) 100%)}
}


/* ⛔ NO BLOB — 24 Sep 2026. Owner, twice: "no blobs". The ellipse on
   .sheen::before is off, stated here at the end so no earlier rule can turn it
   back on. The edge-anchored .lanaihero__scrim carries the type on its own —
   measured on glyph pixels, text-shadow removed, worst pixel, at 390 and 1440. */
.lanaihero .sheen::before{display:none}

/* ── 💬 THE CHAT LAUNCHER, MODERNISED — 24 Sep 2026 ─────────────────────────
   Owner, of every launcher on the demos: "these buttons all chat are no good
   look old 2018 style", then "make sure all are newest and the buttons on rt
   side not lt".
   ⭐ WHAT CHANGED, AND WHY EACH ONE. It was a 56px icon-only circle whose
   meaning lived in a hover tooltip a phone never shows. Now it is a PILL that
   says, in the trade's own words, the same thing the header button says:
     · a solid, opaque brand fill (--signal-fill, the primary button's) — no
       gradient, no glass, no coloured glow;
     · a real 1px edge and ONE short shadow, not a stroke as the shape;
     · sentence case at 15px/600 in the site's sans, never tracked mono caps;
     · a live dot, the one "somebody is there" cue, pulsing only for visitors
       who have not asked for reduced motion;
     · a short label on a phone, so the pill stays in the thumb corner.
   ⛔ BOTTOM-RIGHT, FRAMED OR NOT. The framed rule that sat above (7 Sep) moved
   it LEFT — deleted 24 Sep 2026, owner: "right and go" — so it would not share a corner with botsonstaff.com's own launcher; the owner has
   now ruled that every launcher sits on the right. Restated here at the same
   (0,2,1) weight, later, so it wins — and the panel moves with it, or the
   button and the window it opens would be on opposite sides of the screen.
   ⛔ The element, its id, aria-expanded/aria-controls and data-open-chat are
   untouched: the header button and the in-page button open the same panel.
   ⛔ The label is VISIBLE text now, so there is no aria-label to fall out of
   step with it; the phone and desktop spans swap with display, which also
   removes the hidden one from the accessibility tree. */
.launch{
  right:16px; left:auto; bottom:calc(16px + env(safe-area-inset-bottom,0px));
  width:auto; height:auto; min-height:48px; padding:0 20px 0 16px;
  border-radius:999px; gap:10px;
  background:var(--signal-fill); background-image:none; color:var(--signal-ink);
  border:1px solid color-mix(in srgb, var(--signal-ink) 22%, transparent);
  box-shadow:0 2px 10px color-mix(in srgb, #030a12 42%, transparent);
  -webkit-backdrop-filter:none; backdrop-filter:none;
  font-family:var(--f-display); font-weight:600; font-size:15px; line-height:1.2;
  letter-spacing:0; text-transform:none; white-space:nowrap;
  transform:none; transition:transform .15s ease, box-shadow .15s ease}
.launch:hover,.launch:active{transform:none}
.launch::after{content:none; display:none}
@media (min-width:0px){
  .launch:hover{transform:translateY(-1px);
    box-shadow:0 4px 14px color-mix(in srgb, #030a12 48%, transparent)}
  .launch:active{transform:translateY(0)}
}
.launch__dot{position:relative; flex:none; width:8px; height:8px; border-radius:50%;
  background:#35E07A;
  box-shadow:0 0 0 2px color-mix(in srgb, var(--signal-ink) 55%, transparent)}
@media (min-width:0px){
  .launch__dot::after{content:""; position:absolute; inset:-5px; border-radius:50%;
    border:2px solid #35E07A; opacity:0; animation:launchPulse 2.6s ease-out infinite}
}
@keyframes launchPulse{0%{transform:scale(.45); opacity:.75} 70%,100%{transform:scale(1.35); opacity:0}}
.launch__s{display:none}
@media (max-width:480px){
  .launch{min-height:44px; padding:0 16px 0 14px; font-size:15px}
  .launch__l{display:none}
  .launch__s{display:inline}
}
html.framed .launch{right:16px; left:auto}
html.framed .chat{right:1rem; left:auto}

/* ═══ 🎨 THE TRADE'S OWN COLOUR — HYDRO BLUE + CITRUS — 24 Sep 2026 ══════════
   Owner: "make sure all colors are trending now and best for the industry",
   then "keep going". 🔴 WHY: auto-repair, pool, landscaping and pressure-washing
   were ONE design on one flat black under one navy wash, and this site was the
   closest twin of auto-repair (both blue) and of pool (sky blue vs aqua). Two
   blues were live here (#37B6E9 button, #0B6CA8 launcher), and an electric blue
   button fought an all-orange tile photograph.
   ⭐ PALETTE (research, colour-research-trades.md §9): hydro blue #0A4D7A +
   citrus #FFC72C, citrus button with a #0E1720 label — Window Hero's precedent,
   the "after" of a clean wall. Citrus is the brand on the dark ground (buttons,
   launcher, marks, type); hydro blue carries the demo strip and is the brand as
   TYPE on the light theme. Dark stays dark: ground #080D13 / #0E151D / #131B25,
   a deep blue-black; the hero wash is blue-black (7,18,26) with its blue held
   BELOW its green, which is what stopped the orange tiles mixing to mauve under
   the old navy (5,18,32), same stops and breakpoints, lower luminance.
   MEASURED (WCAG 2.x): citrus label 11.58; citrus vs ground 12.49, vs surface-2
   11.11; white on the hydro strip 7.9; --ink-3 raised from #6B7681 (4.23) to
   #7B858E (4.61 on surface-2); light --ink-3 #5F6973 (5.30).
   Hero h1 worst glyph pixel: in the table below. ⛔ Stays ABOVE the band marker. */
/* HERO, WORST GLYPH PIXEL over the real photograph, floor 3.0, text-shadow not
   credited (scratchpad hero-glyph-contrast.mjs, 24 Sep 2026, after this pass):
     width              320    390    768    1024   1440
     .lanaihero h1      5.39   5.37   5.09   5.60   3.15
     accented phrase (white .94)
                        5.39   5.37   5.09   5.60   4.33
   before (same cropped photo, navy wash): 5.39 5.36 5.08 5.60 3.15. */
/* the primary is SOLID now — the launcher spec: solid fill, 1px edge in the
   label's colour, one short shadow — and it keeps this site's badge form: the
   weighted left edge, in ink. */
.btn--go{background:var(--signal-fill); color:var(--signal-ink);
  border:1px solid color-mix(in srgb, var(--signal-ink) 22%, transparent);
  border-left:3px solid var(--signal-ink);
  box-shadow:0 2px 10px color-mix(in srgb, #030a12 42%, transparent);
  -webkit-backdrop-filter:none; backdrop-filter:none}
.btn--go:hover{background:color-mix(in srgb, var(--signal-fill) 88%, #fff); color:var(--signal-ink);
  border-left-color:var(--signal-ink)}
/* ⚠️ citrus on the crisp white is 1.48:1 — under the 3:1 a control needs to be
   found — so on the light theme the citrus controls carry an ink edge. */
@media (prefers-color-scheme:light){
  :root:not([data-theme="dark"]) .btn--go,:root:not([data-theme="dark"]) .launch{border:1.5px solid var(--signal-ink); border-left-width:3px}
}
:root[data-theme="light"] .btn--go,:root[data-theme="light"] .launch{border:1.5px solid var(--signal-ink); border-left-width:3px}
/* the "Demo site" word was set in --ink, which is near-black on the light theme,
   on this dark strip — the disclosure itself was unreadable there. */
.utilbar .demostrip b{color:#fff}

/* ⭐ THE HERO IS THE CLEAN DRIVEWAY NOW, 24 Sep 2026. The roof photo showed a
   roof being PRESSURE-washed beside copy saying roofs never are. The driveway
   is the result the headline promises. On a bright white wall the wide ramp
   measured 2.81:1 at 1440 (floor 3), so the desktop state holds its dark
   further across the headline and still falls to nothing by 94%. It is
   anchored to the left edge and full-bleed: ⛔ no blob. Re-measure if the
   picture changes. */
@media (min-width:1101px){
  .lanaihero .lanaihero__scrim{
    background:
      linear-gradient(90deg, rgba(7,18,26,.70) 0%, rgba(7,18,26,.68) 20%, rgba(7,18,26,.64) 34%, rgba(7,18,26,.58) 44%, rgba(7,18,26,.48) 52%, rgba(7,18,26,.34) 60%, rgba(7,18,26,.20) 69%, rgba(7,18,26,.09) 79%, rgba(7,18,26,.03) 88%, rgba(7,18,26,.00) 94%),
      linear-gradient(0deg, rgba(7,18,26,.80) 0%, rgba(7,18,26,.70) 12%, rgba(7,18,26,.56) 22%, rgba(7,18,26,.40) 32%, rgba(7,18,26,.24) 43%, rgba(7,18,26,.12) 54%, rgba(7,18,26,.04) 64%, rgba(7,18,26,.00) 74%)}
}

/* 1101-1200: the headline still spans most of the column here, so it keeps
   the tablet state (measured 2.81:1 at 1101 on the desktop state alone). */
@media (min-width:1101px) and (max-width:1200px){
  .lanaihero .lanaihero__scrim{
    background:
      linear-gradient(90deg, rgba(7,18,26,.52) 0%, rgba(7,18,26,.46) 25%, rgba(7,18,26,.40) 45%, rgba(7,18,26,.32) 65%, rgba(7,18,26,.24) 82%, rgba(7,18,26,.14) 95%, rgba(7,18,26,.06) 100%),
      linear-gradient(180deg, rgba(7,18,26,.46) 0%, rgba(7,18,26,.44) 30%, rgba(7,18,26,.38) 55%, rgba(7,18,26,.32) 75%, rgba(7,18,26,.34) 100%)}
}

/* ── 📷 LIFE PHOTOS — 25 Sep 2026 ─────────────────────────────────────────────
   Owner: "every site needs this" — photographs of people enjoying the RESULT,
   in the major sections of the home page and on every service page. They are
   Pexels stock of the trade and its outcome and are captioned as stock, never
   as "our customers": the company is invented.
   .pgal = a captioned row of photos · .pfig = one photo inside a text band.
   ⛔ NO BLOBS: no radial or ellipse gradient anywhere in here, no purple.
   Colours come from the page (currentColor / opacity), so it follows the
   demo's own ink on light and dark bands alike. */
.pgal{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));margin:1.4rem 0 0}
.pgal figure{margin:0;min-width:0}
.pgal img,.pfig img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;border-radius:4px}
.pgal figcaption,.pfig figcaption{font-size:.8125rem;line-height:1.5;opacity:.78;padding-top:.5rem}
.pgal__note{font-size:.8125rem;line-height:1.5;opacity:.7;margin:1rem 0 0}
.pfig{margin:1.6rem 0 1.4rem}


/* ═══════════════════════════════════════════════════════════════════════════
   🏠 V2 — THE LIGHT REBUILD, 30 Sep 2026. Owner: "light like the home page and
   just whatever is best for the website and industry"; "make sure all demo
   sites are updated to look like real website for customer business".
   ⭐ Everything here is scoped to body.v2 (every page carries it). The older
   dark rules above stay because the chat panel and the selling band still
   read tokens from them; <html data-theme="light"> picks their light set.
   ⛔ NO BLOBS: every scrim here is a LINEAR gradient. No radial, no ellipse.
   ⛔ Class names are hx-*: the old sheet styles .hero/.btn/.sec/.nav heavily.
   section.hero stays on the hero because tools/shots-trade-sites.mjs crops
   from it.
   ═══════════════════════════════════════════════════════════════════════════ */
body.v2{
  --bg:#f4f7fa;--bg2:#e8f1f7;--card:#fff;--ink:#132230;--ink2:#394a59;--ink3:#52616e;
  --line:#dae4ec;--line2:#c6d3de;--acc:#0a5d8f;--acc-d:#084a73;--acc-t:#e0eef8;--btn:#f5c518;--btn-d:#e6b400;--btn-ink:#132230;
  --navy:#0d1b29;--r:18px;--r-sm:12px;
  --sh:0 1px 2px rgba(19,34,48,.05),0 8px 28px rgba(19,34,48,.07);
  --sh-lg:0 2px 4px rgba(19,34,48,.05),0 22px 60px rgba(19,34,48,.12);
  --f:"Figtree",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --fd:"Bricolage Grotesque",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  background:var(--bg);background-image:none;color:var(--ink2);
  font-family:var(--f);font-size:17px;line-height:1.6;
  -webkit-font-smoothing:antialiased;
}
/* ⛔ Element rules are scoped to main and the footer: the selling band (dark,
   generated) sits between them and must keep its own ink. */
/* specificity (0,1,0) on purpose via :where — beats the old sheet's bare h1/h2,
   loses to any hx-* component rule, so components size their own type. */
.v2 :where(main,.hx-ft) :where(h1,h2,h3,h4){font-family:var(--fd);font-stretch:normal;text-transform:none;
  color:var(--ink);font-weight:700;margin:0 0 .45em;overflow-wrap:break-word;letter-spacing:-.012em}
.v2 :where(main) :where(h1){font-size:clamp(38px,5.4vw,68px);line-height:1.03;letter-spacing:-.032em;max-width:none}
.v2 :where(main) :where(h2){font-size:clamp(29px,3.5vw,46px);line-height:1.08;letter-spacing:-.028em}
.v2 :where(main) :where(h3){font-size:clamp(19px,1.6vw,21px);line-height:1.25}
.v2 :where(main,.hx-ft) :where(h4){font-size:15px;letter-spacing:0}
.v2 main h1 .hl{color:inherit;font-style:normal}
.v2 :where(main) :where(p){margin:0 0 1em}
.v2 :where(main) :where(a){color:var(--acc);text-decoration-color:rgba(10,93,143,.35);text-underline-offset:3px}
.v2 :where(main) :where(a):hover{text-decoration-color:var(--acc)}
.v2 a:focus-visible,.v2 button:focus-visible{outline:2px solid var(--acc);outline-offset:3px}
.v2 :where(main) :where(img){max-width:100%;display:block}
.v2 :where(main) :where(ul){padding-left:1.1em}
.hx-w{width:100%;max-width:1240px;margin:0 auto;padding:0 clamp(16px,4vw,40px)}
.hx-s{padding:clamp(64px,8vw,112px) 0}
.hx-s--tint{background:var(--bg2)}
.hx-s--white{background:#fff}
.hx-eyebrow{display:inline-flex;align-items:center;gap:10px;font-size:13px;font-weight:650;
  letter-spacing:.08em;text-transform:uppercase;color:var(--acc);margin:0 0 16px}
.hx-eyebrow::before{content:"";width:22px;height:2px;border-radius:2px;background:currentColor}
/* measured: #B04A2A on the tint is 4.37:1 at 13px — the darker shade clears 4.5 */
.hx-s--tint .hx-eyebrow,.hx-s--tint .hx-link,.hx-s--tint .hx-card__more{color:var(--acc-d)}
.hx-lead{font-size:clamp(17px,1.5vw,20px);line-height:1.6;color:var(--ink2);max-width:62ch}
.hx-head{max-width:760px;margin:0 0 clamp(32px,4vw,52px)}
.hx-head--c{margin-inline:auto;text-align:center}
.hx-head--c .hx-eyebrow{justify-content:center}
.hx-head--c .hx-lead{margin-inline:auto}

/* ---------- buttons (not .btn — see header note) ---------- */
.hx-b{display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:52px;
  padding:14px 24px;border-radius:999px;background:var(--btn);color:var(--btn-ink) !important;
  font:700 16px/1.2 var(--f);text-decoration:none;border:1.5px solid var(--btn);
  box-shadow:0 1px 2px rgba(0,0,0,.08),0 8px 22px rgba(160,120,0,.22);
  transition:background .18s ease,transform .18s ease,box-shadow .18s ease;cursor:pointer}
.hx-b:hover{background:var(--btn-d);border-color:var(--btn-d);transform:translateY(-1px)}
.hx-b svg{width:18px;height:18px;flex:0 0 auto}
.hx-b--ghost{background:transparent;color:var(--ink) !important;border-color:var(--line2);box-shadow:none}
.hx-b--ghost:hover{background:#fff;border-color:var(--ink);transform:translateY(-1px)}
.hx-b--light{background:transparent;color:#fff !important;border-color:rgba(255,255,255,.7);box-shadow:none}
.hx-b--light:hover{background:rgba(255,255,255,.12);border-color:#fff}
.hx-row{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.hx-link{font-weight:650;text-decoration:none !important;display:inline-flex;gap:6px;align-items:center}
.hx-link::after{content:"\2192";transition:transform .18s ease}
.hx-link:hover::after{transform:translateX(3px)}

/* ---------- demo strip + header ---------- */
.v2 .utilbar{background:var(--navy);color:#dfe6ee;font-size:13px}
.v2 .utilbar__in{max-width:1240px;margin:0 auto;padding:7px clamp(16px,4vw,40px);display:flex;
  justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}
.v2 .utilbar__hours{margin:0;color:#dfe6ee}
.v2 .utilbar a{color:#fff}
.v2 .demostrip{background:none;box-shadow:none;border:0;padding:0;color:#dfe6ee;font-size:13px;
  display:inline-flex;gap:8px;align-items:center;text-decoration:none}
.v2 .demostrip b{color:#fff}
/* ⛔ the disclosure is never trimmed to "Demo site" on a phone */
.v2 .utilbar .utilbar__demotail{display:inline}
.v2 .demostrip__dot{margin:0;background:#5fd0c6}
.hx-hd{position:sticky;top:0;z-index:50;background:rgba(255,255,255,.94);
  -webkit-backdrop-filter:saturate(1.4) blur(12px);backdrop-filter:saturate(1.4) blur(12px);
  border-bottom:1px solid var(--line)}
html[data-scrolled] .hx-hd{box-shadow:0 6px 24px rgba(19,34,48,.08)}
.hx-hd__in{max-width:1240px;margin:0 auto;padding:0 clamp(16px,4vw,40px);min-height:72px;
  display:flex;align-items:center;gap:20px}
.hx-brand{display:inline-flex;align-items:center;gap:10px;color:var(--ink) !important;text-decoration:none !important;
  font:750 20px/1 var(--fd);letter-spacing:-.02em;white-space:nowrap}
.hx-brand .nav__mark{width:30px;height:30px;color:var(--acc)}
.hx-brand span{font-weight:500;color:var(--ink3)}
/* 🧭 HEADER + MENUS — owner, 30 Sep: "make sure all the menus and hamburger menus drop
   down correctly … if we can make mega do so". ≥1024: Services = mega panel, Company =
   dropdown. <1024: a slide-in drawer driven by the #navq checkbox, so it opens even
   without JS; the script adds scroll lock, Esc, focus and close-on-link.
   ⛔ The drawer and its backdrop live OUTSIDE .hx-hd: the header's backdrop-filter makes
   it the containing block for position:fixed children, which would trap the drawer. */
.hx-hd{z-index:1000}
/* ⛔ three old sheet rules reach into this header at (0,1,1) and must be answered at
   the same weight: header[class] sets z-index 60 (the chat button would sit over the
   mega), nav[aria-label="Main"] a sets 13px with .09em tracking, and
   `header nav ul{overflow-x:auto}` (≥901px) would clip the Company dropdown. */
header.hx-hd{z-index:1000}
nav.hx-nav[aria-label="Main"] a{font-size:15px;letter-spacing:normal}
header.hx-hd nav ul.hx-nav__list{overflow:visible}
/* the old sheet tracks header text out; the new header is set tight */
.hx-hd,.hx-hd *,.hx-drawer,.hx-drawer *{letter-spacing:normal}
.hx-hd,.hx-drawer,.hx-mega__grid b,.hx-drop b,.hx-nav__t{font-family:var(--f)}
.hx-hd .hx-brand,.hx-drawer .hx-brand{letter-spacing:-.02em}
.hx-mega .hx-mega__all{color:var(--acc-d) !important;text-decoration:none !important;font-weight:650}
.hx-mega__side .hx-b{white-space:normal;text-align:center;line-height:1.25}
.hx-nav{margin-left:auto;align-self:stretch;display:flex}
.hx-nav__list{list-style:none;margin:0;padding:0;display:flex;align-items:center;gap:2px}
.hx-nav__list>li{margin:0;display:flex;align-items:center;align-self:stretch}
.hx-nav__t{display:inline-flex;align-items:center;gap:6px;padding:10px 12px;border-radius:10px;color:var(--ink) !important;
  text-decoration:none !important;font-size:15px;font-weight:550;white-space:nowrap;cursor:pointer}
.hx-nav__t svg{width:12px;height:12px;transition:transform .2s ease}
.hx-nav__t:hover,.hx-nav__t[aria-current],.hx-dd.is-open>.hx-nav__t{background:var(--bg)}
.hx-dd.is-open>.hx-nav__t svg{transform:rotate(180deg)}
.hx-mega,.hx-drop{position:absolute;top:100%;visibility:hidden;opacity:0;transform:translateY(-6px);
  transition:opacity .16s ease,transform .16s ease,visibility 0s linear .16s;z-index:5}
.hx-dd.is-open>.hx-mega,.hx-dd.is-open>.hx-drop,
html:not(.hx-js) .hx-dd:hover>.hx-mega,html:not(.hx-js) .hx-dd:focus-within>.hx-mega,
html:not(.hx-js) .hx-dd:hover>.hx-drop,html:not(.hx-js) .hx-dd:focus-within>.hx-drop{visibility:visible;opacity:1;transform:none;transition:opacity .16s ease,transform .16s ease}
.hx-mega{left:0;right:0;background:#fff;border-top:1px solid var(--line);border-bottom:1px solid var(--line);box-shadow:0 30px 60px rgba(19,34,48,.14)}
.hx-mega__in{max-width:1240px;margin:0 auto;padding:26px clamp(16px,4vw,40px) 30px;display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:32px}
.hx-mega__k{font-size:12.5px;font-weight:650;letter-spacing:.08em;text-transform:uppercase;color:var(--acc-d);margin:0 0 10px 12px}
.hx-mega__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px 16px}
.hx-mega__grid a{display:grid;grid-template-columns:44px minmax(0,1fr);column-gap:14px;align-items:center;padding:10px 12px;border-radius:12px;
  text-decoration:none !important;color:var(--ink2) !important}
.hx-mega__grid a:hover,.hx-mega__grid a:focus-visible,.hx-mega__grid a[aria-current]{background:var(--bg)}
.hx-mega__ico{grid-row:span 2;width:44px;height:44px;border-radius:12px;background:var(--acc-t);color:var(--acc-d);display:grid;place-items:center}
.hx-mega__ico svg{width:22px;height:22px}
.hx-mega__grid b{color:var(--ink);font:650 15.5px/1.3 var(--fd)}
.hx-mega__grid small{font-size:13.5px;line-height:1.4}
.hx-mega__all{margin:12px 0 0 12px;font-size:15px}
.hx-mega__side{background:var(--bg);border-radius:16px;overflow:hidden;display:flex;flex-direction:column;margin:0}
.hx-mega__side img{width:100%;height:150px;object-fit:cover;display:block}
.hx-mega__side div{padding:16px 18px 18px;display:grid;gap:10px}
.hx-mega__side b{color:var(--ink);font:700 17px/1.3 var(--fd)}
.hx-mega__side p{margin:0;font-size:14.5px;line-height:1.5;color:var(--ink2)}
.hx-mega__side .hx-b{width:100%;min-height:46px;font-size:15px}
.hx-mega__tel{font-weight:650;text-align:center;color:var(--ink) !important;text-decoration:none !important;font-size:15px}
.hx-dd--drop{position:relative}
.hx-drop{left:-8px;width:300px;margin-top:-6px;background:#fff;border:1px solid var(--line);border-radius:16px;box-shadow:0 24px 48px rgba(19,34,48,.14);padding:8px}
.hx-drop a{display:block;padding:10px 12px;border-radius:10px;text-decoration:none !important;color:var(--ink2) !important}
.hx-drop a:hover,.hx-drop a:focus-visible,.hx-drop a[aria-current]{background:var(--bg)}
.hx-drop b{display:block;color:var(--ink);font:650 15.5px/1.3 var(--fd)}
.hx-drop small{font-size:13.5px}
.hx-tel{font-weight:650;color:var(--ink) !important;text-decoration:none !important;white-space:nowrap;font-size:15px}
.hx-hd .hx-b{min-height:44px;padding:10px 18px;font-size:15px;box-shadow:none;white-space:nowrap}
@media (min-width:1024px) and (max-width:1279px){ .hx-hd .hx-tel{display:none} .hx-nav__t{padding:10px 10px} .hx-hd__in{gap:14px} }
/* hamburger + drawer */
.hx-navq{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.hx-burger{display:none;width:46px;height:46px;border-radius:12px;border:1px solid var(--line2);cursor:pointer;margin-left:auto;
  flex-direction:column;align-items:center;justify-content:center;gap:5px;color:var(--ink)}
.hx-burger i{display:block;width:20px;height:2px;border-radius:2px;background:currentColor;transition:transform .22s ease,opacity .22s ease}
.hx-navq:checked ~ .hx-hd .hx-burger i:nth-child(1){transform:translateY(7px) rotate(45deg)}
.hx-navq:checked ~ .hx-hd .hx-burger i:nth-child(2){opacity:0}
.hx-navq:checked ~ .hx-hd .hx-burger i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
.hx-drawer{position:fixed;top:0;right:0;bottom:0;width:min(420px,88vw);background:#fff;z-index:1200;display:flex;flex-direction:column;
  transform:translateX(100%);visibility:hidden;transition:transform .28s cubic-bezier(.2,.8,.2,1),visibility 0s linear .28s;box-shadow:-20px 0 60px rgba(8,18,28,.18)}
.hx-navq:checked ~ .hx-drawer{transform:none;visibility:visible;transition:transform .28s cubic-bezier(.2,.8,.2,1)}
.hx-backdrop{position:fixed;inset:0;z-index:1150;background:rgba(8,18,28,.5);opacity:0;visibility:hidden;transition:opacity .28s ease,visibility 0s linear .28s}
.hx-navq:checked ~ .hx-backdrop{opacity:1;visibility:visible;transition:opacity .28s ease}
html.hx-lock,html.hx-lock body{overflow:hidden}
.hx-drawer__hd{display:flex;align-items:center;justify-content:space-between;padding:12px 16px 12px 18px;border-bottom:1px solid var(--line)}
.hx-drawer__hd .hx-brand{font-size:18px}
.hx-x{width:46px;height:46px;border-radius:50%;border:1px solid var(--line2);cursor:pointer;position:relative;flex:0 0 auto}
.hx-x i{position:absolute;left:13px;top:22px;width:18px;height:2px;border-radius:2px;background:var(--ink)}
.hx-x i:nth-child(1){transform:rotate(45deg)}.hx-x i:nth-child(2){transform:rotate(-45deg)}
.hx-drawer__nav{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;padding:6px 10px 12px}
.hx-drawer__nav>a,.hx-acc>summary{display:flex;align-items:center;justify-content:space-between;min-height:54px;padding:0 12px;
  font:600 17px/1.2 var(--f);color:var(--ink) !important;text-decoration:none !important;border-bottom:1px solid var(--line);cursor:pointer}
.hx-drawer__nav>a[aria-current]{color:var(--acc-d) !important}
.hx-acc>summary{list-style:none}
.hx-acc>summary::-webkit-details-marker{display:none}
.hx-acc>summary::after{content:"";width:10px;height:10px;border-right:2px solid var(--acc);border-bottom:2px solid var(--acc);transform:rotate(45deg);margin:-4px 4px 0 0;transition:transform .2s ease}
.hx-acc[open]>summary::after{transform:rotate(225deg);margin-top:4px}
.hx-acc>div{padding:4px 0 10px}
.hx-acc>div a{display:flex;align-items:center;min-height:46px;padding:0 12px 0 22px;border-radius:10px;font-size:16px;color:var(--ink2) !important;text-decoration:none !important}
.hx-acc>div a[aria-current]{color:var(--acc-d) !important;font-weight:650;background:var(--bg)}
.hx-drawer__ft{padding:14px 16px calc(14px + env(safe-area-inset-bottom));border-top:1px solid var(--line);display:grid;gap:10px;background:#fff}
.hx-drawer__ft .hx-b{width:100%}
@media (max-width:1023px){
  .hx-nav,.hx-hd .hx-tel{display:none}
  .hx-burger{display:inline-flex}
  .hx-hd__in{min-height:64px;gap:12px}
  .hx-hd .hx-b{margin-left:auto}
  .hx-hd .hx-b + .hx-burger{margin-left:0}
}
@media (min-width:1024px){ .hx-drawer,.hx-backdrop,.hx-burger{display:none !important} }
@media (max-width:560px){ .hx-hd .hx-b{display:none} .hx-brand{font-size:18px} .hx-burger{margin-left:auto} }
@media (prefers-reduced-motion:reduce) and (max-width:0px){ .hx-mega,.hx-drop,.hx-drawer,.hx-backdrop,.hx-burger i,.hx-nav__t svg{transition:none !important} }
/* the floating chat button stays under the menus */
.v2 .launch{z-index:900}

/* ---------- hero (photo) ---------- */
.v2 .hero.hx-hero{position:relative;background:var(--navy);padding:0;border:0;overflow:hidden;
  animation:none;min-height:clamp(600px,82vh,780px);display:flex;align-items:center;isolation:isolate}
.v2 .hero.hx-hero::before,.v2 .hero.hx-hero::after{content:none;display:none}
.hx-hero__img{position:absolute;inset:0;z-index:-2;overflow:hidden}
.hx-hero__img img{width:100%;height:100%;object-fit:cover;object-position:62% 60%}
@media (min-width:0px){
  .hx-hero__img img{animation:hxpush 24s cubic-bezier(.2,.6,.3,1) both;transform-origin:70% 60%}
}
@keyframes hxpush{from{transform:scale(1)}to{transform:scale(1.06)}}
.hx-hero__scrim{position:absolute;inset:0;z-index:-1;
  background:linear-gradient(90deg,rgba(8,18,28,.90) 0%,rgba(8,18,28,.82) 30%,rgba(8,18,28,.52) 50%,rgba(8,18,28,.12) 68%,rgba(8,18,28,0) 80%),
             linear-gradient(0deg,rgba(8,18,28,.55) 0%,rgba(8,18,28,0) 30%)}
.hx-hero__in{width:100%;max-width:1240px;margin:0 auto;padding:clamp(72px,9vw,120px) clamp(16px,4vw,40px) clamp(112px,11vw,150px)}
.hx-hero__copy{max-width:640px}
.v2 .hx-hero h1{color:#fff;margin:0 0 20px;text-shadow:none;max-width:none;animation:none;
  font-size:clamp(38px,5.4vw,68px);line-height:1.03;letter-spacing:-.032em}
.v2 .hx-hero--page h1{font-size:clamp(36px,4.8vw,60px)}
.v2 .hx-hero h1 .hl{color:#ffd84d}
.hx-hero__sub{color:#e9eef3;font-size:clamp(17px,1.5vw,20px);line-height:1.6;max-width:56ch;margin:0 0 30px}
.hx-hero .hx-eyebrow{color:#ffd84d}
.hx-chips{list-style:none;padding:0 !important;margin:26px 0 0;display:flex;flex-wrap:wrap;gap:10px}
.hx-chips li{margin:0;display:inline-flex;align-items:center;gap:8px;padding:8px 14px 8px 10px;border-radius:999px;
  background:rgba(8,18,28,.55);border:1px solid rgba(255,255,255,.28);color:#fff;font-size:14.5px;font-weight:550}
.hx-chips svg{width:18px;height:18px;color:#ffd84d;flex:0 0 auto}
.hx-hero--page{min-height:clamp(480px,62vh,600px)}
/* measured 30 Sep: the pale stucco house behind the services copy read 3.1:1 */
@media (min-width:1101px){
  .hx-hero--page .hx-hero__scrim{background:linear-gradient(90deg,rgba(8,18,28,.93) 0%,rgba(8,18,28,.88) 40%,rgba(8,18,28,.7) 56%,rgba(8,18,28,.2) 76%,rgba(8,18,28,0) 90%),
    linear-gradient(0deg,rgba(8,18,28,.55) 0%,rgba(8,18,28,0) 30%)}
}
.hx-hero--page .hx-hero__in{padding-bottom:clamp(96px,9vw,128px)}
.hx-crumbs{font-size:14px;color:#dfe6ee;margin:0 0 14px}
.hx-crumbs a{color:#fff !important}
/* phones: the photo becomes a plate on top and the words sit below it on the
   navy ground, so the house is SEEN, not buried under a scrim. */
/* ≤1100 (measured: at 1024 the copy covered the bright house, h1 2.4:1) */
@media (max-width:1100px){
  .v2 .hero.hx-hero{min-height:0;display:block}
  .hx-hero__img{position:relative;inset:auto;z-index:0;aspect-ratio:16/9}
  .hx-hero__img img{object-position:62% 60%}
  .hx-hero__scrim{inset:auto;top:0;left:0;right:0;aspect-ratio:16/9;z-index:0;
    background:linear-gradient(180deg,rgba(13,27,41,0) 62%,rgba(13,27,41,.75) 88%,#0d1b29 100%)}
  .hx-hero__in{position:relative;z-index:1;margin-top:-8px;padding-top:0;padding-bottom:96px}
  .hx-hero__copy{max-width:720px}
}
@media (min-width:821px) and (max-width:1100px){ .hx-hero__img,.hx-hero__scrim{aspect-ratio:21/9} }
@media (max-width:820px){ .v2 .utilbar__hours{display:none} }
@media (max-width:560px){ .hx-hero__img,.hx-hero__scrim{aspect-ratio:4/3} }
@media (max-width:560px){ .hx-hero .hx-b{flex:1 1 100%} }
@media (max-width:560px){
  .hx-grid:not(.hx-grid--3):not(.hx-grid--no){grid-template-columns:1fr 1fr;gap:12px}
  .hx-grid:not(.hx-grid--3):not(.hx-grid--no) .hx-card__b{padding:12px 12px 14px;gap:4px}
  .hx-grid:not(.hx-grid--3):not(.hx-grid--no) .hx-card__b h3{font-size:15.5px}
  .hx-grid:not(.hx-grid--3):not(.hx-grid--no) .hx-card__b p{font-size:13.5px;line-height:1.45}
  .hx-grid:not(.hx-grid--3):not(.hx-grid--no) .hx-card__more{display:none}
  .hx-grid:not(.hx-grid--3):not(.hx-grid--no) .hx-card__no{font-size:12.5px}
  .hx-steps>li{display:grid;grid-template-columns:118px 1fr;align-items:stretch}
  .hx-steps__ph,.hx-steps .hx-doc{aspect-ratio:auto;min-height:118px;height:100%}
  .hx-steps__ph::after,.hx-steps .hx-doc::after{left:8px;top:8px;width:32px;height:32px;font-size:15px;line-height:32px}
  .hx-doc__sheet{width:86%;padding:8px;font-size:8.5px}
  .hx-doc__sheet b{font-size:9.5px;margin-bottom:4px}
  .hx-doc__sheet span{padding:2px 0}
  .hx-steps__b{padding:14px 16px}
  .hx-steps__b h3{font-size:16.5px}
  .hx-steps__b p{font-size:14.5px}
  .hx-steps--plain>li{display:block}
}

/* ---------- promise row: the white card on the hero's edge ---------- */
.v2 .offerbar.hx-promise{position:relative;z-index:3;margin:-64px auto 0;max-width:1240px;padding:0 clamp(16px,4vw,40px);
  background:none;border:0;box-shadow:none}
.hx-promise__card{background:#fff;border-radius:var(--r);box-shadow:var(--sh-lg);display:grid;
  grid-template-columns:repeat(4,1fr)}
.hx-promise__i{padding:28px 26px;border-left:1px solid var(--line)}
.hx-promise__i:first-child{border-left:0}
.hx-promise__i svg{width:28px;height:28px;color:var(--acc);margin-bottom:12px}
.hx-promise__i h3{font-size:17px;margin:0 0 6px}
.hx-promise__i p{font-size:15px;line-height:1.55;margin:0;color:var(--ink2)}
@media (max-width:980px){ .hx-promise__card{grid-template-columns:1fr 1fr}
  .hx-promise__i:nth-child(3){border-left:0}
  .hx-promise__i:nth-child(n+3){border-top:1px solid var(--line)} }
@media (max-width:560px){ .v2 .offerbar.hx-promise{margin-top:-40px}
  .hx-promise__card{grid-template-columns:1fr}
  .hx-promise__i{border-left:0;border-top:1px solid var(--line);padding:22px 20px;display:grid;grid-template-columns:36px 1fr;column-gap:12px}
  .hx-promise__i:first-child{border-top:0}
  .hx-promise__i svg{grid-row:span 2;margin:2px 0 0} }

/* ---------- the symptom chooser (signature motion: "formation") ---------- */
.hx-pills{display:flex;flex-wrap:wrap;gap:10px;margin:0 0 28px}
.hx-pill{font:600 15.5px/1.2 var(--f);padding:12px 18px;border-radius:999px;border:1.5px solid var(--line2);
  background:#fff;color:var(--ink);cursor:pointer;transition:background .2s ease,color .2s ease,border-color .2s ease}
.hx-pill:hover{border-color:var(--ink)}
.hx-pill[aria-selected="true"]{background:var(--ink);border-color:var(--ink);color:#fff}
.hx-form{display:grid;gap:18px;grid-template-columns:1.25fr 1fr;grid-template-rows:auto auto;
  grid-template-areas:"ph a" "ph b"}
.hx-form[data-f="1"]{grid-template-columns:1fr 1.25fr;grid-template-areas:"a ph" "b ph"}
.hx-form[data-f="2"]{grid-template-columns:1fr 1fr;grid-template-areas:"ph ph" "a b"}
.hx-fc{background:#fff;border-radius:var(--r);box-shadow:var(--sh);padding:28px;min-width:0;margin:0}
.hx-fc--ph figcaption .hx-ask{color:var(--ink) !important}
.hx-fc--a{grid-area:a}.hx-fc--b{grid-area:b}
.hx-fc--ph{grid-area:ph;padding:0;overflow:hidden;position:relative;min-height:340px}
.hx-fc--ph img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.hx-fc--ph figcaption{position:absolute;left:0;right:0;bottom:0;padding:64px 24px 20px;color:#fff;
  background:linear-gradient(0deg,rgba(8,18,28,.94) 0%,rgba(8,18,28,.86) 45%,rgba(8,18,28,.5) 72%,rgba(8,18,28,0) 100%);
  display:flex;flex-wrap:wrap;gap:10px 16px;align-items:center;justify-content:space-between}
.hx-fc--ph figcaption a{color:#fff !important}
.hx-fc__k{font-size:13px;font-weight:650;letter-spacing:.08em;text-transform:uppercase;color:var(--acc);margin:0 0 10px}
.hx-fc p:last-child{margin:0}
.hx-fc__txt{font-size:18px;line-height:1.55;color:var(--ink)}
.hx-ask{font:650 14.5px/1 var(--f);padding:11px 16px;border-radius:999px;background:#fff;color:var(--ink) !important;
  border:0;cursor:pointer;text-decoration:none !important}
.hx-ask:hover{background:var(--acc-t)}
.hx-fpanel[hidden]{display:none}
@media (max-width:820px){
  .hx-form,.hx-form[data-f]{grid-template-columns:1fr;grid-template-areas:"ph" "a" "b"}
  .hx-fc--ph{min-height:260px}
  .hx-fc{padding:22px}
  .hx-pills{flex-wrap:nowrap;overflow-x:auto;margin-inline:calc(-1 * clamp(16px,4vw,40px));
    padding:2px clamp(16px,4vw,40px) 6px;scrollbar-width:none}
  .hx-pill{flex:0 0 auto}
}

/* ---------- service cards ---------- */
.hx-grid{display:grid;gap:20px;grid-template-columns:repeat(4,1fr)}
.hx-grid--3{grid-template-columns:repeat(3,1fr)}
.hx-card{background:#fff;border-radius:var(--r);overflow:hidden;box-shadow:var(--sh);display:flex;flex-direction:column;
  text-decoration:none !important;color:var(--ink2) !important;transition:transform .22s ease,box-shadow .22s ease;min-width:0}
a.hx-card:hover{transform:translateY(-2px);box-shadow:var(--sh-lg)}
.hx-card__ph{aspect-ratio:4/3;overflow:hidden;background:var(--bg2)}
.hx-card__ph img{width:100%;height:100%;object-fit:cover;transition:transform .5s ease}
a.hx-card:hover .hx-card__ph img{transform:scale(1.03)}
.hx-card__b{padding:20px 22px 22px;display:flex;flex-direction:column;gap:8px;flex:1}
.hx-card__b h3{margin:0;font-size:19px}
.hx-card__b p{margin:0;font-size:15.5px;line-height:1.55}
.hx-card__more{margin-top:auto;padding-top:8px;font-weight:650;color:var(--acc);font-size:15px}
.hx-card__more::after{content:" \2192"}
.hx-card__no{font-size:14.5px;line-height:1.5;color:var(--ink3);border-top:1px solid var(--line);padding-top:12px;margin-top:4px !important}
.hx-card__no b{color:var(--ink)}
@media (max-width:1100px){ .hx-grid{grid-template-columns:repeat(2,1fr)} .hx-grid--3{grid-template-columns:repeat(3,1fr)} }
@media (max-width:820px){ .hx-grid--3{grid-template-columns:1fr} }
@media (max-width:560px){ .hx-grid{grid-template-columns:1fr} }
.hx-more{margin-top:32px;display:flex;justify-content:center}

/* ---------- steps ---------- */
.hx-steps{list-style:none;padding:0 !important;margin:0;display:grid;grid-template-columns:repeat(4,1fr);gap:20px;counter-reset:st}
.hx-steps>li{margin:0;background:#fff;border-radius:var(--r);box-shadow:var(--sh);overflow:hidden;counter-increment:st;display:flex;flex-direction:column}
.hx-steps__ph{aspect-ratio:4/3;overflow:hidden;position:relative;background:var(--bg2)}
.hx-steps__ph img{width:100%;height:100%;object-fit:cover}
.hx-steps__ph::after,.hx-steps .hx-doc::after{content:counter(st);position:absolute;left:16px;top:16px;width:40px;height:40px;border-radius:50%;
  background:#fff;color:var(--acc);font:750 17px/40px var(--fd);text-align:center;box-shadow:var(--sh)}
.hx-steps__b{padding:20px 22px 24px}
.hx-steps__b h3{font-size:18.5px;margin:0 0 6px}
.hx-steps__b p{margin:0;font-size:15.5px;line-height:1.55}
.hx-steps--plain>li{padding:26px 24px}
.hx-steps--plain>li::before{content:counter(st);display:block;width:40px;height:40px;border-radius:50%;background:var(--acc-t);
  color:var(--acc-d);font:750 17px/40px var(--fd);text-align:center;margin-bottom:16px}
.hx-steps--plain h3{font-size:18.5px;margin:0 0 6px}
.hx-steps--plain p{margin:0;font-size:15.5px;line-height:1.55}
.hx-doc{aspect-ratio:4/3;background:linear-gradient(180deg,#f5f8fa,#e8eff4);display:flex;align-items:center;justify-content:center;position:relative}
.hx-doc__sheet{width:66%;background:#fff;border-radius:8px;box-shadow:0 10px 30px rgba(19,34,48,.14);padding:16px 16px 12px;font-size:11.5px;line-height:1.35;color:var(--ink2)}
.hx-doc__sheet b{display:block;font-size:12.5px;color:var(--ink);margin-bottom:8px}
.hx-doc__sheet span{display:flex;justify-content:space-between;gap:8px;border-top:1px solid var(--line);padding:5px 0}
.hx-doc__sheet em{font-style:normal;color:var(--acc);font-weight:650}
@media (max-width:1100px){ .hx-steps{grid-template-columns:repeat(2,1fr)} }
@media (max-width:560px){ .hx-steps{grid-template-columns:1fr} }

/* ---------- splits ---------- */
.hx-split{display:grid;grid-template-columns:5fr 7fr;gap:clamp(32px,5vw,72px);align-items:center}
.hx-split--r{grid-template-columns:7fr 5fr}
.hx-split--even{grid-template-columns:1fr 1fr}
.hx-split__ph{border-radius:var(--r);overflow:hidden;box-shadow:var(--sh);margin:0}
.hx-split__ph img{width:100%;height:100%;object-fit:cover;aspect-ratio:4/5}
.hx-split__ph--wide img{aspect-ratio:4/3}
.hx-split__ph figcaption,.hx-cap{font-size:13.5px;color:var(--ink3);padding:10px 4px 0;line-height:1.45}
.hx-ticks{list-style:none;padding:0 !important;margin:0 0 28px;display:grid;gap:14px}
.hx-ticks li{margin:0;display:grid;grid-template-columns:28px 1fr;gap:12px;align-items:start;color:var(--ink2)}
.hx-ticks li::before{content:"";width:26px;height:26px;border-radius:50%;background:var(--acc-t) no-repeat center/14px
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23084a73' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E")}
.hx-ticks b{color:var(--ink)}
.hx-note{background:#fff;border-left:3px solid var(--acc);border-radius:0 var(--r-sm) var(--r-sm) 0;padding:16px 20px;margin:0 0 28px;font-size:15.5px;color:var(--ink2)}
@media (max-width:900px){ .hx-split,.hx-split--r,.hx-split--even{grid-template-columns:1fr}
  .hx-split__ph img{aspect-ratio:4/3} }

/* ---------- the report card, re-inked as paper ---------- */
.hx-rep{position:relative}
.v2 .rep{background:#fff;border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--sh-lg);color:var(--ink2);overflow:hidden}
.v2 .rep__hd{background:#f6f9fb;border-bottom:1px solid var(--line);padding:20px 22px}
.v2 .rep__title{font:750 19px/1.2 var(--fd);color:var(--ink);text-transform:none;letter-spacing:-.01em;margin:0 0 10px}
.v2 .rep__meta{font-family:var(--f);font-size:13.5px;color:var(--ink3);display:grid;grid-template-columns:1fr 1fr;gap:4px 16px}
.v2 .rep__meta b{color:var(--ink)}
.v2 .rep__row{border:0;border-top:1px solid var(--line);padding:14px 22px;background:#fff;box-shadow:none}
.v2 .rep__row:first-of-type{border-top:0}
.v2 .rep__top{display:flex;justify-content:space-between;gap:12px}
.v2 .rep__no{font-family:var(--f);font-size:13px;color:var(--ink3);letter-spacing:.04em}
.v2 .rep__cost{font-family:var(--f);font-variant-numeric:tabular-nums;font-weight:650;color:var(--ink);font-size:15px}
.v2 .rep__item{font:650 16px/1.35 var(--f);text-transform:none;color:var(--ink);margin:4px 0 4px;letter-spacing:0}
.v2 .rep__note{font-size:14.5px;line-height:1.55;color:var(--ink2);margin:0}
.v2 .rep__row--declined{background:var(--acc-t)}
.v2 .rep__row--declined .rep__cost,.v2 .rep__row--declined .rep__item{color:var(--acc-d)}
.v2 .rep__foot{display:flex;justify-content:space-between;padding:16px 22px;border-top:2px solid var(--ink);font-weight:750;color:var(--ink);background:#fff}
.v2 .rep__sig{font-family:var(--f);font-size:13px;color:var(--ink3);padding:12px 22px 18px;margin:0;background:#fff;border:0}
.hx-rep__tag{position:absolute;top:-12px;right:18px;z-index:2;background:var(--ink);color:#fff;font-size:12.5px;font-weight:650;
  padding:6px 12px;border-radius:999px;letter-spacing:.02em}
.hx-rep.is-clipped .rep{max-height:560px}
.hx-rep.is-clipped .rep::after{content:"";position:absolute;left:1px;right:1px;bottom:1px;height:160px;border-radius:0 0 var(--r) var(--r);
  background:linear-gradient(180deg,rgba(255,255,255,0),#fff 78%)}
.hx-rep__more{position:absolute;left:50%;bottom:22px;transform:translateX(-50%);z-index:2;display:none}
.hx-rep.is-clipped .hx-rep__more{display:inline-flex}
.hx-rep.is-clipped .rep{position:relative}

/* ---------- life band ---------- */
.hx-life{display:grid;grid-template-columns:2fr 1fr 1fr;gap:16px}
.hx-life figure{margin:0;border-radius:var(--r);overflow:hidden;position:relative;min-height:380px}
.hx-life img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.hx-life__big figcaption{position:absolute;left:0;right:0;bottom:0;padding:80px 28px 26px;color:#fff;
  background:linear-gradient(0deg,rgba(8,18,28,.9) 0%,rgba(8,18,28,.7) 55%,rgba(8,18,28,0) 100%)}
.hx-life__big figcaption strong{display:block;font:700 clamp(24px,2.6vw,34px)/1.15 var(--fd);letter-spacing:-.02em;margin-bottom:6px}
.hx-life--two{grid-template-columns:2fr 1fr}
@media (max-width:900px){ .hx-life{grid-template-columns:1fr 1fr} .hx-life__big{grid-column:1 / -1} .hx-life figure{min-height:260px}
  /* reviewer 30 Sep: two photos stack full width, no half-empty row */
  .hx-life--two{grid-template-columns:1fr} }
.hx-dgm{margin:0}
.hx-dgm svg{display:block;width:100%;height:auto;border-radius:var(--r);box-shadow:var(--sh)}

/* 💬 THE FLOATING CHAT BUTTON waits until the hero and the promise card have
   scrolled away (reviewer, 30 Sep: it covered the hero phone button at 390
   and the 4th promise title at 1440). The hero's own button opens the same
   chat. ⛔ visibility, not opacity alone. Without JS the attribute is never
   set, so the button simply shows. */
html[data-hero-in-view] body.v2 .launch{visibility:hidden;opacity:0;pointer-events:none;transform:translateY(12px)}
body.v2 .launch{transition:opacity .25s ease,transform .25s ease,visibility .25s}

/* ---------- chat showcase ---------- */
.hx-chatcard{background:#fff;border-radius:24px;box-shadow:var(--sh-lg);overflow:hidden;max-width:460px;justify-self:end;width:100%}
.hx-chatcard__hd{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--line)}
.hx-chatcard__av{width:40px;height:40px;border-radius:50%;background:var(--acc-t);color:var(--acc);display:grid;place-items:center}
.hx-chatcard__av svg{width:22px;height:22px}
.hx-chatcard__hd b{display:block;color:var(--ink);font-size:16px}
.hx-chatcard__hd small{color:var(--ink3);font-size:13px}
.hx-chatcard__bd{padding:22px 20px;display:grid;gap:12px;background:#f6f9fb}
.hx-bub{max-width:88%;padding:12px 16px;border-radius:18px 18px 18px 6px;background:#fff;border:1px solid var(--line);font-size:15.5px;line-height:1.5;color:var(--ink)}
.hx-chatcard__opts{display:flex;flex-direction:column;gap:8px;align-items:flex-end}
.hx-chatcard__opts a{display:inline-block;padding:11px 16px;border-radius:18px 18px 6px 18px;background:var(--acc);color:#fff !important;
  text-decoration:none !important;font-weight:600;font-size:15px}
.hx-chatcard__opts a:hover{background:var(--acc-d)}
.hx-chatcard__ft{padding:12px 20px;font-size:13px;color:var(--ink3);border-top:1px solid var(--line)}
@media (max-width:900px){ .hx-chatcard{justify-self:stretch;max-width:none} }

/* ---------- areas ---------- */
.hx-towns{list-style:none;padding:0 !important;margin:0 0 28px;display:flex;flex-wrap:wrap;gap:8px}
.hx-towns li{margin:0;padding:8px 14px;border-radius:999px;background:#fff;border:1px solid var(--line);font-size:15px;color:var(--ink);font-weight:550}

/* ---------- FAQ ---------- */
.hx-faq{max-width:860px;margin:0 auto;display:grid;gap:12px}
.hx-faq details{background:#fff;border-radius:var(--r-sm);box-shadow:var(--sh);border:1px solid transparent}
.hx-faq details[open]{border-color:var(--line)}
.hx-faq summary{list-style:none;cursor:pointer;padding:20px 56px 20px 24px;font:650 18px/1.4 var(--fd);color:var(--ink);position:relative}
.hx-faq summary::-webkit-details-marker{display:none}
.hx-faq summary::after{content:"";position:absolute;right:22px;top:50%;width:12px;height:12px;margin-top:-8px;
  border-right:2px solid var(--acc);border-bottom:2px solid var(--acc);transform:rotate(45deg);transition:transform .2s ease}
.hx-faq details[open] summary::after{transform:rotate(225deg);margin-top:-2px}
.hx-faq details>div{padding:0 24px 22px;color:var(--ink2)}
.hx-faq details>div p:last-child{margin:0}
.hx-details{max-width:860px}
.hx-details details{background:#fff;border-radius:var(--r-sm);border:1px solid var(--line);margin:0 0 12px}
.hx-details summary{cursor:pointer;padding:18px 52px 18px 22px;font:650 17px/1.4 var(--fd);color:var(--ink);list-style:none;position:relative}
.hx-details summary::-webkit-details-marker{display:none}
.hx-details summary::after{content:"";position:absolute;right:22px;top:50%;width:10px;height:10px;margin-top:-7px;
  border-right:2px solid var(--acc);border-bottom:2px solid var(--acc);transform:rotate(45deg)}
.hx-details details[open] summary::after{transform:rotate(225deg);margin-top:-2px}
.hx-details details>div{padding:0 22px 20px}
.hx-details h3{font-size:18px;margin:22px 0 8px}

/* ---------- closing photo band ---------- */
.hx-cta{position:relative;overflow:hidden;isolation:isolate;color:#fff;padding:clamp(80px,10vw,140px) 0}
.hx-cta>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:-2}
.hx-cta::before{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(90deg,rgba(8,18,28,.92) 0%,rgba(8,18,28,.86) 45%,rgba(8,18,28,.6) 70%,rgba(8,18,28,.35) 100%)}
.v2 .hx-cta h2{color:#fff;max-width:18ch}
.hx-cta p{color:#e9eef3;max-width:52ch;font-size:18px}
.hx-cta__cap{position:absolute;right:16px;bottom:10px;font-size:12px;color:rgba(255,255,255,.8);margin:0}
@media (max-width:820px){ .hx-cta::before{background:linear-gradient(180deg,rgba(8,18,28,.72) 0%,rgba(8,18,28,.88) 100%)} }
.hx-band{background:#fff;border-radius:24px;box-shadow:var(--sh);padding:clamp(28px,4vw,48px);display:flex;flex-wrap:wrap;
  gap:24px;align-items:center;justify-content:space-between}
.hx-band h2{margin:0 0 6px;font-size:clamp(26px,3vw,36px)}
.hx-band p{margin:0}

/* ---------- small print cards (services page "we do not sell") ---------- */
.hx-no{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
.hx-no>div{background:#fff;border-radius:var(--r);box-shadow:var(--sh);padding:28px}
.hx-no h3{display:flex;gap:10px;align-items:center}
.hx-no h3::before{content:"\00d7";display:inline-grid;place-items:center;width:28px;height:28px;border-radius:50%;background:var(--acc-t);color:var(--acc-d);font-size:20px;line-height:1}
.hx-no p{margin:0;font-size:16px}
@media (max-width:820px){ .hx-no{grid-template-columns:1fr} }
.hx-facts{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.hx-facts>div{background:#fff;border-radius:var(--r);box-shadow:var(--sh);padding:26px}
.hx-facts h3{font-size:18px}
.hx-facts p{margin:0;font-size:15.5px}
@media (max-width:900px){ .hx-facts{grid-template-columns:1fr} }

/* ---------- footer ---------- */
.hx-ft{background:#e4edf3;color:var(--ink2);padding:64px 0 36px;font-size:15px}
.hx-ft__grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:32px}
.hx-ft h4{font-size:14px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink);margin:0 0 14px}
.hx-ft ul{list-style:none;padding:0 !important;margin:0}
.hx-ft li{margin:0 0 8px}
.hx-ft a{color:var(--ink) !important;text-decoration:none}
.hx-ft a:hover{text-decoration:underline}
.hx-ft__brand{display:inline-flex;gap:10px;align-items:center;font:750 20px/1 var(--fd);color:var(--ink);margin:0 0 14px}
.hx-ft__brand svg{width:28px;height:28px;color:var(--acc)}
.hx-ft__addr{margin:0 0 10px;line-height:1.6}
.hx-ft__lic{font-size:14px;color:var(--ink3)}
.hx-ft__legal{margin:40px 0 0;padding-top:22px;border-top:1px solid var(--line2);font-size:13.5px;color:var(--ink3);line-height:1.6}
@media (max-width:900px){ .hx-ft__grid{grid-template-columns:1fr 1fr} }
@media (max-width:560px){ .hx-ft__grid{grid-template-columns:1fr} }

.hx-spec{background:var(--bg);border-radius:var(--r);padding:24px 24px 14px;position:sticky;top:96px}
.hx-spec span{display:grid;grid-template-columns:minmax(110px,38%) 1fr;gap:14px;border-top:1px solid var(--line);padding:10px 0;font-size:14.5px;line-height:1.45;color:var(--ink2)}
.hx-spec b{color:var(--ink);font-weight:650}
@media (max-width:900px){ .hx-spec{position:static} }
/* the old launcher reads fine on cream; give it room above the footer */
.v2 main{display:block}

/* ── the rest of the site, 30 Sep (second review) ───────────────────────── */
/* icon cards — used where no honest photograph exists for the point */
.hx-facts svg{width:28px;height:28px;color:var(--acc);margin:0 0 12px;display:block}
/* related services on a service page: words only, so no photo repeats */
.hx-rel{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
.hx-rel a{display:block;background:#fff;border-radius:var(--r-sm);box-shadow:var(--sh);padding:18px 20px;text-decoration:none !important;color:var(--ink2) !important;transition:transform .2s ease,box-shadow .2s ease}
.hx-rel a:hover{transform:translateY(-2px);box-shadow:var(--sh-lg)}
.hx-rel b{display:block;color:var(--ink);font:650 16.5px/1.3 var(--fd);margin:0 0 4px}
.hx-rel span{font-size:14.5px;line-height:1.5}
@media (max-width:1100px){ .hx-rel{grid-template-columns:repeat(2,1fr)} }
@media (max-width:560px){ .hx-rel{grid-template-columns:1fr} }
/* a page with no photograph at the top (privacy) */
.hx-pagehead{background:var(--bg2);padding:clamp(56px,7vw,96px) 0 clamp(88px,9vw,120px)}
.hx-pagehead .hx-crumbs{color:var(--ink3)}
.hx-pagehead .hx-crumbs a{color:var(--ink) !important}
.v2 .hx-pagehead h1{font-size:clamp(36px,4.8vw,60px);line-height:1.05;letter-spacing:-.03em;margin:0 0 16px}
/* long reading columns */
.hx-prose{max-width:760px}
.v2 .hx-prose h2{font-size:clamp(24px,2.4vw,30px);margin:40px 0 12px}
.v2 .hx-prose h2:first-child{margin-top:0}
.hx-prose p{font-size:17px;line-height:1.7}
/* hero caption, bottom right (stock-people notice) */
.hx-hero__cap{position:absolute;right:16px;bottom:10px;z-index:2;margin:0;font-size:12.5px;color:rgba(255,255,255,.9)}
@media (max-width:1100px){ .hx-hero__cap{bottom:auto;top:10px;right:10px;background:rgba(8,18,28,.66);padding:3px 8px;border-radius:6px} }
/* timeline rows (process) */
.hx-tl{list-style:none;padding:0 !important;margin:0;display:grid;gap:22px;counter-reset:tl}
.hx-tl>li{margin:0;display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);background:#fff;border-radius:var(--r);box-shadow:var(--sh);overflow:hidden;counter-increment:tl}
.hx-tl>li:nth-child(even){grid-template-columns:minmax(0,7fr) minmax(0,5fr)}
.hx-tl>li:nth-child(even) .hx-tl__ph{order:2}
.hx-tl__ph{position:relative;min-height:280px;background:var(--bg2)}
.hx-tl__ph img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.hx-tl__ph .hx-doc{position:absolute;inset:0;aspect-ratio:auto}
.hx-tl__b{padding:clamp(24px,3vw,40px);align-self:center}
.hx-tl__b::before{content:"Step " counter(tl);display:block;font-size:13px;font-weight:650;letter-spacing:.08em;text-transform:uppercase;color:var(--acc);margin:0 0 10px}
.v2 .hx-tl__b h3{font-size:clamp(20px,2vw,24px);margin:0 0 10px}
.hx-tl__b p{margin:0;font-size:16.5px}
@media (max-width:820px){ .hx-tl>li,.hx-tl>li:nth-child(even){grid-template-columns:1fr} .hx-tl>li:nth-child(even) .hx-tl__ph{order:0} .hx-tl__ph{min-height:0;aspect-ratio:16/10} }
/* two-card comparison (warranty) */
.hx-cmp{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.hx-cmp>div{background:#fff;border-radius:var(--r);box-shadow:var(--sh);padding:clamp(24px,3vw,36px)}
.hx-cmp .hx-fc__k{margin-bottom:8px}
.v2 .hx-cmp h3{font-size:22px}
.hx-cmp ul{margin:14px 0 0;padding-left:1.1em}
.hx-cmp li{margin:0 0 8px}
@media (max-width:820px){ .hx-cmp{grid-template-columns:1fr} }
/* drawn diagrams on the light ground */
.hx-draw{margin:0}
.hx-draw svg{display:block;width:100%;height:auto;border-radius:var(--r);box-shadow:var(--sh);background:#f6f9fb}
/* reviews: empty cards that show where real reviews would appear */
.hx-skel{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.hx-skel>div{background:#fff;border:1.5px dashed var(--line2);border-radius:var(--r);padding:26px;min-height:200px;display:flex;flex-direction:column;gap:12px}
.hx-skel i{display:block;height:12px;border-radius:6px;background:#e2e9ef}
.hx-skel i:nth-child(1){width:40%;height:14px}
.hx-skel i:nth-child(2){width:92%}.hx-skel i:nth-child(3){width:84%}.hx-skel i:nth-child(4){width:60%}
.hx-skel p{margin:auto 0 0;font-size:13.5px;color:var(--ink3)}
@media (max-width:900px){ .hx-skel{grid-template-columns:1fr} }
/* the request form (book) */
.hx-qform{background:#fff;border-radius:var(--r);box-shadow:var(--sh-lg);padding:clamp(22px,3vw,36px)}
.v2 .hx-qform .form{background:none;border:0;padding:0;box-shadow:none;background-image:none}
.hx-qform .field{margin:0 0 16px}
.hx-qform .field--split{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.v2 .hx-qform label{display:block;font:600 15px/1.3 var(--f);color:var(--ink);margin:0 0 6px;text-transform:none;letter-spacing:0}
.v2 .hx-qform input,.v2 .hx-qform select,.v2 .hx-qform textarea{width:100%;font:16px/1.4 var(--f);padding:12px 14px;border:1.5px solid var(--line2);border-radius:12px;background:#fff;color:var(--ink);box-sizing:border-box}
.v2 .hx-qform textarea{min-height:130px;resize:vertical}
.v2 .hx-qform input:focus,.v2 .hx-qform select:focus,.v2 .hx-qform textarea:focus{outline:2px solid var(--acc);outline-offset:1px;border-color:var(--acc)}
.v2 .hx-qform .formnote{font-size:14px;color:var(--ink3);margin:14px 0 0}
.hx-qform .hx-b{width:100%}
@media (max-width:560px){ .hx-qform .field--split{grid-template-columns:1fr} }
.hx-stack{display:grid;gap:18px;align-content:start}
.hx-warn{background:#fff8e0;border:1px solid #ecd48a;border-radius:var(--r-sm);padding:18px 20px;color:var(--ink2);font-size:15.5px}
.hx-warn b{color:var(--acc-d)}
.hx-num{list-style:none;padding:0 !important;margin:0;counter-reset:nm;display:grid;gap:12px}
.hx-num li{margin:0;display:grid;grid-template-columns:34px 1fr;gap:12px;align-items:start;counter-increment:nm;color:var(--ink2)}
.hx-num li::before{content:counter(nm);width:30px;height:30px;border-radius:50%;background:var(--acc-t);color:var(--acc-d);font:700 14px/30px var(--fd);text-align:center}
.hx-box{background:#fff;border-radius:var(--r);box-shadow:var(--sh);padding:clamp(22px,3vw,30px)}
.v2 .hx-box h3{font-size:19px;margin:0 0 10px}
.hx-box p:last-child{margin:0}
.hx-faqgrp{max-width:860px;margin:0 auto 40px}
/* reviews (invented for the demo, owner 30 Sep; ⛔ no rating schema, initials not faces) */
.hx-revtop{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:16px 32px;margin:0 0 clamp(28px,3.5vw,44px)}
.hx-revtop .hx-head{margin:0}
.hx-revsum{display:flex;align-items:center;gap:12px;background:#fff;border-radius:999px;box-shadow:var(--sh);padding:10px 20px}
.hx-revsum>b{font:750 28px/1 var(--fd);color:var(--ink)}
.hx-revsum>span:last-child{font-size:15px;color:var(--ink2);font-weight:550}
.hx-stars{display:inline-flex;gap:2px}
.hx-stars svg{width:18px;height:18px;fill:#e0a526}
.hx-stars svg.off{fill:#cbd5de}
.hx-revs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px}
.hx-rev{margin:0;background:#fff;border-radius:var(--r);box-shadow:var(--sh);padding:24px;display:flex;flex-direction:column;gap:12px}
.hx-rev__hd{display:flex;align-items:center;gap:12px}
.hx-rev__av{width:42px;height:42px;border-radius:50%;background:var(--acc-t);color:var(--acc-d);display:grid;place-items:center;font:700 15px/1 var(--fd);flex:0 0 auto}
.hx-rev__hd b{display:block;color:var(--ink);font-size:16px}
.hx-rev__hd small{font-size:13.5px;color:var(--ink3)}
.hx-rev blockquote{margin:0;font-size:16px;line-height:1.6;color:var(--ink2)}
@media (max-width:1000px){ .hx-revs{grid-template-columns:1fr 1fr} }
@media (max-width:640px){ .hx-revs{grid-template-columns:1fr} }
/* a drawn stand-in where no honest photograph exists (the stained roof deck) */
.hx-drawn{display:block;width:100%;height:100%}
.hx-tl__ph .hx-drawn{position:absolute;inset:0}
.v2 .hx-faqgrp h3{font-size:14px;letter-spacing:.08em;text-transform:uppercase;color:var(--acc-d);margin:0 0 12px}
/* ── Kingfisher only, 30 Sep 2026 ────────────────────────────────────────── */
/* the mega menu carries 14 services, so three columns and a wider panel */
.hx-mega__grid{grid-template-columns:repeat(3,minmax(0,1fr))}
.hx-mega__grid a{padding:9px 10px}
.hx-mega__grid small{display:block}
@media (min-width:1024px) and (max-width:1199px){ .hx-mega__grid{grid-template-columns:repeat(2,minmax(0,1fr))} .hx-mega__in{grid-template-columns:minmax(0,1fr) 260px} }
.hx-brand .nav__mark{width:32px;height:32px}
.hx-brand b{font-weight:800}

/* 💬 the floating chat button, re-inked for the light page. ⛔ The old sheet
   draws it from --accent, which moves with the visitor's colour scheme. */
.v2 .launch{background:var(--btn);color:var(--btn-ink);width:60px;height:60px;box-shadow:0 10px 28px rgba(19,34,48,.28)}
.v2 .launch svg{width:26px;height:26px}
.v2 .launch::after{background:#fff;color:var(--ink);border-color:var(--line)}
/* the chat panel on white */
.v2 .chat{background:#fff;border:1px solid var(--line);border-radius:18px;box-shadow:0 24px 60px rgba(19,34,48,.22);z-index:1100;font-family:var(--f)}
.v2 .chat__hd{background:var(--navy);color:#fff;border:0;padding:14px 16px}
.v2 .chat__hd b{color:#fff;font-family:var(--fd);font-size:16px}
.v2 .chat__hd .lbl{color:#cfe0ec;font-family:var(--f);font-size:12px;letter-spacing:.06em;text-transform:uppercase}
.v2 .chat__x{color:#fff;min-width:40px;min-height:40px}
.v2 .chat__log{background:#f6f9fb}
.v2 .msg{font-size:15px;line-height:1.5;border-radius:16px}
.v2 .msg--bot{background:#fff;border:1px solid var(--line);color:var(--ink)}
.v2 .msg--me{background:var(--acc);color:#fff}
.v2 .chat__opts{background:#f6f9fb;padding:4px 14px 14px}
.v2 .chip{border:1.5px solid var(--line2);color:var(--ink);background:#fff;font-size:14px;border-radius:999px;min-height:40px;padding:8px 14px}
.v2 .chip:hover{border-color:var(--acc);color:var(--acc-d)}
.v2 .chat__in{background:#fff;color:var(--ink);border-color:#6b7b89}
.v2 .chat__send{background:var(--btn);color:var(--btn-ink);border-color:var(--btn)}
.v2 .chat__ft{border-top:1px solid var(--line);color:var(--ink3);font-family:var(--f);font-size:12px;background:#fff}

/* ---------- the signature motion: surface cards that turn over ("crema",
   motionin.design /gallery/crema). Front: the surface. Back: how it is cleaned
   and at what pressure. The content swaps at the half-turn. Hover, focus or tap
   turns it; Enter or Space too. ⛔ prefers-reduced-motion: no rotation, the back
   is simply shown under the front. Without JS the back is shown on hover/focus. */
.hx-flips{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:20px}
.hx-flip{position:relative;perspective:1400px;min-height:360px;border-radius:var(--r);cursor:pointer;outline:none}
.hx-flip__in{position:absolute;inset:0;transform-style:preserve-3d;transition:transform .5s cubic-bezier(.3,.7,.2,1)}
.hx-flip.is-on .hx-flip__in,html:not(.hx-js) .hx-flip:hover .hx-flip__in,html:not(.hx-js) .hx-flip:focus-within .hx-flip__in{transform:rotateY(180deg)}
.hx-flip__f,.hx-flip__b{position:absolute;inset:0;border-radius:var(--r);overflow:hidden;-webkit-backface-visibility:hidden;backface-visibility:hidden;box-shadow:var(--sh)}
.hx-flip__f img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.hx-flip__f::after{content:"";position:absolute;left:0;right:0;bottom:0;height:62%;background:linear-gradient(0deg,rgba(8,18,28,.9) 0%,rgba(8,18,28,.55) 55%,rgba(8,18,28,0) 100%)}
.hx-flip__f div{position:absolute;left:0;right:0;bottom:0;z-index:1;padding:20px 20px 18px;color:#fff}
.v2 .hx-flip__f h3{color:#fff;font-size:21px;margin:0 0 4px}
.hx-flip__f p{margin:0;font-size:14.5px;color:#e4edf4;display:flex;align-items:center;gap:8px}
.hx-flip__f p svg{width:16px;height:16px;flex:0 0 auto}
.hx-flip__b{transform:rotateY(180deg);background:var(--navy);color:#dfe8ef;padding:24px 22px;display:flex;flex-direction:column;gap:10px}
.hx-flip__k{font-size:12.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--btn);margin:0}
.v2 .hx-flip__b h3{color:#fff;font-size:22px;margin:0}
.hx-flip__psi{font:800 30px/1 var(--fd);color:#fff;letter-spacing:-.02em;margin:4px 0 0}
.hx-flip__psi small{display:block;font:500 13px/1.4 var(--f);color:#b9cad8;letter-spacing:0;margin-top:6px}
.hx-flip__b p{margin:0;font-size:15px;line-height:1.5}
.hx-flip__b .hx-flip__go{margin-top:auto;display:flex;gap:10px;flex-wrap:wrap}
.hx-flip__b a{color:#fff !important;font-weight:650;font-size:14.5px}
.hx-flip__b a.hx-ask{background:var(--btn);color:var(--btn-ink) !important;text-decoration:none !important}
@media (max-width:1100px){ .hx-flips{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:560px){ .hx-flips{gap:12px} .hx-flip{min-height:330px} .v2 .hx-flip__f h3{font-size:18px} .hx-flip__b{padding:18px 16px} .v2 .hx-flip__b h3{font-size:18px} .hx-flip__psi{font-size:23px} .hx-flip__b p{font-size:13.5px} }
@media (max-width:380px){ .hx-flips{grid-template-columns:1fr} }
@media (prefers-reduced-motion:reduce) and (max-width:0px){
  .hx-flip{min-height:0;perspective:none}
  .hx-flip__in{position:relative;inset:auto;transform:none !important;transition:none;display:grid;gap:0}
  .hx-flip__f,.hx-flip__b{position:relative;inset:auto;transform:none;-webkit-backface-visibility:visible;backface-visibility:visible}
  .hx-flip__f{aspect-ratio:4/3;border-radius:var(--r) var(--r) 0 0;box-shadow:none}
  .hx-flip__b{border-radius:0 0 var(--r) var(--r);box-shadow:none}
}
.hx-hint{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--ink3);margin:-12px 0 22px}
.hx-hint svg{width:18px;height:18px;color:var(--acc)}

/* ---------- the wash card, on paper ---------- */
.hx-wc{background:#fff;border-radius:var(--r);box-shadow:var(--sh-lg);overflow:hidden;border:1px solid var(--line);font-size:15px}
.hx-wc__hd{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:18px 22px;background:#f6f9fb;border-bottom:1px solid var(--line)}
.hx-wc__hd b{display:block;font:750 18px/1.2 var(--fd);color:var(--ink)}
.hx-wc__hd small{color:var(--ink3);font-size:13.5px}
.hx-wc__tag{background:var(--ink);color:#fff;font-size:12px;font-weight:650;padding:5px 10px;border-radius:999px;white-space:nowrap}
.hx-wc__row{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);gap:6px 16px;padding:14px 22px;border-top:1px solid var(--line);align-items:start}
.hx-wc__row:first-of-type{border-top:0}
.hx-wc__row b{color:var(--ink);font-weight:650}
.hx-wc__row span{color:var(--ink2)}
.hx-wc__row em{font-style:normal;display:block;font-size:13.5px;color:var(--ink3);margin-top:2px}
.hx-wc__row .hx-wc__v{text-align:right;font-weight:700;color:var(--acc-d);font-variant-numeric:tabular-nums}
.hx-wc__row--no{background:#fff8e0}
.hx-wc__row--no .hx-wc__v{color:#8a5a00}
.hx-wc__ft{display:flex;flex-wrap:wrap;gap:8px 16px;justify-content:space-between;padding:14px 22px;border-top:2px solid var(--ink);font-weight:650;color:var(--ink);font-size:14.5px}
@media (max-width:560px){ .hx-wc__row{grid-template-columns:1fr} .hx-wc__row .hx-wc__v{text-align:left} }

/* ---------- the $189 offer ---------- */
.hx-offer{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);background:#fff;border-radius:24px;box-shadow:var(--sh-lg);overflow:hidden}
.hx-offer__b{padding:clamp(28px,4vw,52px)}
.hx-offer__ph{position:relative;min-height:320px}
.hx-offer__ph img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.hx-price{display:flex;align-items:baseline;gap:12px;margin:0 0 18px}
.hx-price b{font:800 clamp(52px,6vw,76px)/1 var(--fd);color:var(--ink);letter-spacing:-.04em}
.hx-price span{font-size:15px;color:var(--ink3);font-weight:550}
.hx-offer__fine{font-size:14px;color:var(--ink3);margin:18px 0 0}
.hx-cut{border:2px dashed var(--line2);border-radius:14px;padding:12px 16px;display:inline-flex;gap:10px;align-items:center;font-weight:650;color:var(--ink);font-size:14.5px;margin:0 0 20px}
.hx-cut svg{width:18px;height:18px;color:var(--acc)}
@media (max-width:900px){ .hx-offer{grid-template-columns:1fr} .hx-offer__ph{min-height:0;aspect-ratio:16/10;order:-1} }

/* ---------- a photograph with honest labels on it (roof, mid soft wash) ---------- */
.hx-lab{position:relative;margin:0;border-radius:var(--r);overflow:hidden;box-shadow:var(--sh-lg)}
.hx-lab img{display:block;width:100%;height:auto}
.hx-lab__t{position:absolute;display:inline-flex;align-items:center;gap:8px;background:#fff;color:var(--ink);font:700 14px/1.2 var(--f);padding:8px 12px;border-radius:999px;box-shadow:0 6px 18px rgba(8,18,28,.25)}
.hx-lab__t i{width:10px;height:10px;border-radius:50%;background:var(--acc);flex:0 0 auto}
.hx-lab__t--b i{background:#b7791f}
.hx-lab figcaption{position:relative;background:#fff;padding:14px 18px;font-size:14.5px;color:var(--ink2)}
@media (max-width:560px){ .hx-lab__t{font-size:12px;padding:6px 10px} }

/* ---------- where we work: the drawn peninsula ---------- */
.hx-map{margin:0;border-radius:var(--r);overflow:hidden;box-shadow:var(--sh);background:#d9ebf6}
.hx-map svg{display:block;width:100%;height:auto}
.hx-towns a{color:var(--ink) !important;text-decoration:none !important}
.hx-towncards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
.hx-towncards>div{background:#fff;border-radius:var(--r-sm);box-shadow:var(--sh);padding:20px 22px}
.v2 .hx-towncards h3{font-size:18px;margin:0 0 6px;display:flex;gap:8px;align-items:center}
.hx-towncards h3 svg{width:18px;height:18px;color:var(--acc)}
.hx-towncards p{margin:0;font-size:15px}
@media (max-width:900px){ .hx-towncards{grid-template-columns:1fr 1fr} }
@media (max-width:560px){ .hx-towncards{grid-template-columns:1fr} }

/* ---------- the request form ---------- */
.hx-form2{display:grid;gap:16px}
.hx-form2 .row{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.v2 .hx-form2 label{display:block;font:650 15px/1.3 var(--f);color:var(--ink);margin:0 0 6px}
.v2 .hx-form2 input,.v2 .hx-form2 textarea,.v2 .hx-form2 select{width:100%;box-sizing:border-box;font:16px/1.4 var(--f);padding:12px 14px;border:1.5px solid #8395a4;border-radius:12px;background:#fff;color:var(--ink)}
.v2 .hx-form2 textarea{min-height:120px;resize:vertical}
.hx-checks{display:flex;flex-wrap:wrap;gap:8px;border:0;padding:0;margin:0}
.hx-checks legend{font:650 15px/1.3 var(--f);color:var(--ink);margin:0 0 8px;padding:0}
.hx-checks label{display:inline-flex !important;align-items:center;gap:8px;margin:0 !important;padding:9px 14px;border:1.5px solid var(--line2);border-radius:999px;font-weight:550 !important;cursor:pointer;min-height:44px;box-sizing:border-box}
.v2 .hx-checks input{width:auto;margin:0;accent-color:var(--acc)}
.hx-formnote{font-size:14px;color:var(--ink3);margin:4px 0 0}
.hx-sent{background:var(--acc-t);border-radius:12px;padding:14px 16px;color:var(--ink);font-weight:550}
@media (max-width:560px){ .hx-form2 .row{grid-template-columns:1fr} }
/* the offer strip on inner pages sits under the hero like home's */
.hx-hours{display:grid;grid-template-columns:auto 1fr;gap:6px 18px;margin:0}
.hx-hours dt{font-weight:650;color:var(--ink)}
.hx-hours dd{margin:0}
/* ⛔ clip, never hidden: overflow-x:hidden on html/body makes the sticky header scroll away */
body.v2{overflow-x:clip}
/* ⛔ the old sheet sets `header nav>ul{flex-wrap:wrap}` and `header nav{min-width:0}` at
   desktop widths; answered at the same weight, or Company drops to a second row */
header.hx-hd nav.hx-nav>ul.hx-nav__list{flex-wrap:nowrap}
header.hx-hd nav.hx-nav{min-width:auto;flex:0 0 auto}
@media (min-width:1024px) and (max-width:1279px){ .hx-hd .hx-brand span{display:none} }
@media (min-width:1024px) and (max-width:1179px){ .hx-nav__list>li:nth-child(4){display:none} }
/* the wash card on a phone, drawn (process step 5) */
.hx-phone{position:absolute;inset:0;background:linear-gradient(180deg,#eef4f8,#dde9f1);display:grid;place-items:center}
.hx-phone__in{width:min(210px,62%);aspect-ratio:9/17;background:#fff;border:9px solid #132230;border-radius:30px;box-shadow:0 18px 40px rgba(19,34,48,.25);padding:22px 14px;display:flex;flex-direction:column;gap:8px;font-size:12px;color:var(--ink2)}
.hx-phone__in b{font:700 13px/1.3 var(--fd);color:var(--ink);margin-bottom:4px}
.hx-phone__in span{display:flex;flex-direction:column;border-top:1px solid var(--line);padding-top:6px;font-weight:650;color:var(--ink)}
.hx-phone__in em{font-style:normal;font-weight:500;color:var(--acc-d)}
.hx-phone__in .no{background:#fff8e0;border-radius:6px;padding:6px;border:0}
.hx-phone__in .no em{color:#8a5a00}
.hx-steps__ph .hx-drawn{position:absolute;inset:0}
/* the wordmark's second half gives way on the narrowest phones so the menu button keeps its room */
@media (max-width:430px){ .hx-hd .hx-brand span{display:none} }
/* the old sheet sets `header nav a{white-space:nowrap}`: menu descriptions must wrap */
header.hx-hd .hx-mega a,header.hx-hd .hx-drop a{white-space:normal}
/* a one-line offer strip, for pages where the four promise cards don't fit */
.v2 .offerbar.hx-slim{background:var(--navy);color:#dfe8ef;border:0;box-shadow:none;margin:0;padding:0}
.hx-slim__in{display:flex;flex-wrap:wrap;gap:8px 18px;align-items:center;justify-content:space-between;padding-top:14px;padding-bottom:14px}
.hx-slim p{margin:0;font-size:15px}
.hx-slim p b{color:#fff}
.hx-slim a{color:var(--btn) !important;font-weight:700;text-decoration:none !important;white-space:nowrap}
.hx-slim a::after{content:" \2192"}
/* hiding is instant (a half-faded button over the phone hero's Call button read as a defect);
   only the reveal fades in */
html[data-hero-in-view] body.v2 .launch{transition:none}

/* ═══ 🧭 HEADER v2 — owner, 30 Sep: "make sure the headers on the demo sites all look way
   better"; utility bar: "social, open closed hours, fake address, phone number, make modern and
   clean that match". Rule 8b of the builder brief.
   ⛔ The social icons go nowhere (href="#"): a made-up handle may be somebody's real page.
   ⛔ The shrink is driven by an IntersectionObserver on .hx-sentinel, not a scroll listener. */
.hx-skip{position:absolute;left:12px;top:-60px;z-index:2000;background:var(--btn);color:var(--btn-ink) !important;font-weight:700;padding:10px 16px;border-radius:10px;text-decoration:none !important}
.hx-skip:focus{top:10px}
.hx-sentinel{position:absolute;top:0;left:0;width:1px;height:1px;pointer-events:none}
/* utility bar */
.v2 .utilbar.hx-ub{background:var(--navy);color:#cfdce7;font:500 13px/1.3 var(--f);border:0}
.hx-ub__in{max-width:1240px;margin:0 auto;padding:0 clamp(16px,4vw,40px);min-height:38px;display:flex;align-items:center;gap:22px}
.hx-ub p{margin:0;display:inline-flex;align-items:center;gap:7px;white-space:nowrap}
.hx-ub svg{width:15px;height:15px;flex:0 0 auto}
.v2 .hx-ub .utilbar__hours{color:#cfdce7}
.v2 .hx-ub .utilbar__hours b{color:#fff;font-weight:650}
.hx-ub .utilbar__dot{width:8px;height:8px;border-radius:50%;background:#8aa0b2;box-shadow:0 0 0 3px rgba(138,160,178,.18);display:inline-block}
.hx-ub .utilbar__hours[data-state="open"] .utilbar__dot{background:#3fd07a;box-shadow:0 0 0 3px rgba(63,208,122,.2)}
.hx-ub .utilbar__hours[data-state="soon"] .utilbar__dot{background:#f5c518;box-shadow:0 0 0 3px rgba(245,197,24,.2)}
.hx-ub .utilbar__hours[data-state="shut"] .utilbar__dot{background:#ff8a7a;box-shadow:0 0 0 3px rgba(255,138,122,.2)}
.hx-ub .utilbar__sep{margin:0 2px;color:#8fa6b8}
.hx-ub__addr svg{color:var(--btn)}
.hx-ub__area{color:#b9cad8}
.hx-ub__area::before{content:"";width:1px;height:14px;background:rgba(255,255,255,.18);margin-right:12px}
.v2 .hx-ub a.hx-ub__tel{margin-left:auto;display:inline-flex;align-items:center;gap:7px;color:#fff !important;font-weight:650;text-decoration:none;white-space:nowrap}
.hx-ub__tel svg{color:var(--btn)}
.hx-ub__soc{display:inline-flex;gap:4px}
.v2 .hx-ub__soc a,.hx-drawer__meta .hx-ub__soc a{width:30px;height:30px;border-radius:8px;display:inline-grid;place-items:center;color:#cfdce7 !important;transition:background .18s ease,color .18s ease}
.v2 .hx-ub__soc a:hover{background:rgba(255,255,255,.1);color:#fff !important}
.v2 .hx-ub .demostrip{display:inline-flex;align-items:center;gap:7px;padding:4px 12px 4px 10px;border-radius:999px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.22);font-size:12px;color:#dfe8ef;white-space:nowrap;text-decoration:none}
.v2 .hx-ub .demostrip b{color:#fff;text-transform:uppercase;letter-spacing:.06em;font-size:11.5px}
.v2 .hx-ub .demostrip .utilbar__demotail{color:#cfdce7}
.hx-ub .demostrip__dot{width:7px;height:7px;border-radius:50%;background:var(--btn);margin:0}
@media (max-width:1279px){ .hx-ub__area{display:none} }
@media (max-width:1100px){ .hx-ub__soc{display:none} }
@media (max-width:900px){ .hx-ub__addr{display:none} .v2 .utilbar__hours.hx-ub__status{display:inline-flex} }
@media (max-width:720px){
  .hx-ub__in{flex-wrap:wrap;gap:6px 14px;padding-top:7px;padding-bottom:7px}
  .v2 .hx-ub a.hx-ub__tel{margin-left:auto}
  .v2 .hx-ub .demostrip{flex:1 1 100%;justify-content:center}
}
/* the header itself: glass, sticky, shrinks on scroll */
header.hx-hd{background:rgba(255,255,255,.92);-webkit-backdrop-filter:saturate(1.5) blur(14px);backdrop-filter:saturate(1.5) blur(14px);border-bottom:1px solid rgba(218,228,236,.9);transition:box-shadow .25s ease}
.hx-hd__in{min-height:78px;transition:min-height .25s ease}
html[data-scrolled] header.hx-hd{box-shadow:0 10px 30px rgba(19,34,48,.10);background:rgba(255,255,255,.95)}
html[data-scrolled] .hx-hd__in{min-height:64px}
/* logo lockup */
.hx-hd .hx-brand,.hx-drawer .hx-brand{gap:11px}
.hx-hd .hx-brand .nav__mark,.hx-drawer .hx-brand .nav__mark{width:46px;height:46px;flex:0 0 auto;transition:width .25s ease,height .25s ease;filter:drop-shadow(0 4px 10px rgba(10,93,143,.22))}
html[data-scrolled] .hx-hd .hx-brand .nav__mark{width:40px;height:40px}
.hx-hd .hx-brand .hx-brand__t,.hx-drawer .hx-brand .hx-brand__t{display:flex;flex-direction:column;gap:3px;color:var(--ink)}
.hx-brand__t b{font:800 22px/1 var(--fd);letter-spacing:-.025em;color:var(--ink)}
.hx-brand__t small{font:650 11px/1.1 var(--f);letter-spacing:.09em;text-transform:uppercase;color:var(--ink3)}
/* nav: animated underline, current page underlined in the accent */
.hx-nav__t{position:relative;background:none !important;border-radius:0;padding:10px 12px}
.hx-nav__t::after{content:"";position:absolute;left:12px;right:12px;bottom:4px;height:2px;border-radius:2px;background:var(--acc);transform:scaleX(0);transform-origin:left center;transition:transform .22s cubic-bezier(.3,.7,.2,1)}
.hx-dd>.hx-nav__t::after{right:30px}
.hx-nav__t:hover::after,.hx-nav__t:focus-visible::after,.hx-dd.is-open>.hx-nav__t::after{transform:scaleX(1)}
.hx-nav__t[aria-current]{color:var(--acc-d) !important}
.hx-nav__t[aria-current]::after{transform:scaleX(1);height:3px;background:var(--btn)}
.hx-nav__t svg{transition:transform .22s ease}
/* phone block + CTA */
.v2 a.hx-tel{display:inline-flex;align-items:center;gap:10px;color:var(--ink) !important}
.hx-tel__i{width:40px;height:40px;border-radius:50%;background:var(--acc-t);color:var(--acc);display:grid;place-items:center;flex:0 0 auto;transition:background .18s ease}
.hx-tel__i svg{width:18px;height:18px}
.hx-tel:hover .hx-tel__i{background:#cfe4f3}
.hx-tel__t{display:flex;flex-direction:column;line-height:1.1}
.hx-tel__t small{font-size:11.5px;font-weight:600;color:var(--ink3);letter-spacing:.02em}
.hx-tel__t b{font-size:15.5px;font-weight:750;color:var(--ink)}
.hx-hd .hx-b.hx-hd__cta{min-height:46px;padding:10px 20px}
.hx-hd .hx-b.hx-hd__cta svg{width:17px;height:17px}
/* round call button and the Menu button: phones and tablets */
.hx-callr{display:none;width:44px;height:44px;border-radius:50%;background:var(--acc);color:#fff !important;place-items:center;flex:0 0 auto;box-shadow:0 6px 16px rgba(10,93,143,.25)}
.hx-callr svg{width:19px;height:19px}
.hx-burger{width:auto;height:44px;padding:0 14px 0 12px;border-radius:999px;gap:8px;flex-direction:row;background:#fff}
.hx-burger__i{display:inline-flex;flex-direction:column;gap:4px;width:18px}
.hx-burger__i i{width:18px !important}
.hx-burger__w{font:700 14.5px/1 var(--f);color:var(--ink)}
.hx-navq:checked ~ .hx-hd .hx-burger i:nth-child(1){transform:translateY(6px) rotate(45deg)}
.hx-navq:checked ~ .hx-hd .hx-burger i:nth-child(3){transform:translateY(-6px) rotate(-45deg)}
.hx-drawer__meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 4px;font-size:14px;color:var(--ink2)}
.hx-drawer__meta>svg{width:16px;height:16px;color:var(--acc)}
.hx-drawer__meta .hx-ub__soc{margin-left:auto}
.hx-drawer__meta .hx-ub__soc a{color:var(--ink2) !important;width:36px;height:36px;border:1px solid var(--line)}
.hx-drawer__meta .hx-ub__soc svg{width:16px;height:16px}
@media (min-width:1024px) and (max-width:1279px){ .hx-hd .hx-brand .hx-brand__t{display:flex} .hx-brand__t b{font-size:20px} }
@media (max-width:1023px){
  .hx-hd__in,html[data-scrolled] .hx-hd__in{min-height:60px}
  .hx-callr{display:grid;margin-left:auto}
  .hx-hd .hx-b.hx-hd__cta{margin-left:auto}
  .hx-hd .hx-b.hx-hd__cta + .hx-callr{margin-left:0}
  .hx-hd .hx-brand .nav__mark,html[data-scrolled] .hx-hd .hx-brand .nav__mark{width:38px;height:38px}
  .hx-brand__t b{font-size:20px}
}
@media (max-width:560px){
  .hx-hd .hx-brand .hx-brand__t{display:flex}
  .hx-hd .hx-brand__t small{display:none}
  .hx-hd__in{gap:10px}
}
@media (max-width:359px){ .hx-burger__w{display:none} .hx-burger{padding:0 12px} }
@media (prefers-reduced-motion:reduce) and (max-width:0px){ .hx-hd__in,.hx-hd .hx-brand .nav__mark,.hx-nav__t::after,header.hx-hd{transition:none !important} }
.hx-hd .hx-burger{margin-left:0}
@media (max-width:560px){ .hx-hd .hx-b.hx-hd__cta + .hx-callr{margin-left:auto} }
/* header v2, first look (30 Sep): fixes */
.hx-ub .utilbar__hours{flex:0 0 auto}
.v2 .hx-ub .demostrip{font-family:var(--f)}
.v2 .hx-ub .demostrip b{font-family:var(--f)}
.hx-ub p.hx-ub__area{display:none}
@media (min-width:1500px){ .hx-ub p.hx-ub__area{display:inline-flex} }
@media (max-width:1100px){ .hx-ub p.hx-ub__addr{display:none} }
@media (max-width:1023px){ .v2 .hx-hd a.hx-tel{display:none} }
@media (min-width:1024px) and (max-width:1279px){ .v2 .hx-hd a.hx-tel{display:none} }
/* the drawer keeps its socials at every width (the utility-bar rule hides them under 1100px) */
.hx-drawer .hx-drawer__meta .hx-ub__soc{display:inline-flex}
.hx-drawer__meta .hx-ub__soc a{width:44px !important;height:44px !important}
/* the drawer's wordmark never runs under the close button */
.hx-drawer__hd .hx-brand{min-width:0;flex:1 1 auto}
.hx-drawer__hd .hx-brand__t small{display:none}
@media (min-width:421px){ .hx-drawer__hd .hx-brand__t small{display:block} }

/* ── 🧲 THE SELLING BAND — 24 Sep 2026 ───────────────────────────────────────────
   Written by tools/demo-sellband.mjs. Read that file before changing anything
   here; it records why the ground is ours and fixed while the accent is derived
   from this folder's own brand token and resolved to a literal.
   ⛔ The ground is deliberately NOT var(--anything): borrowing each site's
      surface made this read as the invented company's own sales copy, and the
      point of the band is that a stranger can see it is not.
   ⛔ The accent paints a pip, a rule and a chevron. It never carries prose. */
.sellband{--sb-accent:#ffc72c;--sb-ground:#0f1b2a;--sb-card:#182b40;
  background:var(--sb-ground);color:#eef3f8;
  border-top:3px solid var(--sb-accent);
  font-family:system-ui,-apple-system,"Segoe UI",sans-serif;
  padding:clamp(2.2rem,5vw,3.4rem) 0;
  /* ⛔ isolate: several demos paint body gradients and grids that would show
     through, and one composites a fixed background over its sections. */
  position:relative;z-index:1;isolation:isolate}
.sellband *{box-sizing:border-box}
/* 🔴 TYPE IS NORMALISED, AND THIS IS NOT FUSSINESS.
   Each demo styles its h2 and h3 with its own display face — rv-powersports
   puts them in condensed UPPERCASE, tree-service in a serif, garage-door in a
   third face. Inheriting those made our pitch render in the invented company's
   own signature type on all thirteen, which is precisely the illusion the band
   exists to step outside of, and it made one heading read as their headline.
   ⛔ Compound selectors (0,2,0) on purpose: a bare .sellband__h loses to a site
   rule such as .sec h2 (0,1,1), and it loses SILENTLY.
   ⛔ No backticks anywhere in this CSS — it lives in a template literal. */
.sellband .sellband__h,.sellband .sellband__k,.sellband .sellband__sum{
  font-family:inherit;text-transform:none;font-style:normal}
.sellband .sellband__h{letter-spacing:-.015em}
.sellband .sellband__k,.sellband .sellband__sum{letter-spacing:0}
.sellband .sellband__h::after,.sellband .sellband__k::after,
.sellband .sellband__h::before,.sellband .sellband__k::before{content:none}
.sellband__in{padding-inline:20px}
/* ⛔ flex-start, not center: the eyebrow wraps to two lines at phone width and
   a centred pip then floats in the gap between them, pointing at nothing. */
.sellband__eyebrow{display:flex;align-items:flex-start;gap:.5rem;margin:0 0 .6rem;
  font-size:.72rem;letter-spacing:.11em;text-transform:uppercase;font-weight:700;
  line-height:1.45;color:#c7d4e0}
.sellband__pip{width:8px;height:8px;border-radius:50%;background:var(--sb-accent);
  flex:0 0 auto;margin-top:.38em}
.sellband__h{margin:0 0 1.5rem;font-size:clamp(1.45rem,3.1vw,2.1rem);line-height:1.18;
  font-weight:800;letter-spacing:-.015em;color:#fff;max-width:32ch}
.sellband__grid{display:grid;gap:1rem;grid-template-columns:repeat(3,1fr);align-items:start}
.sellband__card{background:var(--sb-card);border:1px solid #35506e;border-radius:12px;
  padding:1.1rem 1.15rem 1.2rem;min-width:0}
.sellband__k{margin:0 0 .5rem;font-size:.95rem;font-weight:700;color:#fff;line-height:1.3}
.sellband__p{margin:0 0 .6rem;font-size:.9rem;line-height:1.55;color:#dae3ec}
.sellband__fine{margin:.5rem 0 0;font-size:.8rem;line-height:1.5;color:#b2c0ce}
/* ⛔ Each figure is one unwrappable unit. Without this "$349" and "a month"
   land on different lines and the page appears to publish a bare number. */
.sellband__price{margin:0 0 .6rem;font-size:1.05rem;line-height:1.4;color:#fff;
  font-weight:400;display:flex;flex-wrap:wrap;align-items:baseline;gap:.15rem .45rem}
.sellband__fig{white-space:nowrap}
.sellband__price b{font-size:1.5rem;font-weight:800;letter-spacing:-.01em}
.sellband__dot{color:var(--sb-accent)}

/* the disclosure — it answers "what comes with it" where the question is asked,
   which is the only reason to add one. No JavaScript. */
.sellband__more{margin-top:.7rem;border-top:1px solid #35506e;padding-top:.6rem}
.sellband__sum{display:flex;align-items:center;justify-content:space-between;gap:.5rem;
  cursor:pointer;list-style:none;font-size:.85rem;font-weight:700;color:#fff;
  padding:.2rem 0;min-height:32px}
.sellband__sum::-webkit-details-marker{display:none}
.sellband__chev{width:9px;height:9px;border-right:2px solid var(--sb-accent);
  border-bottom:2px solid var(--sb-accent);transform:rotate(45deg);flex:0 0 auto;
  margin-bottom:3px;transition:transform .18s ease}
.sellband__more[open] .sellband__chev{transform:rotate(-135deg);margin-bottom:-3px}
.sellband__list{margin:.5rem 0 0;padding:0;list-style:none;display:grid;gap:.34rem}
.sellband__list li{position:relative;padding-left:1.05rem;font-size:.84rem;
  line-height:1.5;color:#dae3ec}
.sellband__list li::before{content:"";position:absolute;left:0;top:.52em;
  width:5px;height:5px;border-radius:50%;background:var(--sb-accent)}
.sellband__cta{display:flex;flex-wrap:wrap;align-items:center;gap:.8rem;margin:1.4rem 0 0}
.sellband__go{display:inline-flex;align-items:center;min-height:44px;
  padding:.6rem 1.25rem;border-radius:8px;background:#fff;color:#0f1b2a;
  font-weight:700;font-size:.92rem;text-decoration:none;border:1px solid #fff}
.sellband__go:hover{background:#dce7f2;border-color:#dce7f2}
.sellband__alt{display:inline-flex;align-items:center;min-height:44px;
  color:#dae3ec;font-size:.9rem;text-decoration:none;
  border-bottom:1px solid #46586c;padding-bottom:1px}
.sellband__alt:hover{color:#fff;border-bottom-color:#fff}
@media (prefers-reduced-motion:no-preference){
  .sellband__card{transition:border-color .18s ease}
  .sellband__card:hover{border-color:#4a6a8f}
}
@media (max-width:900px){
  .sellband__grid{grid-template-columns:1fr}
  .sellband__h{max-width:28ch}
}
@media (max-width:560px){
  .sellband__in{padding-inline:16px}
  .sellband__price b{font-size:1.35rem}
}
/* ⛔ Inside our own iframe the surrounding chrome already sells; the band is a
   second pitch on top of the pitch. Hidden there and ONLY there, exactly as the
   disclosure chip is. */
html[data-embedded] .sellband{display:none !important}

/* 1 Oct 2026: at 1024-1199 the header CTA ran off the right edge; the hero
   carries the same CTA, so the header drops it in that band. */
@media (min-width:1024px) and (max-width:1199px){ .hx-hd .hx-b{display:none} }

/* ═══ 📷 PHOTO QUOTES BAND — 1 Oct 2026 ═══════════════════════════════════
   The home-page section for the chat's photo path: three steps, "Start my
   photo quote" and "Watch it work" (both run photo-quote.js). The right-hand
   card is a drawing of the chat, labelled Example. ⛔ No blobs: the only
   gradients here are linear, from an edge. */
.pq-band{--pq-card:var(--card);--pq-line:var(--line);--pq-line2:var(--line2);--pq-ink:var(--ink);--pq-ink2:var(--ink2);--pq-ink3:var(--ink3);--pq-acc:var(--acc-d);--pq-tint:var(--acc-t);--pq-bg:var(--bg);--pq-bg2:var(--bg2);--pq-dark:var(--navy);--pq-f:var(--f);--pq-r:var(--r);--pq-rs:var(--r-sm);--pq-sh:var(--sh);--pq-shl:var(--sh-lg)}
.pq-band__in{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:clamp(28px,4vw,56px);align-items:center}
@media (max-width:959px){.pq-band__in{grid-template-columns:minmax(0,1fr)}}
.pq-band h2{margin-top:0;margin-bottom:14px}
.pq-steps{list-style:none;margin:22px 0 0;padding:0 !important;display:grid;gap:12px}
.pq-steps li{display:flex;gap:14px;align-items:flex-start;margin:0;padding:14px 16px;background:var(--pq-card);border:1px solid var(--pq-line);border-radius:var(--pq-rs);box-shadow:var(--pq-sh)}
.pq-steps__n{flex:0 0 auto;display:grid;place-items:center;width:42px;height:42px;border-radius:12px;background:var(--pq-tint);color:var(--pq-acc)}
.pq-steps__n svg{width:22px;height:22px}
.pq-steps h3{margin:0 0 2px;font-size:17px;line-height:1.3;color:var(--pq-ink)}
.pq-steps p{margin:0;font-size:15px;line-height:1.5;color:var(--pq-ink2)}
.pq-band__btns{display:flex;flex-wrap:wrap;gap:12px;margin-top:24px}
.pq-band__btns svg{width:18px;height:18px;flex:0 0 auto}
.pq-band__btns [data-pq]{display:inline-flex;align-items:center;gap:10px}
.pq-b2{display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:52px;padding:14px 24px;border-radius:999px;background:var(--pq-card);color:var(--pq-ink) !important;font:650 16px/1.2 var(--pq-f);text-decoration:none;border:1.5px solid var(--pq-ink);cursor:pointer;transition:background .18s ease}
.pq-b2:hover{background:var(--pq-tint)}
.pq-b2:focus-visible{outline:3px solid var(--pq-acc);outline-offset:3px}
.pq-band__fine{margin:14px 0 0;font-size:14px;line-height:1.5;color:var(--pq-ink3);max-width:60ch}
.pq-demo{margin:0;padding:clamp(14px,2.6vw,28px);background:var(--pq-bg2);border:1px solid var(--pq-line);border-radius:var(--pq-r)}
.pq-demo__win{display:block;max-width:420px;margin:0 auto;background:var(--pq-card);border:1px solid var(--pq-line);border-radius:18px;box-shadow:var(--pq-shl);overflow:hidden}
.pq-demo__hd{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--pq-dark);color:#fff;font:700 14px/1.3 var(--pq-f)}
.pq-demo__hd i{width:8px;height:8px;border-radius:50%;background:#3ccf7a;flex:0 0 auto}
.pq-demo__bd{display:flex;flex-direction:column;gap:10px;padding:14px;font:400 14px/1.45 var(--pq-f);color:var(--pq-ink)}
.pq-demo__bot{align-self:flex-start;max-width:90%;padding:9px 12px;background:var(--pq-bg);border:1px solid var(--pq-line);border-radius:14px 14px 14px 4px}
.pq-demo__me{align-self:flex-end;display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}
.pq-demo__ph{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;width:78px;min-height:60px;padding:6px 4px;border-radius:10px;background:linear-gradient(180deg,var(--pq-tint),var(--pq-bg2));color:var(--pq-acc);text-align:center}
.pq-demo__ph svg{width:18px;height:18px}
.pq-demo__ph small{font-size:10.5px;line-height:1.2;color:var(--pq-ink2)}
.pq-demo__q{display:block;border:1px solid var(--pq-line);border-radius:14px;overflow:hidden}
.pq-demo__qh{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:9px 12px;background:var(--pq-tint);font-weight:700}
.pq-demo__tag{flex:0 0 auto;padding:4px 7px;border-radius:999px;background:var(--pq-ink);color:#fff;font-size:10.5px;line-height:1;font-weight:800;letter-spacing:.08em;text-transform:uppercase}
.pq-demo__l{display:flex;justify-content:space-between;align-items:baseline;gap:10px;padding:7px 12px;border-top:1px solid var(--pq-line);color:var(--pq-ink2)}
.pq-demo__l b{color:var(--pq-ink);white-space:nowrap}
.pq-demo__t{font-weight:800;color:var(--pq-ink)}
.pq-demo__t b{color:var(--pq-acc);font-size:16px}
.pq-demo__yes{display:flex;flex-wrap:wrap;gap:8px}
.pq-demo__yes span{padding:8px 14px;border-radius:999px;border:1.5px solid var(--pq-line2);font-weight:650}
.pq-demo__yes span:first-child{background:var(--pq-acc);border-color:var(--pq-acc);color:#fff}
.pq-demo figcaption{max-width:420px;margin:12px auto 0;font-size:13px;line-height:1.45;color:var(--pq-ink2);text-align:center}
@media (prefers-reduced-motion:reduce) and (max-width:0px){.pq-b2{transition:none}}

/* 📏 HEADER ROW FIT — 1 Oct 2026. Measured: the row needed 1328px and the header
   is capped at 1240, so the estimate button ran 88px off the edge at EVERY
   desktop width (1280 and 1440 alike), not only at 1280. The number already
   sits in the top bar, so the header's "Call now" block goes below 1440; above
   it the header gets a little more room and tighter gaps. */
@media (min-width:1024px) and (max-width:1439px){ .v2 .hx-hd a.hx-tel{display:none} }
@media (min-width:1280px){ .hx-hd__in{max-width:1400px;gap:14px} }

/* 17 Oct 2026 (sample sweep, defect 7): the top-bar phone link was 16-19 px tall on a phone. Padding grows the tap area to 44 px; the equal
   negative margin keeps the bar exactly as tall as before. */
@media (max-width:820px){a.hx-ub__tel,a.ub-tel,a.tx-ub__tel,a.utilbar__tel,a.ub__tel,a.ub--tel,a.hx-ub--tel,a.hx-util__tel,.utilbar a.utilbar__lnk[href^="tel:"]{padding-block:14px;margin-block:-14px}}

/* kf-chat-css:start — built by tools/build-pressure-washing-chat.mjs (22 Oct 2026, update 2). Edit that, not this block. */
/* 💬 Perch's launcher: 56 px, the designer's exact glyph (blue rounded square, white drop). The button is only the glyph. */
:root[data-theme] body.v2 .launch{width:56px;height:56px;min-height:0;padding:0;border:0;border-radius:16px;background:none;color:var(--acc);box-shadow:0 10px 28px rgba(19,34,48,.28);overflow:visible}
:root[data-theme] body.v2 .launch svg{width:56px;height:56px;fill:none;stroke:none;border-radius:16px;display:block}
body.v2 .launch:focus-visible{outline:3px solid var(--navy);outline-offset:3px;box-shadow:0 0 0 6px #fff,0 10px 28px rgba(19,34,48,.28)}
/* the panel */
body.v2 .chat{display:flex;flex-direction:column;width:min(24rem,calc(100vw - 2rem));height:min(44rem,calc(100dvh - 2rem));max-height:none;border-radius:20px;overflow:hidden;background:#fff}
body.v2 .chat__hd{flex:none;display:flex;align-items:center;gap:12px;padding:10px 12px 10px 14px;border:0;background:var(--navy);color:#fff}
.chat__av{flex:none;display:block;width:32px;height:32px;border-radius:9px}
.chat__av svg{width:32px;height:32px;display:block;fill:none;stroke:none;border-radius:9px;box-shadow:0 0 0 1.5px rgba(255,255,255,.45)}
.chat__who{flex:1 1 auto;min-width:0;display:grid;gap:1px}
body.v2 .chat__who b{display:block;color:#fff;font:700 17px/1.2 var(--fd)}
body.v2 .chat__who .lbl{margin:0;color:#d4e3ef;font:600 12px/1.3 var(--f);letter-spacing:.04em;text-transform:uppercase}
body.v2 .chat__x{flex:none;display:grid;place-items:center;width:44px;height:44px;min-width:44px;min-height:44px;padding:0;border:0;border-radius:12px;background:rgba(255,255,255,.14);color:#fff;cursor:pointer}
body.v2 .chat__x:hover{background:rgba(255,255,255,.26)}
.chat__x svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}
.chat__body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;background:#f6f9fb}
body.v2 .chat__log{flex:1 1 0%;min-height:5rem;overflow-y:auto;overscroll-behavior:contain;padding:14px;background:#f6f9fb}
body.v2 .chat__opts{flex:0 1 auto;max-height:58%;overflow-y:auto;overscroll-behavior:contain;padding:12px 14px 12px;border-top:1px solid var(--line);background:#f6f9fb}
.chat__bar{flex:none;display:grid;gap:8px;padding:8px 14px 10px;border-top:1px solid var(--line);background:#f6f9fb}
.chat__faq{position:absolute;left:0;right:0;bottom:0;max-height:80%;overflow-y:auto;padding:12px 14px 14px;background:#fff;border-top:2px solid var(--acc);box-shadow:0 -12px 28px rgba(19,34,48,.16);z-index:3}
.chat__faqh{margin:0 0 8px;font:700 15px/1.3 var(--fd);color:var(--ink)}
.chat__faq .chip--back{margin-top:6px}
.kf-grp{flex:1 0 100%;display:flex;flex-wrap:wrap;gap:8px}
.kf-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
body.v2 .chat .chip{min-height:44px;padding:9px 16px;font:600 15px/1.2 var(--f);display:inline-flex;align-items:center;justify-content:center;text-decoration:none}
body.v2 .chat .chip--back{border-color:transparent;background:transparent;color:var(--acc-d);padding-left:6px}
body.v2 .chat .chat__ask{flex:1 0 100%;display:flex;gap:8px;align-items:stretch;margin:0}
body.v2 .chat .chat__in{flex:1 1 auto;min-width:0;min-height:44px;font:16px/1.3 var(--f);padding:9px 12px;border-radius:12px;border:1.5px solid #6b7b89;background:#fff;color:var(--ink)}
body.v2 .chat .chat__send{flex:0 0 auto;background:var(--acc);border-color:var(--acc);color:#fff;font-weight:700}
.msg__tag{display:block;margin-top:6px;font-size:12.5px;line-height:1.35;color:#3b4a58}
body.v2 .chat .msg{max-width:88%;overflow-wrap:anywhere}
/* the honesty line and the call link are on every screen */
body.v2 .chat__ft{flex:none;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:0 12px;margin:0;padding:6px 14px 8px;border-top:1px solid var(--line);background:#fff;color:#2b3a47;font:500 12.5px/1.4 var(--f)}
.chat__ft span{flex:1 1 15rem}
.chat__call{display:inline-flex;align-items:center;min-height:44px;font-weight:700;color:var(--acc-d);text-underline-offset:3px;white-space:nowrap}
.chat :focus-visible{outline:3px solid var(--acc);outline-offset:2px}
.chat__hd :focus-visible{outline-color:#fff}
@media (max-width:520px){body.v2 .chat{left:8px;right:8px;bottom:8px;width:auto;height:min(46rem,calc(100dvh - 16px));border-radius:18px}}
/* footer legal links (Privacy, Accessibility) sit in a line of text: underlined so they are not told apart by colour alone */
.hx-ft__legal a{text-decoration:underline;text-underline-offset:3px}
/* the engine's end-of-flow buttons (Replay, Try it myself, Start over) sit outside its step box and came unstyled and 21px high */
body.v2 .chat .chat__opts>.pq-chip{min-height:44px;padding:9px 16px;border:1.5px solid var(--line2);border-radius:999px;background:#fff;color:var(--ink);font:600 15px/1.2 var(--f);cursor:pointer}
body.v2 .chat .chat__opts>.pq-chip:hover{border-color:var(--acc);color:var(--acc-d)}
/* on a phone the panel is full width: the options tab on the left edge would sit on top of its buttons, so it steps aside while the chat is open */
@media (max-width:520px){html:has(.chat:not([hidden])) .a11y{display:none}}
/* the engine's Watch-it-work buttons were 36px: 44px here */
body.v2 .chat .pq-ctl button{min-height:44px;padding:8px 16px}
/* the engine's controls under the accessibility options */
html[data-a11y-contrast] .pq-step .pq-chip[aria-pressed="true"]::before{content:"\2713\00a0"}
html[data-a11y-contrast][data-a11y-theme] body .chat .chat__body .pq-step .pq-chip[aria-pressed="true"],
html[data-a11y-contrast][data-a11y-theme] body .chat .chat__body .pq-step .pq-tile[aria-pressed="true"]{outline:3px solid currentColor!important;outline-offset:-3px;font-weight:800!important}
html[data-a11y-contrast][data-a11y-theme] body .chat .chat__body .pq-step .pq-bar{border:2px solid currentColor!important}
html[data-a11y-contrast][data-a11y-theme] body .chat .chat__body .pq-step .pq-bar i,
html[data-a11y-contrast][data-a11y-theme] body .chat .chat__body .pq-step .pq-dots i.on{background-color:currentColor!important}
html[data-a11y-contrast][data-a11y-theme] body .chat .chat__body .pq-step .pq-dots i{border:1px solid currentColor!important}
html[data-a11y-contrast][data-a11y-theme] body .chat .chat__body .msg--me{border:3px solid #000!important}
html[data-a11y-size="2"] body.v2 .chat{height:calc(min(44rem,100dvh - 2rem)/1.25)}
html[data-a11y-size="1"] body.v2 .chat{height:calc(min(44rem,100dvh - 2rem)/1.12)}
/* kf-chat-css:end */
