/* ============================================================
   DIGITILE INTERNATIONAL — site.css

   DESIGN NOTE
   The section skeleton is inherited from our Seven Wonders build
   (hero over video with a quote card, full-bleed photo feature,
   carousels, video CTA, mobile sticky bar). The SKIN is not: this
   was written from zero for a retail-showroom register.

   The register, in one line: white ground, one neutral grey panel,
   near-black ink, a single deep petrol accent, and zero radius on
   every single element. Crisp and functional, not elegant — no
   soft shadows, no pills, no tinted media, no serif.

   Two rules carried in from earlier projects, both learned the
   hard way:

   1. NO TINT OR SCRIM OVER MEDIA, ANYWHERE. Legibility is bought
      with a solid panel behind the words, or by putting near-black
      ink on a region measured bright enough to carry it. The hero
      clip was picked precisely because it is a bright, cool,
      low-variance plate. Verified with tools/luma.js, not by eye.

   2. NO FLAT DARK FILLS. A perfectly flat charcoal area makes the
      display pipeline dither, which shows up as a faint diagonal
      hairline across the whole section. Every dark block here is a
      two-stop gradient with a low-opacity noise layer over it.
   ============================================================ */

/* ================= TOKENS ================= */
:root {
  --white: #FFFFFF;
  --panel: #F4F4F4;
  --panel-2: #EDEDED;

  --ink: #14181A;
  --ink-2: #545B5E;
  --ink-3: #868D90;

  --line: #E1E3E4;
  --line-2: #C9CDCF;

  /* The one accent. Deep petrol reads as stone/slate rather than
     "brand blue", which suits a materials showroom. */
  --deep: #1D414A;
  --deep-2: #17353D;   /* gradient partner — 4 luminance steps apart */
  --deep-3: #26525D;   /* hover */

  --container: 1280px;
  --container-wide: 1560px;
  --pad: 24px;

  --nav-h: 76px;

  --sec-y: clamp(72px, 9vw, 128px);

  --ease: cubic-bezier(.22, .61, .36, 1);
}

/* ================= RESET / BASE ================= */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: var(--nav-h);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--white);
  color: var(--ink);
  font-family: 'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 16px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, video, svg { display: block; max-width: 100%; }
img, video { height: auto; }

a { color: inherit; }

h1, h2, h3, h4, p, figure, blockquote, ul { margin: 0; }
ul { padding: 0; list-style: none; }

button { font: inherit; color: inherit; cursor: pointer; }

:focus-visible {
  outline: 2px solid var(--deep);
  outline-offset: 3px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--ink);
  color: var(--white);
  padding: 12px 18px;
  font-size: 14px;
  font-weight: 700;
}
.skip-link:focus { left: 12px; top: 12px; }

/* Honeypot — off-screen, never announced. */
.hp {
  position: absolute !important;
  left: -9999px !important;
  width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
}

/* The noise layer that keeps flat dark fills from dithering.
   Always paired with a gradient; opacity stays in the 0.025–0.04
   band — higher reads as grain, lower stops masking. */
.noise-overlay {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/></svg>");
}

/* ================= LAYOUT ================= */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--pad);
}
.container--wide { max-width: var(--container-wide); }

.section { padding-block: var(--sec-y); }
.section--panel { background: var(--panel); }

/* ================= TYPE HELPERS ================= */
.label {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--deep);
  margin-bottom: 18px;
}

/* Line-height carries the gap that keeps the .hl block off the line
   above it. An inline element's background box is the font's whole
   content area (~1.15em for this face), not its cap height, so a
   heading set at 1.0 will always have the block colliding upward. */
.display {
  font-size: clamp(30px, 3.7vw, 50px);
  font-weight: 700;
  line-height: 1.22;
  letter-spacing: 0.004em;
  text-transform: uppercase;
  color: var(--ink);
}
.display--xl {
  font-size: clamp(34px, 4.8vw, 66px);
  line-height: 1.16;
}

.lede {
  margin-top: 20px;
  max-width: 62ch;
  font-size: clamp(16px, 1.15vw, 18px);
  line-height: 1.62;
  color: var(--ink-2);
}

/* SIGNATURE TYPE ACCENT
   Exactly one word per major heading sits inside a solid petrol
   block. Square, no radius, no gradient — the same "one tile"
   idea the whole site is about. On dark grounds the block would
   disappear, so it inverts to white-on-petrol there. */
/* The block is trimmed rather than padded: the headings are all-caps,
   so the font's descender space inside the background box is already
   empty room and reads as height the word doesn't need. Kept off zero
   so the glyphs never touch the block's edge. */
