@charset "UTF-8";
/* =============================================================================
   Master Icon Renovation Group — design system
   Built on the MIRG brand palette (see /brand/README.md).

   CONTRAST NOTES (WCAG 2.1 AA, measured against Site Charcoal #14181B):
     Plaster  #EDEBE7  15.0:1  -> body copy, headings          PASS AAA
     Muted    #A8AEB3   7.97:1 -> secondary copy               PASS AAA
     Maple    #DE2438   3.75:1 -> LARGE TEXT + ICONS ONLY      PASS AA Large
     Graphite #6A7075   3.56:1 -> RULES AND BORDERS ONLY       (not text)
     White on Maple     4.76:1 -> button labels                PASS AA

   Maple Lift is deliberately never used for body-size text. Where red is
   needed at small sizes it appears as a filled button with white text, or
   as an icon stroke (graphics need only 3:1).
   ========================================================================== */

/* ------------------------------------------------------------------ tokens */
:root {
  /* brand */
  --ink:        #0B0E10;
  --charcoal:   #14181B;
  --surface:    #191E22;
  --surface-2:  #21272C;
  --line:       #2C3338;
  --line-soft:  rgba(237, 235, 231, .10);
  --plaster:    #EDEBE7;
  --muted:      #A8AEB3;
  --graphite:   #6A7075;
  --red:        #DE2438;
  --red-deep:   #C8102E;
  --red-glow:   rgba(222, 36, 56, .35);

  /* type */
  --f-display: "Anton", "Arial Narrow", "Haettenschweiler", sans-serif;
  --f-ui:      "Oswald", "Arial Narrow", sans-serif;
  --f-body:    "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* fluid type scale */
  --t-xs:   .75rem;   /* 12 - labels only */
  --t-sm:   .875rem;  /* 14 */
  --t-base: 1rem;     /* 16 - mobile body minimum */
  --t-md:   1.0625rem;
  --t-lg:   clamp(1.125rem, .95rem + .55vw, 1.375rem);
  --t-xl:   clamp(1.5rem,  1.2rem + 1.2vw, 2rem);
  --t-2xl:  clamp(2rem,    1.5rem + 2.2vw, 3rem);
  --t-3xl:  clamp(2.5rem,  1.7rem + 3.6vw, 4.5rem);
  /* Hero display. Sized off reference/homepage-reference.png, which is 1672px
     wide: RENOVATION measures 408px there, and Anton (cap height 0.86em) sets
     that word at 440px per 100px of type — so 93px, i.e. 5.56vw at the
     reference width. Capped at 104px so ultra-wide screens do not run
     "OF RENOVATION" into the photo's focal point. */
  --t-hero: clamp(2.2rem, 5.63vw, 105px);

  /* 8pt spacing rhythm */
  --s-1: 4px;  --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 24px; --s-6: 32px;  --s-7: 48px;  --s-8: 64px;
  --s-9: 96px; --s-10: 128px;

  --container: 1280px;
  --gutter: clamp(20px, 5vw, 64px);

  /* Shell grid — the wide track the header, the photo hero and the feature
     bar all share, so the logo, the red rule and the headline hang off one
     left margin. Body sections below the fold stay on the narrower
     --container track for readable measure.

     Every value in this block is expressed as the vw that reproduces the
     matching measurement in reference/homepage-reference.png at its native
     1672px width (1vw = 16.72px there), with min/max caps so the same
     proportions survive down to 375px and up past 1920px. */
  --shell: 1720px;
  --shell-pad: clamp(20px, 2.63vw, 52px);          /* 44px @1672 */

  /* Header. --rail-h is the long glass bar behind the whole header row;
     --hdr-pill is the shared height of the nav pill and the CTA sitting
     inside it. Both stay at/above the 44px touch minimum. */
  --rail-h:  clamp(52px, 5.62vw, 104px);           /* 94px @1672 */
  --hdr-pill: 44px;
  --hdr-top: clamp(10px, 1.2vw, 24px);             /* 20px @1672 */

  /* Hero. --hero-inset is the extra left padding the headline column takes
     inside the shell, so text lands at 88px while the red rule stays out on
     the shell edge at 46px. */
  --hero-inset: clamp(14px, 2.63vw, 46px);         /* 44px @1672 */
  --bar-h: clamp(96px, 8.0vw, 148px);              /* 134px @1672 */

  /* Inner-page hero box. Top clears the fixed header, bottom is breathing
     room only — the compact heroes end on their description. */
  --hero-top:    clamp(112px, 13vh, 168px);
  --hero-bottom: clamp(52px, 6vh, 88px);

  /* One text-shadow system for every hero photograph on the site. Contrast is
     bought within 3px of the stroke, where a letterform edge is actually read;
     the single wide pass is a whisper that settles the type into the picture
     rather than a halo over it. Earlier revisions reached 26-30px, which is
     what made descriptions read as video subtitles. */
  --sh-eyebrow: 0 0 1px rgba(0, 0, 0, .85), 0 0 3px rgba(0, 0, 0, .66),
                0 1px 2px rgba(0, 0, 0, .62), 0 0 6px rgba(0, 0, 0, .4);
  --sh-display: 0 0 1px rgba(0, 0, 0, .8), 0 0 3px rgba(0, 0, 0, .58),
                0 1px 2px rgba(0, 0, 0, .62), 0 0 9px rgba(0, 0, 0, .3);
  /* The red line is the tightest case anywhere: brand red and mid-tone wood
     are close to isoluminant, so the edge has to reach near-black within about
     2px. Density is spent there and nowhere else. */
  --sh-accent:  0 0 1px #000, 0 0 2px #000, 0 0 3px #000, 0 0 3px #000,
                0 0 4px #000, 0 0 4px #000, 0 0 4px #000,
                0 0 6px rgba(0, 0, 0, .6);
  --sh-lede:    0 0 1px rgba(0, 0, 0, .86), 0 0 2px rgba(0, 0, 0, .72),
                0 0 4px rgba(0, 0, 0, .55), 0 1px 2px rgba(0, 0, 0, .62),
                0 0 8px rgba(0, 0, 0, .26);

  --radius: 4px;
  --radius-lg: 10px;
  --radius-pill: 999px;

  /* motion — 150-300ms micro, per platform guidance */
  --ease-out: cubic-bezier(.16, .84, .44, 1);
  --ease-in:  cubic-bezier(.55, .06, .68, .19);
  --dur-fast: 160ms;
  --dur:      240ms;
  --dur-slow: 380ms;

  /* layered z-index scale */
  --z-base: 0; --z-raised: 10; --z-sticky: 40; --z-drawer: 90; --z-modal: 100;
}

/* ------------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 140px; /* clears the floating header on anchor jumps */
}

body {
  font-family: var(--f-body);
  font-size: var(--t-md);
  line-height: 1.65;
  color: var(--plaster);
  background: var(--ink);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden; /* belt-and-braces; no element should exceed 100% */
}

img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; padding: 0; }

/* Visible focus on every interactive element — never removed. */
:focus-visible {
  outline: 3px solid var(--red);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--red); color: #fff; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1 !important; transform: none !important; }
}

/* --------------------------------------------------------------- utilities */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: 50%; top: 8px;
  transform: translate(-50%, -200%);
  background: var(--red); color: #fff;
  padding: 12px 24px; border-radius: var(--radius);
  font-family: var(--f-ui); font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; z-index: var(--z-modal);
  transition: transform var(--dur) var(--ease-out);
}
.skip-link:focus { transform: translate(-50%, 0); }

/* ------------------------------------------------------------- type styles */
h1, h2, h3, h4 { font-weight: 400; line-height: 1.04; letter-spacing: -0.01em; }

.display {
  font-family: var(--f-display);
  text-transform: uppercase;
  line-height: .93;
  letter-spacing: .005em;
}
.display .accent { color: var(--red); }

.eyebrow {
  display: inline-flex; align-items: center; gap: var(--s-3);
  font-family: var(--f-ui); font-weight: 500;
  font-size: var(--t-sm); text-transform: uppercase;
  letter-spacing: .22em; color: var(--muted);
  margin-bottom: var(--s-4);
}
.eyebrow::before {
  content: ""; width: 32px; height: 2px;
  background: var(--red); flex: none;
}

.section-title {
  font-family: var(--f-display);
  font-size: var(--t-3xl);
  text-transform: uppercase;
  line-height: .95;
}

.lede {
  font-size: var(--t-lg);
  /* Pure white, not the muted grey this used to take. Every description
     paragraph on the site is set in #ffffff; --muted stays in the palette for
     eyebrows, labels, metadata and footer links, which are not descriptions. */
  color: #ffffff;
  max-width: 62ch; /* keeps measure inside the 60-75 char band */
}

.prose p { max-width: 68ch; margin-bottom: var(--s-4); color: #ffffff; }
.prose p:last-child { margin-bottom: 0; }
.prose strong { color: #ffffff; font-weight: 600; }

/* ----------------------------------------------------------------- buttons */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--plaster);
  --btn-bd: var(--line);
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s-3);
  min-height: 52px;              /* > 44px touch minimum */
  padding: 14px 28px;
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid var(--btn-bd);
  border-radius: var(--radius);
  font-family: var(--f-ui); font-weight: 600;
  font-size: var(--t-sm); text-transform: uppercase; letter-spacing: .12em;
  cursor: pointer;
  touch-action: manipulation;
  transition: background var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease-out),
              color var(--dur) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur) var(--ease-out);
}
.btn svg { flex: none; width: 18px; height: 18px; }
.btn .arrow { transition: transform var(--dur) var(--ease-out); }
.btn:hover .arrow { transform: translateX(4px); }
.btn:active { transform: scale(.98); }

.btn--primary {
  --btn-bg: var(--red);
  --btn-fg: #fff;
  --btn-bd: var(--red);
  box-shadow: 0 6px 24px -8px var(--red-glow);
}
.btn--primary:hover { --btn-bg: var(--red-deep); --btn-bd: var(--red-deep);
  box-shadow: 0 10px 30px -8px var(--red-glow); }

.btn--outline { --btn-bd: var(--red); }
.btn--outline:hover { --btn-bg: rgba(222, 36, 56, .12); }

.btn--ghost:hover { --btn-bd: var(--plaster); }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: .45; cursor: not-allowed; pointer-events: none;
}

/* play-glyph variant used by the hero */
.btn--play { padding-left: 14px; }
.btn--play .play {
  width: 40px; height: 40px; border: 1px solid currentColor;
  border-radius: 50%; display: grid; place-items: center; flex: none;
}
.btn--play .play svg { width: 12px; height: 12px; }

/* ------------------------------------------------------------------ header */
/* Transparent by default so the hero photograph runs unbroken behind it; the
   header only earns a surface once the page has scrolled. */
.site-header {
  position: fixed; inset: 0 0 auto 0;
  z-index: var(--z-sticky);
  padding: var(--hdr-top) 0;
  transition: background var(--dur) var(--ease-out),
              backdrop-filter var(--dur) var(--ease-out),
              padding var(--dur) var(--ease-out);
}
.site-header.is-stuck {
  padding-block: 8px;
  background: rgba(11, 14, 16, .82);
  backdrop-filter: blur(16px) saturate(120%);
  -webkit-backdrop-filter: blur(16px) saturate(120%);
  border-bottom: 1px solid var(--line-soft);
}

.header-inner {
  position: relative; isolation: isolate;
  display: flex; align-items: center;
  gap: clamp(10px, 1.4vw, 24px);
  max-width: var(--shell); margin-inline: auto;
  padding-inline: var(--shell-pad);
}

/* The long glass rail. In the reference the logo does not sit in a pill of its
   own — one translucent bar runs the width of the header and the nav pill and
   the CTA sit inside it. Painted as a pseudo-element so the header markup, and
   therefore every page that shares it, stays exactly as it was.
   Desktop only; below 1120px the logo takes its own pill back (see the
   breakpoint block at the foot of this file). */
