/* ===========================================================================
   Warrick Brothers Tree Service
   Built only from DESIGN.md. If this file and DESIGN.md disagree, this file is
   wrong. No framework, no external request, and no untinted neutral at
   either end of the lightness scale.
   =========================================================================== */

/* fonts.css is linked separately in <head> so it never blocks behind an @import */

/* ------------------------------------------------------------------ tokens */
:root {
  /* ground: cool overcast paper, tinted to the brand green's own hue 174 */
  --ground:        oklch(0.978 0.005 174);
  --ground-2:      oklch(0.958 0.006 174);
  --ground-3:      oklch(0.932 0.008 174);
  --rule:          oklch(0.885 0.010 174);
  --rule-strong:   oklch(0.800 0.012 174);
  --field:         oklch(0.615 0.018 174);
  --rule-green:    oklch(0.750 0.060 174);   /* faint green tint. Card hover border only */

  /* ink built from the deep green, never from an untinted neutral */
  --ink:           oklch(0.215 0.035 174);   /* 16.27:1 on ground */
  --body:          oklch(0.330 0.030 174);   /* 11.28:1 */
  --muted:         oklch(0.455 0.025 174);   /*  6.79:1 */

  /* brand */
  --green:         oklch(0.318 0.060 174.2); /* #003c30 exact */
  --green-deep:    oklch(0.255 0.052 174.2);
  --orange:        oklch(0.738 0.162 63.4);  /* #f09018 exact. NEVER under text */
  --orange-ink:    oklch(0.520 0.140 58);    /*  5.41:1 on ground. Orange as text */

  --on-green:      oklch(0.965 0.010 174);   /* 11.29:1 on green */
  --on-green-dim:  oklch(0.840 0.022 174);   /*  7.65:1 */
  --on-green-warm: oklch(0.800 0.140 66);    /*  6.47:1 */
  /* The third per ground orange, and it exists for the same reason the other
     two do. A scrimmed VIDEO frame is a much lighter ground than the flat
     --green band: composited over the brightest pixel of the poster and all
     nineteen sampled video frames, --on-green-warm measures 3.34:1 and cannot
     reach AA at any scrim alpha that still lets the clip read. This one
     measures 4.71:1 there and 5.05:1 over the 99th percentile of the region
     the copy actually sits on. Hero only. See DESIGN.md section 4. */
  --on-video-warm: oklch(0.935 0.130 66);    /*  4.71:1 worst case on the scrim */
  /* The measured hero scrim. --green at 0.78 over an opaque --green-deep bed,
     so a browser that paints no video still gets the brand ground. */
  --scrim: 0.78;

  /* type */
  --f-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --f-body: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, sans-serif;
  --f-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --t-base:      clamp(1rem, 0.9583rem + 0.1852vw, 1.125rem);
  --t-lead:      clamp(1.25rem, 1.1875rem + 0.2778vw, 1.4375rem);
  --t-h3:        clamp(1.5625rem, 1.4792rem + 0.3704vw, 1.8125rem);
  --t-h2:        clamp(2rem, 1.75rem + 1.1111vw, 2.75rem);
  --t-h1:        clamp(2.5rem, 1.9583rem + 2.4074vw, 4.125rem);
  --t-display:   clamp(3.25rem, 2.5rem + 3.3333vw, 5.5rem);
  /* --t-hero is NOT a new step in the modular scale. It is a ramp BETWEEN two
     existing steps, used by exactly one element, the home page H1: it resolves
     to --t-h1's own minimum (40px) at 360 and to --t-display's own maximum
     (88px) at 1440 and above. The home headline has to be the biggest thing on
     its page (it was 66px against an 88px score) without setting a 60 character
     sentence at 52px on a phone, which would push the proof line below the
     fold. Adjacent ratio arithmetic is unaffected because no other element
     uses it. */
  --t-hero:      clamp(2.5rem, 1.2rem + 5.2vw, 5.5rem);
  --t-mono:      clamp(0.8125rem, 0.7917rem + 0.0926vw, 0.875rem);
  --t-mono-fine: clamp(0.6875rem, 0.6667rem + 0.0926vw, 0.75rem);
  --t-fine:      0.875rem;

  /* space, 4px base */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 40px; --s-8: 48px;
  --s-9: 64px; --s-10: 80px; --s-11: 96px; --s-12: 128px;
  --pad-sec: clamp(56px, 8vw, 128px);
  /* Spacing is deliberately uneven, and these are the two exceptions that make
     it so. A section that opens directly under a photograph takes --pad-photo
     on top, and the closing band takes --pad-close on both, because it was the
     tallest and emptiest thing on the page. */
  --pad-photo: clamp(32px, 4vw, 64px);
  --pad-close: clamp(48px, 6vw, 88px);

  --w: 1200px;
  --w-wide: 1360px;
  --w-prose: 68ch;
  --gutter: clamp(20px, 5vw, 40px);

  --r: 6px;
  --shadow: 0 1px 0 var(--rule), 0 2px 4px oklch(0.318 0.06 174 / 0.06);
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  --hd-h: 68px;
  color-scheme: light;
}

/* ------------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--ground);
  color: var(--body);
  font-family: var(--f-body);
  font-size: var(--t-base);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}
img, svg, picture, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: var(--green); text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
a:hover { color: var(--green-deep); }

:where(a, button, summary, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 90;
  background: var(--green); color: var(--on-green);
  padding: var(--s-3) var(--s-5); border-radius: 0 0 var(--r) 0;
}
.skip:focus { left: 0; }

/* ------------------------------------------------------------- typesetting */
h1, h2, h3 {
  font-family: var(--f-display);
  color: var(--ink);
  font-weight: 600;
  line-height: 1.08;
  font-variation-settings: "opsz" 36, "SOFT" 50, "WONK" 0;
  text-wrap: balance;
}
h1 {
  font-size: var(--t-h1);
  font-weight: 650;
  line-height: 1.02;
  letter-spacing: -0.02em;
  font-variation-settings: "opsz" 96, "SOFT" 50, "WONK" 0;
}
h2 { font-size: var(--t-h2); letter-spacing: -0.015em; }
h3 { font-size: var(--t-h3); letter-spacing: -0.01em; line-height: 1.15; }

.mono, .eyebrow {
  font-family: var(--f-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--muted);
  line-height: 1.4;
}
.eyebrow { display: block; margin-bottom: var(--s-4); }
/* The 3px rule is one of the four places raw --orange is legal: it carries no
   text and no text sits on it. It replaces a hairline that read as nothing. */
.eyebrow::after {
  content: ""; display: block; width: 28px; height: 3px;
  background: var(--orange); margin-top: var(--s-3);
}
.lead { font-size: var(--t-lead); line-height: 1.5; color: var(--body); max-width: 46ch; }
/* .prose always rides on .wrap, whose 1200px max-width wins on the same element,
   so the measure is applied to the paragraphs themselves, left aligned inside the
   container rather than centring a narrow block. */
.prose > p { max-width: var(--w-prose); }
.prose p + p { margin-top: var(--s-5); }
.fine { font-size: var(--t-fine); color: var(--muted); }

.hl { color: var(--orange-ink); }   /* the highlighted phrase. 5.41:1, measured */

/* -------------------------------------------------------------- containers */
.wrap { width: 100%; max-width: var(--w); margin-inline: auto; padding-inline: var(--gutter); }
.wrap--wide { max-width: var(--w-wide); }
.sec { padding-block: var(--pad-sec); }
.sec--tight { padding-block: calc(var(--pad-sec) * 0.66); }
.sec--after-photo { padding-top: var(--pad-photo); }
.sec--close { padding-block: var(--pad-close); }
.sec--band { background: var(--ground-2); }
.rule-top { border-top: 1px solid var(--rule); }

