/* ==========================================================================
   Elite Builders
   Rows of equal cards use CSS grid; everything else stays flexbox. The site
   was originally flexbox-only to map onto Elementor Flex Containers — that
   target was dropped, and the card rows moved to grid because equal 1fr
   tracks are what keep card images the same height (see .eb-card-grid).
   Contrast ratios noted inline are measured against WCAG 2.1 AA (4.5:1 text).
   ========================================================================== */

/* ---------- Design tokens ------------------------------------------------ */
:root {
  /* Brand neutrals */
  --eb-ink:         #222222;  /* headings, dark buttons, footer band      */
  --eb-ink-soft:    #45473C;  /* body copy — 9.4:1 on white               */
  --eb-white:       #FFFFFF;

  /* Warm bands from the approved mockup.
     The two bands swapped sections: sage now carries Testimonials, taupe
     carries Featured (and was re-toned from #776E61 to #7C766A). Taupe's
     4.51:1 clears the 4.5:1 floor for the 12px ghost-button label on it,
     but only just — lighten that band any further and the button fails. */
  --eb-cream:       #EDE8DF;
  --eb-sage:        #616B64;  /* white text = 5.53:1 (AA pass)            */
  --eb-taupe:       #7C766A;  /* white text = 4.51:1 (AA pass, no room)   */
  --eb-line:        #DCD8CF;
  --eb-line-dark:   rgba(255,255,255,.28);

  /* Focus ring colour is re-declared per band so it stays visible */
  --eb-focus:       #222222;

  /* Type */
  --eb-font:        "Google Sans Flex", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --eb-font-word:   "Radio Canada Big", "Google Sans Flex", Helvetica, Arial, sans-serif;
  --eb-font-quote:  "EB Garamond", Garamond, "Times New Roman", Times, serif;

  /* Form state colours */
  --eb-error:       #A32B18;  /* 6.4:1 on --eb-cream */
  --eb-success:     #2C5B3F;  /* 6.6:1 on --eb-cream */

  /* Rhythm — 4/8px scale */
  --eb-space-1: 8px;
  --eb-space-2: 16px;
  --eb-space-3: 24px;
  --eb-space-4: 32px;
  --eb-space-5: 48px;
  --eb-space-6: 64px;
  --eb-space-7: 96px;

  --eb-container: 1200px;
  --eb-radius: 2px;
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--eb-white);
  color: var(--eb-ink-soft);
  font-family: var(--eb-font);
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
h1, h2, h3 { color: var(--eb-ink); margin: 0; text-wrap: balance; font-weight: 500; }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
a { color: inherit; }
button { font: inherit; color: inherit; }
[hidden] { display: none !important; }

/* Anchor targets clear the sticky header */
:target { scroll-margin-top: 120px; }

/* ---------- Accessibility helpers ---------------------------------------- */
.eb-visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

.eb-skip-link {
  position: absolute;
  left: var(--eb-space-2);
  top: -80px;
  z-index: 200;
  background: var(--eb-ink);
  color: var(--eb-white);
  padding: 12px 20px;
  font-size: .875rem;
  font-weight: 600;
  text-decoration: none;
  transition: top .15s ease;
}
.eb-skip-link:focus { top: var(--eb-space-2); }

:focus-visible {
  outline: 2px solid var(--eb-focus);
  outline-offset: 3px;
  border-radius: 1px;
}

/* ---------- Layout primitives -------------------------------------------- */
.eb-container {
  width: 100%;
  max-width: var(--eb-container);
  margin-inline: auto;
  padding-inline: var(--eb-space-3);
}

/* ---------- Card grid ----------------------------------------------------
   One grid for every row of equal cards: the homepage services pair, the
   kitchen cabinet trio and the bathroom pair. Column count is a modifier.

   Tracks are 1fr, so every card gets an identical content width and
   `aspect-ratio` on the images resolves to identical heights. The previous
   flexbox version put the hairline divider on the card as border-left +
   padding-left, which made every card after the first narrower than the
   first — that is what forced the fixed pixel heights this replaces.
   The divider is now drawn in the gutter by a pseudo-element, so it costs
   no content width and sits centred between tracks.
   ------------------------------------------------------------------------ */
.eb-card-grid {
  display: grid;
  grid-template-columns: repeat(var(--eb-cols, 3), minmax(0, 1fr));
  gap: var(--eb-space-5);
}
.eb-card-grid--2 { --eb-cols: 2; }
.eb-card-grid--3 { --eb-cols: 3; }

.eb-card-grid > * { position: relative; }

@media (min-width: 861px) {
  .eb-card-grid > * + *::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--eb-space-5) / -2);
    width: 1px;
    background: var(--eb-line);
  }
}
@media (max-width: 860px) {
  .eb-card-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- Media row ----------------------------------------------------
   Image on one side, copy on the other. Which side the image lands on is
   set by DOM order alone — the kitchen page authors the media first, the
   bathroom page authors the copy first — so no modifier is needed.
   ------------------------------------------------------------------------ */
.eb-media-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: center;
  gap: var(--eb-space-5);
}
.eb-media-row__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.eb-media-row__title { font-size: clamp(1.625rem, 2.6vw, 2.125rem); margin-bottom: var(--eb-space-2); }
.eb-media-row__intro { max-width: 52ch; color: var(--eb-ink-soft); margin-bottom: var(--eb-space-3); }
.eb-media-row__items { display: flex; flex-direction: column; gap: 10px; }
.eb-media-row__outro { max-width: 52ch; color: var(--eb-ink-soft); margin-top: var(--eb-space-3); }
@media (max-width: 860px) {
  .eb-media-row { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- Neighbourhood chips ------------------------------------------
   Scannable version of the service-area sentence above them. Deliberately
   not links: there are no per-neighbourhood pages to point at, and links
   to thin or missing pages are worse for SEO than plain text. If those
   pages are ever built, swap the <li> content for <a> and add :hover.
   ------------------------------------------------------------------------ */
.eb-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: var(--eb-space-3);
}
/* Height is set directly rather than left to padding + the body's 1.7
   line-height, which was making these as tall as buttons. These are labels,
   not controls, so there is no tap-target minimum to respect. */
.eb-chip {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 0 14px;
  background: #F8F5F1;         /* lighter than --eb-cream so the pill reads on white */
  border: 1px solid #E5DFD3;
  border-radius: 999px;
  /* Same eyebrow treatment as .eb-contact__label and the buttons */
  font-size: .75rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--eb-ink);        /* 14.6:1 on the chip fill */
  white-space: nowrap;
}

/* Interactive variant — same pill, but a real control. 44px so it clears
   the WCAG 2.5.5 target size; the plain label chips above have no such
   minimum because nothing can be pressed. */
.eb-chip--action {
  min-height: 44px;
  padding: 0 20px;
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease, border-color .2s ease;
}
.eb-chip--action:hover { background: #EFEAE2; }
.eb-chip--action[aria-pressed="true"] {
  background: var(--eb-ink);
  border-color: var(--eb-ink);
  color: var(--eb-white);
}

/* ---------- Buttons ------------------------------------------------------ */
.eb-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 12px 28px;
  border: 1px solid transparent;
  border-radius: var(--eb-radius);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease, border-color .2s ease;
}
.eb-btn--sm { min-height: 44px; padding: 10px 22px; }