.header-inner::before {
  content: ""; position: absolute; z-index: -1;
  /* Measured off the reference: the rail's right cap stops on the shell margin
     at 1628/1672 and the CTA overhangs it by ~8px, rather than the rail closing
     around the button. */
  inset: 0 var(--shell-pad);
  display: none;
  border-radius: var(--radius-pill);
  /* Darker fill than it was: over the reference's bright ceiling the rail reads
     as a ~50-count darkening, and over its dark stretches it lifts the backdrop
     by only ~2. The old rgba(30,34,39,.40) top stop lifted dark ground by 10,
     which is what made this read as a grey slab rather than glass. */
  background: linear-gradient(180deg, rgba(10, 12, 15, .36), rgba(6, 8, 11, .50));
  border: 1px solid rgba(237, 235, 231, .085);
  backdrop-filter: blur(14px) saturate(115%);
  -webkit-backdrop-filter: blur(14px) saturate(115%);
}

/* logo lockup */
.logo {
  display: inline-flex; align-items: center; gap: 8px;
  flex: none; min-height: var(--hdr-pill);
  padding: 5px 20px 5px 5px;
  border-radius: var(--radius-pill);
  background: rgba(20, 24, 27, .58);
  border: 1px solid var(--line-soft);
  backdrop-filter: blur(12px) saturate(120%);
  -webkit-backdrop-filter: blur(12px) saturate(120%);
}
.logo__mark {
  width: clamp(38px, 2.6vw, 46px); height: clamp(33px, 2.3vw, 40px);
  color: var(--plaster); flex: none;
  object-fit: contain;
  transition: width var(--dur) var(--ease-out), height var(--dur) var(--ease-out);
}
.logo__text { display: flex; flex-direction: column; line-height: 1; }
.logo__name {
  font-family: var(--f-ui); font-weight: 600;
  font-size: clamp(.95rem, .8rem + .35vw, 1.3rem);
  letter-spacing: .07em; text-transform: uppercase; color: var(--plaster);
}
.logo__sub {
  font-family: var(--f-ui); font-weight: 400;
  font-size: clamp(.6rem, .52rem + .18vw, .78rem);
  letter-spacing: .3em; text-transform: uppercase; color: var(--muted);
  margin-top: 3px;
}

/* desktop nav pill */
.nav {
  display: none;
  margin-inline: auto;
  height: var(--hdr-pill);
  /* The reference pill carries ~8.5px of its own inline padding on top of the
     31.5px each link takes, which is why its end caps sit further from HOME and
     CONTACT than half an inter-item gap. */
  padding-inline: clamp(4px, .508vw, 10px);
  border-radius: var(--radius-pill);
  background: rgba(24, 28, 33, .30);
  border: 1px solid rgba(237, 235, 231, .155);
  backdrop-filter: blur(10px) saturate(115%);
  -webkit-backdrop-filter: blur(10px) saturate(115%);
}
.nav ul { display: flex; align-items: stretch; height: 100%; }
/* the <li> has to be a flex container too, or the <a> inside it sizes to its
   own text and the active underline rides up under the label */