/* ------------------------------------------------------------------ header */
.hd {
  position: sticky; top: 0; z-index: 60;
  background: var(--ground);
  border-bottom: 1px solid transparent;
  transition: border-color 200ms linear;
}
.hd--scrolled { border-bottom-color: var(--rule); }
.hd__in {
  display: flex; align-items: center; gap: var(--s-5);
  min-height: var(--hd-h);
  padding-block: var(--s-2);
}
.brand { display: flex; align-items: center; gap: var(--s-3); text-decoration: none; margin-right: auto; }
.brand img { width: 40px; height: auto; }
.brand__name {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.0625rem;
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--ink);
  font-variation-settings: "opsz" 20, "SOFT" 50, "WONK" 0;
  max-width: 8.5em;
}
.burger {
  display: inline-flex; align-items: center; gap: var(--s-2);
  background: transparent; border: 1px solid var(--rule-strong); border-radius: var(--r);
  color: var(--ink); padding: 9px 12px; cursor: pointer;
  font-family: var(--f-mono); font-size: var(--t-mono-fine);
  text-transform: uppercase; letter-spacing: 0.09em;
}
.burger svg { width: 16px; height: 16px; }

.nav { display: none; }
.hd__acts { display: flex; align-items: center; gap: var(--s-4); }
.hd__tel { display: none; }
/* Below 700px the header CTA is dropped rather than wrapped. The same action
   sits in the drawer, in the hero, and in the dock, so nothing is lost and the
   brand gets the room it needs to set on two lines instead of three. */
@media (max-width: 699.98px) {
  .hd__acts > .btn { display: none; }
  .brand__name { max-width: 9.5em; }
  .brand img { width: 36px; }
}

@media (min-width: 1040px) {
  .nav { display: flex; align-items: center; gap: var(--s-5); }
  .nav a {
    font-size: 0.9375rem; color: var(--body); text-decoration: none;
    padding-block: var(--s-2); border-bottom: 2px solid transparent;
  }
  .nav a:hover { color: var(--ink); border-bottom-color: var(--rule-strong); }
  .nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--orange); }
  .hd__tel { display: inline; font-size: 0.9375rem; color: var(--body); text-decoration: none; white-space: nowrap; }
  .hd__tel:hover { color: var(--ink); text-decoration: underline; }
  .hd .burger { display: none; }
}

/* ------------------------------------------------------------------ button */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  background: var(--green); color: var(--on-green);
  font-weight: 600; font-size: 0.9375rem; line-height: 1;
  padding: 14px var(--s-5);
  border: 1px solid var(--green); border-radius: var(--r);
  text-decoration: none; cursor: pointer;
  transition: background-color 160ms linear, border-color 160ms linear, color 160ms linear;
}
.btn:hover { background: var(--green-deep); border-color: var(--green-deep); color: var(--on-green); }
.btn--ghost { background: transparent; color: var(--green); }
.btn--ghost:hover { background: var(--ground-3); color: var(--green-deep); }
.btn--light { background: var(--ground); color: var(--green); border-color: var(--ground); }
.btn--light:hover { background: var(--ground-2); color: var(--green-deep); border-color: var(--ground-2); }
.btn--sm { padding: 10px var(--s-4); font-size: 0.875rem; }
.btn[disabled] { opacity: 0.55; cursor: progress; }


/* ------------------------------------------------------------------ drawer */
.drawer {
  position: fixed; inset: 0; z-index: 80;
  visibility: hidden;
}
.drawer[data-open="true"] { visibility: visible; }
.drawer__scrim {
  position: absolute; inset: 0;
  background: oklch(0.215 0.035 174 / 0.4);
  opacity: 0; transition: opacity 240ms linear;
}
.drawer[data-open="true"] .drawer__scrim { opacity: 1; }
.drawer__panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(360px, 88vw);
  background: var(--ground);
  border-left: 1px solid var(--rule);
  padding: var(--s-4) var(--s-5) calc(var(--s-8) + env(safe-area-inset-bottom));
  padding-right: calc(var(--s-5) + env(safe-area-inset-right));
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform 280ms var(--ease);
  display: flex; flex-direction: column; gap: var(--s-5);
}
.drawer[data-open="true"] .drawer__panel { transform: translateX(0); }
.drawer__top { display: flex; justify-content: flex-end; }
.drawer__nav { display: flex; flex-direction: column; }
.drawer__nav a {
  font-family: var(--f-display); font-weight: 600; font-size: 1.375rem;
  color: var(--ink); text-decoration: none;
  padding-block: var(--s-3); border-bottom: 1px solid var(--rule);
  font-variation-settings: "opsz" 24, "SOFT" 50, "WONK" 0;
}
.drawer__nav a[aria-current="page"] { color: var(--green); }
.drawer__acts { display: flex; flex-direction: column; gap: var(--s-3); }
body.is-locked { overflow: hidden; }

/* -------------------------------------------------------------------- hero */
.hero { padding-top: clamp(32px, 5vw, 64px); padding-bottom: clamp(40px, 6vw, 88px); }

.hero__grid { display: grid; gap: clamp(36px, 5vw, 64px); align-items: start; }
@media (min-width: 900px) {
  .hero__grid { grid-template-columns: 7fr 5fr; gap: clamp(40px, 5vw, 72px); }
}
/* The heading owns its own measure. A `ch` max-width on a wrapping div would
   resolve against the body font size, not the display font inside it, which is
   how a 66px headline once got clamped to about 170px and broke over six
   lines. */
.hero h1 { margin-bottom: var(--s-5); max-width: 16ch; }
.hero__head { max-width: 100%; }
.hero .lead { max-width: 52ch; }
.hero__acts { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-7); }

.facts {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-3) var(--s-4);
  margin-top: var(--s-7);
  padding-top: var(--s-5);
  border-top: 1px solid var(--rule);
}
.facts li {
  display: flex; align-items: center; gap: var(--s-4);
  font-family: var(--f-mono); font-size: var(--t-mono-fine);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted);
}
.facts li:not(:last-child)::after,
.bros-truck__facts li:not(:last-child)::after {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--orange);  /* the sun. purely decorative separator */
  flex: none;
}
@media (max-width: 699.98px) {
  .bros-truck__facts li:not(:last-child)::after { display: none; }
  .bros-truck__facts li:not(:first-child)::before {
    content: ""; width: 6px; height: 6px; border-radius: 50%;
    background: var(--orange); flex: none; margin-right: var(--s-2);
  }
}
@media (max-width: 719.98px) {
  .facts { display: grid; gap: var(--s-3); }
  .facts li::after { display: none; }
  .facts li::before {
    content: ""; width: 6px; height: 6px; border-radius: 50%;
    background: var(--orange); flex: none;
  }
  .facts li { gap: var(--s-3); align-items: baseline; }
  .facts li::before { align-self: center; }
}

/* ------------------------------------------------------------ video hero */
/* The one full bleed video, and the one frame here that is not the client's
   own: PRODUCT.md's no stock rule is waived for it alone. THE SCRIM IS
   MEASURED, not picked: at 0.78 --on-green is 6.25:1 behind the copy and
   5.82:1 over the brightest pixel in the clip. The bed is OPAQUE
   --green-deep. DESIGN.md section 4 carries the working. */
.vhero {
  position: relative;
  min-height: min(88svh, 860px);
  display: grid;
  align-items: center;
  padding-block: clamp(48px, 7vw, 96px);
  background: var(--green-deep);
  overflow: hidden;
  isolation: isolate;
}
.vhero__media { position: absolute; inset: 0; z-index: 0; }
.vhero__poster, .vhero__vid {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
}
.vhero__poster img, .vhero__vid { object-fit: cover; }
.vhero__poster img { width: 100%; height: 100%; }
/* The video fades in only once it is genuinely playing, so a slow connection
   shows the poster rather than an empty dark box. site.js adds .is-playing. */
