/* ===========================================================================
   Horizon Hues Installations
   ---------------------------------------------------------------------------
   Brand direction: calm, warm, premium, understated. Eric's words for the
   name were "subtle and calming... nothing that gets in your face", and he
   asked for a "black/white, custom home vibe". So the palette is near-black
   and white, and the amber (#F29A2E, the settled brand accent) is used only
   where it means something: rules, active state, and calls to action.
   =========================================================================== */

/* --- Barlow (SIL OFL), self-hosted: no third-party font request ----------- */
@font-face { font-family: Barlow; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/barlow-400.woff2") format("woff2"); }
@font-face { font-family: Barlow; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/barlow-500.woff2") format("woff2"); }
@font-face { font-family: Barlow; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/barlow-600.woff2") format("woff2"); }
@font-face { font-family: Barlow; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/barlow-700.woff2") format("woff2"); }

/* --- Archivo (SIL OFL), display only --------------------------------------
   Chosen over a serif (Sem, 2026-08-01, from a six-way comparison). The
   hierarchy comes from two grotesques of different proportion rather than
   from sans against serif: Archivo is broad and open, Barlow is narrower with
   squarer terminals, so a headline reads as a different voice without the
   page changing register. One weight only, which keeps the boundary honest:
   nav, buttons, eyebrows, body and component titles all stay Barlow.
   ------------------------------------------------------------------------- */
@font-face { font-family: Archivo; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/archivo-600.woff2") format("woff2"); }

/* ===========================================================================
   TOKENS
   =========================================================================== */
:root {
  --amber:      #F29A2E;
  --amber-lift: #FFC271;
  --amber-soft: rgba(242, 154, 46, 0.72);
  --amber-dim:  rgba(242, 154, 46, 0.28);
  --amber-wash: rgba(242, 154, 46, 0.10);

  /* ---------------------------------------------------------------------
     A surface scale, not two near-identical greys.

     The first version ran on #0A0A0B and #111113: a luminance ratio of
     1.29:1, which the eye reads as one flat colour. Measured across the site,
     8,732px of page height was transparent (falling through to the base) and
     the only "other" tone covered 3,963px, so the whole thing was effectively
     single-surface. Five steps, each roughly 1.7x the luminance of the last,
     gives real elevation while everything stays unmistakably dark.

     They are also WARM now. The old values leaned blue (B above R); these
     lean toward the brand's ember, which is what stops a dark page reading as
     "switched off" rather than "lit low".
     --------------------------------------------------------------------- */
  --ink-0:  #060605;   /* deepest: wells, the footer, behind photography  */
  --ink:    #0B0A09;   /* page base                                       */
  --ink-2:  #131110;   /* raised sections                                 */
  --ink-3:  #1A1715;   /* cards, inputs                                   */
  --ink-4:  #231F1B;   /* card tops, hovered surfaces                     */

  --line:   rgba(255, 255, 255, 0.10);
  --line-2: rgba(255, 255, 255, 0.18);

  /* Measured against --ink: white 19.8:1, dim 11.4:1, mute 5.7:1, amber 8.9:1.
     All clear WCAG AA for body text; amber clears AAA. */
  --paper:      #FFFFFF;
  --paper-dim:  rgba(255, 255, 255, 0.78);
  --paper-mute: rgba(255, 255, 255, 0.54);

  --header-h: clamp(68px, 8.5vh, 96px);
  --gutter:   clamp(20px, 5vw, 44px);
  --wrap:     1180px;
  --section:  clamp(68px, 9.5vh, 124px);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 12px);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper-dim);
  font-family: Barlow, "Segoe UI", system-ui, -apple-system, sans-serif;
  font-size: clamp(15.5px, 1.05vw, 17px);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
/* Deliberately NO `overflow-x: hidden` on body. It does not fix sideways
   scroll, it hides it - including from the QA sweep, which then reports a
   clipped hero as clean. If something overflows, fix the something. */

img, svg, picture, video { display: block; max-width: 100%; }
img { height: auto; }                 /* never let an intrinsic attr win */

h1, h2, h3, h4 {
  color: var(--paper);
  font-weight: 600;
  line-height: 1.12;
  margin: 0;
  letter-spacing: -0.005em;
}

/* Headlines carry Archivo. Sizes, weight, tracking and leading are exactly
   what was on the comparison page Sem picked from, so the site matches what
   was approved rather than a re-tuned version of it. */
h1, h2 {
  font-family: Archivo, Barlow, "Segoe UI", system-ui, sans-serif;
  font-weight: 600;
  /* The browser evens the lines out rather than us guessing with a <br>. A
     hard break is tied to one font at one width: swapping the display face
     for a wider one immediately orphaned "tidy," onto a line of its own. */
  text-wrap: balance;
}
h1 { line-height: 1.04; letter-spacing: -0.015em; }
h2 { line-height: 1.06; letter-spacing: -0.012em; }
p { margin: 0; }

a { color: inherit; text-decoration: none; }

ul { margin: 0; padding: 0; list-style: none; }

/* Two-tone ring so focus reads on dark sections, light cards and the amber
   button alike - a single-colour outline vanishes on one of the three. */
:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.85);
  border-radius: 2px;
}

::selection { background: var(--amber); color: var(--ink); }

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

.skip {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -140%);
  z-index: 200;
  background: var(--amber);
  color: var(--ink);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.06em;
  padding: 12px 22px;
  transition: transform 0.2s var(--ease);
}
.skip:focus { transform: translate(-50%, 0); }

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

.section { padding-block: var(--section); }
.section--tight { padding-block: calc(var(--section) * 0.62); }
/* Three tones down the page instead of two, so no two consecutive sections
   sit on the same surface and the scroll has something to keep hold of. */
.section--alt  { background: var(--ink-2); }
.section--deep { background: var(--ink-0); }

.section-head { max-width: 720px; margin-bottom: clamp(38px, 5vw, 64px); }
.section-head--center { margin-inline: auto; text-align: center; }

.eyebrow {
  font-size: clamp(10.5px, 0.8vw, 12px);
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 16px;
}

h1 { font-size: clamp(38px, 6.2vw, 68px); }
h2 { font-size: clamp(30px, 4.3vw, 50px); }
h3 { font-size: clamp(18px, 1.6vw, 22px); }

.lead {
  margin-top: 20px;
  font-size: clamp(16.5px, 1.3vw, 19.5px);
  line-height: 1.62;
  color: var(--paper-dim);
}

/* ---------------------------------------------------------------------------
   The horizon arc, not a straight rule.

   Same device as the business card: a shallow curve, brightest in the middle,
   falling to nothing before each edge, with the colour warming towards white
   at the peak. Two sizes, one idea:
     .rule     short accent under an eyebrow or above a tagline
     .divider  full width, straddling the seam between two sections
   Kept as background images so the curve lives in one file rather than being
   pasted into every page as markup.
   --------------------------------------------------------------------------- */
.rule {
  width: clamp(72px, 7.5vw, 112px);
  height: 16px;
  border: 0;
  margin: 0;
  background: url("/assets/img/arc.svg") center / 100% 100% no-repeat;
}
.rule--center { margin-inline: auto; }

.section { position: relative; }

/* ---------------------------------------------------------------------------
   Section seams.

   The curve is a MASK, and the colour is a CSS gradient underneath it. That
   split is the whole point: one fixed graphic repeated down a page reads as
   copy-and-paste, but with the light supplied in CSS every seam can put its
   bright point somewhere else. Each one is a different moment of the same
   sunset rather than the same picture eight times.

   --peak   where along the horizon the light sits
   --reach  how far the glow spreads before it dies
   --lift   overall intensity
   --------------------------------------------------------------------------- */
/* :where() so this contributes ZERO specificity. The variants below are a
   plain element-plus-nth selector, and without this they would lose the
   cascade to a two-class base rule and every seam would render identical. */
:where(.section + .section, .page-head + .section, .showcase + .section)::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(100% - var(--gutter) * 2, var(--wrap));
  height: 40px;
  pointer-events: none;

  --peak: 50%;
  --reach: 32%;
  --lift: 1;

  background-image:
    /* the travelling highlight, parked off-canvas until the seam is in view */
    linear-gradient(90deg,
      rgba(255, 240, 214, 0) 0%,
      rgba(255, 240, 214, 0.85) 50%,
      rgba(255, 240, 214, 0) 100%),
    /* the resting light */
    linear-gradient(90deg,
      rgba(216, 89, 15, 0)     calc(var(--peak) - var(--reach)),
      rgba(224, 116, 28, 0.42) calc(var(--peak) - var(--reach) * 0.55),
      rgba(242, 154, 46, 0.88) calc(var(--peak) - var(--reach) * 0.2),
      rgba(255, 226, 188, 1)   var(--peak),
      rgba(242, 154, 46, 0.88) calc(var(--peak) + var(--reach) * 0.2),
      rgba(224, 116, 28, 0.42) calc(var(--peak) + var(--reach) * 0.55),
      rgba(216, 89, 15, 0)     calc(var(--peak) + var(--reach)));
  background-repeat: no-repeat;
  background-size: 32% 100%, 100% 100%;
  /* A percentage background-position resolves against (container - image),
     not the container, so with a 32%-wide layer "-36%" still leaves a tail
     showing at the edge. -60% / 160% are the first values that park it
     completely out of frame. */
  background-position: -60% 0, 0 0;
  opacity: var(--lift);

  -webkit-mask-image: url("/assets/img/arc-mask.svg");
  mask-image: url("/assets/img/arc-mask.svg");
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* Six variants, so no two seams you can see at once are the same. */
main > section:nth-of-type(6n + 1)::before { --peak: 54%; --reach: 30%; --lift: 0.92; }
main > section:nth-of-type(6n + 2)::before { --peak: 38%; --reach: 34%; --lift: 1;    }
main > section:nth-of-type(6n + 3)::before { --peak: 63%; --reach: 27%; --lift: 0.8;  }
main > section:nth-of-type(6n + 4)::before { --peak: 45%; --reach: 39%; --lift: 0.97; }
main > section:nth-of-type(6n + 5)::before { --peak: 67%; --reach: 30%; --lift: 0.86; }
main > section:nth-of-type(6n + 6)::before { --peak: 41%; --reach: 26%; --lift: 1;    }

/* Light runs the length of the horizon once, when the seam comes into view.
   Same idea as the driveway trail in the hero photo, at rest scale. */
@keyframes seam-sweep {
  from { background-position: -60% 0, 0 0; }
  to   { background-position: 160% 0, 0 0; }
}
.section.is-lit::before { animation: seam-sweep 2.1s 0.15s var(--ease) both; }

/* ===========================================================================
   BUTTONS
   =========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 46px;
  font-family: inherit;
  font-size: clamp(12px, 0.9vw, 13.5px);
  font-weight: 600;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  text-indent: 0.17em;                 /* offset the trailing letter-space */
  padding: 14px clamp(22px, 2.6vw, 36px);
  border: 1px solid var(--amber-soft);
  background: transparent;
  color: var(--amber);
  cursor: pointer;
  transition: background-color 0.26s var(--ease), border-color 0.26s var(--ease),
              color 0.26s var(--ease), box-shadow 0.26s var(--ease);
}
.btn:hover, .btn:focus-visible {
  background: var(--amber-wash);
  border-color: var(--amber);
  color: var(--amber-lift);
  box-shadow: 0 0 28px rgba(242, 154, 46, 0.20);
}