.nav li { display: flex; }
.nav a {
  /* 31.5px of side padding at the reference width: the mockup sets 63px of
     clear space between adjacent labels, which is two of these. It compresses
     to 14px before the nav is swapped for the drawer at 1120px. */
  --nav-px: clamp(14px, 1.884vw, 36px);
  position: relative;
  display: inline-flex; align-items: center;
  padding-inline: var(--nav-px);
  font-family: var(--f-ui); font-weight: 500;
  font-size: clamp(.8125rem, 1.106vw, 1.35rem);
  text-transform: uppercase; letter-spacing: .09em;
  /* The reference sets every item at the same near-white; only the red rule
     under HOME marks the current page. */
  color: rgba(255, 255, 255, .94);
  white-space: nowrap;
  border-radius: var(--radius-pill);
  transition: color var(--dur) var(--ease-out);
}
.nav a:hover { color: var(--plaster); }
.nav a::after {
  content: ""; position: absolute;
  left: var(--nav-px); right: var(--nav-px);
  bottom: clamp(4px, .42vw, 9px); height: clamp(2px, .18vw, 3px);
  background: var(--red);
  transform: scaleX(0); transform-origin: center;
  transition: transform var(--dur) var(--ease-out);
}
.nav a:hover::after { transform: scaleX(.5); }
/* aria-current is the source of truth for "you are here" */
.nav a[aria-current="page"] { color: #fff; }
.nav a[aria-current="page"]::after { transform: scaleX(1); }

/* Header CTA. Deliberately overrides the .btn defaults: it sits on the same
   optical line as the nav pill, so it takes --hdr-pill for its height rather
   than the 52px touch minimum every other button uses. The reference paints it
   as a red gradient running bright at the left to deep maroon at the right,
   with a hairline top highlight and a soft red glow underneath. */
.header-cta {
  display: inline-flex; flex: none;
  margin-left: auto;
  /* Measured off the reference at its native 1672px: the button is 1350..1636
     x 39..103, i.e. 3px taller than the nav pill beside it, so it carries its
     own height rather than sharing --hdr-pill. */
  min-height: clamp(48px, 3.828vw, 71px);            /* 64px @1672 */
  padding: 0 clamp(8px, 1.083vw, 20px) 0 clamp(6px, .784vw, 15px);
  gap: clamp(8px, .628vw, 12px);
  border-radius: clamp(6px, .658vw, 12px);
  /* The reference label is lighter and much more widely tracked than .btn's
     600/.12em — 15px of cap height spread over 148px of measure. */
  font-weight: 400;
  font-size: clamp(.6875rem, .909vw, 1.1rem);        /* 15.2px @1672 */
  letter-spacing: .193em;
  background:
    linear-gradient(101deg, #C9181C 0%, #C10605 13%, #A20B10 54%, #74070A 100%);
  border: 1px solid rgba(255, 255, 255, .17);
  box-shadow: 0 10px 26px -12px rgba(200, 16, 30, .85),
              0 2px 10px rgba(0, 0, 0, .34),
              inset 0 1px 0 rgba(255, 255, 255, .20);
  will-change: transform;
}
/* Both glyphs sit well inside their 24-unit viewBoxes, so the boxes are
   oversized to bring the drawn strokes up to the reference's size. */
.header-cta svg { width: clamp(20px, 1.884vw, 34px); height: auto; }
/* Set from CSS, not on the markup, so every page's header keeps one source of
   truth: at this box size the 1.75 authored on the glyph draws a 3.7px stroke
   where the reference draws ~2.4px. */
.header-cta .clipboard { width: clamp(28px, 3.05vw, 56px); stroke-width: 1.15; }
/* Phones get the short label alone. The leading word and both glyphs come
   back as soon as there is width for them. */
.header-cta .clipboard,
.header-cta .arrow { display: none; }
.header-cta .cta-lead { display: none; }
.header-cta:hover {
  background:
    linear-gradient(101deg, #D6191E 0%, #CE0708 13%, #AC0C11 54%, #7E080B 100%);
  transform: translateY(-1px);
  box-shadow: 0 14px 30px -12px rgba(200, 16, 30, .95),
              0 6px 16px rgba(0, 0, 0, .36),
              inset 0 1px 0 rgba(255, 255, 255, .24);
}
.header-cta:active { transform: translateY(0) scale(.99); }
@media (prefers-reduced-motion: reduce) {
  .header-cta:hover { transform: none; }
}

/* hamburger */
.nav-toggle {
  width: 44px; height: 44px; flex: none;
  /* place-content as well as place-items: without it the three implicit rows
     stretch to fill 44px, which spreads the bars out and — more visibly — stops
     the two rotated bars meeting, so the "close" state drew a chevron instead
     of an X. */
  display: grid; place-items: center; place-content: center;
  background: rgba(20, 24, 27, .58);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  cursor: pointer; touch-action: manipulation;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.nav-toggle span {
  display: block; width: 18px; height: 2px; background: var(--plaster);
  transition: transform var(--dur) var(--ease-out), opacity var(--dur-fast) linear;
}
.nav-toggle span + span { margin-top: 4px; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* mobile drawer */
.drawer {
  position: fixed; inset: 0; z-index: var(--z-drawer);
  background: rgba(11, 14, 16, .97);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  padding: 104px var(--gutter) var(--s-7);
  display: flex; flex-direction: column; gap: var(--s-2);
  overflow-y: auto;
  transform: translateY(-8px); opacity: 0;
  visibility: hidden; pointer-events: none;
  transition: opacity var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-out),
              visibility 0s linear var(--dur);
}
.drawer[data-open="true"] {
  opacity: 1; transform: translateY(0);
  visibility: visible; pointer-events: auto;
  transition-delay: 0s;
}
.drawer a {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 4px; min-height: 56px;
  font-family: var(--f-display); font-size: 1.75rem;
  text-transform: uppercase; color: var(--plaster);
  border-bottom: 1px solid var(--line);
}
.drawer a[aria-current="page"] { color: var(--red); }
/* Muted, not Graphite: the 01–05 numbering is 12.8px text on the drawer's
   near-opaque --ink ground, where Graphite measures 3.86:1 — under the 4.5:1
   WCAG AA floor for normal-size text. Muted measures 8.64:1. Size, tracking
   and face are unchanged. */
.drawer a span { font-family: var(--f-ui); font-size: .8rem;
  letter-spacing: .2em; color: var(--muted); }
.drawer .btn { margin-top: var(--s-5); }

body.nav-open { overflow: hidden; }
/* The drawer reserves 104px at the top for the header, but at --z-sticky the
   header sits under the drawer's backdrop, so the close button the visitor is
   looking for is invisible. Lift the header over the drawer while it is open. */
body.nav-open .site-header { z-index: calc(var(--z-drawer) + 1); }

/* -------------------------------------------------------------------- hero */
.hero {
  position: relative;
  min-height: 100vh; min-height: 100dvh;   /* dynamic viewport for non-phone layouts */
  display: flex; align-items: center;
  padding: 160px 0 220px;
  isolation: isolate;
  overflow: hidden;
}
.hero--compact {
  min-height: 0;
  padding: var(--hero-top) 0 var(--hero-bottom);
}


/* The photo hero and the feature bar hang off the wide shell grid so the
   headline, the red rule and the logo above them share one left margin.
   Inner-page (.hero--compact) heroes stay on --container with their body copy.

   Padding is deliberately lopsided: the top has to clear the fixed header, and
   the bottom has to clear both the feature bar overlapping the hero's last
   --bar-h pixels and the Canada badge sitting just above it. Between those two
   the flex centring lands the headline where the reference puts it. */
.hero--photo {
  padding: calc(var(--rail-h) + var(--hdr-top) * 2 + clamp(10px, 2.08vw, 38px))
           0
           calc(var(--bar-h) + clamp(30px, 4.3vw, 78px));
}
.hero--photo > .container,
.feature-bar > .container {
  max-width: var(--shell);
  padding-inline: var(--shell-pad);
}

/* Background. Uses a photo if one is dropped in at assets/img/hero.jpg,
   and falls back to a lit-interior gradient so the page never looks broken. */
.hero__bg {
  position: absolute; inset: 0; z-index: -2;
  background-color: var(--ink);
  background-image:
    radial-gradient(1200px 620px at 74% 34%, rgba(255, 168, 76, .26), transparent 62%),
    radial-gradient(900px 520px at 88% 68%, rgba(222, 36, 56, .12), transparent 60%),
    radial-gradient(760px 700px at 18% 22%, rgba(120, 148, 178, .13), transparent 66%),
    linear-gradient(180deg, #10151a 0%, #172026 48%, #0c1013 100%);
  background-size: cover;
  background-position: center;
}
/* Photo path is resolved against THIS file, not the page — browsers disagree
   about relative url() inside a custom property, so keep the default here.
   The photograph carries no overlay of its own: every scrim lives on the
   pseudo-elements below, so the image itself stays a first-class visual and
   the darkening can be kept local to where type actually sits. */
.hero__bg[data-photo] {
  background-image: var(--hero-photo, url("../img/hero.jpg"));
  /* Scaled past cover and pulled left: at the reference's own aspect the photo
     already fills the frame edge to edge, so the only way to lose the blown-out
     shelving on the far right — which the reference does not have — is to crop
     it out. 112% also trims the empty ceiling the reference does not show. */
  background-size: 112% auto;
  background-position: 44% center;
  /* The reference's hero averages L=49 across the photo; at brightness 1.16
     this one averaged 57, so the lift comes off. */
  filter: brightness(1.28) contrast(1.06) saturate(1.05);
}
/* left-edge scrim keeps headline contrast over any photo */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(90deg, rgba(11,14,16,.80) 0%, rgba(11,14,16,.44) 42%, transparent 72%);
  pointer-events: none;
}
/* Photo hero: this photograph is already dark, so it takes only a light,
   soft pool over the copy column — no full-height wash — and the rest of the
   kitchen keeps its own light. */
.hero--photo::before {
  background: radial-gradient(46% 64% at 21% 50%,
    rgba(0,0,0,.36) 0%, rgba(0,0,0,.33) 30%, rgba(0,0,0,.24) 52%,
    rgba(0,0,0,.12) 72%, rgba(0,0,0,.04) 88%, rgba(0,0,0,0) 100%);
}
/* A short fade under the header and into the feature bar, nothing else. */
.hero--photo::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(0,0,0,.22) 0%, rgba(0,0,0,.04) 14%,
    rgba(0,0,0,0) 30%, rgba(0,0,0,0) 76%,
    rgba(0,0,0,.14) 90%, rgba(0,0,0,.30) 100%);
}

/* ------------------------------------------------- hero copy scrim --------
   The five inner photo heroes carry no wash across the photograph. Instead a
   soft pool of shade sits on the copy column itself, sized from the copy, so
   it follows the copy at every width and the rest of each room stays as
   photographed. It peaks behind the headline and eases off over the
   description; letter-edge contrast comes from the shadow tokens in :root.
   --scrim is the pool's depth per page and range. */
.hero--services::before,
.hero--about::before,
.hero--process::before,
.hero--contact::before,
.hero--projects::before { content: none; }

.hero--services .hero__inner::before,
.hero--about .hero__inner::before,
.hero--process .hero__inner::before,
.hero--contact .hero__inner::before,
.hero--projects .hero__inner::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  /* The copy sits at 17.7-81% across this box and 29.4-88.2% down it. */
  inset: -50% -30% -20% -28%;
  background: linear-gradient(90deg,
    rgb(0 0 0 / 0) 0%,
    rgb(0 0 0 / calc(var(--scrim) * .10)) 5%,
    rgb(0 0 0 / calc(var(--scrim) * .38)) 10%,
    rgb(0 0 0 / calc(var(--scrim) * .70)) 14%,
    rgb(0 0 0 / calc(var(--scrim) * .92)) 17.7%,
    rgb(0 0 0 / var(--scrim)) 22%,
    rgb(0 0 0 / var(--scrim)) 46%,
    rgb(0 0 0 / calc(var(--scrim) * .90)) 58%,
    rgb(0 0 0 / calc(var(--scrim) * .66)) 70%,
    rgb(0 0 0 / calc(var(--scrim) * .38)) 81%,
    rgb(0 0 0 / calc(var(--scrim) * .14)) 90%,
    rgb(0 0 0 / 0) 100%);
  -webkit-mask-image: var(--scrim-mask);
          mask-image: var(--scrim-mask);
  --scrim-mask: linear-gradient(180deg,
    rgb(0 0 0 / 0) 0%,
    rgb(0 0 0 / .08) 10%,
    rgb(0 0 0 / .30) 18%,
    rgb(0 0 0 / .62) 25%,
    rgb(0 0 0 / .86) 29.4%,
    rgb(0 0 0 / .97) 35%,
    #000 41%,
    #000 56%,
    rgb(0 0 0 / .93) 68%,
    rgb(0 0 0 / .82) 78%,
    rgb(0 0 0 / .60) 88.2%,
    rgb(0 0 0 / .26) 94%,
    rgb(0 0 0 / 0) 100%);
}

/* Depth per photograph and per range. */
.hero--services { --scrim: .40; }
.hero--about    { --scrim: .40; }
.hero--process  { --scrim: .43; }
.hero--contact  { --scrim: .48; }
.hero--projects { --scrim: .48; }
@media (min-width: 640px) {
  .hero--services { --scrim: .50; }
  .hero--about    { --scrim: .58; }
  .hero--process  { --scrim: .60; }
  .hero--contact  { --scrim: .62; }
  .hero--projects { --scrim: .62; }
}
@media (min-width: 1024px) {
  .hero--services { --scrim: .48; }
  .hero--about    { --scrim: .56; }
  .hero--process  { --scrim: .58; }
  .hero--contact  { --scrim: .63; }
  .hero--projects { --scrim: .60; }
}

/* Phones: the copy fills most of the hero, so the pool fades in just above the
   eyebrow and out just below the description, leaving ceiling and floor clear.
   --scrim-edge/-mid/-foot set the eyebrow, mid-description and last-line depth
   per page; the text edges are a touch denser within 2px to match. */
@media (max-width: 639.98px) {
  .hero--services .hero__inner::before,
  .hero--about .hero__inner::before,
  .hero--process .hero__inner::before,
  .hero--contact .hero__inner::before,
  .hero--projects .hero__inner::before {
    /* Phones rely on the tight black letter edges instead of a dark copy
       background, so the renovation photo stays evenly bright. */
    content: none;
    --scrim-mask: linear-gradient(180deg,
      rgb(0 0 0 / 0) calc(29.4% - 56px),
      rgb(0 0 0 / .08) calc(29.4% - 46px),
      rgb(0 0 0 / .30) calc(29.4% - 32px),
      rgb(0 0 0 / .62) calc(29.4% - 16px),
      rgb(0 0 0 / var(--scrim-edge, .86)) 29.4%,
      rgb(0 0 0 / .97) 35%,
      #000 41%,
      #000 56%,
      rgb(0 0 0 / .93) 68%,
      rgb(0 0 0 / var(--scrim-mid, .82)) 78%,
      rgb(0 0 0 / var(--scrim-foot, .60)) 88.2%,
      rgb(0 0 0 / .26) calc(88.2% + 14px),
      rgb(0 0 0 / 0) calc(88.2% + 30px));
  }
  .hero--services { --scrim-edge: 1; }
  .hero--about    { --scrim-mid: .72; --scrim-foot: .45; }
  .hero--process  { --scrim-mid: .90; --scrim-foot: .80; }
  .hero--contact  { --scrim-mid: .92; --scrim-foot: .85; }
  .hero--projects { --scrim-mid: .88; --scrim-foot: .75; }

  :root {
    --sh-eyebrow: 0 0 1px rgba(0, 0, 0, .95), 0 0 2px rgba(0, 0, 0, .8),
                  0 0 2px rgba(0, 0, 0, .5), 0 0 3px rgba(0, 0, 0, .66),
                  0 1px 2px rgba(0, 0, 0, .62), 0 0 6px rgba(0, 0, 0, .4);
    --sh-accent:  0 0 1px #000, 0 0 1px #000, 0 0 2px #000, 0 0 2px #000,
                  0 0 2px #000, 0 0 2px #000, 0 0 3px #000, 0 0 3px #000,
                  0 0 3px #000, 0 0 3px #000, 0 0 4px #000, 0 0 4px #000,
                  0 0 4px #000, 0 0 6px rgba(0, 0, 0, .6);
    --sh-lede:    0 0 1px rgba(0, 0, 0, .95), 0 0 2px rgba(0, 0, 0, .85),
                  0 0 2px rgba(0, 0, 0, .55), 0 0 3px rgba(0, 0, 0, .4),
                  0 0 4px rgba(0, 0, 0, .55), 0 1px 2px rgba(0, 0, 0, .62),
                  0 0 8px rgba(0, 0, 0, .26);
  }
}

.hero__inner { position: relative; max-width: 760px; }

/* the vertical red rule down the left of the headline */
.hero__rule {
  position: absolute;
  /* Hangs outside the text column, but never further than the gutter can
     spare — so it stays on-canvas at every width. */
  left: calc(-1 * min(30px, var(--gutter) - 14px));
  top: 4px; bottom: 32%; width: 2px;
  background: linear-gradient(180deg, var(--red), transparent);
  display: none;
}

/* Photo hero: the headline column is inset a second time inside the shell, so
   the type lands at 88px while the red rule stays out on the shell edge at
   46px — the two left margins the reference uses. */
.hero--photo .hero__inner {
  max-width: none;
  padding-left: var(--hero-inset);
}
/* Solid, not faded: the reference runs the same red from top to bottom, from
   just above the cap line of MASTERING to the last line of the subtitle. */
.hero--photo .hero__rule {
  left: 2px;
  top: clamp(-14px, -.6vw, -4px);
  bottom: auto;
  height: clamp(210px, 25.7vw, 470px);
  width: 2px;
  background: var(--red);
}

/* ---------------------------------------------- hero type over photographs --
   Every hero headline, eyebrow and description on Home, Services, About,
   Process and Contact reads off the four tokens in :root, so the treatment is
   identical across the five pages and at every width. It replaces the twenty
   or so page-and-breakpoint-specific shadows that used to live further down
   this file, several of which stopped dead at 639.98px and left the tablet
   widths with no edge at all. */
.hero--photo .eyebrow,
.hero--services .eyebrow,
.hero--about .eyebrow,
.hero--process .eyebrow,
.hero--contact .eyebrow,
.hero--projects .eyebrow { color: #fff; text-shadow: var(--sh-eyebrow); }

.hero--photo h1,
.hero--services h1,
.hero--about h1,
.hero--process h1,
.hero--contact h1 { color: #fff; text-shadow: var(--sh-display); }

.hero--photo h1 .accent,
.hero--services h1 .accent,
.hero--about h1 .accent,
.hero--process h1 .accent,
.hero--contact h1 .accent { text-shadow: var(--sh-accent); }

.hero--photo .lede,
.hero--services .lede,
.hero--about .lede,
.hero--process .lede,
.hero--contact .lede { color: #fff; text-shadow: var(--sh-lede); }

/* Projects takes the same edges; its headline and description keep their
   own colours. */
.hero--projects h1           { text-shadow: var(--sh-display); }
.hero--projects h1 .accent   { text-shadow: var(--sh-accent); }
.hero--projects .lede        { text-shadow: var(--sh-lede); }

.hero h1 { font-size: var(--t-hero); margin-bottom: var(--s-5); }
.hero__divider { width: 84px; height: 3px; background: var(--red); margin-bottom: var(--s-5); }
.hero .lede { margin-bottom: var(--s-7); max-width: 46ch; }

/* One rhythm for all four inner heroes: eyebrow, headline, red divider,
   description. Each step is set here rather than page by page, so Services,
   About, Process and Contact share the same hierarchy and the same bottom
   breathing room, and each hero ends up only as tall as its own copy. */
.hero--compact .eyebrow      { margin-bottom: clamp(12px, 1.5vh, 18px); }
/* One headline scale for the four inner heroes. It used to be an inline
   font-size on each page, which meant the stylesheet could not answer for the
   widths where a line crowded or broke badly; the markup now carries none and
   this clamp does the work. The ceiling stays at the 72px these pages already
   set, so nothing grows — .hero h1 above is the homepage's much larger scale
   and must not reach these. */
.hero--compact h1            { font-size: clamp(2.3rem, 1.55rem + 3.4vw, 4.5rem);
                               margin-bottom: clamp(18px, 2.2vh, 28px); line-height: 1; }
/* About carries the longest line on the site — PROCESS YOU CAN TRUST. — so it
   runs one step down from the shared scale. That is what keeps it to two lines
   on a phone instead of dropping TRUST. onto a third by itself, and keeps its
   right edge well inside the frame on a wide screen. */
.hero--about h1              { font-size: clamp(1.75rem, 1.05rem + 3.5vw, 4.25rem); }
.hero--compact .hero__divider{ width: clamp(64px, 18vw, 84px); margin-bottom: clamp(16px, 2vh, 24px); }
/* The shared .lede reserves 48px under itself for a hero CTA. These heroes end
   on the description, so that margin is dead space at the foot of the photo —
   --hero-bottom is the breathing room instead. */
/* A right inset inside the copy column. Without it the longest headline on the
   site — About's — ends flush against the container edge on a phone, and the
   description runs the full measure on a wide screen. It is padding rather than
   a max-width so the red divider and the eyebrow keep their own left alignment. */
.hero--compact .hero__inner  { padding-right: clamp(16px, 6vw, 96px); }
/* text-wrap: balance is what keeps a description from ending on one stranded
   word: the browser evens the rag itself instead of the markup carrying manual
   line breaks. Browsers without it, and paragraphs past the balancing limit,
   simply wrap as before. */
.hero--compact .lede         { margin-bottom: 0; text-wrap: balance; }

/* Photo-hero typography. Tighter leading than the shared .display rule and a
   diffuse shadow instead of a heavier overlay — it buys legibility from the
   type, not from covering the photograph. */
.hero--photo h1 {
  font-size: clamp(2.3rem, 1.55rem + 3.4vw, 4.5rem);
  line-height: 1.10;                 /* 102px of pitch on a 93px face @1672 */
  letter-spacing: 0;
  margin-bottom: clamp(2px, .36vw, 8px);
  /* The reference sets this in pure white, not the warm Plaster the rest of
     the page uses; against the photograph the difference is visible. */
  color: #fff;
}
.hero--photo .hero__divider {
  width: clamp(34px, 3.23vw, 60px);
  height: 2px;
  margin-bottom: clamp(6px, .48vw, 10px);
}
.hero--photo .lede {
  /* Tuned to break as the reference does — "…kitchens," / "…professional" /
     "painting across the GTA." 380px is wide enough for the longest of the
     three lines and narrow enough to push "bathrooms," onto the second. */
  max-width: clamp(292px, 22.75vw, 430px);
  color: #ffffff;
  font-size: var(--t-lg);
  line-height: 1.70;
  margin-bottom: clamp(16px, 1.62vw, 30px);
}

/* -------------------------------------------------- services page hero ----
   The Services page runs the compact hero over a photograph of a finished
   room instead of the gradient: yellow side walls, a grey back wall under a
   row of high windows, and recessed ceiling lights. The photo is the point, so
   the only shade on it is the copy scrim above and a short fade at the foot.
   Scoped entirely to .hero--services so no other page's hero is touched. */
.hero--services {
  min-height: clamp(360px, 42vh, 520px);
}
/* url() is resolved against THIS file, which is why the path lives here and
   not in a custom property on the page (see the note on .hero__bg above). */
.hero--services .hero__bg[data-photo] {
  background-image: url("../img/services-desktop-basement-renovation.webp");
  /* 640px and up; phones swap the file and position in the max-width: 639.98px
     block below. cover, never stretched — the box crops the 1672x941 photo. */
  background-size: cover;
  background-position: center 36%;
  background-repeat: no-repeat;
  /* the photograph is already evenly lit; it is used exactly as supplied */
  filter: none;
}
/* A short fade at the foot so the photograph meets the ink-black services
   grid without a seam; the ceiling and the middle of the room are left alone. */
.hero--services::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(11, 14, 16, 0)   84%,
    rgba(11, 14, 16, .10) 92%,
    rgba(11, 14, 16, .32) 100%);
}

@media (max-width: 639.98px) {
  .hero--services { min-height: 0; }
  /* Phones load their own portrait photograph rather than the landscape
     desktop file, which at this width could only ever be shown as a narrow
     centre slice of a room. It is a vertically composed, centred view of a
     finished room — recessed ceiling lights, a grey back wall under a row of
     high windows, yellow side walls and a pale floor — used exactly as
     supplied, only re-encoded to WebP. Scoped inside this breakpoint, so
     desktop and tablet keep services-hero-residential-renovation.jpg exactly
     as it is. cover, no-repeat and filter: none all still come from the base
     rule above — the only things that differ on a phone are which file loads
     and where it is anchored vertically.

     The file is 981x1603 (0.61), the hero's own aspect at 360px, so from 360
     to 430px wide cover trims no more than about a tenth of the frame and the
     room is shown whole. The overhang only grows toward the top of this
     breakpoint, reaching about 433px of a 1044px covered height at 639. 40%
     takes a little less of that crop off the ceiling than off the floor: the
     lights sit in the top quarter of the frame and the back wall at 29-54%,
     and at 639 the visible band is 17-75%, so two rows of lights, the windows,
     the grey wall and the yellow walls stay in frame with floor below them. */
  .hero--services .hero__bg[data-photo] {
    background-image: url("../img/services-hero-mobile.webp");
    background-position: 50% 30%;
  }
}

/* Landscape phones: .hero above resets min-height here, and the services hero
   has to follow it rather than force a tall banner into a 560px viewport. */
@media (max-height: 560px) and (orientation: landscape) {
  .hero--services { min-height: 0; }
}

/* ------------------------------------------------ services page photo rows --
   Each service on the Services page is a row: its existing card plus one real
   MIRG project photograph. Every rule here hangs off classes only services.html
   uses (.services-list, .service-row, .service-photo), so .grid, .card and
   .split are untouched on every other page, and the cards inside keep their
   own icon, heading, copy, bullets and hover exactly as they were.

   The card comes first in the markup so the heading is read before the photo;
   the photo is placed visually with order and grid placement. The photos are
   plain images, not links, so nothing focusable is reordered.

   Below 900px: photo above card at 4:3, no alternation, so six services do not
   turn into an endless scroll. From 900px: two columns at 5:7, the photo side
   alternating row by row, framed at 4:5 — or 1:1 for a landscape photograph
   (.service-photo--landscape), which a 4:5 frame would cut to under half its
   width. */
.services-list {
  display: grid;
  gap: clamp(56px, 8vw, 104px);   /* the same rhythm as .project + .project */
}
.service-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-4);
}
/* The frame is the .shot frame from the Projects page — surface fill, line
   border, large radius — without the hover lift and zoom badge, because these
   photographs are not links. aspect-ratio reserves the height before the lazy
   image arrives, so nothing below it shifts. */