.vhero__vid { opacity: 0; transition: opacity 600ms linear; }
.vhero__vid.is-playing { opacity: 1; }
.vhero__scrim { position: absolute; inset: 0; background: oklch(0.318 0.060 174.2 / 0.78); }

/* The 60ch belongs on the PROSE, not the wrapper: a ch on a wrapping div
   resolves against the BODY font, so min(60ch,--w) was 564px at 1440 and broke
   an 88px headline over four lines, pushing the button off a 900px screen. The
   heading owns its measure in em. DESIGN.md records the same trap. */
.vhero__in {
  position: relative; z-index: 1;
  max-width: var(--w);
  text-align: center;
  display: grid;
  justify-items: center;
}
.vhero h1 {
  font-size: var(--t-hero);
  color: var(--on-green);
  max-width: 11em;
  letter-spacing: -0.025em;
  margin: 0;
}
.hl-v { color: var(--on-video-warm); }   /* 4.71:1 worst case, measured */

/* An outline chip, not a glass panel. A ground fill LIGHTENS the scrim under
   it: at 0.12 --on-green drops to 4.43:1, under AA. So the fill stays at 0.06
   (5.07:1) and the border makes it read as a chip. */
.vhero__chip {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--s-2) var(--s-3);
  margin-bottom: var(--s-5);
  padding: 7px 14px;
  background: oklch(0.978 0.005 174 / 0.06);
  border: 1px solid oklch(0.978 0.005 174 / 0.40);
  border-radius: var(--r);
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; line-height: 1.4;
  color: var(--on-green);
}
.vhero__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--orange); flex: none;   /* the sun, decorative separator */
}

.vhero__lead {
  margin-top: var(--s-5);
  font-size: var(--t-lead); line-height: 1.5;
  color: var(--on-green); max-width: 60ch;
}
/* Under 700px the lead is its opening sentence only, so the button stays on a
   phone's first screen. Both ship and one is display:none, which keeps it out
   of the accessibility tree too. No JS, so no reflow. */
.vhero__lead[data-short="true"] { display: none; }
@media (max-width: 699.98px) {
  .vhero__lead[data-short="true"] { display: block; }
  .vhero__lead[data-short="false"] { display: none; }
}

.vhero__ticks {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--s-3) var(--s-6);
  margin-top: var(--s-6);
}
.vhero__ticks li {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: 0.9375rem; line-height: 1.4; color: var(--on-green);
}
.vhero__tick { display: flex; flex: none; color: var(--on-video-warm); }
.vhero__tick svg { width: 17px; height: 17px; }
@media (max-width: 699.98px) {
  .vhero__ticks { display: grid; justify-items: start; gap: var(--s-3); text-align: left; }
}

.vhero__acts {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--s-4) var(--s-6);
  margin-top: var(--s-7);
}
.vhero__tel { color: var(--on-green); font-size: 0.9375rem; }
.vhero__tel:hover { color: var(--on-video-warm); }
.vhero__serving {
  margin-top: var(--s-7);
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; line-height: 1.6;
  color: var(--on-green); max-width: 64ch;
}
.nb { white-space: nowrap; }

/* The clip never plays under reduced motion, and site.js never gives it a src
   there, so this is belt and braces rather than the mechanism. */
@media (prefers-reduced-motion: reduce) {
  .vhero__vid { display: none; }
}

/* --------------------------------------------- the side yard, one step down */
/* It used to be the hero. It is now the section under it, and it keeps the
   full bleed band, the two camera timestamps, the strip and the caption. */
.sec--pair { padding-top: clamp(40px, 5vw, 72px); padding-bottom: clamp(40px, 6vw, 88px); }
.pair-sec__lab { margin-bottom: var(--s-3); }
.pair-sec__meta { margin-top: var(--s-5); }

/* -------------------------------------------------------- the before/after */
/* Full bleed by structure, not by 100vw. .heroband is a direct child of the
   hero section rather than a child of .wrap, so it spans the section's own
   width and can never overflow by the width of a scrollbar the way
   width:100vw with a negative inline margin does. */
.heroband { position: relative; }

.wipe {
  /* 68 percent, not 50. At 50 the grip sits exactly where the change is: the
     left half shows the BEFORE's blank siding and the right half shows the
     AFTER's clear passage, and the two register well enough that they read as
     one continuous photograph with nothing to compare. At 68 most of the frame
     is the BEFORE, the overgrown passage is visible, and the sliver of AFTER on
     the right is the thing that invites the drag. */
  --pos: 68%;
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--ground-3);
  touch-action: pan-y;
  cursor: ew-resize;
  user-select: none;
  -webkit-user-select: none;
}
.wipe img { width: 100%; height: 100%; object-fit: cover; }
/* Declared AFTER .wipe on purpose. Both are single class selectors, so source
   order is what decides, and when this block sat above .wipe the base
   aspect-ratio 4/5 won: the band computed 612px tall and 489px WIDE on a
   1440px screen instead of running edge to edge. */
.wipe--band { aspect-ratio: 4 / 5; }
@media (min-width: 900px) {
  .wipe--band { aspect-ratio: auto; height: min(68vh, 660px); }
  /* Biased down the frame: the change lives on the ground, and a centred
     crop of a 16:9 band inside a 2.35:1 box spends its height on siding. */
  .wipe--band img { object-position: 50% 56%; }
}
.wipe__a, .wipe__b { position: absolute; inset: 0; }
.wipe__b { clip-path: inset(0 0 0 var(--pos)); }
.wipe__tag {
  position: absolute; top: 0; z-index: 3;
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  letter-spacing: 0.1em;
  background: var(--ground); color: var(--ink);
  padding: 6px 10px;
}
.wipe__tag--a { left: 0; }
.wipe__tag--b { right: 0; }
.wipe__grip {
  position: absolute; top: 0; bottom: 0; left: var(--pos);
  width: 44px; margin-left: -22px; z-index: 4;
  background: transparent; border: 0; padding: 0;
  cursor: ew-resize;
  display: grid; place-items: center;
}
.wipe__grip::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 21px;
  width: 2px; background: var(--ground);
}
/* THE one filled orange element per viewport that the constitution allows, and
   it carries an --ink glyph at 7.16:1. Site wide, real brand orange was 0.67
   percent of the first screen and almost all of that was the rust headline
   phrase, which is --orange-ink and not the brand colour at all. */
.wipe__knob {
  position: relative;
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--orange); border: 2px solid var(--ink);
  display: grid; place-items: center;
  box-shadow: 0 2px 6px oklch(0.215 0.035 174 / 0.28);
}
.wipe__knob svg { width: 20px; height: 20px; color: var(--ink); }
.wipe__grip:focus-visible .wipe__knob { outline: 3px solid var(--ink); outline-offset: 3px; }
.wipe__cap {
  margin-top: var(--s-3);
  font-size: var(--t-fine); color: var(--muted); max-width: 46ch;
}

/* --------------------------------------------------- the assessment strip */
/* ONE LINE. It is a strip, not a table.

   The first build gave every cell a fill, a 1px gap over a --rule ground and
   flex: 1 1 150px, so five known keys wrapped into three ragged rows and a lone
   DATE key stretched the full width with four words in it. It read as a
   spreadsheet with a broken last row, which is the exact opposite of "a record,
   not a caption".

   flex: 0 1 auto is the load bearing declaration: an item never stretches to
   fill a row, so one leftover key sits at its own width instead of spanning.
   The strip is still allowed to wrap to a second line, but it can never become
   a grid. */