.eb-btn--dark        { background: #C52121; color: var(--eb-white); }
.eb-btn--dark:hover  { background: #A81B1B; }

.eb-btn--light       { background: var(--eb-white); color: var(--eb-ink); }
.eb-btn--light:hover { background: #EFEDE6; }

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

.eb-btn--ghost-light { background: transparent; color: var(--eb-white); border-color: rgba(255,255,255,.8); }
.eb-btn--ghost-light:hover { background: rgba(255,255,255,.16); border-color: #fff; }

/* ==========================================================================
   MASTHEAD — overlays the hero, then the header alone sticks on scroll
   ========================================================================== */
.eb-masthead {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 100;
}

/* ==========================================================================
   TOP BAR
   ========================================================================== */
/* Transparent, overlaying the hero photo — reverted from an earlier
   "always opaque white" version per a later mockup showing it sitting on
   the hero. It's protected by the same vertical scrim as the header row
   below it (.eb-hero::after); contrast verified against the actual photo,
   see the icon color note below. */
.eb-topbar {
  background: transparent;
  color: var(--eb-white);
  font-size: .75rem;
  letter-spacing: .02em;
  border-bottom: 1px solid var(--eb-line-dark);
}
.eb-topbar__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--eb-space-1) var(--eb-space-3);
  /* The 32px links are what set this bar's height, not min-height — so both
     had to come down to actually shed 8px (45px -> 37px). The links keep
     their own padding, which is what holds their tap target. */
  min-height: 32px;
  padding-block: 2px;
}
.eb-topbar__left,
.eb-topbar__right { display: flex; align-items: center; gap: 10px; margin: 0; }
.eb-topbar a {
  color: var(--eb-white);
  text-decoration: none;
  padding: 6px 2px;            /* keeps the target ≥24px tall (WCAG 2.5.8) */
  min-height: 28px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.eb-topbar a:hover { text-decoration: underline; }
.eb-topbar__left { gap: 8px; }   /* icon-to-text gap for the license line */
.eb-topbar__icon { width: 14px; height: 14px; flex: none; }
.eb-topbar__sep { opacity: .5; }

/* ==========================================================================
   HEADER
   ========================================================================== */
/* Default (overlay) state: transparent, light content, sitting on the hero.
   The hero's top scrim is what carries contrast here — see .eb-hero::after. */
.eb-header {
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background-color .25s ease, border-color .25s ease, box-shadow .25s ease;
}

/* Two independent concerns:
   .is-solid — white bar with dark content (scrolled, OR mobile panel open)
   .is-stuck — detached and pinned to the viewport (scrolled only)
   Keeping them separate stops the panel-open state from yanking the header
   up over the top bar while the page is still at scroll-top. */
.eb-header.is-solid {
  background: var(--eb-white);
  border-bottom-color: var(--eb-line);
}
.eb-header.is-stuck {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  box-shadow: 0 2px 14px rgba(20,21,15,.09);
  animation: eb-header-drop .28s ease;
}
@keyframes eb-header-drop {
  from { transform: translateY(-100%); }
  to   { transform: translateY(0); }
}

.eb-header__inner {
  display: flex;
  align-items: center;
  gap: var(--eb-space-3);
  min-height: 76px;
}

/* Brand ------------------------------------------------------------------ */
.eb-brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  flex: 0 0 auto;
}
.eb-brand__mark { width: 34px; height: auto; }
.eb-brand__word {
  font-family: var(--eb-font-word);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--eb-ink);
  white-space: nowrap;
}
.eb-brand--sm .eb-brand__mark { width: 30px; }
.eb-brand--sm .eb-brand__word { font-size: .9375rem; }

/* --- Logo swap: light over hero, red mark once solid --- */
.eb-header .eb-brand__mark--dark  { display: none; }
.eb-header .eb-brand__mark--red   { display: none; }
.eb-header .eb-brand__word        { color: var(--eb-white); }
.eb-header.is-solid .eb-brand__mark--light { display: none; }
.eb-header.is-solid .eb-brand__mark--dark  { display: none; }
.eb-header.is-solid .eb-brand__mark--red   { display: block; }
.eb-header.is-solid .eb-brand__word        { color: var(--eb-ink); }

/* Desktop nav ------------------------------------------------------------ */
.eb-nav { margin-left: auto; }
.eb-nav__list { display: flex; align-items: center; gap: 4px; }

.eb-nav__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 10px 12px;
  background: none;
  border: 0;
  font-size: .9375rem;
  font-weight: 400;
  color: var(--eb-ink);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: color .2s ease;
}
.eb-nav__link:hover { color: #6C6F5E; }
span.eb-nav__link { cursor: default; }
span.eb-nav__link:hover { color: inherit; }

/* Overlay state: white nav on the hero's top scrim (measured ≥4.5:1).
   Stuck state: dark nav on white. */
.eb-header .eb-nav__link          { color: var(--eb-white); }
.eb-header .eb-nav__link:hover    { color: #D8D5C9; }
.eb-header.is-solid .eb-nav__link       { color: var(--eb-ink); }
.eb-header.is-solid .eb-nav__link:hover { color: #6C6F5E; }

.eb-caret { width: 10px; height: 7px; flex: none; transition: transform .2s ease; }
.eb-nav__toggle[aria-expanded="true"] .eb-caret { transform: rotate(180deg); }

.eb-nav__item--has-sub { position: relative; }
.eb-subnav {
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 220px;
  padding: 8px;
  background: var(--eb-white);
  border: 1px solid var(--eb-line);
  box-shadow: 0 12px 28px rgba(20,21,15,.10);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
.eb-nav__item--has-sub:hover .eb-subnav,
.eb-nav__item--has-sub:focus-within .eb-subnav,
.eb-subnav.is-open {
  opacity: 1; visibility: visible; transform: translateY(0);
}
.eb-subnav a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 8px 12px;
  font-size: .9375rem;
  color: var(--eb-ink);
  text-decoration: none;
}
.eb-subnav a:hover { background: var(--eb-cream); }

.eb-header__cta { flex: 0 0 auto; }

/* Overlay state: outlined CTA + light hamburger, white focus ring */
.eb-header:not(.is-solid) { --eb-focus: #FFFFFF; }
.eb-header:not(.is-solid) .eb-header__cta {
  background: transparent;
  color: var(--eb-white);
  border-color: rgba(255,255,255,.82);
}
.eb-header:not(.is-solid) .eb-header__cta:hover {
  background: rgba(255,255,255,.18);
  border-color: #fff;
}
.eb-header:not(.is-solid) .eb-menu-toggle { border-color: rgba(255,255,255,.7); }
.eb-header:not(.is-solid) .eb-menu-toggle__bars span { background: var(--eb-white); }
.eb-header:not(.is-solid) .eb-menu-toggle__label { color: var(--eb-white); }

/* Hamburger -------------------------------------------------------------- */
.eb-menu-toggle {
  display: none;
  align-items: center;
  gap: 10px;
  margin-left: auto;
  min-height: 48px;
  padding: 10px 14px;
  background: none;
  border: 1px solid var(--eb-line);
  border-radius: var(--eb-radius);
  cursor: pointer;
}
.eb-menu-toggle__bars { display: flex; flex-direction: column; gap: 4px; width: 18px; }
.eb-menu-toggle__bars span {
  display: block; height: 2px; width: 100%;
  background: var(--eb-ink);
  transition: transform .2s ease, opacity .2s ease;
}
.eb-menu-toggle[aria-expanded="true"] .eb-menu-toggle__bars span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.eb-menu-toggle[aria-expanded="true"] .eb-menu-toggle__bars span:nth-child(2) { opacity: 0; }
.eb-menu-toggle[aria-expanded="true"] .eb-menu-toggle__bars span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
.eb-menu-toggle__label {
  font-size: .75rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
}

/* Mobile panel ----------------------------------------------------------- */
.eb-mobile-menu {
  border-top: 1px solid var(--eb-line);
  background: var(--eb-white);
  padding: var(--eb-space-2) var(--eb-space-3) var(--eb-space-4);
  max-height: calc(100vh - 116px);
  overflow-y: auto;
}
.eb-mobile-menu__list { display: flex; flex-direction: column; }
.eb-mobile-menu__list > li { border-bottom: 1px solid var(--eb-line); }
.eb-mobile-menu__list a,
.eb-mobile-menu__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 52px;
  padding: 12px 2px;
  background: none;
  border: 0;
  font-size: 1rem;
  color: var(--eb-ink);
  text-decoration: none;
  text-align: left;
  cursor: pointer;
}
.eb-mobile-menu__toggle[aria-expanded="true"] .eb-caret { transform: rotate(180deg); }
.eb-mobile-menu__sub { padding: 0 0 8px 16px; }
.eb-mobile-menu__sub a { min-height: 48px; font-size: .9375rem; color: var(--eb-ink-soft); }
.eb-mobile-menu__cta { width: 100%; margin-top: var(--eb-space-3); }
.eb-mobile-menu__meta {
  display: flex; flex-direction: column; gap: 4px;
  margin-top: var(--eb-space-3);
  font-size: .875rem;
}
.eb-mobile-menu__meta a { min-height: 32px; display: inline-flex; align-items: center; }

/* ==========================================================================
   HERO
   Elementor: Container background image + gradient overlay (not an <img>)
   ========================================================================== */
.eb-hero {
  position: relative;
  overflow: hidden;                /* clips the photo layer's overscan */
  display: flex;
  align-items: center;
  min-height: clamp(520px, 64vh, 660px);
  padding-top: 160px;              /* clears the overlaid masthead */
  padding-bottom: var(--eb-space-6);
  background-color: #222222;
  --eb-focus: #FFFFFF;
}

/* Photo layer, deliberately larger than the hero on every side.
   `background-size: cover` computes a fractional scale (e.g. 1999/1415), and
   browsers round the painted size differently depending on width, zoom and
   device pixel ratio. When it rounds short, the compositor fills the shortfall
   with background-color, which shows as a seam down the edges of the hero —
   most visible on the right, where the scrim is nearly transparent and the
   photo edge (#776E59) is far lighter than any dark fill.
   A 16px overscan is far larger than any rounding error, so the image always
   reaches past the clip boundary. Do not reduce this to a hairline value. */
.eb-hero::before {
  content: "";
  position: absolute;
  inset: -16px;
  pointer-events: none;
  background-image: url("../images/hero-blur.jpg");
  background-size: cover;
  background-position: 62% center;
  background-repeat: no-repeat;
}
/* Kitchen service page — same hero component, different photo. Scrim and
   overscan are unchanged; re-verified against this photo (worst case
   4.57:1 on the H1/lede zone, needs 3:1; 7.9:1+ on the masthead zone,
   needs 4.5:1) at desktop and mobile widths, so no page-specific tint
   was needed. */
.eb-hero--kitchen::before {
  background-image: url("../images/hero-kitchen-oceanview.jpg");
}
/* Bathroom service page — same component, different photo. This photo is
   much taller relative to the hero box than the others (cover crops it
   vertically, not horizontally, at desktop widths), so centering it or
   showing the top both missed the vanity/cabinets. Bottom-aligned instead,
   re-verified at 5.09:1 (masthead, needs 4.5:1) and 5.32:1 (H1/lede, needs
   3:1) at desktop, 10.19:1 / 4.31:1 at mobile (mobile crops horizontally,
   not vertically, so its own position rule below is untouched). */
.eb-hero--bathroom::before {
  background-image: url("../images/hero-bathroom-oceanview.jpg");
  background-position: center bottom;
}
/* Both scrims live on one layer, stacked above the photo and below the copy.
   Both were lightened from the original dark tint per a direct request; the
   two layers were NOT cut by the same amount, and that's deliberate — see
   below.
   Layer 1 (top, vertical, masthead) — cut 25%. Fully re-verified passing at
   every breakpoint with room to spare, so the full reduction was safe here.
   Layer 2 (horizontal, H1/lede/rating) — cut only 18%, not 25%. The H1 is
   already far past the WCAG "large text" size threshold (24px), which caps
   its requirement at 3:1 — being large text further doesn't lower that floor
   any more, so a bigger H1 can't buy back the contrast a fuller 25% cut
   would cost it. 18% is the measured maximum reduction that still holds
   3:1 for the H1 at every breakpoint (worst case 3.02:1 at 375px). The lede
   and rating were both bumped to 24px specifically so they cross into that
   same "large text" 3:1 bracket instead of needing 4.5:1 — that's what
   makes the fuller-looking lightening read correctly on those two without
   itself dropping below threshold. */
.eb-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    /* Re-shaped when the topbar went from opaque-white back to transparent-
       over-hero: that reintroduced a contrast requirement at y=0–45px this
       gradient hadn't been tested against after three rounds of lightening.
       A single-stop fix converged too slowly (failures sit at y=12–34, not
       y=0, so linear interpolation to the old 118px stop barely moved them)
       and needed an unreasonably high peak. Adding a stop at 50px — then
       the topbar's bottom edge — keeps that zone dense (α≈.60–.63, margin
       5.0–14.3:1) while rejoining the already-verified header-row curve at
       118px unchanged (still 5.4–16.8:1 there).
       The topbar has since been shortened to 37px, so its text and the
       header row below it both sit slightly higher — i.e. in denser scrim
       than these figures were measured against. Safe in that direction;
       re-measure before moving anything DOWN the gradient. */
    linear-gradient(to bottom,
      rgba(12,13,9,.63)   0px,
      rgba(12,13,9,.60)   50px,
      rgba(12,13,9,.3924) 118px,
      rgba(12,13,9,.2403) 190px,
      rgba(12,13,9,.0765) 280px,
      rgba(12,13,9,0)     360px),
    linear-gradient(90deg,
      rgba(12,13,9,.59)  0%,
      rgba(12,13,9,.541) 50%,
      rgba(12,13,9,.492) 64%,
      rgba(12,13,9,.262) 80%,
      rgba(12,13,9,.066) 92%,
      rgba(12,13,9,0)    100%);
}
/* Above both scrims. Keeps the 1200px container width so the copy starts at
   the container's left edge (as in the mockup) instead of being centred —
   the text itself is what gets constrained, not the block. */
.eb-hero__inner { position: relative; z-index: 2; }

.eb-hero__title {
  max-width: 620px;
  color: var(--eb-white);
  font-size: clamp(2.125rem, 4.6vw, 3.5rem);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -.02em;
  margin-block: var(--eb-space-3) var(--eb-space-3);
}
.eb-hero__lede {
  color: #F0EFE8;
  /* 24px crosses the WCAG "large text" threshold (needs 3:1, not 4.5:1) —
     required once the scrim was lightened; at the old 17px this fails on
     the lightened tint at 1024px and 375px. See the scrim comment above. */
  font-size: 1.5rem;
  line-height: 1.4;
  max-width: 48ch;
}
.eb-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: var(--eb-space-4);
}

/* Star rating --------------------------------------------------------------
   Wrapped in a frosted white pill rather than sized up to "large text."
   This sidesteps the photo-contrast problem entirely: the text sits on a
   near-white blended background regardless of what's in the photo behind
   it, so it can go back to its normal small size and a dark ink color.
   Measured against the actual photo: even at 50% white opacity the ink
   text clears 10:1+ at every breakpoint; 85% was chosen for a visibly
   "white pill" look while keeping some of the photo's warmth showing
   through, with contrast to spare (15.9–16.3:1). */
.eb-rating {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: rgba(255,255,255,.85);
  border-radius: 999px;
  padding: 8px 16px;
}
.eb-rating__glyph { position: relative; display: inline-block; line-height: 0; }
.eb-rating__base,
.eb-rating__fill { display: flex; gap: 2px; }
.eb-rating__base svg { fill: rgba(20,21,15,.22); }  /* visible "empty" star on a light pill */
.eb-rating__fill {
  position: absolute; inset: 0;
  overflow: hidden;
  white-space: nowrap;
}
.eb-rating__fill svg { fill: #C97A0E; flex: none; }  /* darkened slightly from #F2B441 for contrast on white */
.eb-rating svg { width: 16px; height: 16px; }
.eb-rating__text {
  color: var(--eb-ink);        /* dark text now that the background is light */
  font-size: .8125rem;         /* back to its original small size */
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
}

/* ==========================================================================
   ACCOLADES STRIP
   One centred row while the badges fit. When they stop fitting the track
   becomes a snap-scroller and main.js reveals the arrows — the same markup
   either way, so nothing is duplicated per breakpoint.
   ========================================================================== */
.eb-accolades { background: var(--eb-white); padding-block: var(--eb-space-5); }
.eb-accolades__inner { position: relative; }
.eb-accolades__track {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--eb-space-5);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;            /* arrows and swipe stand in for it */
  -ms-overflow-style: none;
}
.eb-accolades__track::-webkit-scrollbar { display: none; }
.eb-accolades__item { flex: 0 0 auto; scroll-snap-align: center; }
/* Height is the constant; widths vary with each badge's shape, which is
   what keeps the row optically even. */
.eb-accolades__img { height: 96px; width: auto; max-width: none; }   /* was 120px */

/* Overlaid rather than sitting in the flow: as flex siblings they consumed
   track width, so revealing them kept the track overflowing and they could
   never hide again. Out of flow, the track measures the same either way. */
.eb-accolades__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: var(--eb-white);
  border: 1px solid var(--eb-line);
  border-radius: 50%;
  color: var(--eb-ink);
  cursor: pointer;
  transition: background-color .2s ease, border-color .2s ease;
}
.eb-accolades__nav:hover { background: var(--eb-cream); border-color: #C9C4B8; }
.eb-accolades__nav svg { width: 18px; height: 18px; }
.eb-accolades__nav--prev { left: 0; }
.eb-accolades__nav--next { right: 0; }

@media (max-width: 900px) {
  /* Left-aligned once it scrolls — centring a scrolling track hides the
     first item behind the start edge. */
  .eb-accolades__track { justify-content: flex-start; gap: var(--eb-space-4); }
  .eb-accolades__img { height: 77px; }   /* was 96px; both breakpoints scaled by the same 20% */
}

/* ==========================================================================
   INTRO / ABOUT
   ========================================================================== */
.eb-intro { background: var(--eb-cream); padding-block: var(--eb-space-7); }
.eb-intro__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.eb-intro__title {
  font-size: clamp(1.75rem, 3vw, 2.375rem);
  line-height: 1.2;
  margin-bottom: var(--eb-space-3);
}
.eb-intro__body { max-width: 70ch; color: var(--eb-ink-soft); }
.eb-intro__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: var(--eb-space-4);
}