.btn--solid {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--ink);
}
.btn--solid:hover, .btn--solid:focus-visible {
  background: var(--amber-lift);
  border-color: var(--amber-lift);
  color: var(--ink);
}

/* 0.45, not the --line-2 hairline: this button sits over a photograph, and a
   0.18 border disappears entirely against a lit window or a treeline. WCAG
   wants 3:1 on a control's boundary and the hairline was nowhere near it. */
.btn--ghost {
  border-color: rgba(255, 255, 255, 0.45);
  color: var(--paper);
  background: rgba(11, 10, 9, 0.30);
}
.btn--ghost:hover, .btn--ghost:focus-visible {
  border-color: var(--paper);
  background: rgba(255, 255, 255, 0.10);
  color: var(--paper);
  box-shadow: none;
}

.btn--sm { min-height: 40px; padding: 11px 20px; font-size: 11.5px; }
.btn--lg { min-height: 54px; padding: 17px clamp(28px, 3.2vw, 46px); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: clamp(28px, 3.6vw, 40px);
}
.btn-row--center { justify-content: center; }

/* Text link with a permanent underline: colour alone is not a link marker. */
.link {
  color: var(--amber);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  transition: color 0.2s var(--ease);
}
.link:hover { color: var(--amber-lift); }

/* ===========================================================================
   HEADER
   =========================================================================== */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  height: var(--header-h);
  display: flex;
  align-items: center;
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease),
              backdrop-filter 0.3s var(--ease);
  border-bottom: 1px solid transparent;
}
.site-header.is-solid {
  background: rgba(11, 10, 9, 0.90);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--line);
}
/* Interior pages have no hero photo to float over, so the bar starts solid. */
body:not(.home) .site-header { background: rgba(11, 10, 9, 0.90); border-bottom-color: var(--line); }