.strip {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--s-3) var(--s-5);
  margin-top: var(--s-5);
  padding-block: var(--s-3);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.strip > div {
  flex: 0 1 auto; min-width: 0;
  display: flex; align-items: baseline; gap: var(--s-2);
}
/* The sun dot again, as the separator between items. Decorative, aria hidden by
   virtue of being generated content, and the dl structure carries the meaning. */
.strip > div + div::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--orange); flex: none; align-self: center;
  margin-right: var(--s-2);
}
.strip dt {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted);
  flex: none;
}
/* The value is mono too. It used to be the body face, so the spec voice was
   doing only half its job: the label read as a fact and the fact read as copy. */
.strip dd {
  font-family: var(--f-mono); font-size: var(--t-mono); font-weight: 500;
  letter-spacing: 0.01em; line-height: 1.4; color: var(--ink);
}

/* ------------------------------------------------------------- proof band */
/* The proof band is the one section on the page set in the WIDE container, and
   the score sits hard against its left edge. It used to leave the right quarter
   of a 1200px container empty; the Google link now closes that end of the row. */
.proof__head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4) clamp(24px, 4vw, 56px);
}
.proof__lead { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-4) clamp(24px, 4vw, 56px); }
.proof__see { flex: none; }
.proof__score {
  font-family: var(--f-display); font-size: var(--t-display); font-weight: 650;
  line-height: 0.9; letter-spacing: -0.03em; color: var(--ink);
  font-variation-settings: "opsz" 120, "SOFT" 50, "WONK" 0;
}
.proof__from { font-size: var(--t-lead); color: var(--body); max-width: 42ch; margin-top: var(--s-2); }
.stars { display: inline-flex; gap: 3px; }
.stars svg { width: 16px; height: 16px; color: var(--orange-ink); }  /* 5.41:1 */


.quotes { display: grid; gap: var(--s-5); margin-top: var(--s-9); }
@media (min-width: 980px) {
  .quotes { grid-template-columns: 1.32fr 1fr 0.92fr; gap: var(--s-5); align-items: start; }
}
.quote {
  background: var(--ground); border: 1px solid var(--rule); border-radius: var(--r);
  padding: var(--s-5);
}
.sec--band .quote { background: var(--ground); }
.quote__who { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-bottom: var(--s-3); }
.quote__name { font-family: var(--f-display); font-weight: 600; font-size: 1.0625rem; color: var(--ink); }
.quote__when { font-family: var(--f-mono); font-size: var(--t-mono-fine); text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
.quote p { font-size: 0.9375rem; line-height: 1.6; color: var(--body); margin-top: var(--s-3); }

.masonry { columns: 1; column-gap: var(--s-5); margin-top: var(--s-8); }
@media (min-width: 860px) { .masonry { columns: 2; } }
.masonry .quote { break-inside: avoid; margin-bottom: var(--s-5); }

/* ---------------------------------------------------- the credential plate */
.plate { display: grid; gap: clamp(28px, 4vw, 56px); align-items: start; }
@media (min-width: 900px) { .plate { grid-template-columns: 7fr 5fr; } }
.plate__card {
  border: 1px solid var(--rule-strong);
  border-radius: var(--r);
  background: var(--ground);
  padding: clamp(24px, 3.5vw, 44px);
}
.sec--band .plate__card { background: var(--ground); }
.plate__top {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.11em; color: var(--muted);
  padding-bottom: var(--s-3); border-bottom: 1px solid var(--rule);
  margin-bottom: var(--s-5);
}
.plate__name {
  font-family: var(--f-display); font-weight: 650; font-size: var(--t-h2);
  letter-spacing: -0.015em; line-height: 1.05; color: var(--ink);
  font-variation-settings: "opsz" 48, "SOFT" 50, "WONK" 0;
}
.plate__cred { margin-top: var(--s-3); font-size: var(--t-lead); color: var(--body); max-width: 30ch; }
.plate__rows { margin-top: var(--s-6); border-top: 1px solid var(--rule); }
.plate__row { display: grid; gap: 2px; padding-block: var(--s-4); border-bottom: 1px solid var(--rule); }
@media (min-width: 620px) { .plate__row { grid-template-columns: 12rem 1fr; gap: var(--s-4); align-items: baseline; } }
.plate__k {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted);
}
.plate__v { color: var(--ink); }
.plate__v a { font-size: 0.9375rem; }
.plate__body { margin-top: var(--s-6); max-width: 62ch; }
.plate__media figcaption { margin-top: var(--s-3); }

/* ----------------------------------------------------------- service cards */
/* A centred heading, a one line subline, then a grid of equal cards. Ten
   services, five columns by two rows at 1100px and above, two columns below
   it, one column below 600px. Grid stretch (the default, nothing here sets a
   height on any card) is what makes every card in a row match the tallest
   one, and the link sits after an auto top margin so every link in a row
   shares one baseline regardless of how long its own description ran. */
.svc-head { text-align: center; }
.svc-head .lead { margin-inline: auto; }
/* Flex rather than grid so an incomplete last row centres itself: ten cards
   at four across leaves two, and two left aligned orphans read unfinished. */
.svc-cards {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-5);
}
.svc-cards .svc-card { flex: 0 0 100%; }
@media (min-width: 600px) {
  .svc-cards .svc-card { flex-basis: calc((100% - var(--s-5)) / 2); }
}
@media (min-width: 1100px) {
  .svc-cards .svc-card { flex-basis: calc((100% - 3 * var(--s-5)) / 4); }
}
.svc-card {
  display: flex;
  flex-direction: column;
  /* Without this a grid item's implicit min-width:auto lets a long title or
     link (Tree risk assessment) push its own column past the others, since
     the 1fr tracks otherwise size to the widest content's minimum first. */
  min-width: 0;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  padding: var(--s-6);
  text-decoration: none;
  transition: border-color 200ms var(--ease);
}
/* The only hover signal: the border tints toward brand green. No background
   fill and no lift, both of which are the "identical card grid" texture the
   brief bans elsewhere and would look worse repeated ten times. */
.svc-card:hover, .svc-card:focus-visible { border-color: var(--rule-green); }
/* font-size fixed here rather than left to the tag: the hub uses h2 for this
   title (so the outline never skips h1 to h3) and the home page uses h3 (it
   already sits under that section's own h2), and the two must read as the
   same card regardless of which level the outline needs. */
.svc-card__name {
  color: var(--green);
  font-size: var(--t-h3);
  line-height: 1.15;
  letter-spacing: -0.01em;
}
/* Measured, not assumed: at the five column layout a card's content width is
   ~139px, and two of the ten short names ("Structural", "assessment") measure
   143px and 159px at the full --t-h3 size, so the browser force breaks the
   word itself mid letter with no hyphen. 22px keeps both comfortably inside
   the column (108px and 121px measured) without touching --t-h3 anywhere a
   card has the room to use it. */
@media (min-width: 1100px) {
  .svc-card__name { font-size: 1.5rem; }
}
.svc-card__desc { margin-top: var(--s-3); color: var(--body); }
.svc-card__link {
  margin-top: auto;
  padding-top: var(--s-5);
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--f-body);
  font-weight: 600;
  color: var(--green);
}
.svc-card__link svg { width: 16px; height: 16px; flex: 0 0 auto; transition: transform 200ms var(--ease); }
.svc-card:hover .svc-card__link svg, .svc-card:focus-visible .svc-card__link svg { transform: translateX(3px); }

/* --------------------------------------------------------- project records */
/* One geometry for every record, single photo or a before and after pair: the
   photo column is exactly half the container. The side still alternates
   (.rec--flip), the ratio never does, so no record photo is ever a different
   width than another. */