/* ==========================================================================
   SERVICES
   ========================================================================== */
.eb-services { padding-block: var(--eb-space-7); }
.eb-services__title {
  font-size: clamp(1.625rem, 2.6vw, 2.125rem);
  margin-bottom: var(--eb-space-4);
}
.eb-service {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.eb-service__img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  margin-bottom: var(--eb-space-3);
}
.eb-service__title { font-size: 1.125rem; font-weight: 600; margin-bottom: var(--eb-space-2); }
.eb-service__list { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--eb-space-3); }
.eb-service__list li { position: relative; padding-left: 18px; font-size: .9375rem; }
.eb-service__list li::before {
  content: "";
  position: absolute; left: 0; top: .7em;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--eb-ink);
}

/* ==========================================================================
   FEATURED GALLERY
   ========================================================================== */
.eb-featured {
  background: var(--eb-taupe);
  color: var(--eb-white);
  padding-block: var(--eb-space-6);
  --eb-focus: #FFFFFF;
}
.eb-featured__title {
  color: var(--eb-white);
  font-size: clamp(1.625rem, 2.6vw, 2.125rem);
  margin-bottom: var(--eb-space-4);
}
/* Explicit 3-up so six tiles land as a 3 x 2 block. Stated as a column count
   rather than the flex-basis arithmetic this replaces — grid subtracts the
   gaps itself. Drops to 2-up below 640px (3 x 2 becomes 2 x 3). */
