/* =====================================================================
   cerpabuilds.com — landing page components

   Surface: Decide/Learn. One idea per section, hero is justified here
   (it is the conversion page for a homeowner). Composition is NOT the
   generic centered-hero + three-equal-cards default: the hero is a
   split (copy left / contact rail right), and the service pillars are
   a ruled list rather than floating tiles.
   ===================================================================== */

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

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--step-body);
  line-height: 1.65;
  color: var(--body);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; display: block; }
a { color: var(--navy-mid); }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--ink);
  line-height: 1.15;
  margin: 0 0 var(--sp-3);
  font-weight: 600;
  text-wrap: balance;
}
h1 { font-size: var(--step-hero); letter-spacing: -0.02em; }
h2 { font-size: var(--step-h2); letter-spacing: -0.01em; }
h3 { font-size: var(--step-h3); font-family: var(--font-body); font-weight: 600; }

p { margin: 0 0 var(--sp-4); }

:focus-visible {
  outline: 3px solid var(--orange);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

/* -- Layout helpers ------------------------------------------------ */
.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 var(--sp-5); }
.wrap-narrow { max-width: var(--wrap-narrow); }
.section { padding: var(--sp-8) 0; }
.section--alt { background: var(--surface-alt); }
.section--warm { background: var(--surface-warm); }
.section--navy { background: var(--navy); color: rgba(255,255,255,0.82); }
.section--navy h2, .section--navy h3 { color: #fff; }
.section--navy a { color: #fff; }

.section--divider {
  padding: var(--sp-12) 0;
  background: transparent;
}

.eyebrow {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--orange);
  margin-bottom: var(--sp-3);
  display: block;
}
.eyebrow--muted { color: var(--muted); }

.lead { font-size: var(--step-lead); color: var(--muted); line-height: 1.55; }
.section--navy .lead { color: rgba(255,255,255,0.7); }

/* =====================================================================
   Top bar — brand + phone. Sticky, minimal.
   ===================================================================== */
.topbar {
  position: sticky; top: 0; z-index: 50;
  background: rgba(255,255,255,0.96);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.topbar__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); min-height: 68px;
}
.brand {
  display: flex; align-items: center; gap: var(--sp-2);
  font-family: var(--font-display); font-size: 1.3rem; font-weight: 600;
  color: var(--navy); text-decoration: none; letter-spacing: -0.01em;
}
/* The real logo (downloaded from cerpaconstruction.com) rather than a
   re-typeset wordmark. Intrinsic ratio 2694x1176; the width/height
   attributes on the <img> reserve the space so the bar does not jump. */
.brand__logo { width: 180px; height: auto; display: block; }
.brand__logo--light { width: 200px; }
.brand span { font-family: var(--font-body); font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }

.topbar__phone {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-weight: 600; color: var(--navy); text-decoration: none;
  font-size: 1.02rem; white-space: nowrap;
}
.topbar__phone svg { width: 18px; height: 18px; stroke: var(--orange); flex: none; }

.topbar__actions {
  display: flex; align-items: center; gap: var(--sp-4);
}
@media (max-width: 560px) {
  .brand span { display: none; }
  .topbar__actions { gap: var(--sp-3); }
}

/* =====================================================================
   Hero — split: promise on the left, contact rail on the right.
   ===================================================================== */
.hero { padding: var(--sp-8) 0 var(--sp-7); background: var(--surface); }

/* =====================================================================
   Photo hero — a standalone page opening whose backdrop is a
   photograph. Same construction as `.hero--home`: the image sits on a
   `::before` layer at `z-index: -2` so the copy can sit on the section
   surface, and the URL arrives as the `--hero-bg` custom property from
   the HTML (with the asset hash appended), never written into this
   stylesheet — a stylesheet-pinned URL is served from Cloudflare's
   four-hour cache and the browser keeps the OLD photograph.

   The difference from `.hero--home`: this one is a panel, not the whole
   first screen. It stops at 70vh so the section below is visible in the
   same viewport, and the copy is left-aligned in a single column.

   Legibility: a dark gradient veil from the left edge carries the
   white type over the photograph. The veil is a gradient, not a flat
   wash, so the right side of the picture stays readable as a picture.
   ===================================================================== */
.hero--photo {
  position: relative;
  isolation: isolate;
  min-height: 70vh;
  display: flex;
  align-items: center;
  padding: clamp(var(--sp-8), 6vh, 5rem) 0;
  background: var(--navy-deep);
  overflow: hidden;
}
.hero--photo::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  background-image: var(--hero-bg, url('/assets/projects/hero-bg.webp'));
  background-size: cover;
  background-position: center 35%;
  background-repeat: no-repeat;
}
.hero--photo::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(100deg,
              rgba(13, 40, 71, .88) 0%,
              rgba(13, 40, 71, .70) 38%,
              rgba(13, 40, 71, .28) 68%,
              rgba(13, 40, 71, .10) 100%);
}
.hero--photo .hero__copy { color: #fff; max-width: 46ch; }
.hero--photo h1 { color: #fff; }
.hero--photo .eyebrow {
  color: #ffd7a3;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .9), 0 2px 8px rgba(0, 0, 0, .7);
}
.hero--photo .lead {
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .85), 0 2px 10px rgba(0, 0, 0, .65);
}
.hero--photo .hero__copy h1 {
  text-shadow: 0 2px 6px rgba(0, 0, 0, .9), 0 4px 16px rgba(0, 0, 0, .7);
}
@media (max-width: 900px) {
  .hero--photo { min-height: 60vh; }
  .hero--photo::after {
    background: linear-gradient(180deg,
                rgba(13, 40, 71, .85) 0%,
                rgba(13, 40, 71, .62) 55%,
                rgba(13, 40, 71, .55) 100%);
  }
}

/* =====================================================================
   The home hero carries a background photograph of a finished home
   interior (2026-10-26). Jessi: "there should be a background image of
   the inside of a house ... with the letters in the same position, only
   the background is seen".

   The copy stays exactly where it was — centred, same size, same order.
   Only the backdrop changes, so the section is a background layer behind
   the existing copy rather than a new layout.

   The photograph is GENERATED. The 38-photo portfolio has no residential
   interior worth using here (the two `mimosa-after` interiors are staged
   living rooms already used elsewhere, and the rest are construction
   shells). The hero is an invitation to imagine a finished home, not a
   portfolio claim, so it carries no per-image disclaimer — the same
   framing rule the "Live it yourself" section follows.

   LEGIBILITY: the headline is white serif at up to 70px over a
   photograph, so a veil is not optional. Two layers do the work — a
   vertical gradient that darkens the top and bottom bands where the
   eyebrow and the paragraph sit, and a radial wash centred on the
   headline. The veil is sized in `em` off the h1 so it scales with the
   type instead of being pinned to pixel values that drift between
   breakpoints.
   ===================================================================== */
.hero--home {
  position: relative;
  isolation: isolate;
  /* Jessi (2026-10-26): "make the image bigger so it covers the whole
     page, so the white underneath is not visible." The hero is now a TRUE
     full-bleed panel: it fills the viewport height minus the sticky
     topbar, so the photograph is the whole first screen and the white
     band of the section below never shows.

     100dvh is the dynamic viewport height — it tracks the mobile URL bar
     as it collapses on scroll, so the hero does not jump or leave a gap.
     The 100vh fallback covers browsers without dvh support, and the
     calc() subtracts the topbar so the copy never hides behind it.

     The copy keeps its place: it is vertically centred in the panel, so
     it grows with the section instead of sticking to a fixed offset. */
  min-height: calc(100vh - 78px);
  min-height: calc(100dvh - 78px);
  display: flex;
  align-items: center;
  padding: clamp(var(--sp-8), 6vh, 5rem) 0;
  background: var(--navy-deep);
  overflow: hidden;
}
.hero--home .hero__grid { width: 100%; }
.hero--home::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  /* The URL arrives as a custom property from the HTML, with the image
     content hash appended. A background-image written straight into this
     stylesheet keeps a stable URL, and Cloudflare Pages caches it for
     four hours (max-age=14400) — so the browser kept serving the PREVIOUS
     photograph even though the new one was live on the server. The HTML is
     served with max-age=0, so the hash always reaches the browser. */
  background-image: var(--hero-bg, url('/assets/projects/hero-bg.webp'));
  background-size: cover;
  /* 30% puts the headline band on the plain melon wall of the photograph
     rather than on the lit ceiling or the pale oak floor. The image was
     generated with a bare wall for exactly this reason. */
  background-position: center 30%;
  background-repeat: no-repeat;
}
/* NO VEIL. Jessi (2026-10-26): "it should not carry a dark veil, the
   idea is that the background image can be noticed ... remove what
   darkens it". The previous version put a radial + linear gradient over
   the whole photograph, which made it read as a dark blur.

   Legibility is carried by the PHOTOGRAPH instead: the background is
   shot at dusk with real interior shadow across the middle of the
   frame, so white type sits on genuine shadow rather than on a graphic
   overlay. Only a soft text-shadow remains, which darkens the letter
   edges and not the image behind them. */
.hero--home::after { content: none; }
.nowrap { white-space: nowrap; }
/* The wall is a deep CERPA NAVY (#142943), so the copy must be WHITE with
   a strong dark halo. White on deep navy measures ~1.4:1 without shadow,
   so the text-shadow is essential — it carries the contrast. */