.rec { display: grid; gap: clamp(24px, 3.5vw, 48px); align-items: start; }
@media (min-width: 900px) {
  .rec { grid-template-columns: 6fr 6fr; }
  .rec--flip .rec__media { order: -1; }
}
.rec + .rec { margin-top: var(--s-10); padding-top: var(--s-9); border-top: 1px solid var(--rule); }
.rec__city {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted);
  display: block; margin-bottom: var(--s-3);
}
.rec__body { margin-top: var(--s-4); max-width: 56ch; }
.rec__note {
  margin-top: var(--s-4); padding-left: var(--s-4);
  border-left: 1px solid var(--rule-strong);   /* 1px only. Not a side stripe accent */
  font-size: var(--t-fine); color: var(--muted); max-width: 54ch;
}
/* No media means no split. An empty right column is a leftover, not a
   decision. */
.rec--plain { grid-template-columns: 1fr; }
.rec--plain .rec__body { max-width: var(--w-prose); }
/* Every record photograph, single or paired, is 4 by 3, object fit cover,
   centred on the subject, so they all render at the same width and height. A
   before and after pair stacks full width in this column, never two small
   frames side by side, each keeping its mono chip in the top left corner
   (.pair__tag, unchanged below). */
.rec__media .shot,
.rec__media .pair figure {
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.rec__media .shot picture, .rec__media .shot img,
.rec__media .pair picture, .rec__media .pair img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.rec__media .pair { grid-template-columns: 1fr; gap: var(--s-3); }

/* ------------------------------------------------------------- still pairs */
.pair { display: grid; gap: var(--s-3); }
@media (min-width: 560px) { .pair { grid-template-columns: 1fr 1fr; } }
/* Full bleed by structure: .pair--bleed is rendered OUTSIDE .wrap, so it is as
   wide as its section and cannot overflow by a scrollbar's width. */
.pair--bleed { gap: 2px; }
.pair--bleed img { width: 100%; }
.pair figure { margin: 0; position: relative; }
.pair__tag {
  position: absolute; top: 0; left: 0;
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  letter-spacing: 0.1em; text-transform: uppercase;
  background: var(--ground); color: var(--ink); padding: 5px 9px;
}

/* -------------------------------------------------------------- figure/cap */
figure { margin: 0; }
figcaption {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted);
  margin-top: var(--s-3); line-height: 1.5;
}
.shot img { width: 100%; }
.shot--wide img { aspect-ratio: 21 / 9; height: auto; object-fit: cover; object-position: 50% 42%; }

/* -------------------------------------------------- the about chip truck */
/* The only surviving user of .bros-sec is the chip truck block on /about. Both
   users used to carry .bros-sec--contained, so the viewport inset math, the
   --contained overrides that cancelled it and the figcaption padding (nothing
   in this grid carries a caption any more) were all dead and are gone. */
.bros-sec { display: grid; gap: clamp(28px, 4vw, 40px); }
.bros-sec__media { display: grid; gap: 2px; }
@media (min-width: 900px) {
  .bros-sec {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.46fr);
    column-gap: clamp(32px, 4vw, 64px); align-items: start;
  }
  .bros-sec__text { max-width: 64ch; }
}

/* ------------------------------------------- brothers: head, plates, truck */
/* Built by brothers_section() in tools/build.py and rendered on / and /about,
   so the two routes cannot drift. .bros, .bro and .bro__role went with the
   layout they belonged to: the stacked single column bios with two truck
   photographs in a narrow right column, which left roughly 600px of empty
   ground under them at 1440.

   The plates are NOT a card grid. There are two because there are two owners,
   and they differ by construction: three credential chips against two, bodies
   of different length. Grid stretch gives them equal height and the closing
   line takes margin-top: auto, so both land on the same baseline whatever the
   copy above them does. */
.bros-head, .bros-head .lead { max-width: 60ch; }
.bros-head .lead { margin-top: var(--s-5); }

.bros-cards { display: grid; gap: var(--s-5); margin-top: clamp(32px, 4vw, 56px); }
@media (min-width: 800px) { .bros-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.bro-card {
  display: flex; flex-direction: column;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  /* 36px, not 44: the widest chip pair wants 468px and this leaves 474. */
  padding: clamp(24px, 2.6vw, 36px);
}

/* THE PORTRAIT SLOT. 120px square at 1440, 96px on a phone. Alex has a real
   portrait in it: the client identified the climb-harness frame as his brother,
   so `photo` is set on his object in content/site.json. Devin has no photograph
   anywhere in the client's own set, so his slot keeps the monogram. Either way
   nothing below moves, because the slot owns the box rather than its contents.
   The 2px orange rule under it is a bottom border on a decorative box, not a
   side stripe, and carries no text. */
.bro-card__slot {
  width: clamp(96px, 12vw, 120px); flex: none;
  padding-bottom: var(--s-3); margin-bottom: var(--s-5);
  border-bottom: 2px solid var(--orange);
}
.bro-card__mono {
  display: grid; place-items: center; aspect-ratio: 1;
  background: var(--green-deep); border-radius: var(--r);
  color: var(--ground);                    /* 15.9:1 on --green-deep */
  font-family: var(--f-display); font-weight: 650;
  font-size: clamp(48px, 6vw, 64px); line-height: 1;
  font-variation-settings: "opsz" 96, "SOFT" 50, "WONK" 0;
}
/* The source is 3 by 4 and the slot is square, so the crop is vertical only and
   object-position is the only control there is. 38 percent keeps his head and
   the saw on his harness inside a 120px box: at 50 the helmet sat low in the
   frame and the bottom of the square was all boot and trunk. */
.bro-card__slot img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  object-position: 50% 38%; border-radius: var(--r);
}

/* One spec voice rule for all three mono runs in this section rather than three
   copies of the same six declarations. */
.bro-card__role, .bro-card__chips li, .bros-truck__facts li {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted);
  line-height: 1.5;
}
.bro-card__role { margin-top: var(--s-2); }

/* The spec voice, with the uppercase taken back off. Every other mono run on
   this site carries a label, a licence or a timestamp, all of which are
   uppercase by nature. An email address is not, and setting one in caps reads
   as shouting a value that is written in lower case everywhere else it exists.
   overflow-wrap is insurance rather than a fix: the longest of the two
   addresses measures about 240px at 11px mono, inside the 272px a 360px screen
   leaves after the gutter and the card padding. */
.bro-card__mail {
  margin-top: var(--s-3);
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  letter-spacing: 0.01em; line-height: 1.5; overflow-wrap: anywhere;
}
.bro-card__mail a { color: var(--green); }      /* 10.3:1 on --ground */
.bro-card__mail a:hover { color: var(--green-deep); }

.bro-card__chips { display: flex; flex-wrap: wrap; gap: 6px; margin: var(--s-5) 0 0; }
.bro-card__chips li {
  padding: 5px 10px; background: var(--ground-2);
  border: 1px solid var(--rule-strong); border-radius: var(--r);
}

.bro-card__body { margin: var(--s-5) 0 var(--s-5); }
.bro-card__body p { line-height: 1.6; max-width: 46ch; }
.bro-card__body p + p { margin-top: var(--s-4); }

/* A stranger's sentence corroborating the bio above it. NOT a card inside a
   card, which DESIGN.md bans by name: no border box, no radius, no fill and no
   coloured side stripe. The only thing marking it off is the hairline above it,
   which is the same device .bro-card__pull and .strip already use, so the plate
   reads as one spec sheet with three registers rather than as a stack of
   boxes. */
.bro-card__proof { padding-top: var(--s-5); border-top: 1px solid var(--rule); }
.bro-card__proof-k, .bro-card__proof-who {
  margin: 0;
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted);
  line-height: 1.5;
}
.bro-card__proof-q { margin: var(--s-3) 0 var(--s-3); max-width: 42ch; }
.bro-card__proof-q p { line-height: 1.6; color: var(--body); }