.eb-featured__row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--eb-space-2);
}
.eb-tile__link {
  position: relative;
  display: block;
  overflow: hidden;
  text-decoration: none;
}
.eb-tile__link img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform .4s ease;
}
.eb-tile__link:hover img { transform: scale(1.04); }
/* Scrim keeps the caption ≥4.5:1 over any photo */
.eb-tile__caption {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  /* Scrim stops were re-weighted when the caption text was sized up: the
     taller text block pushes its top line further up the gradient, so the
     dense zone was extended (45% -> 62%) to keep that line on the same
     alpha it had at the smaller size. Verified against all six photos. */
  padding: 40px 14px 14px;
  background: linear-gradient(to top, rgba(8,9,6,.90) 0%, rgba(8,9,6,.62) 62%, rgba(8,9,6,0) 100%);
  color: var(--eb-white);
}
.eb-tile__room  { font-size: 1rem; font-weight: 600; line-height: 1.3; }
.eb-tile__place { font-size: .875rem; color: #E8E7DF; line-height: 1.3; }
.eb-featured__actions { margin-top: var(--eb-space-4); }

/* ==========================================================================
   TESTIMONIALS
   ========================================================================== */
.eb-testimonials {
  background: #373633;
  color: var(--eb-white);
  padding-block: var(--eb-space-6);
  --eb-focus: #FFFFFF;
}
.eb-testimonials__inner { display: flex; flex-direction: column; align-items: center; }
.eb-testimonials__title {
  color: var(--eb-white);
  font-size: clamp(1.625rem, 2.6vw, 2.125rem);
  margin-bottom: var(--eb-space-2);
}
/* Reuses the same light color already verified at 5.0:1 on --eb-sage
   for .eb-slide__place below. No real review-platform URL yet, so href="#"
   — underline-on-hover/focus is what signals it's a link in the meantime. */
.eb-testimonials__rating {
  display: inline-block;
  color: #F5F4EF;
  font-size: .8125rem;
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-decoration: none;
  margin-bottom: var(--eb-space-4);
}
.eb-testimonials__rating:hover,
.eb-testimonials__rating:focus-visible {
  text-decoration: underline;
}
.eb-carousel { width: 100%; max-width: 780px; }
.eb-carousel__viewport { min-height: 160px; display: flex; align-items: center; justify-content: center; }

.eb-slide { margin: 0; text-align: center; }
.eb-slide__quote { margin: 0; }
.eb-slide__quote p {
  font-family: var(--eb-font-quote);
  font-size: clamp(1.375rem, 2.6vw, 1.875rem);   /* Garamond runs small — sized up */
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: .005em;
  color: var(--eb-white);
}
.eb-slide__by {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  margin-top: var(--eb-space-3);
}
.eb-slide__avatar { width: 20px; height: 20px; color: #EDEBE4; margin-bottom: 3px; }
.eb-slide__name {
  font-size: .75rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
}
.eb-slide__place { font-size: .75rem; color: #F5F4EF; letter-spacing: .04em; } /* 5.0:1 on --eb-sage */

/* Service page variant — Name leads (sentence case, larger), Location drops
   to an eyebrow label, Service is the smallest line. Both Location and
   Service keep the same already-verified colour as the default .eb-slide__place
   (4.6:1 on --eb-taupe) — only size/weight change, not colour, so no new
   contrast value needed. */
.eb-slide__by--stacked .eb-slide__name {
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: .01em;
  text-transform: none;
  margin-top: 2px;
}
.eb-slide__by--stacked .eb-slide__place {
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.eb-carousel__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--eb-space-2);
  margin-top: var(--eb-space-4);
}
.eb-carousel__arrow {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  background: none;
  border: 1px solid rgba(255,255,255,.55);
  border-radius: 50%;
  color: var(--eb-white);
  cursor: pointer;
  transition: background-color .2s ease, border-color .2s ease;
}
.eb-carousel__arrow:hover { background: rgba(255,255,255,.18); border-color: #fff; }
.eb-carousel__arrow svg { width: 14px; height: 14px; }

.eb-carousel__dots { display: flex; align-items: center; gap: 6px; }
.eb-dot {
  width: 24px; height: 24px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  position: relative;
}
.eb-dot::after {
  content: "";
  position: absolute; inset: 0; margin: auto;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: rgba(255,255,255,.45);
  transition: background-color .2s ease, transform .2s ease;
}
.eb-dot.is-active::after { background: #fff; transform: scale(1.25); }

/* ==========================================================================
   QUOTE FORM
   The surrounding .eb-quote band was removed when the form stopped being
   copied onto every page — it now renders once, in .eb-contact__formblock
   on /contact/. The form and field rules below are unchanged.
   ========================================================================== */
.eb-form {
  display: flex;
  flex-direction: column;
  gap: var(--eb-space-2);
  width: 100%;
  max-width: 620px;
  margin-top: var(--eb-space-4);
  text-align: left;
}
.eb-form__note { font-size: .8125rem; color: var(--eb-ink-soft); }
.eb-form__row { display: flex; flex-wrap: wrap; gap: var(--eb-space-2); }
.eb-form__row .eb-field { flex: 1 1 220px; }

.eb-field { display: flex; flex-direction: column; gap: 6px; }
.eb-field__label { font-size: .875rem; font-weight: 600; color: var(--eb-ink); }
.eb-req { color: var(--eb-error); }

.eb-field__input {
  width: 100%;
  min-height: 48px;
  padding: 12px 14px;
  background: var(--eb-white);
  border: 1px solid #827C70;          /* 3.4:1 on cream, 3.8:1 on the white fill — WCAG 1.4.11 */
  border-radius: var(--eb-radius);
  font-family: inherit;
  font-size: 1rem;                     /* 16px avoids iOS zoom-on-focus */
  color: var(--eb-ink);
  transition: border-color .2s ease, box-shadow .2s ease;
}
.eb-field__input:hover { border-color: #5E594F; }
.eb-field__input:focus { border-color: var(--eb-ink); box-shadow: 0 0 0 2px rgba(20,21,15,.14); }
.eb-field__textarea { min-height: 120px; resize: vertical; line-height: 1.6; }
.eb-field__select { appearance: none; cursor: pointer; padding-right: 40px;
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%2314150F' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 12px 8px;
}

.eb-field__help  { font-size: .8125rem; color: var(--eb-ink-soft); }
.eb-field__error { font-size: .8125rem; font-weight: 500; color: var(--eb-error); }
.eb-field__input[aria-invalid="true"] { border-color: var(--eb-error); border-width: 2px; }

.eb-form__submit { align-self: flex-start; margin-top: var(--eb-space-1); }
.eb-form__success {
  padding: 12px 14px;
  border-left: 3px solid var(--eb-success);
  background: var(--eb-white);
  color: var(--eb-success);
  font-size: .9375rem;
  font-weight: 500;
}

/* ==========================================================================
   CLOSING CTA
   Replaces the duplicated quote form at the foot of the home and service
   pages — same band and rhythm, but it links to /contact/ instead of
   repeating the whole form three times.
   ========================================================================== */
.eb-cta { background: var(--eb-cream); padding-block: var(--eb-space-7); }
.eb-cta__inner { display: flex; flex-direction: column; align-items: center; text-align: center; }
.eb-cta__title {
  font-size: clamp(1.75rem, 3vw, 2.375rem);
  line-height: 1.2;
  margin-bottom: var(--eb-space-2);
}
.eb-cta__lede { max-width: 56ch; color: var(--eb-ink-soft); }
.eb-cta__actions { margin-top: var(--eb-space-4); }
.eb-cta__alt { margin-top: var(--eb-space-3); font-size: .875rem; color: var(--eb-ink-soft); }
.eb-cta__alt a { color: var(--eb-ink); font-weight: 600; }

/* ==========================================================================
   PAGE HEADER (pages with no hero photo)
   Plain ink on white. The masthead above it is normally light-on-dark
   because it overlays a hero photo, so these pages render it in its solid
   state instead (.eb-masthead--solid below). Top padding still has to clear
   the absolutely-positioned masthead, same as the hero does.
   ========================================================================== */
.eb-pagehead {
  /* Clears the absolutely-positioned masthead (~122px) and leaves it room
     to breathe above the title. Mobile keeps its own smaller value below. */
  padding-top: 200px;
  /* No bottom padding: the section that follows brings its own top padding,
     and with the breadcrumb bar gone the two were stacking into a big gap. */
  padding-bottom: 0;
}
.eb-pagehead__title {
  font-size: clamp(2rem, 4vw, 3rem);
  line-height: 1.1;
  letter-spacing: -.02em;
  margin-bottom: var(--eb-space-2);
}
.eb-pagehead__lede { max-width: 56ch; color: var(--eb-ink-soft); font-size: 1.125rem; }

/* Solid masthead — used on pages with no hero photo behind it. The header
   itself reuses the existing .is-solid state (rendered server-side and held
   there by main.js); only the top bar needs its own treatment, since it is
   otherwise white text relying on the hero scrim. */
.eb-masthead--solid .eb-topbar {
  background: var(--eb-white);
  color: var(--eb-ink);        /* 15.9:1 on white */
  /* Both bars are white here, so this hairline is the only thing separating
     the utility strip from the nav below it — it has to stay. */
  border-bottom-color: var(--eb-line);
}
.eb-masthead--solid .eb-topbar a { color: var(--eb-ink); }

/* ==========================================================================
   FAQ ACCORDION
   Built on <details>/<summary>, so expand/collapse, keyboard operation and
   the expanded/collapsed announcement all come from the browser. White band
   so it does not sit cream-on-cream against the CTA below it.
   ========================================================================== */
.eb-faq { background: var(--eb-white); padding-block: var(--eb-space-7); }
/* Heading and list share one centred 820px column so they line up on the
   same left edge. Only the column is centred — the text inside stays
   left-aligned. */
.eb-faq__title {
  font-size: clamp(1.625rem, 2.6vw, 2.125rem);
  margin-bottom: var(--eb-space-4);
  max-width: 820px;
  margin-inline: auto;
}
.eb-faq__list { max-width: 820px; margin-inline: auto; }
.eb-faq__item { border-top: 1px solid var(--eb-line); }
.eb-faq__item:last-child { border-bottom: 1px solid var(--eb-line); }

.eb-faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--eb-space-3);
  min-height: 60px;
  padding-block: var(--eb-space-3);
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--eb-ink);
  cursor: pointer;
  list-style: none;                 /* drop the default disclosure triangle */
}
.eb-faq__q::-webkit-details-marker { display: none; }
.eb-faq__icon { flex: none; width: 20px; height: 20px; transition: transform .28s ease; }
.eb-faq__item[open] .eb-faq__icon { transform: rotate(180deg); }

/* Smooth open/close via ::details-content + interpolate-size (Chrome 131+,
   Safari 18.2+, Firefox nightly as of early 2026). The @supports guard
   means older browsers get the browser's default instant toggle — still
   functional, just without the ease.
   `content-visibility` needs `allow-discrete` because it changes between
   two non-animatable keywords across the state boundary.
   `prefers-reduced-motion` matches the sitewide motion rule further down. */
@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }

  .eb-faq__item::details-content {
    block-size: 0;
    overflow-y: clip;
    transition:
      block-size .28s ease,
      content-visibility .28s ease allow-discrete;
  }
  .eb-faq__item[open]::details-content { block-size: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .eb-faq__item::details-content { transition: none; }
  .eb-faq__icon { transition: none; }
}

.eb-faq__a { padding-bottom: var(--eb-space-4); max-width: 68ch; }
.eb-faq__a p { color: var(--eb-ink-soft); }
.eb-faq__a p + p,
.eb-faq__a ul + p { margin-top: var(--eb-space-3); }
.eb-faq__a ul { margin-top: var(--eb-space-2); margin-bottom: 0; }

/* ==========================================================================
   MAP (contact page)
   The frame keeps a fixed ratio so the lazily-loaded iframe cannot shift
   the page as it arrives.
   ========================================================================== */
.eb-map__frame {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--eb-cream);
  overflow: hidden;
}
.eb-map__frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
/* Shown until an API key exists — same footprint as the live map so the
   layout does not move when one is added. */