.header-inner {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(14px, 2vw, 28px);
}

.brand { display: flex; align-items: center; }
.brand img { height: clamp(32px, 4.2vh, 46px); width: auto; }

.nav { justify-self: center; display: flex; align-items: center; gap: clamp(16px, 2.2vw, 36px); }
.nav a {
  position: relative;
  font-size: clamp(11.5px, 0.85vw, 13px);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.74);
  padding: 10px 0;
  transition: color 0.22s var(--ease);
}
.nav a:hover { color: var(--paper); }
/* The active marker is the same horizon arc as the dividers, not a straight
   underline. It is the one piece of UI chrome a visitor looks at most, so it
   is worth the brand carrying through to it. */
.nav a::after {
  content: "";
  position: absolute;
  left: -5px; right: -5px; bottom: -1px;
  height: 9px;
  background: url("/assets/img/arc.svg") center / 100% 100% no-repeat;
  transform: scaleX(0);
  transition: transform 0.28s var(--ease);
}
.nav a:hover::after { transform: scaleX(0.5); }
.nav a[aria-current="page"] { color: var(--amber); }
.nav a[aria-current="page"]::after { transform: scaleX(1); }

.header-cta { justify-self: end; }

.burger {
  display: none;
  justify-self: end;
  width: 46px; height: 46px;
  padding: 0; border: 0; background: none; cursor: pointer;
  position: relative;
}
.burger span {
  position: absolute; left: 11px;
  width: 24px; height: 1.5px;
  background: var(--paper);
  transition: transform 0.3s var(--ease), opacity 0.2s var(--ease);
}
.burger span:nth-child(1) { top: 17px; }
.burger span:nth-child(2) { top: 23px; }
.burger span:nth-child(3) { top: 29px; }
body.menu-open .burger span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
body.menu-open .burger span:nth-child(2) { opacity: 0; }
body.menu-open .burger span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* --- mobile drawer -------------------------------------------------------- */
.drawer {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: rgba(9, 8, 7, 0.97);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 30px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s var(--ease), visibility 0.3s;
}
body.menu-open .drawer { opacity: 1; visibility: visible; }
body.menu-open { overflow: hidden; }
.drawer a:not(.btn) {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.86);
  padding: 6px 10px;
}
.drawer a[aria-current="page"] { color: var(--amber); }
.drawer .tel { font-size: 15px; letter-spacing: 0.05em; text-transform: none; color: var(--paper-mute); }

/* ===========================================================================
   HERO (home)
   =========================================================================== */
.hero {
  position: relative;
  height: 100vh;
  height: 100svh;
  min-height: 540px;
  overflow: hidden;
  isolation: isolate;
}
@media (orientation: landscape) and (max-height: 560px) { .hero { min-height: 0; } }

.hero-photo, .hero-photo img { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero-photo img { object-fit: cover; object-position: 50% 58%; }

/* --- Lights-on video + travelling trail pulse ------------------------------
   The video plays once over the still and holds its final frame, which IS the
   still; then the pulse idles forever on the driveway light line. The pulse
   layers are masked to the line's own pixels (alpha in trail-*.png - CSS
   mask-mode defaults to alpha, a greyscale mask would pass everything), so
   the glow can only brighten light already in the photograph. Settled values
   2026-08-01; the working reference with live sliders is
   ../hero-glow/hero-glow.html. */
.hero {
  --trail: url(/assets/img/trail-desktop.png);
  --pulse-speed: 5s;      /* one pass, centre of the bar to both ends */
  --pulse-gain: 3;        /* alpha up to 1, brightness on the layers past it */
  --pulse-band: 26%;      /* pulse width at full spread */
  --pulse-split: 0.5;     /* width at ignition, fraction of --pulse-band */
  --pulse-bloom: 45px;    /* halo softness */
  /* sampled off the trail's own pixels, not picked - core #F9AA41 sits within
     a few levels of the brand amber #F29A2E */
  --trail-core: 249, 170, 65;
  --trail-edge: 209, 125, 53;
}
@media (max-width: 900px) and (orientation: portrait) {
  .hero { --trail: url(/assets/img/trail-mobile.png); }
}

.hero-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 58%;
}
html:not(.js) .hero-video { display: none; }

/* mask geometry MUST mirror the photo/video object-fit and object-position,
   or the glow slides off the wire as the viewport changes shape */