.hl {
  background: var(--deep);
  color: var(--white);
  padding: 0.01em 0.22em 0.05em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.sec-head { max-width: 760px; margin-bottom: clamp(38px, 4.5vw, 62px); }
.sec-head .display { max-width: 20ch; }

/* Materials row — a showroom has to answer "what can I actually buy
   here?" above the fold of the services block. Plain hairline chips,
   not a new section: the brief was to mirror the donor's section set. */
.mat-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 26px;
}
.mat {
  background: var(--white);
  border: 1px solid var(--line-2);
  padding: 9px 15px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* ================= BUTTONS ================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 17px 30px;
  border: 1px solid transparent;
  border-radius: 0;
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  line-height: 1;
  cursor: pointer;
  transition: background-color .2s var(--ease), color .2s var(--ease), border-color .2s var(--ease);
}
.btn svg { width: 17px; height: 17px; flex: none; }

.btn--solid {
  background: var(--ink);
  color: var(--white);
  border-color: var(--ink);
}
.btn--solid:hover { background: var(--deep); border-color: var(--deep); }

.btn--deep {
  background: var(--deep);
  color: var(--white);
  border-color: var(--deep);
}
.btn--deep:hover { background: var(--deep-3); border-color: var(--deep-3); }

.btn--line {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}
.btn--line:hover { background: var(--ink); color: var(--white); }

/* Over media. Solid white — never a translucent "glass" fill, which
   is exactly the thing that forces a scrim underneath it. */
.btn--onmedia {
  background: var(--white);
  color: var(--ink);
  border-color: var(--white);
}
.btn--onmedia:hover { background: var(--ink); color: var(--white); border-color: var(--ink); }

.btn--ghostlight {
  background: transparent;
  color: var(--white);
  border-color: rgba(255, 255, 255, .55);
}
.btn--ghostlight:hover { background: var(--white); color: var(--deep); border-color: var(--white); }

/* ================= BRAND LOCKUP ================= */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  color: inherit;
}
.brand-mark { width: 34px; height: 34px; flex: none; }
.brand-text { display: flex; flex-direction: column; line-height: 1.05; }
.brand-name {
  font-size: 17px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.brand-sub {
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 3px;
}

/* ================= NAV =================
   Transparent while parked over the hero, solid white from the
   first scroll tick. The large lockup lives in the hero and hands
   off to the small one in the bar — they never share the screen. */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  height: var(--nav-h);
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background-color .28s var(--ease), border-color .28s var(--ease);
}
.nav-inner {
  height: 100%;
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-inline: var(--pad);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.nav--solid {
  background: var(--white);
  border-bottom-color: var(--line);
}

/* Pages with no hero video under the bar. This needs its own class:
   site.js toggles .nav--solid OFF again on the way back up to
   scroll 0, so hardcoding that class in the markup would make the
   brand mark and the CTA silently disappear at the top of the page. */
.nav--static {
  background: var(--white);
  border-bottom-color: var(--line);
}
.nav--static .nav-brand { opacity: 1; visibility: visible; transform: none; }
.nav--static .nav-btn { display: inline-flex; }

/* The bar's own lockup is hidden until the bar is solid. */
.nav-brand {
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity .28s var(--ease), transform .28s var(--ease), visibility .28s;
}
.nav--solid .nav-brand {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 30px;
  margin-left: auto;
}
.nav-link {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  padding: 6px 0;
  border-bottom: 2px solid transparent;
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.nav-link:hover { border-bottom-color: var(--deep); color: var(--deep); }

.nav-phone { color: var(--ink); }

/* Hidden while transparent — the hero carries its own CTAs, and a
   button floating on video with nothing behind it is the exact
   thing that would need a scrim. */
.nav-btn {
  display: none;
  padding: 13px 22px;
  background: var(--deep);
  color: var(--white);
  border: 1px solid var(--deep);
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color .2s var(--ease);
}
.nav-btn:hover { background: var(--deep-3); }
.nav--solid .nav-btn { display: inline-flex; }

.menu-btn {
  display: none;
  width: 46px; height: 46px;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 5px;
  background: transparent;
  border: 1px solid var(--ink);
  padding: 0;
}
.menu-btn .bar {
  width: 19px; height: 2px;
  background: var(--ink);
  transition: transform .22s var(--ease), opacity .22s var(--ease);
}
.menu-btn[aria-expanded="true"] .bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.menu-btn[aria-expanded="true"] .bar:nth-child(2) { opacity: 0; }
.menu-btn[aria-expanded="true"] .bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ================= MOBILE MENU ================= */
.mobile-menu {
  position: fixed;
  inset: var(--nav-h) 0 auto 0;
  z-index: 55;
  background: var(--white);
  border-bottom: 1px solid var(--line);
  padding: 8px var(--pad) 26px;
  display: flex;
  flex-direction: column;
  transform: translateY(-12px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* Same stepped-visibility rule as the lightbox: site.js focuses the
     first link on open, and an eased visibility would keep the panel
     computed-hidden long enough for that focus() to be a no-op. */
  transition: opacity .24s var(--ease), transform .24s var(--ease), visibility 0s linear .24s;
  max-height: calc(100dvh - var(--nav-h));
  overflow-y: auto;
}
.mobile-menu.open {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
  transition: opacity .24s var(--ease), transform .24s var(--ease), visibility 0s linear 0s;
}
.mobile-menu a {
  padding: 15px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
}
.mobile-menu .mm-phone { color: var(--deep); }
/* `.mobile-menu a` is (0,1,1) and beats `.btn--solid` at (0,1,0), so the
   button's white label was being repainted near-black onto its own
   near-black fill — an invisible CTA. Restated at (0,2,0) to win. */
.mobile-menu .mm-btn {
  margin-top: 20px;
  border-bottom: none;
  justify-content: center;
  color: var(--white);
}
.mobile-menu .mm-btn:hover { color: var(--white); }

/* ================= HERO =================
   Full-bleed clip, untinted. The copy sits straight on the footage
   in near-black; the form gets an opaque white card. Both choices
   are measured, not assumed — see tools/luma.js. */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  padding-top: calc(var(--nav-h) + 28px);
  padding-bottom: clamp(40px, 5vw, 76px);
  overflow: hidden;
  background: var(--panel);
}

.hero-media { position: absolute; inset: 0; z-index: 0; }
.hero-media img,
.hero-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  /* Pushed brighter than source rather than darker — the opposite
     of a scrim. Also lifts the plate for the near-black ink. */
  filter: saturate(1.06) brightness(1.05);
}
.hero-media video { opacity: 0; transition: opacity .6s var(--ease); }
.hero-media video.ready { opacity: 1; }

.hm-mob { display: none; }

.hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-inline: var(--pad);
  display: grid;
  grid-template-columns: minmax(0, 1fr) 420px;
  gap: clamp(28px, 4vw, 64px);
  align-items: end;
}

