/* ============================================================
   STONE / PEARL + ONE COPPER LIGHT

   Copper is not a colour in this palette, it is EMISSION. It may
   appear only where a star is depicted or on a surface that star
   is visibly lighting — the hero corona, the beam it throws, the
   rim along the lit roof plane, and the bloom under the primary
   plate. Never a fill, never text, never a stroke you could trace,
   never in persistent chrome.

   The primary action is a PEARL METAL PLATE. With no saturated
   fill, primacy is carried by VALUE INVERSION: it is the only
   dark-type-on-light-ground object on the site. That is hue
   independent, so it survives colour-blindness and greyscale,
   which a green fill did not.

   Two rules keep it honest:
     CO-VISIBILITY — if two lit things can share a viewport and are
     not one composed gesture, one of them loses its light.
     GRADIENT, NOT EXTENT — a glow is measured by its falloff. If
     you can see where it ends, it has become a fill. Lifting the
     ground to stone dropped the just-noticeable alpha ~41%, so
     soft tails that once vanished into black now read as a wash:
     copper light must be brighter AND tighter, not wider.
   ============================================================ */
:root{
  /* ground + raised surfaces — a cool STONE grey, not black.
     Ramp steps are L* 4.6 / 7.6 / 12.6 / 17.9 / 27.4, so surfaces separate
     by VALUE and no longer need a hairline to prove they are raised. */
  --stone-well:#0E1012;    /* deepest wells: form interiors, modal scrims */
  --stone:#151719;         /* THE PAGE GROUND — pearl reads 16.2:1 here */
  --stone-1:#1F2123;       /* raised 1 — cards, panels, footer */
  --stone-2:#2A2C2E;       /* raised 2 — hovers, chips, tabs */
  --stone-line:#3F4143;    /* solid drawn linework, decorative borders */

  /* kept as aliases so nothing referencing the old names breaks mid-migration */
  --carbon:var(--stone); --carbon-2:var(--stone-well);
  --charcoal:var(--stone-1); --charcoal-2:var(--stone-2);
  --graphite:var(--stone-line);

  /* type ramp */
  --pearl:#F1F3F5;         /* primary text, focus rings, the star  17.9:1 */
  --silver:#C6CBD1;        /* secondary — .lede, list copy, prose body */
  --muted:#A0A2A4;   /* tertiary text — 5.47:1 on the lightest surface */         /* tertiary — breadcrumbs, fine print  6.5:1 */
  --steel:#919395;   /* drafting + mono labels — 4.55:1, the floor */         /* the drafting/mono label colour  5.5:1 */

  /* the metal ramp — structure, specs, icons at rest, edges */
  --metal-lt:#EEF1F4;
  --metal-mid:#BBBDBF;
  --metal-dk:#7B7D7F;
  --metal-dim:rgba(198,203,209,.30);

  /* rules and edges */
  --hair:rgba(236,240,244,.10);
  --hair-2:rgba(236,240,244,.18);
  --edge-ui:rgba(236,240,244,.38);   /* form-control rest border — 3.2:1, a
                                        UI boundary, so it must clear 3:1.
                                        The old blue hairline was ~2.5:1. */
  --edge:rgba(255,255,255,.30);      /* top-edge light on raised surfaces */

  /* THE ACCENT — copper, and it is EMISSION, not paint. Hot metal reads as
     light because it runs bright and desaturates toward its core, so the
     ramp is a heat ramp: --copper-hot at the centre of a source, --copper
     as the body of the light, --copper-deep only where light cannot go.
     Copper also happens to be a roof this company sells. */
  --copper:#E08A4F;        /* the emission body — 6.78:1 on stone */
  --copper-hot:#FFC08A;    /* the specular core of a source — 11.26:1 */
  --copper-deep:#9A5A22;   /* the ONE copper legal on the pearl plate: 4.98:1
                              there, where --copper is 2.4:1 and would vanish */
  --plate-ink:#101216;     /* type on the pearl plate — 16.9:1. Lives here,
                              NOT in the accent block: it was deleted with the
                              green tokens and the label silently inherited
                              pearl, giving white-on-white at ~1.05:1. An
                              undefined var makes the whole declaration invalid
                              at computed-value time and it falls back to
                              inherit — no error, no warning. */
  --copper-ink:#1A0E04;    /* on-copper text, for any filled copper surface */
  --copper-glow:rgba(224,138,79,.92);
  /* The lit hairline. A rule that starts hot and falls to nothing is the one
     shape copper is allowed to take on a line: it reads as light spilling
     from the label it trails, with a falloff, rather than a stroke you could
     trace. The hot point is 1px of --copper-hot, so the source is a POINT and
     the line is only its throw. Used by .sheet i and .area-group h3 i, which
     are the same drafting idiom — a caption with a rule running off it. */
  --rule-lit:linear-gradient(90deg,var(--copper-hot) 0,var(--copper) 4%,rgba(224,138,79,.42) 26%,rgba(224,138,79,.18) 58%,rgba(224,138,79,0) 100%);

  /* motion — one curve for state changes, a three-step duration scale.
     Choreographed animations keep their own timing; this is for hover,
     focus, colour and border changes, which should all feel like the same
     material responding. */
  --ease:cubic-bezier(.4,0,.2,1);
  --dur-1:.16s;   /* colour, border, opacity — should feel immediate */
  --dur-2:.26s;   /* transform and shadow on hover */
  --dur-3:.42s;   /* larger moves: drawers, panels */

  --maxw:1200px;
  --pad:clamp(20px,5vw,64px);
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--carbon);color:var(--pearl);
  font-family:"Barlow",system-ui,-apple-system,Segoe UI,sans-serif;
  font-weight:300;font-size:clamp(16px,1.05vw,17.5px);line-height:1.65;
  overflow-x:hidden;
}
img,svg{display:block;max-width:100%}
a{color:inherit}
h1,h2,h3,h4{font-family:"Chakra Petch",system-ui,sans-serif;font-weight:700;margin:0;line-height:1.05;letter-spacing:.01em}
p{margin:0}
::selection{background:var(--copper);color:var(--copper-ink)}

/* ---------- drafting utilities ---------- */
.mono{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--steel)}
.wrap{width:100%;max-width:var(--maxw);margin:0 auto;padding-left:var(--pad);padding-right:var(--pad)}
.band{position:relative;padding:clamp(72px,9vw,132px) 0}
.band + .band{border-top:1px solid var(--hair)}

.sheet{display:flex;align-items:center;gap:14px;margin-bottom:22px}
.sheet span{white-space:nowrap}
.sheet i{flex:1;height:1px;background:var(--rule-lit);display:block}
.h2{font-size:clamp(30px,4.6vw,54px);text-transform:uppercase;letter-spacing:.005em}
.lede{color:var(--silver);max-width:60ch;margin-top:18px;font-size:clamp(16px,1.5vw,19px)}
.gold{color:var(--metal-lt)}