.hero--home .hero__copy { color: #fff; }
.hero--home h1 { color: #fff; }
.hero--home .eyebrow {
  color: #ffd7a3;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .9), 0 2px 8px rgba(0, 0, 0, .7);
}
.hero--home .lead {
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .85), 0 2px 10px rgba(0, 0, 0, .65);
}
.hero--home .hero__copy h1 {
  text-shadow: 0 2px 6px rgba(0, 0, 0, .9), 0 4px 16px rgba(0, 0, 0, .7);
}


.hero__grid {
  display: grid; gap: var(--sp-7);
  grid-template-columns: minmax(0, 1.25fr) minmax(320px, 0.85fr);
  align-items: start;
}
@media (max-width: 900px) { .hero__grid { grid-template-columns: 1fr; gap: var(--sp-6); } }

/* The home hero has no contact rail — the location card was removed on
   2026-10-23. The grid still declares two columns, so the copy would sit
   in the left track with ~420px of dead space beside it. Collapsed the
   grid to one column and centred it on Jessi's request.

   The headline is set on one line. Measured: "Renovation work done right,
   once." needs 1139px at the display size of 4.75rem, and the wrap only
   offers 1092px of content width, so it wrapped to two lines. The home
   h1 is therefore capped at 4.375rem (70px), which measures 1049px and
   fits with room to spare. The clamp slope keeps it on one line down to
   roughly 840px of viewport; below that it wraps normally. */
.hero__grid:has(> .hero__copy--wide:only-child) { grid-template-columns: minmax(0, 1fr); }
.hero__copy--wide {
  max-width: none;
  text-align: center;
}
.hero__copy--wide h1 {
  font-size: clamp(2.5rem, 1.05rem + 3.9vw, 4.375rem);
  margin-left: auto;
  margin-right: auto;
}
.hero__copy--wide .lead {
  max-width: 56ch;
  margin-left: auto;
  margin-right: auto;
}
.hero__copy--wide .hero__actions { justify-content: center; }
@media (max-width: 900px) {
  .hero__grid:has(> .hero__copy--wide:only-child) { grid-template-columns: 1fr; }
  .hero__copy--wide { max-width: none; }
}

.hero h1 em { font-style: normal; color: var(--orange); }
.hero__proof {
  margin-top: var(--sp-5); padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: var(--sp-5) var(--sp-6);
}
.hero__proof div { min-width: 130px; }
.hero__proof dt { font-size: 0.76rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; margin-bottom: 2px; }
.hero__proof dd { margin: 0; font-family: var(--font-display); font-size: 1.28rem;
  color: var(--ink); font-weight: 600; }

/* -- Accessibility helpers ---------------------------------------- */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--navy); color: #fff; padding: 12px 20px;
  border-radius: 0 0 var(--r-md) 0; font-weight: 600; text-decoration: none;
}
.skip-link:focus { left: 0; }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* =====================================================================
   Photo band — the opening section of a landing page is a photograph.

   Jessi asked for this on the bathroom landing on 2026-10-26: the
   picture is NOT the hero's background, it is its own full-bleed
   section that opens the page and carries the eyebrow and the
   headline. The split hero underneath keeps the page's clean surface
   and the lead form keeps its own light card, like every other
   service page.

   The band is a WIDE crop (21:9 on desktop, 4:5 on a phone) so the
   bathroom reads as a room in every direction rather than a strip of
   wall. The copy sits over the frame's own dark shower alcove — that
   tile measures L=34 against the headline, which is 10.4:1 with white
   type — so there is no scrim and no colour wash over the picture.
   The soft halo on the letters is the same one Jessi's own reference
   mockup uses.
   ===================================================================== */
.photoband {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--navy-deep);
  color: #fff;
  /* Jessi asked on 2026-10-26 for the whole frame to be visible: 'haz la
     imagen principal mas grande que se vea todo el cuadro completo'.

     The band had a FIXED height while its width followed the viewport, so
     on a 1920px screen the 2.36:1 frame was cover-cropped by 31% of its
     height — the shower head was cut off at the top and the vanity was cut
     at the right edge. Matching the band's aspect-ratio to the frame's
     makes the crop exactly zero, so the entire photograph is on screen at
     every width, and the band grows with the viewport instead of cutting
     the picture.

     max-height only bites on ultra-wide displays, where the band would
     otherwise grow taller than the screen; the image is anchored centre
     so any residual crop is symmetric rather than lopsided. */
  aspect-ratio: 1584 / 672;
  max-height: 92svh;
  display: flex;
  align-items: flex-end;
}
.photoband__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.photoband__media picture,
.photoband__media img {
  display: block;
  width: 100%;
  height: 100%;
}
.photoband__media img {
  object-fit: cover;
  /* The dark shower alcove is on the LEFT of the frame and the bright
     room on the right, so the picture is anchored left: the tile stays
     under the copy and the room stays in the open right-hand side. */
  object-position: 0% 50%;
}
.photoband__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1584px;
  margin-left: 0;
  padding-left: calc(var(--sp-5) + 80px);
  padding-bottom: var(--sp-7);
  box-sizing: border-box;
}
.photoband__copy {
  max-width: 42ch;
  text-align: left;
  margin-top: -60px;
}
.photoband h1 {
  color: #fff;
  font-size: clamp(2.4rem, 1.1rem + 3.6vw, 4.25rem);
  margin: 0;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.8),
    0 2px 8px rgba(0, 0, 0, 0.7),
    0 6px 24px rgba(0, 0, 0, 0.6),
    0 0 0 1px rgba(0, 0, 0, 0.6);
}
.photoband .eyebrow {
  /* The stock orange measures 3.25:1 on the photograph's dark tile. A
     pale amber clears AA for 12px text while still reading as the
     brand orange. */
  color: #ffd7a3;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.8),
    0 2px 8px rgba(0, 0, 0, 0.7);
}
.photoband h1,
.photoband .eyebrow {
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.8),
    0 2px 8px rgba(0, 0, 0, 0.7),
    0 6px 24px rgba(0, 0, 0, 0.6),
    0 0 0 1px rgba(0, 0, 0, 0.6);
}

@media (max-width: 900px) {
  .photoband {
    /* The phone frame is 4:5, so the band matches that ratio and the
       whole portrait is visible too. */
    aspect-ratio: 928 / 1152;
    max-height: 92svh;
    /* On a phone the frame is a tall portrait with the dark shower at
       the top and the bright room below, so the copy is anchored to
       the TOP of the band and the picture to the top of the frame. */
    align-items: flex-start;
  }
  .photoband__media img {
    object-position: 50% 0%;
  }
  .photoband__inner {
    padding-top: var(--sp-6);
    padding-bottom: 0;
  }
  .photoband h1 {
    font-size: clamp(2rem, 7.2vw, 2.6rem);
  }
}

/* =====================================================================
   Hero actions — inline pair that wraps on narrow screens.
   ===================================================================== */
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-5); }

.section--warm ul[style] a {
  display: inline-block; padding: 0.6rem 1.1rem; border: 1px solid var(--line);
  border-radius: var(--r-pill); background: var(--surface); font-weight: 600;
  text-decoration: none; color: var(--navy); transition: border-color .16s ease, background .16s ease;
}
.section--warm ul[style] a:hover { border-color: var(--orange); background: #fff8ef; }
.section--warm ul[style] { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: .75rem; }

/* -- Contact rail (the conversion block) ---------------------------- */
.rail {
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  box-shadow: var(--shadow-md);
}
.rail h2 { font-size: 1.4rem; margin-bottom: var(--sp-2); }
.rail__note { font-size: 0.9rem; color: var(--muted); margin-bottom: var(--sp-4); }

.choice { margin-bottom: var(--sp-4); }
.choice__label {
  display: block; font-weight: 600; font-size: 0.92rem;
  color: var(--ink); margin-bottom: var(--sp-2);
}
.choice__label .req { color: var(--orange); margin-left: 3px; }

.contact-list { list-style: none; margin: 0; padding: 0; }
.contact-list li { margin-bottom: var(--sp-2); }
.contact-list label {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border: 1px solid var(--line);
  border-radius: var(--r-md); cursor: pointer; background: var(--surface);
  font-size: 0.95rem; font-weight: 500; color: var(--ink);
  transition: border-color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
  min-height: 48px;
}
.contact-list label:hover { border-color: var(--navy-mid); }
.contact-list input { position: absolute; opacity: 0; pointer-events: none; }
.contact-list input:checked + label {
  border-color: var(--orange); background: #fff8ef;
  box-shadow: 0 0 0 1px var(--orange) inset;
}
.contact-list input:focus-visible + label { outline: 3px solid var(--orange); outline-offset: 2px; }
.contact-list svg { width: 19px; height: 19px; flex: none; stroke: var(--navy-mid); }
.contact-list .hint {
  display: block; font-size: 0.78rem; font-weight: 400;
  color: var(--muted); margin-top: 2px;
}

/* -- Conditional fields ------------------------------------------- */
/* Neither the phone nor the email field is shown until a contact method
   is picked — an empty form should not ask for a number nobody has
   committed to giving yet. */
.phone-note {
  display: none; margin: var(--sp-3) 0 var(--sp-2);
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md);
  background: #fff8ef; border-left: 3px solid var(--orange);
  font-size: 0.86rem; color: #7a4a00; line-height: 1.5;
}
.phone-note.is-visible { display: block; }
.phone-field, .email-field { display: none; }
.phone-field.is-visible { display: block; }
.email-field.is-visible { display: block; }

