/* ═══════════════════════════════════════════════════════════════
   MASTERS AIR DUCT CLEANING SA — "Inside the Duct"

   The site it replaces was an unmodified Bootstrap example. Its own
   markup still credited "Mark Otto, Jacob Thornton, and Bootstrap
   contributors". Cookie-cutter was not an opinion about it, it was
   literally what it was.

   THE IDEA
   Every competitor in this trade opens on a bright stock photo of a
   smiling family in a clean living room. That picture is about the
   result. It is also the picture everyone else has.

   This one opens where the problem actually is: dark, warm, close —
   the inside of a duct — with dust hanging in the air. Then the page
   cleans as you move through it. Dark and dirty at the top, bright
   and clean at the bottom. The layout performs the service.

   COLOUR AS ARGUMENT
     soot     the duct. Cool near-black. It STARTED warm (#100E0C,
              "old sheet metal") and that was wrong: warm black plus
              cyan is a colour clash, and the owner caught it —
              "that brownish colour is too yellowy"
     dust     the enemy. WARM, dirty — and now used for the canvas
              particles and NOTHING else. Real dirt is warm
     haze     every piece of text on a dark ground. Cool, blue-hinted
              light grey. Splitting this out of `dust` is the whole
              fix: the DIRT is warm, the INTERFACE is cool, and they
              are no longer the same token doing two jobs
     fresh    filtered air. Cold cyan, deliberately the OPPOSITE
              temperature to the dust — the two colours argue with
              each other, which is the entire sales pitch in two
              swatches. Every call to action is this colour, so the
              action and the outcome are the same thing on screen
     ember    danger. Dryer-vent fire risk ONLY. Used once on the
              whole page, which is what keeps it meaning something
     clean    the result. The bottom half of the page

   The accent was amber, taken from the yellow glove in the client's
   own hero photograph. It was changed 2026-08-11 because Beeson's —
   another site by this shop — already owns amber-on-dark, and two
   sites from the same builder must not look like the same business.
   Cyan turned out to be the better argument anyway.

   The type is tight, industrial and unfussy. This is a trade that
   sells competence, not taste.
   ═══════════════════════════════════════════════════════════════ */

@font-face{
  font-family:"Inter";
  src:url("../fonts/Inter-latin.woff2") format("woff2");
  font-weight:100 900;
  font-display:swap;
  font-style:normal;
}

:root{
  --soot:    #0D0F12;
  --soot-2:  #14171B;
  --soot-3:  #1E232A;
  --dust:    #D6C9B2;   /* particles ONLY - real dirt is warm */
  --haze:    #C2CCD6;   /* all text on dark - cool, 11.9:1 on soot */
  --fresh:   #45C7E8;
  --fresh-d: #12708F;
  --ember:   #D8462B;   /* on DARK only */
  --ember-d: #B3341C;   /* on light - 5.2:1. #D8462B measured 3.84 there */

  --clean:   #F7F9FC;
  --clean-2: #E7EDF3;
  --ink:     #12151A;
  --ink-2:   #414852;   /* 8.8:1 on clean */
  --ink-3:   #5E6673;   /* 4.91:1 on clean-2, the tighter of the two grounds — do not lighten */
  --rule:    #D3DCE5;

  --shell: 1180px;
  --ease:  cubic-bezier(.22,1,.36,1);
  --ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --num: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;
}

*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *{ animation:none !important; transition:none !important; }
}

body{
  margin:0;
  background:var(--clean);
  color:var(--ink);
  font:400 17px/1.6 var(--ui);
  letter-spacing:-.006em;
  -webkit-font-smoothing:antialiased;
}

h1,h2,h3{ margin:0; font-weight:750; letter-spacing:-.03em; line-height:1.04; }
p{ margin:0 0 1em; }
ol,ul,dl,dd{ margin:0; padding:0; list-style:none; }
img{ max-width:100%; height:auto; display:block; }

a{ color:inherit; }
:focus-visible{ outline:3px solid var(--fresh); outline-offset:3px; border-radius:2px; }

.shell{ width:100%; max-width:var(--shell); margin:0 auto; padding:0 26px; }