.service-photo {
  order: -1;
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
/* cover, never stretched: the frame crops the photograph and never distorts
   it. --svc-x-m / --svc-y-m anchor the phone frame and --svc-x / --svc-y the
   desktop frame, each defaulting to centre. The portraits (1152x2048) only
   have height to spend, so they set the vertical anchor; the 16:9 landscapes
   only have width to spend in a 4:3 or 1:1 frame, so they set the horizontal. */
.service-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--svc-x-m, 50%) var(--svc-y-m, 50%);
}
.service-photo--kitchen    { --svc-x-m: 0%;  --svc-x: 6%; }   /* peninsula and full cabinet run, not the door on the right */
.service-photo--bathroom   { --svc-y-m: 35%; --svc-y: 30%; }  /* shower to vanity, not the hall floor */
.service-photo--basement   { --svc-x: 68%; }                  /* square: high windows, mini-split and bench between both yellow walls; phone frame keeps centre */
.service-photo--painting   { --svc-y-m: 25%; --svc-y: 25%; }  /* ceiling light, both painted walls and baseboards, a strip of floor */
.service-photo--whole-home { --svc-y-m: 20%; --svc-y: 35%; }  /* stairs, hall and kitchen beyond */
.service-photo--flooring   { --svc-y-m: 62%; --svc-y: 68%; }  /* treads, balusters and plank floor */