.field { margin-bottom: var(--sp-4); }

/* "Add details" disclosure — keeps the rail short enough that the CTA
   button sits above the fold instead of below it. */
.more { margin-bottom: var(--sp-4); }
.more > summary {
  cursor: pointer; list-style: none; font-size: 0.9rem; font-weight: 600;
  color: var(--navy-mid); padding: 10px 0; min-height: 44px;
  display: flex; align-items: center; gap: 6px;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.more > summary::-webkit-details-marker { display: none; }
.more > summary::after {
  content: ""; width: 7px; height: 7px; margin-left: auto; flex: none;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform 0.2s ease;
}
.more[open] > summary::after { transform: rotate(225deg); }
.more__hint { font-weight: 400; color: var(--muted); font-size: 0.82rem; }
.more__body { padding-top: var(--sp-4); }
.more__body .field:last-child { margin-bottom: 0; }
.field label { display: block; font-weight: 600; font-size: 0.9rem;
  color: var(--ink); margin-bottom: var(--sp-2); }
.field label .req { color: var(--orange); }
.field .opt { font-weight: 400; color: var(--muted); font-size: 0.84rem; }

input[type="text"], input[type="tel"], input[type="email"], input[type="date"], select, textarea {
  width: 100%; padding: 12px var(--sp-4); font: inherit; font-size: 0.97rem;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-md);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--navy-mid);
  box-shadow: 0 0 0 3px rgba(44,82,130,0.14);
}
input::placeholder, textarea::placeholder { color: #9aa9ba; }
textarea { resize: vertical; min-height: 96px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  width: 100%; min-height: 52px; padding: 14px var(--sp-5);
  font: inherit; font-weight: 600; font-size: 1.02rem;
  border: 1px solid transparent; border-radius: var(--r-md);
  cursor: pointer; text-decoration: none; text-align: center;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease, transform 0.12s ease;
}
.btn:active { transform: translateY(1px); }
/* `.btn` is width:100% on purpose — inside a form a full-width tap target is
   correct on mobile. In running content that same rule makes a CTA stretch
   the entire column. Use .btn--auto for links in prose/sections. */
.btn--auto { width: auto; padding-inline: var(--sp-6); }
.btn--primary { background: var(--orange); color: #fff; }
.btn--primary:hover { background: var(--orange-dark); }
.btn--outline { background: transparent; color: var(--navy); border-color: var(--navy); }
.btn--outline:hover { background: var(--navy); color: #fff; }
.btn--ghost-light { background: rgba(255,255,255,0.08); color: #fff; border-color: rgba(255,255,255,0.4); }
.btn--ghost-light:hover { background: #fff; color: var(--navy); }
.btn[disabled] { opacity: 0.6; cursor: not-allowed; }
.btn .spinner {
  width: 16px; height: 16px; border: 2px solid rgba(255,255,255,0.4);
  border-top-color: #fff; border-radius: 50%;
  animation: spin 0.7s linear infinite; display: none;
}
.btn.is-busy .spinner { display: block; }
.btn.is-busy .btn__label { opacity: 0.75; }
@keyframes spin { to { transform: rotate(360deg); } }

.form-status { margin-top: var(--sp-3); font-size: 0.88rem; line-height: 1.5; min-height: 20px; }
.form-status[data-tone="error"] { color: #b42318; }
.form-status[data-tone="ok"] { color: #067647; }
.form-status[data-tone="info"] { color: var(--navy-mid); }

/* Blog button in the top bar.
   MUST live after the `.btn` block above (line ~273): both selectors are a
   single class, so source order decides, and `.btn` sets cursor:pointer and
   display:flex. Declared up in the topbar section it silently lost.

   Shipped inert first (2026-10-23) because there was no blog page — /blog/,
   /blog.html, /insights/ and /articles/ all 404'd, and Jessi wanted the
   button visible before the page existed. Now /blog/ is built
   (blogIndexPage), so the button is a real link and gets the normal
   pointer cursor and full opacity.

   The mobile hide stays in this block rather than up in the topbar media
   query, for the same source-order reason: `.btn`'s display:flex would
   otherwise win. */
.topbar__blog { white-space: nowrap; }
@media (max-width: 560px) {
  .topbar__blog { display: none; }
}

/* =====================================================================
   Pillars — a ruled list, not three equal floating tiles.
   ===================================================================== */
.pillars { list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--sp-5); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* Home service grid — Jessi asked for 3 on top and 2 below, with the
   bottom row centred rather than left-aligned under the first two
   columns.

   The trick is a six-track grid: each of the top three cards spans two
   tracks, and the bottom two are placed on tracks 2-3 and 4-5, which
   centres them under the row above. Painting is the fourth card, so it
   sits bottom-left of that pair and Kitchen bottom-right.

   The nth-child border colours above still land on the first three
   cards, which are the ones on the top row. */
.pillars--home { grid-template-columns: repeat(6, 1fr); }
.pillars--home > li:nth-child(1) { grid-column: 1 / span 2; }
.pillars--home > li:nth-child(2) { grid-column: 3 / span 2; }
.pillars--home > li:nth-child(3) { grid-column: 5 / span 2; }
.pillars--home > li:nth-child(4) { grid-column: 2 / span 2; }
.pillars--home > li:nth-child(5) { grid-column: 4 / span 2; }

/* Two per row, last one centred, from tablet down to the phone
   breakpoint where a single column takes over. */
@media (max-width: 1023px) {
  .pillars--home { grid-template-columns: repeat(4, 1fr); }
  .pillars--home > li:nth-child(1) { grid-column: 1 / span 2; }
  .pillars--home > li:nth-child(2) { grid-column: 3 / span 2; }
  .pillars--home > li:nth-child(3) { grid-column: 1 / span 2; }
  .pillars--home > li:nth-child(4) { grid-column: 3 / span 2; }
  .pillars--home > li:nth-child(5) { grid-column: 2 / span 2; }
}
@media (max-width: 620px) {
  .pillars--home { grid-template-columns: 1fr; }
  .pillars--home > li:nth-child(n) { grid-column: 1 / -1; }
}

.pillar {
  border-top: 2px solid var(--navy); padding-top: var(--sp-4);
}
.pillar:nth-child(2) { border-top-color: var(--navy-mid); }
.pillar:nth-child(3) { border-top-color: var(--orange); }
.pillar h3 { margin-bottom: var(--sp-2); }
.pillar p { margin: 0; color: var(--muted); font-size: 0.97rem; }

.section--navy .pillar { border-top-color: rgba(255,255,255,0.5); }
.section--navy .pillar:nth-child(2) { border-top-color: rgba(255,255,255,0.3); }
.section--navy .pillar:nth-child(3) { border-top-color: var(--orange); }
.section--navy .pillar p { color: rgba(255,255,255,0.7); }

/* -- Steps (numbered, single column on mobile) ----------------------- */
.steps { list-style: none; margin: 0; padding: 0; counter-reset: step;
  display: grid; gap: var(--sp-5); grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.steps li { counter-increment: step; position: relative; padding-left: 3.4rem; }
.steps li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute; left: 0; top: 0;
  font-family: var(--font-display); font-size: 1.6rem; font-weight: 600;
  color: var(--orange); line-height: 1;
}
.steps h3 { font-size: 1.02rem; margin-bottom: var(--sp-1); }
.steps p { margin: 0; font-size: 0.94rem; color: var(--muted); }
.section--navy .steps p { color: rgba(255,255,255,0.7); }

/* -- Definition rows (cost / timeline factors) ----------------------- */
.rows { display: grid; gap: 0; border-top: 1px solid var(--line); }
.rows__item {
  display: grid; grid-template-columns: minmax(180px, 0.9fr) 2fr;
  gap: var(--sp-5); padding: var(--sp-4) 0; border-bottom: 1px solid var(--line);
}
@media (max-width: 640px) { .rows__item { grid-template-columns: 1fr; gap: var(--sp-2); } }
.rows__item dt { font-family: var(--font-display); font-weight: 600; color: var(--ink); font-size: 1.06rem; }
.rows__item dd { margin: 0; }
.pillar__term { font-family: var(--font-body); font-size: 1.15rem; font-weight: 700; color: var(--navy-deep); margin: 0 0 var(--sp-2); line-height: 1.3; }
.pillar__def { font-size: 0.92rem; line-height: 1.55; color: var(--muted); margin: 0; }

.note {
  font-size: 0.86rem; color: var(--muted); background: var(--surface-alt);
  border-left: 3px solid var(--line); padding: var(--sp-4); border-radius: 0 var(--r-md) var(--r-md) 0;
}

/* -- FAQ ------------------------------------------------------------ */
.faq { display: grid; gap: var(--sp-3); }
.faq details {
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); overflow: hidden;
}
.faq summary {
  cursor: pointer; padding: var(--sp-4); font-weight: 600; color: var(--ink);
  list-style: none; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  min-height: 48px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; width: 9px; height: 9px; flex: none;
  border-right: 2px solid var(--orange); border-bottom: 2px solid var(--orange);
  transform: rotate(45deg) translateY(-2px); transition: transform 0.2s ease;
}
.faq details[open] summary::after { transform: rotate(225deg) translateY(-2px); }
.faq summary:hover { color: var(--navy-mid); }
.faq__body { padding: 0 var(--sp-4) var(--sp-4); color: var(--muted); font-size: 0.96rem; }
.faq__body p:last-child { margin-bottom: 0; }

/* -- Trust / license strip ------------------------------------------- */
.trust {
  display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-6);
  padding: var(--sp-5) 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.trust div { font-size: 0.9rem; color: var(--muted); }
.trust strong { display: block; color: var(--ink); font-size: 1.02rem; font-family: var(--font-display); }

/* -- Final CTA ------------------------------------------------------ */
.cta-final { background: var(--navy); color: rgba(255,255,255,0.85); padding: var(--sp-8) 0; }
.cta-final h2 { color: #fff; }
.cta-final .lead { color: rgba(255,255,255,0.72); }
.cta-final__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-5); }

/* -- Footer --------------------------------------------------------- */
.footer { background: var(--navy-deep); color: rgba(255,255,255,0.62); padding: var(--sp-7) 0 var(--sp-5); font-size: 0.9rem; }
.footer a { color: #fff; text-decoration: none; }
.footer a:hover { text-decoration: underline; }
.footer__grid { display: grid; gap: var(--sp-5); grid-template-columns: 1.4fr 1fr 1fr; }
@media (max-width: 700px) { .footer__grid { grid-template-columns: 1fr; } }
.footer h4 { color: #fff; font-family: var(--font-body); font-size: 0.8rem;
  letter-spacing: 0.14em; text-transform: uppercase; margin-bottom: var(--sp-3); }
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
.footer__bottom { margin-top: var(--sp-6); padding-top: var(--sp-4); border-top: 1px solid rgba(255,255,255,0.14); font-size: 0.82rem; }
.disclaimer { font-size: 0.78rem; line-height: 1.5; color: rgba(255,255,255,0.45); margin-top: var(--sp-3); }

.pillars .btn--project {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  width: 100%;
  text-align: left;
  padding: var(--sp-3) var(--sp-4);
  font-size: 1rem;
  font-weight: 600;
  border-radius: var(--r-md);
  text-decoration: none;
  gap: var(--sp-2);
  background: var(--orange);
  color: #fff;
  border: 1px solid transparent;
}
.pillars .btn--project:hover {
  background: var(--orange-dark);
}
.pillars .btn--project:focus-visible {
  outline: 3px solid var(--orange);
  outline-offset: 2px;
}
   that emits `class="reveal"` MUST also emit `<script src="/assets/lead.js">
  `. The animation is progressive enhancement and must never be the only
   thing that makes content visible.
   ===================================================================== */
.reveal { opacity: 0; transform: translateY(12px); }
.reveal.is-in { opacity: 1; transform: none; transition: opacity 0.5s ease, transform 0.5s ease; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; }
}

/* Inline SVG icons inherit stroke width from their size attribute. */
.icon { fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; }
/* =====================================================================
   Before/after scroll scrub.

   Native CSS scroll-driven animation — no JavaScript. Runs on the
   compositor in Chrome, Firefox and Safari 26.4+.

   The clip-path inset() reveals AREA, not brightness, so the transition
   never reads as a brightness crossfade.

   CRITICAL: the two images must be the same scene at the same angle. Run
   scripts/align-before-after.py on any pair first — raw photos from the
   main site are NOT aligned and produce a visible jump.

   Markup contract:
     <figure class="ba" style="--ba-ar:16/9">
       <img class="ba__before" src="...-before.webp" alt="...">
       <img class="ba__after"  src="...-after.webp"  alt="...">
       <figcaption class="ba__label ba__label--before">Before</figcaption>
       <figcaption class="ba__label ba__label--after">After</figcaption>
     </figure>
   ===================================================================== */
.ba {
  position: relative;
  overflow: hidden;
  aspect-ratio: var(--ba-ar, 16 / 9);
  background: var(--surface-alt);
  border-radius: var(--r-md);
}
.ba__before,
.ba__after {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.ba__before { z-index: 1; }
.ba__after {
  z-index: 2;
  clip-path: inset(0 0 0 100%);
  animation: ba-reveal linear both;
  animation-timeline: view();
  animation-range: entry 25% cover 55%;
}
@keyframes ba-reveal {
  from { clip-path: inset(0 0 0 100%); }
  to   { clip-path: inset(0 0 0 0); }
}

/* Side-by-side variant of the same before/after pair.
   Jessi switched the bathroom page from the scroll-scrub to this on
   2026-10-23: two static frames read better when the "before" is a
   genuinely wrecked room, because a scrub hides half the evidence at any
   given moment and a visitor who stops scrolling sees only the after. */
.ba--sbs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-6);
  overflow: visible;
  background: none;
}
.ba__col {
  display: flex;
  flex-direction: column;
  position: relative;
}
.ba--sbs .ba__before,
.ba--sbs .ba__after {
  position: static;
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto;
  border-radius: var(--r-md);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  clip-path: none;
  animation: none;
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.ba__col:hover img {
  transform: scale(1.02);
}
.ba--sbs .ba__label {
  position: static;
  justify-self: start;
  align-self: start;
  margin-top: var(--sp-3);
  background: var(--navy);
  color: #fff;
  border-radius: var(--r-pill);
}
@media (max-width: 640px) {
  .ba--sbs { grid-template-columns: 1fr; gap: var(--sp-5); }
  .ba--sbs .ba__label { justify-self: stretch; text-align: center; }
}

.ba__label {
  position: absolute;
  bottom: var(--sp-4);
  z-index: 3;
  margin: 0;
  padding: 0.3rem 0.7rem;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--surface);
  background: rgba(22, 32, 43, 0.72);
  border-radius: var(--r-pill);
  backdrop-filter: blur(6px);
  pointer-events: none;
}
.ba__label--before { left: var(--sp-4); }
.ba__label--after  { right: var(--sp-4); }

/* No scroll-driven animation support: show the finished result outright.
   A static after photo beats a static before photo. */
@supports not (animation-timeline: view()) {
  .ba__after { clip-path: inset(0 0 0 0); }
}

/* Reduced motion: no scrub, show the after frame. Never leave the
   before frame as the only visible one — that misrepresents the work. */
@media (prefers-reduced-motion: reduce) {
  .ba__after {
    animation: none;
    clip-path: inset(0 0 0 0);
  }
}

/* Caption under the figure — the honest note about what is shown. */
.ba-note {
  margin-top: var(--sp-3);
  font-size: 0.85rem;
  color: var(--muted);
}

/* =====================================================================
   Photo strip — real project photos on every service page.

   Every landing page carries at least one image; a page with zero images
   does not convert. `aspect-ratio` + explicit width/height attributes
   reserve the box so the images cannot cause layout shift.
   ===================================================================== */
.shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: var(--sp-6);
  margin-top: var(--sp-6);
  overflow: visible;
}
.shot {
  margin: 0;
  display: flex;
  flex-direction: column;
}
.shot img {
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--r-md);
  background: var(--surface-alt);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.shot:hover img {
  transform: scale(1.03);
}
.shot figcaption {
  margin-top: var(--sp-3);
  font-size: 0.92rem;
  color: var(--muted);
  line-height: 1.5;
}

/* Staggered lateral entrance for Our Work images */
.shot--left.reveal {
  opacity: 0;
  transform: translateX(-50px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
.shot--right.reveal {
  opacity: 0;
  transform: translateX(50px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.15s, transform 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.15s;
}
.shot--left.reveal.is-in,
.shot--right.reveal.is-in {
  opacity: 1;
  transform: translateX(0);
}

/* Lateral entrance for Before & After columns */
.reveal--left {
  opacity: 0;
  transform: translateX(-40px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
.reveal--right {
  opacity: 0;
  transform: translateX(40px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.15s, transform 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.15s;
}
.reveal--left.is-in,
.reveal--right.is-in {
  opacity: 1;
  transform: translateX(0);
}

/* Larger before-after presentation */
.section--ba {
  padding-top: var(--sp-8);
  padding-bottom: var(--sp-8);
  background: var(--surface-alt);
}
.wrap--wide {
  max-width: 1280px;
}

/* =====================================================================
   Hero showcase — compact detail gallery balancing the form height
   ===================================================================== */
.hero-showcase {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-4);
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
}
.hero-showcase__item {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-md);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
  background: var(--surface-alt);
  display: flex;
  flex-direction: column;
}
.hero-showcase__item img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.hero-showcase__item:hover img {
  transform: scale(1.04);
}
.hero-showcase__item span {
  padding: 0.65rem 0.8rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--navy-deep);
  background: #fff;
  border-top: 1px solid var(--line-soft, rgba(0,0,0,0.06));
}
@media (max-width: 600px) {
  .hero-showcase {
    grid-template-columns: 1fr;
  }
}

/* =====================================================================
   Sticky mobile action bar — mobile only.

   The pages are long and the form is at the bottom. On a phone this is
   the only always-available action. Desktop hides it: the header already
   carries the phone number and a second bar competes with the CTA.

   Hidden entirely from assistive tech duplication via aria-label on the
   region; the links themselves stay focusable and readable.
   ===================================================================== */
.stickybar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  display: none;
  gap: 1px;
  background: var(--line);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom, 0);
}
.stickybar__act {
  flex: 1 1 0;
  min-height: 56px;              /* comfortable tap target */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.stickybar__act .icon { width: 20px; height: 20px; }
.stickybar__act--wide {
  flex: 1.4 1 0;
  background: var(--orange);
  color: #fff;
}
/* Only show it where it helps: narrow viewports. */
@media (max-width: 860px) {
  .stickybar { display: flex; }
  /* Keep the last content clear of the bar. */
  .stickybar ~ * { padding-bottom: 0; }
  body { padding-bottom: 58px; }
}
@media (min-width: 861px) {
  .stickybar { display: none; }
}

/* The scrub on a service page sits inside the 1140px wrap. Keep it narrower
   than a full-bleed figure so the wipe reads as a considered editorial object
   rather than a banner, and give it breathing room from the photo strip. */
.work-scrub {
  margin-top: var(--sp-6);
  aspect-ratio: 16 / 9;
  box-shadow: var(--shadow-lg);
}
.work-scrub + .ba-note { margin-top: var(--sp-4); }
@media (max-width: 860px) {
  .work-scrub { aspect-ratio: 4 / 3; }
}

/* =====================================================================
   "Vívelo tú mismo" — four room photographs in the corners of a tall
   section that slide around the frame's edges as the page scrolls.

   The panel travel is derived from the panel size rather than guessed:
   the horizontal pitch is (frame width - 2*inset - panel width) and the
   vertical pitch is (frame height - 2*inset - panel height). Each panel
   travels exactly one leg of the loop, so it lands precisely on the
   corner the previous occupant vacated — top-right to top-left,
   top-left to bottom-left, bottom-left to bottom-right, and
   bottom-right back up to top-right.

   Container query units (cqw / cqh) do the measuring, because the
   frame is the sticky viewport-sized box and a percentage there would
   resolve against the panel instead. It also sidesteps the scrollbar:
   100vw includes the scrollbar, the frame's own width does not, and a
   15px mismatch would leave the travelling panel visibly short of its
   target corner.

   The panel size is min() in both axes so it cannot outgrow a short
   viewport — on a wide, short screen a fixed vw height would make the
   vertical pitch negative and the panels would overlap.
   ===================================================================== */
.liveit {
  position: relative;
  height: 300vh;
  view-timeline-name: --liveit;
  view-timeline-axis: block;
  background: var(--surface);
}
.liveit__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  container-type: size;

  --li-inset-x: 3cqw;
  /* The topbar is sticky and about 72px tall, so the top row of panels
     needs a larger inset than the bottom or it sits under the bar. */
  --li-inset-y: 9cqh;
  --li-w: min(36cqw, 56cqh);
  --li-h: calc(var(--li-w) * 0.75);
  --li-pitch-x: calc(100cqw - (2 * var(--li-inset-x)) - var(--li-w));
  --li-pitch-y: calc(100cqh - (2 * var(--li-inset-y)) - var(--li-h));
}
.liveit__copy {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: 0 var(--sp-5);
  text-align: center;
  pointer-events: none;
}
.liveit__copy h2,
.liveit__sub { position: relative; }
/* A soft radial veil behind the words. Without it the lettering sits
   directly on a photograph and becomes unreadable at the larger panel
   size — the panels now overlap the centre of the frame. */
.liveit__copy h2::before,
.liveit__sub::before {
  content: '';
  position: absolute;
  inset: -28% -46%;
  z-index: -1;
  background: radial-gradient(
    ellipse at center,
    rgba(255, 255, 255, .94) 0%,
    rgba(255, 255, 255, .82) 42%,
    rgba(255, 255, 255, 0) 72%
  );
  pointer-events: none;
}
.liveit__copy .liveit__sub::before {
  inset: -70% -70%;
  background: radial-gradient(
    ellipse at center,
    rgba(255, 255, 255, .92) 0%,
    rgba(255, 255, 255, .72) 45%,
    rgba(255, 255, 255, 0) 74%
  );
}
.liveit__copy h2 {
  font-size: clamp(2.2rem, 1.4rem + 3.4vw, 4.4rem);
  letter-spacing: -0.02em;
  max-width: 14ch;
}
.liveit__sub {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(0.95rem, 0.9rem + 0.35vw, 1.15rem);
  color: var(--muted);
  max-width: 34ch;
  text-shadow: 0 0 12px var(--surface), 0 0 26px var(--surface);
}
/* Between 861px and 1024px the panels are proportionally larger and the
   heading starts touching them as they travel the edges. Jessi said the
   images may overlap the text — "it doesn't matter if they cover the
   letters" — so the panels are raised above the copy (z-index 4) and
   the heading gets a soft halo to stay readable where they pass. */
@media (max-width: 1024px) and (min-width: 861px) {
  .liveit__copy h2 { font-size: clamp(1.9rem, 1.2rem + 2.4vw, 2.9rem); max-width: 16ch; }
  .liveit__sub { font-size: .92rem; max-width: 30ch; }
  .liveit__stage { --li-w: min(32cqw, 48cqh); }
}
.liveit__panel {
  position: absolute;
  z-index: 4;
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  background: var(--surface-alt);
  width: var(--li-w);
  height: var(--li-h);
  animation-duration: 1ms;   /* ignored — the timeline drives the timing */
  animation-timing-function: linear;
  animation-fill-mode: both;
  animation-timeline: --liveit;
  /* The movement runs exactly while the stage is pinned: from the
     section's top reaching the viewport top (entry 100%) to the MIDPOINT
     of the section (cover 50%). After that, the panels hold their position
     and the page scrolls normally. */
  animation-range: entry 100% cover 50%;
}
.liveit__panel img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.liveit__panel--tl { top: var(--li-inset-y); left: var(--li-inset-x); animation-name: li-down; }
.liveit__panel--tr { top: var(--li-inset-y); right: var(--li-inset-x); animation-name: li-left; }
.liveit__panel--bl { bottom: var(--li-inset-y); left: var(--li-inset-x); animation-name: li-right; }
.liveit__panel--br { bottom: var(--li-inset-y); right: var(--li-inset-x); animation-name: li-up; }

@keyframes li-left  { from { transform: translateX(0); } to { transform: translateX(calc(-0.5 * var(--li-pitch-x))); } }
@keyframes li-right { from { transform: translateX(0); } to { transform: translateX(calc(0.5 * var(--li-pitch-x))); } }
@keyframes li-down  { from { transform: translateY(0); } to { transform: translateY(calc(0.5 * var(--li-pitch-y))); } }
@keyframes li-up    { from { transform: translateY(0); } to { transform: translateY(calc(-0.5 * var(--li-pitch-y))); } }

/* Without scroll-driven animations the panels stay in their corners,
   which still reads as a four-corner composition. Reduced motion gets
   the same treatment — no travelling. */
@supports not (animation-timeline: view()) {
  .liveit__panel { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
  .liveit__panel { animation: none; }
}

/* Below the tablet breakpoint the travelling composition is too small to
   read and the sticky stage eats the screen. Stack the four panels in a
   plain two-column grid under the heading instead. */
@media (max-width: 860px) {
  .liveit { height: auto; }
  .liveit__stage {
    position: static;
    height: auto;
    overflow: visible;
    container-type: normal;
    /* The topbar is sticky, so the heading needs to clear it rather than
       scroll up underneath it. */
    padding: var(--sp-8) 0 var(--sp-8);
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-4);
  }
  .liveit__copy {
    position: static;
    grid-column: 1 / -1;
    margin-bottom: var(--sp-3);
    pointer-events: auto;
  }
  /* On mobile the heading sits on the plain section background, not on a
     photograph, so the radial veil behind the words has nothing to make
     legible — and its horizontal inset (-46%) pushed the page 210px wider
     than the viewport, giving every phone a horizontal scrollbar. Remove
     it below the breakpoint. */
  .liveit__copy h2::before,
  .liveit__sub::before { content: none; }
  .liveit__panel {
    position: static;
    width: auto;
    height: auto;
    aspect-ratio: 4 / 3;
    animation: none;
  }
}

/* =====================================================================
   Hero band — three-panel interior row under the home headline.

   Layout copied from the reference Jessi supplied: a square-ish panel on
   the left, a wide panel in the middle, a portrait panel on the right,
   separated by even gutters on a cream field.

   Measured from that reference: panel widths 396 / 923 / 371 px at a
   common height of ~400px, giving aspects of 0.992 / 2.290 / 0.872 and
   a 22px gutter. The images are pre-cropped to those aspects in Python,
   so `object-fit: cover` here is a safety net rather than the primary
   crop — nothing important is cut at any width.

   The grid columns are fractional rather than fixed px so the row keeps
   its proportions at every viewport.

   The row is deliberately wider than the text measure (1320px against a
   1140px wrap): the reference band runs nearly edge to edge, and the
   photographs want the width. The headline and body copy stay on the
   narrower measure.

   Each panel is a link to its service page. The zoom on hover is a
   transform on the image, clipped by the panel's overflow:hidden — a
   transform on the panel itself would push it out of the grid instead.
   ===================================================================== */
.heroband {
  width: 100%;
  padding: var(--sp-6) var(--sp-4);
  background: var(--surface-alt);
}
.heroband--financing {
  background: var(--surface-warm, #fdf8f2);
  padding: var(--sp-8) var(--sp-4);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  overflow-x: clip;
}
.heroband__trio {
  width: 100%;
  max-width: 1320px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 0.992fr 2.290fr 0.872fr;
  gap: var(--sp-4);
  align-items: stretch;
}
/* Slide in animations for financing trio: kitchen from left, bathroom from right */
.heroband--financing .heroband__link--kitchen {
  animation: trio-slide-left linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}
.heroband--financing .heroband__link--bathroom {
  animation: trio-slide-right linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}
.heroband--financing .heroband__link--living {
  animation: trio-fade-up linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 25%;
}

@keyframes trio-slide-left {
  from { transform: translate3d(-35%, 0, 0); opacity: 0; }
  to   { transform: translate3d(0, 0, 0); opacity: 1; }
}
@keyframes trio-slide-right {
  from { transform: translate3d(35%, 0, 0); opacity: 0; }
  to   { transform: translate3d(0, 0, 0); opacity: 1; }
}
@keyframes trio-fade-up {
  from { transform: translate3d(0, 20px, 0); opacity: 0; }
  to   { transform: translate3d(0, 0, 0); opacity: 1; }
}

@supports not (animation-timeline: view()) {
  .heroband--financing .heroband__link {
    animation: none;
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .heroband--financing .heroband__link {
    animation: none;
    opacity: 1;
    transform: none;
  }
}
.heroband__link {
  display: block;
  position: relative;
  min-width: 0;
  min-height: 0;
  color: inherit;
  text-decoration: none;
  border-radius: var(--r-md);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  transition: box-shadow .3s ease;
}
.heroband__link:focus-visible {
  outline: 3px solid var(--orange);
  outline-offset: 3px;
}
/* The wide middle link carries the aspect ratio that sets the row
   height; the two flanking links stretch to match it. */
.heroband__link--living { aspect-ratio: 2.290 / 1; }
.heroband__panel {
  margin: 0;
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background: var(--surface);
}
.heroband__panel img {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .45s cubic-bezier(.2, .7, .3, 1);
}
.heroband__link:hover img,
.heroband__link:focus-visible img { transform: scale(1.06); }
.heroband__link:hover,
.heroband__link:focus-visible { box-shadow: var(--shadow-lg); }
.heroband__cta {
  position: absolute;
  left: 50%;
  bottom: var(--sp-4);
  transform: translate(-50%, 8px);
  padding: .5rem .9rem;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .94);
  color: var(--navy);
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .02em;
  white-space: nowrap;
  opacity: 0;
  transition: opacity .3s ease, transform .3s ease;
  pointer-events: none;
}
.heroband__link:hover .heroband__cta,
.heroband__link:focus-visible .heroband__cta {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Below the tablet breakpoint the three-across row stops working: the
   middle panel would be 45% of a phone screen and unreadable. Stack the
   panels instead, keeping their own aspects so nothing is re-cropped.

   The panels are absolutely positioned images inside a relative link, so
   the link has no intrinsic height of its own — each one needs an
   explicit aspect-ratio to size the box. Without it the link collapses
   to 0px and the image has nothing to fill. */
@media (max-width: 860px) {
  .heroband {
    padding: var(--sp-5) var(--sp-4);
  }
  .heroband__trio {
    grid-template-columns: 1fr;
    gap: var(--sp-3);
    max-width: 560px;
  }
  .heroband__link--kitchen   { aspect-ratio: 1015 / 1024; }
  .heroband__link--living    { aspect-ratio: 1600 / 698; }
  .heroband__link--bathroom  { aspect-ratio: 928 / 1064; }
}

/* -- Blog article ------------------------------------------------------- */
/* Styles for the article layout. The article body is rendered from
   structured content, so the page can be long — these rules keep the
   measure comfortable and the hierarchy readable without a framework. */

.post__head {
  padding: var(--sp-8) 0 var(--sp-5);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--sp-6);
}
.post__h1 {
  font-size: clamp(1.9rem, 1.4rem + 2.2vw, 2.9rem);
  line-height: 1.12;
  margin: var(--sp-3) 0 var(--sp-4);
  letter-spacing: -0.015em;
}
.post__subtitle {
  font-size: var(--step-lead);
  color: var(--muted);
  line-height: 1.5;
  margin: 0 0 var(--sp-4);
}
.post__meta {
  font-size: 0.88rem;
  color: var(--muted);
  letter-spacing: 0.04em;
  margin: 0;
}
.post__body { padding-bottom: var(--sp-8); }
.post__body > * { margin-bottom: var(--sp-4); }
.post__body p { line-height: 1.68; }

.post__h2 {
  font-size: clamp(1.3rem, 1.1rem + 0.9vw, 1.7rem);
  line-height: 1.22;
  margin: var(--sp-7) 0 var(--sp-4);
  letter-spacing: -0.01em;
}
.post__block { margin-bottom: var(--sp-6); }
.post__block p { margin-bottom: 0; }

/* The number badge — the visual anchor for the five numbers the title
   promises. It must read as a count, not decoration. */
.post__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2rem;
  height: 2rem;
  margin-right: 0.6rem;
  border-radius: 50%;
  background: var(--orange);
  color: #fff;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  vertical-align: 0.08em;
}

.post__items { display: grid; gap: var(--sp-5); margin-top: var(--sp-4); }
.post__item h3 {
  font-size: 1.12rem;
  margin: 0 0 var(--sp-2);
  color: var(--ink);
}
.post__item p { margin: 0; }

/* The summary table. Wrapped in a scroll container so it stays usable
   on a phone without forcing the page to overflow horizontally. */
.post__tablewrap { overflow-x: auto; margin-top: var(--sp-4); }
.post__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
  min-width: 34rem;
}
.post__table th,
.post__table td {
  text-align: left;
  vertical-align: top;
  padding: 0.7rem 0.85rem;
  border-bottom: 1px solid var(--line);
}
.post__table th {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--ink);
  border-bottom: 2px solid var(--ink);
  white-space: nowrap;
}
.post__table td:first-child { font-weight: 600; color: var(--navy-mid); white-space: nowrap; }

.post__sources {
  list-style: none;
  padding: 0;
  margin: var(--sp-4) 0;
  display: grid;
  gap: var(--sp-3);
}
.post__sources li {
  font-size: 0.92rem;
  color: var(--muted);
  padding-left: 1rem;
  border-left: 2px solid var(--line);
}
.post__sources a { color: var(--navy-mid); font-weight: 600; }

/* The SEO analysis block — collapsible, and honest about being an
   editorial note rather than editorial content. */
.post__seo {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-alt);
  padding: var(--sp-4);
  margin: var(--sp-6) 0;
}
.post__seo summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--ink);
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 32px;
}
.post__seo summary::-webkit-details-marker { display: none; }
.post__seo summary::after {
  content: "";
  width: 9px; height: 9px; flex: none;
  border-right: 2px solid var(--orange);
  border-bottom: 2px solid var(--orange);
  transform: rotate(45deg) translateY(-2px);
  transition: transform 0.2s ease;
}
.post__seo[open] summary::after { transform: rotate(225deg) translateY(-2px); }
.post__seo .faq__body { padding: var(--sp-4) 0 0; }
.post__seo p { font-size: 0.92rem; margin-bottom: var(--sp-3); }

