/* ==========================================================================
   SurfTech, a division of Euclid Refinishing Company, Inc.
   Design system. White-dominant enterprise. Montserrat, per the brand sheet.

   EVERY COLOUR HERE IS SAMPLED FROM SURFTECH'S OWN ARTWORK.
   #00AEEF  SurfTech Blue, stated on the brand sheet.
   #003F91  the panel blue behind every brochure header ("COATING OPTIONS",
            "WHY CHOOSE SURFTECH", "PERFORMANCE BENEFITS") and behind the copy
            block on the Euclid Refinishing one pager.
   #0B0B0A  the black bands across the top of the brochure and the bottom of
            the one pager.
   Nothing in this block is a tint invented by darkening the brand blue.

   ONE COLOUR RULE, and it governs most of the decisions below.
   #00AEEF measures 2.53:1 against white, so it can never carry text or a
   small icon on a light surface. It is the accent: rules, the nav marker,
   the coating swatches, graphics, the on-dark highlight and the logo. Anything
   read on white uses --blue-deep #003F91, which measures 9.9:1 and is a real
   brand colour.

   SHAPE LANGUAGE, also from the artwork. Nothing is a pill: the brochure has
   no rounded shapes anywhere. Panels are square, icons sit in circular badges,
   and rules are hairlines rather than shadows. Decoration that carries no
   meaning has been removed rather than restyled.
   Run `python src/_contrast.py` after touching any colour in this block.
   ========================================================================== */

