/* ═══════════════════════════════════════════════════════════════════════════
   ⛔ HAND-EDITED ON PURPOSE — 23 Sep 2026. THIS FILE HAS DIVERGED FROM ITS
   SOURCE, AND THE DIVERGENCE IS THE POINT.

   public/demo/<name> is normally GENERATED from ~/Desktop/demo-<name>/dist by
   tools/sync-demo.mjs, and the generated headers say never to hand-edit it.
   The hero, type and offer-strip pass below was written HERE, directly, with
   the owner's decision, because:

   - the sources live OUTSIDE this repo, are untracked and are unbacked; and
   - for at least one demo the source provably does NOT reproduce the published
     copy — a rebuild loses photo bands, self-hosted fonts and the town.

   public/demo IS committed to git, so this work is recoverable and reviewable
   and the source is neither.

   ⛔ THEREFORE: do NOT run tools/sync-demo.mjs or any build.py against this
   folder. A sync would silently revert everything below and every page would
   still render, which is exactly the failure mode that costs days here.
   Reconcile the source to THIS file first, or delete the source.
   ═══════════════════════════════════════════════════════════════════════════ */
/* Halyard Roofing — demonstration site. FIELD REPORT.
   System fonts only. No external requests of any kind. */

:root{
  --ink:#080b10;
  --slate:#0e141b;
  --slate2:#131c25;
  --panel:#111922;
  --line:#26323d;
  --line-soft:#1b242d;
  --fog:#93a5b5;
  --fog-dim:#69798a;
  --paper:#d9e2ea;
  --white:#f3f8fc;
  --signal:#ff6a2b;
  --cond:"Arial Narrow","Helvetica Neue Condensed","Liberation Sans Narrow","Roboto Condensed","Segoe UI Condensed",system-ui,sans-serif;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono","Courier New",monospace;
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  background:var(--slate);
  color:var(--paper);
  font-family:var(--sans);
  font-size:16px;
  line-height:1.68;
  overflow-x:hidden;
}
img,svg{max-width:100%}
a{color:var(--white);text-decoration:underline;text-decoration-color:var(--fog-dim);text-underline-offset:3px}
a:hover{text-decoration-color:var(--white)}
p{margin:0 0 1.05em}
ul,ol{margin:0 0 1.15em;padding-left:1.15em}
li{margin:0 0 .5em}
hr{border:0;border-top:1px solid var(--line-soft);margin:34px 0}

.wrap{width:100%;max-width:1140px;margin:0 auto;padding:0 18px}
.narrow{max-width:760px}

/* ---------- demonstration strip ---------- */
.demostrip{
  background:#111a23;
  border-bottom:1px solid var(--line);
  color:var(--fog);
  font-size:12.5px;
  line-height:1.5;
  padding:9px 18px;
  display:flex;
  gap:9px;
  align-items:flex-start;
}
.demostrip b{color:var(--white);font-weight:600}
.demostrip a{color:var(--paper)}
.demostrip__dot{
  flex:0 0 auto;width:8px;height:8px;border-radius:50%;
  background:#5fd0c6;margin-top:6px;
}

/* ---------- type ---------- */
h1,h2,h3,.cond{
  font-family:var(--cond);
  font-stretch:condensed;
  text-transform:uppercase;
  font-weight:700;
  letter-spacing:-0.015em;
  line-height:0.96;
  color:var(--white);
  overflow-wrap:break-word;
  margin:0 0 .5em;
}
h1{font-size:clamp(34px,7.4vw,78px);line-height:0.92;letter-spacing:-0.022em}
h2{font-size:clamp(25px,4.6vw,42px)}
h3{font-size:clamp(18px,3vw,23px);letter-spacing:-0.005em}
h4{font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;letter-spacing:.02em;font-size:17px;color:var(--white);margin:0 0 .35em}
.eyebrow{
  font-family:var(--mono);font-size:11.5px;text-transform:uppercase;
  letter-spacing:.17em;color:var(--fog-dim);margin:0 0 18px;
}
.lede{font-size:clamp(16.5px,2vw,19px);color:var(--paper)}
.muted{color:var(--fog)}
.mono{font-family:var(--mono)}
.tel{font-family:var(--mono);color:var(--signal);text-decoration:none;letter-spacing:-.01em;white-space:nowrap}
.tel:hover{text-decoration:underline;text-decoration-color:var(--signal)}

/* ---------- nav ---------- */
.nav{background:var(--ink);border-bottom:1px solid var(--line)}
.nav__in{display:flex;flex-wrap:wrap;align-items:center;gap:10px 20px;padding:13px 0}
.nav__brand{
  font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;
  font-size:21px;letter-spacing:-.01em;color:var(--white);text-decoration:none;
  white-space:nowrap;font-weight:700;
}
.nav__brand span{color:var(--fog-dim)}
.nav__links{display:flex;flex-wrap:wrap;gap:8px 16px;list-style:none;margin:0;padding:0}
.nav__links a{
  font-family:var(--mono);font-size:11.5px;text-transform:uppercase;letter-spacing:.09em;
  color:var(--fog);text-decoration:none;
}
.nav__links a:hover{color:var(--white)}
.nav__links a[aria-current]{color:var(--white)}
.nav__tel{margin-left:auto;font-size:15px}

/* ---------- sections ---------- */
.hero{background:var(--ink);padding:52px 0 60px;border-bottom:1px solid var(--line)}
.sec{padding:56px 0;border-top:1px solid var(--line-soft)}
.sec--tight{padding:40px 0}
.sec--dark{background:var(--ink)}
.sec:first-of-type{border-top:0}