/* =====================================================================
   WHAT WE DO — full-width service panels.

   Jessi (2026-10-24) asked for the service categories to be split into
   one panel each: a photograph on one side, the category name, its own
   description and a "Get an estimate" link on the other. Her reference
   mockup is a half/half split — photo bleeding to the page edge, text on
   a cream field.

   The panels enter from alternating sides as the page scrolls: the first
   from the left, the second from the right, and so on, so the image and
   the words arrive together from the side they belong to.

   The slide is native CSS scroll-driven animation (`animation-timeline:
   view()`), the same mechanism the before/after scrub and the "Live it
   yourself" section use — no JavaScript.

   Each row is `min-height: 78vh` with the photograph filling the height
   of its column, so the panels read as full-bleed editorial bands rather
   than as cards.

   The photographs are generated illustrations of each trade. The section
   is a services menu, not a portfolio, so it makes no provenance claim
   and carries no per-image disclaimer.
   ===================================================================== */
.svc {
  background: var(--surface-warm);
  /* The slide-in animation translates the row sideways by 14% of its own
     width, which reaches past the viewport edge and produced a horizontal
     scrollbar on the whole page. `clip` contains the overflow without
     turning the section into a scroll container, so the sticky topbar and
     the rest of the page are unaffected. */
  overflow-x: clip;
}
.svc__list {
  list-style: none;
  margin: 0;
  padding: 0;
  margin-top: var(--sp-7);
}
.svc__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  min-height: 78vh;
  /* The text field is the cream one in Jessi's reference mockup; the
     photograph is the only thing that carries colour. */
  background: var(--surface-warm);
  border-top: 1px solid var(--line-soft);
}
.svc__fig {
  margin: 0;
  position: relative;
  overflow: hidden;
  background: var(--surface-alt);
}
.svc__fig img {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.svc__body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-7) clamp(var(--sp-5), 6vw, 6rem);
  max-width: 46rem;
}
.svc__body h3 {
  font-size: clamp(1.6rem, 1.1rem + 1.4vw, 2.4rem);
  line-height: 1.1;
}
.svc__body h3 a { color: inherit; text-decoration: none; }
.svc__body h3 a:hover { color: var(--orange-dark); }
.svc__body p { color: var(--body); max-width: 34rem; }
/* The estimate link uses the site's link navy rather than orange: on the
   cream field orange at this weight reads as a warning label, and every
   other "Get an estimate" link on the site is navy. */