.hero-copy { max-width: 620px; }

/* The large lockup fades as the bar's small one arrives. */
.hero-brand {
  margin-bottom: 30px;
  transition: opacity .28s var(--ease), transform .28s var(--ease), visibility .28s;
}
.hero-brand .brand-mark { width: 52px; height: 52px; }
.hero-brand .brand-name { font-size: 24px; }
.hero-brand .brand-sub { font-size: 9.5px; color: var(--ink-2); }
.nav-past .hero-brand {
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
}

.hero-eyebrow {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 16px;
}

.hero-title {
  font-size: clamp(34px, 5vw, 68px);
  font-weight: 800;
  line-height: 1.14;   /* room for the .hl block on line two */
  letter-spacing: -0.004em;
  text-transform: uppercase;
  color: var(--ink);
  max-width: 15ch;
}

.hero-sub {
  margin-top: 20px;
  max-width: 46ch;
  font-size: clamp(15.5px, 1.15vw, 17.5px);
  line-height: 1.58;
  font-weight: 500;
  color: var(--ink);
}

.hero-actions {
  margin-top: 30px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.rating-badge {
  margin-top: 28px;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  background: var(--white);
  border: 1px solid var(--line);
  padding: 12px 18px;
}
.rb-score {
  font-size: 24px;
  font-weight: 800;
  line-height: 1;
  color: var(--ink);
}
.rb-divider { width: 1px; height: 30px; background: var(--line-2); }
.rb-stars { display: flex; gap: 2px; color: var(--deep); }
.rb-stars svg { width: 14px; height: 14px; }
.rb-sub {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
}

/* Quote card — opaque white, hairline, zero radius. */
.hero-quote {
  background: var(--white);
  border: 1px solid var(--line);
  padding: 28px 26px 26px;
}
.quote-title {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.15;
}
.quote-sub {
  margin-top: 8px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.hero-quote form { margin-top: 20px; }

/* ================= FORM ================= */
.q-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.q-field { margin-bottom: 13px; }
.q-label {
  display: block;
  margin-bottom: 6px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.q-opt { font-weight: 600; letter-spacing: 0.06em; color: var(--ink-3); }
.q-input {
  width: 100%;
  padding: 12px 13px;
  background: var(--white);
  border: 1px solid var(--line-2);
  border-radius: 0;
  font-family: inherit;
  font-size: 15px;
  color: var(--ink);
  transition: border-color .18s var(--ease);
}
.q-input:focus { border-color: var(--deep); outline: none; }
.q-input::placeholder { color: var(--ink-3); }
textarea.q-input { min-height: 88px; resize: vertical; }
.q-select { appearance: none; background-image: none; padding-right: 34px; }
.q-selwrap { position: relative; }
.q-selwrap::after {
  content: "";
  position: absolute;
  right: 14px; top: 50%;
  width: 8px; height: 8px;
  margin-top: -6px;
  border-right: 2px solid var(--ink-2);
  border-bottom: 2px solid var(--ink-2);
  transform: rotate(45deg);
  pointer-events: none;
}
.q-submit { width: 100%; margin-top: 6px; }
.q-note {
  margin-top: 11px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-3);
  text-align: center;
}
.q-note.is-error { color: #A32C1E; font-weight: 700; }
.q-done {
  padding: 22px 20px;
  background: var(--panel);
  border: 1px solid var(--line);
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink);
}

/* ================= SERVICES ================= */
/* Separate cards with a real gap, each carrying its own hairline —
   not a 1px grid gap showing a shared background through, which reads
   as one big table rather than six things. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}
.card {
  background: var(--white);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
}
.card-media { overflow: hidden; background: var(--panel-2); }
.card-media img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  transition: transform .5s var(--ease);
}
.card:hover .card-media img { transform: scale(1.035); }
.card-body { padding: 22px 22px 24px; display: flex; flex-direction: column; flex: 1; }
.card-num {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.18em;
  color: var(--deep);
  margin-bottom: 10px;
}
.card h3 {
  font-size: 17px;
  font-weight: 800;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  line-height: 1.22;
  margin-bottom: 9px;
}
.card p { font-size: 14px; line-height: 1.58; color: var(--ink-2); }
.card-link {
  margin-top: auto;
  padding-top: 18px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
}
.card-link svg { width: 16px; height: 16px; transition: transform .2s var(--ease); }
.card-link:hover { color: var(--deep); }
.card-link:hover svg { transform: translateX(4px); }

.more-wrap { display: none; margin-top: 22px; }
.more-btn {
  width: 100%;
  padding: 16px;
  background: transparent;
  border: 1px solid var(--ink);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* ================= ABOUT FEATURE =================
   Full-bleed photograph with the copy in an opaque white card over
   it. The photo is never tinted; the card is what makes the text
   legible. */
.about-feature {
  position: relative;
  padding-block: clamp(60px, 8vw, 116px);
  overflow: hidden;
  background: var(--panel);
}
.about-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The source frame's right-hand 22% held a visible appliance brand
     name and has been cropped out of the file itself — a client site
     should not be advertising somebody else's logo. */
  object-position: center;
  filter: saturate(1.05) brightness(1.02);
}
.about-feature .container { position: relative; z-index: 2; }
.about-card {
  max-width: 620px;
  background: var(--white);
  border: 1px solid var(--line);
  padding: clamp(30px, 3.4vw, 50px);
}
.about-card-title { margin-bottom: 20px; }
.about-mission {
  font-size: clamp(16px, 1.15vw, 18px);
  line-height: 1.6;
  font-weight: 600;
  color: var(--ink);
}
.about-p {
  margin-top: 16px;
  font-size: 15px;
  line-height: 1.65;
  color: var(--ink-2);
}
.stat-row {
  margin-top: 30px;
  padding-top: 26px;
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
.stat-v {
  font-size: clamp(26px, 2.6vw, 36px);
  font-weight: 800;
  line-height: 1;
  color: var(--deep);
}
.stat-l {
  margin-top: 7px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* ================= CAROUSEL ================= */
.carousel { position: relative; }
.car-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - 2 * 22px) / 3);
  column-gap: 22px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  padding-bottom: 4px;
}
/* The Looks rail runs two-up so each look is actually big enough to
   read the tile pattern. Reviews stay three-up — they are text. */