/* margin-top: auto is what ends two plates of unequal copy level. */
.bro-card__pull {
  margin-top: auto; padding-top: var(--s-6);
  border-top: 1px solid var(--rule);
  font-family: var(--f-display); font-weight: 600; font-size: 20px;
  line-height: 1.35; letter-spacing: -0.005em; max-width: 34ch;
  color: var(--green);                     /* 10.3:1 on --ground */
  font-variation-settings: "opsz" 36, "SOFT" 50, "WONK" 0;
}

/* The truck band, 21 by 9 at the container's full width, cropped high enough
   that the wordmark, the mountains and the phone number all sit in frame. It
   carries the rule and the card radius because it is framed as an object in
   this section: it is the one photograph on the site with a corner radius, and
   DESIGN.md section 6 still reads "0 on every photograph", so that one line
   needs correcting to name this. */
.bros-truck { margin: 40px 0 0; }
.bros-truck img {
  width: 100%; aspect-ratio: 21 / 9; object-fit: cover; object-position: 50% 30%;
  border: 1px solid var(--rule); border-radius: var(--r);
}
/* The global figcaption rule is the spec voice, which left the caption mono and
   indistinguishable from the facts beside it. A caption is a sentence. */
.bros-truck__cap {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: var(--s-3) var(--s-6); margin-top: var(--s-4);
}
.bros-truck__note {
  margin: 0; max-width: 52ch; color: var(--body);
  font-family: var(--f-body); font-size: var(--t-base); font-weight: 400;
  text-transform: none; letter-spacing: normal; line-height: 1.5;
}
.bros-truck__facts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); margin: 0; flex: none; }
.bros-truck__facts li { display: flex; align-items: center; gap: var(--s-4); }
@media (max-width: 699.98px) {
  /* 21 by 9 is 150px tall on a 390px screen, which turns the one photograph
     carrying the wrap and the phone number into a sliver. The source is 16 by
     9, so a phone gets the whole frame with no crop at all. */
  .bros-truck img { aspect-ratio: 16 / 9; object-position: 50% 50%; }
  .bros-truck__cap { display: grid; gap: var(--s-4); }
  .bros-truck__facts { gap: var(--s-2) var(--s-4); }
}

/* --------------------------------------------------------------- area map */
.area { display: grid; gap: clamp(32px, 4vw, 64px); align-items: start; }
@media (min-width: 900px) { .area { grid-template-columns: 5fr 7fr; } }
/* A real Google Maps embed, the keyless legacy iframe, in place of the old
   inline schematic SVG. 16 by 11 above 700px, 4 by 3 below it, framed like
   every other photograph on the site: a 1px rule and the card corner radius. */
.gmap {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 11;
  border: 1px solid var(--rule);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--ground-2);
}
.gmap iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.gmap--sm { max-width: 420px; }
@media (max-width: 699.98px) {
  .gmap { aspect-ratio: 4 / 3; }
  .gmap--all { aspect-ratio: 4 / 5; }
}

.cities { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-5); }
.cities a {
  display: inline-flex; align-items: center;
  border: 1px solid var(--rule-strong); border-radius: var(--r);
  padding: 8px 14px; font-size: 0.9375rem; color: var(--ink); text-decoration: none;
  background: var(--ground);
}
.cities a:hover { background: var(--green); color: var(--on-green); border-color: var(--green); }

/* -------------------------------------------------------------------- faq */
.faq { border-top: 1px solid var(--rule); }
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-5);
  padding-block: var(--s-5); cursor: pointer; list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary h3 { font-size: 1.1875rem; line-height: 1.35; letter-spacing: -0.005em; max-width: 54ch; }
.faq__chev { flex: none; color: var(--green); margin-top: 4px; transition: transform 240ms var(--ease); }
.faq__chev svg { width: 18px; height: 18px; }
.faq details[open] .faq__chev { transform: rotate(180deg); }
.faq__body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 280ms var(--ease); }
.faq details[open] .faq__body { grid-template-rows: 1fr; }
.faq__inner { overflow: hidden; }
.faq__inner p { padding-bottom: var(--s-5); max-width: 68ch; color: var(--body); }

/* -------------------------------------------------------------- three cols */
.cols { display: grid; gap: var(--s-7); margin-top: var(--s-7); }
@media (min-width: 820px) { .cols { grid-template-columns: repeat(3, 1fr); gap: clamp(28px, 3vw, 52px); } }
.cols h3 { font-size: 1.1875rem; margin-bottom: var(--s-4); padding-bottom: var(--s-3); border-bottom: 1px solid var(--rule); }
.cols li { position: relative; padding-left: var(--s-5); margin-bottom: var(--s-3); font-size: 0.9375rem; line-height: 1.55; }
/* A square, not a rule. An 8px horizontal line in front of a list item reads
   as a hyphen at a glance, and a hyphen is the one character this site is not
   allowed to show. */
.cols li::before {
  content: ""; position: absolute; left: 0; top: 0.55em;
  width: 5px; height: 5px; background: var(--orange-ink);
}

/* ------------------------------------------------------ book with confidence */
/* The home page's one dark band besides the video and the footer. NOT four
   cards: DESIGN.md bans identical card grids by name, so this is ONE plate cut
   into quadrants by hairlines, as the credential plate already is. No per tile
   border, radius or shadow. Every colour is a token measured in DESIGN.md. */
.conf { background: var(--green); color: var(--on-green-dim); }
.conf__in { display: grid; gap: clamp(32px, 4vw, 64px); align-items: start; }
@media (min-width: 900px) { .conf__in { grid-template-columns: 5fr 7fr; gap: clamp(40px, 5vw, 88px); } }
.conf__say h2 { color: var(--on-green); max-width: 12ch; }
.eyebrow--dark { color: var(--on-green-dim); }
.conf__lead { margin-top: var(--s-5); color: var(--on-green-dim); max-width: 34ch; font-size: var(--t-lead); line-height: 1.5; }
.conf__say .btn { margin-top: var(--s-7); }

.conf__plate {
  display: grid; gap: 1px;
  background: var(--rule);           /* the 1px gap IS the hairline */
  border: 1px solid var(--rule);
  border-radius: var(--r);
  overflow: hidden;
}
@media (min-width: 600px) { .conf__plate { grid-template-columns: 1fr 1fr; } }
.conf__tile { background: var(--ground); padding: clamp(20px, 2.4vw, 32px); }
.conf__ico { display: block; color: var(--green); }
.conf__ico svg { width: 24px; height: 24px; }
.conf__t {
  margin-top: var(--s-4);
  font-size: 1.0625rem; line-height: 1.3; letter-spacing: -0.005em;
  color: var(--green);
}
.conf__b { margin-top: var(--s-3); font-size: 0.9375rem; line-height: 1.55; color: var(--body); }

/* ----------------------------------------------------------- from the field */
.field__head { max-width: 56ch; }
.field__head .lead { margin-top: var(--s-4); }
/* The gallery's rows are centred under centred headings. The home teaser's
   heading is left aligned, so its row starts at that same edge. */
.field .yt-row { margin-top: var(--s-7); justify-content: start; }
.field .yt-row .yt { margin-inline: 0; }

/* ------------------------------------------------------------- the gallery */
.gal-hero__in { max-width: 62ch; margin-inline: auto; text-align: center; }
.gal-hero__in .eyebrow::after { margin-inline: auto; }
.gal-hero__in .lead { margin-top: var(--s-5); margin-inline: auto; max-width: 52ch; }