.svc__cta a {
  font-weight: 600;
  color: var(--navy-mid);
  text-decoration: none;
}
.svc__cta a:hover { text-decoration: underline; }

/* The photograph leads on the left for the odd rows and on the right for
   the even ones, so the row alternates the way it enters. */
.svc__row--right .svc__fig { order: 2; }
.svc__row--right .svc__body { order: 1; }

/* Slide in from the side the row belongs to. `view()` tracks the row's
   own position in the scrollport, so the motion is tied to the row. */
.svc__row {
  animation: svc-in linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 28%;
}
.svc__row--left { animation-name: svc-in-left; }
.svc__row--right { animation-name: svc-in-right; }
@keyframes svc-in-left {
  from { transform: translate3d(-14%, 0, 0); opacity: 0; }
  to   { transform: translate3d(0, 0, 0); opacity: 1; }
}
@keyframes svc-in-right {
  from { transform: translate3d(14%, 0, 0); opacity: 0; }
  to   { transform: translate3d(0, 0, 0); opacity: 1; }
}

/* Browsers without scroll-driven animation support would otherwise keep
   the rows at the `from` keyframe (invisible). Show them plainly. */
@supports not (animation-timeline: view()) {
  .svc__row { animation: none; opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .svc__row { animation: none; opacity: 1; transform: none; }
}

/* Phones get their own VERTICAL photograph. A 16:9 frame cropped to a
   390px column shows only the middle third of the picture, and the
   paragraph landed on the pale sofa. The vertical frame is shot so the
   navy wall fills the middle 70% of the picture, so the copy sits on the
   wall at any crop. */
@media (max-width: 640px) {
  .hero--home::before {
    background-image: var(--hero-bg-m, url('/assets/projects/hero-bg-mobile.webp'));
    /* The image is PRE-CROPPED in the image pipeline to the slot's exact
       aspect (390:608) with the door frame on the left and the console
       on the right cut away, so the text column lands on bare painted
       wall at every phone width. Nothing is cropped in CSS, so the
       background-position cannot drift into a bright edge. */
    background-position: center center;
  }
}

/* Below the tablet breakpoint the half/half split leaves the text column
   too narrow, so the panels stack: photograph on top, words underneath. */
@media (max-width: 860px) {
  .svc__row {
    grid-template-columns: 1fr;
    min-height: 0;
  }
  .svc__row--right .svc__fig,
  .svc__row--right .svc__body { order: initial; }
  .svc__fig {
    position: relative;
    aspect-ratio: 4 / 3;
  }
  .svc__fig img { position: static; }
  .svc__body { padding: var(--sp-6) var(--sp-5); max-width: none; }
}

/* =====================================================================
   Home — estimate request section
   ---------------------------------------------------------------------
   The home page had no lead form at all: the only conversion path was
   the phone in the top bar, so a visitor who wanted email or WhatsApp
   had nowhere to go. This section carries the SAME contact rail the six
   service landings use, in a two-column layout against the copy.

   The rail is a light card (--surface-alt) so it reads as a distinct
   panel against the white page background.
   ===================================================================== */
/* Section Proof: White background variant requested by Jessi (2026-10-27) */
.section--proof {
  background: var(--surface, #ffffff);
  color: var(--navy);
  border-bottom: 1px solid var(--line);
}
.section--proof h2 {
  color: var(--navy-deep);
}
.section--proof .pillar {
  background: var(--surface-alt, #f6f8fb);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-6) var(--sp-5);
  box-shadow: var(--shadow-sm);
  transition: transform .25s ease, box-shadow .25s ease;
}
.section--proof .pillar:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
}
.section--proof .pillar h3 a {
  color: var(--navy-deep);
}
.section--proof .pillar h3 a:hover {
  color: var(--orange);
}
.section--proof .pillar p {
  color: var(--text-muted);
}
.section--proof .proof__header.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
.section--proof .proof__header.reveal.is-in {
  opacity: 1;
  transform: translateY(0);
}
.section--proof .pillar.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.section--proof .pillar.reveal.is-in {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .section--proof .proof__header.reveal,
  .section--proof .pillar.reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

.home-estimate {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: var(--sp-8);
  align-items: start;
}
.home-estimate h2 { margin-bottom: var(--sp-3); }
.home-estimate .lead { max-width: 52ch; }
.home-estimate__list {
  list-style: none;
  margin: var(--sp-5) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-2);
}
.home-estimate__list li {
  position: relative;
  padding-left: 1.5rem;
  font-size: 0.95rem;
  color: var(--body);
}
.home-estimate__list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--orange);
}

