/* ═══════════════════════════════════════════════════════════════════════════
   ⛔ 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.
   ═══════════════════════════════════════════════════════════════════════════ */
/* =========================================================================
   IRONLEAF GARAGE DOORS — "PANEL GRID"
   A demonstration site. The layout is a sectional door: rectangular panels
   separated by fine seams, stacked in bands.

   RULES BAKED INTO THIS FILE — do not "improve" them out:
   - No border-radius. Anywhere. A door section has square corners.
   - No box-shadow. Depth comes from seams and hairlines only.
   - One accent (caution yellow). It is reserved for the REFUSED line and the
     SAFETY REVERSE TEST. If it starts appearing on buttons and headings it
     stops meaning anything.
   - Type sizes in rem, never px, so a reader who sets a larger base size
     actually gets one (WCAG 1.4.4 / 1.4.10).
   - Zero external requests. System font stacks only. This file is the only
     asset the pages load.
   ========================================================================= */

:root{
  --ground:#E7E9EC;
  --panel:#FFFFFF;
  --panel-2:#DDE1E6;
  --panel-3:#F3F4F6;
  --seam:#BEC4CC;
  --seam-soft:#D6DAE0;
  --ink:#15171A;
  --ink-2:#454B52;
  --ink-3:#6B727A;
  --ink-inv:#EDEFF2;
  --accent:#F6C400;
  --accent-deep:#D8A900;
  --accent-ink:#141310;

  --sans:"Avenir Next","Avenir","Futura","Century Gothic","Trebuchet MS",
         system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Monaco,Consolas,
         "Liberation Mono","Courier New",monospace;

  --t-11:0.6875rem; --t-12:0.75rem;  --t-13:0.8125rem; --t-15:0.9375rem;
  --t-17:1.0625rem; --t-20:1.25rem;  --t-24:1.5rem;    --t-30:1.875rem;
  --t-40:2.5rem;

  --gut:1.25rem;
}

*,*::before,*::after{box-sizing:border-box}
html{font-size:100%;-webkit-text-size-adjust:100%}
body{
  margin:0;
  background:var(--ground);
  color:var(--ink);
  font-family:var(--sans);
  font-size:var(--t-17);
  line-height:1.6;
  overflow-x:hidden;
}
img,svg{max-width:100%}
a{color:inherit}
h1,h2,h3,h4{margin:0;font-weight:700;letter-spacing:-0.015em;line-height:1.04}
p{margin:0 0 0.9em}
p:last-child{margin-bottom:0}
ul,ol{margin:0 0 0.9em;padding-left:1.15em}
li{margin:0 0 0.45em}
li:last-child{margin-bottom:0}
strong,b{font-weight:700}
hr{border:0;border-top:1px solid var(--seam);margin:1.5rem 0}