@media (min-width: 900px) {
  .service-row {
    --svc-gap: clamp(32px, 5vw, 72px);   /* the .split gap */
    gap: var(--svc-gap);
    align-items: center;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  }
  .service-row--photo-right { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .service-row--photo-left  > .service-photo,
  .service-row--photo-right > .card { grid-column: 1; grid-row: 1; }
  .service-row--photo-left  > .card,
  .service-row--photo-right > .service-photo { grid-column: 2; grid-row: 1; }

  .service-photo { aspect-ratio: 4 / 5; }
  /* Square, not 4:5: close to the card's own height beside it, and it keeps
     over half the width of a 16:9 photograph instead of 45%. */
  .service-photo--landscape { aspect-ratio: 1 / 1; }
  .service-photo img { object-position: var(--svc-x, 50%) var(--svc-y, 50%); }

  /* Basement has no photograph yet. Its card keeps exactly the width a card has
     in the photo rows — 7 of the 12 parts left after the gap — and sits on the
     centre line, where the rows alternate around it. */
  .service-row--text { grid-template-columns: minmax(0, 1fr); }
  .service-row--text > .card {
    justify-self: center;
    width: calc((100% - var(--svc-gap)) * 7 / 12);
  }
}

/* --------------------------- about + process + contact hero photo (phone) --
   The About hero runs over a portrait photograph of a finished staircase and
   hallway on phones; from 640px up it loads its own landscape file instead
   (see "about hero photo (tablet + desktop)" below), which nothing in this
   block touches. Every rule below lives inside the 639.98px breakpoint, and
   nothing here changes type, spacing or the hero's box.

   The Process and Contact heroes join the same block rather than getting a set
   of values of their own: they are the same kind of page, want the same
   treatment, and the only thing that legitimately differs between the three is
   which file loads and where it is anchored vertically. Overlay, text shadows
   and the hero box itself are shared selectors below, so the three pages cannot
   drift apart.

   Deliberately NOT wired through [data-photo]: that attribute carries the
   homepage photograph at every width (see .hero__bg above), which would change
   the desktop page. Scoping to .hero--about, .hero--process and .hero--contact
   instead leaves the base rule alone.

   Drop any of these classes from its page to fall back to the gradient
   everywhere. */
@media (max-width: 639.98px) {
  /* url() is resolved against THIS file, which is why the path lives here and
     not in a custom property on the page — the same reason the other two hero
     photographs set their paths in this stylesheet. */
  .hero--about .hero__bg {
    background-image: url("../img/about-mobile-hallway-renovation.webp");
    /* cover, never stretched — the photo keeps its own 941x1450 proportions and
       the hero box crops it. The file is already cropped to roughly the hero's
       own aspect, so cover trims only ~20px off one axis at phone widths and
       the stair treads, the ceiling light, the back door and the hallway floor
       all stay in frame. 50% 38% puts what little trim there is on the near
       foreground rather than on the top of the staircase. */
    background-size: cover;
    background-position: 50% 38%;
    background-repeat: no-repeat;
  }

  /* Process: same three settings as About — cover so the box crops rather than
     stretches, and no filter, so the brightness is the camera's own exactly as
     it is on About. Only the file and the vertical anchor differ. This frame is
     941x1672 against About's 941x1450, so at a phone width it overhangs the
     hero box by more and the anchor has more work to do: the staircase runs
     from a little under halfway down the photograph to the bottom edge, so 78%
     spends the crop on the foreground floor the picture does not need and keeps
     the whole flight, the newel and the top landing in frame at 390, 412 and
     430px alike. Checked at all three: the flight reaches both the top and the
     bottom edge of the hero, so there is no empty band at either end. */
  .hero--process .hero__bg {
    background-image: url("../img/process-mobile-staircase-renovation.webp");
    background-size: cover;
    background-position: 50% 78%;
    background-repeat: no-repeat;
  }

  /* Contact: the same three settings again — cover so the box crops rather than
     stretches, and no filter, so the room is the camera's own brightness. This
     frame is 941x1672, the same proportions as the Process one, but the hero box
     it sits in is shorter: 573 to 603px tall across 360, 390, 412 and 430px
     wide, against a covered photo height of 640 to 764px, so the crop has only
     53 to 161px to spend. 50% splits that evenly top and bottom, which is what
     the composition wants here — the top of the window frame sits 109 to 130px
     down the covered photo and the largest crop any of those widths takes off
     the top is 80px, so the balcony window, the lit wall and the parquet are all
     in frame at every one of them. Pushing the anchor lower to lose the strip of
     ceiling would start cutting the window's head at 430. */
  .hero--contact .hero__bg {
    background-image: url("../img/contact-mobile-room-renovation.webp");
    background-size: cover;
    background-position: 50% 50%;
    background-repeat: no-repeat;
  }

}

/* ------------------------------------- about hero photo (tablet + desktop) --
   From 640px up the About hero runs over a landscape photograph of the same
   kind of finished work: a wood staircase with a white balustrade on the left
   and a parquet hallway running back to a garden door on the right. This rule
   swaps the file and nothing else — the overlay (the copy scrim), the absence of
   any filter, the type and the hero's box are all still the shared ones, so the
   page looks exactly as it did apart from the photograph behind it and the
   edge on the red headline line (the second rule in this block). It starts
   at 640px, exactly where the phone rule above stops, so the two never overlap
   and phones keep about-mobile-hallway-renovation.webp untouched.

   cover, never stretched — the file keeps its own 1672x941 proportions and the
   hero box crops it. Only one axis is ever cropped at a time, so each half of
   the position does its own job:
   - 44% (horizontal) only matters from 640 to 1023px, where the hero is taller
     than the photo and up to about a third of its width is cut. Anchoring left
     of centre keeps more of the handrail and lower treads and takes the extra
     trim from the plain wall on the far right, while the hallway door keeps
     some wall either side of it at 640, 768 and 900.
   - 40% (vertical) matters from 1024px up, where the hero is wider than the
     photo and up to about a third of its height is cut at 1920. Taking a
     little more off the floor than the ceiling keeps the whole garden door
     clear of the header at 1366, 1440 and 1920. */
@media (min-width: 640px) {
  .hero--about .hero__bg {
    background-image: url("../img/about-desktop-staircase-renovation.webp");
    background-size: cover;
    background-position: 44% 40%;
    background-repeat: no-repeat;
  }

}

/* ----------------------------------- process hero photo (tablet + desktop) --
   From 640px up the Process hero runs over a landscape photograph of the same
   staircase the phone file shows: a wood flight rising to a landing, a white
   balustrade with a wood handrail on the left and a plain wall on the right.
   The first rule swaps the file and nothing else — the overlay
   (the copy scrim), the absence of any filter, the type and the hero's box are
   all still the shared ones; the three rules after it add an edge to the red
   headline line, the lede and the eyebrow, and change nothing else about the
   type. It starts at 640px, exactly where the phone rule above stops, so the
   two never overlap and phones keep process-mobile-staircase-renovation.webp
   untouched.

   cover, never stretched — the file keeps its own 1672x941 proportions and the
   hero box crops it. Only one axis is ever cropped at a time:
   - 50% (horizontal) only matters from 640px up to somewhere between about
     1090 and 1290px — the hero's padding is in vh, so a taller screen switches
     later — where the hero is proportionally taller than the photo; at 768
     about a third of the width goes. The flight sits in the middle of the
     frame, so centring it trims the foot of the handrail and the plain wall
     evenly.
   - 65% (vertical) matters past that switch, where the hero is proportionally
     wider than the photo; at 1920 about a third of the height goes. Taking
     roughly two parts off the ceiling to one off the floor spends the crop
     behind the floating header rather than on the treads, so the flight still
     runs to the bottom edge while the landing and the upper balustrade stay in
     frame. */
@media (min-width: 640px) {
  .hero--process .hero__bg {
    background-image: url("../img/process-desktop-staircase-renovation.webp");
    background-size: cover;
    background-position: 38% 62%;
    background-repeat: no-repeat;
  }

}

/* ----------------------------------- contact hero photo (tablet + desktop) --
   From 640px up the Contact hero uses a landscape photo; phones keep
   contact-mobile-room-renovation.webp from the rule above. Overlay, type and
   hero box stay the shared ones. 40% 40% keeps the windows in frame on the
   left and the open wall toward the right. */
@media (min-width: 640px) {
  .hero--contact .hero__bg {
    background-image: url("../img/contact-desktop-room-renovation.webp");
    background-size: cover;
    background-position: 54% 42%;
    background-repeat: no-repeat;
  }
}

/* ------------------------------------------------------ projects hero photo --
   Portrait file on phones, landscape file from 640px up, and nothing else:
   the eyebrow, headline, red line and description now read off the shared
   shadow tokens above, and the copy scrim is the shared one. The photographs,
   their crops, the copy and the layout are untouched. */
@media (max-width: 639.98px) {
  .hero--projects .hero__bg {
    background-image: url("../img/projects-hero-mobile.webp");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
}
@media (min-width: 640px) {
  .hero--projects .hero__bg {
    background-image: url("../img/projects-hero-desktop.webp");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
}

/* Hero CTA. The reference draws this as a hairline red rectangle over near
   black: a ringed play glyph, the label with its own short red underline, and
   a trailing arrow. */
.hero__cta {
  min-height: clamp(52px, 4.96vw, 92px);
  /* Ink positions taken off the reference at 1672: ring 102..150, label
     171..309, arrow 337..363, inside a box running 84..392. */
  margin-left: calc(-1 * clamp(1px, .24vw, 6px));
  padding: 0 clamp(16px, 1.25vw, 24px) 0 clamp(10px, .987vw, 19px);
  gap: clamp(12px, 1.226vw, 23px);
  border-width: 1.5px;
  border-radius: clamp(4px, .36vw, 7px);
  background: rgba(0, 0, 0, .88);
  /* Bigger and lighter than it was: the reference label is 15px of cap height
     in a 400 weight, not 13px in a 500. */
  font-weight: 400;
  font-size: clamp(.8125rem, 1.106vw, 1.32rem);
  letter-spacing: .11em;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.hero__cta .play {
  width: clamp(38px, 2.99vw, 56px); height: clamp(38px, 2.99vw, 56px);
  color: rgba(237, 235, 231, .82);
}
.hero__cta .play svg { width: 34%; height: 34%; color: #fff; }
.hero__cta .btn__label { position: relative; color: #fff; }
.hero__cta .btn__label::after {
  content: ""; position: absolute;
  left: 0; right: 0; bottom: clamp(-8px, -.284vw, -3px);
  height: 2px; background: var(--red);
}
.hero__cta .arrow {
  /* 27x22 of drawn stroke in the reference; this glyph fills 0.70 of its box
     across and 0.57 down, so the box has to be 38.5px to get there. */
  width: clamp(20px, 2.303vw, 43px); height: clamp(20px, 2.303vw, 43px);
  color: #fff;
}
.hero__cta:hover { background: rgba(24, 6, 9, .82); }

/* Canada badge */
.canada-badge {
  position: absolute;
  /* 88px clear of the right edge and 13px clear of the feature bar, which
     overlaps the hero's last --bar-h pixels. */
  right: clamp(20px, 5.26vw, 104px);
  bottom: calc(var(--bar-h) + clamp(6px, .78vw, 15px));
  width: clamp(96px, 9.33vw, 176px); aspect-ratio: 1;
  display: none; place-items: center;
  /* pure white, not --plaster: the reference's ring lettering is brighter than
     body copy so it holds up against the photograph at this size */
  color: #fff;
}
/* Our hero photograph is much brighter behind the badge than the reference's
   is, so a soft edgeless scrim sits under it to keep the lettering readable.
   It reads as part of the photo's own falloff rather than as a shape. */
.canada-badge::before {
  content: ""; position: absolute; inset: -34%; z-index: 0;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(5, 7, 10, .70) 0%, rgba(5, 7, 10, .62) 46%,
    rgba(5, 7, 10, .38) 72%, rgba(5, 7, 10, .11) 90%,
    rgba(5, 7, 10, 0) 100%);
  pointer-events: none;
}
.canada-badge svg { position: relative; z-index: 1; width: 100%; height: 100%; }
.canada-badge .ring { animation: spin 15s linear infinite; transform-origin: 50% 50%; }
@keyframes spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------------- feature bar */
/* Full-bleed black strip sitting over the last --bar-h pixels of the hero, with
   four equal columns divided by short centred hairlines rather than by a grid
   gap — the separators in the reference are inset top and bottom, so they
   cannot come from the grid's own background showing through. */
.feature-bar {
  position: relative; z-index: var(--z-raised);
  min-height: var(--bar-h);
  display: flex; align-items: center;
  background: rgba(4, 5, 6, .90);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid rgba(237, 235, 231, .09);
}
.hero + .feature-bar { margin-top: calc(-1 * var(--bar-h)); }

.feature-bar > .container { padding-inline: 0; }

.feature-bar ul {
  display: grid; gap: 0;
  grid-template-columns: 1fr;
  width: 100%;
}
.feature-bar li {
  position: relative;
  display: flex; align-items: flex-start;
  gap: clamp(12px, 1.32vw, 24px);
  /* Asymmetric on purpose: the reference leaves ~48px before the icon and only
     enough after the copy to keep "QUALITY CRAFTSMANSHIP" on one line. The
     26.5px block padding lands the heading's cap line on 838 of 941, where the
     reference puts it. */
  /* The trailing padding is only there to stop a heading running into the next
     divider — no column's copy reaches it — so it is kept slack enough that the
     widened heading tracking cannot force "QUALITY CRAFTSMANSHIP" onto a second
     line in the 1280-1440 band. */
  padding: clamp(14px, 1.585vw, 30px) clamp(10px, 1.4vw, 30px)
           clamp(14px, 1.585vw, 30px) clamp(16px, 2.87vw, 54px);
}
/* the hairline divider, inset 20% top and bottom as in the reference */
.feature-bar li + li::before {
  content: ""; position: absolute;
  left: 0; top: 20%; bottom: 20%; width: 1px;
  background: rgba(237, 235, 231, .21);
}
/* The icons are drawn on a 24 grid but none of them fills it, so the box has to
   be oversized for the ink to reach the reference's ~56px. */
.feature-bar svg {
  width: clamp(42px, 4.25vw, 80px); height: clamp(42px, 4.25vw, 80px);
  /* The 1.25 authored on these glyphs draws 3.7px at this box size; the
     reference's icons are hairlines at roughly 2.5px. */
  stroke-width: .85;
  color: var(--red); flex: none;
  margin-top: clamp(-8px, -.42vw, -4px);
  margin-left: clamp(0px, .23vw, 5px);
}
/* Smaller, lighter and far more widely tracked than it was: the reference sets
   these at 14px of cap height across the same measure the old 15px/500 needed,
   which only works with the tracking opened up to .173em. */
.feature-bar h3 {
  font-family: var(--f-ui); font-weight: 400;
  font-size: clamp(.875rem, 1.033vw, 1.28rem);
  text-transform: uppercase; letter-spacing: .186em;
  line-height: 1.1;
  margin-bottom: clamp(3px, .311vw, 6px);
  color: #fbfbfa;
}
.feature-bar p {
  font-size: clamp(.8125rem, .957vw, 1.15rem);
  /* Pure white, not the .95-alpha near-white this carried: the strip is body
     description copy and takes the same ink as every other description. */
  color: #ffffff;
  line-height: 1.75;                 /* 28px of pitch @1672 */
}

/* ---------------------------------------------------------------- sections */
.section { padding: clamp(64px, 9vw, 128px) 0; }
.section--tight { padding: clamp(48px, 6vw, 88px) 0; }
.section--surface { background: var(--charcoal); }
.section--raised { background: var(--surface); }

.section-head { max-width: 720px; margin-bottom: var(--s-8); }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .eyebrow { justify-content: center; }
.section-head--center .lede { margin-inline: auto; }
.section-head .lede { margin-top: var(--s-4); }

.split {
  display: grid; gap: clamp(32px, 5vw, 72px);
  grid-template-columns: 1fr;
  align-items: center;
}

/* ------------------------------------------------------------------- cards */
.grid {
  display: grid; gap: clamp(16px, 2vw, 24px);
  /* --min lets a section opt into narrower columns, e.g. style="--min:220px" */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--min, 300px)), 1fr));
}

.card {
  position: relative;
  padding: clamp(24px, 3vw, 40px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease-out),
              background var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-out);
}
.card::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--red); transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-slow) var(--ease-out);
}
.card:hover, .card:focus-within {
  border-color: var(--graphite);
  background: var(--surface-2);
  transform: translateY(-4px);
}
.card:hover::after, .card:focus-within::after { transform: scaleX(1); }