.hero-glow-wrap { position: absolute; inset: 0; pointer-events: none; }
.hero-glow-wrap .glow {
  position: absolute; inset: 0;
  -webkit-mask-image: var(--trail); mask-image: var(--trail);
  -webkit-mask-size: cover; mask-size: cover;
  -webkit-mask-position: 50% 58%; mask-position: 50% 58%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  mix-blend-mode: screen;
  filter: brightness(max(1, var(--pulse-gain)));
}
.hero-glow-wrap .glow--bloom {
  filter: blur(var(--pulse-bloom)) brightness(max(1, var(--pulse-gain)));
  opacity: 0.9;
}
.hero-glow-wrap .glow--wide {
  filter: blur(calc(var(--pulse-bloom) * 2.4)) brightness(max(1, var(--pulse-gain)));
  opacity: 0.55;
}

/* two mirrored halves: the flipped one reuses the same rightward keyframes in
   a mirrored coordinate system, so there is one set of keyframes to keep
   honest instead of two */
.hero-glow-wrap .half { position: absolute; inset: 0; }
.hero-glow-wrap .half--flip { transform: scaleX(-1); }

.hero-glow-wrap .pulse {
  position: absolute; top: -10%; bottom: -10%; left: 0; width: 100%;
  background: linear-gradient(100deg,
    rgba(var(--trail-edge), 0)                                calc(50% - var(--pulse-band)/2),
    rgba(var(--trail-edge), calc(min(var(--pulse-gain),1)*.30)) calc(50% - var(--pulse-band)/6),
    rgba(var(--trail-core), min(var(--pulse-gain),1))         50%,
    rgba(var(--trail-edge), calc(min(var(--pulse-gain),1)*.42)) calc(50% + var(--pulse-band)/6),
    rgba(var(--trail-edge), 0)                                calc(50% + var(--pulse-band)/2));
}
/* the animation only exists once JS marks the video ended - so the pulse can
   never run over the power-up, however slowly the video loaded. Runs for 72%
   of the cycle then rests off-frame: a pulse that restarts the instant it
   leaves reads as a loop, a beat of darkness reads as a system at idle.
   The opacity ramp is the ignition - centre-out starts AT the centre, so
   without it the pulse would pop in fully lit. */