.skip{
  position:absolute; left:-9999px; top:0; z-index:200;
  background:var(--fresh); color:#000; padding:12px 18px; font-weight:700;
}
.skip:focus{ left:0; }

/* ── buttons ─────────────────────────────────────────────────── */
.btn{
  display:inline-flex; align-items:center; gap:10px;
  min-height:48px; padding:0 22px;
  background:var(--fresh); color:#06222B;
  font-weight:700; font-size:16px; letter-spacing:-.01em;
  border:0; border-radius:3px; text-decoration:none;
  transition:background .15s var(--ease), transform .15s var(--ease);
}
.btn:hover{ background:#7ADCF5; transform:translateY(-1px); }
.btn:active{ transform:none; }
.btn--lg{ min-height:58px; padding:0 30px; font-size:18px; }
.btn__ico{ display:inline-flex; }

/* ── masthead ────────────────────────────────────────────────── */
.mast{
  position:sticky; top:0; z-index:60;
  background:rgba(13,15,18,.92);
  backdrop-filter:saturate(1.3) blur(10px);
  border-bottom:1px solid rgba(194,204,214,.16);
}
.mast__in{ display:flex; align-items:center; gap:20px; min-height:70px; }
/* 44px minimum target height (WCAG 2.5.8) — it measured 33px, and a
   masthead home link is a control, not prose. */
.mark{ text-decoration:none; line-height:1.1; display:flex; flex-direction:column; justify-content:center; min-height:44px; }
.mark__m{ display:block; color:var(--clean); font-size:19px; font-weight:800; letter-spacing:-.03em; }
.mark__s{ display:block; color:var(--haze); font-size:11px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; }
.mark__s b{ color:var(--fresh); }
.mast__nav{ display:flex; gap:26px; margin-left:auto; }
.mast__nav a{
  color:var(--haze); font-size:15px; font-weight:500; text-decoration:none;
  display:inline-flex; align-items:center; min-height:44px;
}
.mast__nav a:hover{ color:var(--fresh); }
.mast__call{ font-family:var(--num); font-size:15px; letter-spacing:-.03em; }

/* ── hero ────────────────────────────────────────────────────── */
.hero{
  position:relative; isolation:isolate;
  background:var(--soot);
  min-height:min(90vh,760px);
  display:flex; align-items:flex-end;
  overflow:hidden;
  padding:0 0 68px;
}
.hero__img{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; z-index:0;
  /* Was brightness(.44) under an .86 scrim, which buried the vent and
     the brush entirely — the photograph was paying rent and doing no
     work. Opened up so the subject reads. The scrim is now weighted
     toward the BOTTOM LEFT only, where the copy actually sits, instead
     of flattening the whole frame. */
  filter:grayscale(.18) contrast(1.12) brightness(.66);
}
.hero::after{
  content:""; position:absolute; inset:0; z-index:1;
  background:
    /* Bottom only, +5%. The top of this frame was right and is
       untouched; the extra weight sits behind the glove, under the
       white lettering and down toward the vacuum head. */
    radial-gradient(95% 85% at 22% 88%, rgba(13,15,18,.91) 0%, rgba(13,15,18,.34) 62%, rgba(13,15,18,0) 100%),
    linear-gradient(180deg, rgba(13,15,18,.58) 0%, rgba(13,15,18,.12) 38%, rgba(13,15,18,.83) 100%);
}
/* The canvas sits above the scrim but below the copy, and can never
   take a tap — the phone number must always win. */
.hero__dust{ position:absolute; inset:0; width:100%; height:100%; z-index:2; pointer-events:none; }
.hero__in{ position:relative; z-index:3; }

.eyebrow{
  font-family:var(--num); font-size:12px; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase; color:var(--haze);
  opacity:.85; margin:0 0 24px;
}
.hero h1{
  color:var(--clean);
  font-size:clamp(46px,9vw,116px);
  letter-spacing:-.045em;
  margin:0 0 28px;
  text-shadow:0 2px 40px rgba(0,0,0,.5);
}
.hero h1 em{ font-style:normal; color:var(--fresh); }
.hero__lede{
  color:var(--haze); font-size:clamp(16.5px,1.6vw,19px);
  max-width:56ch; margin:0 0 34px;
}
.hero__act{ display:flex; align-items:center; gap:20px; flex-wrap:wrap; }
.hero__hint{ color:var(--haze); opacity:.7; font-size:14.5px; }

.hero__scroll{
  position:absolute; right:26px; bottom:26px; z-index:3;
  margin:0; font-family:var(--num); font-size:11px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--haze); opacity:.4;
}