@media (max-width: 860px) {
  .home-estimate { grid-template-columns: 1fr; gap: var(--sp-6); }
}

@media (min-width: 641px) {
  .pillars--2x2 {
    grid-template-columns: repeat(2, 1fr);
  }
}

.pillars--cards {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
@media (max-width: 1024px) {
  .pillars--cards {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--sp-5);
  }
}
@media (max-width: 600px) {
  .pillars--cards {
    grid-template-columns: 1fr;
    gap: var(--sp-6);
  }
}

.pillar--has-image {
  border-top: none;
  padding-top: 0;
  display: flex;
  flex-direction: column;
}

.pillar__media {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--r-md, 8px);
  margin-bottom: var(--sp-4);
  background: var(--surface-alt, #f4f6f8);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
}

.pillar__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.pillar--has-image:hover .pillar__media img {
  transform: scale(1.04);
}

.pillar__content {
  display: flex;
  flex-direction: column;
}

.pillar--has-image h3 {
  font-family: var(--font-body);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--navy-deep, #142943);
  margin: 0 0 var(--sp-2);
  line-height: 1.3;
}

.pillar--has-image p {
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--muted, #64748b);
  margin: 0;
}

/* Staggered scroll animation for cards */
.pillars--cards .pillar.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
.pillars--cards .pillar:nth-child(2).reveal { transition-delay: 0.1s; }
.pillars--cards .pillar:nth-child(3).reveal { transition-delay: 0.2s; }
.pillars--cards .pillar:nth-child(4).reveal { transition-delay: 0.3s; }