.eb-map__placeholder {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--eb-space-3);
  aspect-ratio: 4 / 3;
  padding: var(--eb-space-5);
  background: var(--eb-cream);
}
.eb-map__address { font-size: 1.125rem; color: var(--eb-ink); line-height: 1.5; }

@media (max-width: 640px) {
  .eb-map__placeholder { padding: var(--eb-space-4); }
}

/* ==========================================================================
   GALLERY
   Caption sits under the photo here, unlike the homepage tiles which
   overlay it on a scrim — so no contrast scrim is involved, just ink on
   white.
   ========================================================================== */
.eb-gallery { padding-top: var(--eb-space-4); padding-bottom: var(--eb-space-7); }
.eb-gallery__filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding-bottom: var(--eb-space-4);
  margin-bottom: var(--eb-space-5);
  border-bottom: 1px solid var(--eb-line);
}
.eb-gallery__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--eb-space-5) var(--eb-space-3);
}
.eb-project__media { overflow: hidden; margin-bottom: var(--eb-space-2); }
.eb-project__open {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}
.eb-project__img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform .4s ease;
}
.eb-project:hover .eb-project__img { transform: scale(1.04); }
/* Caption = eyebrow (location) over a larger, bold service line. Also used by
   the before/after captions on the service pages so a project reads the same
   way wherever it appears. Eyebrow ink is --eb-ink-soft, 9.4:1 on white. */