/* THE INTAKE — the visible cause of the suction. Sized and placed to
   match what dust.js reads from getBoundingClientRect(), so the maths
   and the picture always agree. */
.intake{
  position:absolute; z-index:2; right:64px; bottom:104px;
  width:74px; height:74px; display:grid; place-items:center;
  pointer-events:none;
}
.intake__ring{
  position:absolute; inset:0; border-radius:50%;
  border:2px solid rgba(194,204,214,.32);
  box-shadow:inset 0 0 22px rgba(0,0,0,.85);
  background:radial-gradient(circle at 50% 50%, rgba(0,0,0,.9) 30%, rgba(38,33,25,.6) 100%);
}
.intake__mesh{
  position:absolute; width:44px; height:44px; border-radius:50%;
  background-image:
    repeating-linear-gradient(0deg, rgba(194,204,214,.30) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg, rgba(194,204,214,.30) 0 1px, transparent 1px 5px);
}

/* ── the offer band ──────────────────────────────────────────── */
.offer{ background:var(--soot-2); border-bottom:1px solid rgba(194,204,214,.14); }
.offer__in{ display:flex; align-items:baseline; gap:26px; flex-wrap:wrap; padding:30px 26px; }
.offer__lab{
  font-family:var(--num); font-size:11.5px; font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color:var(--haze); opacity:.75; margin:0;
}
.offer__fig{
  font-family:var(--num); font-size:clamp(38px,6vw,58px); font-weight:700;
  letter-spacing:-.04em; color:var(--fresh); margin:0; line-height:1;
}
.offer__say{ color:var(--haze); margin:0; max-width:52ch; font-size:16px; }
.offer__say a{ color:var(--fresh); font-family:var(--num); white-space:nowrap; }

/* ── section furniture ───────────────────────────────────────── */
.problem,.process,.close{ padding:96px 0; }
.sec{ max-width:62ch; margin:0 0 56px; }
.sec__lab{
  font-family:var(--num); font-size:11.5px; font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-3); margin:0 0 16px;
}
.sec h2{ font-size:clamp(32px,4.6vw,54px); margin:0 0 18px; }
.sec__sub{ color:var(--ink-2); font-size:clamp(16.5px,1.6vw,19px); margin:0; }

/* ── problem ─────────────────────────────────────────────────── */
.problem{ background:var(--clean-2); }
.facts{ display:grid; grid-template-columns:repeat(3,1fr); gap:0; border-top:1px solid var(--ink); }
.fact{ padding:30px 30px 34px 0; border-bottom:1px solid var(--rule); }
.fact + .fact{ border-left:1px solid var(--rule); padding-left:30px; }
.fact h3{ font-size:21px; margin:0 0 12px; }
.fact p{ color:var(--ink-2); font-size:16px; margin:0; }
/* The single use of ember on the page. One warning is a warning;
   three warnings are wallpaper. */
.fact--danger h3{ color:var(--ember-d); }

.shot{ margin:0; }
.shot img{ width:100%; border-radius:4px; }
.shot figcaption{
  margin-top:12px; font-size:14.5px; color:var(--ink-3);
  font-family:var(--num); letter-spacing:-.01em;
}
.shot--wide{ margin-top:56px; }
.shot--tall img{ max-width:420px; }

/* ── process ─────────────────────────────────────────────────── */
.steps{ display:grid; gap:0; border-top:1px solid var(--ink); }
.step{ padding:44px 0; border-bottom:1px solid var(--rule); display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.15fr); gap:56px; align-items:start; }
.step__n{
  font-family:var(--num); font-size:11.5px; font-weight:700; letter-spacing:.12em;
  text-transform:uppercase; color:var(--ink-3); margin:0 0 12px;
}
.step h3{ font-size:clamp(26px,3.2vw,36px); margin:0 0 16px; }
.step > p{ color:var(--ink-2); font-size:17px; max-width:48ch; }
.step .shot{ grid-column:2; grid-row:1 / span 4; }
.step--key .step__n{ color:var(--fresh-d); }
.step--key h3::after{
  content:""; display:block; width:56px; height:3px; margin-top:16px; background:var(--fresh);
}