/* ------------------------------------------------------------------ 1. Fonts */
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('../fonts/montserrat-latin-var.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('../fonts/montserrat-latin-ext-var.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* A metric-matched stand-in. Montserrat sets about 9.2% wider than Arial at the
   same size, so without this the headline re-wraps when the webfont swaps in
   and the whole page shifts under it. Measured, not guessed: the value is tuned
   against the rendered fallback, not against Arial's nominal metrics, because
   which local face resolves varies by platform. */
@font-face {
  font-family: 'Montserrat Fallback';
  src: local('Arial'), local('Helvetica'), local('Liberation Sans');
  size-adjust: 114.8%;
  ascent-override: 88.6%;
  descent-override: 23.0%;
  line-gap-override: 0%;
}

/* ----------------------------------------------------------------- 2. Tokens */
:root {
  /* Brand, sampled from the SurfTech artwork */
  --blue:        #00AEEF;   /* SurfTech Blue. Accent, rules and swatches only. */
  --blue-deep:   #003F91;   /* The brochure panel blue. 9.9:1 on white, so it
                               carries links, headings and button text too.   */
  --blue-deep-h: #002F6E;   /* pressed and hover, 12.9:1 with white on it     */
  --black:       #000000;
  --white:       #FFFFFF;

  --blue-300:    #6FD3F7;   /* on-dark highlight, 5.8:1 on --blue-deep        */
  --blue-100:    #C6DAF2;
  --blue-050:    #EDF3FA;

  /* Ink and surfaces. Blue-leaning neutrals, not the warm greys of a
     different brand: every one of these sits under SurfTech Blue. */
  --ink:         #0B0B0A;   /* the printed black band, 19.7:1 on white        */
  --ink-70:      #4A5568;   /* 7.5:1  */
  --ink-50:      #5E6B7E;   /* 5.4:1 on white, 4.9:1 on the blue tint         */
  --paper:       #F9FBFD;   /* near-white. Cool enough to read as a second
                               surface, not so cool that half the page looks
                               tinted. Judged against the alternating rhythm,
                               not picked from a scale.                         */
  --line:        #D5DFEC;   /* blue hairline. The brochure rules are blue.    */
  --line-control:#7C889E;   /* 3.2:1 on white. Anything that bounds a control */
  --line-soft:   #E8EFF7;

  /* Typography. One family, per the brand sheet. Weight and tracking carry
     the difference between a headline and a paragraph. */
  --font-head: "Montserrat", "Montserrat Fallback", Arial, sans-serif;
  --font-body: "Montserrat", "Montserrat Fallback", Arial, sans-serif;

  --fs-display: clamp(2.85rem, 6.6vw, 5.6rem);
  --fs-h1:      clamp(2.4rem, 5.2vw, 4.4rem);
  --fs-h2:      clamp(2rem, 4.2vw, 3.5rem);
  --fs-h3:      clamp(1.35rem, 1.9vw, 1.85rem);
  --fs-h4:      clamp(1.05rem, 1.25vw, 1.25rem);
  --fs-deck:    clamp(1.03rem, 1.3vw, 1.3rem);
  --fs-body:    1.0313rem;
  --fs-small:   0.9375rem;
  --fs-micro:   0.8125rem;

  /* Space */
  --gutter:     clamp(1.25rem, 4vw, 3rem);
  --maxw:       1240px;
  --maxw-text:  62ch;
  --sec-y:      clamp(5rem, 12vh, 10rem);

  /* Shape. The brochure and both one pagers contain no rounded shape at all:
     panels are square, plates are cut at an angle, icons are in circles. */
  --radius:      2px;
  --radius-sm:   2px;
  --radius-pill: 2px;      /* kept as a name so components need no rewrite   */
  --cut:         14px;     /* the angled right edge on a panel plate         */

  /* Elevation, and there is almost none. The artwork carries depth with
     hairlines and hard edges, so a card rests flat and lifts only on hover. */
  --shadow-sm:  0 1px 0 rgba(0,63,145,.06);
  --shadow-md:  0 1px 0 rgba(0,63,145,.06), 0 10px 24px -14px rgba(0,63,145,.30);
  --shadow-lg:  0 1px 0 rgba(0,63,145,.08), 0 24px 48px -22px rgba(0,63,145,.34);

  --ease:       cubic-bezier(.22, 1, .36, 1);
  --ease-io:    cubic-bezier(.4, 0, .2, 1);
  --nav-h:      70px;

  color-scheme: light;
}

/* ------------------------------------------------------------ 3. Reset/base */
*, *::before, *::after { box-sizing: border-box; }
/* The UA stylesheet gives <figure> `margin: 1em 40px`. Every photograph on this
   site is in a figure, so that was quietly insetting 50 of them by 80px: on a
   390px phone the split and banner images rendered 270px wide inside a 350px
   column, which is 23% of the width spent on nothing. .band already zeroed it;
   the other two classes did not. Widths come from the grid cell and the wrap,
   never from a UA default. .section-photo re-declares its own margin-bottom. */
figure { margin: 0; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 16px);
}
[id] { scroll-margin-top: calc(var(--nav-h) + 16px); }
body {
  margin: 0;
  background: var(--white);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
img, svg, video, canvas { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
:focus-visible {
  outline: 2.5px solid var(--blue-deep);
  outline-offset: 3px;
  border-radius: 4px;
}
/* --blue-deep is 2.1:1 on the CTA band and 2.2:1 on the black footer, so the
   ring would vanish exactly where the primary conversions are. --blue-300 is
   5.8:1 on the deep band and 12.6:1 on black. */
.cta-band :focus-visible,
.section--deep :focus-visible,
.footer :focus-visible { outline-color: var(--blue-300); }

/* B2: an author-origin `display` beats the UA sheet's [hidden] rule, so
   `el.hidden = true` on .hero__vidctl left it on screen and
   clickable. This restores what the attribute is supposed to mean. */
[hidden] { display: none !important; }
::selection { background: var(--blue-100); color: var(--blue-deep); }

.skip-link {
  position: absolute; left: 12px; top: -100px; z-index: 999;
  background: var(--blue-deep); color: #fff;
  padding: 12px 20px; border-radius: var(--radius-sm);
  font-weight: 600; transition: top .2s var(--ease);
}
.skip-link:focus { top: 12px; }
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ------------------------------------------------------------ 4. Typography */
h1, h2, h3, h4, h5 {
  font-family: var(--font-head);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.032em;
  margin: 0;
  text-wrap: balance;
}
h3, h4 { font-weight: 700; letter-spacing: -0.016em; line-height: 1.18; }
p { margin: 0; text-wrap: pretty; }

.display { font-size: var(--fs-display); line-height: .96; letter-spacing: -0.042em; font-weight: 800; }
.h1 { font-size: var(--fs-h1); }
.h2 { font-size: var(--fs-h2); }
.h3 { font-size: var(--fs-h3); }
.h4 { font-size: var(--fs-h4); }


.deck {
  font-size: var(--fs-deck);
  line-height: 1.6;
  color: var(--ink-70);
  max-width: 56ch;
  font-weight: 400;
}
.deck--center { margin-inline: auto; }
.lede { font-size: clamp(1.2rem,1.9vw,1.5rem); line-height: 1.5; color: var(--ink); font-weight: 400; }
.text-blue { color: var(--blue-deep); }
.text-deep { color: var(--blue-deep); }
.text-muted { color: var(--ink-70); }
.tnum { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- 5. Layout */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.wrap--wide { max-width: 1480px; }
.wrap--narrow { max-width: 880px; }
.section { padding-block: var(--sec-y); position: relative; background: var(--white); }
.section--tight { padding-block: clamp(3rem, 6vh, 5rem); }
.section--paper { background: var(--paper); }
.section--tint { background: var(--blue-050); }
.section--deep { background: var(--blue-deep); color: #fff; }
.section--flush-top { padding-top: 0; }

.rule { height: 1px; background: var(--line); border: 0; margin: 0; }
.grid { display: grid; gap: clamp(1rem, 2vw, 1.75rem); }
.grid-2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.stack > * + * { margin-top: 1.1rem; }
.section-head { max-width: 800px; margin-bottom: clamp(2.75rem, 5.5vw, 5rem); }
/* p has margin:0 globally, so a deck under a headline needs its own gap.
   Without this the subhead sits hard against the h2 baseline. */
.section-head h1 + .deck,
.section-head h2 + .deck,
.page-head h1 + .deck,
.hero h1 + .deck { margin-top: 1.5rem; }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .deck { margin-inline: auto; }

/* -------------------------------------------------------------- 6. Buttons */
.btn {
  --btn-bg: var(--blue-deep);
  --btn-fg: #fff;
  display: inline-flex; align-items: center; justify-content: center; gap: .55em;
  background: var(--btn-bg); color: var(--btn-fg);
  font-family: var(--font-body); font-size: var(--fs-small); font-weight: 600;
  letter-spacing: -0.003em;
  padding: .95rem 1.65rem;
  border: 1px solid transparent; border-radius: var(--radius-pill);
  cursor: pointer; white-space: nowrap;
  transition: transform .32s var(--ease), background .22s var(--ease-io),
              box-shadow .32s var(--ease), border-color .22s var(--ease-io), color .22s var(--ease-io);
  will-change: transform;
}
/* Square, and plain. An earlier pass put a blue strip down the right edge of
   every button; it carried no meaning and read as decoration for its own sake. */
.btn:hover { background: var(--blue-deep-h); transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn:active { transform: translateY(0); }
.btn--ghost { --btn-bg: transparent; --btn-fg: var(--ink); border-color: var(--line-control); }
.btn--ghost:hover { --btn-bg: transparent; --btn-fg: var(--blue-deep); border-color: var(--blue-deep); box-shadow: none; }
.btn--deep { --btn-bg: var(--blue-deep); }
.btn--deep:hover { background: var(--blue-deep-h); }
.btn--white { --btn-bg: #fff; --btn-fg: var(--blue-deep); }
.btn--white:hover { background: #fff; box-shadow: 0 12px 28px -14px rgba(0,0,0,.5); }
.btn--onDeep-ghost { --btn-bg: transparent; --btn-fg: #fff; border-color: rgba(255,255,255,.34); }
.btn--onDeep-ghost:hover { --btn-bg: rgba(255,255,255,.09); --btn-fg: #fff; border-color: rgba(255,255,255,.62); box-shadow: none; }
.btn--lg { padding: 1.1rem 2.1rem; font-size: 1rem; }
.btn--sm { padding: .6rem 1.15rem; font-size: var(--fs-micro); }
.btn .ico { width: 1em; height: 1em; flex: none; }

.link-arrow {
  display: inline-flex; align-items: center; gap: .45em;
  font-weight: 600; font-size: var(--fs-small); color: var(--blue-deep);
  /* Keep the label and its arrow on one line. Without this a two-word label
     wraps and strands the arrow on a line of its own inside narrow cards. */
  white-space: nowrap;
}
.link-arrow .ico { transition: transform .32s var(--ease); }
.link-arrow:hover .ico { transform: translateX(4px); }

/* ------------------------------------------------------------------ 7. Nav */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  height: var(--nav-h);
  display: flex; align-items: center;
  background: rgba(255,255,255,.88);
  -webkit-backdrop-filter: saturate(150%) blur(14px);
  backdrop-filter: saturate(150%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: background .3s var(--ease-io), border-color .3s var(--ease-io), box-shadow .3s var(--ease-io);
}
.nav.is-stuck { background: #fff; border-bottom-color: var(--line); box-shadow: 0 1px 0 rgba(0,63,145,.05); }
.nav__inner {
  width: 100%; max-width: 1480px; margin-inline: auto; padding-inline: var(--gutter);
  display: flex; align-items: center; gap: clamp(1rem,3vw,2.5rem);
}
.nav__brand { display: flex; align-items: center; gap: .6rem; flex: none; }
.nav__brand img { height: 42px; width: auto; }
.nav__links { display: flex; align-items: center; gap: clamp(.5rem,1.6vw,1.5rem); margin-left: auto; }
.nav__link {
  position: relative; font-size: var(--fs-small); font-weight: 500; color: var(--ink);
  padding: .4rem .1rem; transition: color .2s var(--ease-io);
}
/* A plain SurfTech Blue rule. The nav is the one place a marker has a job to
   do, so it stays; everywhere else the decoration came out. */
.nav__link::after {
  content: ""; position: absolute; left: 0; bottom: -3px; width: 100%; height: 3px;
  background: var(--blue); opacity: 0;
  transition: opacity .22s var(--ease-io);
}
.nav__link:hover { color: var(--blue-deep); }
.nav__link:hover::after, .nav__link[aria-current="page"]::after { opacity: 1; }
.nav__link[aria-current="page"] { color: var(--blue-deep); font-weight: 600; }
.nav__actions { display: flex; align-items: center; gap: .75rem; flex: none; }
.nav__tel { font-size: var(--fs-small); font-weight: 600; color: var(--blue-deep); display: inline-flex; align-items: center; gap: .4rem; }
.nav__tel:hover { color: var(--blue-deep-h); }
.nav__tel svg { width: 15px; height: 15px; }

.nav__toggle {
  display: none; width: 44px; height: 44px; border: 0; background: none;
  align-items: center; justify-content: center; cursor: pointer;
}
.nav__toggle span { display: block; width: 22px; height: 2px; background: var(--ink); position: relative; transition: background .2s; }
.nav__toggle span::before, .nav__toggle span::after {
  content: ""; position: absolute; left: 0; width: 22px; height: 2px; background: var(--ink);
  transition: transform .34s var(--ease), top .2s var(--ease);
}
.nav__toggle span::before { top: -7px; }
.nav__toggle span::after  { top:  7px; }
.nav.is-open .nav__toggle span { background: transparent; }
.nav.is-open .nav__toggle span::before { top: 0; transform: rotate(45deg); }
.nav.is-open .nav__toggle span::after  { top: 0; transform: rotate(-45deg); }

/* The sheet is a SIBLING of .nav, never a child: .nav carries backdrop-filter,
   which would make it the containing block for a position:fixed descendant. */
.nav__sheet {
  position: fixed; inset: var(--nav-h) 0 0 0; z-index: 99;
  background: #fff; padding: 2rem var(--gutter) 3rem;
  display: flex; flex-direction: column; gap: .25rem;
  transform: translateY(-8px); opacity: 0; pointer-events: none; visibility: hidden;
  transition: opacity .28s var(--ease-io), transform .38s var(--ease), visibility 0s linear .38s;
  overflow-y: auto;
}
body.is-nav-open .nav__sheet {
  opacity: 1; transform: none; pointer-events: auto; visibility: visible;
  transition: opacity .28s var(--ease-io), transform .38s var(--ease), visibility 0s;
}
.nav__sheet a.nav__sheet-link {
  font-family: var(--font-head); font-size: 1.6rem; font-weight: 700; letter-spacing: -.025em;
  padding: .7rem 0; border-bottom: 1px solid var(--line-soft);
}
.nav__sheet .btn { margin-top: 1.5rem; }
.nav__sheet-meta { margin-top: auto; padding-top: 2rem; color: var(--ink-70); font-size: var(--fs-small); }

/* ----------------------------------------------------------------- 8. Hero */
.site-main { position: relative; z-index: 1; }

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-content: center;
  padding-block: calc(var(--nav-h) + 4rem) 5rem;
  background: var(--white);
}
.hero__media {
  position: absolute; inset: 0; z-index: 0; overflow: hidden;
  background: var(--paper);
}
/* Both children have to be taken out of flow. In normal flow they stack, the
   box is overflow:hidden, and the second one (the video) lands entirely below
   the visible area: it downloads and plays where nobody can see it. */
.hero__media img, .hero__media video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
/* The video ships with preload="none" and no poster, so until it has a frame it
   would paint an empty box over the still. It fades in on loadeddata. */
.hero__media video { opacity: 0; transition: opacity .6s var(--ease-io); }
.hero__media video[data-ready] { opacity: 1; }
/* Scrim. Heavy on the copy side because the headline sits over photography,
   not over a white page. Verified against the real poster by _contrast.py. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(100deg, rgba(255,255,255,.985) 0%, rgba(255,255,255,.962) 32%,
                    rgba(255,255,255,.70) 50%, rgba(255,255,255,.12) 70%,
                    rgba(255,255,255,0) 84%);
}
.hero__inner { max-width: 1480px; margin-inline: auto; padding-inline: var(--gutter); width: 100%; position: relative; z-index: 2; }
.hero__copy { max-width: 56rem; position: relative; z-index: 2; }
.hero .display { color: var(--ink); }
.hero .display em { font-style: normal; color: var(--blue-deep); display: block; margin-top: .1em; }
/* The deck is the claim, so it carries the weight; the town line under it is
   a locator and is set back in the same grey the deck used to use. */
.hero__deck {
  margin-top: 1.6rem; max-width: 46ch; font-size: var(--fs-deck);
  font-weight: 600; color: var(--ink);
}
.hero__place { margin-top: .55rem; font-size: var(--fs-deck); font-weight: 400; color: #3A3E44; }
.hero__cta { margin-top: 2.4rem; display: flex; flex-wrap: wrap; gap: .85rem; }

/* The video starts on its own, so WCAG 2.2.2 requires a control to stop it.
   It is a real button, keyboard operable, and it sits inside the hero. */
.hero__vidctl {
  position: absolute; right: clamp(1rem,2.5vw,2rem); bottom: clamp(1rem,2.5vw,2rem); z-index: 3;
  width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid var(--line); background: rgba(255,255,255,.86);
  display: grid; place-items: center; cursor: pointer; color: var(--blue-deep);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  transition: background .2s var(--ease-io), transform .3s var(--ease);
}
.hero__vidctl:hover { background: #fff; transform: translateY(-2px); }
.hero__vidctl svg { width: 16px; height: 16px; }
.hero__vidctl .ico-play { display: none; }
.hero__vidctl.is-stopped .ico-play { display: block; }
.hero__vidctl.is-stopped .ico-pause { display: none; }

.scroll-cue {
  position: absolute; left: 50%; bottom: 2rem; transform: translateX(-50%); z-index: 2;
  display: flex; flex-direction: column; align-items: center; gap: .6rem;
  font-size: var(--fs-micro); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-50);
}
.scroll-cue__track { width: 1px; height: 42px; background: var(--line); position: relative; overflow: hidden; }
.scroll-cue__track::after {
  content: ""; position: absolute; inset: 0 auto auto 0; width: 1px; height: 16px; background: var(--blue);
  animation: cue 2.4s var(--ease-io) infinite;
}
@keyframes cue { 0% { transform: translateY(-18px); } 60%, 100% { transform: translateY(46px); } }

/* --------------------------------------------------------- 9. Trust strip */
/* The black credential band across the top of the brochure, in the same place
   it sits there: immediately under the headline, carrying the same kind of
   short claims. This is the one surface on the page that is not white. */
.trust {
  background: var(--ink);
  border-top: 4px solid var(--blue);
}
.trust__row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 1rem 2rem; padding-block: 1.4rem;
}
.trust__item {
  display: flex; align-items: center; gap: .7rem;
  font-size: var(--fs-micro); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: #EBEBEB;
}
.trust__item svg { width: 18px; height: 18px; color: var(--blue); flex: none; }

/* ------------------------------------------- 10. Process, sticky photograph */
.process { position: relative; background: var(--white); }
.process__grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 5rem); align-items: start;
}
.process__media {
  position: sticky; top: calc(var(--nav-h) + clamp(1.5rem, 5vh, 4rem));
  aspect-ratio: 4 / 3; border-radius: var(--radius); overflow: hidden;
  background: var(--paper); border: 1px solid var(--line);
}
.process__shot {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .55s var(--ease-io);
}
.process__shot.is-on { opacity: 1; }
.process__steps { position: relative; }
.process__step { min-height: 62svh; display: grid; align-content: center; }
.process__card {
  max-width: 34rem; padding: 0 0 0 clamp(1.2rem, 2.5vw, 2rem);
  border-left: 2px solid var(--line);
  transition: opacity .5s var(--ease-io), border-color .5s var(--ease-io);
}
/* Only dim once the scroll driver is confirmed running. If the module never
   boots, every card stays at full opacity instead of at 38%. */
html.process-ready .process__card { opacity: .38; }
html.process-ready .process__step.is-active .process__card { opacity: 1; }
.process__step.is-active .process__card { border-left-color: var(--blue); }

.process__h { margin: 0; }
.process__head {
  display: block; width: 100%; margin: 0; padding: 0;
  font: inherit; color: inherit; text-align: left;
  background: none; border: 0; cursor: default;
}
.process__title { display: block; font-size: var(--fs-h3); font-family: var(--font-head); font-weight: 700; letter-spacing: -.02em; line-height: 1.15; }
.process__num {
  font-family: var(--font-body); font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-50);
  display: flex; align-items: center; gap: .7rem; margin-bottom: 1rem;
}
.process__step.is-active .process__num { color: var(--blue-deep); }
.process__num i { display: block; width: 22px; height: 1px; background: currentColor; flex: none; }
.process__card p { margin-top: .9rem; color: var(--ink-70); font-size: var(--fs-small); }
.process__tag {
  font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--blue-deep); background: var(--blue-050); border: 1px solid var(--blue-100);
  border-radius: 0; padding: .3rem .7rem;
}
/* The pill is a link to a coating page, so it carries a hover affordance. */
a.process__tag { transition: background .2s var(--ease-io), border-color .2s var(--ease-io), color .2s var(--ease-io); }
a.process__tag:hover { background: var(--blue-100); border-color: var(--blue-300); color: var(--blue-deep); }
@media (pointer: coarse) { a.process__tag { padding: .55rem 1rem; } }
.process__rail {
  position: fixed; right: clamp(1rem,2.5vw,2.5rem); top: 50%; transform: translateY(-50%);
  z-index: 2; display: flex; flex-direction: column; gap: .85rem;
  /* visibility, not opacity alone: a faded-out rail still holds six buttons in
     the tab order, so a keyboard user tabs through controls they cannot see.
     The delayed visibility transition lets the fade-out finish first. */
  opacity: 0; pointer-events: none; visibility: hidden;
  transition: opacity .4s var(--ease-io), visibility 0s linear .4s;
}
.process__rail.is-visible {
  opacity: 1; pointer-events: auto; visibility: visible;
  transition: opacity .4s var(--ease-io), visibility 0s;
}
.process__dot {
  width: 26px; height: 26px; border: 0; background: none; padding: 0; cursor: pointer;
  display: grid; place-items: center;
}
.process__dot i { display: block; width: 7px; height: 7px; border-radius: 50%; background: var(--line); transition: all .34s var(--ease); }
.process__dot[aria-current="true"] i { background: var(--blue); transform: scale(1.6); }
.process__dot:hover i { background: var(--blue-300); }

/* -------------------------------------------------- 11. Compare two cards */
.compare { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.compare__col { background: #fff; padding: clamp(1.5rem, 3vw, 2.4rem); display: flex; flex-direction: column; gap: .9rem; }
.compare__col--hl { background: var(--blue-050); }
.compare__tag { font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--blue-deep); }
.compare__row { display: grid; grid-template-columns: 8.5rem 1fr; gap: 1rem; padding-block: .6rem;
  border-top: 1px solid var(--line-soft); font-size: var(--fs-small); }
.compare__row dt { color: var(--ink-50); font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; }
.compare__row dd { margin: 0; color: var(--ink-70); }

/* ------------------------------------------------ 12. Rule list, no boxes */
/* Used where a bordered card grid would be the third identical grid on one
   page. A hairline and a number carry the structure instead. */
.rulelist { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 0 clamp(1.5rem,3vw,3.5rem); }
.rulelist__item { padding: 1.6rem 0 2rem; border-top: 1px solid var(--line); }
.rulelist__n {
  font-family: var(--font-head); font-size: var(--fs-micro); font-weight: 800;
  letter-spacing: .14em; color: var(--blue-deep); display: block; margin-bottom: .9rem;
  font-variant-numeric: tabular-nums;
}
.rulelist__t { font-family: var(--font-head); font-size: 1.2rem; font-weight: 700; letter-spacing: -.02em; line-height: 1.2; }
.rulelist__d { margin-top: .7rem; font-size: var(--fs-small); color: var(--ink-70); line-height: 1.62; }

/* ---------------------------------------------------------------- 13. Cards */
.card {
  position: relative; background: var(--white);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: clamp(1.4rem, 2.4vw, 2rem);
  display: flex; flex-direction: column; gap: .8rem;
  transition: transform .42s var(--ease), box-shadow .42s var(--ease), border-color .3s var(--ease-io);
  overflow: hidden;
}
/* Flat at rest, with the accent arriving on the top edge rather than the card
   floating. The artwork has no floating panels. */
a.card, .card--hover { position: relative; }
a.card::before, .card--hover::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--blue); transform: scaleX(0); transform-origin: left;
  transition: transform .38s var(--ease);
}
a.card:hover, .card--hover:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--blue-100); }
a.card:hover::before, .card--hover:hover::before { transform: scaleX(1); }
/* A circular badge with a blue ring, which is how every icon is set on both
   the brochure and the Euclid Refinishing one pager. */
.card__ico {
  width: 50px; height: 50px; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  background: #fff; color: var(--blue-deep);
  border: 2px solid var(--blue);
  transition: background .32s var(--ease-io), color .32s var(--ease-io), border-color .32s var(--ease-io);
}
.card__ico svg { width: 24px; height: 24px; }
a.card:hover .card__ico { background: var(--blue-deep); color: #fff; border-color: var(--blue-deep); }
.card__title { font-family: var(--font-head); font-size: 1.15rem; font-weight: 700; letter-spacing: -.018em; line-height: 1.25; }
.card__text { font-size: var(--fs-small); color: var(--ink-70); line-height: 1.6; }
.card__more { margin-top: auto; padding-top: .6rem; }
.card--flat { border-color: transparent; background: var(--paper); }
.card--pad-lg { padding: clamp(1.8rem, 3vw, 2.6rem); }

.card--media { padding: 0; }
.card--media .card__media { aspect-ratio: 16/10; overflow: hidden; background: var(--paper); border-bottom: 1px solid var(--line); }
.card--media .card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform .8s var(--ease); }
a.card--media:hover .card__media img { transform: scale(1.045); }
.card--media .card__body { padding: clamp(1.3rem, 2.2vw, 1.75rem); display: flex; flex-direction: column; gap: .7rem; flex: 1; }

/* A coating card carries its finish as a real swatch, so the range reads as a
   range of appearances rather than a list of codes. */
.card__swatch {
  width: 100%; height: 6px; border-radius: 99px; margin-bottom: .2rem;
  background: var(--sw, var(--line));
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.08);
}
.card__code {
  font-family: var(--font-head); font-weight: 800; letter-spacing: -.03em;
  font-size: 1.5rem; color: var(--blue-deep); font-variant-numeric: tabular-nums;
}
.card__chem { font-size: var(--fs-micro); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-50); }