.pillars--cards .pillar.reveal.is-in {
  opacity: 1;
  transform: translateY(0);
}

.pillar.reveal {
  opacity: 0;
  transform: translateX(-40px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.pillar.reveal.is-in {
  opacity: 1;
  transform: translateX(0);
}
@media (prefers-reduced-motion: reduce) {
  .pillar.reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}


/* =====================================================================
   Financing Page — Custom Components
   Uses design system tokens from base.css
   ===================================================================== */

/* Financing Benefits Grid */
.financing-benefits {
  margin-top: var(--sp-4);
}

.financing-benefits-header {
  text-align: center;
  margin-bottom: var(--sp-10);
}

.financing-benefits-header .eyebrow {
  margin-bottom: var(--sp-3);
}

.financing-benefits-header h2 {
  font-size: 1.1rem;
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--navy-deep);
  line-height: 1.3;
  margin: var(--sp-3) 0;
}

.benefits-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-6);
  max-width: 1200px;
  margin: 0 auto;
}

.benefit-card {
  background: var(--navy);
  border-radius: 16px;
  padding: var(--sp-8) var(--sp-6);
  text-align: center;
  transition: box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), border-bottom-color 0.3s;
  border-bottom: 3px solid transparent;
  color: #fff;
  opacity: 0;
  transform: translateX(-60px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.benefit-card--right {
  transform: translateX(60px);
}

.benefit-card:nth-child(2) { transition-delay: 0.1s; }
.benefit-card:nth-child(3) { transition-delay: 0.2s; }
.benefit-card:nth-child(4) { transition-delay: 0.3s; }
.benefit-card:nth-child(5) { transition-delay: 0.4s; }
.benefit-card:nth-child(6) { transition-delay: 0.5s; }

.benefit-card.is-in {
  opacity: 1;
  transform: translateX(0);
}

.benefit-card:hover {
  box-shadow: 0 20px 40px rgba(0,0,0,0.3);
  transform: translateY(-5px);
  border-bottom-color: var(--orange);
}

@media (prefers-reduced-motion: reduce) {
  .benefit-card {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .benefit-card:hover {
    transform: none;
  }
}

.benefit-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  margin: 0 auto var(--sp-4);
  background: var(--orange);
  border-radius: 12px;
  color: #fff;
}

.benefit-card h3 {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  color: #fff;
  margin-bottom: var(--sp-2);
}

.benefit-card p {
  color: rgba(255,255,255,0.85);
  font-size: 0.9rem;
  line-height: 1.6;
  margin: 0;
}

.financing-cta {
  text-align: center;
  margin-top: var(--sp-8);
}

.financing-note {
  margin-top: var(--sp-3);
  color: var(--muted);
  font-size: 0.85rem;
}

/* How It Works - Financing Steps */
.steps--financing {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-6);
  list-style: none;
  padding: 0;
  margin: 0;
  max-width: 1000px;
  margin-left: auto;
  margin-right: auto;
}