/* ── dryer vents ─────────────────────────────────────────────── */
.dryer{ background:var(--soot); color:var(--clean); padding:96px 0; }
.dryer__in{ display:grid; grid-template-columns:1.05fr .95fr; gap:64px; align-items:center; }
.dryer .sec__lab{ color:var(--haze); opacity:.75; }
.dryer h2{ font-size:clamp(34px,5vw,62px); margin:0 0 22px; color:var(--clean); }
.dryer h2::after{
  content:""; display:block; width:72px; height:4px; margin-top:20px; background:var(--ember);
}
.dryer p{ color:var(--haze); font-size:17.5px; max-width:52ch; }
.dryer__tell{ font-size:16px; opacity:.9; }
.dryer__tell b{ color:var(--clean); }
.dryer__shot img{ border-radius:4px; }

/* ── close ───────────────────────────────────────────────────── */
.close{ text-align:center; }
.close h2{ font-size:clamp(32px,4.6vw,54px); margin:0 0 18px; }
.close__say{ color:var(--ink-2); font-size:18px; max-width:50ch; margin:0 auto 32px; }
.close__act{ margin:0 0 56px; }
.meta{
  display:grid; grid-template-columns:repeat(3,1fr); gap:0;
  border-top:1px solid var(--rule); text-align:left; max-width:900px; margin:0 auto;
}
.meta > div{ padding:22px 24px 22px 0; }
.meta > div + div{ border-left:1px solid var(--rule); padding-left:24px; }
.meta dt{
  font-family:var(--num); font-size:11.5px; font-weight:600; letter-spacing:.12em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:8px;
}
.meta dd{ font-size:16px; color:var(--ink-2); }

/* ── footer ──────────────────────────────────────────────────── */
.foot{ background:var(--soot); color:var(--haze); padding:56px 0 40px; }
.foot p{ margin:0 0 8px; font-size:15.5px; }
.foot__mark{ color:var(--clean); font-size:19px; font-weight:800; letter-spacing:-.03em; }
.foot__mark b{ color:var(--fresh); }
.foot__tel{
  font-family:var(--num); font-size:20px; color:var(--fresh); text-decoration:none;
  display:inline-flex; align-items:center; min-height:44px;
}
.foot__tel:hover{ text-decoration:underline; }
.foot__legal{ margin-top:26px; opacity:.55; font-size:13.5px; }

/* ── sticky call bar (phones only) ───────────────────────────── */
.callbar{ display:none; }

@media (max-width:900px){
  .step{ grid-template-columns:1fr; gap:22px; }
  .step .shot{ grid-column:1; grid-row:auto; }
  .dryer__in{ grid-template-columns:1fr; gap:36px; }
  .facts,.meta{ grid-template-columns:1fr; }
  .fact + .fact{ border-left:0; padding-left:0; }
  .fact{ padding-right:0; }
  .meta > div + div{ border-left:0; padding-left:0; border-top:1px solid var(--rule); }
  .mast__nav{ display:none; }
}

@media (max-width:640px){
  body{ font-size:16px; }
  .shell{ padding:0 18px; }
  .hero{ min-height:82vh; padding-bottom:52px; }
  .problem,.process,.close,.dryer{ padding:64px 0; }
  .offer__in{ padding:24px 18px; gap:14px; }
  .intake{ right:22px; bottom:150px; width:58px; height:58px; }
  .intake__mesh{ width:34px; height:34px; }
  .hero__scroll{ display:none; }
  .shot--tall img{ max-width:100%; }

  /* The masthead call button collapses to the icon; the sticky bar
     below takes over as the primary action. */
  .mast__call{ font-size:0; gap:0; padding:0 14px; }
  .mast__call .btn__ico{ font-size:16px; }

  body{ padding-bottom:62px; }   /* room for the call bar */
  .callbar{
    display:flex; position:fixed; left:0; right:0; bottom:0; z-index:80;
    align-items:center; justify-content:center; gap:10px;
    min-height:58px; padding:0 16px;
    background:var(--fresh); color:#06222B;
    font-size:15px; font-weight:600; text-decoration:none;
    box-shadow:0 -6px 24px rgba(0,0,0,.22);
  }
  .callbar b{ font-family:var(--num); letter-spacing:-.03em; }
}