.eb-project__eyebrow {
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--eb-ink-soft);
  margin-bottom: 4px;
}
.eb-project__title { font-size: 1.125rem; font-weight: 700; line-height: 1.25; }

@media (max-width: 860px) {
  .eb-gallery__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .eb-gallery__grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- Lightbox -----------------------------------------------------
   A <dialog>, so the browser supplies the focus trap, Escape handling and
   ::backdrop dimming. Note the [open] guard on display: a bare
   `display: flex` would override the UA's `dialog:not([open]) {display:none}`
   and leave the viewer on screen permanently.
   ------------------------------------------------------------------------ */
.eb-lightbox {
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: hidden;
}
.eb-lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}
.eb-lightbox::backdrop { background: rgba(0, 0, 0, .88); }

.eb-lightbox__figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--eb-space-3);
  padding: var(--eb-space-3);
}
/* No width/height set, so the image never scales past its natural 800px —
   upscaling these would only soften them. */
.eb-lightbox__img {
  max-width: min(100%, 92vw);
  max-height: 74vh;
  object-fit: contain;
}
.eb-lightbox__caption {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--eb-white);
  text-align: center;
}
/* Same order as the tile: eyebrow above, bold service beneath. */
.eb-lightbox__eyebrow {
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #D8D5C9;              /* 10.8:1 on the dimmed backdrop */
}
.eb-lightbox__service { font-size: 1.25rem; font-weight: 700; }
.eb-lightbox__count {
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .12em;
  color: #B8B5A8;
}