.gal-band {
  background: var(--green);
  padding-block: clamp(48px, 6vw, 96px);
}
.gal-band__head { text-align: center; margin-bottom: clamp(28px, 3.5vw, 48px); }
.gal-band__head h2 { color: var(--on-green); }
.gal-band__head p { margin-top: var(--s-4); color: var(--on-green-dim); max-width: 54ch; margin-inline: auto; }

/* Every sixth tile spans two rows, dense packing backfills the holes. The
   tall tile deliberately has NO aspect ratio: a two row span takes both rows
   PLUS the gap, so it lands flush instead of 8px short. */
.gal-grid {
  display: grid; gap: 8px;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-flow: row dense;
}
@media (min-width: 700px)  { .gal-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1100px) { .gal-grid { grid-template-columns: repeat(4, 1fr); } }
.gal-tile {
  display: block; position: relative;
  aspect-ratio: 1;
  background: var(--green-deep);
  border: 1px solid var(--ground);
  overflow: hidden;
  cursor: zoom-in;
}
.gal-tile--tall { aspect-ratio: auto; grid-row: span 2; }
.gal-tile picture, .gal-tile img { width: 100%; height: 100%; }
.gal-tile img { object-fit: cover; transition: transform 420ms var(--ease); }
.gal-tile:hover img, .gal-tile:focus-visible img { transform: scale(1.03); }
.gal-tile:focus-visible { outline: 3px solid var(--on-green-warm); outline-offset: 2px; }

/* ------------------------------------------------------- the video facades */
/* Nothing from YouTube runs until asked: an idle iframe is ~1MB per tile. */
/* EVERY tile is 9:16, the two full length videos included: they were shot on
   a phone too, so cover cropping YouTube's 1280x720 thumbnail keeps the middle
   405px, exactly the real frame. Measured. build.py has the working. */
.yt-row { display: grid; gap: var(--s-5); justify-content: center; }
.yt-row--full { margin-top: var(--s-7); }
.yt-row--short { margin-top: var(--s-6); }
/* Fixed tracks, not 1fr: a 315px tile inside a 548px 1fr column left a canyon
   between the two, and justify-content cannot close a 1fr row. */
@media (min-width: 560px) { .yt-row--full  { grid-template-columns: repeat(2, minmax(0, 315px)); } }
@media (min-width: 760px) { .yt-row--short { grid-template-columns: repeat(3, minmax(0, 315px)); } }
.yt-row .yt { max-width: 315px; margin-inline: auto; width: 100%; }
.yt__btn {
  display: block; position: relative; width: 100%; padding: 0;
  aspect-ratio: 9 / 16; max-height: 560px;
  background: var(--ground-3); border: 1px solid var(--rule); border-radius: var(--r);
  overflow: hidden; cursor: pointer;
}
.yt__thumb { width: 100%; height: 100%; object-fit: cover; }
.yt__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--ground);
  filter: drop-shadow(0 2px 6px oklch(0.215 0.035 174 / 0.55));
  transition: transform 240ms var(--ease);
}
.yt__play svg { width: 56px; height: 56px; }
.yt__btn:hover .yt__play { transform: scale(1.06); }
.yt__sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.yt__title {
  margin-top: var(--s-3);
  font-family: var(--f-display); font-weight: 600; font-size: 1rem;
  line-height: 1.3; letter-spacing: -0.005em; color: var(--ink);
  font-variation-settings: "opsz" 20, "SOFT" 50, "WONK" 0;
}
.sec--band .yt__btn { background: var(--ground); }
.yt__frame {
  width: 100%; border: 0; display: block;
  aspect-ratio: 9 / 16; height: auto; max-height: 560px;
}
.yt__dur {
  margin-top: var(--s-2);
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted);
}

/* --------------------------------------------------------- social embeds */
/* An Instagram embed's height is LINEAR in its width, and padding-bottom
   resolves a percentage against the container's WIDTH, so calc(a% + bpx) IS
   h = a*w + b, right at every width. Facebook lays out at the width in its URL
   instead. Both sets of numbers and how they were taken are in build.py. */
/* Instagram's embed has a floor: at a 229px column (three columns at an 820px
   viewport) its content overflows its own frame. So columns step only where
   each still holds ~326px: two at 760, three at 1120. */
.embeds { display: grid; gap: var(--s-5); }
@media (min-width: 760px)  { .embeds--ig { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1120px) { .embeds--ig { grid-template-columns: repeat(3, 1fr); } }
/* Two columns only once each holds the 500px plugin. At 820, where this used
   to switch, each column was 356px and every post was clipped. */
@media (min-width: 1120px) { .embeds--fb { grid-template-columns: 1fr 1fr; } }
/* 502, not 500: .embed carries a 1px border, so a 500px cap left a 498px
   content box and clipped the 500 wide plugin by two pixels. */
/* align-self: start, or the grid stretches the shorter post to the taller
   one's height and draws its border around ~490px of empty ground. The
   Instagram boxes set an explicit height so they cannot stretch; these size to
   their iframe and would. */
.embeds--fb > .embed { max-width: 502px; width: 100%; align-self: start; }
.embed { position: relative; background: var(--ground); border: 1px solid var(--rule); border-radius: var(--r); overflow: hidden; }
.embed iframe { display: block; width: 100%; border: 0; }
.embed--ig { height: 0; padding-bottom: calc(var(--ea) + var(--eb)); }
.embed--ig iframe { position: absolute; inset: 0; height: 100%; }
/* The no JS height. site.js overrides it inline off the frame's real width. */
.embed--fb { overflow: hidden; }
.embed--fb iframe { height: var(--eh500); margin-inline: auto; }

/* ---------------------------------------------------------------- lightbox */
.lb {
  position: fixed; inset: 0; z-index: 80;
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: var(--s-3);
  padding: clamp(12px, 3vw, 40px);
  background: oklch(0.215 0.035 174 / 0.94);
}
.lb__fig { display: grid; place-items: center; min-width: 0; height: 100%; }
.lb__fig img {
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
  background: var(--green-deep);
}
.lb__btn {
  display: grid; place-items: center;
  width: 48px; height: 48px; flex: none;
  background: oklch(0.978 0.005 174 / 0.10);
  border: 1px solid oklch(0.978 0.005 174 / 0.45);
  border-radius: var(--r);
  color: var(--ground); cursor: pointer;
}
.lb__btn:hover { background: oklch(0.978 0.005 174 / 0.20); }
.lb__btn svg { width: 22px; height: 22px; }
.lb__prev svg { transform: rotate(180deg); }
.lb__close {
  position: absolute; top: clamp(12px, 3vw, 40px); right: clamp(12px, 3vw, 40px);
  z-index: 2;
}
.lb__count {
  position: absolute; left: 0; right: 0; bottom: clamp(12px, 2vw, 24px);
  text-align: center;
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--ground);
}
@media (max-width: 599.98px) {
  .lb { grid-template-columns: 1fr; }
  .lb__prev, .lb__next { position: absolute; bottom: clamp(40px, 8vw, 64px); z-index: 2; }
  .lb__prev { left: clamp(12px, 3vw, 40px); }
  .lb__next { right: clamp(12px, 3vw, 40px); }
}

/* --------------------------------------------------------------- cta band */
/* A light section on the ground colour, not a green card. Two green blocks
   stacked directly on the footer read as two footers, Sid's own words, so
   this is plain ground with a top rule and the footer stays the only green
   block on the page. The phone gets the same display treatment /contact
   uses; the three checkable facts collapse into one mono line under the
   actions instead of holding their own column. */