/* ── BEFORE / AFTER ──────────────────────────────────────────────
   The best asset on the site, at full width. The photograph is one
   file; each pane shows half of it with background-position, so the
   split cannot drift and nothing downloads twice.

   Each pane carries a particle field: dense warm swirl on the left
   (the air in a dirty duct), a few cool motes running away down the
   duct on the right (airflow). The comparison is the argument, and
   the particles are the argument in motion. */
.cmp{ background:var(--soot); color:var(--clean); padding:96px 0 88px; }
.cmp .sec__lab{ color:var(--haze); opacity:.75; }
.cmp .sec h2{ color:var(--clean); }
.cmp .sec__sub{ color:var(--haze); }

.cmp__pair{
  display:grid; grid-template-columns:1fr 1fr; gap:2px;
  background:rgba(194,204,214,.18);
  border-top:1px solid rgba(194,204,214,.18);
  border-bottom:1px solid rgba(194,204,214,.18);
}
.cmp__pane{
  position:relative; margin:0; overflow:hidden;
  /* The source half is square (620x620), but a square pane rendered
     952px tall on a 1280 screen and pushed everything below it off the
     page. Cropped to 5:4, anchored so the duct mouth stays in frame. */
  aspect-ratio:5 / 4;
  background-image:url("../images/duct-before-after.webp");
  background-size:200% 100%;
  background-repeat:no-repeat;
}
/* 200% wide, anchored left or right — exactly half the source each. */
.cmp__pane--dirty{ background-position:left center; }
.cmp__pane--clean{ background-position:right center; }

/* Both halves of this photograph are BRIGHT — pale galvanised metal.
   Cream particles on top of it were invisible, which defeated the
   entire point. This scrim knocks the photo back just enough for the
   motes to register, and it doubles as the caption's backdrop. It is
   NOT a style choice; without it the effect does not exist. */
.cmp__pane::before{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(180deg, rgba(13,15,18,.34) 0%, rgba(13,15,18,.20) 45%, rgba(13,15,18,.52) 100%);
}
.cmp__pane--dirty::before{
  /* The dirty side gets a touch more, warm-shifted — it should feel
     closer and heavier than the clean one. */
  background:linear-gradient(180deg, rgba(20,16,12,.42) 0%, rgba(20,16,12,.26) 45%, rgba(20,16,12,.58) 100%);
}
.cmp__fx{ position:absolute; inset:0; width:100%; height:100%; z-index:2; pointer-events:none; }

.cmp__pane figcaption{
  position:absolute; left:0; right:0; bottom:0; z-index:3;
  padding:44px 22px 18px;
  font-size:15px; color:var(--clean);
  background:linear-gradient(180deg, rgba(13,15,18,0) 0%, rgba(13,15,18,.88) 62%);
}
.cmp__tag{
  display:inline-block; margin-right:10px; padding:3px 9px;
  font-family:var(--num); font-size:11px; font-weight:700;
  letter-spacing:.1em; text-transform:uppercase; border-radius:2px;
}
.cmp__tag--bad{ background:var(--ember); color:#fff; }
.cmp__tag--good{ background:var(--fresh); color:#06222B; }

.cmp__note{
  display:block; margin:22px auto 0;
  font-family:var(--num); font-size:13.5px; color:var(--haze); opacity:.6;
}

@media (max-width:700px){
  /* Stacked, and shorter — two tall panes on a phone would push the
     rest of the page below three screens of scrolling. */
  .cmp__pair{ grid-template-columns:1fr; }
  .cmp__pane{ aspect-ratio:16 / 11; }
  .cmp{ padding:64px 0 56px; }
}