#work .car-track { grid-auto-columns: calc((100% - 22px) / 2); }
.car-track::-webkit-scrollbar { display: none; }
.car-track > * { scroll-snap-align: start; }

.car-nav {
  position: absolute;
  top: 50%;
  z-index: 5;
  width: 46px; height: 46px;
  margin-top: -23px;
  display: none;
  align-items: center;
  justify-content: center;
  background: var(--white);
  border: 1px solid var(--ink);
  color: var(--ink);
  transition: background-color .18s var(--ease), color .18s var(--ease), opacity .18s var(--ease);
}
.carousel.is-ready .car-nav { display: flex; }
.car-nav svg { width: 20px; height: 20px; }
.car-nav:hover:not(:disabled) { background: var(--ink); color: var(--white); }
.car-nav:disabled { opacity: .25; cursor: default; }
.car-prev { left: -23px; }
.car-next { right: -23px; }

.car-dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 26px;
}
/* background-COLOR, not the `background` shorthand. The touch block at
   the end of this file grows the dot's hit area with padding and
   relies on background-clip:content-box to keep the bar 3px tall — and
   the shorthand resets background-clip, which turned the active dot
   into a solid 26x44 slab on phones. */
.car-dot {
  width: 26px; height: 3px;
  padding: 0;
  background-color: var(--line-2);
  border: none;
  transition: background-color .18s var(--ease);
}
.car-dot[aria-current="true"] { background-color: var(--deep); }

/* Work tiles — caption BELOW the photo. A caption laid over the
   image needs a gradient behind it to be readable, and that is a
   scrim by another name. */
.tile { margin: 0; }

/* The image is a link, not a button: with JS off it still opens the
   full-size file instead of being a dead control. site.js intercepts
   the click and shows the lightbox instead. */
.tile-link {
  position: relative;
  display: block;
  overflow: hidden;
  border: 1px solid var(--line);
  cursor: pointer;
}
.tile-link img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  transition: transform .5s var(--ease);
}
.tile-link:hover img { transform: scale(1.03); }