.hero.lights-held .pulse {
  animation: hero-pulse var(--pulse-speed) linear 0.4s infinite backwards;
}
@keyframes hero-pulse {
  0%   { transform: translateX(0) scaleX(var(--pulse-split)); opacity: 0; }
  6%   { opacity: 1; }
  72%  { transform: translateX(calc(50% + var(--pulse-band)/2)) scaleX(1); opacity: 1; }
  100% { transform: translateX(calc(50% + var(--pulse-band)/2)) scaleX(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-video, .hero-glow-wrap { display: none; }
}

/* Gradients, never a flat tint: a uniform overlay kills the lit windows,
   which are the only reason the photograph is worth having. */
.scrim { position: absolute; inset: 0; pointer-events: none; }
.scrim--top {
  background: linear-gradient(180deg,
    rgba(0,0,0,0.62) 0%, rgba(0,0,0,0.30) 16%, rgba(0,0,0,0) 38%);
}
.scrim--stack {
  background: radial-gradient(ellipse 62% 46% at 50% var(--stack-y, 41%),
    rgba(0,0,0,0.50) 0%, rgba(0,0,0,0.28) 45%, rgba(0,0,0,0) 72%);
}
.scrim--foot {
  background: linear-gradient(180deg,
    rgba(0,0,0,0) 72%, rgba(11,10,9,0.55) 90%, var(--ink) 100%);
}

.hero-stack {
  position: absolute;
  left: 50%;
  top: var(--stack-y, 41%);
  transform: translate(-50%, -50%);
  z-index: 2;
  width: min(92vw, 900px);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
/* Sized against BOTH vw and vh - a short laptop screen otherwise starves
   the layout and the lockup pushes the CTA off the fold. */
.hero-lockup {
  height: min(29svh, 32vw, 288px);
  width: auto;
  filter: drop-shadow(0 6px 34px rgba(0, 0, 0, 0.55));
}
.hero-stack .rule { margin-block: clamp(18px, 3.2vh, 36px) clamp(16px, 2.8vh, 30px); }

.hero-tagline {
  font-size: clamp(11px, min(1.15vw, 1.9vh), 15.5px);
  font-weight: 500;
  letter-spacing: 0.26em;
  margin-right: -0.26em;               /* cancel the trailing letter-space */
  text-transform: uppercase;
  color: var(--paper-dim);
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.7);
}
.hero-stack .btn-row { margin-top: clamp(22px, 4vh, 42px); }

/* Scroll drift. The photograph stays exactly where it was composed and the
   lockup rides up over it, which gives the hero depth without cropping the
   light trail the whole frame is built around. `translate`, not `transform`:
   the entrance animation owns transform. */
.hero-stack {
  translate: 0 var(--drift, 0px);
  /* The fade is a filter, not `opacity`. The entrance animation animates
     opacity with `both` fill, so its final value permanently outranks any
     opacity declared here and the scroll fade silently does nothing. */
  filter: opacity(var(--drift-fade, 1));
  will-change: translate, filter;
}

.hero-scroll {
  position: absolute;
  left: 50%; bottom: clamp(18px, 3.4vh, 34px);
  transform: translateX(-50%);
  z-index: 2;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--paper-mute);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.hero-scroll::after {
  content: "";
  width: 1px; height: 26px;
  background: linear-gradient(180deg, var(--amber), transparent);
}

/* ===========================================================================
   SHOWCASE: a full-bleed photographic band
   ===========================================================================
   For the one moment on the site that should be a picture rather than a
   layout. The copy sits in the dark left third of the frame, so the scrim is
   horizontal rather than the vertical one the hero uses.
   =========================================================================== */
.showcase {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  min-height: clamp(400px, 64vh, 640px);
  padding-block: clamp(56px, 8vh, 96px);
}
.showcase-photo,
.showcase-photo img { position: absolute; inset: 0; width: 100%; height: 100%; }
.showcase-photo img { object-fit: cover; object-position: 50% 58%; }

.showcase .scrim {
  background:
    linear-gradient(90deg,
      rgba(9, 8, 7, 0.95) 0%, rgba(9, 8, 7, 0.86) 30%,
      rgba(9, 8, 7, 0.42) 62%, rgba(9, 8, 7, 0.12) 100%),
    linear-gradient(180deg,
      rgba(11, 10, 9, 0.7) 0%, rgba(11, 10, 9, 0) 22%,
      rgba(11, 10, 9, 0) 78%, rgba(11, 10, 9, 0.7) 100%);
}
.showcase-body { position: relative; z-index: 2; max-width: 34rem; }
.showcase-body .rule { margin-bottom: 24px; }
.showcase-body p { margin-top: 18px; }

@media (max-width: 760px) {
  /* One column of readable text beats a wide scrim on a phone: the copy takes
     the whole width and the photo becomes a full backdrop. */
  .showcase-body { max-width: none; }
  .showcase .scrim {
    background: linear-gradient(180deg,
      rgba(9, 8, 7, 0.72) 0%, rgba(9, 8, 7, 0.9) 45%, rgba(9, 8, 7, 0.95) 100%);
  }
}

/* ===========================================================================
   PAGE HEAD (interior pages)
   =========================================================================== */
.page-head {
  padding-top: calc(var(--header-h) + clamp(46px, 7vh, 92px));
  padding-bottom: clamp(40px, 6vh, 76px);
  /* No hairline: the arc on the following section is the divider. */
  background:
    radial-gradient(ellipse 70% 120% at 15% 0%, rgba(242,154,46,0.07), transparent 62%),
    var(--ink);
}
.page-head .lead { max-width: 640px; }

.crumbs {
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-mute);
  margin-bottom: 18px;
}
.crumbs a { color: var(--paper-mute); display: inline-block; padding: 3px 2px; }
.crumbs a:hover { color: var(--amber); }

/* ---------------------------------------------------------------------------
   Page-head meta column.

   Measured, the interior page heads were 46 to 50 percent empty: a left-hand
   text block with a void beside it. Filling the right with a few facts turns
   the void into a composition and repeats the NAP on every page, which local
   search wants anyway. Grid only when the column is actually present, so the
   pages without it stay single-column.
   --------------------------------------------------------------------------- */
.page-head .wrap:has(.page-head-meta) {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(180px, 260px);
  gap: clamp(28px, 4vw, 72px);
  align-items: end;
}
.page-head-meta { border-top: 1px solid var(--line); }
.page-head-meta div {
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.page-head-meta dt {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper-mute);
  margin-bottom: 5px;
}
.page-head-meta dd { margin: 0; font-size: 15px; color: var(--paper-dim); }
/* Standalone links, not inline in a sentence, so they owe the full 24px
   target. An inline box is only as tall as the glyphs, not the line. */
.page-head-meta a { color: var(--paper); display: inline-block; padding-block: 4px; }
.page-head-meta a:hover { color: var(--amber); }

@media (max-width: 860px) {
  .page-head .wrap:has(.page-head-meta) { grid-template-columns: 1fr; }
  .page-head-meta { margin-top: 8px; }
}

/* ---------------------------------------------------------------------------
   Dense strip.

   Every section on the site was between 16 and 50 percent empty, all of it at
   roughly the same rhythm. Rhythm needs a fast bar as well as slow ones: this
   is the one deliberately tight, information-dense band, and it exists so the
   airy sections either side of it read as generous rather than as default.
   --------------------------------------------------------------------------- */
.strip {
  background: var(--ink-0);
  padding-block: clamp(34px, 4.4vh, 56px);
  position: relative;
}
.strip-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: clamp(22px, 2.6vw, 42px);
}
.strip-grid > * + * {
  border-left: 1px solid var(--line);
  padding-left: clamp(22px, 2.6vw, 42px);
}
.strip h3 {
  font-family: Barlow, "Segoe UI", system-ui, sans-serif;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 12px;
}
.strip p { font-size: 15px; color: var(--paper-dim); }
.strip .big { font-size: clamp(17px, 1.5vw, 20px); font-weight: 600; color: var(--paper); letter-spacing: -0.01em; }
.strip a { color: var(--paper); display: inline-block; padding-block: 3px; }
.strip a:hover { color: var(--amber); }
.strip ul {
  columns: 2;
  column-gap: 20px;
  font-size: 14.5px;
  color: var(--paper-dim);
}
.strip ul li { break-inside: avoid; padding-block: 1px; }

@media (max-width: 700px) {
  .strip-grid > * + * { border-left: 0; padding-left: 0; padding-top: 20px; border-top: 1px solid var(--line); }
}

/* ===========================================================================
   CARDS / GRIDS
   =========================================================================== */