/* ---------------------------------------------------------- 14. Stat block */
.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px; background: var(--line); border-block: 1px solid var(--line);
}
.stat { background: var(--white); padding: clamp(1.6rem,3vw,2.6rem) clamp(1.2rem,2vw,2rem); }
.section--deep .stats { background: rgba(255,255,255,.16); border-color: rgba(255,255,255,.16); }
.section--deep .stat { background: var(--blue-deep); }
.stat__num {
  font-family: var(--font-head); font-weight: 800; letter-spacing: -.04em; line-height: 1;
  font-size: clamp(2.4rem, 4.6vw, 3.9rem); color: var(--blue-deep);
  font-variant-numeric: tabular-nums;
}
.section--deep .stat__num { color: #fff; }
.stat__num sup { font-size: .42em; vertical-align: super; letter-spacing: 0; margin-left: .06em; }
.stat__label { margin-top: .7rem; font-size: var(--fs-small); color: var(--ink-70); line-height: 1.45; }
.section--deep .stat__label { color: #BFCFE4; }

/* -------------------------------------------------------- 15. Split, media */
.split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(2rem, 5vw, 5rem); align-items: center; }
.split--rev .split__media { order: -1; }
.split__media { position: relative; border-radius: var(--radius); overflow: hidden; background: var(--paper); }
.split__media img { width: 100%; height: 100%; object-fit: cover; }
.figure-note {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 1.4rem;
  background: linear-gradient(to top, rgba(11,11,10,.88), transparent);
  color: #fff; font-size: var(--fs-micro); letter-spacing: .06em; text-transform: uppercase; font-weight: 600;
}
/* Wide banner between a section head and its content. It exists to break up
   the long all-text sections. Reuses .figure-note for the caption. */
.section-photo {
  position: relative; overflow: hidden;
  border-radius: var(--radius); border: 1px solid var(--line);
  background: var(--paper); aspect-ratio: 22 / 10;
  margin-bottom: clamp(2rem, 4vw, 3.25rem);
}
.section-photo img { width: 100%; height: 100%; object-fit: cover; }

.checklist { list-style: none; padding: 0; margin: 0; display: grid; gap: .85rem; }
.checklist li { display: grid; grid-template-columns: 22px 1fr; gap: .8rem; align-items: start; font-size: var(--fs-small); color: var(--ink-70); }
.checklist li::before {
  content: ""; width: 20px; height: 20px; margin-top: .12rem; border-radius: 50%;
  background: var(--blue-050) center/12px 12px no-repeat;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23003F91' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  border: 1px solid var(--blue-100);
}
.section--deep .checklist li { color: #C6D4E7; }
.section--deep .checklist li::before {
  background-color: rgba(255,255,255,.1); border-color: rgba(255,255,255,.2);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FFFFFF' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

/* --------------------------------------------------- 16. Full-width band */
.band { margin: 0; position: relative; border-radius: var(--radius); overflow: hidden; background: var(--paper); }
.band img { width: 100%; height: auto; display: block; }

/* Map variant of .band. The photograph above it gets its height from the
   image's intrinsic ratio; an iframe has none, so the ratio is declared here.
   22/10 matches the photo band and keeps the page rhythm, but it is unusable
   on a phone, where a frame 22 wide and 10 tall shows a strip of road and
   nothing either side. Below 700px it squares up instead. */
.band--map { border: 1px solid var(--line); aspect-ratio: 22 / 10; }
.band--map iframe { width: 100%; height: 100%; border: 0; display: block; }

@media (max-width: 700px) {
  .band--map { aspect-ratio: 4 / 3; }
}

.band figcaption {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 1.6rem clamp(1.2rem, 3vw, 2.4rem);
  background: linear-gradient(to top, rgba(11,11,10,.86), transparent);
  color: #fff; font-size: var(--fs-micro); font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
}

/* ------------------------------------------------------------ 17. Industries */
/* Ten cells, and the row is never full. The hairlines are drawn by each cell
   rather than by a 1px grid gap over a grey background, because with a gap the
   empty tail of the last row shows that grey as a large blank block. Drawn this
   way the unused cells are simply white and the grid ends where the items do. */
.industries {
  /* Ten items, so the column count has to divide ten or the last row leaves a
     visible empty box. Five and two are the only counts that tile exactly at
     usable cell widths, so the grid steps straight from five to two. */
  display: grid; grid-template-columns: repeat(5, minmax(0,1fr));
  gap: 0; background: #fff; border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
}
.industry {
  background: #fff; padding: clamp(1.5rem,2.6vw,2.1rem) 1.2rem; text-align: center;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: .9rem;
  box-shadow: 1px 0 0 var(--line), 0 1px 0 var(--line);
  transition: background .3s var(--ease-io);
}
.industry:hover { background: var(--blue-050); }
.industry svg { width: 30px; height: 30px; color: var(--blue-deep); }
.industry span { font-size: var(--fs-micro); font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--blue-deep); line-height: 1.35; }

/* -------------------------------------------------------------- 18. Table */
/* overflow-x:auto forces overflow-y to auto, which makes this the nearest
   scrollport, so the sticky header below needs a bounded height to stick
   inside. Without the max-height the header never sticks to anything. */
.tablewrap { overflow: auto; max-height: min(72vh, 44rem); overscroll-behavior: contain;
  border: 1px solid var(--line); border-radius: var(--radius); background: #fff; }
.dtable { width: 100%; border-collapse: collapse; font-size: var(--fs-small); min-width: 46rem; }
.dtable th, .dtable td { padding: .95rem 1.1rem; text-align: left; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.dtable thead th {
  position: sticky; top: 0; background: var(--paper); z-index: 1;
  font-size: var(--fs-micro); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-50); border-bottom: 1px solid var(--line);
}
.dtable tbody tr:hover { background: var(--blue-050); }
.dtable td { color: var(--ink-70); }
.dtable td:first-child, .dtable th:first-child { padding-left: 1.4rem; }
.dtable .code { font-weight: 700; color: var(--blue-deep); font-variant-numeric: tabular-nums; white-space: nowrap; }
.dot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; vertical-align: -1px; margin-right: .5rem;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.14); }

/* ----------------------------------------------------------- 19. Spec list */
.specs { display: grid; gap: 0; border-top: 1px solid var(--line); }
.specs div { display: grid; grid-template-columns: 12rem 1fr; gap: 1rem;
  padding-block: .9rem; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-small); }