/* Zoom affordance — square, hairline, appears on hover/focus. */
.tile-zoom {
  position: absolute;
  right: 14px; bottom: 14px;
  width: 44px; height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--white);
  color: var(--ink);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .22s var(--ease), transform .22s var(--ease);
  pointer-events: none;
}
.tile-link:hover .tile-zoom,
.tile-link:focus-visible .tile-zoom { opacity: 1; transform: none; }
.tile-zoom svg { width: 19px; height: 19px; }
.tile figcaption {
  padding-top: 14px;
  font-size: 14.5px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
  display: flex;
  flex-direction: column;
}
.tile-n {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.18em;
  color: var(--deep);
  margin-bottom: 6px;
}
.tile-loc {
  margin-top: 4px;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-2);
}

/* ================= LIGHTBOX =================
   Opened from the Looks rail. The backdrop is an OPAQUE gradient plus
   the same noise layer the footer uses, not an rgba(0,0,0,x) wash:
   a flat near-black sheet at this size is exactly the case that makes
   the display pipeline dither a diagonal across the screen. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 56px);
  background-color: #14181A;
  background-image: linear-gradient(165deg, #191F22 0%, #0E1214 100%);
  opacity: 0;
  visibility: hidden;
  /* visibility is stepped, NOT eased. Putting a duration on it leaves
     the computed value 'hidden' for the whole transition, and .focus()
     silently no-ops on a hidden element — so opening the dialog could
     never move focus into it. Delay it on the way out (so the fade is
     seen), switch it instantly on the way in. */
  transition: opacity .22s var(--ease), visibility 0s linear .22s;
}
.lightbox.open {
  opacity: 1;
  visibility: visible;
  transition: opacity .22s var(--ease), visibility 0s linear 0s;
}
@media (prefers-reduced-motion: reduce) {
  .lightbox { transition: none; }
}
.lb-noise {
  position: absolute;
  inset: 0;
  opacity: .035;
  mix-blend-mode: overlay;
  pointer-events: none;
}