.grid { display: grid; gap: clamp(16px, 2vw, 26px); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(272px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* ---------------------------------------------------------------------------
   Cards ride the horizon, not a straight baseline.

   A flat row of equal boxes is the thing that reads as a grid. Rather than
   jogging them up and down at random, the offsets follow the same shallow arc
   as the section dividers: the middle of the row sits highest and the ends
   drop away, so the cards look like they are resting on the horizon line.

   Uses `translate`, NOT `transform`. The cards already spend transform on the
   hover lift and the stagger entrance, and those would clobber each other;
   `translate` is an independent property that composes with both.

   The offsets are balanced around zero (outer down, inner up) so the row's
   centre of mass stays where it was and nothing needs extra padding to avoid
   colliding with the next section.
   --------------------------------------------------------------------------- */
@media (min-width: 1120px) {
  /* Pinned column counts: with auto-fit the column a card lands in depends on
     the width, and an offset keyed to nth-child would curve the wrong ones. */
  .grid--4.grid--offset { grid-template-columns: repeat(4, 1fr); }
  .grid--3.grid--offset,
  .work.grid--offset    { grid-template-columns: repeat(3, 1fr); }

  .grid--offset > * { translate: 0 var(--dy, 0px); }

  /* translate does not affect layout, so the cards that drop below the flat
     baseline eat the section's bottom padding. Hand it back, or on a short
     laptop the lowest card crowds the arc on the next seam. */
  .grid--4.grid--offset { margin-bottom: 22px; }
  .grid--3.grid--offset,
  .work.grid--offset    { margin-bottom: 20px; }

  .grid--4.grid--offset > :nth-child(4n + 1) { --dy: 22px; }
  .grid--4.grid--offset > :nth-child(4n + 2) { --dy: -14px; }
  .grid--4.grid--offset > :nth-child(4n + 3) { --dy: -14px; }
  .grid--4.grid--offset > :nth-child(4n + 4) { --dy: 22px; }

  .grid--3.grid--offset > :nth-child(3n + 1),
  .work.grid--offset    > :nth-child(3n + 1) { --dy: 20px; }
  .grid--3.grid--offset > :nth-child(3n + 2),
  .work.grid--offset    > :nth-child(3n + 2) { --dy: -18px; }
  .grid--3.grid--offset > :nth-child(3n + 3),
  .work.grid--offset    > :nth-child(3n + 3) { --dy: 20px; }
}

.card {
  position: relative;
  background: linear-gradient(160deg, var(--ink-4) 0%, var(--ink-3) 100%);
  border: 1px solid var(--line);
  padding: clamp(26px, 3vw, 38px);
  transition: border-color 0.3s var(--ease), transform 0.3s var(--ease),
              box-shadow 0.3s var(--ease);
}
.card::before {
  content: "";
  position: absolute;
  inset: -1px -1px auto -1px;
  height: 2px;
  background: var(--amber);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--ease);
}
.card:hover, .card:focus-within {
  border-color: var(--line-2);
  transform: translateY(-3px);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.45);
}
.card:hover::before, .card:focus-within::before { transform: scaleX(1); }

/* A light you move across the card, rather than a box that changes colour.
   The pointer position comes from JS as --mx/--my; with no pointer (touch,
   keyboard) the fallback centres it on the card's top edge, so the focus
   state still lights up. Hover-capable devices only: on a phone this would
   sit permanently lit after a tap. */
@media (hover: hover) and (pointer: fine) {
  .card::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 0%),
                rgba(242, 154, 46, 0.15), transparent 62%);
    opacity: 0;
    transition: opacity 0.4s var(--ease);
    pointer-events: none;
  }
  .card:hover::after, .card:focus-within::after { opacity: 1; }
}

/* ---------------------------------------------------------------------------
   Staggered power-on.

   The hero opens with the lights coming on; this carries the same idea into
   the page. Cards energise one after another rather than the whole grid
   arriving at once, which is what made the rows read as a static block.
   --------------------------------------------------------------------------- */
.js .stagger > * {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}
.stagger.is-in > *, html:not(.js) .stagger > * { opacity: 1; transform: none; }
.stagger.is-in > *:nth-child(1) { transition-delay: 0s; }
.stagger.is-in > *:nth-child(2) { transition-delay: 0.09s; }
.stagger.is-in > *:nth-child(3) { transition-delay: 0.18s; }
.stagger.is-in > *:nth-child(4) { transition-delay: 0.27s; }
.stagger.is-in > *:nth-child(5) { transition-delay: 0.36s; }
.stagger.is-in > *:nth-child(6) { transition-delay: 0.45s; }
.stagger.is-in > *:nth-child(7) { transition-delay: 0.54s; }
.stagger.is-in > *:nth-child(8) { transition-delay: 0.63s; }

.card h3 { margin-bottom: 12px; }
.card p { color: var(--paper-dim); font-size: 15.5px; }

.card-icon { color: var(--amber); margin-bottom: 22px; }
.card-icon svg { width: 30px; height: 30px; }

.card-list { margin-top: 18px; display: grid; gap: 9px; }
.card-list li {
  position: relative;
  padding-left: 20px;
  font-size: 14.5px;
  color: var(--paper-mute);
}
.card-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.62em;
  width: 8px; height: 1.5px;
  background: var(--amber);
}

/* A card that is entirely a link: the whole tile is the target. */
.card--link:hover { cursor: pointer; }
/* min-height keeps standalone links at the WCAG 2.2 24px target size - the
   inline-in-a-sentence exception does not cover a link that stands alone at
   the bottom of a card. */
.card-more {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  gap: 8px;
  margin-top: 22px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--amber);
}
/* The dash grows on hover by SCALING, not by changing width. Animating the
   width changes the link's own width, and on the longest label ("Service and
   repair") that was enough to push the text onto a second line the moment you
   pointed at the card. Reserve the full width up front and scale within it. */
/* The dash grows on hover by SCALING past its own width, so the link's layout
   width never changes. Animating `width` pushed the longest label ("Service
   and repair") onto a second line on hover; reserving the grown width up
   front instead just moved the wrap to the resting state. Transform is the
   only version that costs no layout at either end. */
.card-more::after {
  content: "";
  flex: none;
  width: 16px; height: 1.5px;
  background: currentColor;
  transform-origin: left center;
  transition: transform 0.28s var(--ease);
}
.card:hover .card-more::after { transform: scaleX(1.65); }

/* ===========================================================================
   FEATURE BAND (photo + copy)
   =========================================================================== */
.band {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  align-items: center;
  gap: clamp(28px, 4vw, 68px);
}
.band + .band { margin-top: clamp(56px, 7vw, 96px); }
.band--flip .band-media { order: 2; }

.band-media {
  position: relative;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border: 1px solid var(--line);
}
.band-media img { width: 100%; height: 100%; object-fit: cover; }
.band-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0) 45%);
}

.band-body .rule { margin-bottom: 24px; }
.band-body h2 { font-size: clamp(24px, 2.9vw, 34px); }
.band-body p { margin-top: 18px; }

/* ===========================================================================
   EV CALLOUT
   =========================================================================== */