.grid2{display:grid;grid-template-columns:1fr;gap:34px}
@media(min-width:900px){
  .grid2{grid-template-columns:1fr 1fr;gap:44px;align-items:start}
  .grid2--wide-right{grid-template-columns:0.85fr 1.15fr}
}
.grid3{display:grid;grid-template-columns:1fr;gap:26px}
@media(min-width:760px){.grid3{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){.grid3{grid-template-columns:repeat(3,1fr)}}

/* ---------- roofline illustration ---------- */
.scene{display:block;width:100%;height:auto;border:1px solid var(--line);background:#0b1219}
.scene__cap{font-family:var(--mono);font-size:11px;color:var(--fog-dim);margin-top:9px;letter-spacing:.04em}

/* ---------- the report ---------- */
.rep{background:var(--panel);border:1px solid var(--line)}
.rep__hd{padding:15px 16px;border-bottom:1px solid var(--line);background:#0c141c}
.rep__title{
  font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;
  font-size:20px;letter-spacing:.01em;color:var(--white);margin:0 0 8px;font-weight:700;
}
.rep__meta{display:grid;grid-template-columns:1fr;gap:2px;font-family:var(--mono);font-size:11.5px;color:var(--fog)}
@media(min-width:520px){.rep__meta{grid-template-columns:1fr 1fr;gap:2px 16px}}
.rep__meta b{color:var(--paper);font-weight:400}
.rep__row{padding:13px 16px;border-bottom:1px solid var(--line-soft)}
.rep__top{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.rep__no{font-family:var(--mono);font-size:11px;color:var(--fog-dim);letter-spacing:.08em}
.rep__item{
  font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;font-weight:700;
  font-size:16px;letter-spacing:.005em;color:var(--white);margin:1px 0 4px;
}
.rep__note{font-size:13.5px;line-height:1.55;color:var(--fog);margin:0}
.rep__cost{font-family:var(--mono);font-size:13px;color:var(--paper);white-space:nowrap;text-align:right}
.rep__row--declined{
  background:#17110c;
  border-left:3px solid var(--signal);
  border-bottom:1px solid var(--line-soft);
  padding-left:13px;
}
.rep__row--declined .rep__item,
.rep__row--declined .rep__cost,
.rep__row--declined .rep__no{color:var(--signal)}
.rep__row--declined .rep__item{font-size:18px}
.rep__row--declined .rep__note{color:var(--paper);font-size:14px}
.rep__foot{padding:14px 16px;background:#0c141c;display:flex;justify-content:space-between;gap:12px;align-items:baseline}
.rep__foot .rep__cost{font-size:15px;color:var(--white)}
.rep__sig{padding:12px 16px;border-top:1px solid var(--line-soft);font-family:var(--mono);font-size:11px;color:var(--fog-dim);line-height:1.6}

/* ---------- numbered index list ---------- */
.idx{border-top:1px solid var(--line);margin:0;padding:0;list-style:none}
.idx li{margin:0}
.idx__row{
  display:grid;grid-template-columns:40px 1fr;gap:2px 12px;
  padding:19px 0;border-bottom:1px solid var(--line);
  text-decoration:none;color:inherit;
}
@media(min-width:760px){.idx__row{grid-template-columns:64px 1fr 26px;gap:4px 18px;align-items:baseline}}
.idx__row:hover{background:#111a23}
.idx__n{font-family:var(--mono);font-size:12px;color:var(--fog-dim);letter-spacing:.06em;padding-top:4px}
.idx__t{
  font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;font-weight:700;
  font-size:clamp(21px,3.6vw,30px);letter-spacing:-.012em;line-height:1;color:var(--white);
}
.idx__d{grid-column:2;font-size:14.5px;color:var(--fog);margin:6px 0 0;line-height:1.55}
.idx__ar{display:none;color:var(--fog-dim);font-family:var(--mono)}
@media(min-width:760px){.idx__ar{display:block;grid-row:1;grid-column:3;text-align:right}}
.idx__row:hover .idx__ar{color:var(--white)}

/* ---------- spec blocks (published numbers) ---------- */
.spec{border:1px solid var(--line);background:var(--panel);margin:0 0 22px}
.spec__hd{padding:11px 14px;border-bottom:1px solid var(--line);background:#0c141c;
  font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--fog)}
.spec dl{margin:0}
.spec__row{display:grid;grid-template-columns:1fr;gap:1px 14px;padding:11px 14px;border-bottom:1px solid var(--line-soft)}
@media(min-width:560px){.spec__row{grid-template-columns:170px 1fr;align-items:baseline}}
.spec__row:last-child{border-bottom:0}
.spec dt{font-family:var(--mono);font-size:11.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--fog-dim)}
.spec dd{margin:0;font-family:var(--mono);font-size:13.5px;color:var(--white);line-height:1.5}
.spec dd em{font-style:normal;color:var(--fog);font-family:var(--sans);font-size:13.5px}

/* ---------- callout ---------- */
.call{border:1px solid var(--line);border-left:3px solid var(--signal);background:#141019;background:#12161c;padding:20px 20px;margin:0 0 24px}
.call h3{margin-bottom:.35em}
.call p:last-child{margin-bottom:0}
.note{border:1px solid var(--line-soft);background:var(--panel);padding:16px 18px;margin:0 0 22px}
.note p:last-child{margin-bottom:0}
.note h4{margin-bottom:.4em}

/* ---------- steps ---------- */
.steps{list-style:none;margin:0;padding:0;counter-reset:s}
.steps>li{
  counter-increment:s;position:relative;
  padding:22px 0 22px 56px;border-bottom:1px solid var(--line-soft);
}
.steps>li:before{
  content:"0" counter(s);position:absolute;left:0;top:24px;
  font-family:var(--mono);font-size:12px;color:var(--fog-dim);letter-spacing:.08em;
}
.steps h3{margin-bottom:.3em}
.steps p:last-child{margin-bottom:0}

/* ---------- generic table (scrolls, never widens the page) ---------- */
.tablewrap{overflow-x:auto;border:1px solid var(--line);margin:0 0 22px}
table{border-collapse:collapse;width:100%;min-width:460px;font-size:14px}
th,td{text-align:left;padding:10px 13px;border-bottom:1px solid var(--line-soft);vertical-align:top}
th{font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.1em;color:var(--fog-dim);background:#0c141c}
td.num{font-family:var(--mono);white-space:nowrap}

/* ---------- buttons ---------- */
.btn{
  display:inline-block;font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;
  font-weight:700;letter-spacing:.02em;font-size:16px;
  padding:12px 22px;border:1px solid var(--paper);color:var(--ink);background:var(--paper);
  text-decoration:none;
}
.btn:hover{background:var(--white);border-color:var(--white)}
.btn--ghost{background:transparent;color:var(--white);border-color:var(--line)}
.btn--ghost:hover{background:transparent;border-color:var(--paper)}
.btnrow{display:flex;flex-wrap:wrap;gap:12px;align-items:center;margin:22px 0 0}

/* ---------- areas ---------- */
.chips{display:flex;flex-wrap:wrap;gap:8px;list-style:none;margin:0 0 20px;padding:0}
.chips li{
  font-family:var(--mono);font-size:12px;color:var(--paper);
  border:1px solid var(--line);padding:6px 11px;background:var(--panel);
}

/* ---------- forms ---------- */
.form{border:1px solid var(--line);background:var(--panel);padding:20px}
.field{margin:0 0 16px}
.field label{display:block;font-family:var(--mono);font-size:11.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--fog);margin:0 0 6px}
.field input,.field select,.field textarea{
  width:100%;background:#0b1219;border:1px solid var(--line);color:var(--white);
  font-family:var(--sans);font-size:15px;padding:10px 11px;border-radius:0;
}
.field textarea{min-height:120px;resize:vertical}
.field input:focus,.field select,.field textarea:focus{outline:2px solid var(--fog-dim);outline-offset:1px}
.field--split{display:grid;grid-template-columns:1fr;gap:16px}
@media(min-width:600px){.field--split{grid-template-columns:1fr 1fr}}
.formnote{font-size:13px;color:var(--fog);margin:14px 0 0}

/* ---------- empty state (reviews) ---------- */
.empty{
  border:1px dashed var(--line);background:var(--panel);
  padding:36px 22px;text-align:center;margin:0 0 24px;
}
.empty__mark{font-family:var(--mono);font-size:12px;letter-spacing:.16em;color:var(--fog-dim);text-transform:uppercase}
.empty h3{margin:14px 0 .4em}
.empty p{color:var(--fog);max-width:52ch;margin-left:auto;margin-right:auto}

/* ---------- faq ---------- */
.qa{border-top:1px solid var(--line-soft);padding:22px 0}
.qa h3{margin-bottom:.4em}
.qa p:last-child{margin-bottom:0}

/* ---------- footer ---------- */
.foot{background:var(--ink);border-top:1px solid var(--line);padding:44px 0 34px;margin-top:8px}
.foot__grid{display:grid;grid-template-columns:1fr;gap:26px}
@media(min-width:700px){.foot__grid{grid-template-columns:1.3fr 1fr 1fr}}
.foot h4{color:var(--white)}
.foot ul{list-style:none;margin:0;padding:0}
.foot li{margin:0 0 7px}
.foot a{color:var(--fog);text-decoration:none;font-size:14px}
.foot a:hover{color:var(--white)}
.foot__addr{font-family:var(--mono);font-size:12.5px;color:var(--fog);line-height:1.75}
.foot__legal{margin-top:30px;padding-top:18px;border-top:1px solid var(--line-soft);
  font-size:12px;color:var(--fog-dim);line-height:1.7}

/* ---------- chat launcher (bottom LEFT — host page owns bottom right) ---------- */
.chatbtn{
  position:fixed;left:16px;bottom:16px;z-index:80;
  display:flex;align-items:center;gap:9px;
  background:var(--paper);color:var(--ink);border:0;
  font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;font-weight:700;
  letter-spacing:.03em;font-size:15px;padding:11px 16px;cursor:pointer;
  box-shadow:0 6px 24px rgba(0,0,0,.5);
}
.chatbtn:hover{background:#fff}
.chatbtn__dot{width:8px;height:8px;border-radius:50%;background:#1d8f7e;flex:0 0 auto}
.chatpanel{
  position:fixed;left:16px;bottom:16px;z-index:81;
  width:min(340px,calc(100vw - 32px));
  max-height:min(74vh,540px);
  display:none;flex-direction:column;
  background:var(--slate2);border:1px solid var(--line);
  box-shadow:0 10px 40px rgba(0,0,0,.6);
}
.chatpanel[data-open="1"]{display:flex}
.chatpanel__hd{display:flex;align-items:center;gap:10px;padding:11px 12px;border-bottom:1px solid var(--line);background:var(--ink)}
.chatpanel__hd strong{font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;font-size:15px;letter-spacing:0;color:var(--white)}
.chatpanel__hd small{font-family:var(--mono);font-size:10px;color:var(--fog-dim);letter-spacing:.1em;text-transform:uppercase;display:block}
.chatpanel__x{margin-left:auto;background:none;border:0;color:var(--fog);font-size:20px;line-height:1;cursor:pointer;padding:2px 4px}
.chatpanel__x:hover{color:var(--white)}
.chatlog{flex:1 1 auto;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:9px}
.bub{max-width:88%;padding:9px 11px;font-size:13.5px;line-height:1.5}
.bub--bot{background:var(--panel);border:1px solid var(--line);color:var(--paper);align-self:flex-start}
.bub--me{background:var(--paper);color:var(--ink);align-self:flex-end}
.chatopts{display:flex;flex-wrap:wrap;gap:7px;padding:0 12px 12px}
.chatopts button{
  background:transparent;border:1px solid var(--line);color:var(--paper);
  font-family:var(--sans);font-size:12.5px;padding:7px 10px;cursor:pointer;text-align:left;
}
.chatopts button:hover{border-color:var(--paper);color:var(--white)}
.chatform{display:none;gap:7px;padding:0 12px 12px}
.chatform[data-on="1"]{display:flex}
.chatform input{flex:1 1 auto;min-width:0;background:#0b1219;border:1px solid var(--line);color:var(--white);font-size:13.5px;padding:8px 10px;font-family:var(--sans)}
.chatform button{background:var(--paper);border:0;color:var(--ink);font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;font-weight:700;font-size:13px;padding:8px 12px;cursor:pointer}
.chatfoot{padding:9px 12px;border-top:1px solid var(--line);font-family:var(--mono);font-size:10px;color:var(--fog-dim);line-height:1.5}

@media print{.chatbtn,.chatpanel{display:none}}

/* ==========================================================================
   FIELD REPORT — depth, color, motion, illustration.
   Everything below is gradients, hairlines and inline SVG.
   Still zero external requests. System fonts only.
   ========================================================================== */

:root{
  /* values WITHIN the existing palette, so nothing is a flat fill */
  --ink-2:#0a0f16;
  --ink-lift:#0d141d;
  --slate-hi:#16202b;
  --slate-lo:#0b1119;
  --edge-hi:rgba(255,255,255,.055);   /* light falling on a top edge   */
  --edge-hi-2:rgba(255,255,255,.085);
  --signal:#ff6a2b;
  --signal-lo:#c14a17;
  --signal-glow:rgba(255,106,43,.16);
  /* second accent, drawn from roofing itself: aged copper patina.
     Used for ONE thing — the published specification numbers. */
  --patina:#6fae8f;
  --patina-dim:#4d7f68;
  --t-fast:150ms;
  --t-mid:200ms;
  --t-slow:250ms;
  --ease:cubic-bezier(.22,.68,.35,1);
}

/* ---------- ground: give the page dimension ---------- */
body{
  background-color:var(--slate);
  background-image:
    radial-gradient(120% 62% at 50% 0%, #131d27 0%, rgba(19,29,39,0) 62%),
    linear-gradient(180deg,#0e141b 0%,#0c121a 55%,#0a1017 100%);
  background-attachment:fixed,fixed;
}

/* ---------- type: tighter scale, more confident headlines ---------- */
h1{font-size:clamp(35px,7.9vw,88px);line-height:.9;letter-spacing:-.025em}
h2{font-size:clamp(26px,4.9vw,47px);line-height:.94;letter-spacing:-.019em}
h3{font-size:clamp(18.5px,3vw,24px)}
.lede{font-size:clamp(17px,2.05vw,20.5px);line-height:1.56;color:var(--paper)}
.eyebrow{
  position:relative;padding-left:32px;
  letter-spacing:.19em;color:var(--fog-dim);margin:0 0 20px;
}
.eyebrow::before{
  content:"";position:absolute;left:0;top:.72em;width:22px;height:1px;
  background:linear-gradient(90deg,var(--signal),rgba(255,106,43,0));
}

/* ---------- links, everything moves a little ---------- */
a{transition:color var(--t-fast) var(--ease),text-decoration-color var(--t-fast) var(--ease)}
.tel{transition:color var(--t-fast) var(--ease),text-shadow var(--t-mid) var(--ease)}
.tel:hover{color:#ff8a55;text-shadow:0 0 18px var(--signal-glow)}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{
  outline:2px solid var(--signal);outline-offset:2px;
}

/* ---------- demonstration strip: keep it plain, just breathe ---------- */
.demostrip{
  background-image:linear-gradient(180deg,#13202b,#0e1720);
  box-shadow:inset 0 1px 0 var(--edge-hi);
}
.demostrip__dot{box-shadow:0 0 0 3px rgba(95,208,198,.14)}
@media(prefers-reduced-motion:no-preference){
  .demostrip__dot{animation:pulse 3.4s ease-in-out infinite}
}
@keyframes pulse{
  0%,100%{box-shadow:0 0 0 3px rgba(95,208,198,.13)}
  50%{box-shadow:0 0 0 5px rgba(95,208,198,.05)}
}

/* ---------- nav ---------- */
.nav{
  background-image:linear-gradient(180deg,#0c131b 0%,#080b10 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.035),0 1px 0 rgba(0,0,0,.5);
}
.nav__links a{position:relative;padding-bottom:3px;transition:color var(--t-fast) var(--ease)}
.nav__links a::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:var(--signal);transform:scaleX(0);transform-origin:left;
  transition:transform var(--t-mid) var(--ease);
}
.nav__links a:hover::after,.nav__links a[aria-current]::after{transform:scaleX(1)}
.nav__brand{transition:letter-spacing var(--t-slow) var(--ease)}
.nav__brand span{transition:color var(--t-mid) var(--ease)}
.nav__brand:hover span{color:var(--signal)}

/* ---------- hero: a sky with tonal life, and an eave along the bottom ---------- */
.hero{
  position:relative;
  padding:52px 0 56px;
  background-color:var(--ink);
  background-image:
    radial-gradient(88% 78% at 76% 4%, #1b2b3a 0%, rgba(27,43,58,0) 58%),
    radial-gradient(70% 60% at 8% 96%, rgba(255,106,43,.075) 0%, rgba(255,106,43,0) 60%),
    linear-gradient(180deg,#0a1119 0%,#080c12 46%,#0b1219 100%);
  background-size:180% 160%,140% 140%,100% 100%;
  background-position:60% 0,0 100%,0 0;
  border-bottom:1px solid var(--line);
  overflow:hidden;
}
@media(prefers-reduced-motion:no-preference){
  .hero{animation:skydrift 54s ease-in-out infinite alternate}
}
@keyframes skydrift{
  from{background-position:38% 0,0 100%,0 0}
  to{background-position:76% 6%,10% 92%,0 0}
}
.hero .wrap{position:relative;z-index:2}
/* two staggered courses of shingle running along the foot of every hero */
.hero::before,.hero::after{
  content:"";position:absolute;left:0;right:0;height:15px;pointer-events:none;z-index:1;
  background-repeat:repeat-x;
}
.hero::before{
  bottom:15px;
  background-color:rgba(0,0,0,.20);
  background-image:repeating-linear-gradient(90deg,rgba(0,0,0,.34) 0 1.5px,rgba(0,0,0,0) 1.5px 46px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.038);
}
.hero::after{
  bottom:0;
  background-color:rgba(0,0,0,.34);
  background-image:repeating-linear-gradient(90deg,rgba(0,0,0,.40) 0 1.5px,rgba(0,0,0,0) 1.5px 46px);
  background-position:23px 0;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
}

/* ---------- section rhythm: bands, not one continuous color ---------- */
.sec{
  position:relative;
  padding:74px 0;
  border-top:1px solid var(--line-soft);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.028);
}
@media(max-width:700px){.sec{padding:52px 0}}
.sec--tight{padding:46px 0}
.sec--dark{
  background-color:var(--ink);
  background-image:
    linear-gradient(180deg,rgba(255,255,255,.030) 0,rgba(255,255,255,0) 190px),
    radial-gradient(90% 55% at 84% 0%, rgba(111,174,143,.055) 0%, rgba(111,174,143,0) 60%),
    linear-gradient(180deg,#0a0f16 0%,#080b10 60%,#090e15 100%);
  box-shadow:inset 0 1px 0 var(--edge-hi);
}
/* a repeating course pattern, pure gradients, no image files */
.sec--courses{
  background-image:
    repeating-linear-gradient(180deg,rgba(255,255,255,.026) 0 1px,rgba(255,255,255,0) 1px 28px),
    repeating-linear-gradient(180deg,rgba(0,0,0,.22) 0 1px,rgba(0,0,0,0) 1px 112px),
    linear-gradient(180deg,#101922 0%,#0c131b 100%);
  box-shadow:inset 0 1px 0 var(--edge-hi);
}

/* ---------- the report: an object on the page ---------- */
.rep{
  position:relative;
  background-image:linear-gradient(180deg,#141d27 0%,#101821 40%,#0e1620 100%);
  box-shadow:
    inset 0 1px 0 var(--edge-hi-2),
    0 1px 0 rgba(0,0,0,.7),
    0 26px 54px -26px rgba(0,0,0,.95),
    0 3px 0 -1px rgba(255,255,255,.02);
}
.rep::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:2px;
  background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,0) 30%);
}
.rep__hd{
  background-image:linear-gradient(180deg,#101a24,#0a1119);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
}
.rep__title{position:relative;padding-left:14px}
.rep__title::before{
  content:"";position:absolute;left:0;top:.22em;bottom:.22em;width:3px;
  background:linear-gradient(180deg,var(--signal),var(--signal-lo));
}
.rep__row{transition:background-color var(--t-mid) var(--ease)}
.rep__row:hover{background-color:rgba(255,255,255,.022)}
.rep__row--declined{
  background-color:#17110c;
  background-image:
    linear-gradient(90deg,rgba(255,106,43,.10) 0%,rgba(255,106,43,.02) 42%,rgba(255,106,43,0) 100%),
    linear-gradient(180deg,rgba(255,255,255,.03),rgba(255,255,255,0));
  border-left-width:4px;
  box-shadow:inset 0 1px 0 rgba(255,106,43,.16),inset 0 -1px 0 rgba(255,106,43,.10);
}
.rep__row--declined:hover{background-color:#1c130c}
.rep__row--declined .rep__cost{
  font-size:15px;letter-spacing:.02em;
  text-shadow:0 0 20px rgba(255,106,43,.42);
}
@media(prefers-reduced-motion:no-preference){
  .rep__row--declined .rep__cost{animation:declined 5.5s ease-in-out infinite}
}
@keyframes declined{
  0%,100%{text-shadow:0 0 20px rgba(255,106,43,.34)}
  50%{text-shadow:0 0 26px rgba(255,106,43,.62)}
}
.rep__foot{
  background-image:linear-gradient(180deg,#0d151e,#090f16);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
}

/* ---------- numbered index: the rows come alive ---------- */
.idx{border-top:1px solid var(--line)}
.idx__row{
  position:relative;overflow:hidden;
  transition:background-color var(--t-mid) var(--ease),padding-left var(--t-mid) var(--ease);
}
.idx__row::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:2px;
  background:linear-gradient(180deg,var(--signal),var(--signal-lo));
  transform:scaleY(0);transform-origin:top;
  transition:transform var(--t-slow) var(--ease);
}
.idx__row:hover,.idx__row:focus-visible{
  background-color:transparent;
  background-image:linear-gradient(90deg,rgba(255,106,43,.075) 0%,rgba(255,106,43,.012) 34%,rgba(255,255,255,0) 72%);
  padding-left:12px;
}
.idx__row:hover::before,.idx__row:focus-visible::before{transform:scaleY(1)}
.idx__t{transition:color var(--t-mid) var(--ease)}
.idx__row:hover .idx__t{color:var(--signal)}
.idx__n{
  display:flex;flex-direction:column;align-items:flex-start;gap:10px;
  padding-top:3px;
}
.idx__ar{transition:transform var(--t-mid) var(--ease),color var(--t-mid) var(--ease)}
.idx__row:hover .idx__ar{transform:translateX(5px);color:var(--signal)}

/* service icons — one per service, each drawn separately */
.svcico{
  display:block;width:30px;height:30px;flex:0 0 auto;
  color:var(--fog-dim);
  transition:color var(--t-mid) var(--ease),transform var(--t-slow) var(--ease);
}
.idx__row:hover .svcico{color:var(--signal);transform:translateY(-2px)}
@media(max-width:759px){.svcico{width:26px;height:26px}}

/* ---------- spec blocks: the published numbers, in patina ---------- */
.spec{
  background-image:linear-gradient(180deg,#131c26 0%,#0f1720 100%);
  box-shadow:inset 0 1px 0 var(--edge-hi),0 14px 30px -22px rgba(0,0,0,.9);
}
.spec__hd{
  background-image:linear-gradient(180deg,#0f1720,#0a1017);
  color:var(--patina);
  box-shadow:inset 0 1px 0 rgba(111,174,143,.10);
  position:relative;padding-left:26px;
}
.spec__hd::before{
  content:"";position:absolute;left:13px;top:50%;width:6px;height:6px;margin-top:-3px;
  background:var(--patina);box-shadow:0 0 10px rgba(111,174,143,.55);
}
.spec dt{color:var(--patina-dim)}
.spec__row{transition:background-color var(--t-fast) var(--ease)}
.spec__row:hover{background-color:rgba(111,174,143,.035)}

/* ---------- diagrams in the body ---------- */
.dgm{
  margin:0;padding:0;border-bottom:1px solid var(--line-soft);
  background-image:linear-gradient(180deg,#0c131b,#0a1017);
}
.dgm svg{display:block;width:100%;height:auto}
.dgm__cap{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.05em;color:var(--fog-dim);
  margin:0;padding:8px 14px 11px;line-height:1.55;border-top:1px solid var(--line-soft);
}
.dgm__cap b{color:var(--patina);font-weight:400}

/* ---------- the roofline scene ---------- */
.scene{
  background:#0b1219;
  box-shadow:inset 0 1px 0 var(--edge-hi),0 20px 44px -26px rgba(0,0,0,.95);
}
.scene__cap{letter-spacing:.045em;line-height:1.6}
/* one ambient moment: light crossing the roof plane, very slowly */
@media(prefers-reduced-motion:no-preference){
  .scene .sun-sweep{animation:sweep 17s cubic-bezier(.5,0,.5,1) infinite}
  .scene .cloud-a{animation:drift-a 74s linear infinite}
  .scene .cloud-b{animation:drift-b 110s linear infinite}
  .scene .beacon{animation:beacon 4.2s ease-in-out infinite}
}
@keyframes sweep{
  0%{transform:translateX(-320px);opacity:0}
  12%{opacity:.5}
  88%{opacity:.5}
  100%{transform:translateX(700px);opacity:0}
}
@keyframes drift-a{from{transform:translateX(-180px)}to{transform:translateX(700px)}}
@keyframes drift-b{from{transform:translateX(-260px)}to{transform:translateX(760px)}}
@keyframes beacon{0%,100%{opacity:.25}50%{opacity:.9}}

/* ---------- designed slots where a photograph belongs ---------- */
.pslot{margin:0 0 26px;padding:0}
.pslot__frame{
  position:relative;overflow:hidden;
  border:1px solid var(--line);
  background-image:linear-gradient(180deg,#16222e 0%,#0f1a24 52%,#0b1219 100%);
  box-shadow:inset 0 1px 0 var(--edge-hi-2),0 18px 40px -26px rgba(0,0,0,.95);
}
.pslot__art{display:block;width:100%;height:auto}
.pslot__tag{
  position:absolute;left:0;bottom:0;
  font-family:var(--mono);font-size:10px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--fog-dim);background:rgba(8,11,16,.86);
  border-top:1px solid var(--line);border-right:1px solid var(--line);
  padding:5px 10px;
}
.pslot__cap{
  font-family:var(--mono);font-size:11px;color:var(--fog-dim);letter-spacing:.04em;
  margin:9px 0 0;line-height:1.6;
}
.pslot__cap b{color:var(--paper);font-weight:400}
/* corner registration marks, so the frame reads as deliberate */
.pslot__frame::before,.pslot__frame::after{
  content:"";position:absolute;width:13px;height:13px;pointer-events:none;
  border-color:rgba(255,255,255,.16);border-style:solid;
}
.pslot__frame::before{top:8px;left:8px;border-width:1px 0 0 1px}
.pslot__frame::after{top:8px;right:8px;border-width:1px 1px 0 0}

/* ---------- callouts, notes, steps, faq ---------- */
.call{
  background-image:linear-gradient(90deg,rgba(255,106,43,.055),rgba(255,106,43,0) 46%),
                   linear-gradient(180deg,#141d26,#0f1720);
  border-left-width:3px;
  box-shadow:inset 0 1px 0 var(--edge-hi),0 14px 30px -24px rgba(0,0,0,.9);
}
.note{
  background-image:linear-gradient(180deg,#131c26,#0f1720);
  box-shadow:inset 0 1px 0 var(--edge-hi);
}
.steps>li{transition:background-color var(--t-mid) var(--ease)}
.steps>li:before{
  color:var(--signal);
  transition:transform var(--t-slow) var(--ease);
}
.steps>li:hover:before{transform:translateX(3px)}
.qa{transition:border-color var(--t-mid) var(--ease)}
.qa h3{position:relative;padding-left:18px}
.qa h3::before{
  content:"";position:absolute;left:0;top:.30em;width:7px;height:7px;
  background:var(--signal);transition:transform var(--t-slow) var(--ease);
}
.qa:hover h3::before{transform:rotate(45deg)}
.empty{
  background-image:
    repeating-linear-gradient(180deg,rgba(255,255,255,.018) 0 1px,rgba(255,255,255,0) 1px 26px),
    linear-gradient(180deg,#131c26,#0e161f);
  box-shadow:inset 0 1px 0 var(--edge-hi);
}

/* ---------- tables ---------- */
.tablewrap{box-shadow:inset 0 1px 0 var(--edge-hi)}
th{background-image:linear-gradient(180deg,#101822,#0a1017)}
tbody tr{transition:background-color var(--t-fast) var(--ease)}
tbody tr:hover{background-color:rgba(255,255,255,.022)}

/* ---------- buttons ---------- */
.btn{
  position:relative;overflow:hidden;
  background-image:linear-gradient(180deg,#f3f8fc,#cfdae4);
  box-shadow:0 10px 24px -14px rgba(0,0,0,.9);
  transition:transform var(--t-mid) var(--ease),box-shadow var(--t-mid) var(--ease),
             background-color var(--t-mid) var(--ease),border-color var(--t-mid) var(--ease),
             color var(--t-mid) var(--ease);
}
.btn:hover{
  background-image:linear-gradient(180deg,#ffffff,#dfe9f1);
  transform:translateY(-2px);
  box-shadow:0 14px 28px -14px rgba(0,0,0,.95);
}
.btn:active{transform:translateY(0)}
.btn--ghost{
  background-image:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,0));
  color:var(--white);
}
.btn--ghost:hover{
  background-image:linear-gradient(180deg,rgba(255,106,43,.13),rgba(255,106,43,.03));
  border-color:var(--signal);color:#ffd7c4;
}

/* ---------- chips ---------- */
.chips li{
  background-image:linear-gradient(180deg,#141d27,#0f1720);
  box-shadow:inset 0 1px 0 var(--edge-hi);
  transition:border-color var(--t-fast) var(--ease),color var(--t-fast) var(--ease),
             transform var(--t-fast) var(--ease);
}
.chips li:hover{border-color:var(--signal);color:var(--white);transform:translateY(-1px)}

/* ---------- forms ---------- */
.form{
  background-image:linear-gradient(180deg,#131c26,#0e161f);
  box-shadow:inset 0 1px 0 var(--edge-hi),0 20px 44px -30px rgba(0,0,0,.95);
}
.field input,.field select,.field textarea{
  transition:border-color var(--t-fast) var(--ease),background-color var(--t-fast) var(--ease),
             box-shadow var(--t-fast) var(--ease);
}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--fog-dim)}
.field input:focus,.field textarea:focus,.field select:focus{
  outline:none;border-color:var(--signal);
  box-shadow:0 0 0 3px rgba(255,106,43,.14);
}

/* ---------- footer ---------- */
.foot{
  background-image:
    linear-gradient(180deg,rgba(255,255,255,.028) 0,rgba(255,255,255,0) 140px),
    linear-gradient(180deg,#0a0f16,#070a0f);
  box-shadow:inset 0 1px 0 var(--edge-hi);
}
.foot a{transition:color var(--t-fast) var(--ease),padding-left var(--t-fast) var(--ease)}
.foot li a:hover{color:var(--white);padding-left:4px}
.foot h4{position:relative;padding-bottom:9px;margin-bottom:12px}
.foot h4::after{
  content:"";position:absolute;left:0;bottom:0;width:26px;height:1px;
  background:linear-gradient(90deg,var(--signal),rgba(255,106,43,0));
}

/* ---------- chat ---------- */
.chatbtn{
  background-image:linear-gradient(180deg,#f3f8fc,#cfdae4);
  transition:transform var(--t-mid) var(--ease),box-shadow var(--t-mid) var(--ease);
}
.chatbtn:hover{transform:translateY(-2px);box-shadow:0 10px 30px rgba(0,0,0,.6)}
@media(prefers-reduced-motion:no-preference){
  .chatbtn__dot{animation:pulse-g 3s ease-in-out infinite}
}
@keyframes pulse-g{
  0%,100%{box-shadow:0 0 0 0 rgba(29,143,126,.55)}
  70%{box-shadow:0 0 0 7px rgba(29,143,126,0)}
}
.chatpanel{background-image:linear-gradient(180deg,#151f2a,#0f1720)}
.chatopts button{transition:border-color var(--t-fast) var(--ease),color var(--t-fast) var(--ease),background-color var(--t-fast) var(--ease)}
.chatopts button:hover{background-color:rgba(255,106,43,.07);border-color:var(--signal)}
.chatpanel__x{transition:color var(--t-fast) var(--ease),transform var(--t-fast) var(--ease)}
.chatpanel__x:hover{transform:rotate(90deg)}

/* ==========================================================================
   MOTION — reveals.
   The default state of every element is VISIBLE. The .is-in class is added
   by an IntersectionObserver and only ever ADDS an animation. If the script
   never runs, or fails, the page is complete and readable exactly as it is.
   ========================================================================== */
@media(prefers-reduced-motion:no-preference){
  .is-in{animation:rise .62s var(--ease) backwards}
  .hero .eyebrow{animation:rise .6s var(--ease) .04s backwards}
  .hero h1{animation:rise .72s var(--ease) .10s backwards}
  .hero .lede{animation:rise .7s var(--ease) .20s backwards}
  .hero .btnrow{animation:rise .7s var(--ease) .30s backwards}
}
@keyframes rise{
  from{opacity:0;transform:translate3d(0,16px,0)}
  to{opacity:1;transform:none}
}

/* ---------- the one switch that turns all of it off ---------- */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation:none !important;
    transition:none !important;
    scroll-behavior:auto !important;
  }
  .hero{background-position:60% 0,0 100%,0 0}
}

/* ---------- key to the roofline drawing ---------- */
.figkey{
  margin:16px 0 0;border:1px solid var(--line);
  background-image:linear-gradient(180deg,#121b25,#0d151d);
  box-shadow:inset 0 1px 0 var(--edge-hi);
}
.figkey__hd{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--fog-dim);padding:9px 13px;border-bottom:1px solid var(--line-soft);
  background-image:linear-gradient(180deg,#0f1720,#0a1017);margin:0;
}
.figkey ul{list-style:none;margin:0;padding:0}
.figkey li{
  display:grid;grid-template-columns:46px 1fr;gap:0 12px;align-items:center;
  margin:0;padding:9px 13px;border-bottom:1px solid var(--line-soft);
  transition:background-color var(--t-fast) var(--ease);
}
.figkey li:last-child{border-bottom:0}
.figkey li:hover{background-color:rgba(255,255,255,.025)}
.figkey svg{display:block;width:46px;height:22px;color:var(--fog-dim);
  transition:color var(--t-mid) var(--ease)}
.figkey li:hover svg{color:var(--signal)}
.figkey b{
  font-family:var(--cond);font-stretch:condensed;text-transform:uppercase;font-weight:700;
  font-size:14px;letter-spacing:.01em;color:var(--white);display:block;line-height:1.15;
}
.figkey span{font-size:12.5px;color:var(--fog);line-height:1.45;display:block}

/* the number + icon column spans both rows, so the icon never pushes the
   description away from its headline */
.idx__n{grid-row:1 / span 2;align-self:start}
.idx__d{margin-top:4px}
@media(min-width:760px){
  .idx__row{align-items:start}
  .idx__t{padding-top:1px}
  .idx__ar{padding-top:6px}
}

/* On a narrow screen the desktop line-break lockup strands single words.
   Let the headline find its own breaks instead, and keep it big. */
@media(max-width:620px){
  .hero h1 br{display:none}
  h1{font-size:clamp(36px,9.4vw,88px)}
}

.svcico{color:#7c8fa1}
@media(max-width:759px){.svcico{width:28px;height:28px}}

/* the roof condition report is the hero of the home page: keep the section
   that carries it tight against the masthead so it stays at the fold */
.hero + .sec{padding-top:50px}

/* the mark on an empty feed */
.emptymark{display:block;width:200px;max-width:60%;height:auto;margin:0 auto 18px;color:var(--fog-dim)}


/* ═══ 📱 MOBILE MENU — added 8 Sep 2026. See tools/navfix notes.
   ⛔ Do not give this a colour. The panel IS this site's own <nav>, so it
   keeps this site's own paper and rules; the button rides on currentColor.
   ⛔ Do not raise the breakpoint without re-measuring — 860 is where these
   nav rows start wrapping, which is the thing being fixed. */
.navbtn { display: none; }
.navq { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
        overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@media (max-width: 860px) {
  .navbtn {
    display: inline-flex; align-items: center; gap: .55rem;
    padding: .5rem .8rem; border-radius: .5rem; cursor: pointer;
    font: inherit; font-size: .82rem; letter-spacing: .06em; text-transform: uppercase;
    color: currentColor;
    border: 1px solid currentColor;
    background: transparent;
    opacity: .85;
  }
  .navbtn__ico { position: relative; width: 15px; height: 2px; background: currentColor; }
  .navbtn__ico::before, .navbtn__ico::after {
    content: ""; position: absolute; left: 0; width: 15px; height: 2px; background: currentColor;
  }
  .navbtn__ico::before { top: -5px; }
  .navbtn__ico::after  { top:  5px; }
  .navq:focus-visible + .navbtn { outline: 2px solid currentColor; outline-offset: 2px; }

  /* the demo's own nav, closed */
  nav[aria-label="Main"], nav[aria-label="Primary"] { display: none; }
  .navq:checked ~ nav[aria-label="Main"],
  .navq:checked ~ nav[aria-label="Primary"] { display: block; width: 100%; }

  /* ⚠️ the lists inside are flex rows on every one of these sites; they have to
     stack, and the row gap has to become a tap target. */
  nav[aria-label="Main"] ul, nav[aria-label="Primary"] ul {
    display: block; margin: 0; padding: 0; list-style: none;
  }
  nav[aria-label="Main"] li, nav[aria-label="Primary"] li { display: block; }
  nav[aria-label="Main"] li a, nav[aria-label="Primary"] li a,
  nav[aria-label="Main"] > a, nav[aria-label="Primary"] > a {
    display: block; padding: .7rem .2rem; line-height: 1.2;
  }
  nav[aria-label="Main"] li + li, nav[aria-label="Primary"] li + li {
    border-top: 1px solid currentColor;
  }
  nav[aria-label="Main"] li + li, nav[aria-label="Primary"] li + li { border-top-color: color-mix(in srgb, currentColor 18%, transparent); }
}


/* ═══ 🔤 NAV TYPE PASS 9 Sep 2026 — size and tracking only.
   ⛔ Do not add colour, family, case or weight here: those are what keep these
   twelve sites from converging, and converging is the thing that was fixed
   once already. Measured before: 11–12.5px with tracking wide enough that
   the label read as separate letters rather than a word. */
nav[aria-label="Main"] a,
nav[aria-label="Primary"] a {
  font-size: 13px;
  letter-spacing: .09em;
}

/* ═══ 🖼️ HERO ART 9 Sep 2026 — owner: "the demo heros need work too".
   🔴 THIS HERO WAS A WALL OF TYPE WITH A DEAD RIGHT HALF — the same defect the
   botsonstaff home page had the same day. Four of the twelve demos already do
   it right: tree-service carries a botanical plate, garage-door an elevation,
   plumbing-hvac an instrument cluster, screen-enclosures a measured drawing.
   This one had its roofline scene sitting in the section BELOW the fold.
   ⭐ NOTHING WAS DRAWN AND NOTHING IMPORTED — it is this site's own
   illustration, moved up. Its key ("Key to Fig. 1") stays where it was and
   still reads; the figure is simply higher up the page now.
   ⛔ Roofing's own art at roofing's own proportions. This must NOT become a
   shared hero component — twelve demos that look alike is the defect that was
   fixed once already. */
@media (min-width: 980px) {
  .hero .wrap.hero__grid {
    display: grid;
    grid-template-columns: 1.05fr 0.95fr;
    gap: 46px;
    align-items: center;
  }
}
.hero__words { min-width: 0; }
.hero__art { min-width: 0; }
.hero__art .scene { width: 100%; height: auto; display: block; }
.hero__art .scene__cap { margin-top: 10px; }

/* ⚠️ THE HEADLINE HAS TO COME DOWN IN THE TWO-COLUMN HERO. At its full clamp
   (up to 88px) it filled the whole band; in a 46% column the same string plus
   its hard <br> breaks ran to FIVE lines and pushed the lede below the fold —
   which trades one hero problem for another. ⛔ Do not remove the <br>s: they
   are where the sentence wants to break, and without them the line "we sold
   you nothing" splits across two rows and loses the whole joke. */
@media (min-width: 980px) {
  /* ⚠️ 52, not 58: at 58 the middle line broke and left WHERE orphaned on a
     row of its own, which is worse than slightly smaller type. */
  .hero__grid h1 { font-size: clamp(34px, 3.5vw, 52px); }
  .hero__grid .lede.narrow { max-width: none; }
}

/* ═══ THE HERO ART STOPS BEING A PICTURE IN A BOX — 9 Sep 2026 ═══
   Owner, looking at it big: "if you going to make them bis just make it a
   hero you cut off 1/2 thw pic."

   🔴 THE DIAGNOSIS, MEASURED, NOT GUESSED. The roof polygon inside the scene
   runs x:170 → x:640 in a viewBox of 0 0 640 420 — it ENDS EXACTLY ON THE
   FRAME. The drawing was always composed to bleed: no gable return on the
   right, no corner, the eave just leaves. At the old small size that read as
   a crop of something bigger, which is what it is. Blown up to half the hero
   with `border:1px solid var(--line)` drawn around it, the same composition
   reads as a house someone forgot to finish.

   ⭐ SO THE FIX IS THE FRAME, NOT THE DRAWING. ⛔ Do not "fix" the artwork by
   shrinking the roof to fit inside 640 — that centres a building in a box and
   loses the one thing the image has, which is scale. Take the border off and
   let it run to the edge of the screen, which is what a bleed is for.

   ⚠️ 1140 and 18px are .wrap's own max-width and padding (line 40). If either
   moves, this moves with it. max(0px, …) keeps the sum at -18px below 1140,
   where it simply cancels the rail padding instead of going positive. */
@media (min-width: 980px) {
  .hero { overflow-x: clip; }        /* clip, not hidden: no scroll container */

  .hero__art {
    margin-right: calc(-1 * (max(0px, (100vw - 1140px) / 2) + 18px));
  }

  .hero__art .scene {
    border: 0;
    border-radius: 0;
    box-shadow: none;
    /* ⭐ THE LEFT EDGE DISSOLVES INSTEAD OF ENDING. A bled image still has one
       vertical cut in it — the side facing the words. Fading that side is the
       difference between "photo pasted next to text" and one continuous band.
       The right side is deliberately NOT masked: it is meant to leave. */
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 16%);
            mask-image: linear-gradient(to right, transparent 0, #000 16%);
  }

  /* ⚠️ THE CAPTION IS TYPE, SO IT STAYS ON THE RAIL — and it is sized with
     WIDTH, not padding. box-sizing is border-box on this site, so a padding of
     168px inside a max-width of 46ch left the caption 136px wide and it came
     out as a ten-line ribbon down the side of the roof. ⛔ Never trim a bled
     element with padding under border-box; take the bleed off the width. */
  .hero__art .scene__cap {
    width: calc(100% - (max(0px, (100vw - 1140px) / 2) + 18px));
    max-width: 52ch;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   ⭐ THE PROFESSIONAL PASS — 11 Sep 2026.
   Owner: "demo need to look as good as thoa site or better no ecseptions",
   and "they need to look professiona like thoa".

   Measured against the reference client rebuild, this demo was missing five things, and
   not one of them was a photograph:
     1. no brand MARK, only a wordmark set in type
     2. no colour anywhere in the headline
     3. the accent appeared once, in the phone number
     4. no offer strip, so nothing said what you actually get
     5. the primary button was white — the loudest thing on the page was
        neutral, so the eye had nowhere to go
   ⛔ Do NOT close this gap with an AI photograph. The hero art is captioned
   "drawn, not photographed" and that sentence is the demo's honesty, not a
   limitation. the reference site's own tachometer is a RENDERED GRAPHIC, not a photo.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the mark ─────────────────────────────────────────────────────────── */
.nav__brand{display:inline-flex;align-items:center;gap:9px}
.nav__mark{
  width:26px;height:26px;flex:0 0 auto;color:var(--signal);
  transition:transform var(--t-mid) var(--ease),color var(--t-mid) var(--ease);
}
.nav__brand:hover .nav__mark{transform:translateY(-1px) rotate(-2deg)}

/* ── the headline carries the colour ──────────────────────────────────── */
/* ⚠️ <em> so it still means emphasis with styles off; the slant is removed
   because a condensed display face already leans on weight, not italics. */
h1 .hl{font-style:normal;color:var(--signal)}

/* ── the offer strip ──────────────────────────────────────────────────── */
/* ⭐ This is the the reference site coupon bar, in roofing's own terms. It states the thing
   the whole site is about before the visitor has scrolled at all. ⛔ It must
   never carry a price this demo has not published elsewhere. */
.offerbar{
  border-bottom:1px solid var(--line-soft);
  background-image:linear-gradient(180deg,#121a24,#0d141c);
}
.offerbar__in{
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding-top:10px;padding-bottom:10px;
}
.offerbar__tag{
  font-family:var(--mono);font-size:10.5px;text-transform:uppercase;
  letter-spacing:.14em;color:var(--signal);
  border:1px solid color-mix(in srgb, var(--signal) 40%, transparent);
  border-radius:999px;padding:3px 9px;flex:0 0 auto;
}
.offerbar__msg{margin:0;font-size:13.5px;line-height:1.45;color:var(--fog);flex:1 1 340px;min-width:0}
.offerbar__msg b{color:var(--white);font-weight:650}
.offerbar__go{
  flex:0 0 auto;font-family:var(--mono);font-size:11.5px;text-transform:uppercase;
  letter-spacing:.09em;color:var(--white);text-decoration:none;white-space:nowrap;
  border-bottom:1px solid color-mix(in srgb, var(--signal) 60%, transparent);
  padding-bottom:2px;
}
.offerbar__go:hover{color:var(--signal)}
@media (max-width:640px){
  .offerbar__go{display:none}          /* the hero button is 200px below it */
  .offerbar__msg{font-size:12.5px}
}

/* ── the primary button is the accent, not white ──────────────────────── */
/* 🔴 A white primary on a dark page makes the NEUTRAL element the loudest
   thing on screen. the reference site's primary is its red. This one is the signal. */
.btn{
  background-image:linear-gradient(180deg,var(--signal),var(--signal-lo));
  border-color:color-mix(in srgb, var(--signal) 55%, transparent);
  color:#180a03;font-weight:700;
}
.btn:hover{
  background-image:linear-gradient(180deg,#ff8048,var(--signal));
  border-color:var(--signal);color:#180a03;
}

/* ── depth on the drawn hero ──────────────────────────────────────────── */
/* ⭐ What separates the reference site's instrument from a flat vector is one light and a
   real shadow. The drawing already has the light; it had no shadow and no
   contact with the page. */
.hero__art .scene{
  filter:drop-shadow(0 40px 60px rgba(0,0,0,.55)) contrast(1.06) saturate(1.05);
}
@media (prefers-reduced-motion:reduce){
  .nav__brand:hover .nav__mark{transform:none}
}

/* 🔴 AND THE GHOST HAS TO BE RESTATED AFTER IT. `.btn--ghost` is declared ~200
   lines above, so the `.btn` rule in this block — same specificity, later in
   the file — won the background and BOTH buttons rendered solid orange. Two
   primaries is no primary. ⛔ Any future `.btn` rule appended below this point
   must restate the ghost again, or unlayered source order will eat it. */
.btn--ghost{
  background-image:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,0));
  border-color:var(--line);color:var(--white);
}
.btn--ghost:hover{
  background-image:linear-gradient(180deg,rgba(255,106,43,.13),rgba(255,106,43,.03));
  border-color:var(--signal);color:#ffd7c4;
}

/* ── the headline says WHAT and WHERE before it says anything clever ─────
   🔴 Owner, 11 Sep 2026: "make the hereo header make sence… we are the leader
   in Orlando roffing..... jsut a example but way better".
   The old h1 opened "You keep the report." — a good line that never told a cold
   visitor what this company DOES or WHERE. the reference site's does both in one line: "San
   Diego's North Park mechanical and auto body repair shop."
   ⛔ AND IT IS NOT "THE LEADER IN". We cannot prove a market position, it is the
   class of claim that puts a client in front of a regulator, and the reference site's outranks
   without it. Trade + town is the stronger sentence anyway — and it is the half
   a city page needs to be about a city at all. */
.h1__where{color:var(--fog);font-weight:inherit}
@media (max-width:640px){ .h1__where{display:block} }

/* 🔴 TWO CORRECTIONS, 11 Sep 2026, owner: "use white text sometimes not always
   the light blue".
   1. `.h1__where` was var(--fog) — the same cool blue-grey this site already
      uses for every secondary line. A headline set in the body-copy colour
      reads as a caption. It is WHITE now; the hierarchy comes from the accent
      below it, not from dimming the first line.
   2. The accent ran across TWO full lines of a four-line headline, which is not
      an accent any more — it is a second colour scheme. It is one phrase. */
.h1__where{color:var(--white);font-weight:inherit}

/* 🔴 THE HARD <br>s CAME OUT — 11 Sep 2026. Owner: "make sure all elements line
   up and are organized proper thriugh the pages".
   Hand-placed breaks looked deliberate at exactly one window width and ragged at
   every other: "INCLUDING" stranded alone on its own line, and the accent phrase
   split across two.
   ⭐ AND THEY DO NOT SURVIVE A CITY SWAP, which is the other half of the ask.
   "Roofing in Apopka" and "Roofing in Winter Garden" are different lengths, so a
   break tuned for one town is wrong for the next. The measure does the wrapping
   now and `text-wrap:balance` evens the lines out at any width, for any town. */
.hero h1{
  max-width:17ch;
  text-wrap:balance;
}
@media (min-width:900px){ .hero h1{max-width:20ch} }
/* ⚠️ The where-line stays on its own line on every size — it is the WHAT and
   WHERE, and it should not run into the sentence that follows it. */
.h1__where{display:block;color:var(--white);font-weight:inherit}

/* 🔴 THE HEADLINE WAS 84 CHARACTERS AND RAN FIVE LINES — cut 11 Sep 2026.
   Even balanced, a sentence that long breaks the accent phrase across lines at
   most widths, and "WE / SOLD YOU NOTHING" is not an accent, it is a mess. The
   reference headline (the reference site's) is 60 characters.
   ⭐ It says the two things a cold visitor needs — the trade and the town, then
   the promise — and the refusal line, which is the best sentence on the site,
   moved to the front of the lede where it has room to be read. Nothing was
   deleted; it was put where it fits. */
.hero h1{max-width:14ch}
@media (min-width:900px){ .hero h1{max-width:16ch} }

/* ⚠️ 14ch stranded it in four short lines inside a 590px column — the measure
   was doing the job the sentence length should. Each phrase is ~19 characters,
   so 21ch lands one phrase per line and the balance rule keeps it that way when
   a longer town name arrives. */
.hero h1{max-width:21ch}
@media (min-width:900px){ .hero h1{max-width:21ch} }

/* ── 💬 THE LAUNCHER IS PART OF THE BRAND — 11 Sep 2026 ──────────────────
   Owner: "the ask chat bot need to be creative also". It was a white pill with
   a generic green dot — the one element on the page that looked bought rather
   than built, and it sat on top of everything else on every screen.
   ⭐ It carries the roof mark now, in the site's own ground and signal, so the
   thing that follows you down the page is the brand rather than a widget.
   ⛔ The live dot stays. It is the only honest part of the old design: it says
   something is actually running. It moved to the RIGHT of the label so the mark
   leads and the status follows, which is the order you read them in. */
.chatbtn{
  background-image:linear-gradient(180deg,#182230,#0e1620);
  color:var(--white);
  border:1px solid color-mix(in srgb, var(--signal) 34%, transparent);
  border-radius:999px;
  padding:10px 16px 10px 13px;
  box-shadow:0 10px 30px -10px rgba(0,0,0,.85);
}
.chatbtn:hover{
  background-image:linear-gradient(180deg,#1e2a3a,#121b26);
  border-color:var(--signal);
}
.chatbtn__mark{width:20px;height:20px;flex:0 0 auto;color:var(--signal)}
.chatbtn__txt{line-height:1}
.chatbtn__dot{
  width:7px;height:7px;border-radius:50%;
  background:#3fd07a;flex:0 0 auto;margin-left:2px;
}
@media (max-width:520px){
  /* ⚠️ On a phone the label plus the mark is wider than the thumb zone it sits
     in, and it covered the hero button underneath. The mark alone is still a
     44px target, which is the floor. */
  .chatbtn__txt{display:none}
  .chatbtn{padding:12px 13px}
}

/* ── 📐 SERVICE PAGE ART — 11 Sep 2026 ────────────────────────────────────
   Owner: "i want pics on every page service" and "i cant keep saving all your
   pics you save yorself".
   ⭐ SO THEY ARE DRAWN, NOT DOWNLOADED. Every one is inline SVG written into
   the page: nothing to export, nothing to save, nothing to licence, no external
   request for the sync guard to refuse, and it is crisp at any size. Browser
   downloads are blocked in this environment anyway, so a pipeline that depends
   on saving 130 files out of a web app was never going to finish.
   ⚠️ Each is a CROSS-SECTION of the detail that service is about — a deck
   build-up, a pipe boot, step flashing, a ridge vent. That is education, not a
   claim about a job we did, which is what keeps it inside the rule that a
   picture may never be the work or the premises. */
.svcart .scene{width:100%;height:auto;display:block;border:1px solid var(--line-soft);border-radius:10px}
.svcart__cap{
  margin:10px 0 0;font-family:var(--mono);font-size:11px;letter-spacing:.06em;
  color:var(--fog-dim);
}
/* ⚠️ The service hero had one `.wrap` and no grid — it is the home page's
   two-column hero now, so the art needs the same treatment the home art gets. */
.hero__grid{display:grid;gap:28px}
@media (min-width:900px){
  .hero__grid{grid-template-columns:minmax(0,1fr) minmax(0,460px);align-items:center;gap:44px}
}
.hero__words{min-width:0}

/* photographs alongside the drawn diagrams — the diagrams stay drawn and their
   "Fig. — drawn, not photographed" caption stays true of them */

/* ── 🏠 THE DAY, AS THE CUSTOMER HAS IT — 11 Sep 2026 ─────────────────────
   ⭐ The hero names the trade and the town and hands the accent to the hook.
   This block is the hook happening, hour by hour, on the homeowner's side of
   it. The site's own argument is sharper than "you never climb the ladder":
   the condition report is written and handed over WHETHER OR NOT you hire us,
   and the roof you came to buy is allowed to be a refusal at $0.00.
   ⛔ No motion of its own — the page's existing reveal already respects
   prefers-reduced-motion and this inherits it by being a `.sec > .wrap` child. */
.sec--day .day{list-style:none;margin:26px 0 0;padding:0;display:grid;gap:0}
.day__i{
  display:grid;grid-template-columns:104px 1fr;gap:0 22px;
  padding:18px 0;border-top:1px solid var(--line-soft);align-items:start;
}
.day__i:first-child{border-top:0}
.day__t{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--signal);padding-top:4px;white-space:nowrap;
}
.day__i h3{margin:0 0 5px;font-size:clamp(17px,2.2vw,20px);line-height:1.25;color:var(--white);max-width:62ch}
.day__i p{margin:0;font-size:15px;line-height:1.62;color:var(--fog);max-width:62ch}
/* ⭐ one beat is marked, and it is the one where the headline comes true. */
.day__i--live .day__t::after{
  content:"";display:inline-block;width:6px;height:6px;border-radius:50%;
  background:var(--signal);margin-left:7px;vertical-align:middle;
}
/* 🔴 THE THREE-CHILD GRID BUG. `.day__i` holds THREE children — the timestamp,
   the h3 and the p — in a TWO-column grid, so auto-placement puts the
   paragraph back in column one, at the width of a timestamp. At 1280px that
   renders a 15px paragraph in a 104px gutter and the beat is four times as
   tall as it should be. Both text children are pinned to column two.
   ⛔ Reset this inside the single-column breakpoint or the `2` invents an
   implicit second column on a phone. */
.day__i > h3,
.day__i > p{grid-column:2}
.day__foot{
  margin:24px 0 0;padding-top:18px;border-top:1px solid var(--line-soft);
  font-size:15px;line-height:1.65;color:var(--fog);max-width:66ch;
}
.day__foot b{color:var(--paper)}
@media(max-width:560px){
  .day__i{grid-template-columns:1fr;gap:5px}
  .day__t{padding-top:0}
  .day__i > h3,.day__i > p{grid-column:1}
}


/* ── 📷 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; }


/* ── 🧭 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:#0f1b2a;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){.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:#07182a}
.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(5,18,32,.93) 0%, rgba(5,18,32,.86) 38%,
                           rgba(5,18,32,.46) 70%, rgba(5,18,32,.24) 100%),
    linear-gradient(0deg,  rgba(5,18,32,.42) 0%, rgba(5,18,32,.30) 50%,
                           rgba(5,18,32,.14) 100%)}
/* ⛔ 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,22,38,.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,22,38,.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(5,18,32,.80) 0%, rgba(5,18,32,.72) 50%,
                          rgba(5,18,32,.55) 100%),
    linear-gradient(90deg, rgba(5,18,32,.55) 0%, rgba(5,18,32,.30) 100%)}
  .lanaihero h1{max-width:none}
}




/* 🔴 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,22,38,.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,22,38,.66);border-color:#fff}

.lanaihero .btn--ghost{
  border-color:rgba(255,255,255,.62);
  color:#fff;
  background:rgba(7,22,38,.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(6,19,34,.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. */
@media (min-width:901px){
  header .top__in,
  header .masthead__row,
  header .brandline{flex-wrap:nowrap}
  header nav{min-width:0}
}
@media (min-width:901px){
  header nav ul{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
  header nav ul::-webkit-scrollbar{display:none}
  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}

/* ═══ 2026 HERO + TYPE PASS ═══ hand-edited, see the note at the top of this file.
   ⛔ Everything below is APPENDED deliberately. This file redefines body, h1 and
   .lede three times over (lines ~23/351, ~74/360/836/943); an unlayered
   stylesheet resolves that by source order, so a correction that is not last
   does not apply. Measure the computed value, never the rule you wrote. */

/* ── 1. TYPE IN rem, AND AN h1 THAT CANNOT FALL BELOW 3x BODY ──────────────
   MEASURED BEFORE: body 16px (a hard px pin — a reader who sets a larger
   browser base size gets nothing, WCAG 1.4.4 Resize Text). The hero h1 resolved
   to 32px at 320px and 375px wide, a ratio of 2.00 against a bar of 3.0; it
   only reached 3.50 at 1280.
   ⭐ THE FIX IS THAT BOTH TERMS ARE NOW rem. The floor is 3rem against a 1rem
   body, so the ratio is >=3.0 at EVERY width by construction, not by luck — and
   it stays >=3.0 when the reader scales, because both ends scale together. */
body{font-size:1rem}
h1,
.hero__grid h1,
.lanaihero h1{
  font-size:clamp(3rem, 1.5rem + 4.6vw, 4rem);
  line-height:1.03;
  letter-spacing:-0.032em;
  font-weight:500;          /* ⭐ 500, not 800 — display weight, off the reference */
  text-wrap:balance;
}

/* ── 2. THE HERO IS THE DOCUMENT ───────────────────────────────────────────
   The offer strip used to be a 26-word paragraph DESCRIBING a report that
   carries refusals at $0.00, wrapped around an uppercase pill. It is now the
   row itself, off report HR-4471: eight words, the $0.00 a literal, and it sits
   inside the hero section so it is read without scrolling. */
.offerbar__tag,.offerbar__msg,.offerbar__go{display:none}   /* the old capsule device */
.herodoc{
  display:flex;align-items:baseline;flex-wrap:wrap;gap:.45rem 1.05rem;
  padding-top:.85rem;padding-bottom:.9rem;
}
.herodoc__no{
  flex:0 0 auto;font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;
  color:var(--fog-dim);
}
.herodoc__item{
  flex:1 1 15rem;min-width:0;color:var(--white);
  font-size:clamp(.9375rem,1.5vw,1.0625rem);line-height:1.35;
}
.herodoc__item b{color:var(--signal);font-weight:600}
.herodoc__amt{
  flex:0 0 auto;font-family:var(--mono);font-weight:700;
  font-variant-numeric:tabular-nums;font-size:clamp(1rem,1.7vw,1.1875rem);
  color:var(--signal);letter-spacing:-.01em;
}

/* ── 3. THE HERO HOLDS THE FOLD, AND IT IS ONE THING ───────────────────────
   ⛔ min-height, never height: at 320px a four-line h1 is taller than the
   viewport and a fixed height clips it. */
.lanaihero{min-height:min(88vh,44rem);justify-content:flex-end}
/* ⛔ padding-TOP only. `.lanaihero .offerbar` is position:absolute, pinned to the
   bottom of the band, and the clearance that keeps the buttons out from under it
   is `.lanaihero__in{padding-bottom:clamp(5.5rem,9vw,7rem)}` further up this
   file — with a smaller value in the <=860px branch where the bar goes static.
   Setting padding-bottom here overrode BOTH and buried 35px of a 53px button row
   under the strip. Measured, not guessed: btnrow 726.8-779.7, strip top 744.4. */
.lanaihero__in{padding-top:3rem}
.lanaihero .btnrow{margin-top:1.15rem}
/* ⭐ over a photograph there is no secondary text: full ink, and a shadow that
   travels with the glyphs rather than a darker lid on the picture. */
.lanaihero__in :is(h1,.btnrow){text-shadow:0 1px 2px rgba(0,0,0,.70),0 2px 20px rgba(0,0,0,.52)}
/* 🔴 `a.btn`, NOT `.btn`. THE TWO-PRIMARIES BUG: this stylesheet is unlayered,
   so a bare `.btn{` rule appended here would beat the `.btn--ghost{` rules above
   it at equal specificity and BOTH hero buttons would render solid — which is a
   defect that has actually shipped on this site. Qualifying the selector keeps
   the last `.btn{` above the last `.btn--ghost{`, which is what
   tools/check-demo-polish.mjs enforces. ⛔ Never append a bare `.btn{` below. */
.lanaihero a.btn{text-shadow:none}

/* ── 4. THE 2018 TELLS ─────────────────────────────────────────────────────
   Measured on this file: 26 text-transform:uppercase (one of them on EVERY
   h1/h2/h3), 40 non-none box-shadows, 22 `border:1px solid` boxes.
   ⭐ Density comes from hairlines, spacing and light. Uppercase survives only
   where it is a document convention — a report title, a table head, a spec
   key — because there it is the selling device, not decoration. */
h1,h2,h3,h4,.cond{text-transform:none}
.eyebrow,.btn,.nav__brand,.nav__links a,.navbtn,.idx__t,
.figkey__hd,.figkey b,.pslot__tag,.chatbtn{
  text-transform:none;letter-spacing:.005em;
}
.eyebrow{border:0;border-radius:0;padding-left:0;color:var(--fog)}
/* the drop shadows go; the inset top-lip hairline that models the light stays */
.chatbtn,.chatbtn:hover,.chatpanel,.btn,.btn:hover,
html[data-scrolled] header{box-shadow:none}
.rep,.spec,.scene,.pslot__frame,.form{box-shadow:inset 0 1px 0 var(--edge-hi)}
.btn:hover{transform:none}
.chatbtn:hover{transform:none}
/* boxes become hairlines: same rectangle, a third of the ink */
.rep,.spec,.form,.pslot__frame,.idx,.figkey{border-color:var(--line-soft)}

/* 🔴 THE TWO-PRIMARIES BUG — AND THE PASS ABOVE RE-ARMED IT.
   This stylesheet is unlayered, so `.btn` rules resolve by SOURCE ORDER. The
   `.lanaihero a.btn{…}` line above is the last `.btn` rule in the file, which
   means the ghost would lose its treatment and both hero buttons would render
   solid. That exact defect has shipped on this site before.
   ⭐ So the ghost is restated HERE, last, identical to its winning definition.
   ⛔ Anything you append below that matches `.btn` must repeat this block after
   it. tools/check-demo-polish.mjs enforces the ordering; it is not advisory. */
.btn--ghost{
  background-image:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,0));
  border-color:var(--line);color:var(--white);
}

/* ⚠️ the row number sat at 4.18-4.49:1 against a floor of 4.5 — marginal, and
   marginal on the one row the whole site is built to make somebody read. */
.lanaihero .herodoc__no{color:rgba(255,255,255,.82)}

/* 🔴 A POOL UNDER THE WORDS, NOT A LID ON THE PICTURE.
   MEASURED with the type hidden, before: band mean luma 37-45 against a floor
   of 60, picture side 58-79 against 80. At that level the photograph is paying
   for bandwidth and a licence obligation and contributing nothing anyone can
   see — and the owner's instruction is a full-picture hero, professionally
   done. Meanwhile the headline was sitting at 11-17:1 against a floor of 3.0.
   ⭐ So the margin gets SPENT ON THE PHOTOGRAPH, which is the right order: make
   the foreground carry itself, then take the darkness back.
   🔴 EVERY VERTICAL STOP IS IN px. A gradient percentage resolves against the
   ELEMENT, and this band is 704px tall against a 900px screen — the old
   `linear-gradient(0deg, …50%…)` was aiming its mid stop ~350px below where it
   looked like it was. Horizontal % is safe: the element is viewport-wide.
   ⭐ The last third stays at .06. That number IS the picture. If contrast ever
   fails here, fix the foreground or the near stops — never the far end. */
.lanaihero__scrim{
  background:
    linear-gradient(0deg,  rgba(5,18,32,.46) 0px, rgba(5,18,32,0) 240px),
    linear-gradient(92deg, rgba(5,18,32,.88) 0%,  rgba(5,18,32,.80) 40%,
                           rgba(5,18,32,.66) 58%, rgba(5,18,32,.20) 78%,
                           rgba(5,18,32,.06) 100%)}

/* the mark is a cut square now, not a 28x28 line drawing — give it its ratio */
.nav__mark{width:auto;height:26px}
.nav__brand:hover .nav__mark{transform:translateY(-1px);rotate:0deg}

/* ── A PHOTOGRAPH WHERE A DRAWING WAS ────────────────────────────────────────
   ⛔ Not a picture in a rounded card with margin round it — the owner's words
   are "full picture … done professionally". Full-bleed to the gutter, one
   hairline caption under it, no shadow, no radius, no frame. */
.photoband{margin:0 0 2.25rem;padding:0}
.photoband img{display:block;width:100%;height:clamp(240px,34vw,460px);
  object-fit:cover;object-position:center 55%}
.photoband__cap{max-width:74ch;margin:.7rem auto 0;padding:0 clamp(14px,3vw,26px);
  font-size:.8125rem;line-height:1.5;opacity:.72}

/* ═══ GLASS — ROOFING: A HARD-EDGED PLATE ════════════════════════════════════
   Owner, 23 Sep: "use glass buttons all the newest elements".
   ⛔ THE BASE DECLARATION IS THE FALLBACK, AND IT CLEARS 4.5:1 ON ITS OWN.
   backdrop-filter is not universal and some privacy settings switch it off. If
   the blurred, low-alpha version were the base, every reader without it would
   get a near-transparent chip with a label floating on a photograph. So: opaque
   enough alone first, and the blur is an @supports enhancement that BUYS the
   lower alpha. Never the other way round.
   ⭐ --glass-fb carries the fallback colour so tools/_glass.mjs can measure both
   states without me editing the file to test it.
   ⭐ HIERARCHY: the primary is the brand orange at .94 with DARK ink on it; the
   secondary is smoked glass with white. Two clear glass buttons side by side
   are two secondary buttons. ⛔ White on this orange is 2.6:1 — the primary's
   label is dark on purpose, not for contrast alone but because it is what makes
   it read as the filled one. */
.lanaihero .btn,.lanaihero .btn--ghost{
  border-radius:2px;                      /* a plate, not a pill */
  border:1px solid rgba(255,255,255,.22);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.30);   /* the lit top edge */
  transition:background-color .22s var(--ease),border-color .22s var(--ease);
}
.lanaihero .btn{
  --glass-fb:rgba(255,106,43,.94);
  background:rgba(255,106,43,.94);
  color:#170a03;border-color:rgba(255,166,120,.62);
}
@supports ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  .lanaihero .btn{background:rgba(255,106,43,.88);
    -webkit-backdrop-filter:blur(16px) saturate(1.35);backdrop-filter:blur(16px) saturate(1.35)}
}
/* ⭐ a real focus ring, not the browser default and not nothing */
.lanaihero .btn:focus-visible,.lanaihero .btn--ghost:focus-visible{
  outline:2px solid #fff;outline-offset:3px}
/* 🔴 RESTATED LAST — the unlayered `.btn` rules above would otherwise strip the
   secondary's own glass. tools/check-demo-polish.mjs enforces this ordering. */
.btn--ghost{
  --glass-fb:rgba(9,14,20,.88);
  background:rgba(9,14,20,.88);
  color:var(--white);border:1px solid rgba(255,255,255,.34);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22);border-radius:2px;
}
@supports ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  .btn--ghost{background:rgba(9,14,20,.62);
    -webkit-backdrop-filter:blur(16px) saturate(1.2);backdrop-filter:blur(16px) saturate(1.2)}
}

/* 🔴 A VALUE THAT WILL NOT MOVE MEANS THE WRONG RULE.
   tools/_glass.mjs reported the primary and the secondary with the SAME alpha
   (0.94) and the same 6.16:1. Two different rules cannot produce one number.
   CAUSE: the markup is `class="btn btn--ghost"`, so `.lanaihero .btn` (0,2,0)
   matches the SECONDARY too and beats `.btn--ghost` (0,1,0). Both hero buttons
   were rendering as orange glass — the two-primaries bug again, arriving by
   specificity this time instead of by source order.
   ⭐ Resolve by naming one more class on the more specific rule, not by
   !important and not by a :not() — `:not()` carries the weight of its argument
   and raises the score it pretends to narrow. */
.lanaihero .btn.btn--ghost{
  background:rgba(9,14,20,.88);color:var(--white);
  border:1px solid rgba(255,255,255,.34);border-radius:2px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22);
  --glass-fb:rgba(9,14,20,.88);
}
@supports ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  .lanaihero .btn.btn--ghost{background:rgba(9,14,20,.62);
    -webkit-backdrop-filter:blur(16px) saturate(1.2);backdrop-filter:blur(16px) saturate(1.2)}
}
/* restated last for the ordering gate — see the note further up */
.btn--ghost{background:rgba(9,14,20,.88);color:var(--white);border-radius:2px}

/* ⚠️ MARGIN, MEASURED. The tinted primary carries a DARK label, so its worst
   case is the DARKEST backdrop it can sit on, not the brightest — the glass
   gets closer to the photograph and further from its own hue. Measured at
   a=0.80/0.82 the blurred state sat at 4.88 and 4.67 against a floor of 4.5,
   which is margin thin enough that one darker photograph would break it.
   Raising the tint alpha moves the composite back toward the pure brand colour
   (roofing's orange on its ink is 6.53:1 at full strength) and costs almost
   nothing visually: the blur and the saturation are what read as glass here,
   and the SECONDARY at .62 is what carries the visible translucency. */

/* ═══ THE SHEEN IS A SHAPE, NOT A SHEET ══════════════════════════════════════
   Owner, 23 Sep 2026: "i think the picture should be full hero then shene out
   where you want the text" · "evry hero needs to be perfecto".
   🔴 MEASURED BEFORE (tools/_sheen.mjs): sheen extent 100% of the frame on all
   four heroes — a global wash. The band, with the opaque offer strip excluded,
   sat at roughly HALF the photograph's own luminance across the whole picture,
   including the sky and the far side where no word is ever printed.

   ⭐ THE TECHNIQUE: a vertical intensity profile PAINTED, multiplied by a
   horizontal falloff MASKED. Two background layers would composite additively
   and still cover the whole box; a mask multiplies, so the result is a genuine
   two-dimensional shape that is soft on both axes and reaches true zero.
   ⛔ Soft stops only. A visible boundary where the sheen ends is worse than the
   flat wash it replaces.
   🔴 EVERY VERTICAL STOP IS IN px. A gradient percentage resolves against the
   ELEMENT, and this band is min(88vh,44rem) — its height moves with the window.
   Horizontal % is safe: the element is viewport-wide.
   ⭐ The horizontal stops are keyed to THIS site's measured type block, not to a
   shape that looked right — the widest run of ink ends at a different place on
   each of the four.
   ⚠️ FALLBACK: where mask-image is unsupported the vertical gradient paints
   alone — a bottom wash at full width. Dimmer than intended, never unreadable,
   which is the right direction for a graceful failure. */
.lanaihero__scrim{
  background:linear-gradient(0deg,
    rgba(5,18,32,.92) 0px,
    rgba(5,18,32,.88) 170px,
    rgba(5,18,32,.66) 330px,
    rgba(5,18,32,.30) 460px,
    rgba(5,18,32,0)   600px);
  -webkit-mask-image:linear-gradient(90deg,#000 0%,#000 44%,
    rgba(0,0,0,.58) 60%, rgba(0,0,0,.18) 72%, transparent 82%);
  mask-image:linear-gradient(90deg,#000 0%,#000 44%,
    rgba(0,0,0,.58) 60%, rgba(0,0,0,.18) 72%, transparent 82%);
}
/* ⛔ A PHONE IS NOT A NARROW DESKTOP. Below 741px the headline wraps to four or
   five lines and the type occupies almost the whole frame, so a horizontal
   falloff keyed to a desktop text block would leave the last words bare. The
   sheen goes nearly full width and taller; what still protects the photograph
   here is the VERTICAL profile, because the top half carries no type. */
@media (max-width:46.25rem){
  .lanaihero__scrim{
    background:linear-gradient(0deg,
      rgba(5,18,32,.93) 0px,
      rgba(5,18,32,.90) 220px,
      rgba(5,18,32,.70) 400px,
      rgba(5,18,32,.32) 530px,
      rgba(5,18,32,0)   660px);
    -webkit-mask-image:linear-gradient(90deg,#000 0%,#000 80%,rgba(0,0,0,.45) 92%,transparent 100%);
    mask-image:linear-gradient(90deg,#000 0%,#000 80%,rgba(0,0,0,.45) 92%,transparent 100%);
  }
}

/* 🔴 THE REFUSAL WAS OFF-SCREEN ON A PHONE. Diagnosed, not guessed: the glyph
   probe returned NO DIFF for the whole document row at 320 and 375, and a
   no-diff has three causes — invisible, unpainted, covered — of which only one
   is a contrast defect. tools/_heroluma.mjs now reports which; it said
   UNPAINTED, outside the captured viewport. The hero was min(88vh,44rem) = 704px
   tall, and 704 + the masthead put the strip 27px below an 812px fold.
   ⛔ That breaks the one rule this whole hero exists for: the refusal is read
   WITHOUT SCROLLING. A shorter band on a phone, so the row is on screen.
   ⛔ min-height, never height — the band still grows when a five-line headline
   needs it, which is the case a fixed height would clip. */
@media (max-width:46.25rem){
  .lanaihero{min-height:min(70vh,34rem)}
}

/* ⚠️ THE PHONE RAMP HAD TO SHRINK WITH THE PHONE BAND. Shortening the hero so
   the refusal clears the fold left the previous px stops (zero at 660px) taller
   than the band itself (~544px), so the sheen never reached zero and the whole
   phone hero went to band luma 53 against a floor of 60 — darker than the
   desktop version it was supposed to improve on.
   ⛔ This is the cost of px stops, and it is still the right trade: a % stop
   would drift with the viewport height instead, which is harder to see and
   harder to fix. When the band's height changes, the ramp is a number you own. */
@media (max-width:46.25rem){
  .lanaihero__scrim{
    background:linear-gradient(0deg,
      rgba(5,18,32,.93) 0px,
      rgba(5,18,32,.88) 140px,
      rgba(5,18,32,.62) 260px,
      rgba(5,18,32,.26) 350px,
      rgba(5,18,32,0)   430px);
  }
}

/* ⚖️ THE PHONE HERO IS A THREE-WAY TRADE AND THE FIRST TWO ATTEMPTS EACH LOST
   ONE LEG OF IT: a tall band put the refusal below the fold; a short band with
   a short ramp pushed the top of the headline to 1.89:1 against a floor of 3.
   ⛔ Contrast is not the leg that gives. So the TYPE BLOCK shrinks instead:
   the two hero buttons sit side by side rather than stacked full width, which
   returns a whole row (~55px) to the photograph, and the band grows back.
   ⭐ Then the ramp covers the type's real height and still reaches zero with
   clean photograph above it. Measured, not reasoned. */
@media (max-width:46.25rem){
  .lanaihero{min-height:min(72vh,36rem)}
  /* ⛔ comma-listed with the ghost on purpose: a bare `.btn{` rule here would
     become the last one in this unlayered file and strip the secondary. */
  .lanaihero .btn,.lanaihero .btn--ghost,
  .band--photo .btn,.band--photo .btn--out{width:auto;flex:0 1 auto}
  .lanaihero__scrim{
    background:linear-gradient(0deg,
      rgba(5,18,32,.93) 0px,
      rgba(5,18,32,.90) 200px,
      rgba(5,18,32,.74) 330px,
      rgba(5,18,32,.40) 440px,
      rgba(5,18,32,.12) 510px,
      rgba(5,18,32,0)   580px);
  }
}

/* 🔴 THE CHAT LAUNCHER WAS PAINTED ON TOP OF THE REFUSAL. Measured at 375px:
   the launcher is position:fixed at left:16px bottom:16px, box 16,748→107,796,
   and the document row sits at 0,723→375,777. document.elementFromPoint at the
   row's left end returned the launcher, not the row — so the first words of the
   one line this whole hero exists for were covered, at scroll 0, before the
   reader has done anything.
   ⚠️ This is the case ux-build calls unavoidable: a floating element is escapable
   by scrolling everywhere EXCEPT where the reader already is.
   ⭐ Raise it clear of the strip on phones rather than shrinking the hero again
   — the strip's own height is the offset, taken from the same tokens it uses. */
@media (max-width:46.25rem){
  .chatbtn,.chatpanel{bottom:calc(16px + 3.9rem + env(safe-area-inset-bottom,0px))}
}

/* ⚠️ AND THE SAME COLLISION AT 741px, WHICH THE PHONE BREAKPOINT MISSED.
   The probe found chat-btn painted over "Line 6" at 741 on tree-service: at that
   width the nav wraps, the masthead grows, and the strip is pushed far enough
   down the viewport to meet a launcher pinned 16px off the bottom. It does NOT
   collide at 1280 or 1440, where the header is short again — so this is a
   header-height effect, not a width effect, and the breakpoint has to be wide
   enough to cover every layout where the two can meet. Verified at 320, 375,
   741, 1280 and 1440. */
@media (max-width:64rem){
  .chatbtn,.chatpanel{bottom:calc(16px + 3.9rem + env(safe-area-inset-bottom,0px))}
}

/* 🔴 AND IT COLLIDES AT 1280 TOO — RAISED AT EVERY WIDTH.
   Measured directly on the page: the launcher covers 84% of the row's line
   number and document.elementFromPoint at its centre returns the launcher, not
   the row. My own glyph probe called 1280 CLEAN, because the uncovered 16% of
   the glyphs still produced a diff and so it returned a ratio instead of the
   no-diff that would have flagged it. ⛔ A contrast probe is not an overlap
   probe; do not let one stand in for the other.
   ⭐ The media query is gone. A launcher pinned 16px off the bottom will meet
   the strip at whatever width the header happens to be tall enough — 741 and
   1280 both did — so the offset is unconditional. It costs one row of empty
   space at the bottom of the screen and removes the whole class of collision. */
.chatbtn,.chatpanel{bottom:calc(16px + 3.9rem + env(safe-area-inset-bottom,0px))}

/* 🔴 MOVING THE LAUNCHER WAS THE WRONG FIX AND IT CAUSED WHAT IT CURED.
   Measured per width instead of reasoned: at 320 the strip ends 72px above the
   fold and at 741 it ends 88px above, and the launcher (42-48px plus its 16px
   inset) ALREADY FIT UNDERNEATH at both. Lifting it 78px moved it off the
   photograph and INTO the strip — two new collisions in exchange for one. And a
   single lift cannot be right everywhere: the room below the strip is 95px on
   roofing at 1280 and 8px on tree-service at the same width, because the two
   heroes are not the same height.
   ⭐ SO THE STRIP MOVES, NOT THE LAUNCHER. A fixed gutter at the foot of the
   band puts the document row clear of anything pinned to the bottom of the
   viewport, at every width, on every one of the four — and the launcher goes
   back where its own stylesheet put it.
   ⛔ Both declarations are needed: the strip is position:absolute in the wide
   layout and static below 860px, and only one of them answers to each. */
.chatbtn,.chatpanel{bottom:16px}
.lanaihero{padding-bottom:4.6rem}
.lanaihero .offerbar{bottom:4.6rem}

/* ⚠️ AT 320 THE ROW WRAPS TO THREE LINES and the strip grows from ~55px to
   ~108px, which ate the 4.6rem gutter and put it back under the launcher —
   plumbing-hvac's ran 671..779 in a 780px viewport, flush against the fold.
   The gutter has to clear the TALLEST the strip gets, not the shortest.
   ⭐ 320 is SC 1.4.10's own number and where a real iPhone SE at 400% zoom
   lands; it is the width where everything is worst and the one most likely to
   be skipped. Measured here, not extrapolated from 375. */
@media (max-width:22.5rem){
  .lanaihero{padding-bottom:7.6rem}
  .lanaihero .offerbar{bottom:7.6rem}
}

/* 🔴 AT 320 THE $0.00 WAS 97% COVERED BY THE CHAT LAUNCHER.
   The row is a flex line; at 320 it wraps to three stacked lines and the amount
   drops to the THIRD line, left-aligned — which is precisely where a launcher
   pinned to the bottom-left sits. document.elementFromPoint at the middle of
   "$0.00" returned the launcher. The single most important literal on the site,
   invisible, at the width the accessibility criterion names.
   ⭐ A two-column grid keeps the amount on the right edge where a document puts
   it, clear of anything docked bottom-left, and drops the strip from three
   lines to two — which also buys back the fold. */
@media (max-width:22.5rem){
  .herodoc{display:grid;grid-template-columns:minmax(0,1fr) auto;
    align-items:baseline;column-gap:.7rem;row-gap:.2rem}
  .herodoc__no{grid-column:1/-1}
  .herodoc__item{grid-column:1}
  .herodoc__amt{grid-column:2;justify-self:end}
}

/* ⚠️ AT 320 THE CONTENT ALREADY FILLS THE SCREEN, so a min-height only pushes
   the foot of the band past the fold. A six-line headline at the 3x-body floor
   plus two buttons plus the document row is taller than 320x780 leaves after
   the masthead — there is nothing for a minimum to add, and what it adds comes
   off the bottom, which is where the refusal lives.
   ⛔ Still min-height:auto rather than a fixed height: the band must be free to
   grow, it just must not be padded taller than its content. */
@media (max-width:22.5rem){
  .lanaihero{min-height:auto}
}

/* ⚠️ THE 320 GUTTER WAS BELOW THE FOLD AND SO BOUGHT NOTHING. Measured on
   plumbing-hvac: band 162..886 in a 780px viewport, strip 670..764, gutter
   121.6px sitting at 764..886 — entirely off-screen. The strip's position is
   set by the CONTENT ABOVE IT, not by padding underneath, so the only way to
   lift it is to take height out of what comes first.
   ⭐ ~56px recovered from the hero's top padding, the headline's leading and
   the button gap — none of it from the type SIZE, which is pinned at 3x body. */
@media (max-width:22.5rem){
  .lanaihero .lanaihero__in,.lanaihero>.wrap{padding-top:1rem}
  .lanaihero h1,.lanaihero .h1{line-height:.98}
  .lanaihero .btnrow{margin-top:.8rem}
  .lanaihero{padding-bottom:1.5rem}
}

/* ⚖️ THE 320 RAMP, AND THE TRADE I REFUSED TO MAKE.
   Letting content drive the band at 320 made it shorter again, so the previous
   ramp (zero at 580px) never reached zero inside it and the whole hero went to
   band luma 46 against a floor of 60. This ramp is scaled to the band that is
   actually there.
   ⛔ AND IT STILL COVERS THE TYPE FIRST. At 320 a headline pinned at 3x body is
   six lines, and with two buttons and the document row the type occupies almost
   the whole band — there is no width of clean photograph to be had without
   either shrinking the type below its floor or pushing the refusal past the
   fold. Both are worse than a darker picture on the narrowest phone, so the
   picture-side number is reported under floor rather than bought. */
@media (max-width:22.5rem){
  .lanaihero__scrim{
    background:linear-gradient(0deg,
      rgba(5,18,32,.93) 0px,
      rgba(5,18,32,.90) 180px,
      rgba(5,18,32,.76) 300px,
      rgba(5,18,32,.46) 400px,
      rgba(5,18,32,.16) 470px,
      rgba(5,18,32,0)   530px);
  }
}

/* ⚖️ 320 RAMP, TUNED TO THIS SITE'S OWN BAND. One shared set of stops could not
   serve four heroes whose headlines wrap to four, five and six lines: the same
   ramp left roofing's band at 48 luma with contrast to spare, and dropped
   plumbing-hvac's headline to 2.61 against a floor of 3. The type is covered
   first, everywhere; what varies is how much photograph is left over. */
@media (max-width:22.5rem){
  .lanaihero__scrim{
    background:linear-gradient(0deg,
      rgba(5,18,32,.93) 0px,
      rgba(5,18,32,.90) 150px,
      rgba(5,18,32,.74) 250px,
      rgba(5,18,32,.44) 330px,
      rgba(5,18,32,.15) 390px,
      rgba(5,18,32,0)   440px);
  }
}