/* --- skip link ---------------------------------------------------------- */
.skip{position:absolute;left:-9999px;top:0;z-index:200}
.skip:focus{left:0;background:var(--ink);color:#fff;padding:.6rem 1rem;font-size:var(--t-13)}

/* --- demonstration strip ------------------------------------------------ */
.demostrip{
  background:var(--ink);
  color:var(--ink-inv);
  font-size:var(--t-13);
  line-height:1.45;
  padding:.6rem var(--gut);
  display:flex;
  gap:.6rem;
  align-items:flex-start;
  border-bottom:2px solid var(--accent);
}
.demostrip b{color:#fff}
.demostrip a{color:var(--accent);text-underline-offset:2px}
.demostrip__dot{
  flex:0 0 auto;width:.5rem;height:.5rem;background:var(--accent);
  margin-top:.45rem;
}

/* --- shell -------------------------------------------------------------- */
.wrap{max-width:74rem;margin:0 auto;padding:0 var(--gut)}
.band{padding:2.75rem 0}
.band--tight{padding:1.75rem 0}
.band--ink{background:var(--ink);color:var(--ink-inv)}
.band--ink h2,.band--ink h3{color:#fff}

/* --- masthead ----------------------------------------------------------- */
.mast{background:var(--panel);border-bottom:1px solid var(--seam)}
.mast__in{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem 1.25rem;padding-top:.85rem;padding-bottom:.85rem}
.mark{display:flex;align-items:center;gap:.7rem;text-decoration:none;min-width:0}
.mark__glyph{flex:0 0 auto}
.mark__nm{display:block;font-weight:700;font-size:var(--t-17);letter-spacing:.02em;text-transform:uppercase;line-height:1.1}
.mark__sub{display:block;font-size:var(--t-11);letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);font-weight:600}
.mast__tel{margin-left:auto;display:flex;align-items:center;gap:1rem;flex-wrap:wrap}
.tel{
  font-family:var(--mono);font-size:var(--t-15);font-weight:700;
  text-decoration:none;letter-spacing:.02em;
  border:1px solid var(--ink);padding:.5rem .8rem;display:inline-block;
}
.tel:hover{background:var(--ink);color:#fff}
.tel--big{font-size:var(--t-20);padding:.7rem 1rem}

/* --- navigation: a hard horizontal band, square hit areas ---------------- */
.nav{background:var(--ink)}
.nav ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap}
.nav li{margin:0}
.nav a{
  display:block;text-decoration:none;color:#B9BFC7;
  font-size:var(--t-11);letter-spacing:.15em;text-transform:uppercase;font-weight:600;
  padding:.95rem 1rem;border-right:1px solid #2C3036;
}
.nav a:hover,.nav a:focus{background:#292D33;color:#fff}
.nav a[aria-current="page"]{background:#33383F;color:#fff;box-shadow:inset 0 -3px 0 #fff}

/* --- the panel grid ----------------------------------------------------- */
.grid{
  display:grid;gap:1px;background:var(--seam);
  border:1px solid var(--seam);
}
.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
.g3{grid-template-columns:repeat(3,minmax(0,1fr))}
.g4{grid-template-columns:repeat(4,minmax(0,1fr))}
.p{background:var(--panel);padding:1.35rem;min-width:0}
.p--2{background:var(--panel-2)}
.p--3{background:var(--panel-3)}
.p--ink{background:var(--ink);color:var(--ink-inv)}
.p--ink h2,.p--ink h3{color:#fff}
.p--pad{padding:1.9rem}
/* A flat panel holds the drawing. It must FILL the panel: the drawing block has
   its own ground, so a partly-filled panel reads as two colors in one box. */
.p--flat{padding:0;display:flex;flex-direction:column}
.s2{grid-column:span 2}
.s3{grid-column:span 3}
.s4{grid-column:span 4}
.r2{grid-row:span 2}
.p--mid{display:flex;flex-direction:column;justify-content:center}

/* --- labels, eyebrows, type -------------------------------------------- */
.lab{
  display:block;font-size:var(--t-11);letter-spacing:.19em;text-transform:uppercase;
  font-weight:700;color:var(--ink-3);margin:0 0 .55rem;
}
.p--ink .lab{color:#98A1AB}
.h1{font-size:clamp(2rem,6.2vw,3.6rem);letter-spacing:-0.028em}
.h2{font-size:clamp(1.5rem,3.6vw,2.25rem)}
.h3{font-size:var(--t-20)}
.h4{font-size:var(--t-17);letter-spacing:.01em}
.lead{font-size:var(--t-20);line-height:1.45;color:var(--ink-2)}
/* A .lead sitting directly on an ink band inherits the DARK gray above and
   disappears into the background. Both selectors are required: .p--ink covers a
   lead inside an ink panel, .band--ink covers one on the band itself. */
.p--ink .lead,.band--ink .lead{color:#C6CCD3}
.small{font-size:var(--t-13);color:var(--ink-2);line-height:1.55}
.p--ink .small{color:#AEB6BE}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}
.upper{text-transform:uppercase;letter-spacing:.09em}
.muted{color:var(--ink-3)}

/* --- the square number on a service panel ------------------------------- */
.num{
  display:inline-flex;align-items:center;justify-content:center;
  width:2.1rem;height:2.1rem;background:var(--ink);color:#fff;
  font-family:var(--mono);font-size:var(--t-13);font-weight:700;
  margin:0 0 .8rem;
}
.p--ink .num{background:#fff;color:var(--ink)}

/* --- buttons: rectangles, no radius ------------------------------------- */
.btn{
  display:inline-block;text-decoration:none;font-weight:700;
  font-size:var(--t-13);letter-spacing:.12em;text-transform:uppercase;
  padding:.85rem 1.15rem;border:1px solid var(--ink);background:var(--ink);color:#fff;
}
.btn:hover{background:#000}
.btn--out{background:transparent;color:var(--ink)}
.btn--out:hover{background:var(--ink);color:#fff}
.p--ink .btn--out{color:#fff;border-color:#fff}
.p--ink .btn--out:hover{background:#fff;color:var(--ink)}
.btnrow{display:flex;flex-wrap:wrap;gap:.6rem;margin-top:1.1rem}

/* --- the drawing -------------------------------------------------------- */
.dwg{display:block;width:100%;height:auto}
.dwg-wrap{background:var(--panel-3);padding:1.1rem;flex:1;
  display:flex;flex-direction:column;justify-content:center}
.dwg-cap{font-size:var(--t-11);letter-spacing:.11em;text-transform:uppercase;color:var(--ink-3);
  font-weight:700;margin-top:.75rem;border-top:1px solid var(--seam-soft);padding-top:.55rem}

/* --- THE TICKET --------------------------------------------------------- */
.ticket{background:var(--panel)}
.ticket__hd{
  display:flex;flex-wrap:wrap;gap:.5rem 1.5rem;align-items:baseline;
  padding:1.1rem 1.35rem;background:var(--ink);color:#fff;
}
.ticket__no{font-family:var(--mono);font-weight:700;font-size:var(--t-20);letter-spacing:.04em}
.ticket__meta{font-size:var(--t-11);letter-spacing:.16em;text-transform:uppercase;color:#9AA3AD;font-weight:700}
.spec{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;background:var(--seam-soft)}
.spec div{background:var(--panel-3);padding:.8rem 1rem}
.spec dt{font-size:var(--t-11);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);font-weight:700}
.spec dd{margin:.2rem 0 0;font-family:var(--mono);font-size:var(--t-13);line-height:1.4}
.line{
  display:grid;grid-template-columns:2.2rem minmax(0,1fr) 6rem;
  gap:.9rem;padding:1rem 1.35rem;border-top:1px solid var(--seam-soft);align-items:start;
}
.line__n{font-family:var(--mono);font-size:var(--t-12);color:var(--ink-3);font-weight:700;padding-top:.2rem}
.line__t{font-size:var(--t-15);line-height:1.55}
.line__t b{display:block;text-transform:uppercase;letter-spacing:.055em;font-size:var(--t-13);margin-bottom:.3rem}
.line__a{font-family:var(--mono);font-size:var(--t-15);text-align:right;font-weight:700;white-space:nowrap}
.line--zero .line__a{color:var(--ink-3)}
.line--tot{background:var(--panel-2);border-top:2px solid var(--ink)}
.line--tot .line__t b{font-size:var(--t-15)}
.line--tot .line__a{font-size:var(--t-20)}

/* THE REFUSED LINE. This is the most important thing on the site. */
.line--refused{
  background:var(--accent);color:var(--accent-ink);
  border-top:3px solid var(--accent-ink);border-bottom:3px solid var(--accent-ink);
}
.line--refused .line__n{color:rgba(20,19,16,.6)}
.line--refused .line__t b{font-size:var(--t-15);letter-spacing:.045em}
.line--refused .line__a{color:var(--accent-ink)}

/* --- the safety block (the only other place the accent is allowed) ------- */
.safety{background:var(--accent);color:var(--accent-ink)}
.safety .lab{color:rgba(20,19,16,.62)}
.safety a{color:var(--accent-ink)}
.safety__rule{border-top:2px solid var(--accent-ink);margin:.9rem 0}

/* --- fact rows ---------------------------------------------------------- */
.facts{display:grid;gap:1px;background:var(--seam)}
.fact{background:var(--panel);padding:1.1rem 1.35rem;display:grid;
  grid-template-columns:9.5rem minmax(0,1fr);gap:1.1rem;align-items:start}
.fact dt{font-family:var(--mono);font-size:var(--t-13);font-weight:700;line-height:1.45}
.fact dd{margin:0;font-size:var(--t-15);color:var(--ink-2);line-height:1.55}

/* --- prose panel -------------------------------------------------------- */
.prose{max-width:44rem}
.prose h2{margin:1.9rem 0 .7rem;font-size:var(--t-24)}
.prose h3{margin:1.5rem 0 .5rem;font-size:var(--t-17);text-transform:uppercase;letter-spacing:.075em}
.prose h2:first-child,.prose h3:first-child{margin-top:0}
.prose p,.prose li{color:var(--ink-2)}
.prose strong{color:var(--ink)}

/* --- callout ------------------------------------------------------------ */
.note{border-left:4px solid var(--ink);background:var(--panel-3);padding:1rem 1.15rem;margin:1.2rem 0}
.note .lab{margin-bottom:.35rem}

/* --- form --------------------------------------------------------------- */
.field{display:block;margin:0 0 1rem}
.field span{display:block;font-size:var(--t-11);letter-spacing:.15em;text-transform:uppercase;
  font-weight:700;color:var(--ink-3);margin-bottom:.35rem}
.field input,.field select,.field textarea{
  width:100%;font:inherit;font-size:var(--t-15);padding:.65rem .7rem;
  border:1px solid var(--seam);background:#fff;color:var(--ink);border-radius:0;
}
.field textarea{min-height:7rem;resize:vertical}
.field input:focus,.field select:focus,.field textarea:focus{outline:2px solid var(--ink);outline-offset:-2px}

/* --- reviews empty state ------------------------------------------------ */
.empty{border:2px dashed var(--seam);background:var(--panel-3);padding:2.25rem 1.35rem;text-align:center}
.empty__glyph{width:2.5rem;height:2.5rem;border:2px solid var(--ink-3);margin:0 auto .9rem;
  display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-weight:700;color:var(--ink-3)}

/* --- footer ------------------------------------------------------------- */
.foot{background:var(--ink);color:#A8B0B9;font-size:var(--t-13);line-height:1.6}
.foot a{color:#D9DEE4;text-decoration:none}
.foot a:hover{color:#fff;text-decoration:underline}
.foot h4{color:#fff;font-size:var(--t-11);letter-spacing:.19em;text-transform:uppercase;margin:0 0 .7rem}
.foot__cols{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1.75rem;padding:2.25rem 0}
.foot ul{list-style:none;margin:0;padding:0}
.foot li{margin:0 0 .35rem}
.foot__base{border-top:1px solid #2C3036;padding:1.1rem 0;font-size:var(--t-12);color:#7C848D}

/* --- chat launcher: bottom LEFT (the host page owns bottom right) -------- */
.chat{position:fixed;left:1rem;bottom:1rem;z-index:120;font-family:var(--sans)}
.chat__btn{
  display:flex;align-items:center;gap:.55rem;background:var(--ink);color:#fff;
  border:0;border-radius:0;cursor:pointer;font:inherit;font-size:var(--t-13);
  font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:.85rem 1rem;
}
.chat__btn:hover{background:#000}
.chat__btn i{display:block;width:.55rem;height:.55rem;background:var(--accent);font-style:normal}
.chat__win{
  position:absolute;left:0;bottom:3.4rem;width:min(21rem,calc(100vw - 2rem));
  background:var(--panel);border:1px solid var(--ink);display:none;
}
.chat__win[data-open="1"]{display:block}
.chat__hd{background:var(--ink);color:#fff;padding:.7rem .85rem;display:flex;
  justify-content:space-between;align-items:center;gap:.5rem}
.chat__hd b{font-size:var(--t-13);letter-spacing:.09em;text-transform:uppercase}
.chat__hd small{display:block;font-size:var(--t-11);color:#98A1AB;letter-spacing:.04em;text-transform:none}
.chat__x{background:none;border:0;color:#fff;font:inherit;font-size:var(--t-17);cursor:pointer;padding:0 .2rem;line-height:1}
.chat__log{padding:.85rem;max-height:19rem;overflow-y:auto;background:var(--panel-3)}
.chat__msg{font-size:var(--t-13);line-height:1.55;padding:.6rem .7rem;margin:0 0 .5rem;max-width:92%}
.chat__msg--bot{background:#fff;border:1px solid var(--seam-soft)}
.chat__msg--me{background:var(--ink);color:#fff;margin-left:auto}
.chat__msg--warn{background:var(--accent);color:var(--accent-ink);border:1px solid var(--accent-ink)}
.chat__opts{padding:.6rem .85rem .85rem;border-top:1px solid var(--seam);background:var(--panel);display:grid;gap:.4rem}
.chat__opt{
  text-align:left;font:inherit;font-size:var(--t-13);cursor:pointer;
  background:#fff;border:1px solid var(--ink);color:var(--ink);padding:.55rem .65rem;border-radius:0;
}
.chat__opt:hover{background:var(--ink);color:#fff}
.chat__in{display:flex;gap:.4rem}
.chat__in input{flex:1;min-width:0;font:inherit;font-size:var(--t-13);padding:.55rem;border:1px solid var(--seam);border-radius:0}
.chat__in button{font:inherit;font-size:var(--t-12);font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  background:var(--ink);color:#fff;border:0;border-radius:0;padding:.55rem .8rem;cursor:pointer}
.chat__foot{padding:.5rem .85rem;background:var(--panel-2);font-size:var(--t-11);color:var(--ink-2);line-height:1.45}

/* --- responsive --------------------------------------------------------- */
@media (max-width:64rem){
  .foot__cols{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:56rem){
  .g4{grid-template-columns:repeat(2,minmax(0,1fr))}
  .g3{grid-template-columns:repeat(2,minmax(0,1fr))}
  .s3,.s4{grid-column:span 2}
  .r2{grid-row:auto}
  .spec{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:34rem){
  :root{--gut:0.9rem}
  .g2,.g3,.g4{grid-template-columns:minmax(0,1fr)}
  .s2,.s3,.s4{grid-column:span 1}
  .spec{grid-template-columns:minmax(0,1fr)}
  .fact{grid-template-columns:minmax(0,1fr);gap:.3rem}
  .line{grid-template-columns:1.6rem minmax(0,1fr);gap:.6rem}
  .line__a{grid-column:2;text-align:left;margin-top:.35rem}
  .nav a{padding:.8rem .75rem;font-size:0.625rem;letter-spacing:.1em}
  .mast__tel{margin-left:0;width:100%}
  .foot__cols{grid-template-columns:minmax(0,1fr);gap:1.4rem}
  .p{padding:1.05rem}
  .p--pad{padding:1.2rem}
}

@media print{.chat,.nav{display:none}}


/* =========================================================================
   ENRICHMENT LAYER — depth, light, motion, illustration, image slots.

   Everything below obeys the rules at the top of this file. Read them again
   before adding anything here:
     - Still no border-radius. Still no box-shadow. Depth is made of
       HAIRLINES and TONE, exactly the way an embossed door section reads:
       a bright line along the top edge, a dark line along the bottom edge,
       and a directional wash so the light comes from one side.
     - The caution yellow is still reserved for the safety reverse test and
       the REFUSED line. A second, COOL family (galvanized blue-gray — the
       color of a track, a drum, a length of shaft) carries structure so the
       page can have color without spending the yellow.
     - Motion NEVER hides content. A reveal is an .is-in class that only
       enhances; if the script never runs, every panel is already visible.
     - Zero external requests. Nothing here loads a font, an image or a
       script from anywhere.
   ========================================================================= */

:root{
  /* the cool secondary: galvanized track, drum, shaft */
  --steel:#5C6B7A;
  --steel-2:#78889A;
  --steel-deep:#37444F;
  --steel-tint:#D9E1E9;
  --steel-wash:#ECF0F4;
  --steel-line:#9FAEBC;

  /* hairlines that make a panel read as embossed steel */
  --edge-hi:rgba(255,255,255,.92);
  --edge-lo:rgba(21,23,26,.14);
  --edge-hi-ink:rgba(255,255,255,.10);
  --edge-lo-ink:rgba(0,0,0,.42);

  /* a tighter, more confident scale */
  --t-34:2.125rem; --t-48:3rem;

  --ease:cubic-bezier(.22,.7,.25,1);
  --dur:190ms;
}

/* --- LIGHT ACROSS THE GRID ----------------------------------------------
   The seam layer under every grid carries a directional gradient, and the
   panels sitting on it are very slightly translucent. The result is that a
   panel on the left of a band is a genuine step lighter than the same panel
   on the right — light falling across a door, not a flat field of boxes. */
.grid{
  background-color:var(--seam);
  background-image:linear-gradient(102deg,#CDD4DC 0%,#BFC5CD 48%,#AFB7C1 100%);
}
.grid--hero{ background-image:none; }  /* moved to .band--hero so it reaches the page edges */
.facts{
  background-color:var(--seam);
  background-image:linear-gradient(102deg,#CDD4DC 0%,#BFC5CD 100%);
}

/* --- THE EMBOSSED PANEL -------------------------------------------------
   background-color stays a separate property from background-image on
   purpose: the tone modifiers below only change the color, and the two
   hairlines plus the vertical wash survive every one of them. */
.p{
  background-color:rgba(255,255,255,.88);
  background-image:
    linear-gradient(to bottom,var(--edge-hi),var(--edge-hi)),
    linear-gradient(to bottom,var(--edge-lo),var(--edge-lo)),
    linear-gradient(177deg,rgba(255,255,255,.60) 0%,rgba(255,255,255,0) 34%,rgba(21,23,26,.05) 100%);
  background-repeat:no-repeat;
  background-position:0 0,0 100%,0 0;
  background-size:100% 1px,100% 1px,100% 100%;
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.p--2{background-color:rgba(216,221,227,.86)}
.p--3{background-color:rgba(246,248,250,.90)}
/* a step lighter / a step darker, for tonal variation inside one band */
.p--lit{background-color:rgba(255,255,255,.97)}
.p--dim{background-color:rgba(203,210,218,.88)}
.p--steel{background-color:rgba(217,225,233,.92)}

.p--ink{
  background-color:var(--ink);
  background-image:
    linear-gradient(to bottom,var(--edge-hi-ink),var(--edge-hi-ink)),
    linear-gradient(to bottom,var(--edge-lo-ink),var(--edge-lo-ink)),
    linear-gradient(177deg,rgba(255,255,255,.055) 0%,rgba(255,255,255,0) 42%,rgba(0,0,0,.22) 100%);
}
.safety{
  background-color:var(--accent);
  background-image:
    linear-gradient(to bottom,rgba(255,255,255,.55),rgba(255,255,255,.55)),
    linear-gradient(to bottom,rgba(20,19,16,.30),rgba(20,19,16,.30)),
    linear-gradient(177deg,rgba(255,255,255,.34) 0%,rgba(255,255,255,0) 40%,rgba(20,19,16,.10) 100%);
  background-repeat:no-repeat;
  background-position:0 0,0 100%,0 0;
  background-size:100% 1px,100% 1px,100% 100%;
}

/* --- HOVER: a panel lifts one tone, its seam brightens ------------------ */
.p:hover{background-color:rgba(255,255,255,.99)}
.p--2:hover{background-color:rgba(231,235,240,.95)}
.p--3:hover{background-color:rgba(255,255,255,.99)}
.p--dim:hover{background-color:rgba(224,230,236,.94)}
.p--steel:hover{background-color:rgba(233,239,245,.97)}
.p--ink:hover{background-color:#1E2126}
.safety:hover{background-color:#FFD01A}
.p--flat:hover{background-color:rgba(255,255,255,.88)}

/* --- BAND GROUNDS: the page itself gets light, not one flat gray -------- */
body{background-image:linear-gradient(178deg,#EDEFF2 0%,#E7E9EC 38%,#DFE3E8 100%);
  background-attachment:fixed}
/* 🔴 THE HERO PANEL RUNS FULL PAGE - 13 Sep 2026. Owner: "the header was
   supposed to be full page in the service website hero this is broke". The
   steel panel was painted on .grid--hero, which lives INSIDE .wrap
   (max-width:74rem), so the hero was a 1136px card while the site header above
   it ran the full 1280 - the band stopped short of the page edges and read as
   boxed-in. The ground belongs on the BAND; .wrap still constrains the content
   inside it. DO NOT put a hero's ground back on an element a max-width wrapper
   contains. */
.band--hero{background-image:linear-gradient(101deg,rgba(255,255,255,.55) 0%,rgba(255,255,255,0) 62%),linear-gradient(102deg,#D2D9E1 0%,#BEC4CC 44%,#A8B1BC 100%)}
.band--ink{
  background-image:
    linear-gradient(101deg,#1C2026 0%,#15171A 46%,#101215 100%),
    none;
  border-top:1px solid #2E333A;border-bottom:1px solid #0A0C0E;
}
.band--steel{
  background-image:linear-gradient(101deg,var(--steel-wash) 0%,var(--steel-tint) 100%);
  border-top:1px solid var(--steel-line);border-bottom:1px solid var(--steel-line);
}

/* --- TYPE: bigger headlines, tighter scale ------------------------------ */
.h1{font-size:clamp(2.15rem,6.4vw,4rem);letter-spacing:-0.034em;line-height:1.0}
.h2{font-size:clamp(1.6rem,3.9vw,2.5rem);letter-spacing:-0.026em;line-height:1.05}
.h3{font-size:var(--t-20);letter-spacing:-0.014em}
.lead{font-size:clamp(1.0625rem,1.9vw,1.3125rem);line-height:1.42;letter-spacing:-0.006em}
.lab{color:var(--steel);position:relative;padding-left:.95rem}
.lab::before{content:"";position:absolute;left:0;top:.42em;width:.5rem;height:.5rem;
  background:var(--steel-2)}
.p--ink .lab{color:#9FB0C0}
.p--ink .lab::before{background:var(--steel-2)}
.safety .lab{color:rgba(20,19,16,.70)}
.safety .lab::before{background:rgba(20,19,16,.62)}
.prose h2{letter-spacing:-0.022em}

/* --- LINKS, BUTTONS, NAV: everything answers the pointer in 150–250ms --- */
a{transition:color var(--dur) var(--ease),background-color var(--dur) var(--ease),
  border-color var(--dur) var(--ease)}
.btn{
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease),
    border-color var(--dur) var(--ease),letter-spacing var(--dur) var(--ease);
  position:relative;
}
.btn:hover{background-color:#000;letter-spacing:.135em}
.btn--out{border-color:var(--steel-deep);color:var(--steel-deep)}
.btn--out:hover{background-color:var(--steel-deep);border-color:var(--steel-deep);color:#fff}
.p--ink .btn--out{border-color:#8FA0B0;color:#E6ECF2}
.p--ink .btn--out:hover{background-color:#fff;border-color:#fff;color:var(--ink)}
.tel{transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease),
  border-color var(--dur) var(--ease)}
.nav a{transition:background-color 150ms var(--ease),color 150ms var(--ease),
  box-shadow 150ms var(--ease)}
.nav a:hover,.nav a:focus{box-shadow:inset 0 -3px 0 var(--steel-2)}
.foot a{transition:color var(--dur) var(--ease)}
.mark__glyph rect{transition:fill var(--dur) var(--ease)}
.mark:hover .mark__glyph rect:nth-of-type(3){fill:#C7D2DC}
.demostrip a:hover{color:#fff}

/* a service panel is a link target end to end */
.p--link{cursor:pointer}
.p--link h3 a,.p--link h4 a{background-image:linear-gradient(var(--steel),var(--steel));
  background-repeat:no-repeat;background-position:0 100%;background-size:0 2px;
  transition:background-size 200ms var(--ease)}
.p--link:hover h3 a,.p--link:hover h4 a,.p--link h3 a:focus{background-size:100% 2px}

/* --- THE NUMBER CHIP ---------------------------------------------------- */
.num{transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease)}
.p--link:hover .num{background-color:var(--steel-deep)}

/* --- MECHANICAL ICONS --------------------------------------------------- */
.ico{display:block;width:3.1rem;height:3.1rem;margin:0 0 .8rem;color:var(--steel);
  transition:color var(--dur) var(--ease),transform 220ms var(--ease)}
.ico--lg{width:4.4rem;height:4.4rem}
.p--ink .ico{color:#A9BBCA}
.p--link:hover .ico{color:var(--ink);transform:translateY(-2px)}
/* The second tone inside an icon is INK, not the caution yellow. The yellow
   is spent only where it means something: the safety test. */
.ico .hot{color:var(--ink)}
.p--ink .ico{color:#8FA6B8}
.p--ink .ico .hot{color:#FFFFFF}
.ico--safety .hot{color:var(--accent-deep)}
.p--ink .ico--safety .hot{color:var(--accent)}
.safety .ico{color:rgba(20,19,16,.72)}
.safety .ico .hot{color:var(--accent-ink)}

/* --- DRAWINGS ----------------------------------------------------------- */
.dwg-wrap{
  background-color:var(--steel-wash);
  background-image:linear-gradient(160deg,#F5F8FA 0%,#E6ECF2 55%,#D8E1E9 100%);
}
.dwg-cap{border-top-color:var(--steel-line);color:var(--steel)}
.dwg-note{font-size:var(--t-13);color:var(--ink-2);line-height:1.5;margin-top:.7rem}

/* a diagram sitting inside a normal panel rather than filling a flat one */
.diag{display:block;width:100%;height:auto;margin:.9rem 0 0;
  border:1px solid var(--steel-line);background-color:#F6F9FB}
.safety .diag{border-color:rgba(20,19,16,.45);background-color:rgba(255,255,255,.72)}
.diag-cap{font-size:var(--t-11);letter-spacing:.1em;text-transform:uppercase;font-weight:700;
  color:var(--steel);margin-top:.5rem}
.safety .diag-cap{color:rgba(20,19,16,.72)}

/* --- IMAGE SLOTS — built for a photograph that has not been taken yet ----
   Stable class names. When the real photograph arrives it replaces the
   <svg class="shot__art"> and nothing else moves. The slot is drawn line
   art on a tonal wash with a hard label on it: it must never be mistaken
   for a photograph, and it must never read as a broken image. */
.shot{position:relative;display:block;border:1px solid var(--steel-line);
  background-color:var(--steel-wash);
  transition:border-color var(--dur) var(--ease)}
.shot:hover{border-color:var(--steel-deep)}
.shot__art{display:block;width:100%;height:auto}
.shot__tag{position:absolute;top:0;left:0;background:var(--ink);color:#fff;
  font-family:var(--mono);font-size:var(--t-11);font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;padding:.3rem .55rem;line-height:1.2}
.shot__ratio{position:absolute;top:0;right:0;background:rgba(21,23,26,.86);color:#C9D3DC;
  font-family:var(--mono);font-size:var(--t-11);font-weight:700;letter-spacing:.1em;
  padding:.3rem .55rem;line-height:1.2}
.shot__cap{border-top:1px solid var(--steel-line);background:rgba(255,255,255,.72);
  padding:.6rem .75rem;font-size:var(--t-12);line-height:1.45;color:var(--ink-2)}
.shot__cap b{display:block;font-size:var(--t-11);letter-spacing:.15em;text-transform:uppercase;
  color:var(--steel-deep);margin-bottom:.2rem}
.shotgrid{display:grid;gap:1px;background:var(--steel-line);border:1px solid var(--steel-line);
  grid-template-columns:repeat(2,minmax(0,1fr))}
.shotgrid>.shot{border:0}
@media (max-width:44rem){.shotgrid{grid-template-columns:minmax(0,1fr)}}

/* --- THE TICKET, rendered as an object spanning the grid ----------------- */
.ticket{
  background-color:var(--panel);
  background-image:linear-gradient(101deg,#FFFFFF 0%,#FAFBFC 58%,#F0F3F6 100%);
}
.ticket__hd{
  background-image:linear-gradient(101deg,#22272E 0%,#15171A 55%,#0E1013 100%);
  border-bottom:2px solid var(--steel);
}
.ticket__no{position:relative}
.spec{background-color:var(--steel-line)}
.spec div{background-color:#F4F7F9;
  background-image:linear-gradient(to bottom,rgba(255,255,255,.9),rgba(255,255,255,.9)),
    linear-gradient(177deg,rgba(255,255,255,.55) 0,rgba(255,255,255,0) 50%,rgba(21,23,26,.045) 100%);
  background-repeat:no-repeat;background-position:0 0,0 0;
  background-size:100% 1px,100% 100%;
  transition:background-color var(--dur) var(--ease)}
.spec div:hover{background-color:#FFFFFF}
.spec dd{color:var(--steel-deep)}
.line{transition:background-color var(--dur) var(--ease)}
.line:hover{background-color:#FBFCFD}
.line__n{color:var(--steel)}
.line--zero:hover{background-color:#F7F9FB}
.line--tot{background-color:var(--panel-2);
  background-image:linear-gradient(101deg,#E4E8ED 0%,#D8DDE3 100%)}
.line--tot:hover{background-color:var(--panel-2)}
/* THE REFUSED LINE. Enriched, never diluted: it gets the strongest edge on
   the page and it does not move. */
.line--refused{
  background-image:linear-gradient(101deg,#FFD426 0%,#F6C400 52%,#E8B800 100%);
}
.line--refused:hover{background-color:var(--accent);
  background-image:linear-gradient(101deg,#FFDC45 0%,#FFCE10 52%,#F0C000 100%)}

/* --- FACTS, NOTES, EMPTY STATE ------------------------------------------ */
.fact{background-color:rgba(255,255,255,.9);
  background-image:linear-gradient(to bottom,var(--edge-hi),var(--edge-hi));
  background-repeat:no-repeat;background-size:100% 1px;background-position:0 0;
  transition:background-color var(--dur) var(--ease)}
.fact:hover{background-color:#fff}
.fact dt{color:var(--steel-deep)}
.note{border-left-color:var(--steel-deep);
  background-color:var(--steel-wash);
  background-image:linear-gradient(101deg,rgba(255,255,255,.8) 0%,rgba(255,255,255,0) 100%)}
.note .lab{color:var(--steel-deep)}
.empty{border-color:var(--steel-line);
  background-image:linear-gradient(160deg,#F4F7FA 0%,#E6ECF2 100%)}
.empty__glyph{border-color:var(--steel);color:var(--steel)}

/* --- FORM --------------------------------------------------------------- */
.field input,.field select,.field textarea{
  transition:border-color var(--dur) var(--ease),background-color var(--dur) var(--ease)}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--steel)}
.field span{color:var(--steel-deep)}

/* --- FOOTER: light falls across it too ---------------------------------- */
.foot{background-image:linear-gradient(101deg,#1B1F25 0%,#15171A 50%,#0F1114 100%)}
.foot h4{position:relative;padding-left:.9rem}
.foot h4::before{content:"";position:absolute;left:0;top:.35em;width:.42rem;height:.42rem;
  background:var(--steel-2)}

/* --- CHAT --------------------------------------------------------------- */
.chat__btn{transition:background-color var(--dur) var(--ease),letter-spacing var(--dur) var(--ease)}
.chat__btn:hover{letter-spacing:.115em}
.chat__opt{transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease),
  border-color var(--dur) var(--ease)}
.chat__hd{background-image:linear-gradient(101deg,#22272E,#15171A)}

/* =========================================================================
   MOTION
   Every rule here is additive. .rv is a plain container until a script adds
   .is-in; with no script, nothing is ever hidden. Nothing below uses
   opacity:0 as a resting state.
   ========================================================================= */
@keyframes ilPanelIn{
  from{opacity:0;transform:translate3d(0,-18px,0)}
  to{opacity:1;transform:none}
}
@keyframes ilBeam{
  0%,100%{opacity:.42}
  50%{opacity:1}
}
@keyframes ilMarch{
  to{stroke-dashoffset:-30}
}
@keyframes ilTravel{
  0%,72%,100%{transform:translateY(0)}
  82%,90%{transform:translateY(-4px)}
}
@keyframes ilCoil{
  0%,100%{transform:translateX(0)}
  50%{transform:translateX(2px)}
}
@keyframes ilBoard{
  0%,55%,100%{transform:translateY(0)}
  25%,40%{transform:translateY(9px)}
}

/* Panels arriving in sequence, the way a door stacks: top section first. */
.rv.is-in>*{animation:ilPanelIn 280ms var(--ease) backwards}
.rv.is-in>*:nth-child(1){animation-delay:0ms}
.rv.is-in>*:nth-child(2){animation-delay:55ms}
.rv.is-in>*:nth-child(3){animation-delay:110ms}
.rv.is-in>*:nth-child(4){animation-delay:165ms}
.rv.is-in>*:nth-child(5){animation-delay:220ms}
.rv.is-in>*:nth-child(6){animation-delay:260ms}
.rv.is-in>*:nth-child(7){animation-delay:300ms}
.rv.is-in>*:nth-child(n+8){animation-delay:340ms}

/* The one ambient moment: the photo-eye beam crossing the opening, and the
   door easing up a few units and settling. Both live in the hero drawing. */
.dwg-beam{animation:ilBeam 3.4s ease-in-out infinite,ilMarch 1.6s linear infinite}
.dwg-eye{animation:ilBeam 3.4s ease-in-out infinite}
.dwg-door{animation:ilTravel 11s ease-in-out infinite}
.dwg-spring{animation:ilCoil 5.5s ease-in-out infinite}
.diag-board{animation:ilBoard 6s ease-in-out infinite}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:1ms !important;animation-iteration-count:1 !important;
    transition-duration:1ms !important;scroll-behavior:auto !important;
  }
  .rv.is-in>*{animation:none}
  .dwg-beam,.dwg-eye,.dwg-door,.dwg-spring,.diag-board{animation:none}
  body{background-attachment:scroll}
}

/* --- small screens ------------------------------------------------------ */
@media (max-width:34rem){
  .ico{width:2.6rem;height:2.6rem}
  .ico--lg{width:3.2rem;height:3.2rem}
  .shot__tag,.shot__ratio{font-size:0.625rem;letter-spacing:.08em;padding:.25rem .4rem}
  body{background-attachment:scroll}
}
@media print{.rv.is-in>*{animation:none}}

/* --- the number chip and its mechanism icon, side by side --------------- */
.numrow{display:flex;align-items:center;gap:.9rem;margin:0 0 .85rem}
.numrow .num{margin:0;flex:0 0 auto}
.numrow .ico{margin:0;flex:0 0 auto}
.p--flat>.shot{border:0}
.p--flat>.shot .shot__cap{background:rgba(255,255,255,.82)}
.shotband{display:grid;gap:1px;background:var(--seam);border:1px solid var(--seam)}
.ico--sm{width:2.2rem;height:2.2rem}
@media (max-width:34rem){.ico--sm{width:1.9rem;height:1.9rem}}

/* --- a LIGHT panel sitting inside an INK band ---------------------------
   .band--ink paints every h2/h3 under it white so the headings on the band
   itself are readable. A .p inside that band is a light plate, and a white
   heading on it is an invisible heading. Both selectors are needed: the one
   that repaints the heading, and the one that keeps the plate opaque so the
   dark band does not bleed up through it. */
.band--ink .p:not(.p--ink){background-color:#F1F4F7}
.band--ink .p:not(.p--ink):hover{background-color:#FFFFFF}
.band--ink .p:not(.p--ink) h2,
.band--ink .p:not(.p--ink) h3,
.band--ink .p:not(.p--ink) h4{color:var(--ink)}
.band--ink .p:not(.p--ink) .lead{color:var(--ink-2)}
.band--ink .grid{background-image:linear-gradient(102deg,#4A525C 0%,#39414A 48%,#2A313A 100%)}

/* --- diagram legends ----------------------------------------------------
   The callouts inside a drawing are set in SVG user units, so they shrink
   with the drawing. In a one-column panel on a phone the drawing is about
   half its desktop size and those callouts stop being readable. So every
   diagram also carries a legend in REAL text, which resizes with the
   reader's own font size, and below 44rem the in-drawing labels stand down
   and leave the legend to do the work. The picture still reads: an arrow
   down, an arrow up, and a board on the floor. */
.diag-key{list-style:none;margin:.6rem 0 0;padding:0;display:grid;gap:.3rem}
.diag-key li{margin:0;font-size:var(--t-13);line-height:1.45;color:var(--ink-2);
  display:grid;grid-template-columns:auto minmax(0,1fr);gap:.5rem;align-items:baseline}
.diag-key b{font-family:var(--mono);font-size:var(--t-12);font-weight:700;
  color:var(--steel-deep);white-space:nowrap}
.safety .diag-key li{color:var(--accent-ink)}
.safety .diag-key b{color:rgba(20,19,16,.78)}
@media (max-width:44rem){
  .diag text{display:none}
}
/* dimension and leader lines belong to the hidden callouts, not the picture */
@media (max-width:44rem){ .diag .diag-dim{display:none} }
/* a heading inside a panel needs air before its first paragraph */
.p>h2+p,.p>h3+p,.p>h4+p,.p>.h2+p,.p>.h3+p,.p>.h4+p{margin-top:.55rem}


/* ═══ 📱 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: .1em;
}

/* ══════════════════════════════════════════════════════════════════════════
   ⭐ THE PROFESSIONAL PASS — garage door, 11 Sep 2026.
   The five gaps the roofing demo had, answered in this site's own materials.
   ⚠️ THIS SITE IS LIGHT and its accent is SAFETY YELLOW, which is the one
   accent that cannot be used as text on a pale ground — #F6C400 on #EDEFF2 is
   about 1.7:1 and unreadable. So the strip stays light like the rest of the
   site and the yellow is a FILL with near-black type on it, which is how
   safety yellow is used on a real door anyway. ⛔ Never yellow text on paper.
   ⛔ No photograph. The door is a drawing and stays one.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the mark ─────────────────────────────────────────────────────────── */
/* Was four flat bars in a hard-coded dark square. Now a drawn sectional door
   — head jamb, two jambs, three stacked panels — in currentColor, so it takes
   the accent and inverts inside an ink panel without a second copy. */
.mark__glyph{ width:28px; height:28px; flex:0 0 auto; color:var(--accent-deep);
  transition:transform var(--dur) var(--ease), color var(--dur) var(--ease); }
.mark:hover .mark__glyph{ transform:translateY(-1px); color:var(--accent); }
/* 🔴 A STALE RULE FROM THE OLD GLYPH. `.mark:hover .mark__glyph rect:nth-of-type(3)` still
   sets a fill, and a CSS fill on the rect BEATS the `fill="none"` it inherits from
   its <g> presentation attribute — so hovering the brand filled the bottom door
   panel pale grey. The drawing changed; the rule keyed to it had to change too. */
.mark:hover .mark__glyph rect:nth-of-type(3){ fill:none; }

/* ── the headline carries the colour ──────────────────────────────────── */
/* ⚠️ Yellow type would vanish here, so the headline phrase takes the accent's
   dark end — the same hue, at a weight a headline on white can hold. */
h1 .hl,.h1 .hl{ font-style:normal; color:#8A6B00; }

/* ── the offer strip ──────────────────────────────────────────────────── */
/* ⭐ What the customer actually gets, in the site's own sentence. ⛔ Never a
   price, a rating or a count — this demo has published none of those. */
.offerbar{
  border-top:3px solid var(--accent);
  border-bottom:1px solid var(--seam);
  background-image:linear-gradient(180deg,#FFFFFF,#F1F3F5);
}
.offerbar__in{
  display:flex;align-items:center;gap:.9rem;flex-wrap:wrap;
  padding-top:.6rem;padding-bottom:.6rem;
}
.offerbar__tag{
  font-family:var(--mono);font-size:var(--t-11);text-transform:uppercase;
  letter-spacing:.13em;font-weight:700;
  background:var(--accent);color:var(--accent-ink);
  border:1px solid var(--accent-deep);
  padding:.22rem .55rem;flex:0 0 auto;
}
.offerbar__msg{
  margin:0;font-size:var(--t-13);line-height:1.45;color:var(--ink-2);
  flex:1 1 340px;min-width:0;
}
.offerbar__msg b{ color:var(--ink);font-weight:700; }
.offerbar__go{
  flex:0 0 auto;font-family:var(--mono);font-size:var(--t-11);text-transform:uppercase;
  letter-spacing:.1em;font-weight:700;color:var(--ink);text-decoration:none;white-space:nowrap;
  border-bottom:2px solid var(--accent);padding-bottom:2px;
  transition:color var(--dur) var(--ease),border-bottom-color var(--dur) var(--ease);
}
.offerbar__go:hover{ color:var(--steel-deep);border-bottom-color:var(--accent-deep); }
@media (max-width:640px){
  .offerbar__go{ display:none; }     /* the hero button is just below it */
  .offerbar__msg{ font-size:var(--t-12); }
}

/* ── the primary button is the accent, not the neutral ────────────────── */
/* 🔴 A near-black primary on a grey page makes the NEUTRAL element the loudest
   thing on screen. The accent was already declared and used almost nowhere. */
.btn{
  background:var(--accent);border-color:var(--accent-deep);color:var(--accent-ink);
}
.btn:hover{ background-color:var(--accent-deep);border-color:var(--accent-deep);color:var(--accent-ink); }

/* ── depth on the drawn door ──────────────────────────────────────────── */
.band--hero .dwg-wrap{
  box-shadow:0 20px 34px rgba(21,23,26,.15), 0 2px 6px rgba(21,23,26,.08);
}
.band--hero .dwg{
  filter:drop-shadow(0 12px 16px rgba(21,23,26,.20)) contrast(1.05) saturate(1.04);
}

@media (prefers-reduced-motion:reduce){
  .mark:hover .mark__glyph{ transform:none; }
}

/* 🔴 AND THE SECONDARY IS RESTATED AFTER IT. This site's ghost is `.btn--out`,
   declared ~300 lines above; the file is unlayered, so the `.btn` rule above —
   same specificity, later in the file — takes its background and BOTH buttons
   go solid yellow. Two primaries is no primary. ⛔ Any future `.btn` rule
   appended below this point must restate these again. */
.btn--out{ background:transparent;border-color:var(--steel-deep);color:var(--steel-deep); }
.btn--out:hover{ background-color:var(--steel-deep);border-color:var(--steel-deep);color:#fff; }
.p--ink .btn--out{ background:transparent;border-color:#8FA0B0;color:#E6ECF2; }
.p--ink .btn--out:hover{ background-color:#fff;border-color:#fff;color:var(--ink); }

/* ── 🚪 THE MORNING, AS THE CUSTOMER HAS IT — 11 Sep 2026 ─────────────────
   ⭐ The old headline led with the ticket, which is OUR fact. What the person
   on the other end is buying is the morning that still starts on time, so the
   headline names the trade and the town and hands the accent to that.
   ⛔ The accent colour here is #8A6B00, the SAME dark gold the h1 accent uses
   — the brand yellow (--accent) is a fill on this light ground and is not
   legible as type on it. Do not "fix" this back to var(--accent).
   ⛔ No motion of its own; the page's .rv reveal is already gated on
   prefers-reduced-motion and this section does not use it. */
.band--day .day{list-style:none;margin:1.5rem 0 0;padding:0;display:grid;gap:0}
.day__i{
  display:grid;grid-template-columns:76px 1fr;gap:0 1.4rem;
  padding:1.05rem 0;border-top:1px solid var(--seam-soft);align-items:start;
}
.day__i:first-child{border-top:0}
.day__t{
  font-family:var(--mono);font-size:var(--t-12);letter-spacing:.1em;font-weight:600;
  /* 🔴 #8A6B00 is the h1 accent's gold and it clears contrast AT HEADLINE SIZE
     only — 4.13:1 on the --ground here, which a 12px timestamp does not get to
     claim. Dropped to #755A00, measured at 5.36:1 on rgb(231,233,236). */
  color:#755A00;padding-top:.28rem;white-space:nowrap;font-variant-numeric:tabular-nums;
}
.day__i h3{margin:0 0 .32rem;font-size:var(--t-17);line-height:1.2;letter-spacing:-0.012em;color:var(--ink);max-width:62ch}
.day__i p{margin:0;font-size:var(--t-15);line-height:1.6;color:var(--ink-2);max-width:62ch}
/* ⭐ one beat is marked, and it is the one where the headline comes true — the
   door passes the test and the car comes out. */
.day__i--live .day__t::after{
  content:"";display:inline-block;width:6px;height:6px;border-radius:50%;
  background:var(--accent-deep);margin-left:6px;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 the beat's paragraph in a 100-odd-pixel gutter and makes the row
   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:1.4rem 0 0;padding-top:1.05rem;border-top:1px solid var(--seam-soft);
  font-size:var(--t-15);line-height:1.65;color:var(--ink-2);max-width:66ch;
}
.day__foot b{color:var(--ink);font-weight:700}
@media (max-width:560px){
  .day__i{grid-template-columns:1fr;gap:.28rem}
  .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 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 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}
}

/* ═══ 2026 HERO + TYPE PASS ═══ hand-edited, see the note at the top of this file.
   ⛔ APPENDED deliberately: `.h1` is defined at ~154 and again at ~478, and this
   file is unlayered, so only the LAST definition applies. Measure the computed
   value, never the rule you wrote.
   ⭐ THE FILE'S OWN RULES ARE KEPT, not "improved" out: no border-radius, no
   box-shadow, one accent (caution yellow) and it is still spent on exactly one
   thing — the REFUSED line, which is now the strip at the foot of the hero. */

/* ── 1. h1 >= 3x BODY AT EVERY WIDTH ───────────────────────────────────────
   This was the only one of the four already setting body in rem (--t-17 =
   1.0625rem under html{font-size:100%}), and its header calls that out as a
   baked-in rule — so 1.4.4 was already satisfied here and body is untouched.
   What was NOT satisfied: `.h1` floored at 2.15rem = 34.4px against a 17px
   body, a ratio of 2.02 at 320 and 375 against a bar of 3.0.
   ⭐ The floor is 3.1875rem — exactly 3x --t-17, in the same unit, so the ratio
   cannot drift at any width or at any reader base size. */
.h1{
  font-size:clamp(3.1875rem, 1.6rem + 4.8vw, 4.25rem);
  line-height:1.02;letter-spacing:-0.034em;font-weight:500;text-wrap:balance;
}

/* ── 2. THE HERO IS ONE THING, AND IT IS THE PHOTOGRAPH ────────────────────
   MEASURED BEFORE: 318 words in the fold — a label, the headline, a 56-word
   lead and four side cards (the safety test, the phone, who turns up, the
   spring we will not talk you through) — against a bar of 30. And the hero
   photograph was not in the hero at all: ph-index.jpg sat in its own <figure>
   BELOW the band, so the picture and the headline were two separate objects.
   ⭐ Now: one band, the photograph is its ground, the headline, two doors and
   the refused line. The four cards are the band directly beneath, unchanged —
   they were good writing in the wrong room. */
.band--photo{position:relative;isolation:isolate;overflow:hidden;
  display:flex;flex-direction:column;justify-content:flex-end;
  min-height:min(88vh,44rem);   /* ⛔ min-height, never height: at 320px a
                                   five-line h1 is taller than the viewport and
                                   a fixed height would clip it */
  background:#14181C;padding:0}
.band--photo__bg{position:absolute;inset:0;z-index:0;width:100%;height:100%;
  object-fit:cover;object-position:center 56%}
/* ⭐ A POOL UNDER THE WORDS, NOT A LID ON THE PICTURE. Every VERTICAL stop is
   in px on purpose — a gradient percentage resolves against the ELEMENT, and
   this band runs taller than the screen, so a stop at "58%" would aim below the
   fold. The last third stays at .06: that is the photograph. */
.band--photo__scrim{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    linear-gradient(0deg,rgba(12,14,17,.50) 0px,rgba(12,14,17,0) 240px),
    linear-gradient(92deg,rgba(12,14,17,.88) 0%,rgba(12,14,17,.80) 40%,
      rgba(12,14,17,.66) 58%,rgba(12,14,17,.20) 78%,rgba(12,14,17,.06) 100%)}
/* ⛔ width:100% IS LOAD-BEARING. `.band--photo` is display:flex/column and
   `.wrap` carries `margin:0 auto` — and an auto inline margin on a FLEX item
   absorbs the free space instead of centring inside it, so the wrap collapsed
   to fit-content. Measured: wrap 237px wide inside a 1280px band, the headline
   rendering at 68px in a 197px column over eleven lines, and the hero 1060px
   tall against a 704px min-height. Nothing looked broken; it looked "tall". */
.band--photo>.wrap{position:relative;z-index:2;width:100%;
  padding-top:3.5rem;padding-bottom:1.6rem}
.band--photo .offerbar{position:relative;z-index:2}
/* ⛔ NOT `ch`. `ch` resolves against the element's OWN font-size, and .hero1 is
   a plain 17px block — so `max-width:20ch` bought a 170px column and wrapped a
   68px headline over eleven lines. A measure for display type has to be set in
   a unit that does not depend on which element you happened to hang it on. */
.hero1{max-width:min(100%,48rem)}
/* ⭐ over a photograph there is no secondary text — full ink, and a shadow that
   travels with the glyphs instead of a darker lid on the picture */
.band--photo .h1,.band--photo .btnrow{color:#fff;
  text-shadow:0 1px 2px rgba(0,0,0,.70),0 2px 20px rgba(0,0,0,.52)}
.band--photo .h1 .h1__where{color:#fff}
.band--photo .h1 .hl{color:var(--accent)}      /* the one accent, on the promise */
.band--photo .btnrow{margin-top:1.3rem;display:flex;flex-wrap:wrap;gap:.7rem}
/* 🔴 ONE SELECTOR, TWO BACKDROPS. `.band--photo .btnrow{color:#fff}` above is
   right for the ghost button sitting on the photograph and WRONG for the solid
   one, which is caution yellow: the white label inherited straight through and
   measured 1.15:1 against a floor of 4.5 — a primary call-to-action nobody
   could read. Restore the button's own ink; it is a light backdrop, not a dark
   one. ⛔ Never let a hero's blanket "white over a picture" rule reach a filled
   control. Measured with tools/_heroluma.mjs, glyph pixels, worst tenth. */
.band--photo a.btn{text-shadow:none;color:var(--accent-ink)}
.band--photo a.btn:hover{color:var(--accent-ink)}
.band--photo a.btn--out{background:rgba(12,14,17,.42);border-color:rgba(255,255,255,.62);
  color:#fff;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.band--photo a.btn--out:hover{background:rgba(12,14,17,.66);border-color:#fff}

/* ── 3. THE OFFER STRIP IS THE DOCUMENT ROW ────────────────────────────────
   It was 40 words describing a ticket, behind an uppercase pill, on all
   eighteen pages. It is now line 07 of ticket IL-4472: the force increase this
   customer asked for, refused, at $0.00. Six words, and the caution yellow is
   doing the job the header reserved it for. */
.offerbar__tag,.offerbar__msg,.offerbar__go{display:none}
.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(--ink-2);
}
.herodoc__item{
  flex:1 1 15rem;min-width:0;color:var(--ink);
  font-size:clamp(.9375rem,1.5vw,1.0625rem);line-height:1.35;
}
.herodoc__item b{color:#6B5200;font-weight:700}   /* caution yellow is unreadable
                                                     as text on white paper; this
                                                     is the same hue at 7.4:1 */
.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(--ink);letter-spacing:-.01em;
}

/* ── 4. THE 2018 TELLS, AND THIS FILE'S OWN RULES IT HAD STOPPED KEEPING ───
   Measured: 23 text-transform:uppercase (the heaviest density of the four,
   including `.prose h3`, a real content heading), 7 box-shadows under a header
   that says "No box-shadow. Depth comes from seams and hairlines only", and
   five non-zero border-radius values under "No border-radius. Anywhere."
   ⭐ Uppercase survives only inside the ticket and the spec list, where it is
   how the document itself reads. */
.mark__nm,.mark__sub,.nav a,.lab,.btn--out,.prose h3,.field span,.foot h4,
.chat__btn,.shot__tag,.navbtn{text-transform:none;letter-spacing:.005em}
.lab{border:0;padding-left:0;color:var(--ink-2)}
.photo,.dwg-wrap,.chat__panel,.shot,.navbtn,html[data-scrolled] header{
  box-shadow:none;border-radius:0}
.btn,.btn--out,.field input,.field textarea,.field select{border-radius:0}

/* ⭐ THE MARK IS A FILLED PANEL STACK NOW, 26x30 portrait — and it is made of
   <path>, not <rect>, on purpose: this file still carries
   `.mark:hover .mark__glyph rect:nth-of-type(3){fill:…}`, a stale rule from a
   glyph that no longer exists, and a CSS fill on a rect beats the attribute.
   Using paths means that rule cannot reach the new mark at all. */
.mark__glyph{width:auto;height:30px}

/* ── 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 — GARAGE DOOR: A SQUARE SLAB ═════════════════════════════════════
   ⛔ RADIUS 0, AND THAT IS THE FILE'S RULE, NOT MY TASTE. This stylesheet's own
   header says "No border-radius. Anywhere. A door section has square corners."
   Glass works perfectly well with square corners — it is a material, not a
   shape — so there is nothing to override and no exception to ask for. What
   makes it read as glass here is the translucency, the blur and the lit top
   seam, which is also this file's existing depth language (seams, never
   shadows). ⭐ This is deliberately the hardest-edged of the four.
   ⛔ Base declaration is the fallback and clears 4.5:1 unblurred. */
.band--photo .btn,.band--photo .btn--out{
  border-radius:0;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.34);
  transition:background-color .2s var(--ease),border-color .2s var(--ease);
}
.band--photo .btn{
  --glass-fb:rgba(246,196,0,.96);
  background:rgba(246,196,0,.96);color:var(--accent-ink);
  border:1px solid rgba(255,230,120,.70);
}
@supports ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  .band--photo .btn{background:rgba(246,196,0,.86);
    -webkit-backdrop-filter:blur(14px) saturate(1.3);backdrop-filter:blur(14px) saturate(1.3)}
}
.band--photo .btn:focus-visible,.band--photo .btn--out:focus-visible{
  outline:2px solid #fff;outline-offset:3px}
/* ⛔ `.btn--out`, NOT `.btn--ghost`. This file has never had a ghost class and
   introducing one would change what check-demo-polish.mjs measures. Restated
   AFTER the `.btn` rules above, which is the same ordering hazard by another
   name: unlayered CSS, equal specificity, source order decides. */
.band--photo .btn--out{
  --glass-fb:rgba(16,19,23,.88);
  background:rgba(16,19,23,.88);color:#fff;
  border:1px solid rgba(255,255,255,.40);
}
@supports ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  .band--photo .btn--out{background:rgba(16,19,23,.62);
    -webkit-backdrop-filter:blur(14px) saturate(1.15);backdrop-filter:blur(14px) saturate(1.15)}
}

/* 🔴 THE SECONDARY WAS NEVER GETTING ITS GLASS. tools/_glass.mjs read alpha
   0.42 where the rule says 0.62: `.band--photo a.btn--out` (0,2,1) from the
   hero pass further up beats `.band--photo .btn--out` (0,2,0). Same lesson as
   the other three, different route — an `a.` qualifier I wrote myself.
   ⛔ `.btn--out`, never `.btn--ghost`: this file has no ghost class and adding
   one changes what check-demo-polish.mjs measures. */
.band--photo a.btn--out{
  background:rgba(16,19,23,.88);color:#fff;
  border:1px solid rgba(255,255,255,.40);border-radius:0;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.34);
  -webkit-backdrop-filter:none;backdrop-filter:none;
  --glass-fb:rgba(16,19,23,.88);
}
@supports ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  .band--photo a.btn--out{background:rgba(16,19,23,.62);
    -webkit-backdrop-filter:blur(14px) saturate(1.15);backdrop-filter:blur(14px) saturate(1.15)}
}
.band--photo a.btn--out:hover{background:rgba(16,19,23,.80);border-color:#fff}

/* ═══ 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. */
.band--photo__scrim{
  background:linear-gradient(0deg,
    rgba(12,14,17,.92) 0px,
    rgba(12,14,17,.88) 170px,
    rgba(12,14,17,.66) 330px,
    rgba(12,14,17,.30) 460px,
    rgba(12,14,17,0)   600px);
  -webkit-mask-image:linear-gradient(90deg,#000 0%,#000 46%,
    rgba(0,0,0,.58) 62%, rgba(0,0,0,.18) 76%, transparent 86%);
  mask-image:linear-gradient(90deg,#000 0%,#000 46%,
    rgba(0,0,0,.58) 62%, rgba(0,0,0,.18) 76%, transparent 86%);
}
/* ⛔ 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){
  .band--photo__scrim{
    background:linear-gradient(0deg,
      rgba(12,14,17,.93) 0px,
      rgba(12,14,17,.90) 220px,
      rgba(12,14,17,.70) 400px,
      rgba(12,14,17,.32) 530px,
      rgba(12,14,17,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){
  .band--photo{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){
  .band--photo__scrim{
    background:linear-gradient(0deg,
      rgba(12,14,17,.93) 0px,
      rgba(12,14,17,.88) 140px,
      rgba(12,14,17,.62) 260px,
      rgba(12,14,17,.26) 350px,
      rgba(12,14,17,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){
  .band--photo{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}
  .band--photo__scrim{
    background:linear-gradient(0deg,
      rgba(12,14,17,.93) 0px,
      rgba(12,14,17,.90) 200px,
      rgba(12,14,17,.74) 330px,
      rgba(12,14,17,.40) 440px,
      rgba(12,14,17,.12) 510px,
      rgba(12,14,17,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){
  .chat{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){
  .chat{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. */
.chat{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. */
.chat{bottom:1rem}
.band--photo{padding-bottom:4.6rem}
.band--photo .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){
  .band--photo{padding-bottom:7.6rem}
  .band--photo .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){
  .band--photo{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){
  .band--photo .lanaihero__in,.band--photo>.wrap{padding-top:1rem}
  .band--photo h1,.band--photo .h1{line-height:.98}
  .band--photo .btnrow{margin-top:.8rem}
  .band--photo{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){
  .band--photo__scrim{
    background:linear-gradient(0deg,
      rgba(12,14,17,.93) 0px,
      rgba(12,14,17,.90) 180px,
      rgba(12,14,17,.76) 300px,
      rgba(12,14,17,.46) 400px,
      rgba(12,14,17,.16) 470px,
      rgba(12,14,17,0)   530px);
  }
}