.lb-figure {
  position: relative;
  z-index: 1;
  margin: 0;
  max-width: min(1180px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.lb-figure img {
  max-width: 100%;
  max-height: calc(100dvh - 210px);
  width: auto;
  object-fit: contain;
  background: var(--panel-2);
}
.lb-cap {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 16px;
  color: var(--white);
}
.lb-title {
  font-size: 16px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.lb-sub { font-size: 14px; color: rgba(255, 255, 255, .68); }
.lb-count {
  margin-left: auto;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: rgba(255, 255, 255, .55);
}

.lb-btn {
  position: absolute;
  z-index: 2;
  width: 52px; height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, .34);
  color: var(--white);
  padding: 0;
  transition: background-color .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.lb-btn:hover:not(:disabled) { background: var(--white); color: var(--ink); border-color: var(--white); }
.lb-btn:disabled { opacity: .28; cursor: default; }
.lb-btn svg { width: 22px; height: 22px; }
.lb-close { top: clamp(14px, 3vw, 30px); right: clamp(14px, 3vw, 30px); }
.lb-prev { left: clamp(10px, 2vw, 26px); top: 50%; margin-top: -26px; }
.lb-next { right: clamp(10px, 2vw, 26px); top: 50%; margin-top: -26px; }

@media (max-width: 680px) {
  .lb-figure img { max-height: calc(100dvh - 260px); }
  /* Side arrows would sit on top of the picture at this width. */
  .lb-prev { left: 50%; top: auto; bottom: 18px; margin: 0 0 0 -58px; }
  .lb-next { right: 50%; top: auto; bottom: 18px; margin: 0 -58px 0 0; }
  .lb-count { margin-left: 0; width: 100%; }
}

/* ================= REVIEWS ================= */
.reviews-head {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  align-items: flex-end;
  justify-content: space-between;
  margin-bottom: clamp(34px, 4vw, 54px);
}
.reviews-score {
  display: flex;
  align-items: center;
  gap: 34px;
  flex-wrap: wrap;
}
.rs-item .stat-v { color: var(--deep); }

.review {
  margin: 0;
  padding: 30px 28px;
  background: var(--white);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  height: 100%;
}
.rv-stars { display: flex; gap: 3px; color: var(--deep); margin-bottom: 16px; }
.rv-stars svg { width: 15px; height: 15px; }
/* flex:1 so the attribution sits on the card's bottom edge no matter
   how short the review is — these run from four words to a paragraph. */
.review blockquote {
  flex: 1;
  font-size: 14.5px;
  line-height: 1.68;
  color: var(--ink-2);
}
.review figcaption {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.rv-name {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}
.rv-meta { font-size: 12px; color: var(--ink-3); }

/* ================= CTA =================
   Full-bleed clip of a tile going down. White type over it, no
   tint — legibility comes from the copy sitting in the frame's
   own dark, low-variance region plus a contour shadow. */
.cta {
  position: relative;
  min-height: 560px;
  display: flex;
  align-items: center;
  padding-block: clamp(78px, 9vw, 130px);
  overflow: hidden;
  background: var(--deep-2);
}
.cta-media { position: absolute; inset: 0; z-index: 0; }
.cta-media img,
.cta-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 62%;
  filter: saturate(1.05) brightness(1.02);
}
.cta-media video { opacity: 0; transition: opacity .6s var(--ease); }
.cta-media video.ready { opacity: 1; }

.cta-inner { position: relative; z-index: 2; }

/* The copy rides in an opaque petrol panel rather than on bare
   footage: this clip is a high-variance jobsite plate (wet thinset
   ridges, grout lines, a moving hand) and no ink colour survives
   it everywhere. Panel the copy — do not chase crops. */
.cta-copy {
  max-width: 620px;
  padding: clamp(32px, 3.6vw, 52px);
  background: var(--deep);
  background-image: linear-gradient(160deg, var(--deep) 0%, var(--deep-2) 100%);
  position: relative;
  overflow: hidden;
}
.cta-noise {
  position: absolute;
  inset: 0;
  opacity: .035;
  mix-blend-mode: overlay;
  pointer-events: none;
}
.cta-copy > *:not(.cta-noise) { position: relative; z-index: 1; }
.cta-copy .label { color: #A8C6CE; }
.cta-copy .display { color: var(--white); }
.cta-copy .lede { color: rgba(255, 255, 255, .84); }
/* Inverted accent — a petrol block on petrol would vanish. */
.cta-copy .hl { background: var(--white); color: var(--deep); }
.cta-actions {
  margin-top: 30px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

/* ================= FOOTER ================= */
.footer {
  position: relative;
  /* Gradient + noise, never a flat fill — see the header note. */
  background: linear-gradient(180deg, var(--deep) 0%, var(--deep-2) 100%);
  color: rgba(255, 255, 255, .74);
  padding-block: clamp(56px, 7vw, 88px) 0;
  overflow: hidden;
}
.footer-noise {
  position: absolute;
  inset: 0;
  opacity: .035;
  mix-blend-mode: overlay;
  pointer-events: none;
}
.footer .container { position: relative; z-index: 1; }

.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1.3fr;
  gap: clamp(28px, 3.4vw, 56px);
}
.footer-brand .brand { color: var(--white); margin-bottom: 18px; }
.footer-brand .brand-sub { color: rgba(255, 255, 255, .6); }
.footer-brand p { font-size: 14px; line-height: 1.65; max-width: 34ch; }
.footer-reg {
  margin-top: 14px !important;
  font-size: 12px;
  color: rgba(255, 255, 255, .55);
}
.footer h3 {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--white);
  margin-bottom: 18px;
}
.footer ul li { margin-bottom: 11px; }
.footer ul a, .footer ul span {
  font-size: 14px;
  text-decoration: none;
  color: rgba(255, 255, 255, .74);
  transition: color .18s var(--ease);
}
.footer ul a:hover { color: var(--white); }

.footer-social { display: flex; gap: 10px; margin-top: 18px; }
.footer-social a {
  width: 40px; height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, .28);
  color: var(--white);
  transition: background-color .18s var(--ease), color .18s var(--ease);
}
.footer-social a:hover { background: var(--white); color: var(--deep); }
.footer-social svg { width: 18px; height: 18px; }

.footer-bottom {
  margin-top: clamp(40px, 5vw, 64px);
  padding-block: 24px;
  border-top: 1px solid rgba(255, 255, 255, .16);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  font-size: 12.5px;
  color: rgba(255, 255, 255, .58);
}

/* ================= MOBILE STICKY BAR =================
   Two individual buttons floating just above the bottom edge — NOT a
   full-width bar flush to the screen (which sat behind browser chrome
   and read as one blocked panel). Lifted by a real bottom margin plus
   the safe-area inset, and set apart with a gap so they read as two
   distinct actions. A modest shadow lifts them off arbitrary content —
   the only shadow on the site, and a functional one, since these float
   over whatever is scrolled behind them. */
.mobile-cta {
  position: fixed;
  left: 16px; right: 16px;
  bottom: calc(env(safe-area-inset-bottom) + 16px);
  z-index: 58;
  display: none;
  gap: 10px;
  transform: translateY(calc(100% + 24px));
  transition: transform .28s var(--ease), opacity .28s var(--ease);
  opacity: 0;
  background: transparent;
}
.mobile-cta.show { transform: none; opacity: 1; }
.mobile-cta a {
  flex: 1;
  min-height: 54px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  text-decoration: none;
  box-shadow: 0 6px 20px rgba(20, 24, 26, .22);
}
.mobile-cta svg { width: 17px; height: 17px; }
.mc-call { background: var(--white); color: var(--ink); border: 1px solid var(--line-2); }
.mc-quote { background: var(--deep); color: var(--white); }

/* ================= REVEAL ================= */
.js-reveal .reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.js-reveal .reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal { opacity: 1; transform: none; transition: none; }
}

/* ================= CONTACT PAGE ================= */
.page-head {
  padding-top: calc(var(--nav-h) + clamp(48px, 6vw, 90px));
  padding-bottom: clamp(40px, 5vw, 66px);
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 460px;
  gap: clamp(34px, 4.5vw, 72px);
  align-items: start;
}
.info-list { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); }
.info-row {
  background: var(--white);
  padding: 22px 24px;
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: 18px;
  align-items: baseline;
}
.info-k {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.info-v { font-size: 15.5px; line-height: 1.6; color: var(--ink); }
.info-v a { color: var(--deep); font-weight: 700; text-decoration: none; }
.info-v a:hover { text-decoration: underline; }
.contact-card {
  background: var(--white);
  border: 1px solid var(--line);
  padding: clamp(26px, 2.6vw, 36px);
}
.map-embed {
  margin-top: 34px;
  border: 1px solid var(--line);
  width: 100%;
  height: 340px;
  filter: grayscale(1) contrast(1.05);
}

/* ================= RESPONSIVE =================
   NOTE: a media query adds no specificity. Anything appended after
   these blocks at the same specificity wins regardless of width, so
   the touch/mobile rules must stay LAST in this file. */
/* The wide container is capped by the viewport on most laptops, so the
   page gutter is whatever --pad is. 24px leaves body copy almost
   touching the screen edge; 36px keeps the extra width the layout
   gained without the text hugging. */
@media (min-width: 1200px) {
  :root { --pad: 36px; }
}

@media (max-width: 1180px) {
  .hero-inner { grid-template-columns: minmax(0, 1fr) 380px; }
  .nav-links { gap: 22px; }
}

@media (max-width: 1080px) {
  .nav-links { display: none; }
  .menu-btn { display: flex; }
  .nav-brand { opacity: 1; visibility: visible; transform: none; }
  .hero-brand { display: none; }

  .hero {
    min-height: auto;
    padding-top: calc(var(--nav-h) + 26px);
  }
  .hero-inner { grid-template-columns: 1fr; }
  .hero-copy { max-width: none; }
  .hero-quote { margin-top: 8px; }

  .card-grid { grid-template-columns: repeat(2, 1fr); }
  .car-track { grid-auto-columns: calc((100% - 22px) / 2); }
  .car-prev { left: 8px; }
  .car-next { right: 8px; }

  .footer-grid { grid-template-columns: 1fr 1fr; }
  .contact-grid { grid-template-columns: 1fr; }
}

@media (max-width: 820px) {
  .about-card { max-width: none; }
  .reviews-head { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 680px) {
  :root { --pad: 20px; --nav-h: 64px; }

  .hm-desk { display: none; }
  .hm-mob { display: block; }

  /* MOBILE HERO — the clip fills the first screen and the copy is
     pinned to the BOTTOM of it, so the top of the frame reads clean as
     tile samples and the near-black ink sits over the bright ground at
     the base. Everything down to the rating badge stays inside the fold
     on any phone: the sizes below track svh, the short-viewport block
     lower down tightens further and drops the sub's tail, and the form
     flows in just under the fold on the light panel. Verified against
     real viewports (320-wide up) by tools/verify.js, not assumed. */
  .hero {
    min-height: 100svh;
    align-items: stretch;
    padding-top: 0;
    padding-bottom: 0;
    background: var(--panel);
  }
  .hero-media { height: 100svh; }
  .hero-inner { gap: 0; }

  .hero-copy {
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding-top: calc(var(--nav-h) + 8px);
    /* Bottom-aligned, but lifted off the bottom edge so the block sits in
       the lower third rather than jammed at the very bottom. Scales with
       svh so the lift is proportional, not a hardcoded offset; the
       short-viewport tiers keep it from pushing the copy under the nav. */
    padding-bottom: clamp(72px, 13svh, 120px);
    max-width: none;
  }
  .hero-eyebrow { margin-bottom: 10px; }
  .hero-title {
    font-size: clamp(30px, 8svh, 46px);
    line-height: 1.1;
    max-width: none;
  }
  .hero-sub {
    margin-top: 14px;
    max-width: none;
    font-size: 15px;
  }
  .hero-actions {
    margin-top: 22px;
    flex-direction: column;
    align-items: stretch;
  }
  .hero-actions .btn { width: 100%; }
  .rating-badge { margin-top: 20px; width: 100%; }

  .hero-quote {
    margin-top: 30px;
    margin-inline: calc(var(--pad) * -1);
    border-inline: none;
    border-bottom: none;
  }

  .card-grid { grid-template-columns: 1fr; }
  .card-grid[data-collapsed="true"] > .card:nth-child(n + 4) { display: none; }
  .more-wrap { display: block; }

  /* SHOWROOM — the floating-card-over-full-bleed-photo pattern only
     works with room to spare it. On a phone the card fills the frame and
     the photo vanishes, so stack instead: a real image band on top, the
     card below overlapping it, so both are visible. No tint on the photo. */
  .about-feature { padding-block: 0; }
  .about-bg {
    position: static;
    width: 100%;
    height: clamp(230px, 40svh, 320px);
    object-position: center;
  }
  .about-feature .container { padding-inline: var(--pad); }
  .about-card {
    margin-top: -56px;   /* overlap the band */
    max-width: none;
    padding: 28px 22px 26px;
  }

  /* CTA — same problem: the copy panel covered the whole clip. Pin it to
     the bottom of a taller block so the tile-setting footage reads above
     it. The panel stays opaque (this clip is high-variance; no ink
     survives it bare — the panel is the legibility, never a tint). */
  .cta {
    /* Floor as well as svh: the opaque panel is ~465px, so on a short
       phone 86svh alone leaves no video above it. max() keeps a video
       strip over the panel on every size. */
    min-height: max(86svh, 620px);
    align-items: flex-end;
    padding-block: 0;
  }
  .cta-media { height: 100%; }
  .cta-inner { padding-bottom: clamp(20px, 5svh, 46px); }
  .cta-copy { padding: 28px 22px 26px; }
  .cta-actions { margin-top: 24px; }

  /* One item at a time on mobile — including the Looks rail, whose
     two-up desktop rule (#work .car-track) has to be re-beaten here
     with equal-or-higher specificity, so each look is big enough to
     read. */
  .car-track,
  #work .car-track { grid-auto-columns: 100%; }

  /* Show the arrows on mobile as a clear "this scrolls" affordance,
     flanking the dots in a control row at the bottom of each carousel
     rather than floating over the media. */
  .carousel { padding-bottom: 56px; }
  .carousel.is-ready .car-nav {
    display: flex;
    position: absolute;
    top: auto;
    bottom: 0;
    margin-top: 0;
    width: 46px;
    height: 46px;
  }
  .car-prev { left: 0; right: auto; }
  .car-next { right: 0; left: auto; }
  .car-dots {
    position: absolute;
    left: 56px;
    right: 56px;
    bottom: 0;
    margin-top: 0;
    height: 46px;
    align-items: center;
  }

  .q-row { grid-template-columns: 1fr; gap: 0; }

  .stat-row { grid-template-columns: 1fr 1fr; gap: 22px 16px; }

  .footer-grid { grid-template-columns: 1fr; gap: 34px; }
  .footer-bottom { flex-direction: column; gap: 8px; }

  .info-row { grid-template-columns: 1fr; gap: 6px; }

  .mobile-cta { display: flex; }
  /* Clear the sticky bar so it never covers the last line of copy. */
  .footer { padding-bottom: 78px; }
}

@media (max-width: 400px) {
  .rating-badge { gap: 10px; padding: 11px 14px; }
  /* Keep the two sticky labels on one line each on narrow phones. */
  .mobile-cta a { font-size: 11px; letter-spacing: 0.04em; gap: 7px; }
  .mobile-cta { gap: 8px; left: 12px; right: 12px; }
}

/* SHORT-VIEWPORT PHONES — keep the whole copy block (down to the rating
   badge) above the fold even on small screens, by tightening the rhythm
   and dropping the sub's second sentence, which reads complete without
   it. Two tiers so a really short screen compresses harder. The
   badge-in-fold guarantee is asserted per-viewport in verify.js. */
@media (max-width: 680px) and (max-height: 760px) {
  .hero-eyebrow { margin-bottom: 8px; }
  .hero-title { font-size: clamp(26px, 6.6svh, 40px); line-height: 1.08; }
  .hero-sub { margin-top: 10px; font-size: 14px; }
  .hero-sub-more { display: none; }
  .hero-actions { margin-top: 16px; gap: 9px; }
  .rating-badge { margin-top: 14px; }
}
@media (max-width: 680px) and (max-height: 640px) {
  .hero-title { font-size: clamp(23px, 5.8svh, 32px); line-height: 1.05; }
  .hero-sub { margin-top: 8px; font-size: 13.5px; }
  .hero-actions { margin-top: 12px; }
  .rating-badge { margin-top: 12px; }
  .hero-copy { padding-bottom: 14px; }
}

/* ---------- TOUCH SIZING — keep this block LAST ---------- */
@media (hover: none) and (pointer: coarse) {
  .nav-link, .mobile-menu a, .card-link, .footer ul a { min-height: 44px; display: flex; align-items: center; }
  .card-link { display: inline-flex; }
  .btn { min-height: 48px; }
  /* The dot stays a 3px bar visually; the padding is what makes the
     hit area reach 44px. background-clip keeps the padding transparent
     so the bar does not grow with it. */
  .car-dot { min-height: 44px; background-clip: content-box; padding-block: 21px; }
  .menu-btn { width: 48px; height: 48px; }
  .footer-social a { width: 44px; height: 44px; }
  /* Hover zoom is meaningless without a pointer and only causes a
     stuck transform after a tap. */
  .card:hover .card-media img { transform: none; }
}