.ev {
  position: relative;
  border: 1px solid var(--amber-dim);
  background:
    radial-gradient(ellipse 80% 140% at 100% 0%, rgba(242,154,46,0.12), transparent 60%),
    var(--ink-2);
  padding: clamp(32px, 4.4vw, 62px);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: clamp(26px, 3.4vw, 54px);
  align-items: center;
}
.ev h2 { font-size: clamp(24px, 2.9vw, 34px); }
.ev .eyebrow { margin-bottom: 14px; }

/* ===========================================================================
   PROCESS
   =========================================================================== */
.steps { counter-reset: step; display: grid; gap: clamp(18px, 2.4vw, 30px); }
.step {
  position: relative;
  padding-left: clamp(56px, 6vw, 80px);
  padding-block: 4px;
}
.step::before {
  counter-increment: step;
  content: "0" counter(step);
  position: absolute;
  left: 0; top: 0;
  font-size: clamp(20px, 2.2vw, 27px);
  font-weight: 700;
  color: var(--amber);
  letter-spacing: -0.02em;
}
.step h3 { margin-bottom: 8px; }
.step p { font-size: 15.5px; }

/* ===========================================================================
   WORK GRID (placeholder tiles until Eric's photos land)
   =========================================================================== */
.work { display: grid; gap: clamp(14px, 1.8vw, 22px); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.work-tile {
  position: relative;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--line);
  background:
    radial-gradient(ellipse 120% 90% at 50% 118%, rgba(242,154,46,0.16), transparent 62%),
    linear-gradient(160deg, var(--ink-4), var(--ink-3));
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: clamp(22px, 2.4vw, 30px);
  overflow: hidden;
  transition: border-color 0.3s var(--ease), transform 0.3s var(--ease);
}
.work-tile:hover { border-color: var(--line-2); transform: translateY(-3px); }
.work-tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.work-tile .work-icon {
  position: absolute;
  top: clamp(22px, 2.4vw, 30px);
  left: clamp(22px, 2.4vw, 30px);
  color: var(--amber);
  opacity: 0.9;
}
.work-tile .work-icon svg { width: 26px; height: 26px; }
.work-tile h3 { font-size: 18px; }
.work-tile p {
  margin-top: 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--paper-mute);
}

/* ===========================================================================
   FAQ
   =========================================================================== */
.faq { max-width: 820px; margin-inline: auto; border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding: clamp(20px, 2.4vw, 28px) 0;
  cursor: pointer;
  color: var(--paper);
  font-size: clamp(16px, 1.35vw, 19px);
  font-weight: 600;
  line-height: 1.35;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  flex: none;
  width: 13px; height: 13px;
  margin-top: 6px;
  border-right: 1.5px solid var(--amber);
  border-bottom: 1.5px solid var(--amber);
  transform: rotate(45deg);
  transition: transform 0.3s var(--ease);
}
.faq details[open] summary::after { transform: rotate(-135deg); margin-top: 10px; }
.faq .answer { padding-bottom: clamp(20px, 2.4vw, 28px); max-width: 68ch; }
.faq .answer p + p { margin-top: 12px; }

/* ===========================================================================
   CONTACT
   =========================================================================== */
.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: clamp(32px, 4.6vw, 76px);
  align-items: start;
}
@media (max-width: 860px) { .contact-grid { grid-template-columns: 1fr; } }

/* Its own breakpoint, not .grid--2: the form sits in the narrower column of
   .contact-grid, so a 300px minimum collapses email/phone back into one
   column on a desktop that has plenty of room for two. */
.field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  column-gap: 22px;
}

.field { display: grid; gap: 8px; margin-bottom: 20px; }
.field label {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--paper-mute);
}
.field .req { color: var(--amber); }

.field input,
.field select,
.field textarea {
  width: 100%;
  font-family: inherit;
  font-size: 16px;                     /* 16px stops iOS zooming on focus */
  color: var(--paper);
  background: var(--ink-2);
  border: 1px solid var(--line);
  padding: 14px 16px;
  transition: border-color 0.22s var(--ease), background-color 0.22s var(--ease);
}
.field textarea { min-height: 148px; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: rgba(255,255,255,0.32); }
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--line-2); }
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--amber);
  background: var(--ink-3);
}
.field .hint { font-size: 13.5px; color: var(--paper-mute); letter-spacing: 0; text-transform: none; font-weight: 400; }
.field .error { font-size: 13.5px; color: #FF9C82; }
.field[data-invalid] input,
.field[data-invalid] select,
.field[data-invalid] textarea { border-color: #FF9C82; }

/* Honeypot: real people never fill this, bots fill everything. */
.hp { position: absolute; left: -9999px; opacity: 0; height: 0; overflow: hidden; }

.form-note {
  margin-top: 18px;
  font-size: 13.5px;
  color: var(--paper-mute);
}
.form-status {
  margin-top: 20px;
  padding: 16px 18px;
  border: 1px solid var(--line-2);
  background: var(--ink-2);
  font-size: 15px;
  color: var(--paper-dim);
}
.form-status:empty { display: none; }
.form-status[data-tone="ok"] { border-color: var(--amber-dim); background: var(--amber-wash); }

.info-block + .info-block { margin-top: 30px; padding-top: 30px; border-top: 1px solid var(--line); }
.info-block h3 { font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--paper-mute); font-weight: 600; margin-bottom: 12px; }
.info-block a:not(.btn) { color: var(--paper); display: inline-block; padding-block: 3px; }
.info-block a:not(.btn):hover { color: var(--amber); }
.info-big { font-size: clamp(20px, 2vw, 25px); font-weight: 600; color: var(--paper); letter-spacing: -0.01em; }

/* ===========================================================================
   PROSE (legal pages)
   =========================================================================== */
.prose { max-width: 68ch; }
.prose h2 {
  font-size: clamp(19px, 1.8vw, 24px);
  margin-top: clamp(38px, 4.4vw, 56px);
  margin-bottom: 14px;
}
.prose h2:first-of-type { margin-top: clamp(30px, 3.4vw, 42px); }
.prose p + p { margin-top: 16px; }
.prose ul.card-list { margin-block: 16px; }
.prose ul.card-list li { font-size: 15.5px; color: var(--paper-dim); }
.prose strong { color: var(--paper); font-weight: 600; }
.prose-meta {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--paper-mute);
  padding-bottom: 26px;
  margin-bottom: 30px;
  border-bottom: 1px solid var(--line);
}