.eb-lightbox__close,
.eb-lightbox__nav {
  position: absolute;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  background: rgba(255, 255, 255, .1);
  border: 1px solid rgba(255, 255, 255, .35);
  border-radius: 50%;
  color: var(--eb-white);
  cursor: pointer;
  transition: background-color .2s ease, border-color .2s ease;
}
.eb-lightbox__close:hover,
.eb-lightbox__nav:hover { background: rgba(255, 255, 255, .22); border-color: #fff; }
.eb-lightbox__close svg { width: 20px; height: 20px; }
.eb-lightbox__nav svg { width: 22px; height: 22px; }
.eb-lightbox__close { top: var(--eb-space-3); right: var(--eb-space-3); }
.eb-lightbox__nav--prev { left: var(--eb-space-3); }
.eb-lightbox__nav--next { right: var(--eb-space-3); }
.eb-lightbox { --eb-focus: #FFFFFF; }

@media (max-width: 560px) {
  /* Arrows move off the photo and under it, where thumbs can reach them. */
  .eb-lightbox__nav { top: auto; bottom: var(--eb-space-3); }
  .eb-lightbox__img { max-height: 62vh; }
}

/* ==========================================================================
   CONTACT
   ========================================================================== */
.eb-contact { background: var(--eb-white); padding-top: var(--eb-space-5); padding-bottom: var(--eb-space-7); }
.eb-contact__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--eb-space-6) var(--eb-space-5);
}
.eb-contact__details { flex: 1 1 280px; }
.eb-contact__map { flex: 1 1 420px; }
/* The form moved out of the row and below it, so it keeps its own
   620px measure rather than stretching the full container. */
.eb-contact__formblock { margin-top: var(--eb-space-6); }
.eb-contact__heading { font-size: 1.25rem; font-weight: 600; margin-bottom: var(--eb-space-3); }
.eb-contact__list { display: flex; flex-direction: column; gap: var(--eb-space-3); }
.eb-contact__label {
  display: block;
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--eb-ink-soft);
  margin-bottom: 2px;
}
.eb-contact__value { font-size: 1rem; color: var(--eb-ink); text-decoration: none; }
a.eb-contact__value:hover { text-decoration: underline; }
/* The form carries its own top margin for the stacked page layout it used
   to live in; inside the contact column the heading already provides it. */
.eb-contact__formblock .eb-form { margin-top: var(--eb-space-3); }

/* ==========================================================================
   FOOTER
   ========================================================================== */
.eb-footer {
  background: #222222;
  color: var(--eb-white);
  padding-top: var(--eb-space-6);
  --eb-focus: #FFFFFF;         /* the ink focus ring is invisible on this band */
}
/* The wordmark and headings inherit --eb-ink from the global h1-h3 rule,
   so both need resetting here. The mark itself is swapped to the white SVG
   in footer.njk — the black one would vanish. */
.eb-footer .eb-brand__word,
.eb-footer__heading { color: var(--eb-white); }
.eb-footer__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--eb-space-5) var(--eb-space-4);
  padding-bottom: var(--eb-space-6);
}
.eb-footer__brand { flex: 1 1 260px; display: flex; flex-direction: column; gap: var(--eb-space-2); }
.eb-footer__tagline { font-size: .875rem; color: #B8B5A8; max-width: 24ch; }  /* 7.7:1 */

.eb-footer__col { flex: 1 1 150px; }
.eb-footer__heading {
  font-size: .9375rem;
  font-weight: 600;
  margin-bottom: var(--eb-space-2);
}
.eb-footer__col ul { display: flex; flex-direction: column; gap: 2px; }
.eb-footer__col a,
.eb-footer__contact li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  font-size: .875rem;
  color: #D8D5C9;              /* 10.8:1 on #222222 */
  text-decoration: none;
}
.eb-footer__col a:hover { color: var(--eb-white); text-decoration: underline; }
/* currentColor, so these automatically follow each item's colour (or its
   hover colour) — no separate override needed. */
.eb-footer__icon { width: 15px; height: 15px; flex: none; }

.eb-social { flex-direction: row !important; gap: 4px !important; margin-top: var(--eb-space-2); }
.eb-social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  color: #D8D5C9;
}
.eb-social svg { width: 22px; height: 22px; }
.eb-social a:hover { color: var(--eb-white); }

/* Legal strip ------------------------------------------------------------ */
/* Shares the footer's band — the rule above it is what separates the two,
   not a fill, so that rule has to be light now rather than --eb-line. */
.eb-legal {
  color: #D8D5C9;                /* 10.8:1 on #222222 */
  border-top: 1px solid var(--eb-line-dark);
  font-size: .75rem;
}
.eb-legal__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--eb-space-1) var(--eb-space-3);
  padding-block: var(--eb-space-2);
}
.eb-legal__links { display: flex; flex-wrap: wrap; gap: var(--eb-space-2); }
.eb-legal a {
  color: var(--eb-white);
  text-decoration: none;
  display: inline-flex; align-items: center; min-height: 28px;
}
.eb-legal a:hover { text-decoration: underline; }

/* ==========================================================================
   SERVICE PAGE SECTIONS
   These only carry band colour, padding and headings now — the layouts
   themselves come from the shared .eb-card-grid / .eb-media-row primitives
   near the top of this file, which both service pages and the homepage use.
   ========================================================================== */
.eb-feature-section { padding-block: var(--eb-space-7); }
.eb-feature-section--cream { background: var(--eb-cream); }
.eb-feature-section__title {
  font-size: clamp(1.625rem, 2.6vw, 2.125rem);
  margin-bottom: var(--eb-space-5);
}
.eb-feature-card { display: flex; flex-direction: column; }
.eb-feature-card__img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  margin-bottom: var(--eb-space-3);
}
.eb-feature-card__title { font-size: 1.0625rem; font-weight: 600; margin-bottom: var(--eb-space-2); }
.eb-feature-card__text { font-size: .9375rem; color: var(--eb-ink-soft); }

/* ==========================================================================
   TESTIMONIAL SERVICE LABEL
   Reuses .eb-slide__place's already-verified colour (5.0:1 on --eb-sage)
   rather than a new, unverified shade. Now larger than the location line
   above it (13px vs 11px) — sized up twice for legibility. The location
   still reads as the stronger label because it is uppercase, 600 weight
   and tracked out, while this stays sentence case at 400.
   ========================================================================== */
.eb-slide__service {
  font-size: .8125rem;
  font-weight: 400;
  color: #F5F4EF;
  letter-spacing: .02em;
  text-transform: none;
  margin-top: 1px;
}

/* ==========================================================================
   SERVICE PAGE — BEFORE / AFTER COMPARE SLIDER
   "After" is the full-size base layer; "before" sits on top, clipped with
   clip-path to [0, pos%] so it reveals from the left — that keeps Before
   on the left / After on the right, matching their tag positions, and
   avoids shrinking a wrapper's width (which would squeeze the inner
   image as the reveal narrows). A native range input is the source of
   truth (free keyboard support + a stable focus state); the frame also
   accepts pointer drag directly, both paths converge on
   the same --eb-compare-pos custom property. See main.js.
   ========================================================================== */