.steps--financing > li {
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 16px;
  padding: var(--sp-8);
  text-align: center;
  position: relative;
  overflow: hidden;
  opacity: 0;
  transform: translateX(-40px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.steps--financing > li:nth-child(2) { transition-delay: 0.1s; }
.steps--financing > li:nth-child(3) { transition-delay: 0.2s; }

.steps--financing > li.is-in {
  opacity: 1;
  transform: translateX(0);
}

.steps--financing > li::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(90deg, var(--orange), #ffb84d);
}

.step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  margin: 0 auto var(--sp-4);
  background: rgba(255,140,0,0.15);
  border-radius: 50%;
  color: var(--orange);
  font-size: 1.5rem;
  font-weight: 700;
  font-family: var(--font-display);
}

.steps--financing h3 {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  color: #fff;
  margin-bottom: var(--sp-2);
}

.steps--financing p {
  color: rgba(255,255,255,0.75);
  font-size: 0.95rem;
  line-height: 1.6;
  margin: 0;
}

/* Stats Bar */
.stats-bar {
  background: var(--navy-deep);
  padding: var(--sp-10) 0;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-6);
  text-align: center;
  max-width: 1000px;
  margin: 0 auto;
}

.stat-item {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.stat-item:nth-child(2) { transition-delay: 0.1s; }
.stat-item:nth-child(3) { transition-delay: 0.2s; }
.stat-item:nth-child(4) { transition-delay: 0.3s; }

.stat-item.is-in {
  opacity: 1;
  transform: translateY(0);
}

.stat-number {
  font-size: clamp(2.5rem, 2rem + 3vw, 3.5rem);
  font-weight: 800;
  font-family: var(--font-display);
  color: var(--orange);
  line-height: 1;
  letter-spacing: -0.02em;
}

.stat-label {
  color: rgba(255,255,255,0.7);
  font-size: 0.95rem;
  margin-top: var(--sp-2);
  font-weight: 500;
}

/* Eligible Projects Grid

   The six tiles are one uniform set: three across, every card the same
   width and the same height in its row. `align-items` is left at the
   grid default (stretch) on purpose — the description lengths differ,
   so `start` would give each tile its own height and the row would
   read as six different boxes again.

   NOTE: this grid only works if every card is a real child of it. An
   unclosed <p> in the card markup lets the browser's HTML parser close
   the wrapper early, which drops cards 2-6 outside the grid and makes
   them full-width. Keep the </p> on every paragraph. */
.eligible-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-6);
  max-width: 1000px;
  margin: 0 auto;
}

.eligible-card {
  background: var(--surface);
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--line);
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.eligible-card--left { transform: translateX(-40px); }
.eligible-card--right { transform: translateX(40px); }

.eligible-card:nth-child(2) { transition-delay: 0.1s; }
.eligible-card:nth-child(3) { transition-delay: 0.2s; }
.eligible-card:nth-child(4) { transition-delay: 0.3s; }
.eligible-card:nth-child(5) { transition-delay: 0.4s; }
.eligible-card:nth-child(6) { transition-delay: 0.5s; }

.eligible-card.is-in {
  opacity: 1;
  transform: translateX(0) translateY(0);
}

.eligible-card:hover {
  transform: translateY(-8px);
  box-shadow: var(--shadow-lg);
}

.eligible-card__media {
  aspect-ratio: 16/10;
  overflow: hidden;
  position: relative;
}

.eligible-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.eligible-card:hover .eligible-card__media img {
  transform: scale(1.05);
}

.eligible-card__content {
  padding: var(--sp-6);
  /* Column flex with the price row pinned to the bottom: the six
     descriptions are different lengths, so without this the price
     baseline sits at a different height on every card and the row of
     tiles reads as six unrelated boxes instead of one set. */
  display: flex;
  flex-direction: column;
  height: 100%;
}
.eligible-card__content p { flex: 1 0 auto; }

.eligible-card__content h3 {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--navy-deep);
  margin-bottom: var(--sp-2);
}

.eligible-card__content p {
  color: var(--body);
  font-size: 0.9rem;
  line-height: 1.6;
  margin-bottom: var(--sp-3);
}

.eligible-card__price {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line-soft);
}

.price-range {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--orange);
}

.price-note {
  color: var(--muted);
  font-size: 0.85rem;
}

/* Section Header */
.section-header {
  text-align: center;
  margin-bottom: var(--sp-10);
}

.section-header .eyebrow {
  margin-bottom: var(--sp-3);
}

.section-header h2 {
  font-size: clamp(1.9rem, 1.4rem + 2.2vw, 2.9rem);
  font-family: var(--font-display);
  color: var(--navy-deep);
  line-height: 1.2;
  margin: var(--sp-3) 0;
}

.section-header .lead {
  max-width: 55ch;
  margin: 0 auto;
  font-size: 1.05rem;
  line-height: 1.6;
}

/* GC Advantage Section */
.gc-advantage-section {
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.gc-advantage-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--sp-8);
  align-items: start;
}

.gc-advantage__content h3 {
  font-size: clamp(1.5rem, 1.1rem + 1.5vw, 2rem);
  font-family: var(--font-display);
  color: var(--navy-deep);
  margin: 0.3rem 0 0.6rem;
}

.gc-advantage__content p {
  color: var(--body);
  font-size: 1rem;
  line-height: 1.65;
  margin: 0;
}

.gc-advantage__actions {
  margin-top: var(--sp-5);
  display: flex;
  gap: var(--sp-3);
  align-items: center;
  flex-wrap: wrap;
}

.gc-advantage__card {
  background: var(--surface-alt);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
  border: 1px solid var(--line-soft);
}

.gc-advantage__card h4 {
  margin: 0 0 var(--sp-4);
  font-size: 1.05rem;
  color: var(--navy);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.gc-advantage__card ul {
  margin: 0;
  padding-left: 1.2rem;
  font-size: 0.9rem;
  color: var(--body);
  line-height: 1.6;
  display: grid;
  gap: 0.5rem;
}

/* Final CTA */
.cta-final {
  background: var(--navy-deep);
  padding: var(--sp-14) 0;
}

.cta-final h2 {
  color: #fff;
  font-size: clamp(1.9rem, 1.4rem + 2.2vw, 2.9rem);
  font-family: var(--font-display);
  margin-bottom: var(--sp-3);
}

.cta-final .lead {
  color: rgba(255,255,255,0.75);
  max-width: 55ch;
  margin: 0 auto var(--sp-6);
  font-size: 1.1rem;
}

.cta-final__actions {
  display: flex;
  gap: var(--sp-3);
  justify-content: center;
  flex-wrap: wrap;
}

.cta-final__disclaimer {
  margin-top: var(--sp-5);
  color: rgba(255,255,255,0.45);
  font-size: 0.85rem;
}

/* Hero Media */
.hero__media {
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--shadow-xl);
}

.hero__media img {
  width: 100%;
  height: auto;
  display: block;
}

/* Responsive */
@media (max-width: 1024px) {
  .benefits-grid,
  .eligible-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .steps--financing {
    grid-template-columns: repeat(2, 1fr);
  }
  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .gc-advantage-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 640px) {
  .benefits-grid,
  .eligible-grid,
  .steps--financing,
  .stats-grid {
    grid-template-columns: 1fr;
  }
}

/* =====================================================================
   Splash Screen (Opción B: Fondo Navy #142943 + CERPA Naranja #ff8c00)
   Jessi Val (2026-10-27): pantalla de bienvenida completa que dura 3s
   y luego se desliza hacia arriba revelando el sitio web.
   ===================================================================== */
.splash {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  z-index: 99999;
  background-color: var(--navy-deep, #142943);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: clamp(1.5rem, 4vh, 3.5rem) clamp(1.5rem, 4vw, 4rem);
  box-sizing: border-box;
  overflow: hidden;
  user-select: none;
  pointer-events: auto;
  transform: translateY(0);
  transition: transform 0.9s cubic-bezier(0.77, 0, 0.175, 1);
  will-change: transform;
}

.splash--slide-up {
  transform: translateY(-100%);
  pointer-events: none;
}

/* =====================================================================
   Financing Benefits Grid - Scroll Animations
   Cards alternate: odd from left, even from right
   ===================================================================== */
.benefits-grid .benefit-card.reveal {
  opacity: 0;
  transform: translateX(-60px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.benefits-grid .benefit-card--right.reveal {
  transform: translateX(60px);
}

.benefits-grid .benefit-card:nth-child(2).reveal { transition-delay: 0.1s; }
.benefits-grid .benefit-card:nth-child(3).reveal { transition-delay: 0.2s; }
.benefits-grid .benefit-card:nth-child(4).reveal { transition-delay: 0.3s; }
.benefits-grid .benefit-card:nth-child(5).reveal { transition-delay: 0.4s; }
.benefits-grid .benefit-card:nth-child(6).reveal { transition-delay: 0.5s; }

.benefits-grid .benefit-card.reveal.is-in {
  opacity: 1;
  transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
  .benefits-grid .benefit-card.reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Hover effect for financing benefit cards */
.benefits-grid .benefit-card:hover {
  box-shadow: 0 20px 40px rgba(0,0,0,0.3);
  transform: translateY(-5px);
  border-bottom-color: var(--orange);
}

@media (prefers-reduced-motion: reduce) {
  .benefits-grid .benefit-card:hover {
    transform: none;
  }
}
  display: flex;
  align-items: center;
  justify-content: center;
}

.splash__logo {
  width: clamp(100px, 28vw, 200px);
  height: auto;
  max-width: 100%;
}