.specs dt { color: var(--ink-50); font-size: var(--fs-micro); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.specs dd { margin: 0; color: var(--ink); font-weight: 500; }

/* -------------------------------------------------------------- 20. Notice */
.notice {
  border: 1px solid var(--blue-100); background: var(--blue-050); color: var(--blue-deep);
  border-radius: var(--radius-sm); padding: 1rem 1.2rem; font-size: var(--fs-small);
  display: grid; grid-template-columns: 20px 1fr; gap: .85rem; align-items: start;
}
.notice svg { width: 20px; height: 20px; color: var(--blue-deep); margin-top: .15rem; }

/* ----------------------------------------------------------- 21. Timeline */
.timeline { list-style: none; margin: 0; padding: 0; position: relative; }
.timeline::before { content: ""; position: absolute; left: 6.5rem; top: .6rem; bottom: .6rem; width: 1px; background: var(--line); }
.timeline li { display: grid; grid-template-columns: 6.5rem 1fr; gap: 0 2rem; padding-block: 1.15rem; position: relative; }
.timeline li::before {
  content: ""; position: absolute; left: calc(6.5rem - 4px); top: 1.55rem;
  width: 9px; height: 9px; border-radius: 50%; background: var(--blue);
  border: 2px solid #fff; box-shadow: 0 0 0 3px var(--blue-050);
}
.timeline time { font-family: var(--font-head); font-weight: 800; font-size: 1.05rem; color: var(--blue-deep); letter-spacing: -.02em; }
.timeline p { font-size: var(--fs-small); color: var(--ink-70); }

/* ----------------------------------------------------------- 22. CTA band */
/* The brochure's blue plate: a flat panel with a hard SurfTech Blue edge on
   top. No gradient glow; the artwork has none. */
.cta-band { position: relative; overflow: hidden; background: var(--blue-deep); color: #fff;
  border-top: 5px solid var(--blue); }
.cta-band > * { position: relative; }
.cta-band .h2 { color: #fff; max-width: 18ch; }
.cta-band .deck { color: #BFCFE4; }
.cta-band__row { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 2.2rem; }
.cta-band__grid { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(2rem,5vw,5rem); align-items: center; }
.cta-band__aside { display: grid; gap: 1.1rem; }
.cta-line { display: grid; grid-template-columns: 20px 1fr; gap: .9rem; align-items: start; font-size: var(--fs-small); color: #C6D4E7; }
.cta-line svg { width: 20px; height: 20px; color: var(--blue-300); margin-top: .12rem; }
.cta-line strong { display: block; color: #fff; font-weight: 600; }

/* --------------------------------------------------------------- 23. Forms */
.form { display: grid; gap: 1.05rem; }
.field { display: grid; gap: .45rem; }
.field label { font-size: var(--fs-micro); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-70); }
.field .req { color: var(--blue-deep); }
.field input, .field select, .field textarea {
  width: 100%; background: #fff; border: 1px solid var(--line-control); border-radius: var(--radius-sm);
  padding: .85rem 1rem; color: var(--ink);
  /* Never below 16px: iOS Safari zooms the viewport on focus otherwise, which
     throws the user out of position halfway through a quote request. */
  font-size: 16px;
  transition: border-color .2s var(--ease-io), box-shadow .2s var(--ease-io);
}
.field textarea { min-height: 132px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--blue-deep); box-shadow: 0 0 0 3px var(--blue-100);
}
.field--error input, .field--error select, .field--error textarea { border-color: #B3271A; }
.field__err { font-size: var(--fs-micro); color: #B3271A; display: none; }
.field--error .field__err { display: block; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.05rem; }
.radio-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.radio-chip { position: relative; }
.radio-chip input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.radio-chip span {
  display: block; padding: .55rem 1.05rem; border: 1px solid var(--line-control); border-radius: var(--radius-pill);
  font-size: var(--fs-micro); font-weight: 600; color: var(--ink-70); cursor: pointer;
  transition: all .22s var(--ease-io);
}
.radio-chip input:checked + span { background: var(--blue-050); border-color: var(--blue-deep); color: var(--blue-deep); }
.radio-chip input:focus-visible + span { outline: 2.5px solid var(--blue-deep); outline-offset: 2px; }
.form__note { font-size: var(--fs-micro); color: var(--ink-50); }
.form-status { display: none; padding: 1rem 1.1rem; border-radius: var(--radius-sm); font-size: var(--fs-small); }
.form-status.is-ok { display: block; background: var(--blue-050); border: 1px solid var(--blue-100); color: var(--blue-deep); }
.form-status.is-error { display: block; background: #FDF3F2; border: 1px solid #B3271A; color: #7A1A11; }
/* The file input stays native. A custom control here would mean rebuilding
   keyboard access, the file picker and the selected-file announcement, and
   every one of those is a way to break an RFQ. It is given room instead. */
.dropzone {
  border: 1px dashed var(--line-control); border-radius: var(--radius-sm);
  padding: 1.4rem; text-align: center; color: var(--ink-50); font-size: var(--fs-small);
  transition: border-color .2s var(--ease-io), background .2s var(--ease-io);
}
.dropzone input[type="file"] {
  display: block; width: 100%; max-width: 26rem; margin-inline: auto;
  font-size: var(--fs-small); color: var(--ink-70); cursor: pointer;
}
.dropzone input[type="file"]::file-selector-button {
  font: inherit; font-weight: 600; color: var(--blue-deep);
  background: var(--blue-050); border: 1px solid var(--blue-100);
  border-radius: var(--radius-pill); padding: .55rem 1.1rem; margin-right: .9rem;
  cursor: pointer; transition: background .2s var(--ease-io), border-color .2s var(--ease-io);
}
.dropzone input[type="file"]::file-selector-button:hover { background: var(--blue-100); border-color: var(--blue-300); }
.dropzone.is-over { border-color: var(--blue-deep); background: var(--blue-050); }
.filelist { list-style: none; margin: .8rem 0 0; padding: 0; display: grid; gap: .4rem; font-size: var(--fs-micro); color: var(--ink-70); }
.filelist li { display: flex; gap: .6rem; align-items: center; justify-content: space-between; }

/* ----------------------------------------------------------- 24. Accordion */
.acc { border-top: 1px solid var(--line); }
.acc__item { border-bottom: 1px solid var(--line); }
.acc__btn {
  width: 100%; background: none; border: 0; cursor: pointer; text-align: left;
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding: 1.4rem 0; font-family: var(--font-head); font-size: 1.05rem; font-weight: 700; letter-spacing: -.018em;
  color: var(--ink); transition: color .2s var(--ease-io);
}
.acc__btn:hover { color: var(--blue-deep); }
.acc__btn i { flex: none; width: 18px; height: 18px; position: relative; }
.acc__btn i::before, .acc__btn i::after {
  content: ""; position: absolute; top: 50%; left: 0; width: 18px; height: 2px;
  background: var(--blue-deep); border-radius: 0; transition: transform .34s var(--ease);
}
.acc__btn i::after { transform: rotate(90deg); }
.acc__btn[aria-expanded="true"] i::after { transform: rotate(0deg); }
/* Open by default, and collapsed only once app.js has said it can reopen
   them. A dead module leaves a readable FAQ instead of a list of questions
   with no answers. Same principle as the reveal gate. */
.acc__panel { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .4s var(--ease); }
.acc__panel > div { overflow: hidden; }
.acc.is-ready .acc__panel { grid-template-rows: 0fr; }
.acc.is-ready .acc__item.is-open .acc__panel { grid-template-rows: 1fr; }
.acc__panel p { padding-bottom: 1.5rem; color: var(--ink-70); font-size: var(--fs-small); max-width: 68ch; }

/* ------------------------------------------------- 25. Touch target sizes */
/* Pointer-coarse only, so the desktop rhythm is untouched. WCAG 2.5.8 asks for
   24px; calling is a primary conversion here, so these get the full 44px. */
@media (pointer: coarse) {
  .footer ul a,
  .crumbs a,
  .cta-line a,
  .nav__sheet-meta a,
  .prose a {
    display: inline-block;
    min-height: 44px;
    line-height: 44px;
  }
  .footer ul { gap: 0; }
  .radio-chip span { padding: .8rem 1.2rem; }
  .acc__btn { padding-block: 1.15rem; min-height: 56px; }
  .link-arrow { min-height: 44px; align-items: center; }
  .nav__tel { min-height: 44px; }
  .nav__brand { min-height: 44px; align-items: center; }
  .nav__sheet-meta a, .nav__sheet-meta a strong { display: inline-block; min-height: 44px; line-height: 44px; }
}

/* ------------------------------------------------------------- 26. Footer */
.footer {
  position: relative; z-index: 1;
  background: var(--black); color: #A0A6AD;
  padding-block: clamp(3.5rem,7vw,5.5rem) 2rem; font-size: var(--fs-small);
}
.footer__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1.3fr; gap: clamp(2rem,4vw,3.5rem); }
.footer__h {
  color: #fff; font-size: var(--fs-micro); letter-spacing: .14em; text-transform: uppercase;
  font-family: var(--font-body); font-weight: 700; margin-bottom: 1.1rem;
  line-height: 1.4;
}
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.footer a:hover { color: #fff; }
/* The supplied logo is blue script on a black ellipse. On the black footer the
   ellipse disappears and the script reads cleanly, so it is used as supplied,
   with no colour operation. See TODO-DATA.md on the sanctioned variants. */
.footer__brand img { height: 46px; width: auto; margin-bottom: 1.2rem; }
.footer__brand p { max-width: 34ch; line-height: 1.6; }
.footer__bar {
  margin-top: clamp(2.5rem,5vw,4rem); padding-top: 1.6rem; border-top: 1px solid rgba(255,255,255,.14);
  display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: space-between; align-items: center;
  font-size: var(--fs-micro); color: #8B9299;
}
.footer__legal { display: flex; flex-wrap: wrap; gap: 0 1.4rem; }
.footer__legal a { color: #8B9299; display: inline-block; padding-block: .6rem; }
.footer__legal a:hover { color: #fff; }
.footer__iso { display: inline-flex; align-items: center; gap: .6rem; color: #D2D6DA; font-weight: 600; }
.footer__iso svg { width: 16px; height: 16px; color: var(--blue); }

/* -------------------------------------------------------- 27. Page header */
.page-head {
  padding-block: calc(var(--nav-h) + clamp(3rem,6vw,5.5rem)) clamp(2.5rem,5vw,4rem);
  background: var(--white); position: relative;
}
.page-head .h1 { max-width: 17ch; }
.page-head .deck { margin-top: 1.4rem; }
.crumbs { display: flex; flex-wrap: wrap; gap: .5rem; font-size: var(--fs-micro); color: var(--ink-50); margin: 0 0 1.4rem; list-style: none; padding: 0; }
.crumbs li + li::before { content: "/"; margin-right: .5rem; color: var(--line); }
.crumbs a:hover { color: var(--blue-deep); }

/* --------------------------------------------------------------- 28. Prose */
.prose { max-width: var(--maxw-text); }
.prose > * + * { margin-top: 1.35rem; }
.prose h2 { font-size: clamp(1.55rem,2.5vw,2.05rem); margin-top: 3rem; }
.prose h3 { font-size: clamp(1.2rem,1.8vw,1.45rem); margin-top: 2.2rem; }
.prose p, .prose li { color: var(--ink-70); line-height: 1.72; }
.prose ul, .prose ol { padding-left: 1.35rem; display: grid; gap: .6rem; }
.prose strong { color: var(--ink); font-weight: 600; }
.prose a { color: var(--blue-deep); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }

/* ------------------------------------------------------- 29. Customer list */
/* This used to scroll. Moving content that starts on its own needs a control
   to stop it under WCAG 2.2.2, and the control was the only reason the button
   existed. Standing still, the names are easier to read and the obligation
   goes away with the motion. */
.customers {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: .9rem clamp(1.5rem, 4vw, 3.5rem);
}
.customers li {
  font-family: var(--font-head); font-weight: 700;
  font-size: clamp(.95rem, 1.4vw, 1.15rem);
  letter-spacing: -.01em; color: var(--ink-50); white-space: nowrap;
}

/* ------------------------------------------------------- 30. Entrance motion */
/* Applied to groups, never to every element. A grid arrives as a grid and its
   cards follow the reading order; the same fade stamped on eighteen separate
   elements is filler and reads as filler. About six containers per page.

   Content is VISIBLE by default. html.reveal-on is set by an inline head
   script before first paint and removes itself after 3s unless app.js has
   claimed it, so no JavaScript, a dead module or a thrown initReveal all end
   with copy on screen. There is no path where a group stays hidden. */
html.reveal-on [data-reveal] > * { opacity: 0; transform: translateY(10px); }
html.reveal-on [data-reveal].is-in > * {
  opacity: 1; transform: none;
  transition: opacity .5s var(--ease-io), transform .55s var(--ease);
  transition-delay: var(--d, 0ms);
}
html.reveal-on [data-reveal].is-in > *:nth-child(2) { --d: 55ms; }
html.reveal-on [data-reveal].is-in > *:nth-child(3) { --d: 110ms; }
html.reveal-on [data-reveal].is-in > *:nth-child(4) { --d: 165ms; }
html.reveal-on [data-reveal].is-in > *:nth-child(5) { --d: 220ms; }
html.reveal-on [data-reveal].is-in > *:nth-child(6) { --d: 275ms; }
html.reveal-on [data-reveal].is-in > *:nth-child(7) { --d: 330ms; }
html.reveal-on [data-reveal].is-in > *:nth-child(8) { --d: 385ms; }
html.reveal-on [data-reveal].is-in > *:nth-child(9) { --d: 440ms; }
html.reveal-on [data-reveal].is-in > *:nth-child(10) { --d: 495ms; }

/* A figure is one object, so it moves as one rather than staggering its own
   image against its own caption. */
html.reveal-on figure[data-reveal] { opacity: 0; transform: translateY(12px); }
html.reveal-on figure[data-reveal].is-in {
  opacity: 1; transform: none;
  transition: opacity .6s var(--ease-io), transform .7s var(--ease);
}
html.reveal-on figure[data-reveal] > *,
html.reveal-on figure[data-reveal].is-in > * { opacity: 1; transform: none; transition: none; }

/* ----------------------------------------------------------- 32. Responsive */
@media (max-width: 1080px) {
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .cta-band__grid { grid-template-columns: 1fr; }
  .grid-4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
/* The six links, the phone number and the quote button stop fitting at about
   1054px, well before the 900px the rest of the layout steps at. Collapsing the
   bar has its own breakpoint so 901 to 1060px is not left with an overflowing
   nav and no hamburger. Kept in step with the resize handler in app.js. */
@media (max-width: 1060px) {
  .nav__links, .nav__tel { display: none; }
  .nav__actions { margin-left: auto; }
  .nav__toggle { display: flex; }
}
@media (max-width: 900px) {
  :root { --nav-h: 62px; }
  .grid-3 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .rulelist { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .industries { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .split { grid-template-columns: minmax(0, 1fr); }
  .split--rev .split__media { order: 0; }

  /* Process, phone and small tablet. The desktop mechanism is a sticky photo
     that cross-fades as six full-height beats scroll past it, which cannot
     work in one narrow column. Every step is open, exactly like the numbered
     list further down the page, and the sticky photo follows whichever step
     you have scrolled to. Nothing is hidden and nothing needs tapping. */
  .process__grid { display: block; }
  .process__media {
    position: sticky; top: calc(var(--nav-h) + .5rem); z-index: 2;
    aspect-ratio: 16 / 9;
    /* Without the cap, a 16:9 box at 100% width is taller than a landscape
       phone viewport and hides every step behind it. */
    max-height: calc(100svh - var(--nav-h) - 9rem);
    margin-bottom: 1.6rem;
    box-shadow: var(--shadow-sm);
  }
  .process__steps { border-top: 1px solid var(--line); }
  .process__step { min-height: 0; display: block; }
  html .process__card,
  html.process-ready .process__card,
  html.process-ready .process__step.is-active .process__card {
    max-width: none; padding: 0 0 0 1rem; opacity: 1;
    border-left: 2px solid var(--line-soft);
  }
  html .process__step.is-active .process__card,
  html.process-ready .process__step.is-active .process__card { border-left-color: var(--blue); }
  .process__step + .process__step { border-top: 1px solid var(--line); }
  .process__head { display: block; cursor: default; padding-block: 1.15rem .1rem; }
  .process__num   { margin-bottom: .45rem; }
  .process__title { font-size: 1.1rem; }
  .process__card p { margin-top: .55rem; padding-bottom: 1.35rem; }
  .process__rail { display: none; }
  .compare { grid-template-columns: 1fr; }
  .specs div { grid-template-columns: 9rem 1fr; }
}
@media (max-width: 620px) {
  /* The h1 floor of 2.4rem sets "recommendation." wider than the column it has
     to live in, and no hyphenation rescues a single word at that size. */
  /* --fs-display is sized so the <em>, "for parts and tools.", holds a single
     line at every phone width, which puts the headline on three lines instead
     of four. The em needs about 9.2x its own font size, and the column is
     100vw minus two gutters, so anything above ~9.4vw wraps it. 9vw leaves
     about 14px spare at 320px, which is the narrowest phone still in use and
     the case with the least room. The old floor of
     2.35rem was what actually bound at 375px and forced the fourth line. */
  :root { --fs-h1: clamp(1.95rem, 8.4vw, 2.6rem); --fs-display: clamp(1.8rem, 9vw, 3.4rem); }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .rulelist { grid-template-columns: 1fr; }
  .field-row { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr; }
  .timeline::before { left: 4px; }
  .timeline li { grid-template-columns: 1fr; gap: .3rem; padding-left: 1.75rem; }
  .timeline li::before { left: 0; top: 1.5rem; }
  .section-photo { aspect-ratio: 16 / 9; }
  .stat__num { font-size: 2.7rem; }
  .nav__brand img { height: 36px; }
  .specs div { grid-template-columns: 1fr; gap: .1rem; padding-block: .75rem; }

  /* Hero, phone. The video stops being a background and becomes a card.
     Behind the copy it needed a ~95% white scrim to keep the headline legible,
     which erased it: megabytes of footage bought a pale haze. Stacked instead,
     the headline gets full contrast on paper and the footage is visible.
     `display: contents` dissolves the two wrappers so the headline, the video,
     the deck and the buttons become direct flex children of .hero and can be
     ordered independently, with the markup untouched. */
  .hero {
    display: flex; flex-direction: column; gap: 0;
    min-height: 0;
    padding: calc(var(--nav-h) + 2.25rem) var(--gutter) 2.75rem;
  }
  .hero::before { display: none; }
  .hero__inner, .hero__copy { display: contents; }
  .hero .display { order: 1; }
  .hero__deck  { order: 3; margin-top: 1.5rem; }
  .hero__place { order: 4; margin-top: .5rem; }
  .hero__cta   { order: 5; margin-top: 1.6rem; }
  .hero__cta .btn { flex: 1 1 14rem; }
  .hero__media {
    order: 2; position: relative; inset: auto;
    margin: 1.5rem 0 0;
    aspect-ratio: 16 / 9;
    border-radius: var(--radius);
    border: 1px solid var(--line);
    box-shadow: var(--shadow-md);
    background: var(--blue-050);
  }
  /* This used to sit at the hero's top-right, which on a phone is exactly
     where the h1's first line is: the button covered the last letters of
     "Low temperature". It cannot be positioned against .hero__media, which is
     a sibling, so it is put back in flow directly after the card and pulled
     onto the card's bottom-right corner. The bottom margin gives back what the
     negative top margin takes, so nothing below it moves. `relative` rather
     than `static` because .hero__media is a positioned z-index:0 box and would
     otherwise paint over a static sibling. */
  /* Kept, because the video autoplays and WCAG 2.2.2 owes the reader a way to
     stop it, but styled down: what made it objectionable was that it used to
     sit at the hero's top-right, on top of the h1's first line. It now rides
     the video card's own bottom-right corner and is quieter than the desktop
     one. The box stays 44px because a touch target has to. */
  .hero__vidctl {
    position: relative; z-index: 3; inset: auto;
    order: 2; align-self: flex-end;
    margin: -3.5rem .6rem .6rem 0;
    background: rgba(255,255,255,.7); border-color: rgba(255,255,255,.55);
  }
  .hero__vidctl svg { width: 13px; height: 13px; }
  .scroll-cue { display: none; }

  .band figcaption {
    padding: 1.1rem 1rem .9rem;
    font-size: .6875rem; letter-spacing: .06em; line-height: 1.45;
  }

  /* Trust strip, phone. `space-between` wrapping produced a ragged left stack
     with an orphan on the last line. A two-up hairline grid keeps it reading
     as a credential bar and lands the fifth item across the full width. */
  .trust__row {
    display: grid; grid-template-columns: 1fr 1fr;
    align-items: stretch; gap: 0; padding-block: 0;
  }
  .trust__item {
    align-items: flex-start; padding-block: .9rem;
    font-size: .72rem; letter-spacing: .05em; line-height: 1.35; gap: .5rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .trust__item svg { width: 15px; height: 15px; margin-top: .1em; }
  .trust__item:nth-child(odd)  { padding-right: .85rem; }
  .trust__item:nth-child(even) { padding-left: .85rem; border-left: 1px solid var(--line-soft); }
  .trust__item:last-child {
    grid-column: 1 / -1; justify-content: center;
    border-bottom: 0; border-left: 0; padding-inline: 0;
  }
}

/* --------------------------------------------------- 33. Reduced motion */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
  .film__layer { opacity: 1; transform: none; }
  html.reveal-on [data-reveal] > *,
  html.reveal-on figure[data-reveal] { opacity: 1; transform: none; }
  .scroll-cue__track::after { display: none; }
}

/* -------------------------------------------------------------- 30. Tools */
/* The two interactive screeners. `.notice` is a two column grid expecting an
   SVG in the gutter; these blocks carry prose instead, so they opt out of the
   icon column rather than sitting against an empty one. */
[data-survival-out] .notice,
[data-tol-out] .notice { display: block; }
[data-survival-out] .notice > * + *,
[data-tol-out] .notice > * + * { margin-top: .7rem; }
[data-survival-out] .notice .h3,
[data-survival-out] .notice .h4,
[data-tol-out] .notice .h3,
[data-tol-out] .notice .h4 { margin: 0 0 .5rem; color: var(--blue-deep); }

/* Verdict rows. Colour is a second channel here, never the only one: every row
   also carries the verdict in words, because a red row that only reads as red
   is unusable to anyone who cannot see it and meaningless in print. */
.tool-row th[scope="row"] { font-weight: 700; }
.tool-row td strong { display: block; }
.tool-row.is-destroyed { background: #FDF2F2; box-shadow: inset 3px 0 0 #B42318; }
.tool-row.is-over      { background: #FFF8EC; box-shadow: inset 3px 0 0 #B54708; }
.tool-row.is-other     { background: #FFFBEB; box-shadow: inset 3px 0 0 #A16207; }
.tool-row.is-unknown   { background: var(--paper); box-shadow: inset 3px 0 0 var(--line-control); }
.tool-row.is-clear     { background: #F2FBF5; box-shadow: inset 3px 0 0 #067647; }
.tool-row.is-destroyed td strong { color: #912018; }
.tool-row.is-over      td strong { color: #93370D; }
.tool-row.is-other     td strong { color: #854A0E; }
.tool-row.is-clear     td strong { color: #05603A; }

/* The SurfTech row in the static reference table, marked so it reads as this
   company's own published figure rather than one more industry range. */
.dtable tr.is-surftech th[scope="row"] { color: var(--blue-deep); }
.dtable tr.is-surftech { background: var(--blue-050); }

@media (max-width: 640px) {
  [data-survival-out] .tablewrap,
  [data-tol-out] .tablewrap { margin-inline: calc(var(--gutter) * -1); }
}

/* Small print under a table or a screener result. Distinct from .figure-note,
   which is an absolutely positioned caption that sits over a photograph and is
   wrong for anything in normal flow. */
.tool-note {
  margin-top: 1.1rem; font-size: var(--fs-small); color: var(--ink-70);
  max-width: 68ch; line-height: 1.55;
}
.tool-note + .tool-note { margin-top: .6rem; }