.cta-close h2 { font-size: var(--t-h1); max-width: 18ch; letter-spacing: -0.02em; }
.cta-close .lead { max-width: 40ch; }
.cta-close__act {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-7) var(--s-9);
  margin-top: var(--s-8);
}
.cta-close__call .mono { margin-bottom: var(--s-2); }
.cta-close__facts {
  margin-top: var(--s-8);
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-3) var(--s-4);
  color: var(--muted);
}
.cta-close__facts .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--orange); flex: none;   /* the sun, decorative separator */
}

/* ----------------------------------------------------------------- footer */
.ft { background: var(--green-deep); color: var(--on-green-dim); padding-block: var(--s-10) var(--s-7); }
.ft a { color: var(--on-green); }
.ft a:hover { color: var(--on-green-warm); }
.ft__grid { display: grid; gap: var(--s-8); }
@media (min-width: 860px) { .ft__grid { grid-template-columns: 5fr 3fr 4fr; gap: clamp(32px, 4vw, 72px); } }
.ft__name {
  font-family: var(--f-display); font-weight: 650; font-size: 1.5rem;
  color: var(--on-green); letter-spacing: -0.015em; line-height: 1.1;
  font-variation-settings: "opsz" 28, "SOFT" 50, "WONK" 0;
}
.ft__line { margin-top: var(--s-4); max-width: 40ch; font-size: 0.9375rem; }
.ft__h {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.11em; color: var(--on-green-warm);
  margin-bottom: var(--s-4);
}
.ft__list li { margin-bottom: var(--s-2); font-size: 0.9375rem; }
.ft__cities { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); }
.ft__cities a { font-size: 0.9375rem; }
.ft__legal {
  margin-top: var(--s-9); padding-top: var(--s-5);
  border-top: 1px solid oklch(0.965 0.010 174 / 0.18);
  display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); justify-content: space-between;
}
.ft__legal .mono { color: var(--on-green-dim); }

/* ------------------------------------------------------------------- dock */
/* The dock is the future home of the AI booking assistant. Its contents can be
   replaced without touching the layout: it is a fixed 6px rectangle anchored to
   the safe area, and nothing else on the page depends on its size. */
#wb-dock {
  position: fixed; z-index: 55;
  right: calc(16px + env(safe-area-inset-right));
  bottom: calc(16px + env(safe-area-inset-bottom));
  display: inline-flex; align-items: center; gap: var(--s-2);
  background: var(--green); color: var(--on-green);
  border: 1px solid var(--green-deep); border-radius: var(--r);
  padding: 12px 16px; text-decoration: none;
  font-size: 0.9375rem; font-weight: 600; line-height: 1;
  box-shadow: 0 2px 6px oklch(0.215 0.035 174 / 0.22);
}
#wb-dock:hover { background: var(--green-deep); color: var(--on-green); }
#wb-dock svg { width: 17px; height: 17px; flex: none; }
body.is-locked #wb-dock { display: none; }
/* Under 900px the dock is a 48px circle that expands to the label on tap.
   Driving the home page at 390 in 400px steps, sixteen distinct text nodes
   rendered underneath the old 109 by 43 pill, including the review score and
   the hero strip's date. The circle is a quarter of the area and sits in the
   corner; main and the footer also carry bottom padding so the end of the page
   can never sit under it. */
@media (max-width: 899.98px) {
  #wb-dock {
    width: 48px; height: 48px; padding: 0;
    border-radius: 50%; justify-content: center; overflow: hidden;
    transition: width 200ms var(--ease), border-radius 200ms var(--ease);
  }
  #wb-dock span { display: none; }
  #wb-dock[data-open="true"] {
    width: auto; padding: 0 16px 0 14px;
    border-radius: var(--r); gap: var(--s-2);
  }
  #wb-dock[data-open="true"] span { display: inline; }
  main { padding-bottom: 96px; }
  .ft { padding-bottom: calc(var(--s-9) + env(safe-area-inset-bottom)); }
}

/* ------------------------------------------------------------------- form */
.form { max-width: 44rem; }
.form__grid { display: grid; gap: var(--s-5); }
@media (min-width: 640px) { .form__row2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); } }
.f { display: grid; gap: var(--s-2); }
/* :not(.consent) matters: the consent row is a label too, and the spec voice
   is for a three word field name, not for a sentence a customer has to read
   and agree to. Without this the whole consent paragraph set in tracked
   uppercase mono. */
.f > label:not(.consent) {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted);
}
.f input, .f select, .f textarea {
  width: 100%;
  background: var(--ground);
  border: 1px solid var(--field); border-radius: var(--r);
  padding: 12px 14px; color: var(--ink);
  font-size: 1rem; line-height: 1.4;
}
.f textarea { min-height: 8rem; resize: vertical; }
.f select { appearance: none; padding-right: 40px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23495b55' stroke-width='1.6'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 16px;
}
.f input:focus, .f select:focus, .f textarea:focus { border-color: var(--green); }
.f[data-bad="true"] input, .f[data-bad="true"] select, .f[data-bad="true"] textarea { border-color: var(--orange-ink); }
.f__err { font-size: var(--t-fine); color: var(--orange-ink); }
.f__err:empty { display: none; }
.hp { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.consent {
  display: flex; gap: var(--s-3); align-items: flex-start;
  font-family: var(--f-body); text-transform: none; letter-spacing: normal;
  color: var(--body); cursor: pointer;
}
.consent input { width: 20px; height: 20px; flex: none; margin-top: 3px; accent-color: var(--green); }
.consent span { font-size: var(--t-fine); color: var(--body); line-height: 1.55; }
.form__note { font-size: var(--t-fine); color: var(--muted); max-width: 60ch; }
.form__state {
  border: 1px solid var(--rule-strong); border-radius: var(--r);
  background: var(--ground-2); padding: clamp(24px, 3vw, 40px); max-width: 44rem;
}
.form__state h3 { margin-bottom: var(--s-3); }
[hidden] { display: none !important; }

/* -------------------------------------------------------------- utilities */
.stack > * + * { margin-top: var(--s-5); }
.dl-facts { border-top: 1px solid var(--rule); }
.dl-facts > div { display: grid; gap: 2px; padding-block: var(--s-4); border-bottom: 1px solid var(--rule); }
@media (min-width: 620px) { .dl-facts > div { grid-template-columns: 16rem 1fr; gap: var(--s-4); align-items: baseline; } }
.dl-facts dt {
  font-family: var(--f-mono); font-size: var(--t-mono-fine); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted);
}
.dl-facts dd { color: var(--ink); }
.crumbs { padding-top: var(--s-5); }
.crumbs ol { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-3); }
.crumbs li { font-family: var(--f-mono); font-size: var(--t-mono-fine); text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted); }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--ink); }
.crumbs li + li::before { content: "\203A"; margin-right: var(--s-3); color: var(--rule-strong); }
.related { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-5); }
/* ONE display line, once. The page used to set the number at display size and
   then repeat it one line down at H3 size, which halves the impact of the best
   moment on the page. */
.big-link { font-family: var(--f-display); font-weight: 650; font-size: var(--t-hero); letter-spacing: -0.025em; line-height: 1.02; color: var(--ink); text-decoration: none; display: inline-block; font-variation-settings: "opsz" 96, "SOFT" 50, "WONK" 0; }
.big-link:hover { color: var(--green); text-decoration: underline; }
.small-acts { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); margin-top: var(--s-5); }
.small-acts a { font-family: var(--f-mono); font-size: var(--t-mono); font-weight: 500; text-transform: uppercase; letter-spacing: 0.09em; }

/* ---------------------------------------------------------------- reveals */
html.js .rv { opacity: 0; transform: translateY(14px); }
html.js .rv.in {
  opacity: 1; transform: none;
  transition: opacity 460ms var(--ease), transform 460ms var(--ease);
  transition-delay: calc(var(--d, 0) * 70ms);
}

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