.card__icon {
  width: 52px; height: 52px; margin-bottom: var(--s-5);
  display: grid; place-items: center;
  border: 1px solid var(--red); border-radius: var(--radius);
  color: var(--red);
}
.card__icon svg { width: 26px; height: 26px; }
.card h3 {
  font-family: var(--f-ui); font-weight: 600;
  font-size: var(--t-xl); text-transform: uppercase; letter-spacing: .04em;
  margin-bottom: var(--s-3);
}
.card p { color: #ffffff; font-size: var(--t-base); }
.card ul { margin-top: var(--s-4); display: grid; gap: var(--s-2); }
.card ul li {
  position: relative; padding-left: 22px;
  font-size: var(--t-sm); color: #ffffff;
}
.card ul li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 8px; height: 2px; background: var(--red);
}
/* Rows that lead with their own icon take no dash marker. */
.card ul.contact-list li::before { content: none; }

/* whole-card link target */
.card__link::before { content: ""; position: absolute; inset: 0; z-index: 1; }
.card__more {
  display: inline-flex; align-items: center; gap: var(--s-2);
  margin-top: var(--s-5);
  font-family: var(--f-ui); font-weight: 600; font-size: var(--t-sm);
  text-transform: uppercase; letter-spacing: .12em; color: var(--plaster);
}
.card__more svg { width: 16px; height: 16px; color: var(--red);
  transition: transform var(--dur) var(--ease-out); }
.card:hover .card__more svg { transform: translateX(4px); }

/* -------------------------------------------------------------- numbering */
.steps { display: grid; gap: clamp(16px, 2vw, 24px); counter-reset: step; }
@media (min-width: 900px) { .steps { grid-template-columns: repeat(2, 1fr); } }

.step {
  position: relative; counter-increment: step;
  display: grid; grid-template-columns: auto 1fr; gap: var(--s-5);
  padding: clamp(24px, 3vw, 36px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.step::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--f-display); font-size: clamp(2.25rem, 1.6rem + 2vw, 3.25rem);
  line-height: .85; color: var(--red);
}
.step h3 {
  font-family: var(--f-ui); font-weight: 600; font-size: var(--t-lg);
  text-transform: uppercase; letter-spacing: .05em; margin-bottom: var(--s-2);
}
.step p { color: #ffffff; font-size: var(--t-base); }

/* ------------------------------------------------------------------- stats */
.stats {
  display: grid; gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  overflow: hidden;
  /* Always 2x2. Both usages sit in the narrow column of .split--wide, where
     the old min-width:900px "4 across" rule overflowed and overflow:hidden
     above silently cut the last stat in half. auto-fit is no good either —
     with four items it can settle on three columns and orphan the fourth. */
  grid-template-columns: repeat(2, 1fr);
}
.stat { padding: clamp(20px, 3vw, 36px); background: var(--surface); text-align: center; }
.stat dt {
  font-family: var(--f-display);
  font-size: clamp(2rem, 1.3rem + 2.6vw, 3.5rem);
  color: var(--plaster); line-height: 1;
  font-variant-numeric: tabular-nums; /* stops width jitter while counting */
}
.stat dt .unit { color: var(--red); }
.stat dd {
  margin-top: var(--s-2);
  font-family: var(--f-ui); font-size: var(--t-xs);
  text-transform: uppercase; letter-spacing: .18em; color: var(--muted);
}

/* ------------------------------------------------------------ testimonials */
.quote {
  display: flex; flex-direction: column; gap: var(--s-4);
  padding: clamp(24px, 3vw, 40px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  height: 100%;
}
.quote__stars { display: flex; gap: 3px; color: var(--red); }
.quote__stars svg { width: 18px; height: 18px; }
.quote blockquote { font-size: var(--t-lg); line-height: 1.5; color: var(--plaster); }
.quote figcaption { margin-top: auto; padding-top: var(--s-4); border-top: 1px solid var(--line); }
.quote cite { font-style: normal; font-family: var(--f-ui); font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase; font-size: var(--t-sm); }
.quote .meta { display: block; font-size: var(--t-sm); color: var(--muted); margin-top: 2px; }

/* placeholder flag — remove once real reviews are in */
.is-placeholder {
  position: relative;
  border-style: dashed;
  border-color: var(--graphite);
}
.is-placeholder::before {
  content: "SAMPLE — REPLACE WITH A REAL REVIEW";
  position: absolute; top: 10px; right: 12px;
  font-family: var(--f-ui); font-size: 10px; letter-spacing: .16em;
  color: var(--ink); background: var(--muted);
  padding: 3px 8px; border-radius: 2px;
}

/* --------------------------------------------------------------- accordion */
.accordion { border-top: 1px solid var(--line); }
.accordion__item { border-bottom: 1px solid var(--line); }
.accordion__trigger {
  width: 100%; min-height: 64px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-5) 0;
  background: none; border: 0; cursor: pointer; text-align: left;
  font-family: var(--f-ui); font-weight: 500; font-size: var(--t-lg);
  color: var(--plaster);
  transition: color var(--dur) var(--ease-out);
}
.accordion__trigger:hover { color: var(--red); }
.accordion__icon {
  position: relative; width: 20px; height: 20px; flex: none;
}
.accordion__icon::before, .accordion__icon::after {
  content: ""; position: absolute; inset: 50% 0 auto 0;
  height: 2px; background: var(--red);
  transition: transform var(--dur) var(--ease-out);
}
.accordion__icon::after { transform: rotate(90deg); }
.accordion__trigger[aria-expanded="true"] .accordion__icon::after { transform: rotate(0); }

.accordion__panel {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-slow) var(--ease-out);
}
.accordion__panel > div { overflow: hidden; }
.accordion__trigger[aria-expanded="true"] + .accordion__panel { grid-template-rows: 1fr; }
.accordion__panel p { padding-bottom: var(--s-5); color: #ffffff; max-width: 70ch; }

/* ------------------------------------------------------------------- forms */
.form { display: grid; gap: var(--s-5); }
.field { display: grid; gap: var(--s-2); }
.field label {
  font-family: var(--f-ui); font-weight: 500; font-size: var(--t-sm);
  text-transform: uppercase; letter-spacing: .12em; color: var(--plaster);
}
.field .req { color: var(--red); margin-left: 2px; }
.field .hint { font-size: var(--t-sm); color: var(--muted); }

.field input, .field select, .field textarea {
  width: 100%; min-height: 52px;      /* >= 44px touch target */
  padding: 14px 16px;
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--plaster);
  font-size: var(--t-base);           /* 16px stops iOS auto-zoom */
  transition: border-color var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out);
}
.field textarea { min-height: 148px; resize: vertical; line-height: 1.6; }
/* Muted, not Graphite. Placeholder text sits on the input's --ink ground, so
   Graphite measured 3.86:1 against the 4.5:1 AA floor. Muted measures 8.64:1
   and still reads as secondary against the --plaster the visitor types in. */
.field input::placeholder, .field textarea::placeholder { color: var(--muted); }
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--graphite); }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(222, 36, 56, .22);
}
.field select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%23A8AEB3' stroke-width='1.6'%3E%3Cpath d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 18px center;
  background-size: 12px;
  padding-right: 44px;
}