.eb-compare-section { background: var(--eb-white); padding-block: var(--eb-space-7); }
/* Heading and slider share one centred 860px column, so they line up on the
   same left edge instead of the widget hugging the container. Text inside
   stays left-aligned — only the column is centred. */
.eb-compare-section__title {
  font-size: clamp(1.625rem, 2.6vw, 2.125rem);
  margin-bottom: var(--eb-space-4);
  max-width: 860px;
  margin-inline: auto;
}
.eb-compare { max-width: 860px; margin-inline: auto; --eb-compare-pos: 50%; }
.eb-compare__frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border-radius: var(--eb-radius);
  cursor: ew-resize;
  touch-action: pan-y;
}
.eb-compare__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}
.eb-compare__img--before {
  clip-path: inset(0 calc(100% - var(--eb-compare-pos)) 0 0);
}
.eb-compare__tag {
  position: absolute;
  top: 16px;
  padding: 6px 14px;
  background: rgba(34,34,34,.72);
  color: var(--eb-white);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  border-radius: 999px;
  pointer-events: none;
}
.eb-compare__tag--before { left: 16px; }
.eb-compare__tag--after { right: 16px; }
.eb-compare__divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--eb-compare-pos);
  width: 3px;
  background: var(--eb-white);
  box-shadow: 0 0 0 1px rgba(20,21,15,.25);
  transform: translateX(-50%);
  pointer-events: none;
}
.eb-compare__grip {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--eb-white);
  color: var(--eb-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 10px rgba(20,21,15,.25);
}
.eb-compare__caption { margin-top: var(--eb-space-3); }
.eb-compare__range {
  display: block;
  width: 100%;
  margin-top: var(--eb-space-3);
  accent-color: var(--eb-ink);
  cursor: ew-resize;
}

/* ==========================================================================
   QUOTE DRAWER
   Slides in from the right with a tinted overlay. .is-open drives both the
   panel slide and the background fade so enter and exit animations play.
   <dialog>.showModal() provides the focus trap, Escape and aria-modal.
   ========================================================================== */
/* ==========================================================================
   QUOTE MODAL
   Centered dialog — works with showModal()'s natural centering rather than
   fighting it. Appears with a quick fade + scale; exits in reverse.
   ========================================================================== */
.eb-drawer {
  width: min(580px, 92vw);
  border: none;
  border-radius: 6px;
  padding: var(--eb-space-5) var(--eb-space-4);
  background: var(--eb-white);
  box-shadow: 0 8px 48px rgba(0, 0, 0, .22);
}

.eb-drawer::backdrop { background: rgba(0, 0, 0, .55); }

.eb-drawer[open] {
  animation: eb-modal-in .22s cubic-bezier(.2, .8, .4, 1) both;
}

.eb-drawer.is-closing {
  animation: eb-modal-out .16s ease forwards;
}

@keyframes eb-modal-in  {
  from { opacity: 0; transform: scale(.96) translateY(-6px); }
  to   { opacity: 1; transform: scale(1)   translateY(0); }
}

@keyframes eb-modal-out {
  from { opacity: 1; transform: scale(1)   translateY(0); }
  to   { opacity: 0; transform: scale(.96) translateY(-6px); }
}

.eb-drawer__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--eb-space-2);
  margin-bottom: var(--eb-space-3);
}
.eb-drawer__title { font-size: 1.375rem; font-weight: 600; }
.eb-drawer__intro { font-size: .9375rem; color: var(--eb-ink-soft); margin-top: 4px; }

.eb-drawer__close {
  flex: none;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: var(--eb-radius);
  color: var(--eb-ink-soft);
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.eb-drawer__close:hover { background: var(--eb-cream); color: var(--eb-ink); }

.eb-drawer .eb-form { max-width: none; margin-top: 0; }

.eb-drawer__alt {
  font-size: .875rem;
  color: var(--eb-ink-soft);
  border-top: 1px solid var(--eb-line);
  padding-top: var(--eb-space-3);
  margin-top: var(--eb-space-3);
}
.eb-drawer__alt a { color: var(--eb-ink); font-weight: 500; text-underline-offset: 3px; }
.eb-drawer__alt a:hover { text-decoration: none; }

@media (max-width: 480px) {
  .eb-drawer { padding: var(--eb-space-4) var(--eb-space-3); }
  .eb-drawer .eb-form__row { flex-direction: column; }
}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 1080px) {
  .eb-nav,
  .eb-header__cta { display: none; }
  .eb-menu-toggle { display: inline-flex; }
}

@media (max-width: 860px) {
  :root { --eb-space-7: 64px; --eb-space-6: 48px; }
  .eb-hero { min-height: 540px; }
  .eb-hero::before { background-position: 68% center; }
  .eb-header.is-stuck { animation: none; }   /* no slide on touch scroll */
  /* Below 860px the hero copy spans nearly the full width, so the horizontal
     scrim stays even across it. The mobile crop shows the full image height
     including the bright ceiling, so it runs denser than the desktop stops.
     The vertical layer is repeated unchanged — overriding background-image
     replaces the whole stack. */
  .eb-hero::after {
    background-image:
      linear-gradient(to bottom,
        rgba(12,13,9,.63)   0px,
        rgba(12,13,9,.60)   50px,
        rgba(12,13,9,.3924) 118px,
        rgba(12,13,9,.2403) 190px,
        rgba(12,13,9,.0765) 280px,
        rgba(12,13,9,0)     360px),
      linear-gradient(90deg,
        rgba(12,13,9,.60)  0%,
        rgba(12,13,9,.578) 55%,
        rgba(12,13,9,.548) 100%);
  }
}

@media (max-width: 640px) {
  .eb-container { padding-inline: 20px; }
  .eb-topbar__inner { justify-content: center; text-align: center; }
  .eb-topbar__left { flex: 1 1 100%; justify-content: center; }
  .eb-topbar__right { flex: 1 1 100%; justify-content: center; }
  .eb-header__inner { min-height: 68px; }
  /* top bar wraps to two rows here, so both banner types need more clearance */
  .eb-hero,
  .eb-pagehead { padding-top: 184px; }
  .eb-brand__word { font-size: .9375rem; letter-spacing: .1em; }
  .eb-hero__actions .eb-btn { flex: 1 1 100%; }
  .eb-intro__actions .eb-btn { flex: 1 1 100%; }
  .eb-form__submit { align-self: stretch; }
  .eb-featured__row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .eb-legal__inner { justify-content: flex-start; }
}

@media (max-width: 420px) {
  .eb-brand__mark { width: 30px; }
  .eb-menu-toggle__label { display: none; }
}

/* ==========================================================================
   MOTION PREFERENCES
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .eb-tile__link:hover img { transform: none; }
  .eb-project:hover .eb-project__img { transform: none; }
}

/* ==========================================================================
   PRINT
   ========================================================================== */
@media print {
  .eb-topbar, .eb-menu-toggle, .eb-mobile-menu, .eb-carousel__controls { display: none !important; }
  .eb-hero::before { background: none; }
  body { color: #000; }
}