/* blueprint field */
.grid-bg{position:fixed;inset:0;pointer-events:none;z-index:0;
  background-image:linear-gradient(var(--hair) 1px,transparent 1px),linear-gradient(90deg,var(--hair) 1px,transparent 1px);
  background-size:64px 64px;opacity:.5;
  -webkit-mask-image:radial-gradient(120% 80% at 50% 0%,#000 20%,transparent 78%);
          mask-image:radial-gradient(120% 80% at 50% 0%,#000 20%,transparent 78%);
}
.stars{position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
   radial-gradient(1.4px 1.4px at 12% 18%,rgba(255,255,255,.9),transparent),
   radial-gradient(1.2px 1.2px at 68% 9%,rgba(255,255,255,.75),transparent),
   radial-gradient(1.6px 1.6px at 84% 32%,rgba(236,240,244,.8),transparent),
   radial-gradient(1.1px 1.1px at 32% 42%,rgba(255,255,255,.55),transparent),
   radial-gradient(1.3px 1.3px at 52% 26%,rgba(255,255,255,.6),transparent),
   radial-gradient(1.1px 1.1px at 91% 12%,rgba(255,255,255,.5),transparent),
   radial-gradient(1.5px 1.5px at 22% 6%,rgba(255,255,255,.7),transparent),
   radial-gradient(900px 520px at 62% -8%,rgba(150,154,158,.16),transparent 70%),
   radial-gradient(700px 460px at 18% 4%,rgba(170,174,178,.10),transparent 70%);
}
main,header,footer{position:relative;z-index:1}

/* ---------- header ---------- */
header{position:sticky;top:0;z-index:60;backdrop-filter:blur(12px);
  background:linear-gradient(180deg,rgba(21,23,25,.92),rgba(21,23,25,.62));
  border-bottom:1px solid var(--hair)}
/* A tile face behind the nav, at 7%. It has to be a layer of its own rather
   than another background on the header, because a background image would go
   in opaque and cost the bar its backdrop-filter — the header is translucent
   on purpose, and the page blurring through it is most of why it reads as
   glass rather than a slab.
   z-index:-1 is doing real work: header is position:sticky with z-index:60,
   so it opens a stacking context and a negative-z child cannot escape it. It
   lands above the header's own gradient and below every bit of content, which
   is the one slot that puts texture behind text without a positioned overlay
   covering the text. Anything z-index:0 here would paint OVER the links.
   At 7% over a mean face of 60/255 the nav's worst background shifts by about
   two values, so --pearl and --muted keep the ratios they were measured at. */
header::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:image-set(url("img/tex-header.webp") type("image/webp"),url("img/tex-header.jpg") type("image/jpeg")) center/cover no-repeat;
  opacity:.07}
.nav{display:flex;align-items:center;justify-content:space-between;gap:20px;height:72px}
/* Scroll progress. It sits ON the header's bottom hairline rather than under
   it, so at zero scroll there is nothing new on the page — the rule was
   already there. Same reasoning as the hero tab fill: the bar's leading edge
   is where light would collect, and it moves, so the copper is a specular
   event rather than a painted strip. Everything behind the tip is metal.
   --p is set by site.js; with no JS the bar simply stays at zero width. */
/* It is a SCALE BAR, not a loading bar. The track carries a ruler — minor
   graduations every 24px, a major every 120px, hanging from the header's own
   hairline the way ticks hang from a datum line. Unlit they sit at .05/.09
   alpha, which is under the threshold where you would call them a pattern:
   you read the scale ahead of you without the header acquiring furniture.
   The fill relights the same graduations as it passes them, so the notches
   accumulate with the distance travelled.

   Both layers are anchored at left:0 of the same box and their tick
   background-sizes match, so the phases align exactly — the lit ticks land on
   the unlit ones rather than beating against them. The rule itself is 1.5px;
   the ticks hang below it, so the bar reads finer than the 2px line it
   replaces while carrying more information.

   --tick / --tick-major are here so the graduation can be re-spaced in one
   place; changing either needs BOTH the track and the fill, hence the var. */
.progress{position:absolute;left:0;right:0;bottom:-1px;height:5px;pointer-events:none;overflow:hidden;
  --tick:24px; --tick-major:120px}
.progress::before{content:"";position:absolute;inset:0;
  background:
    repeating-linear-gradient(90deg,rgba(236,240,244,.09) 0 1px,transparent 1px var(--tick-major)),
    repeating-linear-gradient(90deg,rgba(236,240,244,.05) 0 1px,transparent 1px var(--tick));
  background-size:var(--tick-major) 5px,var(--tick) 3px;
  background-repeat:repeat-x;background-position:0 0}
.progress i{display:block;position:relative;height:100%;width:calc(var(--p,0) * 100%);
  background:
    /* the copper is a fixed 84px at the tip, never a share of the width —
       tied to a percentage it would swallow the whole bar near the top of
       the page and read as an orange strip rather than a hot edge. */
    linear-gradient(90deg,rgba(224,138,79,0),var(--copper) 58%,var(--copper-hot)),
    repeating-linear-gradient(90deg,rgba(238,241,244,.52) 0 1px,transparent 1px var(--tick-major)),
    repeating-linear-gradient(90deg,rgba(238,241,244,.26) 0 1px,transparent 1px var(--tick)),
    /* the tail fades over a FIXED 72px, for the same reason the tip is fixed:
       as a share of the width it made the whole rule dark near the top of the
       page, which is exactly when the bar most needs to be legible. It sits
       above the rule and below the graduations, so it dims the line without
       taking the notches with it. */
    linear-gradient(90deg,var(--stone),rgba(21,23,25,0)),
    linear-gradient(90deg,var(--metal-mid),var(--metal-lt));
  background-size:84px 1.5px,var(--tick-major) 5px,var(--tick) 3px,72px 1.5px,100% 1.5px;
  background-repeat:no-repeat,repeat-x,repeat-x,no-repeat,no-repeat;
  background-position:right top,0 0,0 0,left top,0 0}
/* the bloom belongs to the tip, so it anchors to the fill, not the track */
.progress i::after{content:"";position:absolute;top:0;right:0;width:26px;height:9px;
  transform:translate(36%,-32%);border-radius:50%;
  background:radial-gradient(closest-side,rgba(255,192,138,.95),rgba(224,138,79,.42) 46%,rgba(224,138,79,0) 100%)}
/* At the very top there is no light yet — nothing has been travelled. */
.progress[data-at-top] i{opacity:0}
/* A phone header is 44px of a 375px screen; a 24px graduation there is noise,
   so the scale opens up and the bar keeps only its major marks. */
@media (max-width:760px){.progress{--tick:34px;--tick-major:102px;height:4px}}
.brand{display:flex;align-items:center;gap:12px;text-decoration:none}
.brand svg{width:34px;height:34px;flex:none}
.brand b{font-family:"Chakra Petch";font-weight:700;font-size:19px;letter-spacing:.14em;
  background:linear-gradient(180deg,#fff,#ABADAF 60%,#EDEFF1);-webkit-background-clip:text;background-clip:text;color:transparent}
.brand em{font-style:normal;font-family:"IBM Plex Mono";font-size:9.5px;letter-spacing:.34em;color:var(--metal-mid);display:block;margin-top:1px}
.menu{display:flex;align-items:center;gap:30px}
.menu a{font-size:14px;letter-spacing:.06em;text-transform:uppercase;text-decoration:none;color:var(--silver);font-weight:500}
.menu a:hover{color:#fff}
.nav .btn{padding:11px 20px;font-size:13px}
/* The burger carries a copper bloom. Note this is the one place on the site
   where copper sits in PERSISTENT CHROME, which the doctrine otherwise
   forbids — it is here because it was asked for, and it is kept honest the
   only way that idiom can be: the glow is a falloff, not a fill. The rim is
   a 1px 34%-alpha ring (below the weight of a drawn border), the bloom is a
   22px spread with no visible end, and the box itself stays unpainted. It
   brightens on hover and while the drawer is open, so the light still tracks
   attention rather than sitting inert. */
.burger{display:none;background:none;border:1px solid rgba(224,138,79,.20);border-radius:2px;
  width:44px;height:44px;color:var(--pearl);align-items:center;justify-content:center;cursor:pointer;
  box-shadow:0 0 18px -10px rgba(224,138,79,.55),inset 0 0 10px -9px rgba(255,192,138,.7);
  transition:box-shadow var(--dur-2) var(--ease),border-color var(--dur-2) var(--ease)}
/* Held back at rest so it does not compete with the wordmark opposite it, and
   the hover carries most of the range — the light should answer attention
   rather than announce itself. */
.burger:hover,.burger[aria-expanded="true"]{
  border-color:rgba(224,138,79,.44);
  box-shadow:0 0 26px -8px rgba(224,138,79,.75),inset 0 0 12px -7px rgba(255,192,138,.85)}
.burger span{width:18px;height:1.5px;background:currentColor;display:block;position:relative}
.burger span::before,.burger span::after{content:"";position:absolute;left:0;width:18px;height:1.5px;background:currentColor}
.burger span::before{top:-6px}.burger span::after{top:6px}
.drawer{display:none}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font-family:"Chakra Petch";font-weight:600;font-size:14px;letter-spacing:.1em;text-transform:uppercase;
  padding:15px 26px;border-radius:2px;text-decoration:none;cursor:pointer;border:1px solid transparent;
  transition:transform var(--dur-1) var(--ease),box-shadow var(--dur-2) var(--ease),background var(--dur-2) var(--ease),color var(--dur-1) var(--ease)}
.btn-gold{
  background:
    linear-gradient(180deg,rgba(255,255,255,.9),rgba(255,255,255,0) 42%),
    linear-gradient(180deg,#FCFDFD 0%,#EFF1F2 38%,#DFE2E4 60%,#F4F6F7 100%);
  color:var(--plate-ink);
  border:1px solid rgba(255,255,255,.72);
  border-bottom-color:rgba(120,126,132,.55);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.95),
    inset 0 -1px 0 rgba(0,0,0,.10),
    0 0 0 1px rgba(224,138,79,.34),
    0 10px 22px -12px var(--copper-glow),
    0 2px 6px -2px rgba(0,0,0,.55);
  position:relative;overflow:hidden;isolation:isolate}
/* A highlight crossing the plate, the way light crosses brushed metal when it
   tips toward you. It only exists during the hover, so it never becomes
   chrome, and its edges are a gradient rather than a shape. */
.btn-gold::after{content:"";position:absolute;inset:-1px;z-index:-1;pointer-events:none;
  background:linear-gradient(105deg,rgba(255,192,138,0) 34%,rgba(255,192,138,.55) 47%,rgba(224,138,79,.30) 53%,rgba(224,138,79,0) 66%);
  transform:translateX(-120%);opacity:0}
.btn-gold:hover::after{animation:plate-sweep .72s var(--ease) forwards}
@keyframes plate-sweep{from{transform:translateX(-120%);opacity:0}
  18%{opacity:1}to{transform:translateX(120%);opacity:0}}
@media (prefers-reduced-motion:reduce){
  .btn-gold:hover::after{animation:none;opacity:0}}
.btn-gold:hover{
  background:
    linear-gradient(180deg,rgba(255,255,255,.96),rgba(255,255,255,0) 44%),
    linear-gradient(180deg,#FFFFFF 0%,#F6F8F8 38%,#E7EAEC 60%,#FFFFFF 100%);
  box-shadow:
    inset 0 1px 0 #fff,
    inset 0 -1px 0 rgba(0,0,0,.08),
    0 0 0 1px rgba(224,138,79,.62),
    0 14px 30px -12px var(--copper-glow),
    0 3px 8px -2px rgba(0,0,0,.6);
  transform:translateY(-1px)}
.btn-gold:active{
  transform:translateY(0);
  box-shadow:
    inset 0 2px 4px rgba(0,0,0,.16),
    0 0 0 1px rgba(224,138,79,.5),
    0 4px 10px -8px var(--copper-glow)}
.btn-ghost{border-color:var(--hair-2);color:var(--pearl);background:rgba(28,30,32,.35)}
.btn-ghost:hover{border-color:var(--metal-mid);color:var(--metal-lt);transform:translateY(-2px)}
:focus-visible{outline:2px solid var(--pearl);outline-offset:3px}

/* ---------- hero ---------- */
.hero{position:relative;padding:clamp(48px,7vw,96px) 0 clamp(56px,7vw,104px);overflow:hidden}
.hero-grid{display:grid;grid-template-columns:1.02fr .98fr;gap:clamp(28px,4vw,64px);align-items:center}
.eyebrow{display:inline-flex;align-items:center;gap:10px;border:1px solid var(--hair-2);border-radius:2px;padding:7px 12px}
.eyebrow b{color:var(--metal-mid);font-weight:500}
h1{font-size:clamp(38px,6.4vw,76px);text-transform:uppercase;margin:22px 0 0;letter-spacing:-.01em}
h1 .sil{background:linear-gradient(180deg,#FFFFFF 6%,#B9BBBD 48%,#7B7D7F 62%,#F2F7FF 96%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  text-shadow:0 1px 0 rgba(255,255,255,.06)}
h1 .au,.slide-h .au{background:linear-gradient(180deg,#EEF1F4,var(--metal-mid) 55%,#7B7D7F);-webkit-background-clip:text;background-clip:text;color:transparent}
.hero p.lede{margin-top:20px}
.cta-row{display:flex;flex-wrap:wrap;gap:14px;margin-top:32px}
.hero-facts{display:flex;flex-wrap:wrap;gap:8px 26px;margin-top:30px;padding-top:22px;border-top:1px solid var(--hair)}
.hero-facts div{display:flex;align-items:center;gap:8px}
.hero-facts svg{width:15px;height:15px;color:var(--metal-mid)}
.hero-facts span{font-size:13.5px;color:#ABADAF;letter-spacing:.02em}

/* drawing */
.draw{position:relative}
.draw svg{width:100%;height:auto;overflow:visible}
.bp{fill:none;stroke:#919395;stroke-width:1.1;opacity:.65}
.bp-2{fill:none;stroke:#7B7D7F;stroke-width:1;opacity:.45}
.au-line{fill:none;stroke:url(#g-gold);stroke-width:13;stroke-linejoin:miter}
.tick{stroke:var(--steel);stroke-width:1;opacity:.6}
.note{font-family:"IBM Plex Mono";font-size:9px;letter-spacing:.14em;fill:var(--steel);text-transform:uppercase}
.star-core{fill:#F1F3F5}

/* --- light thrown by the star, and the drafting furniture it falls on --- */
.beam{mix-blend-mode:plus-lighter;pointer-events:none}
.roof-rim{fill:none;stroke:var(--copper);stroke-width:1.6;stroke-linecap:round;
  filter:drop-shadow(0 0 5px rgba(224,138,79,.75))}
.hatch{opacity:.5}
.detail,.secmark,.scalebar{opacity:0}

@media (prefers-reduced-motion:no-preference){
  .dash{stroke-dasharray:var(--len,900);stroke-dashoffset:var(--len,900);animation:draw 1.5s cubic-bezier(.4,0,.2,1) forwards}
  .d2{animation-delay:.25s}.d3{animation-delay:.45s}.d4{animation-delay:.62s}
  .au-line{stroke-dasharray:640;stroke-dashoffset:640;animation:draw 1.1s cubic-bezier(.4,0,.2,1) .55s forwards}
  .starburst{opacity:0;transform-origin:300px 58px;animation:pop .9s cubic-bezier(.2,1.2,.3,1) 1.35s forwards}
  .note,.tick{opacity:0;animation:fade .8s ease 1.5s forwards}
  /* the rim is drawn on last — the light arrives after the structure does */
  .roof-rim{stroke-dasharray:276;stroke-dashoffset:276;
    animation:draw 1s cubic-bezier(.4,0,.2,1) 1.5s forwards}
  .beam{opacity:0;animation:fade 1.4s ease 1.75s forwards}
  .hatch{opacity:0;animation:fade .7s ease 1.1s forwards}
  .detail{animation:fade .7s ease 1.85s forwards}
  .secmark{animation:fade .7s ease 2s forwards}
  .scalebar{animation:fade .7s ease 2.1s forwards}
  @keyframes draw{to{stroke-dashoffset:0}}
  @keyframes pop{0%{opacity:0;transform:scale(.5) rotate(-25deg)}100%{opacity:1;transform:scale(1) rotate(0)}}
  @keyframes fade{to{opacity:.6}}
  .reveal{opacity:0;transform:translateY(22px);transition:opacity var(--dur-3) var(--ease),transform var(--dur-3) var(--ease)}
  .reveal.in{opacity:1;transform:none}
}
/* The star: white-hot core, COPPER corona. The white stage is tighter than
   it was for green — copper sits far closer to white in both hue and
   luminance, so a wide white core simply swallowed the corona and the whole
   thing read as a warm white blob. Narrow the white, and step the copper
   down through a heat ramp so the falloff itself carries the colour. Three drop-shadow stages is the
   ceiling — each blurs the result of the previous one, so a fourth softens
   the core it is meant to make sharp. The outer stage is capped at 46px
   because the lifted ground dropped the just-noticeable alpha by ~41%: a
   wide soft tail that used to vanish into black now renders as a green wash.
   Green light is measured by its GRADIENT, not its extent — if you can see
   where the glow ends, it has become a fill. */
.glow{filter:drop-shadow(0 0 4px rgba(255,255,255,.95))
             drop-shadow(0 0 13px rgba(255,196,140,.95))
             drop-shadow(0 0 30px rgba(224,138,79,.95))
             drop-shadow(0 0 54px rgba(200,110,55,.45))}

/* ---------- credential strip ---------- */
.strip{border-top:1px solid var(--hair);border-bottom:1px solid var(--hair);background:rgba(31,33,35,.45)}
.strip .wrap{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;padding-top:0;padding-bottom:0}
/* `.strip div` used to be the selector here, which also matched the .wrap
   itself — .wrap IS a div inside .strip — so its padding shorthand beat
   `.wrap{padding-left:var(--pad)}` at (0,1,1) vs (0,1,0) and zeroed the
   band's side padding on every page at every width. On desktop the centring
   margin hid it; on a phone the text ran to the screen edge. Scoped to the
   wrap's own children now. */
.strip .wrap>div{padding:26px 4px 26px 0}
.strip b{display:block;font-family:"Chakra Petch";font-size:clamp(22px,2.6vw,30px);color:#fff;line-height:1}
.strip .mono{margin-top:9px;display:block;color:var(--muted)}

/* ---------- cards ---------- */
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:44px}
.card{border:1px solid var(--hair);background:linear-gradient(180deg,rgba(37,40,43,.85),rgba(31,33,35,.85));
  padding:30px 26px 32px;border-radius:2px;position:relative;transition:border-color var(--dur-2) var(--ease),transform var(--dur-2) var(--ease),background var(--dur-2) var(--ease)}
.card:hover{border-color:var(--metal-dim);transform:translateY(-4px);background:linear-gradient(180deg,rgba(42,44,46,.6),rgba(14,16,18,.55))}
.card h3{font-size:20px;text-transform:uppercase;margin:18px 0 10px;letter-spacing:.03em}
.card p{color:var(--silver);font-size:15.5px}
.card .ic{width:38px;height:38px;color:var(--metal-mid)}
.card .tag{position:absolute;top:14px;right:16px}

/* ---------- process ---------- */
.steps{margin-top:48px;display:grid;grid-template-columns:repeat(5,1fr);gap:0}
/* A three-up variant, as a CLASS. It shipped as an inline style, which beat
   every breakpoint below and left /storm-damage 89px wider than a 375px
   phone — the second inline-style override found in this file. */
.steps-3{grid-template-columns:repeat(3,1fr);row-gap:44px}
.step{padding:0 22px 0 0;position:relative}
.step::before{content:"";position:absolute;left:0;right:0;top:26px;height:1px;background:var(--hair-2)}
.step .dot{width:11px;height:11px;border-radius:50%;background:var(--carbon);border:1px solid var(--metal-mid);position:relative;margin:21px 0 22px;box-shadow:0 0 0 4px rgba(21,23,25,1),0 0 16px rgba(236,240,244,.6)}
.step h3{font-size:17px;text-transform:uppercase;margin-bottom:8px}
.step p{font-size:14.5px;color:#A1A3A5}
.step .mono{color:var(--metal-mid);opacity:.85}

/* ---------- storm ---------- */
.split{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,72px);align-items:center}
.checks{list-style:none;padding:0;margin:30px 0 0;display:grid;gap:14px}
.checks li{display:flex;gap:13px;align-items:flex-start;font-size:15.5px;color:#B9BBBD}
.checks svg{width:17px;height:17px;color:var(--metal-mid);flex:none;margin-top:4px}
.panel{border:1px solid var(--hair);background:rgba(31,33,35,.5);padding:clamp(24px,3vw,38px);border-radius:2px}
.panel .rule{height:1px;background:var(--hair);margin:22px 0}

/* ---------- materials ---------- */
.mats{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:44px}
.mat{border:1px solid var(--hair);border-radius:2px;overflow:hidden;background:rgba(31,33,35,.6)}
.mat .top{height:132px;position:relative;border-bottom:1px solid var(--hair);overflow:hidden}
.mat .top svg{position:absolute;inset:0;width:100%;height:100%}
.mat .body{padding:22px 24px 26px}
.mat h3{font-size:18px;text-transform:uppercase;margin-bottom:8px}
.mat p{font-size:14.5px;color:#A0A2A4}
.mat dl{display:flex;gap:22px;margin:16px 0 0;flex-wrap:wrap}
.mat dt{font-family:"IBM Plex Mono";font-size:9.5px;letter-spacing:.16em;color:var(--muted);text-transform:uppercase}
.mat dd{margin:2px 0 0;font-family:"Chakra Petch";font-size:15px;color:var(--metal-lt)}

/* ---------- reviews ---------- */
.quotes{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:44px}
.quote{border:1px solid var(--hair);padding:28px 26px;border-radius:2px;background:rgba(31,33,35,.5)}
.quote blockquote{margin:0;font-size:16.5px;color:#DCDEE0;font-weight:400}
.quote footer{margin-top:20px;padding-top:16px;border-top:1px solid var(--hair);display:flex;justify-content:space-between;gap:12px;align-items:baseline}
.quote cite{font-style:normal;font-family:"Chakra Petch";font-size:14px;letter-spacing:.06em;text-transform:uppercase}
.stars-row{color:var(--metal-mid);letter-spacing:2px;font-size:13px}

/* ---------- area ---------- */
.area{display:flex;flex-wrap:wrap;gap:10px;margin-top:34px}
.area span{border:1px solid var(--hair);padding:9px 15px;border-radius:2px;font-size:14px;color:#BFC1C3;letter-spacing:.02em}
.area span:hover{border-color:var(--metal-dim);color:var(--metal-lt)}

/* ---------- faq ---------- */
.faq{margin-top:40px;border-top:1px solid var(--hair)}
details{border-bottom:1px solid var(--hair)}
summary{cursor:pointer;list-style:none;padding:24px 46px 24px 0;position:relative;
  font-family:"Chakra Petch";font-weight:600;font-size:clamp(16px,1.9vw,20px);letter-spacing:.02em}
summary::-webkit-details-marker{display:none}
summary::after{content:"";position:absolute;right:6px;top:50%;width:13px;height:13px;margin-top:-7px;
  border-right:1.5px solid var(--metal-mid);border-bottom:1.5px solid var(--metal-mid);transform:rotate(45deg);transition:transform var(--dur-2) var(--ease)}
details[open] summary::after{transform:rotate(-135deg)}
details p{padding:0 0 26px;color:var(--silver);max-width:78ch}

/* ---------- contact ---------- */
#quote{background:linear-gradient(180deg,rgba(31,33,35,.55),rgba(21,23,25,0))}
.form{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:30px}
.field{display:flex;flex-direction:column;gap:8px}
.field.full{grid-column:1 / -1}
label{font-family:"IBM Plex Mono";font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--steel)}
input,select,textarea{font-family:"Barlow";font-size:16px;color:var(--pearl);background:var(--stone-well);
  border:1px solid var(--edge-ui);border-radius:2px;padding:14px 15px;width:100%;transition:border-color var(--dur-1) var(--ease)}
input:hover,select:hover,textarea:hover{border-color:var(--metal-mid)}
/* A form control's rest border is a UI boundary and needs 3:1 — the hairline
   it used was ~1.7:1, which is what --edge-ui was added for. And the old rule
   killed the outline outright, which outranks :focus-visible and left keyboard
   users with no ring on any field. */
input:focus-visible,select:focus-visible,textarea:focus-visible{
  border-color:var(--pearl);outline:2px solid var(--pearl);outline-offset:2px}
input:focus,select:focus,textarea:focus{border-color:var(--metal-mid)}
::placeholder{color:var(--muted);opacity:1}
textarea{resize:vertical;min-height:110px}
select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--metal-mid) 50%),linear-gradient(135deg,var(--metal-mid) 50%,transparent 50%);
  background-position:calc(100% - 20px) 22px,calc(100% - 14px) 22px;background-size:6px 6px;background-repeat:no-repeat}
.form-note{grid-column:1/-1;display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin-top:6px}
.form-note .mono{color:var(--muted);letter-spacing:.12em}
.ok{grid-column:1/-1;border:1px solid var(--metal-dim);background:rgba(236,240,244,.09);padding:16px 18px;border-radius:2px;font-size:15px;display:none}
.ok.show{display:block}

/* ---------- footer ---------- */
footer{border-top:1px solid var(--hair);padding:clamp(48px,6vw,80px) 0 40px;background:linear-gradient(180deg,rgba(14,16,18,0),rgba(31,33,35,.5))}
.f-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:36px}
.f-grid h4{font-family:"IBM Plex Mono";font-weight:500;font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--steel);margin-bottom:16px}
.f-grid ul{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.f-grid a{text-decoration:none;color:var(--silver);font-size:15px}
.f-grid a:hover{color:var(--metal-lt)}
.f-bot{margin-top:48px;padding-top:24px;border-top:1px solid var(--hair);display:flex;flex-wrap:wrap;gap:12px;justify-content:space-between}
.f-bot .mono{color:#838587}

/* ---------- responsive ---------- */
@media (max-width:1024px){
  .hero-grid{grid-template-columns:1fr;gap:12px}
  /* justify-self, NOT auto margins: auto inline margins on a grid item
     disable stretch, .draw shrink-to-fits, and the SVG falls back to the
     300px default replaced-element width — the drawing rendered at half
     size on every viewport at or below this breakpoint. */
  .draw{order:-1;width:min(560px,100%);justify-self:center;margin:0 0 8px}
  .cards,.mats,.quotes{grid-template-columns:repeat(2,1fr)}
  .steps{grid-template-columns:repeat(3,1fr);gap:34px 24px}
  .steps-3{grid-template-columns:repeat(2,1fr);gap:34px 24px}
  .step::before{display:none}
  .step .dot{margin:0 0 16px}
  .f-grid{grid-template-columns:1fr 1fr;gap:32px}
  .split{grid-template-columns:1fr;gap:38px}
}
/* Drawer styling is keyed to 1000px — the same breakpoint that reveals the
   burger. When these two disagreed, 761-1000px got a burger that opened
   nothing at all, because .drawer{display:none} still applied. */
@media (max-width:1000px){
  .drawer{display:block;position:fixed;inset:72px 0 auto;background:rgba(21,23,25,.98);backdrop-filter:blur(14px);
    border-bottom:1px solid var(--hair-2);padding:12px var(--pad) 26px;transform:translateY(-120%);
    transition:transform .35s cubic-bezier(.3,.8,.3,1);z-index:55;
    max-height:calc(100dvh - 72px);overflow-y:auto;-webkit-overflow-scrolling:touch}
  .drawer.open{transform:none}
  .drawer a{display:block;padding:15px 0;border-bottom:1px solid var(--hair);text-decoration:none;
    font-family:"Chakra Petch";font-size:16px;letter-spacing:.08em;text-transform:uppercase}
  /* .drawer a{display:block} is more specific than .btn{display:inline-flex},
     so the CTA lost its flex layout: the star stacked above the label and the
     text went left-aligned with 15px 0 padding. Restore the button. */
  .drawer .btn{display:inline-flex;width:100%;margin-top:20px;
    padding:15px 26px;text-align:center;border-bottom:0}
}
@media (max-width:760px){
  /* On a phone the 2x2 grid fell apart: the four labels are wildly different
     lengths, so each cell set its own height, the rows drifted apart, and the
     whole band read as four bits of text floating in a dark box rather than a
     set of facts. It becomes a SCHEDULE instead — one fact per row, value on
     the left, label right-aligned against it, a hairline between. That is the
     same construction as the spec cards, so the band now belongs to the
     drafting language instead of being a loose stat grid, and it is ~40%
     shorter into the bargain. */
  .strip .wrap{grid-template-columns:1fr;gap:0}
  .strip .wrap>div{display:flex;align-items:baseline;justify-content:space-between;gap:16px;
    padding:15px 0;border-top:1px solid var(--hair)}
  .strip .wrap>div:first-child{border-top:0}
  .strip b{font-size:23px;line-height:1;flex:none}
  /* min-width:0 lets the longest label ("Manufacturer-approved materials",
     223px against ~350px of content) wrap to a second line instead of
     pushing the row wider than the screen. */
  .strip .mono{margin-top:0;text-align:right;font-size:10px;letter-spacing:.1em;
    line-height:1.45;flex:1;min-width:0}
  .cards,.mats,.quotes{grid-template-columns:1fr}
  .steps,.steps-3{grid-template-columns:1fr;gap:28px}
  .row{grid-template-columns:1fr;gap:10px}
  .row .row-num{margin-bottom:2px}
  .form{grid-template-columns:1fr}
  .f-grid{grid-template-columns:1fr}
  .cta-row .btn{flex:1 1 100%}
  .hero{padding-top:26px}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* mobile sticky call bar */
.callbar{display:none}
@media (max-width:1000px){
  .callbar{display:grid;grid-template-columns:1fr 1fr;gap:10px;position:fixed;left:0;right:0;bottom:0;z-index:70;
    padding:10px var(--pad) calc(10px + env(safe-area-inset-bottom));
    /* Denser than it was (.70 at the top). The bar floats over arbitrary page
       content, so at .70 its ground was whatever happened to be behind it —
       which put the copper label's darkest stop at 4.18:1 over a bright
       photo. A fixed overlay carrying type has to bring its own ground. */
    background:linear-gradient(180deg,rgba(21,23,25,.90),rgba(21,23,25,.985));
    backdrop-filter:blur(10px);border-top:1px solid var(--hair-2)}
  /* "Free inspection" plus its star did not fit one line in a half-width
     button at 390px. Tracking, not size, is what buys the room back. */
  .callbar .btn{padding:14px 8px;font-size:12.5px;letter-spacing:.04em;gap:8px;white-space:nowrap}
  body{padding-bottom:78px}
}
/* ================= multi-page additions ================= */
.page-hero{padding:clamp(52px,7vw,96px) 0 clamp(40px,5vw,64px);border-bottom:1px solid var(--hair);position:relative}
.crumb{display:flex;gap:10px;align-items:center;margin-bottom:26px}
.crumb a{text-decoration:none;color:var(--muted)}
.crumb a:hover{color:var(--metal-lt)}
.crumb span{color:var(--muted)}
.page-hero h1{font-size:clamp(34px,5.6vw,64px);max-width:16ch}
.page-hero .lede{margin-top:22px}

.split-wide{display:grid;grid-template-columns:1fr 1.15fr;gap:clamp(28px,5vw,72px);align-items:start}
.sticky-side{position:sticky;top:104px}

/* detail rows */
.rows{margin-top:44px;border-top:1px solid var(--hair)}
.row{display:grid;grid-template-columns:110px 1fr 1fr;gap:clamp(18px,3vw,44px);padding:clamp(28px,3.5vw,44px) 0;border-bottom:1px solid var(--hair);align-items:start}
.row:hover .row-num{color:var(--metal-lt)}
.row-num{font-family:"IBM Plex Mono";font-size:11px;letter-spacing:.18em;color:var(--steel);text-transform:uppercase;transition:color var(--dur-2) var(--ease);padding-top:6px}
.row h3{font-size:clamp(21px,2.6vw,28px);text-transform:uppercase}
.row .sub{margin-top:12px;color:var(--metal-lt);font-family:"IBM Plex Mono";font-size:11px;letter-spacing:.14em;text-transform:uppercase}
.row p{color:var(--silver);font-size:15.5px}
.row ul{list-style:none;padding:0;margin:16px 0 0;display:grid;gap:9px}
.row li{display:flex;gap:11px;font-size:14.5px;color:var(--silver)}
.row li::before{content:"";width:5px;height:5px;margin-top:9px;flex:none;background:var(--metal-mid);transform:rotate(45deg)}

/* spec table */
.spec{width:100%;border-collapse:collapse;margin-top:36px;font-size:15px}
.spec th,.spec td{text-align:left;padding:16px 14px;border-bottom:1px solid var(--hair)}
.spec th{font-family:"IBM Plex Mono";font-weight:500;font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--steel)}
.spec td:first-child{font-family:"Chakra Petch";font-weight:600;color:#fff;letter-spacing:.02em}
.spec tbody tr:hover{background:rgba(36,38,40,.4)}
.spec .num{font-family:"IBM Plex Mono";font-size:13.5px;color:var(--metal-lt);white-space:nowrap}
.table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}

/* callout */
.callout{border:1px solid var(--metal-dim);background:rgba(236,240,244,.07);padding:clamp(22px,3vw,32px);border-radius:2px;margin-top:40px}
.callout h3{font-size:20px;text-transform:uppercase;margin-bottom:10px}
.callout p{color:#C9CBCD;font-size:15.5px;max-width:62ch}

/* team / values */
.duo{display:grid;grid-template-columns:repeat(2,1fr);gap:18px;margin-top:40px}

/* gallery */
.gal{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:44px}
.gal figure{margin:0;border:1px solid var(--hair);border-radius:2px;overflow:hidden;background:rgba(31,33,35,.6)}
.gal .frame{aspect-ratio:4/3;position:relative;background:linear-gradient(160deg,#25282B,#1C1F21)}
.gal .frame svg{position:absolute;inset:0;width:100%;height:100%}
.gal figcaption{padding:16px 18px;border-top:1px solid var(--hair);display:flex;justify-content:space-between;gap:12px;align-items:baseline}
.gal figcaption b{font-family:"Chakra Petch";font-weight:600;font-size:14.5px;letter-spacing:.04em;text-transform:uppercase}

/* mini cta band */
.band-cta{border-top:1px solid var(--hair);background:linear-gradient(180deg,rgba(31,33,35,.6),rgba(21,23,25,0))}
.band-cta .wrap{display:flex;flex-wrap:wrap;gap:24px;justify-content:space-between;align-items:center}
.band-cta h2{font-size:clamp(24px,3.4vw,38px);text-transform:uppercase;max-width:20ch}

/* The current page was distinguished by colour alone, and only 1.44:1 against
   its siblings — below any useful threshold and silent to assistive tech. The
   underline carries it without relying on hue; aria-current carries the
   semantics. */
.menu a.here,.drawer a.here{color:var(--pearl);
  text-decoration:underline;text-underline-offset:6px;
  text-decoration-thickness:2px;text-decoration-color:var(--metal-mid)}

@media (max-width:1024px){
  .split-wide{grid-template-columns:1fr}
  .sticky-side{position:static}
  .gal{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:760px){
  .row{grid-template-columns:1fr;gap:14px;padding:30px 0}
  .row-num{padding-top:0}
  .gal,.duo{grid-template-columns:1fr}
  .band-cta .wrap{flex-direction:column;align-items:flex-start}
}

/* ================= photo slots ================= */
.shot{position:relative;overflow:hidden;background:linear-gradient(160deg,#25282B,#1C1F21);isolation:isolate}
.shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:2;opacity:0;transition:opacity var(--dur-3) var(--ease)}
.shot img.loaded{opacity:1}
picture{display:contents}
.compare picture{display:block}
.shot .art{position:absolute;inset:0;z-index:1}
.shot .art svg{width:100%;height:100%;display:block}
.shot::after{content:"";position:absolute;inset:0;z-index:3;pointer-events:none;
  background:linear-gradient(180deg,rgba(21,23,25,.12),rgba(21,23,25,.55));mix-blend-mode:multiply}
.shot .cap{position:absolute;left:14px;bottom:12px;z-index:4;font-family:"IBM Plex Mono";font-size:9.5px;
  letter-spacing:.16em;text-transform:uppercase;color:rgba(236,238,240,.72);text-shadow:0 1px 6px rgba(0,0,0,.9)}
.card .shot{height:168px;margin:-30px -26px 6px;border-bottom:1px solid var(--hair)}
.mat .top.shot{height:150px}

/* ================= hero slider ================= */
.hero-slider{position:relative;padding:0;overflow:hidden;border-bottom:1px solid var(--hair)}
.slides{position:relative}
.slide{position:absolute;inset:0;opacity:0;visibility:hidden;transition:opacity var(--dur-3) var(--ease),visibility var(--dur-3);
  display:flex;align-items:center;padding:clamp(44px,6vw,84px) 0}
.slide:first-child{position:relative}
.slide.is-active{opacity:1;visibility:visible;z-index:2}
.slide-bg{position:absolute;inset:0;z-index:0;overflow:hidden}
.slide-bg img{width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity var(--dur-3) var(--ease)}
/* Photo slides run at FULL opacity. The old flat 100deg wash darkened the
   whole frame to protect type that only occupies the left third, and it still
   missed: measured against the brightest composited pixel under each role, the
   shipped slider failed at every breakpoint (lede 2.97:1 at 1440, headline
   1.77:1, and worse on tablet). Darkening further is the wrong lever.

   Instead the scrim is a POOL — dense only in the copy's own footprint,
   falling off on a raised-cosine curve to nothing by 98%. A two-stop ramp
   Mach-bands across a surface this large; the eleven stops are a smooth
   falloff, not decoration. The photograph outside the pool is untouched.
   Worst case after this: lede 5.88:1, headline 6.93:1, eyebrow 6.94:1. */
.slide-bg img.loaded{opacity:1}
.slide-bg::after{content:"";position:absolute;inset:0;
  background:
    radial-gradient(56% 84% at 20% 52%,rgba(21,23,25,.88) 0%,rgba(21,23,25,.88) 26%,rgba(21,23,25,.854) 34%,rgba(21,23,25,.777) 42%,rgba(21,23,25,.66) 50%,rgba(21,23,25,.516) 58%,rgba(21,23,25,.364) 66%,rgba(21,23,25,.22) 74%,rgba(21,23,25,.103) 82%,rgba(21,23,25,.026) 90%,rgba(21,23,25,0) 98%),
    linear-gradient(180deg,rgba(21,23,25,.24) 0%,rgba(21,23,25,.05) 20%,rgba(21,23,25,.05) 62%,rgba(21,23,25,.42) 100%)}
.slide .wrap{position:relative;z-index:2;width:100%}
/* Type over photography, not over stone.
   `h1 .au,.slide-h .au` ramps down to #7B7D7F — a grey chosen against the
   stone ground, where it is fine. Over a photograph the bottom third of every
   letter vanished into it: 1.77:1 at 1440, against a 3:1 floor for large text.
   `h1 .sil` never matched .slide-h at all, which is why the first line read
   bright and the second read grey — an accident, not a two-tone.
   The slider gets its own ramp that bottoms out at --silver, and the
   supporting roles come up one step each. That, not a heavier scrim, is what
   let the photographs go to full opacity. */
.slide-h .au{background:linear-gradient(180deg,#FFFFFF 0%,var(--metal-lt) 46%,var(--silver) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent}
.slide-copy .lede{color:var(--pearl)}
.slide-copy .eyebrow.mono{color:var(--metal-mid)}
.slide-copy .hero-facts span{color:var(--metal-mid)}
.slide-copy>*{opacity:0;transform:translateY(26px)}
.slide.is-active .slide-copy>*{animation:rise .8s cubic-bezier(.2,.7,.25,1) forwards}
.slide.is-active .slide-copy>*:nth-child(1){animation-delay:.14s}
.slide.is-active .slide-copy>*:nth-child(2){animation-delay:.24s}
.slide.is-active .slide-copy>*:nth-child(3){animation-delay:.34s}
.slide.is-active .slide-copy>*:nth-child(4){animation-delay:.44s}
.slide.is-active .slide-copy>*:nth-child(5){animation-delay:.54s}
@keyframes rise{to{opacity:1;transform:none}}
.slide.is-active .slide-bg img.loaded{animation:kenburns 13s ease-out forwards}
@keyframes kenburns{from{transform:scale(1.02)}to{transform:scale(1.14) translate3d(-1.5%,-1%,0)}}

/* spec card on photo slides */
.spec-card{border:1px solid var(--hair-2);background:rgba(24,26,28,.82);backdrop-filter:blur(10px);
  padding:26px 26px 24px;border-radius:2px;max-width:400px;margin-left:auto}
.spec-card h3{font-size:18px;text-transform:uppercase;letter-spacing:.04em}
.spec-card dl{margin:18px 0 0;display:grid;gap:0}
.spec-card dl>div{display:flex;justify-content:space-between;gap:16px;padding:11px 0;border-top:1px solid var(--hair)}
.spec-card dt{font-family:"IBM Plex Mono";font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--steel)}
.spec-card dd{margin:0;font-family:"Chakra Petch";font-weight:600;font-size:15px;color:var(--metal-lt);white-space:nowrap}

/* controls */
.hero-nav{position:relative;z-index:6;border-top:1px solid var(--hair);background:rgba(21,23,25,.55);backdrop-filter:blur(8px)}
.hero-nav .wrap{display:flex;gap:0}
.hero-tab{flex:1;background:none;border:0;border-right:1px solid var(--hair);color:var(--muted);cursor:pointer;
  text-align:left;padding:18px 22px 20px;position:relative;font-family:inherit;transition:color var(--dur-2) var(--ease),background var(--dur-2) var(--ease)}
.hero-tab:last-child{border-right:0}
.hero-tab:hover{color:#E2E4E6;background:rgba(36,38,40,.4)}
.hero-tab .n{font-family:"IBM Plex Mono";font-size:10px;letter-spacing:.2em;display:block;margin-bottom:7px;opacity:.75}
.hero-tab .t{font-family:"Chakra Petch";font-weight:600;font-size:14.5px;letter-spacing:.05em;text-transform:uppercase;display:block}
.hero-tab[aria-selected="true"]{color:#fff}
.hero-tab[aria-selected="true"] .n{color:var(--metal-mid)}
.hero-tab .bar{position:absolute;left:0;bottom:0;height:2px;width:100%;background:rgba(236,240,244,.16)}
/* The tip of a filling bar is where light would collect, and it moves — a
   specular event, not a painted edge. The copper occupies the last 12% and
   falls off backwards into metal, so there is no line you could trace. */
.hero-tab .bar i{display:block;height:100%;width:0;position:relative;
  background:linear-gradient(90deg,var(--metal-dk) 0%,var(--metal-mid) 62%,var(--metal-lt) 88%,var(--copper) 97%,var(--copper-hot) 100%)}
.hero-tab .bar i::after{content:"";position:absolute;right:0;top:50%;width:26px;height:11px;
  transform:translate(40%,-50%);border-radius:50%;pointer-events:none;
  background:radial-gradient(closest-side,var(--copper-hot),rgba(224,138,79,.55) 42%,rgba(224,138,79,0) 100%)}
.hero-tab[aria-selected="true"] .bar i{animation:fill var(--dur,7s) linear forwards}
@keyframes fill{to{width:100%}}
.slider-paused .hero-tab .bar i{animation-play-state:paused}

.hero-arrows{position:absolute;z-index:6;right:var(--pad);top:50%;transform:translateY(-50%);display:grid;gap:10px}
/* These float over the photograph, and once the photo went to full opacity a
   60%-opaque ground stopped being a ground — over slide 4's hi-vis sleeve the
   buttons read green. They get an opaque stone face and a blur behind it, so
   the chevron has one reliable edge on any frame beneath it. */
.hero-arrows button{width:44px;height:44px;border:1px solid var(--hair-2);
  background:rgba(21,23,25,.92);backdrop-filter:blur(6px);
  color:var(--pearl);cursor:pointer;border-radius:2px;display:grid;place-items:center;
  box-shadow:0 6px 20px -10px rgba(0,0,0,.9);
  transition:border-color var(--dur-2),color var(--dur-2),background var(--dur-2)}
.hero-arrows button:hover{border-color:var(--metal-mid);color:var(--metal-lt)}
.hero-arrows svg{width:16px;height:16px}

@media (max-width:1024px){
  .spec-card{margin-left:0;max-width:none;margin-top:30px}
  .hero-arrows{display:none}
  /* The grid goes single-column here, so the copy stops being a left-hand
     band and becomes a top-hand one. The pool has to follow it — without
     this the desktop pool sat beside the text rather than under it, which is
     why the ghost button measured 3.64:1 at 1024 and 3.37:1 at 768. */
  .slide-bg::after{background:
    radial-gradient(89% 56% at 34% 33%,rgba(21,23,25,.88) 0%,rgba(21,23,25,.88) 26%,rgba(21,23,25,.854) 34%,rgba(21,23,25,.777) 42%,rgba(21,23,25,.66) 50%,rgba(21,23,25,.516) 58%,rgba(21,23,25,.364) 66%,rgba(21,23,25,.22) 74%,rgba(21,23,25,.103) 82%,rgba(21,23,25,.026) 90%,rgba(21,23,25,0) 98%),
    /* heavier at the very top than the desktop veil: single-column puts the
       eyebrow in the first 18% of the frame, which on slide 4 is open sky. */
    linear-gradient(180deg,rgba(21,23,25,.52) 0%,rgba(21,23,25,.07) 24%,rgba(21,23,25,.07) 62%,rgba(21,23,25,.42) 100%)}
}
@media (max-width:760px){
  /* One column, copy over the whole frame, so the pool becomes a band.
     The photo runs at full opacity here too. What made that possible was
     giving the CHROME its own grounds (see .slide .eyebrow and
     .slide .btn-ghost below): while the eyebrow pill and the ghost button
     were relying on the scrim, they were the binding constraint and the band
     had to stay dense right through the middle of the frame to serve them.
     Grounded, the only free-standing type left is the headline and the lede,
     which sit higher and need far less. Net effect at the darkest point is
     roughly double the photograph getting through: .80x(1-.78) before
     against 1.0x(1-.67) now. Worst case measured: lede 5.5:1, headline
     4.1:1, both over their floors. */
  .slide-bg img.loaded{opacity:1}
  .slide-bg::after{background:
    linear-gradient(180deg,rgba(21,23,25,.54) 0%,rgba(21,23,25,.67) 12%,rgba(21,23,25,.65) 40%,rgba(21,23,25,.37) 58%,rgba(21,23,25,.16) 76%,rgba(21,23,25,.42) 100%)}
  /* Chrome over a full-opacity photograph needs its own ground. Both of these
     were translucent enough to have none — the same fault the hero arrows had
     at .6 over slide 4's hi-vis sleeve. */
  .slide .eyebrow{background:rgba(21,23,25,.78);backdrop-filter:blur(6px);border-color:rgba(236,240,244,.26)}
  .slide .btn-ghost{background:rgba(21,23,25,.84);backdrop-filter:blur(6px);border-color:rgba(236,240,244,.30)}
  .hero-nav .wrap{padding-left:0;padding-right:0}
  .hero-tab{padding:14px 12px 16px}
  .hero-tab .t{font-size:12px;letter-spacing:.03em}
  .hero-tab .n{font-size:9px;margin-bottom:5px}
  .card .shot{height:150px}
  .slide{padding:38px 0 30px}
}
@media (prefers-reduced-motion:reduce){
  .slide.is-active .slide-bg img.loaded{animation:none;transform:none}
  .slide-copy>*{opacity:1;transform:none;animation:none!important}
  .hero-tab[aria-selected="true"] .bar i{animation:none;width:100%}
}

/* ================= exploded assembly drawings ================= */
/* Generated by build/diagrams.py. The SVG carries the drawing and numbered
   balloons; the words are HTML beside it, because the generator cannot know
   the metrics of faces that load from Google Fonts and every width estimate
   it made clipped. That split also makes the labels selectable, searchable
   and translatable, which text inside an SVG is not. */
.dg-head{display:flex;align-items:center;gap:14px;margin-bottom:clamp(20px,3vw,32px)}
.dg-head .mono{white-space:nowrap}
.dg-head i{flex:1;height:1px;background:var(--rule-lit);display:block}
.dg-body{display:grid;grid-template-columns:1.3fr .7fr;gap:clamp(20px,3.5vw,54px);align-items:center}
.dg-art svg{width:100%;height:auto;display:block;overflow:visible}
.parts{list-style:none;margin:0;padding:0}
.parts li{display:grid;grid-template-columns:32px 1fr;gap:14px;padding:15px 0;border-top:1px solid var(--hair)}
.parts li:first-child{border-top:0}
.parts .n{font-family:"Chakra Petch";font-weight:600;font-size:11.5px;letter-spacing:.08em;
  color:var(--steel);border:1px solid var(--hair-2);border-radius:50%;
  width:30px;height:30px;display:grid;place-items:center}
.parts h4{margin:0 0 3px;font-size:15px;letter-spacing:.05em;text-transform:uppercase}
.parts p{margin:0;font-size:14px;color:var(--silver);line-height:1.5}
/* The lit part is that page's own argument — the underlayment on tile, the
   clip on metal, the topcoat on foam — so it carries the accent and nothing
   else in the drawing does. */
.parts li.lit .n{color:var(--copper-hot);border-color:rgba(224,138,79,.5);
  box-shadow:0 0 15px -6px var(--copper-glow)}
.parts li.lit h4{color:var(--copper-hot)}
.dg-cap{margin:clamp(20px,3vw,30px) 0 0;color:var(--steel);font-size:14.5px;max-width:74ch}

/* The explode. Screen y is (x+y)/2 - z, so translating a layer DOWN by
   exactly its z draws it as though it were at z=0 — the collapsed stack.
   The animation is that transform running back to zero, which is why the
   layers land in register rather than near it. */
/* Guarded on `scripting: enabled`, the same way .ph-bg img is. The collapsed
   state hides three layers of four behind the top one, so unlike the rest of
   the site's reveals — where a missed .in leaves text at opacity 0, invisible
   but not wrong — a missed .in here leaves a drawing that says something
   untrue about the roof. Without script the resting state is the finished
   exploded drawing and a fully visible parts list.
   Worth knowing: an IntersectionObserver does not fire at all in a background
   tab, so "the reveal never arrives" is a real state, not a hypothetical. */
@media (scripting: enabled){
  /* --x is how exploded the stack is, 0..1. The reveal sets it to 1; the
     slider sets it anywhere. The layer's transform is dz*(1-x), so the same
     expression serves the reveal and the scrub, and `transition: transform`
     smooths either — a custom property change moves the computed transform,
     which is what the transition is watching. */
  .dg{--x:0}
  .dg.in{--x:1}
  .dg-art .ly{transform:translateY(calc(var(--dz) * (1 - var(--x))));
    transition:transform 1.05s cubic-bezier(.2,.7,.25,1) calc(var(--i) * 110ms),
               opacity var(--dur-2) var(--ease)}
  /* direct manipulation must not lag behind the hand: no easing, no stagger */
  .dg.scrub .dg-art .ly{transition:opacity var(--dur-2) var(--ease)}
  .parts li{opacity:0;transform:translateY(10px);
    transition:opacity var(--dur-3) var(--ease) calc(var(--i) * 110ms + 360ms),
               transform var(--dur-3) var(--ease) calc(var(--i) * 110ms + 360ms)}
  .dg.in .parts li{opacity:1;transform:none}
  /* balloons wait for the explode to land — their leaders are drawn for the
     exploded position, and during the reveal the layers are still moving */
  .dg:not(.dg-flat) .bal{opacity:0;transition:opacity .4s ease 1.2s}
  .dg:not(.dg-flat).in .bal{opacity:1}
  .dg-ctl{opacity:0;transition:opacity .4s ease 1.3s}
  .dg.in .dg-ctl{opacity:1}
  .dg-flat .dg-art{opacity:0;transform:translateY(8px);
    transition:opacity .6s var(--ease),transform .6s var(--ease)}
  .dg-flat.in .dg-art{opacity:1;transform:none}
}

/* ---- isolating a part: hover, focus, or tap on either the list or the
   balloon. The rest of the drawing steps back rather than the chosen part
   shouting — the same value logic as the scrim under the hero copy. */
.parts li{cursor:pointer;border-radius:2px;outline-offset:4px;
  transition:background var(--dur-1) var(--ease)}
.parts li:hover{background:rgba(236,240,244,.03)}
.parts li:focus-visible{outline:2px solid var(--pearl)}
.dg.has-active .ly:not(.is-on),
.dg.has-active .hl:not(.is-on){opacity:.28}
.dg.has-active .parts li:not(.is-on){opacity:.55}
.dg.has-active .bal:not(.is-on){opacity:.4}
.ly,.hl,.bal{transition:opacity var(--dur-2) var(--ease)}
.parts li.is-on .n{color:var(--copper-hot);border-color:rgba(224,138,79,.55);
  box-shadow:0 0 15px -6px var(--copper-glow)}
.parts li.is-on h4{color:var(--copper-hot)}
.parts li[aria-pressed="true"]{background:rgba(224,138,79,.05)}
.bal{cursor:pointer}
.bal.is-on circle:last-of-type{stroke:var(--copper);stroke-width:.8}
.bal.is-on text{fill:var(--copper-hot)}
.hl.is-on{filter:drop-shadow(0 0 1.6px rgba(255,192,138,.95))
                 drop-shadow(0 0 4px rgba(224,138,79,.6))}
.ly.is-on{filter:drop-shadow(0 0 1.2px rgba(255,192,138,.9))
                 drop-shadow(0 0 3.5px rgba(224,138,79,.55))}

/* ---- the explode control. A drafting slider: a hairline track, a plain
   thumb, and the mono labels either end. */
.dg-ctl{display:flex;align-items:center;gap:14px;margin-top:18px}
.dg-ctl .mono{font-size:10px;letter-spacing:.16em}
.dg-ctl input{flex:1;-webkit-appearance:none;appearance:none;height:22px;background:none;cursor:ew-resize;margin:0}
.dg-ctl input::-webkit-slider-runnable-track{height:1px;background:var(--hair-2)}
.dg-ctl input::-moz-range-track{height:1px;background:var(--hair-2)}
.dg-ctl input::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;margin-top:-6.5px;
  border-radius:50%;background:var(--stone);border:1px solid var(--metal-mid);
  box-shadow:0 0 0 3px var(--stone);transition:border-color var(--dur-1),box-shadow var(--dur-2)}
.dg-ctl input::-moz-range-thumb{width:12px;height:12px;border-radius:50%;background:var(--stone);
  border:1px solid var(--metal-mid);box-shadow:0 0 0 3px var(--stone)}
.dg-ctl input:hover::-webkit-slider-thumb,.dg-ctl input:focus-visible::-webkit-slider-thumb{
  border-color:var(--copper);box-shadow:0 0 0 3px var(--stone),0 0 14px -4px var(--copper-glow)}
.dg-ctl input:focus-visible{outline:none}

/* ---- the sections' air and water move. A marching dash on the shaft only;
   the head stays put, so it still reads as an arrow and not a dotted line. */
.dg.in .flow{stroke-dasharray:4.2 1.8;animation:dg-flow 1.4s linear infinite}
@keyframes dg-flow{to{stroke-dashoffset:-6}}
@media (prefers-reduced-motion:reduce){
  /* resting state is ASSEMBLED-and-exploded: the drawing is the content, so
     it must be complete without motion, not waiting on a transition. */
  /* No motion, but the slider still WORKS — it just jumps. --x stays live. */
  .dg{--x:1}
  .dg-art .ly{transition:none}
  .parts li{transition:none;opacity:1;transform:none}
  .dg-flat .dg-art{transition:none;opacity:1;transform:none}
  .dg:not(.dg-flat) .bal,.dg-ctl{transition:none;opacity:1}
  .dg.in .flow{animation:none;stroke-dasharray:none}
}
@media (max-width:860px){
  .dg-body{grid-template-columns:1fr;gap:26px}
  .parts li{padding:13px 0}
}

/* ================= before / after compare ================= */
.compare{position:relative;overflow:hidden;border:1px solid var(--hair);border-radius:2px;
  background:#23262A;aspect-ratio:4/3;--pos:50%;touch-action:pan-y}
.compare picture{position:absolute;inset:0;display:block}
.compare img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;user-select:none;-webkit-user-drag:none}
.compare .cmp-before{clip-path:inset(0 calc(100% - var(--pos)) 0 0);z-index:2}
.compare input[type=range]{position:absolute;inset:0;width:100%;height:100%;z-index:6;margin:0;
  opacity:0;cursor:ew-resize;-webkit-appearance:none;appearance:none;background:transparent}
.compare input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:64px;height:100%;cursor:ew-resize}
.compare input[type=range]::-moz-range-thumb{width:64px;height:400px;border:0;background:transparent;cursor:ew-resize}
/* The handle floats over photography, so a translucent ring had nothing
   reliable to contrast against — measured 2.41:1 on average. An opaque pearl
   ring with a dark separator keeps one legal edge on any image beneath it. */
.compare input[type=range]:focus-visible ~ .cmp-handle{
  box-shadow:0 0 0 2px var(--stone-well),0 0 0 5px var(--pearl)}
.cmp-line{position:absolute;top:0;bottom:0;left:var(--pos);width:2px;z-index:4;pointer-events:none;
  background:linear-gradient(180deg,rgba(236,240,244,.25),var(--metal-mid) 22%,var(--metal-mid) 78%,rgba(236,240,244,.25));
  box-shadow:0 0 18px rgba(236,240,244,.55)}
.cmp-handle{position:absolute;top:50%;left:var(--pos);z-index:5;transform:translate(-50%,-50%);pointer-events:none;
  width:52px;height:52px;border-radius:50%;display:grid;place-items:center;
  background:rgba(24,26,28,.86);border:1px solid var(--metal-mid);backdrop-filter:blur(4px);
  box-shadow:0 8px 30px -8px rgba(0,0,0,.9)}
.cmp-handle svg{width:22px;height:22px;color:var(--metal-lt)}
.cmp-tag{position:absolute;bottom:14px;z-index:5;pointer-events:none;
  font-family:"IBM Plex Mono";font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  background:rgba(21,23,25,.72);border:1px solid var(--hair-2);padding:6px 11px;border-radius:2px;color:#E2E4E6}
.cmp-tag-l{left:14px}
.cmp-tag-r{right:14px;border-color:var(--metal-dim);color:var(--metal-lt)}
.compare-hint{margin-top:12px;color:var(--muted)}

.feature{display:grid;grid-template-columns:1.25fr .85fr;gap:clamp(26px,4vw,56px);align-items:center;margin-top:44px}
.feature .meta h3{font-size:clamp(24px,3.2vw,34px);text-transform:uppercase;margin-bottom:14px}
.feature .meta p{color:var(--silver);font-size:15.5px}
.feature dl{margin:26px 0 0;border-top:1px solid var(--hair)}
.feature dl>div{display:flex;justify-content:space-between;gap:18px;padding:13px 0;border-bottom:1px solid var(--hair)}
.feature dt{font-family:"IBM Plex Mono";font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--steel)}
.feature dd{margin:0;font-family:"Chakra Petch";font-weight:600;font-size:15px;color:#fff;text-align:right}
@media (max-width:1024px){.feature{grid-template-columns:1fr}}

/* four material systems */
.mats{grid-template-columns:repeat(5,1fr)}
@media (max-width:1240px){.mats{grid-template-columns:repeat(3,1fr)}}
@media (max-width:1100px){.mats{grid-template-columns:repeat(2,1fr)}}
@media (max-width:640px){.mats{grid-template-columns:1fr}}

/* finished-work strip */
.strip3{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:44px}
.strip3 .shot{aspect-ratio:16/9;border:1px solid var(--hair);border-radius:2px}
@media (max-width:900px){.strip3{grid-template-columns:1fr}}

/* compare: drag anywhere on the photo */
.compare{cursor:ew-resize;user-select:none;-webkit-user-select:none}
.compare input[type=range]{pointer-events:none}
.compare.dragging{cursor:grabbing}
.cmp-handle{transition:transform var(--dur-1) var(--ease),box-shadow var(--dur-1) var(--ease)}
.compare:hover .cmp-handle{transform:translate(-50%,-50%) scale(1.06)}
.compare.dragging .cmp-handle{transform:translate(-50%,-50%) scale(1.12);
  box-shadow:0 0 0 3px rgba(236,240,244,.35),0 8px 30px -8px rgba(0,0,0,.9)}
.compare.dragging .cmp-line{box-shadow:0 0 26px rgba(236,240,244,.85)}
@media (prefers-reduced-motion:reduce){.cmp-handle{transition:none}}

/* service area, grouped by region */
.area-groups{margin-top:38px;display:grid;gap:30px}
.area-group h3{font-family:"IBM Plex Mono";font-weight:500;font-size:10px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--metal-mid);margin-bottom:14px;display:flex;align-items:center;gap:12px}
.area-group h3 i{flex:1;height:1px;background:var(--rule-lit);display:block}
@media (min-width:900px){.area-groups{grid-template-columns:repeat(3,1fr);gap:34px}}

/* hero slide 1: photo sits far back so the drawing and star stay dominant */
/* Slide 1 keeps the photograph further back than the other three, because the
   drafting elevation and its star have to stay the brightest thing in frame —
   that is the co-visibility rule, not timidity. But .30 buried the ridgeline
   entirely, so it comes up to .46 and the scrim is re-pooled over the copy
   instead of washing the whole plate. */
.slide-bg.subtle img.loaded{opacity:.46;filter:saturate(.82) contrast(.95)}
.slide-bg.subtle::after{
  background:
    radial-gradient(72% 92% at 22% 50%,rgba(21,23,25,.80) 0%,rgba(21,23,25,.78) 24%,rgba(21,23,25,.70) 38%,rgba(21,23,25,.56) 52%,rgba(21,23,25,.40) 66%,rgba(21,23,25,.24) 80%,rgba(21,23,25,.12) 92%,rgba(21,23,25,.06) 100%),
    radial-gradient(58% 62% at 72% 48%,rgba(21,23,25,.42) 0%,rgba(21,23,25,.30) 46%,rgba(21,23,25,.12) 78%,rgba(21,23,25,0) 100%),
    linear-gradient(180deg,rgba(21,23,25,.52) 0%,rgba(21,23,25,.22) 26%,rgba(21,23,25,.54) 66%,rgba(21,23,25,.86) 100%)}
.slide.is-active .slide-bg.subtle img.loaded{animation:kenburns-slow 26s ease-out forwards}
@keyframes kenburns-slow{from{transform:scale(1.03)}to{transform:scale(1.10)}}
@media (prefers-reduced-motion:reduce){
  .slide.is-active .slide-bg.subtle img.loaded{animation:none;transform:none}
}
@media (max-width:760px){
  /* Slide 1's pool is anchored at 22% 50% and shaped 72%x92% — a landscape
     geometry. On a 390x1009 portrait frame it covers nearly everything, which
     is why this slide stayed dark while the other three brightened. The
     drawing sits at the TOP here and the copy below it, so the mobile answer
     is a vertical band, deepened through 42-56% where the headline's gradient
     runs down to #7B7D7F. Photo up from .46 to .70; measured luminance in the
     drawing's own band goes 0.018 -> 0.048, and every role still clears:
     headline 3.57:1, eyebrow 7.85:1, lede 12.18:1. */
  .slide-bg.subtle img.loaded{opacity:.70}
  .slide-bg.subtle::after{background:
    linear-gradient(180deg,rgba(21,23,25,.50) 0%,rgba(21,23,25,.44) 10%,rgba(21,23,25,.56) 28%,rgba(21,23,25,.70) 42%,rgba(21,23,25,.72) 56%,rgba(21,23,25,.56) 74%,rgba(21,23,25,.44) 88%,rgba(21,23,25,.62) 100%)}
}

/* The north mark. Everything but the star is drawn in metal at drafting
   weight; the star is the only lit thing, and what it throws falls off rather
   than stopping at an edge. Co-visibility holds because the primary plate's
   copper bloom sits far below the fold on these pages — the mark is alone in
   its viewport. The whole thing is decorative, so it is aria-hidden and
   carries no contrast obligation; --copper is 6.78:1 on stone regardless. */
.north{position:absolute;z-index:1;top:clamp(18px,3vw,34px);right:var(--pad);
  width:clamp(46px,5vw,64px);height:auto;pointer-events:none;opacity:.9}
.n-ring{fill:none;stroke:var(--metal-dim);stroke-width:1}
.n-tick{fill:none;stroke:var(--steel);stroke-width:1;stroke-linecap:round;opacity:.8}
.n-needle{fill:none;stroke:var(--metal-mid);stroke-width:1.2;stroke-linejoin:round}
.n-star{fill:var(--copper-hot);
  filter:drop-shadow(0 0 2px rgba(255,255,255,.9))
         drop-shadow(0 0 6px rgba(255,192,138,.85))
         drop-shadow(0 0 14px rgba(224,138,79,.7))}
@media (prefers-reduced-motion:no-preference){
  .n-ring{stroke-dasharray:132;stroke-dashoffset:132;animation:draw 1.1s var(--ease) .2s forwards}
  .n-needle{stroke-dasharray:64;stroke-dashoffset:64;animation:draw .8s var(--ease) .5s forwards}
  .n-tick{opacity:0;animation:fade .6s ease .9s forwards}
  /* the light arrives after the instrument is drawn */
  .n-star{opacity:0;transform-origin:32px 12px;animation:pop .8s cubic-bezier(.2,1.2,.3,1) 1.05s forwards}
}
@media (max-width:760px){.north{display:none}}

.contact-shot{aspect-ratio:4/3;border:1px solid var(--hair);border-radius:2px;margin-bottom:22px}

/* A finished poster, carrying its own wordmark and display type. It is placed
   whole and never cropped, and it is capped well short of the measure so the
   page's own h1 stays the largest type in view — two competing headlines at
   the same scale is the failure mode here. The band above it is tightened so
   it reads as belonging to the header rather than opening the gallery. */
.band-poster{padding-top:clamp(28px,3vw,44px);padding-bottom:clamp(40px,5vw,72px)}
.band-poster + .band{border-top:0;padding-top:clamp(24px,3vw,40px)}
.poster{margin:0;display:flex;justify-content:center}
.poster img{width:min(440px,100%);height:auto;border:1px solid var(--hair);border-radius:2px;
  box-shadow:0 26px 60px -32px rgba(0,0,0,.9)}

/* interior page headers get an atmospheric plate */
.page-hero{overflow:hidden}
.ph-bg{position:absolute;inset:0;z-index:0;overflow:hidden}
.ph-bg img{width:100%;height:100%;object-fit:cover;object-position:50% 28%;opacity:.34;transition:opacity var(--dur-3) var(--ease);transform:scale(1.04)}
@media (scripting: enabled){.ph-bg img:not(.loaded){opacity:0}}
.ph-bg img.loaded{opacity:.34}
.ph-bg::after{content:"";position:absolute;inset:0;
  background:
    /* Monotonic, and weighted to keep the type column dark: rasterising the
       composited header showed --steel (the eyebrow, the dimmest type here)
       landing at 4.36:1 under a more open curve. These stops hold it above
       the 4.5:1 floor while still letting the photograph through outboard
       of the text. */
    linear-gradient(100deg,rgba(21,23,25,.97) 0%,rgba(21,23,25,.94) 36%,rgba(21,23,25,.64) 64%,rgba(21,23,25,.34) 86%,rgba(21,23,25,.25) 100%),
    linear-gradient(180deg,rgba(21,23,25,.5),rgba(21,23,25,.22) 42%,rgba(21,23,25,.94))}
.page-hero .wrap{position:relative;z-index:2}
/* Header rhythm, moved off an inline style="margin-top:16px" that shipped on
   the h1 of all 15 interior pages — a layout value in a style attribute, which
   no media query can reach. */
.page-hero h1{margin-top:clamp(12px,1.5vw,18px)}
.page-hero .lede{margin-top:clamp(16px,2vw,22px)}
@media (max-width:760px){.ph-bg img.loaded{opacity:.26}}

/* photos inside material system rows */
.row-media{margin-top:20px;border:1px solid var(--hair);border-radius:2px;aspect-ratio:4/3}
.row .row-media.wide{aspect-ratio:16/10}
.mat-banner{margin-top:40px;border:1px solid var(--hair);border-radius:2px;aspect-ratio:16/6;position:relative}
.mat-banner .cap{left:18px;bottom:16px}
@media (max-width:760px){.mat-banner{aspect-ratio:16/9}}

/* brand band */
.brand-band{display:grid;grid-template-columns:.9fr 1.1fr;gap:clamp(28px,5vw,64px);align-items:center}
.brand-art{position:relative;display:flex;align-items:center;justify-content:center;
  padding:clamp(8px,2vw,24px)}
.brand-art img{width:100%;height:auto;display:block;max-width:460px;
  filter:drop-shadow(0 0 46px rgba(236,240,244,.20)) drop-shadow(0 8px 30px rgba(0,0,0,.55))}
/* the star at the top of the mark breathes with the constellation behind it */
@media (prefers-reduced-motion:no-preference){
  .brand-art img{animation:markglow 7s ease-in-out infinite}
}
@keyframes markglow{
  0%,100%{filter:drop-shadow(0 0 42px rgba(236,240,244,.18)) drop-shadow(0 10px 34px rgba(0,0,0,.85))}
  50%    {filter:drop-shadow(0 0 66px rgba(236,240,244,.32)) drop-shadow(0 10px 34px rgba(0,0,0,.85))}
}
@media (max-width:900px){.brand-band{grid-template-columns:1fr;gap:30px}.brand-art{max-width:480px;margin:0 auto}}

/* six-up value grid */
.cards.why{grid-template-columns:repeat(3,1fr)}
.cards.why .card h3{margin-top:16px}
@media (max-width:1024px){.cards.why{grid-template-columns:repeat(2,1fr)}}
@media (max-width:760px){.cards.why{grid-template-columns:1fr}}

/* promise checklist */
.promise{list-style:none;padding:0;margin:0;display:grid;gap:13px}
.promise li{display:flex;gap:13px;align-items:flex-start;font-size:15.5px;color:var(--silver)}
.promise svg{width:16px;height:16px;color:var(--metal-mid);flex:none;margin-top:4px}

/* carousel pause control — WCAG 2.2.2 requires a way to stop auto-updating content */
.hero-nav{position:relative}
.hero-pause{position:absolute;right:var(--pad);top:-56px;z-index:8;display:inline-flex;align-items:center;gap:8px;
  background:rgba(24,26,28,.72);border:1px solid var(--hair-2);border-radius:2px;color:var(--pearl);
  padding:9px 14px;cursor:pointer;font-family:inherit;backdrop-filter:blur(6px);
  transition:border-color var(--dur-2) var(--ease),color var(--dur-2) var(--ease)}
.hero-pause:hover{border-color:var(--metal-mid);color:var(--metal-lt)}
.hero-pause svg{width:13px;height:13px}
.hero-pause .i-play{display:none}
.hero-pause[aria-pressed="true"] .i-pause{display:none}
.hero-pause[aria-pressed="true"] .i-play{display:block}
.hero-pause .lbl{color:inherit;letter-spacing:.16em}
@media (max-width:760px){
  .hero-pause{top:-48px;right:var(--pad);padding:8px 11px}
  .hero-pause .lbl{display:none}
}

/* ============================================================
   SILVER — the second metal
   Roles are split so the two never compete:
     GOLD   = action and brand  (CTAs, the star, active state, "after")
     SILVER = structure and metal (numbers, specs, icons at rest, edges)
   ============================================================ */
/* (the silver ramp merged into the token block at the top of this file) */

/* brushed-metal type, lifted from the logo lettering */
.metal{
  background:linear-gradient(180deg,#FFFFFF 4%,#D9DBDD 34%,#97999B 54%,#EDEFF1 78%,#B9BBBD 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  text-shadow:0 1px 0 rgba(255,255,255,.05)}

/* stat strip reads as machined numbers */
.strip b{
  background:linear-gradient(180deg,#FFFFFF 6%,#D6D8DA 40%,#9B9D9F 58%,#EEF0F2 92%);
  -webkit-background-clip:text;background-clip:text;color:transparent}

/* a hairline of light on the top edge of every raised surface */
.card,.panel,.mat,.quote,.spec-card,.brand-art,.compare,.shot,.mat-banner,.row-media{
  position:relative}
.card::before,.panel::before,.mat::before,.spec-card::before{
  content:"";position:absolute;left:0;right:0;top:0;height:1px;z-index:3;
  background:linear-gradient(90deg,transparent,var(--edge) 22%,rgba(238,241,244,.34) 50%,var(--edge) 78%,transparent);
  pointer-events:none}

/* icons rest in silver, warm to gold on hover — gold marks intent, not decoration */
.card .ic{color:var(--metal-mid);transition:color var(--dur-2) var(--ease)}
.card:hover .ic{color:var(--pearl)}   /* was --metal-mid: a no-op hover */

/* ghost button becomes the silver sibling of the gold one */
.btn-ghost{border-color:var(--metal-dim);color:var(--metal-lt);
  background:linear-gradient(180deg,rgba(198,203,209,.09),rgba(14,16,18,.5))}
.btn-ghost:hover{border-color:var(--metal-mid);color:#fff;
  background:linear-gradient(180deg,rgba(198,203,209,.16),rgba(14,16,18,.45))}

/* spec values: the data is silver, the emphasis stays gold */
.spec .num{color:var(--metal-lt)}
.spec td:first-child{color:var(--metal-lt)}
.feature dd,.spec-card dd{color:var(--metal-lt)}
.mat dd{color:var(--metal-lt)}

/* section rules pick up the metal */
.sheet i{background:linear-gradient(90deg,var(--metal-dim),transparent)}
.area-group h3 i{background:linear-gradient(90deg,var(--metal-dim),transparent)}

/* Before/after reads by VALUE, not hue — the two tags had collapsed to
   identical computed styles when the accent that used to separate them was
   retired. The old roof sits back; the finished work comes forward. */
.cmp-tag-l{border-color:var(--hair-2);color:var(--muted);background:rgba(14,16,18,.72)}
.cmp-tag-r{border-color:rgba(236,240,244,.42);color:var(--pearl);
  background:rgba(31,33,35,.82);box-shadow:0 2px 10px -4px rgba(0,0,0,.7)}

/* process dots read as fasteners */
.step .dot{border-color:var(--metal-mid);
  box-shadow:0 0 0 4px var(--carbon),0 0 14px rgba(198,203,209,.35)}
.step .mono{color:var(--metal-mid)}

/* The credential-strip labels are set by .strip .mono earlier in this file.
   A second rule here used to override them to --metal-dk, which measures
   4.14:1 on the strip's own composited background — under AA, and silently,
   because the first rule looks correct in isolation. --metal-dk is a
   stroke/marker value, not a text value. */

/* constellation → truss animation behind the brand band */
#brand-story{position:relative;overflow:hidden;isolation:isolate;
  padding-top:clamp(84px,10vw,150px);padding-bottom:clamp(84px,10vw,150px)}
.constellation{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;
  -webkit-mask-image:radial-gradient(120% 90% at 50% 45%,#000 42%,transparent 88%);
          mask-image:radial-gradient(120% 90% at 50% 45%,#000 42%,transparent 88%)}
#brand-story .wrap{position:relative;z-index:1}

/* drafting-dimension animation behind the About header */
.measure-fx{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none;
  -webkit-mask-image:linear-gradient(100deg,transparent 0%,transparent 26%,rgba(0,0,0,.5) 42%,#000 60%,#000 100%);
          mask-image:linear-gradient(100deg,transparent 0%,transparent 26%,rgba(0,0,0,.5) 42%,#000 60%,#000 100%)}
.page-hero .wrap{z-index:2}
@media (max-width:760px){
  .measure-fx{-webkit-mask-image:linear-gradient(180deg,transparent 0%,rgba(0,0,0,.35) 40%,#000 82%);
                      mask-image:linear-gradient(180deg,transparent 0%,rgba(0,0,0,.35) 40%,#000 82%)}
}

/* real logo lockup in header and footer */
.brand{display:inline-flex;align-items:center;text-decoration:none;line-height:0}
.brand picture{display:block}
.brand img{height:38px;width:auto;display:block;
  filter:drop-shadow(0 1px 10px rgba(236,240,244,.16));
  transition:filter var(--dur-2) var(--ease),opacity var(--dur-2) var(--ease)}
.brand:hover img{filter:drop-shadow(0 1px 16px rgba(236,240,244,.34))}
.brand-lg img{height:48px}
@media (max-width:760px){
  .brand img{height:30px}
  .brand-lg img{height:40px}
}
@media (max-width:380px){ .brand img{height:26px} }

/* where the measuring animation runs, pull the photo back so the drafting reads */
.page-hero:has(.measure-fx) .ph-bg img.loaded{opacity:.20}
.page-hero:has(.measure-fx) .ph-bg::after{
  background:
    linear-gradient(100deg,rgba(21,23,25,.95) 0%,rgba(21,23,25,.88) 40%,rgba(21,23,25,.66) 74%,rgba(21,23,25,.58) 100%),
    linear-gradient(180deg,rgba(21,23,25,.6),rgba(21,23,25,.4) 40%,rgba(21,23,25,.9))}

/* honeypot: off-screen but not display:none, which some bots detect */
.hp{position:absolute!important;left:-9999px!important;top:auto;width:1px;height:1px;overflow:hidden}
.form-error{grid-column:1/-1;margin:0 0 4px;padding:13px 16px;border-radius:2px;font-size:15px;
  border:1px solid rgba(214,120,90,.45);background:rgba(214,120,90,.10);color:#F0C7B6}
.turnstile-wrap{min-height:0}
.turnstile-wrap:empty{display:none}
input[aria-invalid="true"],textarea[aria-invalid="true"]{border-color:rgba(214,120,90,.7)}

/* system page at-a-glance list */
.spec-card-dl{display:grid;gap:0}
.spec-card-dl>div{display:flex;justify-content:space-between;gap:16px;padding:11px 0;border-top:1px solid var(--hair)}
.spec-card-dl dt{font-family:"IBM Plex Mono";font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--steel)}
.spec-card-dl dd{margin:0;font-family:"Chakra Petch";font-weight:600;font-size:15px;color:var(--metal-lt);white-space:nowrap}

/* material cards as links to their system pages */
a.mat-link{text-decoration:none;color:inherit;display:block;
  transition:border-color var(--dur-2) var(--ease),transform var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease)}
a.mat-link:hover{border-color:var(--metal-dim);transform:translateY(-4px);
  box-shadow:0 18px 40px -22px rgba(0,0,0,.85)}
a.mat-link:focus-visible{outline:2px solid var(--pearl);outline-offset:3px}
.mat-more{display:inline-block;margin-top:14px;color:var(--metal-lt);letter-spacing:.14em;
  transition:transform var(--dur-2) var(--ease)}
a.mat-link:hover .mat-more{transform:translateX(4px)}
a.mat-link:hover h3{color:var(--metal-lt)}
.mat h3{transition:color var(--dur-2) var(--ease)}

/* interior headers: type sits proud of the plate */
.page-hero h1{text-shadow:0 2px 22px rgba(21,23,25,.85),0 1px 3px rgba(21,23,25,.9)}
.page-hero .lede{text-shadow:0 1px 12px rgba(21,23,25,.9);max-width:56ch}
.page-hero .crumb,.page-hero>.wrap>.mono{text-shadow:0 1px 8px rgba(21,23,25,.9)}
/* plates get a touch more presence now the scrim protects the copy */
.ph-bg img.loaded{opacity:.44}
.page-hero:has(.measure-fx) .ph-bg img.loaded{opacity:.22}
@media (max-width:760px){.ph-bg img.loaded{opacity:.30}}

/* ============================================================
   Service vignettes — each one performs its trade in miniature.
   Pure CSS keyframes; they run only after the row's .reveal fires
   (.in) and stop entirely under prefers-reduced-motion.
   ============================================================ */
.svc-fx{display:block;width:min(210px,100%);height:64px;margin-top:14px;opacity:.9}

/* shared palette */
.svc-fx .c,.svc-fx .seam,.svc-fx .tarp,.svc-fx .coat,.svc-fx .t,.svc-fx .ridge{stroke:var(--metal-mid)}
.svc-fx .scan,.svc-fx .ring,.svc-fx .rule{stroke:var(--steel)}
.svc-fx .ping,.svc-fx .torch,.svc-fx .d,.svc-fx .hail circle,.svc-fx .spray circle{fill:#D6D8DA}

/* 01 replacement: courses lay themselves, ridge caps it */
.fx-replace .c{stroke-dasharray:194;stroke-dashoffset:194;opacity:.85}
.fx-replace .ridge{stroke-dasharray:194;stroke-dashoffset:194}
.in .fx-replace .c1{animation:lay 1s .1s ease forwards}
.in .fx-replace .c2{animation:lay 1s .5s ease forwards}
.in .fx-replace .c3{animation:lay 1s .9s ease forwards}
.in .fx-replace .c4{animation:lay 1s 1.3s ease forwards}
.in .fx-replace .ridge{animation:lay .8s 1.8s ease forwards}
@keyframes lay{to{stroke-dashoffset:0}}

/* 02 repairs: scanner sweeps the roofline, finds the leak, ring confirms */
.fx-repair .scan{transform-origin:105px 32px}
.in .fx-repair .scan{animation:sweep 3.6s ease-in-out infinite}
.fx-repair .ping{opacity:0}
.in .fx-repair .ping{animation:ping 3.6s ease-in-out infinite}
.fx-repair .ring{opacity:0;transform-origin:128px 41px}
.in .fx-repair .ring{animation:ringout 3.6s ease-out infinite}
@keyframes sweep{0%{transform:translateX(-58px)}45%,60%{transform:translateX(23px)}100%{transform:translateX(-58px)}}
@keyframes ping{0%,38%{opacity:0}48%,70%{opacity:1}85%,100%{opacity:0}}
@keyframes ringout{0%,44%{opacity:0;transform:scale(1)}52%{opacity:.9}78%,100%{opacity:0;transform:scale(3.4)}}

/* 03 storm: hail falls, tarp line snaps over the ridge */
.in .fx-storm .hail circle{animation:hailfall 2.8s linear infinite}
.fx-storm .hail circle:nth-child(2){animation-delay:.7s}
.fx-storm .hail circle:nth-child(3){animation-delay:.3s}
.fx-storm .hail circle:nth-child(4){animation-delay:1.1s}
.fx-storm .tarp{stroke-dasharray:100;stroke-dashoffset:100}
.in .fx-storm .tarp{animation:lay 1s 1.2s ease forwards}
@keyframes hailfall{0%{transform:translateY(0);opacity:0}12%{opacity:.9}72%{opacity:.9}86%{transform:translateY(44px);opacity:0}100%{transform:translateY(44px);opacity:0}}

/* 04 commercial: the hot-air weld runs the seam */
.fx-weld .seam{stroke-dasharray:182;stroke-dashoffset:182}
.in .fx-weld .seam{animation:lay 2.6s .3s ease-in-out infinite alternate}
.in .fx-weld .torch{animation:torchrun 2.6s .3s ease-in-out infinite alternate}
@keyframes torchrun{from{transform:translateX(0)}to{transform:translateX(182px)}}

/* 05 gutters: drops ride the run and drop the downspout */
.fx-gutter .d{opacity:0;offset-path:path('M12 22 H160 Q170 22 170 32 V56');offset-rotate:0deg}
.in .fx-gutter .d1{animation:flow 2.4s linear infinite}
.in .fx-gutter .d2{animation:flow 2.4s .8s linear infinite}
.in .fx-gutter .d3{animation:flow 2.4s 1.6s linear infinite}
@keyframes flow{0%{offset-distance:0%;opacity:0}8%{opacity:1}88%{opacity:1}100%{offset-distance:100%;opacity:0}}

/* 06 foam: the coat draws across while spray particles drift */
.fx-foam .coat{stroke-dasharray:186;stroke-dashoffset:186}
.in .fx-foam .coat{animation:lay 2.2s .2s ease forwards}
.fx-foam .spray{opacity:0}
.in .fx-foam .spray{animation:sprayrun 2.2s .2s ease forwards}
@keyframes sprayrun{0%{opacity:1;transform:translateX(12px)}96%{opacity:1}100%{opacity:0;transform:translateX(198px)}}

/* 07 inspections: three checks tick down the list, rule underlines */
.fx-inspect .t{stroke-dasharray:26;stroke-dashoffset:26}
.in .fx-inspect .t1{animation:lay .5s .2s ease forwards}
.in .fx-inspect .t2{animation:lay .5s .7s ease forwards}
.in .fx-inspect .t3{animation:lay .5s 1.2s ease forwards}
.fx-inspect .rule{stroke-dasharray:186;stroke-dashoffset:186;opacity:.5}
.in .fx-inspect .rule{animation:lay 1s 1.6s ease forwards}

@media (prefers-reduced-motion: reduce){
  .svc-fx *{animation:none!important}
  .fx-replace .c,.fx-replace .ridge,.fx-storm .tarp,.fx-weld .seam,
  .fx-foam .coat,.fx-inspect .t,.fx-inspect .rule{stroke-dashoffset:0}
  .fx-repair .ping{opacity:1}
  .fx-gutter .d{opacity:0}
}

/* gutter fallback where offset-path is unsupported: straight run along the trough */
@supports not (offset-path: path('M0 0H10')){
  .fx-gutter .d{offset-path:none;cy:22px}
  .in .fx-gutter .d1,.in .fx-gutter .d2,.in .fx-gutter .d3{animation-name:flowx}
  @keyframes flowx{0%{transform:translateX(12px);opacity:0}10%{opacity:1}80%{opacity:1}100%{transform:translateX(168px);opacity:0}}
}

/* social buttons — hidden until real profile URLs replace the # placeholders */
.social{display:flex;gap:12px;margin-top:20px}
.soc{display:inline-flex;align-items:center;justify-content:center;width:42px;height:42px;
  border:1px solid var(--metal-dim);border-radius:2px;color:var(--metal-mid);
  background:linear-gradient(180deg,rgba(198,203,209,.06),rgba(14,16,18,.4));
  transition:color var(--dur-2) var(--ease),border-color var(--dur-2) var(--ease),transform var(--dur-2) var(--ease)}
.soc svg{width:19px;height:19px}
.soc:hover{color:var(--metal-lt);border-color:var(--metal-dim);transform:translateY(-2px)}
.soc:focus-visible{outline:2px solid var(--pearl);outline-offset:3px}
.soc[href="#"]{display:none}

/* ============================================================
   Form controls: fixed, predictable heights.
   Inputs previously took their height from content, which left them
   open to being stretched by their container. Pinning the height
   makes every control the same size regardless of context.
   ============================================================ */
.form{align-items:start}
.field{min-width:0;align-self:start}
.field input,.field select,
.panel input:not([type=hidden]):not([type=range]),
.panel select{
  height:52px;min-height:52px;max-height:52px;
  padding:0 15px;line-height:normal;box-sizing:border-box;flex:none}
.field textarea,.panel textarea{
  height:auto;min-height:120px;max-height:none;padding:14px 15px;line-height:1.6;flex:none}
/* the honeypot must never inherit those dimensions */
.hp input{height:1px!important;min-height:0!important;max-height:1px!important;padding:0!important}

/* ============================================================
   Booking dialog — two ways in: call, or request a time.
   ============================================================ */
.bk-overlay{position:fixed;inset:0;z-index:120;display:flex;align-items:flex-start;justify-content:center;
  padding:clamp(12px,4vh,56px) 16px;overflow-y:auto;background:rgba(3,5,11,.82);
  backdrop-filter:blur(7px);animation:bkfade .22s ease}
.bk-overlay[hidden]{display:none}
@keyframes bkfade{from{opacity:0}to{opacity:1}}
.bk-panel{position:relative;width:min(620px,100%);border:1px solid var(--hair-2);border-radius:2px;
  background:linear-gradient(180deg,rgba(31,33,35,.97),rgba(8,10,12,.99));
  padding:clamp(26px,4vw,42px);box-shadow:0 40px 90px -30px rgba(0,0,0,.9);
  animation:bkrise .28s cubic-bezier(.2,.8,.3,1)}
@keyframes bkrise{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
.bk-panel::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:linear-gradient(90deg,transparent,rgba(238,241,244,.34) 50%,transparent)}
.bk-panel h2{font-family:"Chakra Petch";font-weight:700;text-transform:uppercase;
  font-size:clamp(24px,3.6vw,34px);line-height:1.04;margin:12px 0 0}
.bk-intro{color:var(--silver);font-size:15.5px;margin:14px 0 24px;max-width:52ch}
.bk-close{position:absolute;top:14px;right:14px;width:40px;height:40px;display:inline-flex;
  align-items:center;justify-content:center;background:none;border:1px solid var(--hair);
  border-radius:2px;color:var(--metal-mid);cursor:pointer;transition:color var(--dur-1),border-color var(--dur-1)}
.bk-close:hover{color:var(--metal-lt);border-color:var(--metal-dim)}
.bk-close svg{width:17px;height:17px}

/* call path — deliberately the loudest thing in the dialog */
.bk-call{display:flex;align-items:center;gap:16px;text-decoration:none;
  border:1px solid var(--metal-dim);border-radius:2px;padding:16px 20px;
  background:linear-gradient(180deg,rgba(236,240,244,.14),rgba(236,240,244,.05));
  transition:border-color var(--dur-2) var(--ease),transform var(--dur-2) var(--ease)}
.bk-call:hover{border-color:var(--metal-mid);transform:translateY(-2px)}
.bk-call svg{width:26px;height:26px;color:var(--metal-mid);flex:none}
.bk-call b{display:block;font-family:"Chakra Petch";font-weight:700;font-size:20px;color:var(--pearl)}
.bk-call em{display:block;font-style:normal;font-family:"IBM Plex Mono";font-size:10px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--metal-lt);margin-top:4px}

.bk-or{display:flex;align-items:center;gap:14px;margin:22px 0 4px}
.bk-or i{flex:1;height:1px;background:var(--hair)}
.bk-or span{color:var(--muted);letter-spacing:.16em}
.bk-form .form{margin-top:18px}
.bk-form label .opt{text-transform:none;letter-spacing:0;color:var(--muted);font-size:10px}
.bk-fine{display:block;margin-top:18px;color:var(--muted);letter-spacing:.1em;font-size:10px}
body.bk-open{overflow:hidden}
@media (max-width:560px){
  .bk-call{padding:14px 16px;gap:12px}
  .bk-call b{font-size:17px}
}
@media (prefers-reduced-motion: reduce){
  .bk-overlay,.bk-panel{animation:none}
}

/* ---- brand band: lift content off the constellation ----
   Shadows alone don't fix line-through-letterform; each block gets a soft
   radial backing so the drawing fades out beneath it, then a shadow for edge. */
#brand-story .split > *{position:relative}
#brand-story .split > *::before{
  content:"";position:absolute;inset:-14% -10%;z-index:-1;pointer-events:none;
  background:radial-gradient(68% 62% at 50% 50%,
    rgba(21,23,25,.94) 0%, rgba(21,23,25,.86) 38%, rgba(21,23,25,.55) 64%, rgba(21,23,25,0) 84%)}

/* the mark gets a deeper well, since the truss apex crosses right behind it */
#brand-story .brand-art::before{inset:-18% -14%;
  background:radial-gradient(62% 58% at 50% 46%,
    rgba(21,23,25,.96) 0%, rgba(21,23,25,.9) 40%, rgba(21,23,25,.6) 66%, rgba(21,23,25,0) 86%)}
#brand-story .brand-art img{
  filter:drop-shadow(0 0 46px rgba(236,240,244,.22)) drop-shadow(0 10px 34px rgba(0,0,0,.85))}

/* type edges */
#brand-story h2{text-shadow:0 2px 20px rgba(21,23,25,.95),0 1px 3px rgba(21,23,25,.95)}
#brand-story p{text-shadow:0 1px 14px rgba(21,23,25,.95),0 1px 2px rgba(21,23,25,.85)}
#brand-story .sheet .mono{text-shadow:0 1px 10px rgba(21,23,25,.95)}
#brand-story .sheet i{opacity:.7}

/* pull the drawing back a touch where it crosses the middle of the panel */
#brand-story .constellation{
  -webkit-mask-image:radial-gradient(125% 95% at 50% 45%,#000 30%,rgba(0,0,0,.55) 58%,transparent 88%);
          mask-image:radial-gradient(125% 95% at 50% 45%,#000 30%,rgba(0,0,0,.55) 58%,transparent 88%)}

/* ---- human check: put the missing piece back in the roof ----------------
   Built by site.js, so it stays hidden until the script runs — a visitor with
   JS disabled never sees a puzzle they could not solve, and the form keeps
   working for them. */
.puzzle{border:1px solid var(--hair);border-radius:2px;padding:16px 16px 14px;
  background:linear-gradient(160deg,rgba(31,33,35,.55),rgba(10,12,14,.55))}
.pz-top{display:flex;align-items:center;justify-content:space-between;gap:12px}
.pz-state{font-size:10.5px;letter-spacing:.16em;color:var(--metal-mid)}
.pz-state[data-wrong]{color:#F0A6A6}
.pz-ask{margin:8px 0 12px;font-size:14.5px;color:var(--silver)}
.pz-stage{display:flex;justify-content:center}
.pz-roof{width:100%;max-width:300px;height:auto;overflow:visible}

/* the socket the piece drops into */
.pz-socket{fill:rgba(255,255,255,.04);stroke:var(--metal-mid);stroke-width:1.4;
  stroke-dasharray:4 4;opacity:.85;transition:fill var(--dur-1) var(--ease),opacity var(--dur-1) var(--ease)}
.pz-socket.over{fill:rgba(236,240,244,.18);opacity:1}
.pz-roof .body{fill:rgba(255,255,255,.05);stroke:var(--hair);stroke-width:1}
.pz-roof .deck{fill:rgba(144,146,148,.16);stroke:rgba(198,203,209,.5);stroke-width:1.1}
.pz-roof .filled{fill:var(--metal-mid);stroke:var(--metal-mid);stroke-width:1.1;
  animation:pzSnap .32s cubic-bezier(.2,1.4,.4,1)}
@keyframes pzSnap{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:none}}

.pz-tray{display:flex;gap:10px;margin-top:14px;flex-wrap:wrap}
.pz-piece{flex:0 0 auto;width:84px;height:58px;padding:0;cursor:grab;
  background:rgba(255,255,255,.03);border:1px solid var(--hair);border-radius:2px;
  display:grid;place-items:center;touch-action:none;
  transition:border-color var(--dur-1) var(--ease),background var(--dur-1) var(--ease),transform var(--dur-1) var(--ease)}
.pz-piece:hover:not(:disabled){border-color:var(--metal-mid);background:rgba(236,240,244,.08)}
.pz-piece:focus-visible{outline:2px solid var(--metal-mid);outline-offset:2px}
.pz-piece svg{width:60px;height:42px;overflow:visible}
.pz-piece polygon{fill:rgba(236,240,244,.85);stroke:var(--metal-mid);stroke-width:1.2;
  vector-effect:non-scaling-stroke}
.pz-piece.dragging{opacity:.35;cursor:grabbing}
.pz-piece.used{visibility:hidden}
.pz-piece:disabled{cursor:default;opacity:.4}
.pz-piece.nope{animation:pzNope .32s ease}
@keyframes pzNope{0%,100%{transform:none}25%{transform:translateX(-5px)}
  75%{transform:translateX(5px)}}

/* the piece that follows the pointer while dragging */
.pz-ghost{position:fixed;z-index:200;pointer-events:none;width:84px;height:58px;
  display:grid;place-items:center;opacity:.95;
  filter:drop-shadow(0 8px 18px rgba(0,0,0,.55))}
.pz-ghost svg{width:60px;height:42px;overflow:visible}
.pz-ghost polygon{fill:var(--metal-mid);stroke:var(--metal-mid);stroke-width:1.2}

.puzzle.solved{border-color:rgba(236,240,244,.5)}
.puzzle.solved .pz-tray,.puzzle.solved .pz-ask{display:none}
.pz-reset{margin-top:12px;background:none;border:0;padding:0;cursor:pointer;
  font-family:"IBM Plex Mono",monospace;font-size:10.5px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);text-decoration:underline}
.pz-reset:hover{color:var(--metal-mid)}
@media (max-width:520px){.pz-piece{width:72px;height:52px}.pz-piece svg{width:52px;height:36px}}
@media (prefers-reduced-motion:reduce){
  .pz-roof .filled,.pz-piece.nope{animation:none}}

/* ---- animated card marks (About · how we operate) ----------------------
   Same blueprint language as the hero drawing: blue outlines that draw
   themselves in, then a gold accent that keeps moving. They start when the
   card scrolls into view rather than on load, because these sit well below
   the fold and would otherwise finish before anyone saw them — the .in class
   comes from the same IntersectionObserver that drives .reveal. */
.cardfx{width:48px;height:40px;display:block;margin:0 0 4px;overflow:visible}
.cardfx .l{fill:none;stroke:#919395;stroke-width:1.3;opacity:.7;
  stroke-linecap:round;stroke-linejoin:round}
.cardfx .trace,.cardfx .chk,.cardfx .ring{fill:none;stroke:var(--metal-mid);
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.cardfx .dot{fill:var(--metal-mid)}
.card:hover .cardfx .l{opacity:.95}

@media (prefers-reduced-motion:no-preference){
  .cardfx .l{stroke-dasharray:var(--l,90);stroke-dashoffset:var(--l,90)}
  .reveal.in .cardfx .l{animation:draw 1s cubic-bezier(.4,0,.2,1) forwards}

  /* a short gold dash running the roof slope, and the pen along the line */
  .cardfx .trace{stroke-dasharray:7 var(--t,45);stroke-dashoffset:0;opacity:0}
  .reveal.in .cardfx .trace{opacity:1;
    animation:fxTrace 2.8s linear .9s infinite}
  @keyframes fxTrace{to{stroke-dashoffset:var(--td,-52)}}

  /* the aperture turning */
  .cardfx .ring{stroke-dasharray:5 6;transform-origin:24px 22px;opacity:0}
  .reveal.in .cardfx .ring{opacity:.95;animation:fxSpin 7s linear .9s infinite}
  @keyframes fxSpin{to{transform:rotate(360deg)}}

  .cardfx .dot{opacity:.25}
  .reveal.in .cardfx .dot{animation:fxBlink 2.6s ease 1s infinite}
  @keyframes fxBlink{0%,55%{opacity:.25}68%{opacity:1}100%{opacity:.25}}

  /* the warranty tick draws once, then breathes */
  .cardfx .chk{stroke-dasharray:24;stroke-dashoffset:24}
  .reveal.in .cardfx .chk{
    animation:draw .75s cubic-bezier(.4,0,.2,1) .5s forwards,
              fxBreathe 3.4s ease 1.5s infinite}
  @keyframes fxBreathe{0%,100%{opacity:1}50%{opacity:.42}}
}

/* The second line of the name story. Sits between the h2 and the copy, so it
   is sized to read as a subtitle rather than compete with ASTERA above it. */
.name-line{font-family:"Chakra Petch";font-weight:600;text-transform:uppercase;
  letter-spacing:.10em;font-size:clamp(15px,1.7vw,19px);color:var(--silver);
  margin:10px 0 20px}

/* Inline link inside body copy. Gold is the site's action colour, so a link in
   running text earns it — but the underline stays, so it still reads as a link
   for anyone who can't pick the gold out from the body grey. */
.ilink{color:var(--metal-mid);text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px;text-decoration-color:rgba(236,240,244,.45);
  transition:text-decoration-color var(--dur-1) var(--ease)}
.ilink:hover{text-decoration-color:var(--metal-mid)}

/* ---- long-form guide pages -------------------------------------------
   The system pages carry short, dense copy in .rows and .lede. These guides
   are the other thing: several thousand words of headings, lists and tables
   that have to stay readable. Measure is capped at 72ch — past that the eye
   loses the line return on a dark background. */
.prose{max-width:72ch}
.prose > h2{font-family:"Chakra Petch";font-weight:700;text-transform:uppercase;
  font-size:clamp(23px,2.7vw,32px);letter-spacing:.01em;line-height:1.12;
  margin:58px 0 0;padding-top:30px;color:var(--pearl);
  border-top:1px solid var(--hair);position:relative}
.prose > h2::before{content:"";position:absolute;top:-1px;left:0;width:44px;height:2px;
  background:var(--metal-mid)}
.prose > h2:first-child{margin-top:0;padding-top:0;border-top:0}
.prose > h2:first-child::before{content:none}
.prose > h3{font-family:"Chakra Petch";font-weight:600;font-size:clamp(18.5px,2vw,20px);
  letter-spacing:.02em;margin:42px 0 0;padding-left:17px;color:var(--metal-lt);position:relative}
.prose > h3::before{content:"";position:absolute;left:1px;top:.4em;width:7px;height:7px;
  background:var(--metal-mid);transform:rotate(45deg)}
.prose p{color:var(--silver);font-size:clamp(16px,1.5vw,17.5px);line-height:1.72;margin:16px 0 0}
.prose ul,.prose ol{margin:18px 0 0;padding-left:0;list-style:none;display:grid;gap:11px}
.prose ol{counter-reset:step}
.prose li{position:relative;padding-left:26px;color:var(--silver);
  font-size:clamp(15.5px,1.45vw,17px);line-height:1.6}
.prose ul > li::before{content:"";position:absolute;left:4px;top:.62em;
  width:6px;height:6px;background:var(--metal-mid);transform:rotate(45deg)}
.prose ol > li{counter-increment:step;padding-left:32px}
.prose ol > li::before{content:counter(step);position:absolute;left:0;top:0;
  font-family:"IBM Plex Mono";font-size:12px;color:var(--metal-mid);line-height:1.9}
.prose strong{color:var(--metal-lt);font-weight:600}
.prose a:not(.btn){color:var(--metal-mid);text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px;text-decoration-color:rgba(236,240,244,.4)}
.prose a:not(.btn):hover{text-decoration-color:var(--metal-mid)}

/* Wide comparison tables scroll rather than squashing the page. */
.prose .table-scroll{margin-top:26px}
.prose table{width:100%;border-collapse:collapse;min-width:640px;font-size:15px}
.prose th,.prose td{text-align:left;padding:13px 16px;border-bottom:1px solid var(--hair);
  vertical-align:top;color:var(--silver)}
.prose th{font-family:"IBM Plex Mono";font-size:10.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);font-weight:400;white-space:nowrap}
.prose tbody tr:first-child td{color:var(--metal-lt)}

/* pull-out closing block */
.prose .wrapup{margin-top:52px;border:1px solid var(--hair);border-left:2px solid var(--metal-mid);
  border-radius:2px;padding:26px 28px;background:rgba(31,33,35,.4)}
.prose .wrapup h2{margin-top:0}

/* "Read the guide" link at the foot of a services row. */
.rowlink{display:inline-flex;align-items:center;gap:8px;margin-top:20px;
  font-family:"IBM Plex Mono";font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--metal-mid);text-decoration:none;
  border-bottom:1px solid rgba(236,240,244,.3);padding-bottom:4px;
  transition:border-color var(--dur-1) var(--ease),gap var(--dur-1) var(--ease)}
.rowlink:hover{border-bottom-color:var(--metal-mid);gap:12px}
.rowlink span{font-size:13px;line-height:1}

/* ---- the star on inspection CTAs ----------------------------------------
   Injected by star_ctas() in build.py into every gold button that offers an
   inspection. A slow glint — quarter turn while it breathes — that hurries up
   under the cursor. Static when motion is turned down. */
.btn .bstar{width:13px;height:13px;flex:none;color:var(--copper-deep)}
@media (prefers-reduced-motion:no-preference){
  .btn .bstar{animation:bstarGlint 2.6s ease-in-out infinite;transform-origin:50% 50%}
  .btn:hover .bstar{animation-duration:1.1s}
  @keyframes bstarGlint{0%,100%{transform:scale(1) rotate(0deg)}
    50%{transform:scale(.76) rotate(90deg)}}
}

/* Slides 2-4 carry h2s so the page has exactly one h1 (the active slide set
   is presentation, not document outline) — styled identically to the h1. */
.slide-copy h2.slide-h{font-size:clamp(38px,6.4vw,76px);text-transform:uppercase;
  margin:22px 0 0;letter-spacing:-.01em;font-weight:700}


/* The plate is near-white, so a pearl focus ring would be invisible on it.
   The ring stays pearl for the page at large and the plate gets a stone
   separator, which reads against both the light face and the dark ground. */
.btn-gold:focus-visible,.bk-call:focus-visible{
  outline:2px solid var(--pearl);outline-offset:3px;
  box-shadow:0 0 0 3px var(--stone),0 0 0 4px rgba(224,138,79,.45)}

/* .gold is retired. It was the second half of nearly every h2 on the
   site — 26 markup sites — and it is the largest single population that
   would otherwise have turned green. Emphasis becomes a VALUE contrast
   against --silver headings rather than a HUE contrast, which is the
   more architectural gesture anyway and is what lets the accent be spent
   in exactly one place. The class name stays so markup is untouched. */
.gold{
  background:linear-gradient(180deg,#FFFFFF 4%,#D9DBDD 34%,#97999B 54%,#EDEFF1 78%,#B9BBBD 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent}

/* Persistent chrome never holds the accent.
   The header is sticky and the mobile call bar is fixed, so either one being
   green guarantees two green things share a viewport the moment a page CTA
   scrolls into view — and "one controlled accent" is a viewport claim, not a
   page claim. These keep .btn-gold (so star_ctas() still finds them and the
   brand star survives) and are demoted to a pearl outline here instead. */
/* The quiet CTA's label is struck copper. The ramp is built the way the
   machined-metal headings are — highlight, body, a darker turn, a lift back —
   but it is clamped so its DARKEST stop is --copper itself at 6.78:1. It must
   not reach --copper-deep: that value is legal on the pearl plate and nowhere
   else, and background-clip:text puts the last stop at the BOTTOM of every
   glyph, which is exactly how .slide-h .au ended up at 1.77:1 over a photo.
   Measured here against the call bar's own ground: 6.2:1 at the worst stop,
   over the 4.5:1 floor for 13-14px type. */
.btn-quiet{background:none;
  background-image:linear-gradient(180deg,#FFD9BC 4%,var(--copper-hot) 34%,#EFA167 58%,var(--copper) 74%,#FFCFA4 96%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  border:1px solid var(--edge-ui);box-shadow:none}
.btn-quiet:hover{background-color:rgba(224,138,79,.08);border-color:rgba(224,138,79,.55);
  box-shadow:none}
/* the star keeps its own light — it is the source the label is lit by */
.btn-quiet .bstar{color:var(--copper-hot);-webkit-text-fill-color:var(--copper-hot)}


/* With motion reduced the animations above never run, so anything that rests
   hidden would stay hidden permanently. Restore the finished state. */
@media (prefers-reduced-motion:reduce){
  .detail,.secmark,.scalebar,.beam{opacity:1}
  .roof-rim{stroke-dashoffset:0}
}

/* ============================================================
   TABLET — the 761-1200px band, which had almost nothing.
   The ladder was real only at 1024 (layout) and 760 (nav), so
   between them the site ran desktop structure in a body 150-210px
   too narrow, and below 1024 it ran phone layout in a body
   300-600px too wide.
   ============================================================ */

/* WORST FAILURE, and it is above the old breakpoints so nothing caught it:
   .rows is a 110px + 1fr + 1fr grid built for a 1200px wrap, but on the five
   system pages it is nested inside the 1.15fr column of .split-wide. From
   1025px to full desktop the body copy ran in a 143-170px column — sixteen
   characters per line. The page was literally more readable on a phone.
   Collapse the nested variant to title-over-copy until there is room. */
/* The nested variant, at every width. .row is a 110px + 1fr + 1fr grid built
   for a 1200px wrap, but inside .split-wide's 1.15fr column it never has the
   room: the measure was 143-170px — about 22 characters per line — from
   1025px all the way to 1440. Capping this at 1200px, as a first attempt did,
   left the desktop case just as unreadable. */
/* The min-width floor is load-bearing, not tidiness: `.split-wide .row` is
   (0,2,0) and would otherwise beat the phone rule `.row{...:1fr}` at (0,1,0)
   whatever the source order, dragging the 110px number gutter back onto
   phones — measured at 375px, copy squeezed to 211px behind dead space. */
@media (min-width:761px){
  .split-wide .row{grid-template-columns:110px 1fr;gap:14px clamp(14px,2.4vw,28px)}
  .split-wide .row > :last-child{grid-column:2}
}

/* The desktop header needs 888px of content and only gets 676-795px from
   ~1000px down, so flex-shrink crushed the lockup from its 4.17 aspect to
   1.84 — a 56% horizontal squash of brand artwork — wrapped "Storm damage",
   and pushed the CTA to two lines inside a 72px bar. Collapse to the burger
   while the header still fits. */
@media (max-width:1000px){
  .menu{display:none}
  .burger{display:inline-flex}
  .nav .btn:not(.burger){display:none}
  .brand img{height:34px}
}


/* The panel button goes full-width when .sticky-side unsticks at 1024; as a
   684px pearl slab it would be the most conspicuous object on the page. */
@media (max-width:1024px){
  .panel .btn-gold{max-width:340px}
}

/* Three-up galleries at 216-236px thumbnails are too small to read; two-up
   through the tablet band. The inline style build.py writes on .strip3 is
   handled at its source — an inline attribute would beat this rule. */
@media (min-width:761px) and (max-width:1024px){
  .strip3{grid-template-columns:repeat(2,1fr)}
  .cards,.mats,.quotes{grid-template-columns:repeat(2,1fr)}
  .prose{max-width:64ch}
}

/* Column count as an attribute, not an inline style. As `style=` it beat every
   media query in this file, freezing the galleries at three 216px thumbnails
   on tablet no matter what the breakpoints said. */
.strip3[data-cols="2"]{grid-template-columns:repeat(2,1fr)}
.strip3[data-cols="3"]{grid-template-columns:repeat(3,1fr)}
@media (max-width:1024px){
  .strip3[data-cols="3"]{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:760px){
  .strip3[data-cols="2"],.strip3[data-cols="3"]{grid-template-columns:1fr}
}

/* Layout and colour that used to ship as INLINE STYLES, which no media query
   can override. This file has now produced four such bugs (.strip3, .steps,
   .feature, .f-grid) plus a hardcoded pre-retheme blue in the footer, so the
   rule is: no layout or palette values in a style attribute. */
.f-blurb{color:var(--muted);font-size:15px;max-width:34ch}
.feature-wide{grid-template-columns:.85fr 1.25fr;margin-top:clamp(48px,6vw,88px)}
.f-grid-3{grid-template-columns:repeat(3,1fr);text-align:left}
@media (max-width:1024px){
  .feature-wide{grid-template-columns:1fr}
}
@media (max-width:760px){
  .f-grid-3{grid-template-columns:1fr}
}

/* A single specular sweep across the plate on hover — the one thing that
   makes brushed metal read as metal rather than as a light grey rectangle.
   It animates transform, not background-position, so it composites instead
   of repainting, and it is inside no-preference so reduced motion gets a
   plain plate. */
.btn-gold::after{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(105deg,transparent 38%,rgba(255,255,255,.85) 50%,transparent 62%);
  transform:translateX(-120%);opacity:0}
@media (prefers-reduced-motion:no-preference){
  .btn-gold:hover::after{opacity:1;transition:transform .62s ease,opacity .2s ease;
    transform:translateX(120%)}
}

/* The drawer's link rule is (0,1,1) and beats .btn-gold at (0,1,0) on COLOUR
   as well as display — the same trap that stacked the star above the label.
   The :not(.btn-quiet) is load-bearing: the drawer's CTA is a QUIET button
   (outline on dark), and .drawer .btn-gold at (0,2,0) would otherwise beat
   .btn-quiet at (0,1,0) and paint dark plate-ink onto a dark face. */
.drawer .btn-gold:not(.btn-quiet){color:var(--plate-ink)}

/* ---- the six "why" icons ------------------------------------------------
   Each one animates the thing it describes rather than just appearing:
   time passes, the shield is checked, courses stack, the tick is drawn, the
   pin lands, the lens sweeps. They run once when the card scrolls in (the
   .reveal/.in observer already drives these cards) and replay on hover.

   Everything that rests hidden or displaced is restored under
   prefers-reduced-motion, because the global animation:none kill would
   otherwise freeze it that way permanently. */
.ic .course,.ic .check,.ic .body,.ic .dial,.ic .lens,.ic .handle{
  vector-effect:non-scaling-stroke}

@media (prefers-reduced-motion:no-preference){
  /* clock — two decades go by */
  .ic-clock .hand-h,.ic-clock .hand-m{transform-origin:16px 16px}
  .reveal.in .ic-clock .hand-h{animation:icSpin 2.2s cubic-bezier(.35,0,.2,1) .1s both}
  .reveal.in .ic-clock .hand-m{animation:icSpin 1.1s cubic-bezier(.35,0,.2,1) .1s both}
  .card:hover .ic-clock .hand-h{animation:icSpin 1.6s cubic-bezier(.35,0,.2,1) both}
  .card:hover .ic-clock .hand-m{animation:icSpin .8s cubic-bezier(.35,0,.2,1) both}
  .ic-clock .ticks{opacity:0}
  .reveal.in .ic-clock .ticks{animation:icFade .6s ease .5s forwards}
  @keyframes icSpin{from{transform:rotate(0)}to{transform:rotate(720deg)}}

  /* shield — outline draws, then the check is struck */
  .ic-shield .body{stroke-dasharray:78;stroke-dashoffset:78}
  .ic-shield .check{stroke-dasharray:17;stroke-dashoffset:17}
  .reveal.in .ic-shield .body{animation:icDraw .9s ease .05s forwards}
  .reveal.in .ic-shield .check{animation:icDraw .45s ease .7s forwards}
  .card:hover .ic-shield .check{animation:icDraw .4s ease both}

  /* materials — courses lay up from the deck */
  .ic-stack .course{opacity:0;transform:translateY(5px)}
  .reveal.in .ic-stack .c4{animation:icLay .5s ease .05s forwards}
  .reveal.in .ic-stack .c3{animation:icLay .5s ease .18s forwards}
  .reveal.in .ic-stack .c2{animation:icLay .5s ease .31s forwards}
  .reveal.in .ic-stack .c1{animation:icLay .5s ease .44s forwards}
  @keyframes icLay{to{opacity:1;transform:none}}

  /* satisfaction — the tick is drawn, not revealed */
  .ic-check .check{stroke-dasharray:20;stroke-dashoffset:20}
  .ic-check .ring{stroke-dasharray:77;stroke-dashoffset:77}
  .reveal.in .ic-check .ring{animation:icDraw .8s ease .05s forwards}
  .reveal.in .ic-check .check{animation:icDraw .45s ease .55s forwards}
  .card:hover .ic-check .check{animation:icDraw .4s ease both}

  /* local — the pin lands and the ground answers */
  .ic-pin .pin{transform:translateY(-7px);opacity:0}
  .ic-pin .ripple{opacity:0;transform-origin:16px 27.4px}
  .reveal.in .ic-pin .pin{animation:icDrop .6s cubic-bezier(.3,1.5,.5,1) .1s forwards}
  .reveal.in .ic-pin .ripple{animation:icRipple 1.5s ease .6s infinite}
  @keyframes icDrop{to{transform:none;opacity:1}}
  @keyframes icRipple{0%{opacity:.65;transform:scale(.45)}
    70%{opacity:0;transform:scale(1.25)}100%{opacity:0}}

  /* inspection — the lens sweeps the surface */
  .ic-lens .glass{transform-origin:14px 14px}
  .reveal.in .ic-lens .glass{animation:icSweep 2.6s ease-in-out .4s infinite}
  .card:hover .ic-lens .glass{animation-duration:1.2s}
  .ic-lens .scan{stroke-dasharray:16;stroke-dashoffset:16}
  .reveal.in .ic-lens .scan{animation:icDraw .5s ease .5s forwards}
  @keyframes icSweep{0%,100%{transform:translate(0,0)}
    35%{transform:translate(3.2px,-2px)}70%{transform:translate(-2.4px,1.6px)}}

  @keyframes icDraw{to{stroke-dashoffset:0}}
  @keyframes icFade{to{opacity:1}}
}

@media (prefers-reduced-motion:reduce){
  .ic-shield .body,.ic-shield .check,.ic-check .check,.ic-check .ring,
  .ic-lens .scan{stroke-dashoffset:0}
  .ic-stack .course,.ic-pin .pin,.ic-clock .ticks{opacity:1;transform:none}
  .ic-pin .ripple{opacity:.5}
}