.field .error {
  display: none;
  font-size: var(--t-sm); color: #FF8A96;  /* lightened red — 4.5:1 on charcoal */
}
.field[data-invalid="true"] input,
.field[data-invalid="true"] select,
.field[data-invalid="true"] textarea { border-color: #FF8A96; }
.field[data-invalid="true"] .error { display: block; }

.form__row { display: grid; gap: var(--s-5); }
@media (min-width: 700px) { .form__row { grid-template-columns: 1fr 1fr; } }

/* Honeypot field (name="_gotcha"). Kept out of sight and out of the layout so
   the grid gap does not open a gap where nothing is drawn. Belt and braces on
   top of the [hidden] attribute it also carries. */
.hp { position: absolute !important; left: -9999px; width: 1px; height: 1px;
      opacity: 0; pointer-events: none; }

.form__status {
  padding: var(--s-4) var(--s-5);
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: var(--t-base);
}
.form__status[hidden] { display: none; }
.form__status[data-tone="ok"] { border-color: #3F7D52; color: #A7E0B8; }
.form__status[data-tone="err"] { border-color: #8C3340; color: #FF8A96; }

/* -------------------------------------------------------------- cta band */
.cta-band {
  position: relative; overflow: hidden;
  background:
    radial-gradient(760px 300px at 50% 0%, rgba(222, 36, 56, .18), transparent 70%),
    var(--charcoal);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  text-align: center;
}
.cta-band .section-title { margin-bottom: var(--s-5); }
.cta-band .actions {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  justify-content: center; margin-top: var(--s-7);
}

/* ------------------------------------------------------------------ footer */
.site-footer { background: var(--ink); padding: clamp(56px, 7vw, 96px) 0 var(--s-6); }
.footer-grid {
  display: grid; gap: clamp(32px, 4vw, 56px);
  grid-template-columns: 1fr;
  padding-bottom: var(--s-8);
  border-bottom: 1px solid var(--line);
}
@media (min-width: 700px)  { .footer-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .footer-grid { grid-template-columns: 2fr 1fr 1fr 1.4fr; } }

.footer-brand .logo { background: none; border: 0; padding: 0; margin-bottom: var(--s-4); }
.footer-brand p { color: #ffffff; font-size: var(--t-base); max-width: 38ch; }

.footer-col h4 {
  font-family: var(--f-ui); font-weight: 600; font-size: var(--t-sm);
  text-transform: uppercase; letter-spacing: .16em;
  color: var(--plaster); margin-bottom: var(--s-4);
}
.footer-col ul { display: grid; gap: var(--s-3); }
.footer-col a, .footer-col li { color: var(--muted); font-size: var(--t-base); }
.footer-col a { display: inline-block; min-height: 24px; transition: color var(--dur) var(--ease-out); }
.footer-col a:hover { color: var(--red); }

.footer-bottom {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  align-items: center; justify-content: space-between;
  padding-top: var(--s-5);
  /* Muted, not Graphite. This row is 14px body text on --ink, where Graphite
     measures 3.86:1 — under the 4.5:1 WCAG AA floor for normal-size text.
     Muted is the palette's existing secondary-text colour and the same one the
     footer columns above already use; on --ink it measures 8.64:1. */
  font-size: var(--t-sm); color: var(--muted);
}
.footer-bottom .made {
  display: inline-flex; align-items: center; gap: var(--s-2);
}
.footer-bottom .made svg { width: 14px; height: 16px; color: var(--red); }

/* --------------------------------------------------------- scroll reveal */
.reveal {
  opacity: 0; transform: translateY(18px);
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}
.reveal.is-visible { opacity: 1; transform: none; }

/* ------------------------------------------------------------ breakpoints */
/* ---- 375px and up (base) ---------------------------------------------- */
/* The base rules above are the phone layout: single-column feature bar, drawer
   navigation, badge hidden, hero headline on the 2.35rem floor of --t-hero. */
@media (max-width: 639.98px) {
  /* Phone: nothing may exceed the viewport, and the hero has to breathe
     without the feature bar overlapping it. The badge stays — it is part of
     the identity — but shrinks and sits beside the CTA rather than over it. */
  .hero--photo {
    min-height: 0;
    padding: 122px 0 clamp(40px, 9vw, 72px);
  }
  .hero + .feature-bar { margin-top: 0; }
  .feature-bar { min-height: 0; }
  .hero--photo .hero__inner { padding-left: 14px; }
  .hero--photo .hero__rule { display: block; left: 0; top: -6px; height: 62%; }

  /* Phones load a portrait frame of the same kitchen instead of the landscape
     hero.jpg, which a phone could only ever show as a narrow centre slice.
     Scoped to this breakpoint, so from 640px up hero.jpg is untouched. cover,
     never stretched — the hero box crops the 941x1672 file, and 62% still
     spends that crop on the patio door rather than on the island. */
  .hero--photo .hero__bg[data-photo] {
    background-image: url("../img/home-hero-mobile.webp");
    background-size: cover;
    background-repeat: no-repeat;
    background-position: 62% center;
  }

  /* This kitchen is dark enough that the copy needs no pool on a phone: the
     text edges carry it, and the fades are kept only as a hint. */
  .hero--photo::before { background: none; }
  .hero--photo::after { content: none; }

  /* Held just short of the full column: on a 430px phone the second line
     otherwise runs all the way onto the bright specular edge of the island,
     which is the one place in the frame white type cannot hold. Capping the
     measure keeps the reference's three-line rag at every phone width and lands
     the short last line over the dark floor. */
  .hero--photo .lede {
    max-width: 21.5rem;
    /* One step down from the 16px the desktop clamp floors at, with tighter
       leading so the paragraph reads as a single block and hands some of the
       frame back to the photograph. */
    font-size: clamp(.96rem, 4.17vw, 1.04rem);
    /* a shade tighter than it was, so the larger type costs the block only a
       couple of pixels of height */
    line-height: 1.50;
  }
  .canada-badge {
    display: grid;
    width: 84px;
    right: 14px;
    bottom: clamp(34px, 8vw, 64px);
  }
  /* let the copy rewrap to the column instead of breaking mid-thought */
  .feature-bar .opt { display: none; }
  .feature-bar li + li::before { left: clamp(16px, 3.11vw, 58px); right: 0; top: 0;
    bottom: auto; width: auto; height: 1px; }
}

/* ------------------------------------ phone heroes fill the whole screen ----
   On a phone every one of the six photo heroes runs the full height of the
   visible viewport, so nothing of the section below shows before the visitor
   scrolls. JavaScript records the opening innerHeight in pixels and keeps it
   stable while browser chrome collapses, so the hero and its cover image cannot
   resize or appear to zoom during the first swipe. The CSS fallback uses svh.

   flex-start, not the inherited centring: the copy keeps the top padding it
   already had, so the eyebrow, headline, rule, lede and button sit exactly
   where they sat before and the extra height is photograph below them. This
   block has to stay after the .hero--photo rules above and before the
   landscape-phone block below, both of which set min-height themselves. */
@media (max-width: 639.98px) {
  .hero--photo,
  .hero--services,
  .hero--about,
  .hero--process,
  .hero--contact,
  .hero--projects {
    min-height: 100vh;
    min-height: var(--mobile-hero-height, 100svh);
    align-items: flex-start;
  }

  /* Keep the fixed header geometry stable when the first scroll marks it stuck. */
  .site-header.is-stuck { padding-block: var(--hdr-top); }
}

/* Homepage CTA sits on the Canada badge's centre line from 375px up; narrower,
   it would run into the badge. ::after holds its original 52px so the inner
   box and red rule stay put, and max() never lifts it above where it sat. */
@media (max-width: 639.98px) {
  @media (min-width: 375px) {
    .hero--photo .hero__inner::after { content: ""; display: block; height: 52px; }
    .hero--photo .hero__cta {
      position: absolute;
      /* hero height - top padding - badge offset - half badge - half button */
      top: max(100% - 52px, var(--mobile-hero-height, 100svh) - 122px
                            - clamp(34px, 8vw, 64px) - 42px - 26px);
    }
  }
}

/* -------------------------------------------- phone hero type (phones only) --
   Only the six photo heroes are named here, so Careers, Reviews and 404 remain
   untouched. This block changes type size and description colour only. */
@media (max-width: 639.98px) {
  .hero--photo .eyebrow,
  .hero--services .eyebrow,
  .hero--about .eyebrow,
  .hero--process .eyebrow,
  .hero--contact .eyebrow,
  .hero--projects .eyebrow { font-size: 1rem; }

  .hero--photo h1 { font-size: 2.48rem; }

  .hero--services h1,
  .hero--process h1,
  .hero--contact h1 { font-size: clamp(2.70rem, 1.80rem + 3.9vw, 4.65rem); }

  /* About carries the longest headline, so its smaller increase protects the
     existing two-line composition across phone widths. */
  .hero--about h1 { font-size: min(9.1vw, 2.62rem); }

  .hero--projects h1 {
    font-size: clamp(2.55rem, 1.71rem + 3.6vw, 4.65rem);
  }

  .hero--services .lede,
  .hero--about .lede,
  .hero--process .lede,
  .hero--contact .lede,
  .hero--projects .lede { line-height: 1.56; }

  .hero--photo .lede,
  .hero--services .lede,
  .hero--about .lede,
  .hero--process .lede,
  .hero--contact .lede,
  .hero--projects .lede {
    color: #fff;
    font-size: 1.3125rem;
  }
}

/* Phones: the whole header group comes down by about a tenth — the lockup, the
   CTA box and the menu bars — which hands that height back to the hero above
   the fold. The CTA label itself is left at 13px, the size it was deliberately
   raised to, and the button loses the height instead; the menu button keeps its
   full 44x44 box and only its bars shrink. Both tap targets are still at or
   above the 44x44 floor. The rule below re-sets several of these for small
   phones, so it has to stay after this one. */
@media (max-width: 639.98px) {
  .logo { padding: 4px 17px 4px 4px; gap: 6px; }
  .logo__mark { width: 38px; height: 33px; }
  .logo__name { font-size: .93rem; letter-spacing: .125em; }
  .logo__sub  { font-size: .59rem; letter-spacing: .26em; }
  .header-cta {
    min-height: 46px;
    font-size: .875rem;
  }
  .nav-toggle { width: 46px; height: 46px; }
  .nav-toggle span { width: 21px; }
}

/* Small phones: the logo lockup, the CTA and the hamburger have to share
   ~340px of usable width, so the lockup and the button both step down. */
@media (max-width: 479.98px) {
  :root { --shell-pad: 14px; }
  .header-inner { gap: 8px; }
  .logo { padding: 4px 11px 4px 4px; gap: 5px; }
  .logo__mark { width: 34px; height: 29px; }
  .logo__name { font-size: .79rem; letter-spacing: .125em; }
  .logo__sub  { font-size: .5rem; letter-spacing: .2em; }
  .header-cta { padding: 0 10px; font-size: .75rem; letter-spacing: .05em; }
}

@media (min-width: 640px) {
  .feature-bar ul { grid-template-columns: repeat(2, 1fr); }
  .canada-badge { display: grid; }
  /* room for the full label and the trailing arrow from tablet up */
  .header-cta .cta-lead { display: inline; }
  .header-cta .arrow { display: block; }
  /* the red rule is part of the hero's identity, so it comes back as soon as
     the gutter can hold it rather than waiting for the 900px breakpoint */
  .hero--photo .hero__rule { display: block; }
}

/* ---- 768px tablet ------------------------------------------------------ */
@media (min-width: 640px) and (max-width: 1023.98px) {
  /* Two columns of two. The middle divider would otherwise be the only one on
     the second row, so rows get a hairline of their own instead. */
  .feature-bar { min-height: 0; }
  .hero + .feature-bar { margin-top: 0; }
  .feature-bar li:nth-child(n + 3)::before {
    left: 0; right: 0; top: 0; bottom: auto; width: auto; height: 1px;
  }
  .feature-bar li:nth-child(3)::before { content: ""; }
  .feature-bar li:nth-child(2)::before,
  .feature-bar li:nth-child(4)::before {
    left: 0; top: 20%; bottom: 20%; width: 1px; height: auto;
  }
  .feature-bar .opt { display: none; }
  .hero--photo {
    min-height: 0;
    padding: clamp(150px, 20vw, 210px) 0 clamp(96px, 13vw, 150px);
  }
  .canada-badge {
    right: clamp(20px, 4vw, 42px);
    bottom: clamp(20px, 4vw, 44px);
    width: clamp(96px, 13vw, 128px);
  }
  .hero--photo .lede { max-width: 34ch; }
  .hero--photo .hero__rule { height: clamp(230px, 46vw, 380px); }
}

/* Homepage description only, tablet and up. This changes its line measure,
   not its typography or any hero/photo geometry. */
@media (min-width: 640px) {
  .hero--photo .lede {
    max-width: min(46rem, calc(100vw - var(--shell-pad) - var(--shell-pad) - var(--hero-inset) - 20px));
  }
}

@media (min-width: 900px) {
  .split--half { grid-template-columns: 1fr 1fr; }
  .split--wide { grid-template-columns: 1.25fr 1fr; }
  .hero__rule { display: block; }
  .header-cta .clipboard { display: block; }
}

/* ---- 1024px laptop / tablet landscape and up --------------------------- */
@media (min-width: 1024px) {
  .feature-bar { min-height: var(--bar-h); }
  .hero + .feature-bar { margin-top: calc(-1 * var(--bar-h)); }
  .feature-bar ul { grid-template-columns: repeat(4, 1fr); }
  .hero--photo { min-height: 100vh; min-height: 100dvh; }
  .canada-badge {
    bottom: calc(var(--bar-h) + clamp(18px, 1.45vw, 28px));
  }
  /* From 1024px the header becomes the tall glass rail, so the compact heroes
     measure their top padding from the rail itself rather than from vh — that
     is what keeps the eyebrow the same distance under the header at 1024,
     1440 and 1920 alike. */
  .hero--compact {
    padding-top: calc(var(--rail-h) + var(--hdr-top) * 2 + clamp(28px, 3.2vw, 62px));
  }
  /* The five photo heroes fill the screen like the homepage's. The copy stays
     where it was; the extra height is photograph below it. */
  .hero--services,
  .hero--about,
  .hero--process,
  .hero--contact,
  .hero--projects {
    min-height: 100vh; min-height: 100dvh;
    align-items: flex-start;
  }
}

/* Laptop: four columns still, but the vw-derived sizes would leave the widest
   heading a few pixels short of its column, so the strip tightens rather than
   wrapping or spilling below the fold. */
@media (min-width: 1024px) and (max-width: 1279.98px) {
  .feature-bar li { gap: 10px; padding: 14px 10px 14px 16px; }
  .feature-bar svg { width: 34px; height: 34px; margin: 0; }
  .feature-bar h3 { font-size: .8125rem; letter-spacing: .06em; }
  .feature-bar p  { font-size: .78125rem; line-height: 1.7; }
}

/* ---- the reference layout in full -------------------------------------- */
@media (min-width: 1024px) {
  :root { --hdr-pill: clamp(48px, 3.708vw, 70px); }  /* 62px @1672 — reference nav pill is 39..101 */
  .nav { display: block; }
  .nav-toggle, .drawer { display: none; }
  /* the long glass rail replaces the logo's own pill */
  .header-inner::before { display: block; }
  .header-inner { min-height: var(--rail-h); }
  .logo {
    background: none; border-color: transparent; backdrop-filter: none;
    -webkit-backdrop-filter: none;
    padding: 0; gap: clamp(6px, .75vw, 14px);
    margin-left: clamp(2px, .359vw, 7px);
  }
  .site-header .logo__mark {
    width: clamp(58px, 5.98vw, 112px);
    height: clamp(46px, 4.67vw, 88px);
    /* The reference hangs the house 2.5px above the row's optical centre; the
       wordmark stays put, so this rides on the mark alone. */
    position: relative; top: calc(-1 * clamp(1px, .15vw, 4px));
    color: #fff;
  }
  /* The reference sets the wordmark in a wide bold grotesque, not in the
     condensed Oswald the rest of the UI uses — Oswald cannot reach the
     reference's 208px at the reference's 19px cap height without tracking so
     wide the lockup falls apart. Inter, already loaded for body copy, lands on
     both measurements at once. Header only: the footer lockup is untouched. */
  .site-header .logo__name {
    font-family: var(--f-body); font-weight: 700;
    font-size: clamp(1rem, 1.597vw, 1.95rem); letter-spacing: .09em;
    color: #fff;
  }
  .site-header .logo__sub {
    font-family: var(--f-body); font-weight: 400;
    font-size: clamp(.62rem, .82vw, 1rem); letter-spacing: .335em;
    margin-top: clamp(3px, .36vw, 7px);
    /* Reference reads rgb(239,240,240) here, not the muted grey the rest of
       the UI uses for secondary type. */
    color: #efefee;
  }
  /* The reference hangs the wordmark below the centre of the house mark rather
     than centring the two on each other. */
  .site-header .logo__text { position: relative; top: clamp(2px, .42vw, 10px); }
  /* Nav pill and CTA sit a few pixels below the rail's own centre line, the
     CTA a shade lower than the nav — both tops land on 39 in the reference
     even though the button is the taller of the two. */
  .nav, .header-cta { position: relative; }
  /* The auto margins centre the pill in the free space, which lands it ~4.5px
     right of where the reference draws its left cap. */
  .nav { top: clamp(0px, .209vw, 5px); left: calc(-1 * clamp(0px, .269vw, 7px)); }
  .header-cta {
    top: clamp(0px, .269vw, 7px);
    /* The reference lets the button overhang the rail's right cap by ~8px. */
    margin-right: calc(-1 * clamp(0px, .478vw, 11px));
  }
  /* the nav pill's auto margins already distribute the free space */
  .header-cta { margin-left: 0; }
  /* Subtle shrink on scroll, not a jump. The mark comes down with it so the
     rail keeps matching the nav and CTA exactly. */
  .site-header.is-stuck { --rail-h: clamp(48px, 4.4vw, 78px); --hdr-pill: clamp(44px, 3.1vw, 56px); }
  .site-header.is-stuck .logo__mark {
    width: clamp(44px, 3.9vw, 72px); height: clamp(35px, 3.05vw, 56px);
  }
  .site-header.is-stuck .logo__text { top: clamp(1px, .24vw, 5px); }
}

/* landscape phones: give the hero back some room */
@media (max-height: 560px) and (orientation: landscape) {
  .hero { min-height: 0; padding: 140px 0 180px; }
  /* the inner heroes keep their own, much shorter box here too */
  .hero--compact { padding: 104px 0 48px; }
  .hero--photo { min-height: 0; padding: 128px 0 clamp(96px, 13vw, 150px); }
  /* The badge used to be dropped here, which also took it off any desktop
     window shorter than 560px — a laptop with the dev tools docked, for one.
     It is part of the hero's identity, so it stays and steps down instead. */
  .canada-badge { width: 76px; }
}

/* ------------------------------------------------------------------- print */
@media print {
  .site-header, .drawer, .canada-badge, .cta-band, .btn { display: none !important; }
  body { background: #fff; color: #000; }
  /* Description copy is pure white on screen (see the rules above); on paper
     it has to come back to black or it prints as nothing at all. */
  .lede, .prose p, .prose strong, .card p, .card ul li, .step p,
  .accordion__panel p, .feature-bar p, .footer-brand p {
    color: #000 !important;
    text-shadow: none !important;
  }
  .card, .step, .quote { border-color: #999; background: none; }
}

/* =============================================================================
   PROJECTS PAGE — portfolio
   Used only by projects.html; nothing above this block references it.

   Ratios are chosen to suit the source photographs rather than to force one
   house format, which is why the page mixes portrait and landscape frames:

     amenity room, condo interiors   2048x1152 / 1672x941  -> 16/9  uncropped
     condo corridors                 1536x2048             -> 3/4   uncropped
     bathroom                        1152x2048             -> natural, uncropped
     whole-home set                  1152x2048 (9/16)      -> trimmed to 4/5
                                                              (cover) and 3/4

   Only the whole-home set is trimmed, and every photograph opens full size and
   uncropped in the lightbox, so no detail is permanently lost to a crop.
   ========================================================================== */

/* ---------------------------------------------------------------- one shot */
/* A real <a href> to the image file, not a button: with JavaScript off the
   link still opens the full photograph, which is how the rest of this site
   degrades. main.js intercepts the click and opens the lightbox instead. */
.shot {
  display: block; position: relative; width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-out);
}
.shot img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
.shot:hover, .shot:focus-visible { border-color: var(--graphite); transform: translateY(-3px); }

/* Natural-ratio variant — the frame takes the proportions of the photograph. */
.shot--natural img { height: auto; object-fit: fill; }

/* Zoom affordance. Kept partly visible rather than hover-only, so it is also
   there on touch, where :hover never fires. */
.shot__zoom {
  position: absolute; right: 10px; bottom: 10px;
  width: 34px; height: 34px; display: grid; place-items: center;
  background: rgba(11, 14, 16, .74);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  color: var(--plaster); opacity: .8;
  transition: opacity var(--dur) var(--ease-out);
}
.shot__zoom svg { width: 17px; height: 17px; }
.shot:hover .shot__zoom, .shot:focus-visible .shot__zoom { opacity: 1; }

/* -------------------------------------------------------------- a project */
.project + .project { margin-top: clamp(56px, 8vw, 104px); }

.project__head { max-width: 760px; margin-bottom: clamp(20px, 3vw, 32px); }
.project__head h3 {
  font-family: var(--f-display); font-weight: 400;
  font-size: var(--t-2xl); line-height: 1.04;
  text-transform: uppercase; letter-spacing: .01em;
  margin-top: var(--s-2);
}
.project__head p { color: var(--plaster); margin-top: var(--s-4); }

/* Small caption under a frame, e.g. the room name on an interior. */
.shot-caption {
  margin-top: var(--s-3);
  font-family: var(--f-ui); font-weight: 500;
  font-size: var(--t-sm); letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
}

/* ------------------------------------------ project 01 — featured layout */
/* Left: the portrait cover at 4/5. Right: the six supporting rooms in a
   three-column 3/4 grid. The two columns come out within ~30px of the same
   height at desktop widths, which is why the split is 2fr / 3fr. */
.project-feature { display: grid; gap: clamp(16px, 2.4vw, 28px); }
.project-feature__cover .shot { aspect-ratio: 4 / 5; }

.project-rooms {
  display: grid; gap: clamp(10px, 1.3vw, 16px);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* Three across from small-tablet width up: two columns of 3/4 portraits on a
   768px screen makes this block roughly 1350px tall on its own. */
@media (min-width: 560px) {
  .project-rooms { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.project-rooms .shot { aspect-ratio: 3 / 4; }

@media (min-width: 900px) {
  .project-feature { grid-template-columns: 2fr 3fr; align-items: start; }
}

/* --------------------------------- project 02 — during / after comparison */
.compare { display: grid; gap: clamp(16px, 2.2vw, 24px); }
.compare .shot { aspect-ratio: 16 / 9; }
.compare figure { margin: 0; }

/* The arrow between the two frames: sideways on desktop, downward once the
   pair stacks, so the During-to-After reading survives the breakpoint. */
.compare__arrow {
  display: grid; place-items: center;
  color: var(--red);
  transform: rotate(90deg);
}
.compare__arrow svg { width: 28px; height: 28px; }

@media (min-width: 880px) {
  .compare { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; }
  .compare__arrow { transform: none; }
}

/* Stage label. "During" reads as a neutral site state, "After" takes the
   brand red — white on Maple is the 4.76:1 pairing the palette already uses
   for button labels. */
.stage {
  display: inline-flex; align-items: center;
  font-family: var(--f-ui); font-weight: 600;
  font-size: var(--t-xs); letter-spacing: .18em; text-transform: uppercase;
  padding: 5px 12px; border-radius: var(--radius);
}
.stage--during { background: var(--surface-2); color: var(--plaster); border: 1px solid var(--graphite); }
.stage--after { background: var(--red); color: #fff; }

.compare figcaption {
  margin-top: var(--s-3);
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  font-size: var(--t-sm); color: var(--muted);
}

/* the two extra construction-stage frames below the comparison */
.project-extra {
  display: grid; gap: clamp(16px, 2.2vw, 24px);
  /* Enough clear air that the After caption above reads as belonging to the
     comparison rather than to the first construction frame below it. */
  margin-top: clamp(32px, 4vw, 44px);
}
.project-extra .shot { aspect-ratio: 16 / 9; }
@media (min-width: 720px) { .project-extra { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* --------------------------------- projects 03 + 04 — two compact blocks */
.project-duo { display: grid; gap: clamp(48px, 7vw, 72px); }
/* The sibling margin above is for projects stacked in one column. Inside the
   duo grid it would push the second column down by its own height, so the
   grid gap does the separating in both axes instead. */
.project-duo .project + .project { margin-top: 0; }
@media (min-width: 960px) { .project-duo { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; } }

/* Bathroom: portrait, shown at its own proportions and never cropped, so the
   tiled surround and the rainfall head stay in frame at every width. */
.shot-portrait { max-width: 320px; }

/* Corridor: two finished views, side by side at their native 3/4. */
.pair { display: grid; gap: clamp(12px, 1.6vw, 20px); }
.pair .shot { aspect-ratio: 3 / 4; }
@media (min-width: 520px) { .pair { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---------------------------------------------- selected condo interiors */
.interiors {
  display: grid; gap: clamp(16px, 2vw, 24px);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
}
.interiors .shot { aspect-ratio: 16 / 9; }
.interiors figure { margin: 0; }

/* ----------------------------------------------------------- lightbox */
.lightbox {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: grid; place-items: center;
  padding: clamp(12px, 3vw, 40px);
}
.lightbox[hidden] { display: none; }

.lightbox__backdrop {
  position: absolute; inset: 0;
  background: rgba(11, 14, 16, .92);
}

.lightbox__panel {
  position: relative;
  display: grid; gap: var(--s-4);
  justify-items: center;
  max-width: min(1100px, 100%);
  max-height: 100%;
}

.lightbox__img {
  max-width: 100%;
  /* leaves room for the caption under a tall portrait on a short window */
  max-height: calc(100vh - 150px);
  width: auto; height: auto;
  object-fit: contain;
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.lightbox__caption {
  max-width: 62ch; text-align: center;
  font-size: var(--t-sm); color: var(--muted);
}

/* 44px minimum touch target, matching the header CTA rule elsewhere here */
.lightbox__close {
  position: absolute; top: 0; right: 0;
  transform: translate(25%, -25%);
  width: 46px; height: 46px;
  display: grid; place-items: center;
  background: var(--surface); color: var(--plaster);
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.lightbox__close:hover { background: var(--red); border-color: var(--red); color: #fff; }
.lightbox__close svg { width: 20px; height: 20px; }

/* On a phone the close button sits inside the panel rather than hanging off
   its corner, where the translate could push it under the screen edge. */
@media (max-width: 560px) {
  .lightbox__close { transform: none; top: 6px; right: 6px; }
  .lightbox__img { max-height: calc(100vh - 170px); }
}

body.lightbox-open { overflow: hidden; }

@media print {
  .lightbox { display: none !important; }
  .shot__zoom { display: none !important; }
}