/* ===========================================================================
   CTA BAND
   =========================================================================== */
/* No overflow:hidden here - it would clip the top half of the arc divider,
   which deliberately straddles this section's upper edge. */
.cta-band {
  position: relative;
  /* Measured at 44% empty on full section padding. A band that is only a
     heading, a line and two buttons does not need the same air as a section
     carrying four cards. */
  padding-block: calc(var(--section) * 0.72);
  background:
    radial-gradient(ellipse 60% 130% at 50% 108%, rgba(242,154,46,0.16), transparent 64%),
    var(--ink);
  text-align: center;
}
.cta-band h2 { font-size: clamp(25px, 3.2vw, 38px); }
.cta-band .lead { margin-inline: auto; max-width: 560px; }

/* ===========================================================================
   FOOTER
   =========================================================================== */
.site-footer {
  border-top: 1px solid var(--line);
  background: var(--ink-2);
  padding-block: clamp(46px, 6vw, 72px) 0;
  font-size: 15px;
}
.footer-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(auto-fit, minmax(150px, 1fr));
  gap: clamp(30px, 4vw, 56px);
}
.footer-brand img { height: 62px; width: auto; margin-bottom: 20px; }
.footer-brand p { color: var(--paper-mute); max-width: 34ch; font-size: 14.5px; }

/* Footer column headings are h2 for structure but they are UI labels, not
   headlines, so they stay on the grotesque. */
.site-footer h2 {
  font-family: Barlow, "Segoe UI", system-ui, sans-serif;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--paper-mute);
  margin-bottom: 18px;
}
.site-footer li + li { margin-top: 10px; }
.site-footer a { color: var(--paper-dim); }
.site-footer a:hover { color: var(--amber); }
.site-footer li a { display: inline-block; padding-block: 3px; }

.footer-bottom {
  margin-top: clamp(38px, 5vw, 58px);
  border-top: 1px solid var(--line);
  padding-block: 22px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  color: var(--paper-mute);
}
.footer-bottom nav { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.footer-bottom nav a { display: inline-block; padding: 5px 2px; }
.credit { margin-left: auto; }
.credit a { color: var(--paper-mute); display: inline-block; padding: 5px 2px; }
.credit a:hover { color: var(--amber); }

/* A heading that is entirely a link still has to be a 24px target. */
.card h3 a, .work-tile h3 a { display: inline-block; padding-block: 3px; }

/* ===========================================================================
   RESPONSIVE
   =========================================================================== */
@media (max-width: 980px) {
  .nav, .header-cta { display: none; }
  .burger { display: block; }
  .header-inner { grid-template-columns: auto 1fr; }
  .header-inner .brand { grid-column: 1; }
  .burger { grid-column: 2; }
}

@media (max-width: 900px) and (orientation: portrait) {
  :root { --stack-y: 38%; }
  .hero-lockup { height: min(26svh, 50vw, 250px); }
  .hero-tagline { letter-spacing: 0.2em; margin-right: -0.2em; }
  .scrim--stack {
    background: radial-gradient(ellipse 80% 34% at 50% var(--stack-y),
      rgba(0,0,0,0.46) 0%, rgba(0,0,0,0.24) 48%, rgba(0,0,0,0) 74%);
  }
}

@media (orientation: landscape) and (max-height: 560px) {
  :root { --stack-y: 46%; }
  .hero-lockup { height: min(36svh, 21vw, 180px); }
  .hero-scroll { display: none; }
}

@media (max-width: 640px) {
  .band--flip .band-media { order: 0; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
  .footer-bottom { justify-content: flex-start; }
  .credit { margin-left: 0; width: 100%; }
}

/* ===========================================================================
   MOTION
   =========================================================================== */
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes lift {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 16px)); }
  to   { opacity: 1; transform: translate(-50%, -50%); }
}

.home .site-header { animation: rise 0.9s 0.15s var(--ease) both; }
.hero-stack { animation: lift 1.1s 0.30s var(--ease) both; }
.hero-scroll { animation: rise 0.8s 1.1s var(--ease) both; }

/* Sections power on as they come into view and stay on - a breaker does not
   flip itself back off. JS adds .is-in; without JS everything is visible. */
.reveal { opacity: 0; transform: translateY(18px); }
.js .reveal { transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }
.reveal.is-in, html:not(.js) .reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    transition-duration: 0.001ms !important;
  }
  .reveal, .stagger > * { opacity: 1; transform: none; }
  .section.is-lit::before { animation: none; }
}

/* ===========================================================================
   FILM GRAIN
   ===========================================================================
   A near-black page covered in soft radial glows bands badly on an 8-bit
   panel: you can count the steps in every gradient. A faint noise tile over
   the whole thing dithers those steps away, and it gives the screen a little
   of the tactility the printed card gets from its stock. Kept low enough that
   you notice its absence rather than its presence.
   =========================================================================== */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  background-image: url("/assets/img/grain.png");
  background-size: 128px 128px;
  opacity: 0.32;
}

/* ===========================================================================
   PAGE TRANSITIONS
   ===========================================================================
   Cross-document view transitions: a multi-page static site gets a crossfade
   between pages instead of a white flash, from one at-rule. Naming the header
   and footer keeps them planted while the content changes underneath, which
   is what makes it read as one app rather than a slideshow. Browsers without
   support simply navigate as before.
   =========================================================================== */
@view-transition { navigation: auto; }

.site-header { view-transition-name: site-header; }
.site-footer { view-transition-name: site-footer; }

::view-transition-old(root),
::view-transition-new(root) { animation-duration: 0.34s; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

@media print {
  .site-header, .drawer, .burger, .hero-scroll, .cta-band { display: none; }
  body { background: #fff; color: #000; }
}
